]> granicus.if.org Git - apache/blob - modules/http2/h2_config.c
merged latest changes in 2.4.x
[apache] / modules / http2 / h2_config.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
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
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
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.
14  */
15
16 #include <assert.h>
17
18 #include <httpd.h>
19 #include <http_core.h>
20 #include <http_config.h>
21 #include <http_log.h>
22 #include <http_vhost.h>
23
24 #include <ap_mpm.h>
25
26 #include <apr_strings.h>
27
28 #include "h2_alt_svc.h"
29 #include "h2_ctx.h"
30 #include "h2_conn.h"
31 #include "h2_config.h"
32 #include "h2_private.h"
33
34 #define DEF_VAL     (-1)
35
36 #define H2_CONFIG_GET(a, b, n) \
37     (((a)->n == DEF_VAL)? (b) : (a))->n
38
39 static h2_config defconf = {
40     "default",
41     100,              /* max_streams */
42     64 * 1024,        /* window_size */
43     -1,               /* min workers */
44     -1,               /* max workers */
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 */
52 };
53
54 static int files_per_session = 0;
55
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 
59      * information.
60      */
61     int max_files = 256;
62     int conn_threads = 1;
63     int tx_files = max_files / 4;
64     
65     (void)pool;
66     ap_mpm_query(AP_MPMQ_MAX_THREADS, &conn_threads);
67     switch (h2_conn_mpm_type()) {
68         case H2_MPM_PREFORK:
69         case H2_MPM_WORKER:
70         case H2_MPM_EVENT:
71             /* allow that many transfer open files per mplx */
72             files_per_session = (tx_files / conn_threads);
73             break;
74         default:
75             /* don't know anything about it, stay safe */
76             break;
77     }
78 }
79
80 static void *h2_config_create(apr_pool_t *pool,
81                               const char *prefix, const char *x)
82 {
83     h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
84     
85     const char *s = x? x : "unknown";
86     char *name = apr_pcalloc(pool, strlen(prefix) + strlen(s) + 20);
87     strcpy(name, prefix);
88     strcat(name, "[");
89     strcat(name, s);
90     strcat(name, "]");
91     
92     conf->name                 = name;
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;
103     return conf;
104 }
105
106 void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
107 {
108     return h2_config_create(pool, "srv", s->defn_name);
109 }
110
111 void *h2_config_create_dir(apr_pool_t *pool, char *x)
112 {
113     return h2_config_create(pool, "dir", x);
114 }
115
116 void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
117 {
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));
121
122     char *name = apr_pcalloc(pool, 20 + strlen(add->name) + strlen(base->name));
123     strcpy(name, "merged[");
124     strcat(name, add->name);
125     strcat(name, ", ");
126     strcat(name, base->name);
127     strcat(name, "]");
128     n->name = name;
129
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);
141     
142     return n;
143 }
144
145 int h2_config_geti(h2_config *conf, h2_config_var_t var)
146 {
147     int n;
148     switch(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);
165         case H2_CONF_DIRECT:
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);
169             if (n < 0) {
170                 n = files_per_session;
171             }
172             return n;
173         default:
174             return DEF_VAL;
175     }
176 }
177
178 h2_config *h2_config_sget(server_rec *s)
179 {
180     h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, 
181                                                        &h2_module);
182     AP_DEBUG_ASSERT(cfg);
183     return cfg;
184 }
185
186
187 static const char *h2_conf_set_max_streams(cmd_parms *parms,
188                                            void *arg, const char *value)
189 {
190     h2_config *cfg = h2_config_sget(parms->server);
191     cfg->h2_max_streams = (int)apr_atoi64(value);
192     (void)arg;
193     if (cfg->h2_max_streams < 1) {
194         return "value must be > 0";
195     }
196     return NULL;
197 }
198
199 static const char *h2_conf_set_window_size(cmd_parms *parms,
200                                            void *arg, const char *value)
201 {
202     h2_config *cfg = h2_config_sget(parms->server);
203     cfg->h2_window_size = (int)apr_atoi64(value);
204     (void)arg;
205     if (cfg->h2_window_size < 1024) {
206         return "value must be > 1k";
207     }
208     return NULL;
209 }
210
211 static const char *h2_conf_set_min_workers(cmd_parms *parms,
212                                            void *arg, const char *value)
213 {
214     h2_config *cfg = h2_config_sget(parms->server);
215     cfg->min_workers = (int)apr_atoi64(value);
216     (void)arg;
217     if (cfg->min_workers < 1) {
218         return "value must be > 1";
219     }
220     return NULL;
221 }
222
223 static const char *h2_conf_set_max_workers(cmd_parms *parms,
224                                            void *arg, const char *value)
225 {
226     h2_config *cfg = h2_config_sget(parms->server);
227     cfg->max_workers = (int)apr_atoi64(value);
228     (void)arg;
229     if (cfg->max_workers < 1) {
230         return "value must be > 1";
231     }
232     return NULL;
233 }
234
235 static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
236                                                     void *arg, const char *value)
237 {
238     h2_config *cfg = h2_config_sget(parms->server);
239     cfg->max_worker_idle_secs = (int)apr_atoi64(value);
240     (void)arg;
241     if (cfg->max_worker_idle_secs < 1) {
242         return "value must be > 1";
243     }
244     return NULL;
245 }
246
247 static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms,
248                                                    void *arg, const char *value)
249 {
250     h2_config *cfg = h2_config_sget(parms->server);
251     
252     
253     cfg->stream_max_mem_size = (int)apr_atoi64(value);
254     (void)arg;
255     if (cfg->stream_max_mem_size < 1024) {
256         return "value must be > 1k";
257     }
258     return NULL;
259 }
260
261 static const char *h2_add_alt_svc(cmd_parms *parms,
262                                   void *arg, const char *value)
263 {
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);
267         if (!as) {
268             return "unable to parse alt-svc specifier";
269         }
270         if (!cfg->alt_svcs) {
271             cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*));
272         }
273         APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
274     }
275     (void)arg;
276     return NULL;
277 }
278
279 static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
280                                                void *arg, const char *value)
281 {
282     h2_config *cfg = h2_config_sget(parms->server);
283     cfg->alt_svc_max_age = (int)apr_atoi64(value);
284     (void)arg;
285     return NULL;
286 }
287
288 static const char *h2_conf_set_session_extra_files(cmd_parms *parms,
289                                                    void *arg, const char *value)
290 {
291     h2_config *cfg = h2_config_sget(parms->server);
292     apr_int64_t max = (int)apr_atoi64(value);
293     if (max <= 0) {
294         return "value must be a positive number";
295     }
296     cfg->session_extra_files = (int)max;
297     (void)arg;
298     return NULL;
299 }
300
301 static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
302                                                  void *arg, const char *value)
303 {
304     h2_config *cfg = h2_config_sget(parms->server);
305     if (!strcasecmp(value, "On")) {
306         cfg->serialize_headers = 1;
307         return NULL;
308     }
309     else if (!strcasecmp(value, "Off")) {
310         cfg->serialize_headers = 0;
311         return NULL;
312     }
313     
314     (void)arg;
315     return "value must be On or Off";
316 }
317
318 static const char *h2_conf_set_direct(cmd_parms *parms,
319                                       void *arg, const char *value)
320 {
321     h2_config *cfg = h2_config_sget(parms->server);
322     if (!strcasecmp(value, "On")) {
323         cfg->h2_direct = 1;
324         return NULL;
325     }
326     else if (!strcasecmp(value, "Off")) {
327         cfg->h2_direct = 0;
328         return NULL;
329     }
330     
331     (void)arg;
332     return "value must be On or Off";
333 }
334
335 #define AP_END_CMD     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
336
337
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"),
361     AP_END_CMD
362 };
363
364
365 h2_config *h2_config_rget(request_rec *r)
366 {
367     h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, 
368                                                        &h2_module);
369     return cfg? cfg : h2_config_sget(r->server); 
370 }
371
372 h2_config *h2_config_get(conn_rec *c)
373 {
374     h2_ctx *ctx = h2_ctx_get(c);
375     if (ctx->config) {
376         return ctx->config;
377     }
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.
385          */
386         apr_uri_t uri;
387         request_rec r;
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*)"/";
395         
396         memset(&r, 0, sizeof(r));
397         r.uri = (char*)"/";
398         r.connection = c;
399         r.pool = c->pool;
400         r.hostname = ctx->hostname;
401         r.headers_in = apr_table_make(c->pool, 1);
402         r.parsed_uri = uri;
403         r.status = HTTP_OK;
404         r.server = r.connection->base_server;
405         ap_update_vhost_from_headers(&r);
406         ctx->server = r.server;
407     }
408     
409     if (ctx->server) {
410         ctx->config = h2_config_sget(ctx->server);
411         return ctx->config;
412     }
413     
414     return h2_config_sget(c->base_server);
415 }
416