1 
2 /*
3  * Copyright (C) Nginx, Inc.
4  * Copyright (C) Valentin V. Bartenev
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 #include <ngx_http_v2_module.h>
12 
13 
14 static ngx_int_t ngx_http_v2_add_variables(ngx_conf_t *cf);
15 
16 static ngx_int_t ngx_http_v2_variable(ngx_http_request_t *r,
17     ngx_http_variable_value_t *v, uintptr_t data);
18 
19 static ngx_int_t ngx_http_v2_module_init(ngx_cycle_t *cycle);
20 
21 static void *ngx_http_v2_create_main_conf(ngx_conf_t *cf);
22 static char *ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf);
23 static void *ngx_http_v2_create_srv_conf(ngx_conf_t *cf);
24 static char *ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent,
25     void *child);
26 static void *ngx_http_v2_create_loc_conf(ngx_conf_t *cf);
27 static char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent,
28     void *child);
29 
30 static char *ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
31 
32 static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,
33     void *data);
34 static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data);
35 static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data);
36 static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,
37     void *data);
38 static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);
39 static char *ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd,
40     void *conf);
41 
42 
43 static ngx_conf_deprecated_t  ngx_http_v2_recv_timeout_deprecated = {
44     ngx_conf_deprecated, "http2_recv_timeout", "client_header_timeout"
45 };
46 
47 static ngx_conf_deprecated_t  ngx_http_v2_idle_timeout_deprecated = {
48     ngx_conf_deprecated, "http2_idle_timeout", "keepalive_timeout"
49 };
50 
51 static ngx_conf_deprecated_t  ngx_http_v2_max_requests_deprecated = {
52     ngx_conf_deprecated, "http2_max_requests", "keepalive_requests"
53 };
54 
55 static ngx_conf_deprecated_t  ngx_http_v2_max_field_size_deprecated = {
56     ngx_conf_deprecated, "http2_max_field_size", "large_client_header_buffers"
57 };
58 
59 static ngx_conf_deprecated_t  ngx_http_v2_max_header_size_deprecated = {
60     ngx_conf_deprecated, "http2_max_header_size", "large_client_header_buffers"
61 };
62 
63 
64 static ngx_conf_post_t  ngx_http_v2_recv_buffer_size_post =
65     { ngx_http_v2_recv_buffer_size };
66 static ngx_conf_post_t  ngx_http_v2_pool_size_post =
67     { ngx_http_v2_pool_size };
68 static ngx_conf_post_t  ngx_http_v2_preread_size_post =
69     { ngx_http_v2_preread_size };
70 static ngx_conf_post_t  ngx_http_v2_streams_index_mask_post =
71     { ngx_http_v2_streams_index_mask };
72 static ngx_conf_post_t  ngx_http_v2_chunk_size_post =
73     { ngx_http_v2_chunk_size };
74 
75 
76 static ngx_command_t  ngx_http_v2_commands[] = {
77 
78     { ngx_string("http2_recv_buffer_size"),
79       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
80       ngx_conf_set_size_slot,
81       NGX_HTTP_MAIN_CONF_OFFSET,
82       offsetof(ngx_http_v2_main_conf_t, recv_buffer_size),
83       &ngx_http_v2_recv_buffer_size_post },
84 
85     { ngx_string("http2_pool_size"),
86       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
87       ngx_conf_set_size_slot,
88       NGX_HTTP_SRV_CONF_OFFSET,
89       offsetof(ngx_http_v2_srv_conf_t, pool_size),
90       &ngx_http_v2_pool_size_post },
91 
92     { ngx_string("http2_max_concurrent_streams"),
93       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
94       ngx_conf_set_num_slot,
95       NGX_HTTP_SRV_CONF_OFFSET,
96       offsetof(ngx_http_v2_srv_conf_t, concurrent_streams),
97       NULL },
98 
99     { ngx_string("http2_max_concurrent_pushes"),
100       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
101       ngx_conf_set_num_slot,
102       NGX_HTTP_SRV_CONF_OFFSET,
103       offsetof(ngx_http_v2_srv_conf_t, concurrent_pushes),
104       NULL },
105 
106     { ngx_string("http2_max_requests"),
107       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
108       ngx_http_v2_obsolete,
109       0,
110       0,
111       &ngx_http_v2_max_requests_deprecated },
112 
113     { ngx_string("http2_max_field_size"),
114       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
115       ngx_http_v2_obsolete,
116       0,
117       0,
118       &ngx_http_v2_max_field_size_deprecated },
119 
120     { ngx_string("http2_max_header_size"),
121       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
122       ngx_http_v2_obsolete,
123       0,
124       0,
125       &ngx_http_v2_max_header_size_deprecated },
126 
127     { ngx_string("http2_body_preread_size"),
128       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
129       ngx_conf_set_size_slot,
130       NGX_HTTP_SRV_CONF_OFFSET,
131       offsetof(ngx_http_v2_srv_conf_t, preread_size),
132       &ngx_http_v2_preread_size_post },
133 
134     { ngx_string("http2_streams_index_size"),
135       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
136       ngx_conf_set_num_slot,
137       NGX_HTTP_SRV_CONF_OFFSET,
138       offsetof(ngx_http_v2_srv_conf_t, streams_index_mask),
139       &ngx_http_v2_streams_index_mask_post },
140 
141     { ngx_string("http2_recv_timeout"),
142       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
143       ngx_http_v2_obsolete,
144       0,
145       0,
146       &ngx_http_v2_recv_timeout_deprecated },
147 
148     { ngx_string("http2_idle_timeout"),
149       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
150       ngx_http_v2_obsolete,
151       0,
152       0,
153       &ngx_http_v2_idle_timeout_deprecated },
154 
155     { ngx_string("http2_chunk_size"),
156       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
157       ngx_conf_set_size_slot,
158       NGX_HTTP_LOC_CONF_OFFSET,
159       offsetof(ngx_http_v2_loc_conf_t, chunk_size),
160       &ngx_http_v2_chunk_size_post },
161 
162     { ngx_string("http2_push_preload"),
163       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
164       ngx_conf_set_flag_slot,
165       NGX_HTTP_LOC_CONF_OFFSET,
166       offsetof(ngx_http_v2_loc_conf_t, push_preload),
167       NULL },
168 
169     { ngx_string("http2_push"),
170       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
171       ngx_http_v2_push,
172       NGX_HTTP_LOC_CONF_OFFSET,
173       0,
174       NULL },
175 
176       ngx_null_command
177 };
178 
179 
180 static ngx_http_module_t  ngx_http_v2_module_ctx = {
181     ngx_http_v2_add_variables,             /* preconfiguration */
182     NULL,                                  /* postconfiguration */
183 
184     ngx_http_v2_create_main_conf,          /* create main configuration */
185     ngx_http_v2_init_main_conf,            /* init main configuration */
186 
187     ngx_http_v2_create_srv_conf,           /* create server configuration */
188     ngx_http_v2_merge_srv_conf,            /* merge server configuration */
189 
190     ngx_http_v2_create_loc_conf,           /* create location configuration */
191     ngx_http_v2_merge_loc_conf             /* merge location configuration */
192 };
193 
194 
195 ngx_module_t  ngx_http_v2_module = {
196     NGX_MODULE_V1,
197     &ngx_http_v2_module_ctx,               /* module context */
198     ngx_http_v2_commands,                  /* module directives */
199     NGX_HTTP_MODULE,                       /* module type */
200     NULL,                                  /* init master */
201     ngx_http_v2_module_init,               /* init module */
202     NULL,                                  /* init process */
203     NULL,                                  /* init thread */
204     NULL,                                  /* exit thread */
205     NULL,                                  /* exit process */
206     NULL,                                  /* exit master */
207     NGX_MODULE_V1_PADDING
208 };
209 
210 
211 static ngx_http_variable_t  ngx_http_v2_vars[] = {
212 
213     { ngx_string("http2"), NULL,
214       ngx_http_v2_variable, 0, 0, 0 },
215 
216       ngx_http_null_variable
217 };
218 
219 
220 static ngx_int_t
ngx_http_v2_add_variables(ngx_conf_t * cf)221 ngx_http_v2_add_variables(ngx_conf_t *cf)
222 {
223     ngx_http_variable_t  *var, *v;
224 
225     for (v = ngx_http_v2_vars; v->name.len; v++) {
226         var = ngx_http_add_variable(cf, &v->name, v->flags);
227         if (var == NULL) {
228             return NGX_ERROR;
229         }
230 
231         var->get_handler = v->get_handler;
232         var->data = v->data;
233     }
234 
235     return NGX_OK;
236 }
237 
238 
239 static ngx_int_t
ngx_http_v2_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)240 ngx_http_v2_variable(ngx_http_request_t *r,
241     ngx_http_variable_value_t *v, uintptr_t data)
242 {
243 
244     if (r->stream) {
245 #if (NGX_HTTP_SSL)
246 
247         if (r->connection->ssl) {
248             v->len = sizeof("h2") - 1;
249             v->valid = 1;
250             v->no_cacheable = 0;
251             v->not_found = 0;
252             v->data = (u_char *) "h2";
253 
254             return NGX_OK;
255         }
256 
257 #endif
258         v->len = sizeof("h2c") - 1;
259         v->valid = 1;
260         v->no_cacheable = 0;
261         v->not_found = 0;
262         v->data = (u_char *) "h2c";
263 
264         return NGX_OK;
265     }
266 
267     *v = ngx_http_variable_null_value;
268 
269     return NGX_OK;
270 }
271 
272 
273 static ngx_int_t
ngx_http_v2_module_init(ngx_cycle_t * cycle)274 ngx_http_v2_module_init(ngx_cycle_t *cycle)
275 {
276     return NGX_OK;
277 }
278 
279 
280 static void *
ngx_http_v2_create_main_conf(ngx_conf_t * cf)281 ngx_http_v2_create_main_conf(ngx_conf_t *cf)
282 {
283     ngx_http_v2_main_conf_t  *h2mcf;
284 
285     h2mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_main_conf_t));
286     if (h2mcf == NULL) {
287         return NULL;
288     }
289 
290     h2mcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;
291 
292     return h2mcf;
293 }
294 
295 
296 static char *
ngx_http_v2_init_main_conf(ngx_conf_t * cf,void * conf)297 ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf)
298 {
299     ngx_http_v2_main_conf_t *h2mcf = conf;
300 
301     ngx_conf_init_size_value(h2mcf->recv_buffer_size, 256 * 1024);
302 
303     return NGX_CONF_OK;
304 }
305 
306 
307 static void *
ngx_http_v2_create_srv_conf(ngx_conf_t * cf)308 ngx_http_v2_create_srv_conf(ngx_conf_t *cf)
309 {
310     ngx_http_v2_srv_conf_t  *h2scf;
311 
312     h2scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_srv_conf_t));
313     if (h2scf == NULL) {
314         return NULL;
315     }
316 
317     h2scf->pool_size = NGX_CONF_UNSET_SIZE;
318 
319     h2scf->concurrent_streams = NGX_CONF_UNSET_UINT;
320     h2scf->concurrent_pushes = NGX_CONF_UNSET_UINT;
321 
322     h2scf->preread_size = NGX_CONF_UNSET_SIZE;
323 
324     h2scf->streams_index_mask = NGX_CONF_UNSET_UINT;
325 
326     return h2scf;
327 }
328 
329 
330 static char *
ngx_http_v2_merge_srv_conf(ngx_conf_t * cf,void * parent,void * child)331 ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
332 {
333     ngx_http_v2_srv_conf_t *prev = parent;
334     ngx_http_v2_srv_conf_t *conf = child;
335 
336     ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
337 
338     ngx_conf_merge_uint_value(conf->concurrent_streams,
339                               prev->concurrent_streams, 128);
340     ngx_conf_merge_uint_value(conf->concurrent_pushes,
341                               prev->concurrent_pushes, 10);
342 
343     ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536);
344 
345     ngx_conf_merge_uint_value(conf->streams_index_mask,
346                               prev->streams_index_mask, 32 - 1);
347 
348     return NGX_CONF_OK;
349 }
350 
351 
352 static void *
ngx_http_v2_create_loc_conf(ngx_conf_t * cf)353 ngx_http_v2_create_loc_conf(ngx_conf_t *cf)
354 {
355     ngx_http_v2_loc_conf_t  *h2lcf;
356 
357     h2lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_loc_conf_t));
358     if (h2lcf == NULL) {
359         return NULL;
360     }
361 
362     /*
363      * set by ngx_pcalloc():
364      *
365      *     h2lcf->pushes = NULL;
366      */
367 
368     h2lcf->chunk_size = NGX_CONF_UNSET_SIZE;
369 
370     h2lcf->push_preload = NGX_CONF_UNSET;
371     h2lcf->push = NGX_CONF_UNSET;
372 
373     return h2lcf;
374 }
375 
376 
377 static char *
ngx_http_v2_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)378 ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
379 {
380     ngx_http_v2_loc_conf_t *prev = parent;
381     ngx_http_v2_loc_conf_t *conf = child;
382 
383     ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);
384 
385     ngx_conf_merge_value(conf->push, prev->push, 1);
386 
387     if (conf->push && conf->pushes == NULL) {
388         conf->pushes = prev->pushes;
389     }
390 
391     ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0);
392 
393     return NGX_CONF_OK;
394 }
395 
396 
397 static char *
ngx_http_v2_push(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)398 ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
399 {
400     ngx_http_v2_loc_conf_t *h2lcf = conf;
401 
402     ngx_str_t                         *value;
403     ngx_http_complex_value_t          *cv;
404     ngx_http_compile_complex_value_t   ccv;
405 
406     value = cf->args->elts;
407 
408     if (ngx_strcmp(value[1].data, "off") == 0) {
409 
410         if (h2lcf->pushes) {
411             return "\"off\" parameter cannot be used with URI";
412         }
413 
414         if (h2lcf->push == 0) {
415             return "is duplicate";
416         }
417 
418         h2lcf->push = 0;
419         return NGX_CONF_OK;
420     }
421 
422     if (h2lcf->push == 0) {
423         return "URI cannot be used with \"off\" parameter";
424     }
425 
426     h2lcf->push = 1;
427 
428     if (h2lcf->pushes == NULL) {
429         h2lcf->pushes = ngx_array_create(cf->pool, 1,
430                                          sizeof(ngx_http_complex_value_t));
431         if (h2lcf->pushes == NULL) {
432             return NGX_CONF_ERROR;
433         }
434     }
435 
436     cv = ngx_array_push(h2lcf->pushes);
437     if (cv == NULL) {
438         return NGX_CONF_ERROR;
439     }
440 
441     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
442 
443     ccv.cf = cf;
444     ccv.value = &value[1];
445     ccv.complex_value = cv;
446 
447     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
448         return NGX_CONF_ERROR;
449     }
450 
451     return NGX_CONF_OK;
452 }
453 
454 
455 static char *
ngx_http_v2_recv_buffer_size(ngx_conf_t * cf,void * post,void * data)456 ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
457 {
458     size_t *sp = data;
459 
460     if (*sp <= 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE) {
461         return "value is too small";
462     }
463 
464     return NGX_CONF_OK;
465 }
466 
467 
468 static char *
ngx_http_v2_pool_size(ngx_conf_t * cf,void * post,void * data)469 ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data)
470 {
471     size_t *sp = data;
472 
473     if (*sp < NGX_MIN_POOL_SIZE) {
474         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
475                            "the pool size must be no less than %uz",
476                            NGX_MIN_POOL_SIZE);
477 
478         return NGX_CONF_ERROR;
479     }
480 
481     if (*sp % NGX_POOL_ALIGNMENT) {
482         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
483                            "the pool size must be a multiple of %uz",
484                            NGX_POOL_ALIGNMENT);
485 
486         return NGX_CONF_ERROR;
487     }
488 
489     return NGX_CONF_OK;
490 }
491 
492 
493 static char *
ngx_http_v2_preread_size(ngx_conf_t * cf,void * post,void * data)494 ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data)
495 {
496     size_t *sp = data;
497 
498     if (*sp > NGX_HTTP_V2_MAX_WINDOW) {
499         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
500                            "the maximum body preread buffer size is %uz",
501                            NGX_HTTP_V2_MAX_WINDOW);
502 
503         return NGX_CONF_ERROR;
504     }
505 
506     return NGX_CONF_OK;
507 }
508 
509 
510 static char *
ngx_http_v2_streams_index_mask(ngx_conf_t * cf,void * post,void * data)511 ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
512 {
513     ngx_uint_t *np = data;
514 
515     ngx_uint_t  mask;
516 
517     mask = *np - 1;
518 
519     if (*np == 0 || (*np & mask)) {
520         return "must be a power of two";
521     }
522 
523     *np = mask;
524 
525     return NGX_CONF_OK;
526 }
527 
528 
529 static char *
ngx_http_v2_chunk_size(ngx_conf_t * cf,void * post,void * data)530 ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data)
531 {
532     size_t *sp = data;
533 
534     if (*sp == 0) {
535         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
536                            "the http2 chunk size cannot be zero");
537 
538         return NGX_CONF_ERROR;
539     }
540 
541     if (*sp > NGX_HTTP_V2_MAX_FRAME_SIZE) {
542         *sp = NGX_HTTP_V2_MAX_FRAME_SIZE;
543     }
544 
545     return NGX_CONF_OK;
546 }
547 
548 
549 static char *
ngx_http_v2_obsolete(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)550 ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
551 {
552     ngx_conf_deprecated_t  *d = cmd->post;
553 
554     ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
555                        "the \"%s\" directive is obsolete, "
556                        "use the \"%s\" directive instead",
557                        d->old_name, d->new_name);
558 
559     return NGX_CONF_OK;
560 }
561