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