1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
19 #include <http_core.h>
20 #include <http_config.h>
22 #include <http_vhost.h>
26 #include <apr_strings.h>
28 #include "h2_alt_svc.h"
31 #include "h2_config.h"
32 #include "h2_private.h"
36 #define H2_CONFIG_GET(a, b, n) \
37 (((a)->n == DEF_VAL)? (b) : (a))->n
39 static h2_config defconf = {
41 100, /* max_streams */
42 64 * 1024, /* window_size */
45 10 * 60, /* max workers idle secs */
46 64 * 1024, /* stream max mem size */
47 NULL, /* no alt-svcs */
48 -1, /* alt-svc max age */
49 0, /* serialize headers */
50 -1, /* h2 direct mode */
51 -1, /* # session extra files */
54 static int files_per_session = 0;
56 void h2_config_init(apr_pool_t *pool) {
57 /* Determine a good default for this platform and mpm?
58 * TODO: not sure how APR wants to hand out this piece of
63 int tx_files = max_files / 4;
66 ap_mpm_query(AP_MPMQ_MAX_THREADS, &conn_threads);
67 switch (h2_conn_mpm_type()) {
71 /* allow that many transfer open files per mplx */
72 files_per_session = (tx_files / conn_threads);
75 /* don't know anything about it, stay safe */
80 static void *h2_config_create(apr_pool_t *pool,
81 const char *prefix, const char *x)
83 h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
85 const char *s = x? x : "unknown";
86 char *name = apr_pcalloc(pool, strlen(prefix) + strlen(s) + 20);
93 conf->h2_max_streams = DEF_VAL;
94 conf->h2_window_size = DEF_VAL;
95 conf->min_workers = DEF_VAL;
96 conf->max_workers = DEF_VAL;
97 conf->max_worker_idle_secs = DEF_VAL;
98 conf->stream_max_mem_size = DEF_VAL;
99 conf->alt_svc_max_age = DEF_VAL;
100 conf->serialize_headers = DEF_VAL;
101 conf->h2_direct = DEF_VAL;
102 conf->session_extra_files = DEF_VAL;
106 void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
108 return h2_config_create(pool, "srv", s->defn_name);
111 void *h2_config_create_dir(apr_pool_t *pool, char *x)
113 return h2_config_create(pool, "dir", x);
116 void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
118 h2_config *base = (h2_config *)basev;
119 h2_config *add = (h2_config *)addv;
120 h2_config *n = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
122 char *name = apr_pcalloc(pool, 20 + strlen(add->name) + strlen(base->name));
123 strcpy(name, "merged[");
124 strcat(name, add->name);
126 strcat(name, base->name);
130 n->h2_max_streams = H2_CONFIG_GET(add, base, h2_max_streams);
131 n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size);
132 n->min_workers = H2_CONFIG_GET(add, base, min_workers);
133 n->max_workers = H2_CONFIG_GET(add, base, max_workers);
134 n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs);
135 n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size);
136 n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs;
137 n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age);
138 n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers);
139 n->h2_direct = H2_CONFIG_GET(add, base, h2_direct);
140 n->session_extra_files = H2_CONFIG_GET(add, base, session_extra_files);
145 int h2_config_geti(h2_config *conf, h2_config_var_t var)
149 case H2_CONF_MAX_STREAMS:
150 return H2_CONFIG_GET(conf, &defconf, h2_max_streams);
151 case H2_CONF_WIN_SIZE:
152 return H2_CONFIG_GET(conf, &defconf, h2_window_size);
153 case H2_CONF_MIN_WORKERS:
154 return H2_CONFIG_GET(conf, &defconf, min_workers);
155 case H2_CONF_MAX_WORKERS:
156 return H2_CONFIG_GET(conf, &defconf, max_workers);
157 case H2_CONF_MAX_WORKER_IDLE_SECS:
158 return H2_CONFIG_GET(conf, &defconf, max_worker_idle_secs);
159 case H2_CONF_STREAM_MAX_MEM:
160 return H2_CONFIG_GET(conf, &defconf, stream_max_mem_size);
161 case H2_CONF_ALT_SVC_MAX_AGE:
162 return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age);
163 case H2_CONF_SER_HEADERS:
164 return H2_CONFIG_GET(conf, &defconf, serialize_headers);
166 return H2_CONFIG_GET(conf, &defconf, h2_direct);
167 case H2_CONF_SESSION_FILES:
168 n = H2_CONFIG_GET(conf, &defconf, session_extra_files);
170 n = files_per_session;
178 h2_config *h2_config_sget(server_rec *s)
180 h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config,
182 AP_DEBUG_ASSERT(cfg);
187 static const char *h2_conf_set_max_streams(cmd_parms *parms,
188 void *arg, const char *value)
190 h2_config *cfg = h2_config_sget(parms->server);
191 cfg->h2_max_streams = (int)apr_atoi64(value);
193 if (cfg->h2_max_streams < 1) {
194 return "value must be > 0";
199 static const char *h2_conf_set_window_size(cmd_parms *parms,
200 void *arg, const char *value)
202 h2_config *cfg = h2_config_sget(parms->server);
203 cfg->h2_window_size = (int)apr_atoi64(value);
205 if (cfg->h2_window_size < 1024) {
206 return "value must be > 1k";
211 static const char *h2_conf_set_min_workers(cmd_parms *parms,
212 void *arg, const char *value)
214 h2_config *cfg = h2_config_sget(parms->server);
215 cfg->min_workers = (int)apr_atoi64(value);
217 if (cfg->min_workers < 1) {
218 return "value must be > 1";
223 static const char *h2_conf_set_max_workers(cmd_parms *parms,
224 void *arg, const char *value)
226 h2_config *cfg = h2_config_sget(parms->server);
227 cfg->max_workers = (int)apr_atoi64(value);
229 if (cfg->max_workers < 1) {
230 return "value must be > 1";
235 static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
236 void *arg, const char *value)
238 h2_config *cfg = h2_config_sget(parms->server);
239 cfg->max_worker_idle_secs = (int)apr_atoi64(value);
241 if (cfg->max_worker_idle_secs < 1) {
242 return "value must be > 1";
247 static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms,
248 void *arg, const char *value)
250 h2_config *cfg = h2_config_sget(parms->server);
253 cfg->stream_max_mem_size = (int)apr_atoi64(value);
255 if (cfg->stream_max_mem_size < 1024) {
256 return "value must be > 1k";
261 static const char *h2_add_alt_svc(cmd_parms *parms,
262 void *arg, const char *value)
264 if (value && strlen(value)) {
265 h2_config *cfg = h2_config_sget(parms->server);
266 h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool);
268 return "unable to parse alt-svc specifier";
270 if (!cfg->alt_svcs) {
271 cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*));
273 APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
279 static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
280 void *arg, const char *value)
282 h2_config *cfg = h2_config_sget(parms->server);
283 cfg->alt_svc_max_age = (int)apr_atoi64(value);
288 static const char *h2_conf_set_session_extra_files(cmd_parms *parms,
289 void *arg, const char *value)
291 h2_config *cfg = h2_config_sget(parms->server);
292 apr_int64_t max = (int)apr_atoi64(value);
294 return "value must be a positive number";
296 cfg->session_extra_files = (int)max;
301 static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
302 void *arg, const char *value)
304 h2_config *cfg = h2_config_sget(parms->server);
305 if (!strcasecmp(value, "On")) {
306 cfg->serialize_headers = 1;
309 else if (!strcasecmp(value, "Off")) {
310 cfg->serialize_headers = 0;
315 return "value must be On or Off";
318 static const char *h2_conf_set_direct(cmd_parms *parms,
319 void *arg, const char *value)
321 h2_config *cfg = h2_config_sget(parms->server);
322 if (!strcasecmp(value, "On")) {
326 else if (!strcasecmp(value, "Off")) {
332 return "value must be On or Off";
335 #define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
338 const command_rec h2_cmds[] = {
339 AP_INIT_TAKE1("H2MaxSessionStreams", h2_conf_set_max_streams, NULL,
340 RSRC_CONF, "maximum number of open streams per session"),
341 AP_INIT_TAKE1("H2WindowSize", h2_conf_set_window_size, NULL,
342 RSRC_CONF, "window size on client DATA"),
343 AP_INIT_TAKE1("H2MinWorkers", h2_conf_set_min_workers, NULL,
344 RSRC_CONF, "minimum number of worker threads per child"),
345 AP_INIT_TAKE1("H2MaxWorkers", h2_conf_set_max_workers, NULL,
346 RSRC_CONF, "maximum number of worker threads per child"),
347 AP_INIT_TAKE1("H2MaxWorkerIdleSeconds", h2_conf_set_max_worker_idle_secs, NULL,
348 RSRC_CONF, "maximum number of idle seconds before a worker shuts down"),
349 AP_INIT_TAKE1("H2StreamMaxMemSize", h2_conf_set_stream_max_mem_size, NULL,
350 RSRC_CONF, "maximum number of bytes buffered in memory for a stream"),
351 AP_INIT_TAKE1("H2AltSvc", h2_add_alt_svc, NULL,
352 RSRC_CONF, "adds an Alt-Svc for this server"),
353 AP_INIT_TAKE1("H2AltSvcMaxAge", h2_conf_set_alt_svc_max_age, NULL,
354 RSRC_CONF, "set the maximum age (in seconds) that client can rely on alt-svc information"),
355 AP_INIT_TAKE1("H2SerializeHeaders", h2_conf_set_serialize_headers, NULL,
356 RSRC_CONF, "on to enable header serialization for compatibility"),
357 AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL,
358 RSRC_CONF, "on to enable direct HTTP/2 mode"),
359 AP_INIT_TAKE1("H2SessionExtraFiles", h2_conf_set_session_extra_files, NULL,
360 RSRC_CONF, "number of extra file a session might keep open"),
365 h2_config *h2_config_rget(request_rec *r)
367 h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config,
369 return cfg? cfg : h2_config_sget(r->server);
372 h2_config *h2_config_get(conn_rec *c)
374 h2_ctx *ctx = h2_ctx_get(c);
378 if (!ctx->server && ctx->hostname) {
379 /* We have a host agreed upon via TLS SNI, but no request yet.
380 * The sni host was accepted and therefore does match a server record
381 * (vhost) for it. But we need to know which one.
382 * Normally, it is enough to be set on the initial request on a
383 * connection, but we need it earlier. Simulate a request and call
384 * the vhost matching stuff.
388 memset(&uri, 0, sizeof(uri));
389 uri.scheme = (char*)"https";
390 uri.hostinfo = (char*)ctx->hostname;
391 uri.hostname = (char*)ctx->hostname;
392 uri.port_str = (char*)"";
393 uri.port = c->local_addr->port;
394 uri.path = (char*)"/";
396 memset(&r, 0, sizeof(r));
400 r.hostname = ctx->hostname;
401 r.headers_in = apr_table_make(c->pool, 1);
404 r.server = r.connection->base_server;
405 ap_update_vhost_from_headers(&r);
406 ctx->server = r.server;
410 ctx->config = h2_config_sget(ctx->server);
414 return h2_config_sget(c->base_server);