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