Coverage Report

Created: 2025-08-28 06:20

/src/h2o/lib/handler/configurator/headers_util.c
Line
Count
Source (jump to first uncovered line)
1
#include "h2o.h"
2
#include "h2o/configurator.h"
3
4
struct headers_util_add_arg_t {
5
    yoml_t *node;
6
    h2o_iovec_t *name;
7
    h2o_iovec_t value;
8
};
9
10
struct headers_util_configurator_t {
11
    h2o_configurator_t super;
12
    h2o_configurator_t *child;
13
    h2o_configurator_get_headers_commands_cb get_commands;
14
};
15
16
static int extract_name(const char *src, size_t len, h2o_iovec_t **_name)
17
0
{
18
0
    h2o_iovec_t name;
19
0
    const h2o_token_t *name_token;
20
21
0
    name = h2o_str_stripws(src, len);
22
0
    if (name.len == 0)
23
0
        return -1;
24
25
0
    name = h2o_strdup(NULL, name.base, name.len);
26
0
    h2o_strtolower(name.base, name.len);
27
28
0
    if ((name_token = h2o_lookup_token(name.base, name.len)) != NULL) {
29
0
        *_name = (h2o_iovec_t *)&name_token->buf;
30
0
        free(name.base);
31
0
    } else {
32
0
        *_name = h2o_mem_alloc(sizeof(**_name));
33
0
        **_name = name;
34
0
    }
35
36
0
    return 0;
37
0
}
38
39
static int extract_name_value(const char *src, h2o_iovec_t **name, h2o_iovec_t *value)
40
0
{
41
0
    const char *colon = strchr(src, ':');
42
43
0
    if (colon == NULL)
44
0
        return -1;
45
46
0
    if (extract_name(src, colon - src, name) != 0)
47
0
        return -1;
48
0
    *value = h2o_str_stripws(colon + 1, strlen(colon + 1));
49
0
    *value = h2o_strdup(NULL, value->base, value->len);
50
51
0
    return 0;
52
0
}
53
54
static int is_list_cmd(int cmd_id)
55
0
{
56
0
    return cmd_id == H2O_HEADERS_CMD_UNSET || cmd_id == H2O_HEADERS_CMD_UNSETUNLESS || cmd_id == H2O_HEADERS_CMD_COOKIE_UNSET ||
57
0
           cmd_id == H2O_HEADERS_CMD_COOKIE_UNSETUNLESS;
58
0
}
59
60
static int add_cmd(h2o_configurator_command_t *cmd, int cmd_id, struct headers_util_add_arg_t *args, size_t num_args,
61
                   h2o_headers_command_when_t when, h2o_headers_command_t **cmds)
62
0
{
63
0
    for (size_t i = 0; i < num_args; i++) {
64
0
        if (h2o_iovec_is_token(args[i].name)) {
65
0
            const h2o_token_t *token = (void *)args[i].name;
66
0
            if (h2o_headers_is_prohibited_name(token)) {
67
0
                h2o_configurator_errprintf(cmd, args[i].node, "the named header cannot be rewritten");
68
0
                return -1;
69
0
            }
70
0
        }
71
0
        if (!is_list_cmd(cmd_id))
72
0
            h2o_headers_append_command(cmds, cmd_id, &(h2o_headers_command_arg_t){args[i].name, args[i].value}, 1, when);
73
0
    }
74
0
    if (is_list_cmd(cmd_id)) {
75
0
        h2o_headers_command_arg_t cmdargs[num_args];
76
0
        for (size_t i = 0; i < num_args; ++i)
77
0
            cmdargs[i] = (h2o_headers_command_arg_t){args[i].name, args[i].value};
78
0
        h2o_headers_append_command(cmds, cmd_id, cmdargs, num_args, when);
79
0
    }
80
81
0
    return 0;
82
0
}
83
84
static int parse_header_node(h2o_configurator_command_t *cmd, yoml_t **node, yoml_t ***headers, size_t *num_headers,
85
                             h2o_headers_command_when_t *when)
