/src/h2o/lib/handler/configurator/proxy.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2014 DeNA Co., Ltd. |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | * of this software and associated documentation files (the "Software"), to |
6 | | * deal in the Software without restriction, including without limitation the |
7 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
8 | | * sell copies of the Software, and to permit persons to whom the Software is |
9 | | * furnished to do so, subject to the following conditions: |
10 | | * |
11 | | * The above copyright notice and this permission notice shall be included in |
12 | | * all copies or substantial portions of the Software. |
13 | | * |
14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
19 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
20 | | * IN THE SOFTWARE. |
21 | | */ |
22 | | #include <inttypes.h> |
23 | | #include <errno.h> |
24 | | #include <stdio.h> |
25 | | #include <openssl/crypto.h> |
26 | | #include <openssl/err.h> |
27 | | #include <openssl/ssl.h> |
28 | | #include "h2o.h" |
29 | | #include "h2o/configurator.h" |
30 | | #include "h2o/balancer.h" |
31 | | #include "h2o/socket.h" |
32 | | |
33 | | struct proxy_config_vars_t { |
34 | | h2o_proxy_config_vars_t conf; |
35 | | SSL_CTX *ssl_ctx; |
36 | | }; |
37 | | |
38 | | struct proxy_configurator_t { |
39 | | h2o_configurator_t super; |
40 | | unsigned connect_timeout_set : 1; |
41 | | unsigned first_byte_timeout_set : 1; |
42 | | struct proxy_config_vars_t *vars; |
43 | | struct proxy_config_vars_t _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1]; |
44 | | }; |
45 | | |
46 | | static void warn_deprecation(h2o_configurator_command_t *cmd, yoml_t *node, const char *new_cmd, const char *extra) |
47 | 0 | { |
48 | 0 | if (strcasecmp(cmd->name, new_cmd) != 0) |
49 | 0 | h2o_configurator_errprintf(cmd, node, "the command is deprecated; use %s%s", new_cmd, extra); |
50 | 0 | } |
51 | | |
52 | | static int on_config_timeout_io(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
53 | 0 | { |
54 | 0 | int ret; |
55 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
56 | 0 | ret = h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->conf.io_timeout); |
57 | 0 | if (ret < 0) |
58 | 0 | return ret; |
59 | 0 | if (!self->connect_timeout_set) |
60 | 0 | self->vars->conf.connect_timeout = self->vars->conf.io_timeout; |
61 | 0 | if (!self->first_byte_timeout_set) |
62 | 0 | self->vars->conf.first_byte_timeout = self->vars->conf.io_timeout; |
63 | 0 | return ret; |
64 | 0 | } |
65 | | |
66 | | static int on_config_timeout_connect(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
67 | 0 | { |
68 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
69 | 0 | self->connect_timeout_set = 1; |
70 | 0 | return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->conf.connect_timeout); |
71 | 0 | } |
72 | | |
73 | | static int on_config_timeout_first_byte(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
74 | 0 | { |
75 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
76 | 0 | self->first_byte_timeout_set = 1; |
77 | 0 | return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->conf.first_byte_timeout); |
78 | 0 | } |
79 | | |
80 | | static int on_config_timeout_keepalive(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
81 | 0 | { |
82 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
83 | 0 | return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->conf.keepalive_timeout); |
84 | 0 | } |
85 | | |
86 | | static int on_config_happy_eyeballs_name_resolution_delay(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, |
87 | | yoml_t *node) |
88 | 0 | { |
89 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
90 | 0 | return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->conf.happy_eyeballs.name_resolution_delay); |
91 | 0 | } |
92 | | |
93 | | static int on_config_happy_eyeballs_connection_attempt_delay(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, |
94 | | yoml_t *node) |
95 | 0 | { |
96 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
97 | 0 | return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->conf.happy_eyeballs.connection_attempt_delay); |
98 | 0 | } |
99 | | |
100 | | static int on_config_preserve_host(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
101 | 0 | { |
102 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
103 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
104 | 0 | if (ret == -1) |
105 | 0 | return -1; |
106 | 0 | self->vars->conf.preserve_host = (int)ret; |
107 | 0 | return 0; |
108 | 0 | } |
109 | | |
110 | | static int on_config_proxy_protocol(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
111 | 0 | { |
112 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
113 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
114 | 0 | if (ret == -1) |
115 | 0 | return -1; |
116 | 0 | self->vars->conf.use_proxy_protocol = (int)ret; |
117 | 0 | return 0; |
118 | 0 | } |
119 | | |
120 | | static int on_config_connect_proxy_status(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
121 | 0 | { |
122 | 0 | warn_deprecation(cmd, node, "proxy.connect.emit-proxy-status", ""); |
123 | |
|
124 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
125 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
126 | 0 | if (ret == -1) |
127 | 0 | return -1; |
128 | 0 | self->vars->conf.connect_proxy_status_enabled = (int)ret; |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | | static int on_config_proxy_status_identity(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
133 | 0 | { |
134 | 0 | warn_deprecation(cmd, node, "proxy.proxy-status.identity", ""); |
135 | | |
136 | | /* https://tools.ietf.org/html/rfc8941#section-3.3.4 */ |
137 | 0 | static const char *tfirst = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz*"; |
138 | 0 | static const char *tchars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&'*+-.^_`|~:/"; |
139 | |
|
140 | 0 | const char *s = node->data.scalar; |
141 | 0 | size_t slen = strlen(s); |
142 | 0 | for (size_t i = 0; i < slen; ++i) { |
143 | 0 | unsigned char b = s[i]; |
144 | 0 | if (b < 0x20 || b > 0x7E) { |
145 | 0 | h2o_configurator_errprintf(cmd, node, "the identity must only consist of printable ASCII characters"); |
146 | 0 | return -1; |
147 | 0 | } |
148 | 0 | } |
149 | 0 | if (s[0] != '\0' && strchr(tfirst, s[0]) != NULL && strspn(s, tchars) == slen) { |
150 | | /* sf-token */ |
151 | 0 | ctx->globalconf->proxy_status_identity = h2o_strdup(NULL, s, slen); |
152 | 0 | } else { |
153 | | /* sf-string */ |
154 | 0 | ctx->globalconf->proxy_status_identity = h2o_encode_sf_string(NULL, s, slen); |
155 | 0 | } |
156 | |
|
157 | 0 | return 0; |
158 | 0 | } |
159 | | |
160 | | static int on_config_tunnel(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
161 | 0 | { |
162 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
163 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
164 | 0 | if (ret == -1) |
165 | 0 | return -1; |
166 | 0 | self->vars->conf.tunnel_enabled = (int)ret; |
167 | 0 | return 0; |
168 | 0 | } |
169 | | |
170 | | static SSL_CTX *create_ssl_ctx(void) |
171 | 0 | { |
172 | 0 | long options; |
173 | 0 | SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method()); |
174 | 0 | options = SSL_CTX_get_options(ctx) | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; |
175 | 0 | #ifdef SSL_OP_NO_RENEGOTIATION |
176 | | /* introduced in openssl 1.1.0h */ |
177 | 0 | options |= SSL_OP_NO_RENEGOTIATION; |
178 | 0 | #endif |
179 | 0 | SSL_CTX_set_options(ctx, options); |
180 | 0 | SSL_CTX_set_session_id_context(ctx, H2O_SESSID_CTX, H2O_SESSID_CTX_LEN); |
181 | 0 | SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE); |
182 | 0 | SSL_CTX_sess_set_new_cb(ctx, h2o_socket_ssl_new_session_cb); |
183 | 0 | return ctx; |
184 | 0 | } |
185 | | |
186 | | static h2o_cache_t *create_ssl_session_cache(size_t capacity, uint64_t duration) |
187 | 0 | { |
188 | 0 | return h2o_cache_create(H2O_CACHE_FLAG_MULTITHREADED, capacity, duration, h2o_socket_ssl_destroy_session_cache_entry); |
189 | 0 | } |
190 | | |
191 | | static void update_ssl_ctx(SSL_CTX **ctx, X509_STORE *cert_store, int verify_mode, h2o_cache_t **session_cache) |
192 | 0 | { |
193 | 0 | assert(*ctx != NULL); |
194 | | |
195 | | /* inherit the properties that weren't specified */ |
196 | 0 | if (cert_store == NULL) |
197 | 0 | cert_store = SSL_CTX_get_cert_store(*ctx); |
198 | 0 | X509_STORE_up_ref(cert_store); |
199 | 0 | if (verify_mode == -1) |
200 | 0 | verify_mode = SSL_CTX_get_verify_mode(*ctx); |
201 | 0 | h2o_cache_t *new_session_cache; |
202 | 0 | if (session_cache == NULL) { |
203 | 0 | h2o_cache_t *current = h2o_socket_ssl_get_session_cache(*ctx); |
204 | 0 | new_session_cache = |
205 | 0 | current == NULL ? NULL : create_ssl_session_cache(h2o_cache_get_capacity(current), h2o_cache_get_duration(current)); |
206 | 0 | } else { |
207 | 0 | new_session_cache = *session_cache; |
208 | 0 | } |
209 | | |
210 | | /* free the existing context */ |
211 | 0 | if (*ctx != NULL) |
212 | 0 | SSL_CTX_free(*ctx); |
213 | | |
214 | | /* create new ctx */ |
215 | 0 | *ctx = create_ssl_ctx(); |
216 | 0 | SSL_CTX_set_session_id_context(*ctx, H2O_SESSID_CTX, H2O_SESSID_CTX_LEN); |
217 | 0 | SSL_CTX_set_cert_store(*ctx, cert_store); |
218 | 0 | SSL_CTX_set_verify(*ctx, verify_mode, NULL); |
219 | 0 | if (new_session_cache != NULL) |
220 | 0 | h2o_socket_ssl_set_session_cache(*ctx, new_session_cache); |
221 | 0 | } |
222 | | |
223 | | static int on_config_ssl_verify_peer(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
224 | 0 | { |
225 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
226 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
227 | 0 | if (ret == -1) |
228 | 0 | return -1; |
229 | | |
230 | 0 | update_ssl_ctx(&self->vars->ssl_ctx, NULL, ret != 0 ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, |
231 | 0 | NULL); |
232 | |
|
233 | 0 | return 0; |
234 | 0 | } |
235 | | |
236 | | static int on_config_ssl_cafile(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
237 | 0 | { |
238 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
239 | 0 | X509_STORE *store = X509_STORE_new(); |
240 | 0 | int ret = -1; |
241 | |
|
242 | 0 | if (X509_STORE_load_locations(store, node->data.scalar, NULL) == 1) { |
243 | 0 | update_ssl_ctx(&self->vars->ssl_ctx, store, -1, NULL); |
244 | 0 | ret = 0; |
245 | 0 | } else { |
246 | 0 | h2o_configurator_errprintf(cmd, node, "failed to load certificates file:%s", node->data.scalar); |
247 | 0 | ERR_print_errors_fp(stderr); |
248 | 0 | } |
249 | |
|
250 | 0 | X509_STORE_free(store); |
251 | 0 | return ret; |
252 | 0 | } |
253 | | |
254 | | static int on_config_ssl_session_cache(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
255 | 0 | { |
256 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
257 | 0 | size_t capacity = 0; |
258 | 0 | uint64_t duration = 0; |
259 | 0 | h2o_cache_t *current_cache = h2o_socket_ssl_get_session_cache(self->vars->ssl_ctx); |
260 | |
|
261 | 0 | switch (node->type) { |
262 | 0 | case YOML_TYPE_SCALAR: |
263 | 0 | if (strcasecmp(node->data.scalar, "OFF") == 0) { |
264 | 0 | if (current_cache != NULL) { |
265 | | /* set the cache NULL */ |
266 | 0 | h2o_cache_t *empty_cache = NULL; |
267 | 0 | update_ssl_ctx(&self->vars->ssl_ctx, NULL, -1, &empty_cache); |
268 | 0 | } |
269 | 0 | return 0; |
270 | 0 | } else if (strcasecmp(node->data.scalar, "ON") == 0) { |
271 | | /* use default values */ |
272 | 0 | capacity = H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_CAPACITY; |
273 | 0 | duration = H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_DURATION; |
274 | 0 | } else { |
275 | 0 | h2o_configurator_errprintf(cmd, node, "scalar argument must be either of: `OFF`, `ON`"); |
276 | 0 | return -1; |
277 | 0 | } |
278 | 0 | break; |
279 | 0 | case YOML_TYPE_MAPPING: { |
280 | 0 | yoml_t **capacity_node, **lifetime_node; |
281 | 0 | if (h2o_configurator_parse_mapping(cmd, node, "capacity:*,lifetime:*", NULL, &capacity_node, &lifetime_node) != 0) |
282 | 0 | return -1; |
283 | 0 | if (h2o_configurator_scanf(cmd, *capacity_node, "%zu", &capacity) != 0) |
284 | 0 | return -1; |
285 | 0 | if (capacity == 0) { |
286 | 0 | h2o_configurator_errprintf(cmd, *capacity_node, "capacity must be greater than zero"); |
287 | 0 | return -1; |
288 | 0 | } |
289 | 0 | unsigned lifetime = 0; |
290 | 0 | if (h2o_configurator_scanf(cmd, *lifetime_node, "%u", &lifetime) != 0) |
291 | 0 | return -1; |
292 | 0 | if (lifetime == 0) { |
293 | 0 | h2o_configurator_errprintf(cmd, *lifetime_node, "lifetime must be greater than zero"); |
294 | 0 | return -1; |
295 | 0 | } |
296 | 0 | duration = (uint64_t)lifetime * 1000; |
297 | 0 | } break; |
298 | 0 | default: |
299 | 0 | h2o_configurator_errprintf(cmd, node, "node must be a scalar or a mapping"); |
300 | 0 | return -1; |
301 | 0 | } |
302 | | |
303 | 0 | if (current_cache != NULL) { |
304 | 0 | size_t current_capacity = h2o_cache_get_capacity(current_cache); |
305 | 0 | uint64_t current_duration = h2o_cache_get_duration(current_cache); |
306 | 0 | if (capacity == current_capacity && duration == current_duration) { |
307 | | /* parameters aren't changed, so reuse it */ |
308 | 0 | return 0; |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | 0 | h2o_cache_t *new_cache = create_ssl_session_cache(capacity, duration); |
313 | 0 | update_ssl_ctx(&self->vars->ssl_ctx, NULL, -1, &new_cache); |
314 | 0 | return 0; |
315 | 0 | } |
316 | | |
317 | | static h2o_socketpool_target_t *parse_backend(h2o_configurator_command_t *cmd, yoml_t *backend) |
318 | 0 | { |
319 | 0 | yoml_t **url_node; |
320 | 0 | h2o_socketpool_target_conf_t lb_per_target_conf = {0}; /* default weight of each target */ |
321 | |
|
322 | 0 | switch (backend->type) { |
323 | 0 | case YOML_TYPE_SCALAR: |
324 | 0 | url_node = &backend; |
325 | 0 | break; |
326 | 0 | case YOML_TYPE_MAPPING: { |
327 | 0 | yoml_t **weight_node; |
328 | 0 | if (h2o_configurator_parse_mapping(cmd, backend, "url:s", "weight:*", &url_node, &weight_node) != 0) |
329 | 0 | return NULL; |
330 | 0 | if (weight_node != NULL) { |
331 | 0 | unsigned weight; |
332 | 0 | if (h2o_configurator_scanf(cmd, *weight_node, "%u", &weight) != 0) |
333 | 0 | return NULL; |
334 | 0 | if (!(1 <= weight && weight <= H2O_SOCKETPOOL_TARGET_MAX_WEIGHT)) { |
335 | 0 | h2o_configurator_errprintf(cmd, *weight_node, "weight must be an integer in range 1 - 256"); |
336 | 0 | return NULL; |
337 | 0 | } |
338 | 0 | lb_per_target_conf.weight_m1 = weight - 1; |
339 | 0 | } |
340 | 0 | } break; |
341 | 0 | default: |
342 | 0 | h2o_configurator_errprintf(cmd, backend, |
343 | 0 | "items of arguments passed to proxy.reverse.url must be either a scalar or a mapping"); |
344 | 0 | return NULL; |
345 | 0 | } |
346 | | |
347 | 0 | h2o_url_t url; |
348 | 0 | if (h2o_url_parse((*url_node)->data.scalar, SIZE_MAX, &url) != 0) { |
349 | 0 | h2o_configurator_errprintf(cmd, *url_node, "failed to parse URL: %s\n", (*url_node)->data.scalar); |
350 | 0 | return NULL; |
351 | 0 | } |
352 | 0 | return h2o_socketpool_create_target(&url, &lb_per_target_conf); |
353 | 0 | } |
354 | | |
355 | | static int on_config_reverse_url(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
356 | 0 | { |
357 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
358 | |
|
359 | 0 | yoml_t **backends, **balancer_conf = NULL; |
360 | 0 | size_t i, num_backends = 0; |
361 | 0 | h2o_balancer_t *balancer = NULL; |
362 | | |
363 | | /* collect the nodes */ |
364 | 0 | switch (node->type) { |
365 | 0 | case YOML_TYPE_SCALAR: |
366 | 0 | backends = &node; |
367 | 0 | num_backends = 1; |
368 | 0 | break; |
369 | 0 | case YOML_TYPE_SEQUENCE: |
370 | 0 | backends = node->data.sequence.elements; |
371 | 0 | num_backends = node->data.sequence.size; |
372 | 0 | break; |
373 | 0 | case YOML_TYPE_MAPPING: |
374 | 0 | if (h2o_configurator_parse_mapping(cmd, node, "backends:*", "balancer:s", &backends, &balancer_conf) != 0) |
375 | 0 | return -1; |
376 | 0 | switch ((*backends)->type) { |
377 | 0 | case YOML_TYPE_SCALAR: |
378 | 0 | num_backends = 1; |
379 | 0 | break; |
380 | 0 | case YOML_TYPE_SEQUENCE: |
381 | 0 | num_backends = (*backends)->data.sequence.size; |
382 | 0 | backends = (*backends)->data.sequence.elements; |
383 | 0 | break; |
384 | 0 | default: |
385 | 0 | h2o_configurator_errprintf(cmd, *backends, "value for the `backends` property must be either a scalar or a sequence"); |
386 | 0 | return -1; |
387 | 0 | } |
388 | 0 | break; |
389 | 0 | default: |
390 | 0 | h2o_fatal("unexpected node type"); |
391 | 0 | return -1; |
392 | 0 | } |
393 | 0 | if (num_backends == 0) { |
394 | 0 | h2o_configurator_errprintf(cmd, node, "at least one backend url must be set"); |
395 | 0 | return -1; |
396 | 0 | } |
397 | | |
398 | | /* determine the balancer */ |
399 | 0 | if (balancer_conf != NULL) { |
400 | 0 | if (strcmp((*balancer_conf)->data.scalar, "round-robin") == 0) { |
401 | 0 | balancer = h2o_balancer_create_rr(); |
402 | 0 | } else if (strcmp((*balancer_conf)->data.scalar, "least-conn") == 0) { |
403 | 0 | balancer = h2o_balancer_create_lc(); |
404 | 0 | } else { |
405 | 0 | h2o_configurator_errprintf( |
406 | 0 | cmd, node, "specified balancer is not supported. Currently supported ones are: round-robin, least-conn"); |
407 | 0 | return -1; |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | | /* parse the backends */ |
412 | 0 | h2o_socketpool_target_t **targets = alloca(sizeof(*targets) * num_backends); |
413 | 0 | for (i = 0; i != num_backends; ++i) |
414 | 0 | if ((targets[i] = parse_backend(cmd, backends[i])) == NULL) |
415 | 0 | return -1; |
416 | | |
417 | | /* check consistency */ |
418 | 0 | if (self->vars->conf.keepalive_timeout != 0 && self->vars->conf.use_proxy_protocol) { |
419 | 0 | h2o_configurator_errprintf(cmd, node, |
420 | 0 | "please either set `proxy.use-proxy-protocol` to `OFF` or disable keep-alive by " |
421 | 0 | "setting `proxy.timeout.keepalive` to zero; the features are mutually exclusive"); |
422 | 0 | return -1; |
423 | 0 | } |
424 | 0 | if (self->vars->conf.protocol_ratio.http2 + self->vars->conf.protocol_ratio.http3 > 100) { |
425 | 0 | h2o_configurator_errprintf(cmd, node, "sum of http2.ratio and http3.ratio cannot be greater than 100"); |
426 | 0 | return -1; |
427 | 0 | } |
428 | 0 | if (self->vars->conf.http2.force_cleartext && self->vars->conf.protocol_ratio.http2 != 100) { |
429 | 0 | h2o_configurator_errprintf( |
430 | 0 | cmd, node, "when `proxy.http2.force-cleartext` is `ON`, `proxy.http2.ratio` must be set to `100` (percent)"); |
431 | 0 | return -1; |
432 | 0 | } |
433 | | |
434 | 0 | if (self->vars->conf.headers_cmds != NULL) |
435 | 0 | h2o_mem_addref_shared(self->vars->conf.headers_cmds); |
436 | |
|
437 | 0 | h2o_socketpool_t *sockpool = h2o_mem_alloc(sizeof(*sockpool)); |
438 | 0 | memset(sockpool, 0, sizeof(*sockpool)); |
439 | | /* init socket pool */ |
440 | 0 | h2o_socketpool_init_specific(sockpool, SIZE_MAX /* FIXME */, targets, num_backends, balancer); |
441 | 0 | h2o_socketpool_set_timeout(sockpool, self->vars->conf.keepalive_timeout); |
442 | 0 | h2o_socketpool_set_ssl_ctx(sockpool, self->vars->ssl_ctx); |
443 | 0 | h2o_proxy_register_reverse_proxy(ctx->pathconf, &self->vars->conf, sockpool); |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | static int on_config_connect_proxy(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
448 | 0 | { |
449 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
450 | | |
451 | | /* Convert list of ACLs to internal representation; input is a sequence of: [+-]address(?::port|) */ |
452 | 0 | h2o_connect_acl_entry_t acl_entries[node->data.sequence.size]; |
453 | 0 | for (size_t i = 0; i < node->data.sequence.size; ++i) { |
454 | 0 | if (node->data.sequence.elements[i]->type != YOML_TYPE_SCALAR) { |
455 | 0 | h2o_configurator_errprintf(cmd, node->data.sequence.elements[i], "ACL entry must be a scalar"); |
456 | 0 | return -1; |
457 | 0 | } |
458 | 0 | const char *err = h2o_connect_parse_acl(acl_entries + i, node->data.sequence.elements[i]->data.scalar); |
459 | 0 | if (err != NULL) { |
460 | 0 | h2o_configurator_errprintf(cmd, node->data.sequence.elements[i], "%s", err); |
461 | 0 | return -1; |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | 0 | h2o_connect_register(ctx->pathconf, &self->vars->conf, acl_entries, node->data.sequence.size); |
466 | 0 | return 0; |
467 | 0 | } |
468 | | |
469 | | static int on_config_emit_x_forwarded_headers(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
470 | 0 | { |
471 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
472 | 0 | if (ret == -1) |
473 | 0 | return -1; |
474 | 0 | ctx->globalconf->proxy.emit_x_forwarded_headers = (int)ret; |
475 | 0 | return 0; |
476 | 0 | } |
477 | | |
478 | | static int on_config_emit_via_header(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
479 | 0 | { |
480 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
481 | 0 | if (ret == -1) |
482 | 0 | return -1; |
483 | 0 | ctx->globalconf->proxy.emit_via_header = (int)ret; |
484 | 0 | return 0; |
485 | 0 | } |
486 | | |
487 | | static int on_config_emit_missing_date_header(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
488 | 0 | { |
489 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
490 | 0 | if (ret == -1) |
491 | 0 | return -1; |
492 | 0 | ctx->globalconf->proxy.emit_missing_date_header = (int)ret; |
493 | 0 | return 0; |
494 | 0 | } |
495 | | |
496 | | static int on_config_zerocopy(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
497 | 0 | { |
498 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON,ALWAYS"); |
499 | 0 | switch (ret) { |
500 | 0 | case 0: |
501 | 0 | ctx->globalconf->proxy.zerocopy = H2O_PROXY_ZEROCOPY_DISABLED; |
502 | 0 | break; |
503 | 0 | case 1: |
504 | 0 | ctx->globalconf->proxy.zerocopy = H2O_PROXY_ZEROCOPY_ENABLED; |
505 | 0 | break; |
506 | 0 | case 2: |
507 | 0 | ctx->globalconf->proxy.zerocopy = H2O_PROXY_ZEROCOPY_ALWAYS; |
508 | 0 | break; |
509 | 0 | default: |
510 | 0 | return -1; |
511 | 0 | } |
512 | 0 | return 0; |
513 | 0 | } |
514 | | |
515 | | static int on_config_preserve_x_forwarded_proto(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
516 | 0 | { |
517 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
518 | 0 | if (ret == -1) |
519 | 0 | return -1; |
520 | 0 | ctx->globalconf->proxy.preserve_x_forwarded_proto = (int)ret; |
521 | 0 | return 0; |
522 | 0 | } |
523 | | |
524 | | static int on_config_max_buffer_size(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
525 | 0 | { |
526 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
527 | 0 | if (h2o_configurator_scanf(cmd, node, "%zu", &self->vars->conf.max_buffer_size) != 0) |
528 | 0 | return -1; |
529 | 0 | if (self->vars->conf.max_buffer_size == 0) { |
530 | 0 | h2o_configurator_errprintf(cmd, node, "proxy.buffer_size must be a positive value"); |
531 | 0 | return -1; |
532 | 0 | } |
533 | 0 | return 0; |
534 | 0 | } |
535 | | |
536 | | static int on_config_http2_max_concurrent_streams(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
537 | 0 | { |
538 | 0 | warn_deprecation(cmd, node, "proxy.http2.max-concurrent-streams", " (notice `-` and `_`)"); |
539 | |
|
540 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
541 | 0 | return h2o_configurator_scanf(cmd, node, "%u", &self->vars->conf.http2.max_concurrent_streams); |
542 | 0 | } |
543 | | |
544 | | static int on_config_http2_force_cleartext(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
545 | 0 | { |
546 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
547 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
548 | 0 | if (ret < 0) |
549 | 0 | return -1; |
550 | 0 | self->vars->conf.http2.force_cleartext = (unsigned)ret; |
551 | 0 | return 0; |
552 | 0 | } |
553 | | |
554 | | static int on_config_http2_ratio(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
555 | 0 | { |
556 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
557 | 0 | int ret = h2o_configurator_scanf(cmd, node, "%" SCNd8, &self->vars->conf.protocol_ratio.http2); |
558 | 0 | if (ret < 0) |
559 | 0 | return ret; |
560 | 0 | if (self->vars->conf.protocol_ratio.http2 < 0 || 100 < self->vars->conf.protocol_ratio.http2) { |
561 | 0 | h2o_configurator_errprintf(cmd, node, "proxy.http2.ratio must be between 0 and 100"); |
562 | 0 | return -1; |
563 | 0 | } |
564 | 0 | return 0; |
565 | 0 | } |
566 | | |
567 | | static int on_config_http3_ratio(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
568 | 0 | { |
569 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
570 | 0 | int ret = h2o_configurator_scanf(cmd, node, "%" SCNd8, &self->vars->conf.protocol_ratio.http3); |
571 | 0 | if (ret < 0) |
572 | 0 | return ret; |
573 | 0 | if (self->vars->conf.protocol_ratio.http3 < 0 || 100 < self->vars->conf.protocol_ratio.http3) { |
574 | 0 | h2o_configurator_errprintf(cmd, node, "proxy.http3.ratio must be between 0 and 100"); |
575 | 0 | return -1; |
576 | 0 | } |
577 | 0 | return 0; |
578 | 0 | } |
579 | | |
580 | | static int on_config_forward_close_connection(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) |
581 | 0 | { |
582 | 0 | struct proxy_configurator_t *self = (void *)cmd->configurator; |
583 | 0 | ssize_t ret = h2o_configurator_get_one_of(cmd, node, "OFF,ON"); |
584 | 0 | if (ret == -1) |
585 | 0 | return -1; |
586 | 0 | self->vars->conf.forward_close_connection = (int)ret; |
587 | 0 | return 0; |
588 | 0 | } |
589 | | |
590 | | static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) |
591 | 0 | { |
592 | 0 | struct proxy_configurator_t *self = (void *)_self; |
593 | |
|
594 | 0 | memcpy(self->vars + 1, self->vars, sizeof(*self->vars)); |
595 | 0 | if (self->vars[1].conf.headers_cmds != NULL) |
596 | 0 | h2o_mem_addref_shared(self->vars[1].conf.headers_cmds); |
597 | 0 | ++self->vars; |
598 | 0 | self->connect_timeout_set = 0; |
599 | 0 | self->first_byte_timeout_set = 0; |
600 | |
|
601 | 0 | if (ctx->pathconf == NULL && ctx->hostconf == NULL) { |
602 | | /* is global conf, setup the default SSL context */ |
603 | 0 | self->vars->ssl_ctx = create_ssl_ctx(); |
604 | 0 | char *ca_bundle = h2o_configurator_get_cmd_path("share/h2o/ca-bundle.crt"); |
605 | 0 | if (SSL_CTX_load_verify_locations(self->vars->ssl_ctx, ca_bundle, NULL) != 1) |
606 | 0 | h2o_error_printf("Warning: failed to load the default certificates file at %s. Proxying to HTTPS servers may fail.\n", |
607 | 0 | ca_bundle); |
608 | 0 | free(ca_bundle); |
609 | 0 | SSL_CTX_set_verify(self->vars->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); |
610 | 0 | h2o_cache_t *ssl_session_cache = |
611 | 0 | create_ssl_session_cache(H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_CAPACITY, H2O_DEFAULT_PROXY_SSL_SESSION_CACHE_DURATION); |
612 | 0 | h2o_socket_ssl_set_session_cache(self->vars->ssl_ctx, ssl_session_cache); |
613 | 0 | } else { |
614 | 0 | SSL_CTX_up_ref(self->vars->ssl_ctx); |
615 | 0 | } |
616 | |
|
617 | 0 | return 0; |
618 | 0 | } |
619 | | |
620 | | static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node) |
621 | 0 | { |
622 | 0 | struct proxy_configurator_t *self = (void *)_self; |
623 | |
|
624 | 0 | if (ctx->pathconf == NULL && ctx->hostconf == NULL) { |
625 | | /* is global conf */ |
626 | 0 | ctx->globalconf->proxy.io_timeout = self->vars->conf.io_timeout; |
627 | 0 | ctx->globalconf->proxy.connect_timeout = self->vars->conf.connect_timeout; |
628 | 0 | ctx->globalconf->proxy.first_byte_timeout = self->vars->conf.first_byte_timeout; |
629 | 0 | ctx->globalconf->proxy.keepalive_timeout = self->vars->conf.keepalive_timeout; |
630 | 0 | ctx->globalconf->proxy.max_buffer_size = self->vars->conf.max_buffer_size; |
631 | 0 | ctx->globalconf->proxy.http2.max_concurrent_streams = self->vars->conf.http2.max_concurrent_streams; |
632 | 0 | ctx->globalconf->proxy.protocol_ratio.http2 = self->vars->conf.protocol_ratio.http2; |
633 | 0 | ctx->globalconf->proxy.protocol_ratio.http3 = self->vars->conf.protocol_ratio.http3; |
634 | 0 | h2o_socketpool_set_ssl_ctx(&ctx->globalconf->proxy.global_socketpool, self->vars->ssl_ctx); |
635 | 0 | h2o_socketpool_set_timeout(&ctx->globalconf->proxy.global_socketpool, self->vars->conf.keepalive_timeout); |
636 | 0 | } |
637 | 0 | SSL_CTX_free(self->vars->ssl_ctx); |
638 | |
|
639 | 0 | if (self->vars->conf.headers_cmds != NULL) |
640 | 0 | h2o_mem_release_shared(self->vars->conf.headers_cmds); |
641 | |
|
642 | 0 | --self->vars; |
643 | 0 | return 0; |
644 | 0 | } |
645 | | |
646 | | static h2o_headers_command_t **get_headers_commands(h2o_configurator_t *_self) |
647 | 0 | { |
648 | 0 | struct proxy_configurator_t *self = (void *)_self; |
649 | 0 | return &self->vars->conf.headers_cmds; |
650 | 0 | } |
651 | | |
652 | | void h2o_proxy_register_configurator(h2o_globalconf_t *conf) |
653 | 0 | { |
654 | 0 | struct proxy_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c)); |
655 | | |
656 | | /* set default vars */ |
657 | 0 | c->vars = c->_vars_stack; |
658 | 0 | c->vars->conf = (h2o_proxy_config_vars_t){ |
659 | 0 | .io_timeout = H2O_DEFAULT_PROXY_IO_TIMEOUT, |
660 | 0 | .connect_timeout = H2O_DEFAULT_PROXY_IO_TIMEOUT, |
661 | 0 | .first_byte_timeout = H2O_DEFAULT_PROXY_IO_TIMEOUT, |
662 | 0 | .happy_eyeballs = |
663 | 0 | { |
664 | 0 | .name_resolution_delay = H2O_DEFAULT_HAPPY_EYEBALLS_NAME_RESOLUTION_DELAY, |
665 | 0 | .connection_attempt_delay = H2O_DEFAULT_HAPPY_EYEBALLS_CONNECTION_ATTEMPT_DELAY, |
666 | 0 | }, |
667 | 0 | .tunnel_enabled = 0, /* experimental support for tunneling (e.g., CONNECT, websocket) is disabled by default */ |
668 | 0 | .max_buffer_size = SIZE_MAX, |
669 | 0 | .http2.max_concurrent_streams = H2O_DEFAULT_PROXY_HTTP2_MAX_CONCURRENT_STREAMS, |
670 | 0 | .protocol_ratio.http2 = -1, |
671 | 0 | .keepalive_timeout = h2o_socketpool_get_timeout(&conf->proxy.global_socketpool), |
672 | 0 | }; |
673 | | |
674 | | /* setup handlers */ |
675 | 0 | c->super.enter = on_config_enter; |
676 | 0 | c->super.exit = on_config_exit; |
677 | 0 | h2o_configurator_define_command(&c->super, "proxy.reverse.url", |
678 | 0 | H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | |
679 | 0 | H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE | H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING | |
680 | 0 | H2O_CONFIGURATOR_FLAG_DEFERRED, |
681 | 0 | on_config_reverse_url); |
682 | 0 | h2o_configurator_define_command(&c->super, "proxy.connect", |
683 | 0 | H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE | |
684 | 0 | H2O_CONFIGURATOR_FLAG_DEFERRED, |
685 | 0 | on_config_connect_proxy); |
686 | 0 | h2o_configurator_define_command(&c->super, "proxy.connect.emit-proxy-status", |
687 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
688 | 0 | on_config_connect_proxy_status); |
689 | 0 | h2o_configurator_define_command(&c->super, "proxy.connect.proxy-status", |
690 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
691 | 0 | on_config_connect_proxy_status); |
692 | 0 | h2o_configurator_define_command(&c->super, "proxy.proxy-status.identity", |
693 | 0 | H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
694 | 0 | on_config_proxy_status_identity); |
695 | 0 | h2o_configurator_define_command(&c->super, "proxy-status.identity", |
696 | 0 | H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
697 | 0 | on_config_proxy_status_identity); |
698 | 0 | h2o_configurator_define_command(&c->super, "proxy.preserve-host", |
699 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
700 | 0 | on_config_preserve_host); |
701 | 0 | h2o_configurator_define_command(&c->super, "proxy.proxy-protocol", |
702 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
703 | 0 | on_config_proxy_protocol); |
704 | 0 | h2o_configurator_define_command(&c->super, "proxy.timeout.io", |
705 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_timeout_io); |
706 | 0 | h2o_configurator_define_command(&c->super, "proxy.timeout.connect", |
707 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
708 | 0 | on_config_timeout_connect); |
709 | 0 | h2o_configurator_define_command(&c->super, "proxy.timeout.first_byte", |
710 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
711 | 0 | on_config_timeout_first_byte); |
712 | 0 | h2o_configurator_define_command(&c->super, "proxy.timeout.keepalive", |
713 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
714 | 0 | on_config_timeout_keepalive); |
715 | 0 | h2o_configurator_define_command(&c->super, "proxy.happy-eyeballs.name-resolution-delay", |
716 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
717 | 0 | on_config_happy_eyeballs_name_resolution_delay); |
718 | 0 | h2o_configurator_define_command(&c->super, "proxy.happy-eyeballs.connection-attempt-delay", |
719 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
720 | 0 | on_config_happy_eyeballs_connection_attempt_delay); |
721 | 0 | h2o_configurator_define_command(&c->super, "proxy.tunnel", |
722 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_tunnel); |
723 | 0 | h2o_configurator_define_command(&c->super, "proxy.ssl.verify-peer", |
724 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
725 | 0 | on_config_ssl_verify_peer); |
726 | 0 | h2o_configurator_define_command(&c->super, "proxy.ssl.cafile", |
727 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_ssl_cafile); |
728 | 0 | h2o_configurator_define_command(&c->super, "proxy.ssl.session-cache", H2O_CONFIGURATOR_FLAG_ALL_LEVELS, |
729 | 0 | on_config_ssl_session_cache); |
730 | 0 | h2o_configurator_define_command(&c->super, "proxy.preserve-x-forwarded-proto", |
731 | 0 | H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
732 | 0 | on_config_preserve_x_forwarded_proto); |
733 | 0 | h2o_configurator_define_command(&c->super, "proxy.emit-x-forwarded-headers", |
734 | 0 | H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
735 | 0 | on_config_emit_x_forwarded_headers); |
736 | 0 | h2o_configurator_define_command(&c->super, "proxy.emit-via-header", |
737 | 0 | H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_emit_via_header); |
738 | 0 | h2o_configurator_define_command(&c->super, "proxy.emit-missing-date-header", |
739 | 0 | H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
740 | 0 | on_config_emit_missing_date_header); |
741 | 0 | h2o_configurator_define_command(&c->super, "proxy.zerocopy", H2O_CONFIGURATOR_FLAG_GLOBAL | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
742 | 0 | on_config_zerocopy); |
743 | 0 | h2o_configurator_define_headers_commands(conf, &c->super, "proxy.header", get_headers_commands); |
744 | 0 | h2o_configurator_define_command(&c->super, "proxy.max-buffer-size", |
745 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
746 | 0 | on_config_max_buffer_size); |
747 | 0 | h2o_configurator_define_command(&c->super, "proxy.http2.max-concurrent-streams", |
748 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
749 | 0 | on_config_http2_max_concurrent_streams); |
750 | 0 | h2o_configurator_define_command(&c->super, "proxy.http2.max-concurrent_streams", |
751 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
752 | 0 | on_config_http2_max_concurrent_streams); |
753 | 0 | h2o_configurator_define_command(&c->super, "proxy.http2.force-cleartext", |
754 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
755 | 0 | on_config_http2_force_cleartext); |
756 | 0 | h2o_configurator_define_command(&c->super, "proxy.http2.ratio", |
757 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_http2_ratio); |
758 | 0 | h2o_configurator_define_command(&c->super, "proxy.http3.ratio", |
759 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_http3_ratio); |
760 | 0 | h2o_configurator_define_command(&c->super, "proxy.forward.close-connection", |
761 | 0 | H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, |
762 | 0 | on_config_forward_close_connection); |
763 | 0 | } |