/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 | } |