86
0
{
87
88
0
    if ((*node)->type == YOML_TYPE_SCALAR) {
89
0
        *headers = node;
90
0
        *num_headers = 1;
91
0
        *when = H2O_HEADERS_CMD_WHEN_FINAL;
92
0
    } else if ((*node)->type == YOML_TYPE_SEQUENCE) {
93
0
        *headers = (*node)->data.sequence.elements;
94
0
        *num_headers = (*node)->data.sequence.size;
95
0
        *when = H2O_HEADERS_CMD_WHEN_FINAL;
96
0
    } else {
97
0
        yoml_t **header_node;
98
0
        yoml_t **when_node = NULL;
99
0
        if (h2o_configurator_parse_mapping(cmd, *node, "header:sa", "when:*", &header_node, &when_node) != 0)
100
0
            return -1;
101
0
        if ((*header_node)->type == YOML_TYPE_SEQUENCE) {
102
0
            *headers = (*header_node)->data.sequence.elements;
103
0
            *num_headers = (*header_node)->data.sequence.size;
104
0
        } else {
105
0
            *headers = header_node;
106
0
            *num_headers = 1;
107
0
        }
108
0
        if (when_node == NULL) {
109
0
            *when = H2O_HEADERS_CMD_WHEN_FINAL;
110
0
        } else {
111
0
            switch (h2o_configurator_get_one_of(cmd, *when_node, "final,early,all")) {
112
0
            case 0:
113
0
                *when = H2O_HEADERS_CMD_WHEN_FINAL;
114
0
                break;
115
0
            case 1:
116
0
                *when = H2O_HEADERS_CMD_WHEN_EARLY;
117
0
                break;
118
0
            case 2:
119
0
                *when = H2O_HEADERS_CMD_WHEN_ALL;
120
0
                break;
121
0
            default:
122
0
                return -1;
123
0
            }
124
0
        }
125
0
    }
126
0
    return 0;
127
0
}
128
129
static int on_config_header_2arg(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, int cmd_id, yoml_t *node,
130
                                 h2o_headers_command_t **headers_cmds)
