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 <nginx.h>
12#include <ngx_http_v2_module.h>
13
14
15/*
16 * This returns precise number of octets for values in range 0..253
17 * and estimate number for the rest, but not smaller than required.
18 */
19
20#define ngx_http_v2_integer_octets(v)  (1 + (v) / 127)
21
22#define ngx_http_v2_literal_size(h)                                           \
23    (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
24
25#define ngx_http_v2_indexed(i)      (128 + (i))
26#define ngx_http_v2_inc_indexed(i)  (64 + (i))
27
28#define ngx_http_v2_write_name(dst, src, len, tmp)                            \
29    ngx_http_v2_string_encode(dst, src, len, tmp, 1)
30#define ngx_http_v2_write_value(dst, src, len, tmp)                           \
31    ngx_http_v2_string_encode(dst, src, len, tmp, 0)
32
33#define NGX_HTTP_V2_ENCODE_RAW            0
34#define NGX_HTTP_V2_ENCODE_HUFF           0x80
35
36#define NGX_HTTP_V2_STATUS_INDEX          8
37#define NGX_HTTP_V2_STATUS_200_INDEX      8
38#define NGX_HTTP_V2_STATUS_204_INDEX      9
39#define NGX_HTTP_V2_STATUS_206_INDEX      10
40#define NGX_HTTP_V2_STATUS_304_INDEX      11
41#define NGX_HTTP_V2_STATUS_400_INDEX      12
42#define NGX_HTTP_V2_STATUS_404_INDEX      13
43#define NGX_HTTP_V2_STATUS_500_INDEX      14
44
45#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28
46#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31
47#define NGX_HTTP_V2_DATE_INDEX            33
48#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44
49#define NGX_HTTP_V2_LOCATION_INDEX        46
50#define NGX_HTTP_V2_SERVER_INDEX          54
51#define NGX_HTTP_V2_VARY_INDEX            59
52
53
54static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
55    u_char *tmp, ngx_uint_t lower);
56static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
57    ngx_uint_t value);
58static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
59    ngx_http_request_t *r, u_char *pos, u_char *end);
60
61static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
62    ngx_chain_t *in, off_t limit);
63
64static ngx_chain_t *ngx_http_v2_filter_get_shadow(
65    ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
66static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame(
67    ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first,
68    ngx_chain_t *last);
69
70static ngx_inline ngx_int_t ngx_http_v2_flow_control(
71    ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
72static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
73    ngx_http_v2_stream_t *stream);
74
75static ngx_inline ngx_int_t ngx_http_v2_filter_send(
76    ngx_connection_t *fc, ngx_http_v2_stream_t *stream);
77
78static ngx_int_t ngx_http_v2_headers_frame_handler(
79    ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
80static ngx_int_t ngx_http_v2_data_frame_handler(
81    ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
82static ngx_inline void ngx_http_v2_handle_frame(
83    ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);
84static ngx_inline void ngx_http_v2_handle_stream(
85    ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
86
87static void ngx_http_v2_filter_cleanup(void *data);
88
89static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf);
90
91
92static ngx_http_module_t  ngx_http_v2_filter_module_ctx = {
93    NULL,                                  /* preconfiguration */
94    ngx_http_v2_filter_init,               /* postconfiguration */
95
96    NULL,                                  /* create main configuration */
97    NULL,                                  /* init main configuration */
98
99    NULL,                                  /* create server configuration */
100    NULL,                                  /* merge server configuration */
101
102    NULL,                                  /* create location configuration */
103    NULL                                   /* merge location configuration */
104};
105
106
107ngx_module_t  ngx_http_v2_filter_module = {
108    NGX_MODULE_V1,
109    &ngx_http_v2_filter_module_ctx,        /* module context */
110    NULL,                                  /* module directives */
111    NGX_HTTP_MODULE,                       /* module type */
112    NULL,                                  /* init master */
113    NULL,                                  /* init module */
114    NULL,                                  /* init process */
115    NULL,                                  /* init thread */
116    NULL,                                  /* exit thread */
117    NULL,                                  /* exit process */
118    NULL,                                  /* exit master */
119    NGX_MODULE_V1_PADDING
120};
121
122
123static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
124
125
126static ngx_int_t
127ngx_http_v2_header_filter(ngx_http_request_t *r)
128{
129    u_char                     status, *pos, *start, *p, *tmp;
130    size_t                     len, tmp_len;
131    ngx_str_t                  host, location;
132    ngx_uint_t                 i, port;
133    ngx_list_part_t           *part;
134    ngx_table_elt_t           *header;
135    ngx_connection_t          *fc;
136    ngx_http_cleanup_t        *cln;
137    ngx_http_v2_out_frame_t   *frame;
138    ngx_http_core_loc_conf_t  *clcf;
139    ngx_http_core_srv_conf_t  *cscf;
140    u_char                     addr[NGX_SOCKADDR_STRLEN];
141
142    static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
143#if (NGX_HTTP_GZIP)
144    static const u_char accept_encoding[12] =
145        "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
146#endif
147
148    static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
149    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];
150
151    static size_t nginx_ver_build_len =
152                                  ngx_http_v2_literal_size(NGINX_VER_BUILD);
153    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];
154
155    if (!r->stream) {
156        return ngx_http_next_header_filter(r);
157    }
158
159    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
160                   "http2 header filter");
161
162    if (r->header_sent) {
163        return NGX_OK;
164    }
165
166    r->header_sent = 1;
167
168    if (r != r->main) {
169        return NGX_OK;
170    }
171
172    fc = r->connection;
173
174    if (fc->error) {
175        return NGX_ERROR;
176    }
177
178    if (r->method == NGX_HTTP_HEAD) {
179        r->header_only = 1;
180    }
181
182    switch (r->headers_out.status) {
183
184    case NGX_HTTP_OK:
185        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX);
186        break;
187
188    case NGX_HTTP_NO_CONTENT:
189        r->header_only = 1;
190
191        ngx_str_null(&r->headers_out.content_type);
192
193        r->headers_out.content_length = NULL;
194        r->headers_out.content_length_n = -1;
195
196        r->headers_out.last_modified_time = -1;
197        r->headers_out.last_modified = NULL;
198
199        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX);
200        break;
201
202    case NGX_HTTP_PARTIAL_CONTENT:
203        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX);
204        break;
205
206    case NGX_HTTP_NOT_MODIFIED:
207        r->header_only = 1;
208        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX);
209        break;
210
211    default:
212        r->headers_out.last_modified_time = -1;
213        r->headers_out.last_modified = NULL;
214
215        switch (r->headers_out.status) {
216
217        case NGX_HTTP_BAD_REQUEST:
218            status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX);
219            break;
220
221        case NGX_HTTP_NOT_FOUND:
222            status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX);
223            break;
224
225        case NGX_HTTP_INTERNAL_SERVER_ERROR:
226            status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX);
227            break;
228
229        default:
230            status = 0;
231        }
232    }
233
234    len = status ? 1 : 1 + ngx_http_v2_literal_size("418");
235
236    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
237
238    if (r->headers_out.server == NULL) {
239
240        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
241            len += 1 + nginx_ver_len;
242
243        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
244            len += 1 + nginx_ver_build_len;
245
246        } else {
247            len += 1 + sizeof(nginx);
248        }
249    }
250
251    if (r->headers_out.date == NULL) {
252        len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
253    }
254
255    if (r->headers_out.content_type.len) {
256        len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len;
257
258        if (r->headers_out.content_type_len == r->headers_out.content_type.len
259            && r->headers_out.charset.len)
260        {
261            len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
262        }
263    }
264
265    if (r->headers_out.content_length == NULL
266        && r->headers_out.content_length_n >= 0)
267    {
268        len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN;
269    }
270
271    if (r->headers_out.last_modified == NULL
272        && r->headers_out.last_modified_time != -1)
273    {
274        len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
275    }
276
277    if (r->headers_out.location && r->headers_out.location->value.len) {
278
279        if (r->headers_out.location->value.data[0] == '/'
280            && clcf->absolute_redirect)
281        {
282            if (clcf->server_name_in_redirect) {
283                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
284                host = cscf->server_name;
285
286            } else if (r->headers_in.server.len) {
287                host = r->headers_in.server;
288
289            } else {
290                host.len = NGX_SOCKADDR_STRLEN;
291                host.data = addr;
292
293                if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {
294                    return NGX_ERROR;
295                }
296            }
297
298            port = ngx_inet_get_port(fc->local_sockaddr);
299
300            location.len = sizeof("https://") - 1 + host.len
301                           + r->headers_out.location->value.len;
302
303            if (clcf->port_in_redirect) {
304
305#if (NGX_HTTP_SSL)
306                if (fc->ssl)
307                    port = (port == 443) ? 0 : port;
308                else
309#endif
310                    port = (port == 80) ? 0 : port;
311
312            } else {
313                port = 0;
314            }
315
316            if (port) {
317                location.len += sizeof(":65535") - 1;
318            }
319
320            location.data = ngx_pnalloc(r->pool, location.len);
321            if (location.data == NULL) {
322                return NGX_ERROR;
323            }
324
325            p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
326
327#if (NGX_HTTP_SSL)
328            if (fc->ssl) {
329                *p++ = 's';
330            }
331#endif
332
333            *p++ = ':'; *p++ = '/'; *p++ = '/';
334            p = ngx_cpymem(p, host.data, host.len);
335
336            if (port) {
337                p = ngx_sprintf(p, ":%ui", port);
338            }
339
340            p = ngx_cpymem(p, r->headers_out.location->value.data,
341                              r->headers_out.location->value.len);
342
343            /* update r->headers_out.location->value for possible logging */
344
345            r->headers_out.location->value.len = p - location.data;
346            r->headers_out.location->value.data = location.data;
347            ngx_str_set(&r->headers_out.location->key, "Location");
348        }
349
350        r->headers_out.location->hash = 0;
351
352        len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len;
353    }
354
355    tmp_len = len;
356
357#if (NGX_HTTP_GZIP)
358    if (r->gzip_vary) {
359        if (clcf->gzip_vary) {
360            len += 1 + sizeof(accept_encoding);
361
362        } else {
363            r->gzip_vary = 0;
364        }
365    }
366#endif
367
368    part = &r->headers_out.headers.part;
369    header = part->elts;
370
371    for (i = 0; /* void */; i++) {
372
373        if (i >= part->nelts) {
374            if (part->next == NULL) {
375                break;
376            }
377
378            part = part->next;
379            header = part->elts;
380            i = 0;
381        }
382
383        if (header[i].hash == 0) {
384            continue;
385        }
386
387        if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
388            ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
389                          "too long response header name: \"%V\"",
390                          &header[i].key);
391            return NGX_ERROR;
392        }
393
394        if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
395            ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
396                          "too long response header value: \"%V: %V\"",
397                          &header[i].key, &header[i].value);
398            return NGX_ERROR;
399        }
400
401        len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
402                 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
403
404        if (header[i].key.len > tmp_len) {
405            tmp_len = header[i].key.len;
406        }
407
408        if (header[i].value.len > tmp_len) {
409            tmp_len = header[i].value.len;
410        }
411    }
412
413    tmp = ngx_palloc(r->pool, tmp_len);
414    pos = ngx_pnalloc(r->pool, len);
415
416    if (pos == NULL || tmp == NULL) {
417        return NGX_ERROR;
418    }
419
420    start = pos;
421
422    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
423                   "http2 output header: \":status: %03ui\"",
424                   r->headers_out.status);
425
426    if (status) {
427        *pos++ = status;
428
429    } else {
430        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
431        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
432        pos = ngx_sprintf(pos, "%03ui", r->headers_out.status);
433    }
434
435    if (r->headers_out.server == NULL) {
436
437        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
438            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
439                           "http2 output header: \"server: %s\"",
440                           NGINX_VER);
441
442        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
443            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
444                           "http2 output header: \"server: %s\"",
445                           NGINX_VER_BUILD);
446
447        } else {
448            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
449                           "http2 output header: \"server: nginx\"");
450        }
451
452        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
453
454        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
455            if (nginx_ver[0] == '\0') {
456                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,
457                                            sizeof(NGINX_VER) - 1, tmp);
458                nginx_ver_len = p - nginx_ver;
459            }
460
461            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);
462
463        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
464            if (nginx_ver_build[0] == '\0') {
465                p = ngx_http_v2_write_value(nginx_ver_build,
466                                            (u_char *) NGINX_VER_BUILD,
467                                            sizeof(NGINX_VER_BUILD) - 1, tmp);
468                nginx_ver_build_len = p - nginx_ver_build;
469            }
470
471            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);
472
473        } else {
474            pos = ngx_cpymem(pos, nginx, sizeof(nginx));
475        }
476    }
477
478    if (r->headers_out.date == NULL) {
479        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
480                       "http2 output header: \"date: %V\"",
481                       &ngx_cached_http_time);
482
483        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
484        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,
485                                      ngx_cached_http_time.len, tmp);
486    }
487
488    if (r->headers_out.content_type.len) {
489        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
490
491        if (r->headers_out.content_type_len == r->headers_out.content_type.len
492            && r->headers_out.charset.len)
493        {
494            len = r->headers_out.content_type.len + sizeof("; charset=") - 1
495                  + r->headers_out.charset.len;
496
497            p = ngx_pnalloc(r->pool, len);
498            if (p == NULL) {
499                return NGX_ERROR;
500            }
501
502            p = ngx_cpymem(p, r->headers_out.content_type.data,
503                           r->headers_out.content_type.len);
504
505            p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1);
506
507            p = ngx_cpymem(p, r->headers_out.charset.data,
508                           r->headers_out.charset.len);
509
510            /* updated r->headers_out.content_type is also needed for logging */
511
512            r->headers_out.content_type.len = len;
513            r->headers_out.content_type.data = p - len;
514        }
515
516        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
517                       "http2 output header: \"content-type: %V\"",
518                       &r->headers_out.content_type);
519
520        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,
521                                      r->headers_out.content_type.len, tmp);
522    }
523
524    if (r->headers_out.content_length == NULL
525        && r->headers_out.content_length_n >= 0)
526    {
527        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
528                       "http2 output header: \"content-length: %O\"",
529                       r->headers_out.content_length_n);
530
531        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
532
533        p = pos;
534        pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n);
535        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);
536    }
537
538    if (r->headers_out.last_modified == NULL
539        && r->headers_out.last_modified_time != -1)
540    {
541        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
542
543        ngx_http_time(pos, r->headers_out.last_modified_time);
544        len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
545
546        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
547                       "http2 output header: \"last-modified: %*s\"",
548                       len, pos);
549
550        /*
551         * Date will always be encoded using huffman in the temporary buffer,
552         * so it's safe here to use src and dst pointing to the same address.
553         */
554        pos = ngx_http_v2_write_value(pos, pos, len, tmp);
555    }
556
557    if (r->headers_out.location && r->headers_out.location->value.len) {
558        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
559                       "http2 output header: \"location: %V\"",
560                       &r->headers_out.location->value);
561
562        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
563        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,
564                                      r->headers_out.location->value.len, tmp);
565    }
566
567#if (NGX_HTTP_GZIP)
568    if (r->gzip_vary) {
569        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
570                       "http2 output header: \"vary: Accept-Encoding\"");
571
572        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
573        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));
574    }
575#endif
576
577    part = &r->headers_out.headers.part;
578    header = part->elts;
579
580    for (i = 0; /* void */; i++) {
581
582        if (i >= part->nelts) {
583            if (part->next == NULL) {
584                break;
585            }
586
587            part = part->next;
588            header = part->elts;
589            i = 0;
590        }
591
592        if (header[i].hash == 0) {
593            continue;
594        }
595
596#if (NGX_DEBUG)
597        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
598            ngx_strlow(tmp, header[i].key.data, header[i].key.len);
599
600            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
601                           "http2 output header: \"%*s: %V\"",
602                           header[i].key.len, tmp, &header[i].value);
603        }
604#endif
605
606        *pos++ = 0;
607
608        pos = ngx_http_v2_write_name(pos, header[i].key.data,
609                                     header[i].key.len, tmp);
610
611        pos = ngx_http_v2_write_value(pos, header[i].value.data,
612                                      header[i].value.len, tmp);
613    }
614
615    frame = ngx_http_v2_create_headers_frame(r, start, pos);
616    if (frame == NULL) {
617        return NGX_ERROR;
618    }
619
620    ngx_http_v2_queue_blocked_frame(r->stream->connection, frame);
621
622    cln = ngx_http_cleanup_add(r, 0);
623    if (cln == NULL) {
624        return NGX_ERROR;
625    }
626
627    cln->handler = ngx_http_v2_filter_cleanup;
628    cln->data = r->stream;
629
630    r->stream->queued = 1;
631
632    fc->send_chain = ngx_http_v2_send_chain;
633    fc->need_last_buf = 1;
634
635    return ngx_http_v2_filter_send(fc, r->stream);
636}
637
638
639static u_char *
640ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,
641    ngx_uint_t lower)
642{
643    size_t  hlen;
644
645    hlen = ngx_http_v2_huff_encode(src, len, tmp, lower);
646
647    if (hlen > 0) {
648        *dst = NGX_HTTP_V2_ENCODE_HUFF;
649        dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen);
650        return ngx_cpymem(dst, tmp, hlen);
651    }
652
653    *dst = NGX_HTTP_V2_ENCODE_RAW;
654    dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len);
655
656    if (lower) {
657        ngx_strlow(dst, src, len);
658        return dst + len;
659    }
660
661    return ngx_cpymem(dst, src, len);
662}
663
664
665static u_char *
666ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)
667{
668    if (value < prefix) {
669        *pos++ |= value;
670        return pos;
671    }
672
673    *pos++ |= prefix;
674    value -= prefix;
675
676    while (value >= 128) {
677        *pos++ = value % 128 + 128;
678        value /= 128;
679    }
680
681    *pos++ = (u_char) value;
682
683    return pos;
684}
685
686
687static ngx_http_v2_out_frame_t *
688ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
689    u_char *end)
690{
691    u_char                    type, flags;
692    size_t                    rest, frame_size;
693    ngx_buf_t                *b;
694    ngx_chain_t              *cl, **ll;
695    ngx_http_v2_stream_t     *stream;
696    ngx_http_v2_out_frame_t  *frame;
697
698    stream = r->stream;
699    rest = end - pos;
700
701    frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
702    if (frame == NULL) {
703        return NULL;
704    }
705
706    frame->handler = ngx_http_v2_headers_frame_handler;
707    frame->stream = stream;
708    frame->length = rest;
709    frame->blocked = 1;
710    frame->fin = r->header_only;
711
712    ll = &frame->first;
713
714    type = NGX_HTTP_V2_HEADERS_FRAME;
715    flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
716    frame_size = stream->connection->frame_size;
717
718    for ( ;; ) {
719        if (rest <= frame_size) {
720            frame_size = rest;
721            flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
722        }
723
724        b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);
725        if (b == NULL) {
726            return NULL;
727        }
728
729        b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
730        *b->last++ = flags;
731        b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
732
733        b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
734
735        cl = ngx_alloc_chain_link(r->pool);
736        if (cl == NULL) {
737            return NULL;
738        }
739
740        cl->buf = b;
741
742        *ll = cl;
743        ll = &cl->next;
744
745        b = ngx_calloc_buf(r->pool);
746        if (b == NULL) {
747            return NULL;
748        }
749
750        b->pos = pos;
751
752        pos += frame_size;
753
754        b->last = pos;
755        b->start = b->pos;
756        b->end = b->last;
757        b->temporary = 1;
758
759        cl = ngx_alloc_chain_link(r->pool);
760        if (cl == NULL) {
761            return NULL;
762        }
763
764        cl->buf = b;
765
766        *ll = cl;
767        ll = &cl->next;
768
769        rest -= frame_size;
770
771        if (rest) {
772            frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
773
774            type = NGX_HTTP_V2_CONTINUATION_FRAME;
775            flags = NGX_HTTP_V2_NO_FLAG;
776            continue;
777        }
778
779        b->last_buf = r->header_only;
780        cl->next = NULL;
781        frame->last = cl;
782
783        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
784                       "http2:%ui create HEADERS frame %p: len:%uz",
785                       stream->node->id, frame, frame->length);
786
787        return frame;
788    }
789}
790
791
792static ngx_chain_t *
793ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
794{
795    off_t                      size, offset;
796    size_t                     rest, frame_size;
797    ngx_chain_t               *cl, *out, **ln;
798    ngx_http_request_t        *r;
799    ngx_http_v2_stream_t      *stream;
800    ngx_http_v2_loc_conf_t    *h2lcf;
801    ngx_http_v2_out_frame_t   *frame;
802    ngx_http_v2_connection_t  *h2c;
803
804    r = fc->data;
805    stream = r->stream;
806
807#if (NGX_SUPPRESS_WARN)
808    size = 0;
809#endif
810
811    while (in) {
812        size = ngx_buf_size(in->buf);
813
814        if (size || in->buf->last_buf) {
815            break;
816        }
817
818        in = in->next;
819    }
820
821    if (in == NULL) {
822
823        if (stream->queued) {
824            fc->write->active = 1;
825            fc->write->ready = 0;
826
827        } else {
828            fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
829        }
830
831        return NULL;
832    }
833
834    h2c = stream->connection;
835
836    if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
837        fc->write->active = 1;
838        fc->write->ready = 0;
839        return in;
840    }
841
842    if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
843        cl = ngx_alloc_chain_link(r->pool);
844        if (cl == NULL) {
845            return NGX_CHAIN_ERROR;
846        }
847
848        cl->buf = in->buf;
849        in->buf = cl->buf->shadow;
850
851        offset = ngx_buf_in_memory(in->buf)
852                 ? (cl->buf->pos - in->buf->pos)
853                 : (cl->buf->file_pos - in->buf->file_pos);
854
855        cl->next = stream->free_bufs;
856        stream->free_bufs = cl;
857
858    } else {
859        offset = 0;
860    }
861
862    if (limit == 0 || limit > (off_t) h2c->send_window) {
863        limit = h2c->send_window;
864    }
865
866    if (limit > stream->send_window) {
867        limit = (stream->send_window > 0) ? stream->send_window : 0;
868    }
869
870    h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
871
872    frame_size = (h2lcf->chunk_size < h2c->frame_size)
873                 ? h2lcf->chunk_size : h2c->frame_size;
874
875#if (NGX_SUPPRESS_WARN)
876    cl = NULL;
877#endif
878
879    for ( ;; ) {
880        if ((off_t) frame_size > limit) {
881            frame_size = (size_t) limit;
882        }
883
884        ln = &out;
885        rest = frame_size;
886
887        while ((off_t) rest >= size) {
888
889            if (offset) {
890                cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
891                                                   offset, size);
892                if (cl == NULL) {
893                    return NGX_CHAIN_ERROR;
894                }
895
896                offset = 0;
897
898            } else {
899                cl = ngx_alloc_chain_link(r->pool);
900                if (cl == NULL) {
901                    return NGX_CHAIN_ERROR;
902                }
903
904                cl->buf = in->buf;
905            }
906
907            *ln = cl;
908            ln = &cl->next;
909
910            rest -= (size_t) size;
911            in = in->next;
912
913            if (in == NULL) {
914                frame_size -= rest;
915                rest = 0;
916                break;
917            }
918
919            size = ngx_buf_size(in->buf);
920        }
921
922        if (rest) {
923            cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
924            if (cl == NULL) {
925                return NGX_CHAIN_ERROR;
926            }
927
928            cl->buf->flush = 0;
929            cl->buf->last_buf = 0;
930
931            *ln = cl;
932
933            offset += rest;
934            size -= rest;
935        }
936
937        frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl);
938        if (frame == NULL) {
939            return NGX_CHAIN_ERROR;
940        }
941
942        ngx_http_v2_queue_frame(h2c, frame);
943
944        h2c->send_window -= frame_size;
945
946        stream->send_window -= frame_size;
947        stream->queued++;
948
949        if (in == NULL) {
950            break;
951        }
952
953        limit -= frame_size;
954
955        if (limit == 0) {
956            break;
957        }
958    }
959
960    if (offset) {
961        cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
962        if (cl == NULL) {
963            return NGX_CHAIN_ERROR;
964        }
965
966        in->buf = cl->buf;
967        ngx_free_chain(r->pool, cl);
968    }
969
970    if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
971        return NGX_CHAIN_ERROR;
972    }
973
974    if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
975        fc->write->active = 1;
976        fc->write->ready = 0;
977    }
978
979    return in;
980}
981
982
983static ngx_chain_t *
984ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
985    off_t offset, off_t size)
986{
987    ngx_buf_t    *chunk;
988    ngx_chain_t  *cl;
989
990    cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
991    if (cl == NULL) {
992        return NULL;
993    }
994
995    chunk = cl->buf;
996
997    ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
998
999    chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
1000    chunk->shadow = buf;
1001
1002    if (ngx_buf_in_memory(chunk)) {
1003        chunk->pos += offset;
1004        chunk->last = chunk->pos + size;
1005    }
1006
1007    if (chunk->in_file) {
1008        chunk->file_pos += offset;
1009        chunk->file_last = chunk->file_pos + size;
1010    }
1011
1012    return cl;
1013}
1014
1015
1016static ngx_http_v2_out_frame_t *
1017ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
1018    size_t len, ngx_chain_t *first, ngx_chain_t *last)
1019{
1020    u_char                    flags;
1021    ngx_buf_t                *buf;
1022    ngx_chain_t              *cl;
1023    ngx_http_v2_out_frame_t  *frame;
1024
1025    frame = stream->free_frames;
1026
1027    if (frame) {
1028        stream->free_frames = frame->next;
1029
1030    } else {
1031        frame = ngx_palloc(stream->request->pool,
1032                           sizeof(ngx_http_v2_out_frame_t));
1033        if (frame == NULL) {
1034            return NULL;
1035        }
1036    }
1037
1038    flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
1039
1040    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
1041                   "http2:%ui create DATA frame %p: len:%uz flags:%ui",
1042                   stream->node->id, frame, len, (ngx_uint_t) flags);
1043
1044    cl = ngx_chain_get_free_buf(stream->request->pool,
1045                                &stream->free_frame_headers);
1046    if (cl == NULL) {
1047        return NULL;
1048    }
1049
1050    buf = cl->buf;
1051
1052    if (buf->start == NULL) {
1053        buf->start = ngx_palloc(stream->request->pool,
1054                                NGX_HTTP_V2_FRAME_HEADER_SIZE);
1055        if (buf->start == NULL) {
1056            return NULL;
1057        }
1058
1059        buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
1060        buf->last = buf->end;
1061
1062        buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1063        buf->memory = 1;
1064    }
1065
1066    buf->pos = buf->start;
1067    buf->last = buf->pos;
1068
1069    buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
1070                                               NGX_HTTP_V2_DATA_FRAME);
1071    *buf->last++ = flags;
1072
1073    buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
1074
1075    cl->next = first;
1076    first = cl;
1077
1078    last->buf->flush = 1;
1079
1080    frame->first = first;
1081    frame->last = last;
1082    frame->handler = ngx_http_v2_data_frame_handler;
1083    frame->stream = stream;
1084    frame->length = len;
1085    frame->blocked = 0;
1086    frame->fin = last->buf->last_buf;
1087
1088    return frame;
1089}
1090
1091
1092static ngx_inline ngx_int_t
1093ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
1094{
1095    stream->blocked = 1;
1096
1097    if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
1098        fc->error = 1;
1099        return NGX_ERROR;
1100    }
1101
1102    stream->blocked = 0;
1103
1104    if (stream->queued) {
1105        fc->buffered |= NGX_HTTP_V2_BUFFERED;
1106        fc->write->active = 1;
1107        fc->write->ready = 0;
1108        return NGX_AGAIN;
1109    }
1110
1111    fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1112
1113    return NGX_OK;
1114}
1115
1116
1117static ngx_inline ngx_int_t
1118ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
1119    ngx_http_v2_stream_t *stream)
1120{
1121    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1122                   "http2:%ui available windows: conn:%uz stream:%z",
1123                   stream->node->id, h2c->send_window, stream->send_window);
1124
1125    if (stream->send_window <= 0) {
1126        stream->exhausted = 1;
1127        return NGX_DECLINED;
1128    }
1129
1130    if (h2c->send_window == 0) {
1131        ngx_http_v2_waiting_queue(h2c, stream);
1132        return NGX_DECLINED;
1133    }
1134
1135    return NGX_OK;
1136}
1137
1138
1139static void
1140ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
1141    ngx_http_v2_stream_t *stream)
1142{
1143    ngx_queue_t           *q;
1144    ngx_http_v2_stream_t  *s;
1145
1146    if (stream->waiting) {
1147        return;
1148    }
1149
1150    stream->waiting = 1;
1151
1152    for (q = ngx_queue_last(&h2c->waiting);
1153         q != ngx_queue_sentinel(&h2c->waiting);
1154         q = ngx_queue_prev(q))
1155    {
1156        s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
1157
1158        if (s->node->rank < stream->node->rank
1159            || (s->node->rank == stream->node->rank
1160                && s->node->rel_weight >= stream->node->rel_weight))
1161        {
1162            break;
1163        }
1164    }
1165
1166    ngx_queue_insert_after(q, &stream->queue);
1167}
1168
1169
1170
1171static ngx_int_t
1172ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
1173    ngx_http_v2_out_frame_t *frame)
1174{
1175    ngx_chain_t           *cl, *ln;
1176    ngx_http_v2_stream_t  *stream;
1177
1178    stream = frame->stream;
1179    cl = frame->first;
1180
1181    for ( ;; ) {
1182        if (cl->buf->pos != cl->buf->last) {
1183            frame->first = cl;
1184
1185            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1186                           "http2:%ui HEADERS frame %p was sent partially",
1187                           stream->node->id, frame);
1188
1189            return NGX_AGAIN;
1190        }
1191
1192        ln = cl->next;
1193
1194        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1195            cl->next = stream->free_frame_headers;
1196            stream->free_frame_headers = cl;
1197
1198        } else {
1199            cl->next = stream->free_bufs;
1200            stream->free_bufs = cl;
1201        }
1202
1203        if (cl == frame->last) {
1204            break;
1205        }
1206
1207        cl = ln;
1208    }
1209
1210    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1211                   "http2:%ui HEADERS frame %p was sent",
1212                   stream->node->id, frame);
1213
1214    stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1215                                    + frame->length;
1216
1217    ngx_http_v2_handle_frame(stream, frame);
1218
1219    ngx_http_v2_handle_stream(h2c, stream);
1220
1221    return NGX_OK;
1222}
1223
1224
1225static ngx_int_t
1226ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
1227    ngx_http_v2_out_frame_t *frame)
1228{
1229    ngx_buf_t             *buf;
1230    ngx_chain_t           *cl, *ln;
1231    ngx_http_v2_stream_t  *stream;
1232
1233    stream = frame->stream;
1234    cl = frame->first;
1235
1236    if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1237
1238        if (cl->buf->pos != cl->buf->last) {
1239            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1240                           "http2:%ui DATA frame %p was sent partially",
1241                           stream->node->id, frame);
1242
1243            return NGX_AGAIN;
1244        }
1245
1246        ln = cl->next;
1247
1248        cl->next = stream->free_frame_headers;
1249        stream->free_frame_headers = cl;
1250
1251        if (cl == frame->last) {
1252            goto done;
1253        }
1254
1255        cl = ln;
1256    }
1257
1258    for ( ;; ) {
1259        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1260            buf = cl->buf->shadow;
1261
1262            if (ngx_buf_in_memory(buf)) {
1263                buf->pos = cl->buf->pos;
1264            }
1265
1266            if (buf->in_file) {
1267                buf->file_pos = cl->buf->file_pos;
1268            }
1269        }
1270
1271        if (ngx_buf_size(cl->buf) != 0) {
1272
1273            if (cl != frame->first) {
1274                frame->first = cl;
1275                ngx_http_v2_handle_stream(h2c, stream);
1276            }
1277
1278            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1279                           "http2:%ui DATA frame %p was sent partially",
1280                           stream->node->id, frame);
1281
1282            return NGX_AGAIN;
1283        }
1284
1285        ln = cl->next;
1286
1287        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1288            cl->next = stream->free_bufs;
1289            stream->free_bufs = cl;
1290
1291        } else {
1292            ngx_free_chain(stream->request->pool, cl);
1293        }
1294
1295        if (cl == frame->last) {
1296            goto done;
1297        }
1298
1299        cl = ln;
1300    }
1301
1302done:
1303
1304    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1305                   "http2:%ui DATA frame %p was sent",
1306                   stream->node->id, frame);
1307
1308    stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1309
1310    ngx_http_v2_handle_frame(stream, frame);
1311
1312    ngx_http_v2_handle_stream(h2c, stream);
1313
1314    return NGX_OK;
1315}
1316
1317
1318static ngx_inline void
1319ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
1320    ngx_http_v2_out_frame_t *frame)
1321{
1322    ngx_http_request_t  *r;
1323
1324    r = stream->request;
1325
1326    r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
1327
1328    if (frame->fin) {
1329        stream->out_closed = 1;
1330    }
1331
1332    frame->next = stream->free_frames;
1333    stream->free_frames = frame;
1334
1335    stream->queued--;
1336}
1337
1338
1339static ngx_inline void
1340ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
1341    ngx_http_v2_stream_t *stream)
1342{
1343    ngx_event_t       *wev;
1344    ngx_connection_t  *fc;
1345
1346    if (stream->waiting || stream->blocked) {
1347        return;
1348    }
1349
1350    fc = stream->request->connection;
1351
1352    if (!fc->error && stream->exhausted) {
1353        return;
1354    }
1355
1356    wev = fc->write;
1357
1358    wev->active = 0;
1359    wev->ready = 1;
1360
1361    if (!fc->error && wev->delayed) {
1362        return;
1363    }
1364
1365    ngx_post_event(wev, &ngx_posted_events);
1366}
1367
1368
1369static void
1370ngx_http_v2_filter_cleanup(void *data)
1371{
1372    ngx_http_v2_stream_t *stream = data;
1373
1374    size_t                     window;
1375    ngx_event_t               *wev;
1376    ngx_queue_t               *q;
1377    ngx_http_v2_out_frame_t   *frame, **fn;
1378    ngx_http_v2_connection_t  *h2c;
1379
1380    if (stream->waiting) {
1381        stream->waiting = 0;
1382        ngx_queue_remove(&stream->queue);
1383    }
1384
1385    if (stream->queued == 0) {
1386        return;
1387    }
1388
1389    window = 0;
1390    h2c = stream->connection;
1391    fn = &h2c->last_out;
1392
1393    for ( ;; ) {
1394        frame = *fn;
1395
1396        if (frame == NULL) {
1397            break;
1398        }
1399
1400        if (frame->stream == stream && !frame->blocked) {
1401            *fn = frame->next;
1402
1403            window += frame->length;
1404
1405            if (--stream->queued == 0) {
1406                break;
1407            }
1408
1409            continue;
1410        }
1411
1412        fn = &frame->next;
1413    }
1414
1415    if (h2c->send_window == 0 && window) {
1416
1417        while (!ngx_queue_empty(&h2c->waiting)) {
1418            q = ngx_queue_head(&h2c->waiting);
1419
1420            ngx_queue_remove(q);
1421
1422            stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
1423
1424            stream->waiting = 0;
1425
1426            wev = stream->request->connection->write;
1427
1428            wev->active = 0;
1429            wev->ready = 1;
1430
1431            if (!wev->delayed) {
1432                ngx_post_event(wev, &ngx_posted_events);
1433            }
1434        }
1435    }
1436
1437    h2c->send_window += window;
1438}
1439
1440
1441static ngx_int_t
1442ngx_http_v2_filter_init(ngx_conf_t *cf)
1443{
1444    ngx_http_next_header_filter = ngx_http_top_header_filter;
1445    ngx_http_top_header_filter = ngx_http_v2_header_filter;
1446
1447    return NGX_OK;
1448}
1449