131
0
{
132
0
    yoml_t **headers;
133
0
    size_t num_headers;
134
0
    h2o_headers_command_when_t when;
135
136
0
    if (parse_header_node(cmd, &node, &headers, &num_headers, &when) != 0)
137
0
        return -1;
138
139
0
    struct headers_util_add_arg_t args[num_headers];
140
0
    int i;
141
0
    for (i = 0; i != num_headers; ++i) {
142
0
        args[i].node = headers[i];
143
0
        if (extract_name_value(args[i].node->data.scalar, &args[i].name, &args[i].value) != 0) {
144
0
            h2o_configurator_errprintf(cmd, args[i].node, "failed to parse the value; should be in form of `name: value`");
145
0
            return -1;
146
0
        }
147
0
    }
148
0
    if (add_cmd(cmd, cmd_id, args, num_headers, when, headers_cmds) != 0) {
149
0
        for (i = 0; i != num_headers; i++) {
150
0
            if (!h2o_iovec_is_token(args[i].name))
151
0
                free(args[i].name->base);
152
0
            free(args[i].value.base);
153
0
        }
154
0
        return -1;
155
0
    }
156
0
    return 0;
157
0
}
158
159
static int on_config_unset_core(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node, int cmd_id)
160
0
{
161
0
    yoml_t **headers;
162
0
    size_t num_headers;
163
0
    h2o_headers_command_when_t when;
164
0
    struct headers_util_configurator_t *self = (void *)cmd->configurator;
165
166
0
    if (parse_header_node(cmd, &node, &headers, &num_headers, &when) != 0)
167
0
        return -1;
168
169
0
    struct headers_util_add_arg_t args[num_headers];
170
0
    for (size_t i = 0; i != num_headers; ++i) {
171
0
        args[i].node = headers[i];
172
0
        if (cmd_id == H2O_HEADERS_CMD_UNSET || cmd_id == H2O_HEADERS_CMD_UNSETUNLESS) {
173
0
            if (extract_name(args[i].node->data.scalar, strlen(args[i].node->data.scalar), &args[i].name) != 0) {
174
0
                h2o_configurator_errprintf(cmd, args[i].node, "invalid header name");
175
0
                return -1;
176
0
            }
177
0
        } else {
178
0
            h2o_iovec_t tmp = h2o_str_stripws(args[i].node->data.scalar, strlen(args[i].node->data.scalar));
179
0
            if (tmp.len == 0) {
180
0
                h2o_configurator_errprintf(cmd, args[i].node, "invalid header name");
181
0
                return -1;
182
0
            }
183
0
            args[i].name = h2o_mem_alloc(sizeof(*args[0].name));
184
0
            *args[i].name = h2o_strdup(NULL, tmp.base, tmp.len);
185
0
        }
186
0
        args[i].value = h2o_iovec_init(NULL, 0);
187
0
    }
188
0
    if (add_cmd(cmd, cmd_id, args, num_headers, when, self->get_commands(self->child)) != 0) {
189
0
        for (size_t i = 0; i != num_headers; i++) {
190
0
            if (!h2o_iovec_is_token(args[i].name))
191
0
                free(args[i].name->base);
192
0
        }
193
0
        return -1;
194
0
    }
195
196
0
    return 0;
197
0
}
198
199
static int on_config_header_unset(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
200
0
{
201
0
    return on_config_unset_core(cmd, ctx, node, H2O_HEADERS_CMD_UNSET);
202
0
}
203
static int on_config_header_unsetunless(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
204
0
{
205
0
    return on_config_unset_core(cmd, ctx, node, H2O_HEADERS_CMD_UNSETUNLESS);
206
0
}
207
208
static int on_config_cookie_unset(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
209
0
{
210
0
    return on_config_unset_core(cmd, ctx, node, H2O_HEADERS_CMD_COOKIE_UNSET);
211
0
}
212
static int on_config_cookie_unsetunless(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
213
0
{
214
0
    return on_config_unset_core(cmd, ctx, node, H2O_HEADERS_CMD_COOKIE_UNSETUNLESS);
215
0
}
216
217
#define DEFINE_2ARG(fn, cmd_id)                                                                                                    \
218
    static int fn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)                                  \
219
0
    {                                                                                                                              \
220
0
        struct headers_util_configurator_t *self = (void *)cmd->configurator;                                                      \
221
0
        return on_config_header_2arg(cmd, ctx, cmd_id, node, self->get_commands(self->child));                                     \
222
0
    }
Unexecuted instantiation: headers_util.c:on_config_header_add
Unexecuted instantiation: headers_util.c:on_config_header_append
Unexecuted instantiation: headers_util.c:on_config_header_merge
Unexecuted instantiation: headers_util.c:on_config_header_set
Unexecuted instantiation: headers_util.c:on_config_header_setifempty
223
224
DEFINE_2ARG(on_config_header_add, H2O_HEADERS_CMD_ADD)
225
DEFINE_2ARG(on_config_header_append, H2O_HEADERS_CMD_APPEND)
226
DEFINE_2ARG(on_config_header_merge, H2O_HEADERS_CMD_MERGE)
227
DEFINE_2ARG(on_config_header_set, H2O_HEADERS_CMD_SET)
228
DEFINE_2ARG(on_config_header_setifempty, H2O_HEADERS_CMD_SETIFEMPTY)
229
230
#undef DEFINE_2ARG
231
232
void h2o_configurator_define_headers_commands(h2o_globalconf_t *global_conf, h2o_configurator_t *conf, const char *prefix,
233
                                              h2o_configurator_get_headers_commands_cb get_commands)
234
0
{
235
0
    struct headers_util_configurator_t *c = (void *)h2o_configurator_create(global_conf, sizeof(*c));
236
0
    c->child = conf;
237
0
    c->get_commands = get_commands;
238
0
    size_t prefix_len = strlen(prefix);
239
240
0
#define DEFINE_CMD_NAME(name, suffix)                                                                                              \
241
0
    char *name = h2o_mem_alloc(prefix_len + sizeof(suffix));                                                                       \
242
0
    memcpy(name, prefix, prefix_len);                                                                                              \
243
0
    memcpy(name + prefix_len, suffix, sizeof(suffix))
244
245
0
    DEFINE_CMD_NAME(add_directive, ".add");
246
0
    DEFINE_CMD_NAME(append_directive, ".append");
247
0
    DEFINE_CMD_NAME(merge_directive, ".merge");
248
0
    DEFINE_CMD_NAME(set_directive, ".set");
249
0
    DEFINE_CMD_NAME(setifempty_directive, ".setifempty");
250
0
    DEFINE_CMD_NAME(unset_directive, ".unset");
251
0
    DEFINE_CMD_NAME(unsetunless_directive, ".unsetunless");
252
0
    DEFINE_CMD_NAME(cookie_unset_directive, ".cookie.unset");
253
0
    DEFINE_CMD_NAME(cookie_unsetunless_directive, ".cookie.unsetunless");
254
0
#undef DEFINE_CMD_NAME
255
256
0
#define DEFINE_CMD(name, cb)                                                                                                       \
257
0
    h2o_configurator_define_command(&c->super, name,                                                                               \
258
0
                                    H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_HOST | H2O_CONFIGURATOR_FLAG_PATH |       \
259
0
                                        H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE |              \
260
0
                                        H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING,                                                      \
261
0
                                    cb)
262
0
    DEFINE_CMD(add_directive, on_config_header_add);
263
0
    DEFINE_CMD(append_directive, on_config_header_append);
264
0
    DEFINE_CMD(merge_directive, on_config_header_merge);
265
0
    DEFINE_CMD(set_directive, on_config_header_set);
266
0
    DEFINE_CMD(setifempty_directive, on_config_header_setifempty);
267
0
    DEFINE_CMD(unset_directive, on_config_header_unset);
268
0
    DEFINE_CMD(unsetunless_directive, on_config_header_unsetunless);
269
0
    DEFINE_CMD(cookie_unset_directive, on_config_cookie_unset);
270
0
    DEFINE_CMD(cookie_unsetunless_directive, on_config_cookie_unsetunless);
271
0
#undef DEFINE_CMD
272
0
}