Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Nginx, Inc.
0004  * Copyright (C) Valentin V. Bartenev
0005  * Copyright (C) Ruslan Ermilov
0006  */
0007 
0008 
0009 #include <ngx_config.h>
0010 #include <ngx_core.h>
0011 #include <ngx_http.h>
0012 #include <nginx.h>
0013 #include <ngx_http_v2_module.h>
0014 
0015 
0016 /*
0017  * This returns precise number of octets for values in range 0..253
0018  * and estimate number for the rest, but not smaller than required.
0019  */
0020 
0021 #define ngx_http_v2_integer_octets(v)  (1 + (v) / 127)
0022 
0023 #define ngx_http_v2_literal_size(h)                                           \
0024     (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
0025 
0026 
0027 #define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1
0028 
0029 
0030 typedef struct {
0031     ngx_str_t      name;
0032     u_char         index;
0033     ngx_uint_t     offset;
0034 } ngx_http_v2_push_header_t;
0035 
0036 
0037 static ngx_http_v2_push_header_t  ngx_http_v2_push_headers[] = {
0038     { ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX,
0039       offsetof(ngx_http_headers_in_t, host) },
0040 
0041     { ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,
0042       offsetof(ngx_http_headers_in_t, accept_encoding) },
0043 
0044     { ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,
0045       offsetof(ngx_http_headers_in_t, accept_language) },
0046 
0047     { ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX,
0048       offsetof(ngx_http_headers_in_t, user_agent) },
0049 };
0050 
0051 #define NGX_HTTP_V2_PUSH_HEADERS                                              \
0052     (sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))
0053 
0054 
0055 static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);
0056 static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,
0057     ngx_str_t *path, ngx_str_t *binary);
0058 
0059 static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
0060     ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
0061 static ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame(
0062     ngx_http_request_t *r, u_char *pos, u_char *end);
0063 static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
0064     ngx_http_request_t *r);
0065 
0066 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
0067     ngx_chain_t *in, off_t limit);
0068 
0069 static ngx_chain_t *ngx_http_v2_filter_get_shadow(
0070     ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
0071 static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame(
0072     ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first,
0073     ngx_chain_t *last);
0074 
0075 static ngx_inline ngx_int_t ngx_http_v2_flow_control(
0076     ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
0077 static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
0078     ngx_http_v2_stream_t *stream);
0079 
0080 static ngx_inline ngx_int_t ngx_http_v2_filter_send(
0081     ngx_connection_t *fc, ngx_http_v2_stream_t *stream);
0082 
0083 static ngx_int_t ngx_http_v2_headers_frame_handler(
0084     ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
0085 static ngx_int_t ngx_http_v2_push_frame_handler(
0086     ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
0087 static ngx_int_t ngx_http_v2_data_frame_handler(
0088     ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
0089 static ngx_inline void ngx_http_v2_handle_frame(
0090     ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);
0091 static ngx_inline void ngx_http_v2_handle_stream(
0092     ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
0093 
0094 static void ngx_http_v2_filter_cleanup(void *data);
0095 
0096 static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf);
0097 
0098 
0099 static ngx_http_module_t  ngx_http_v2_filter_module_ctx = {
0100     NULL,                                  /* preconfiguration */
0101     ngx_http_v2_filter_init,               /* postconfiguration */
0102 
0103     NULL,                                  /* create main configuration */
0104     NULL,                                  /* init main configuration */
0105 
0106     NULL,                                  /* create server configuration */
0107     NULL,                                  /* merge server configuration */
0108 
0109     NULL,                                  /* create location configuration */
0110     NULL                                   /* merge location configuration */
0111 };
0112 
0113 
0114 ngx_module_t  ngx_http_v2_filter_module = {
0115     NGX_MODULE_V1,
0116     &ngx_http_v2_filter_module_ctx,        /* module context */
0117     NULL,                                  /* module directives */
0118     NGX_HTTP_MODULE,                       /* module type */
0119     NULL,                                  /* init master */
0120     NULL,                                  /* init module */
0121     NULL,                                  /* init process */
0122     NULL,                                  /* init thread */
0123     NULL,                                  /* exit thread */
0124     NULL,                                  /* exit process */
0125     NULL,                                  /* exit master */
0126     NGX_MODULE_V1_PADDING
0127 };
0128 
0129 
0130 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0131 
0132 
0133 static ngx_int_t
0134 ngx_http_v2_header_filter(ngx_http_request_t *r)
0135 {
0136     u_char                     status, *pos, *start, *p, *tmp;
0137     size_t                     len, tmp_len;
0138     ngx_str_t                  host, location;
0139     ngx_uint_t                 i, port, fin;
0140     ngx_list_part_t           *part;
0141     ngx_table_elt_t           *header;
0142     ngx_connection_t          *fc;
0143     ngx_http_cleanup_t        *cln;
0144     ngx_http_v2_stream_t      *stream;
0145     ngx_http_v2_out_frame_t   *frame;
0146     ngx_http_v2_connection_t  *h2c;
0147     ngx_http_core_loc_conf_t  *clcf;
0148     ngx_http_core_srv_conf_t  *cscf;
0149     u_char                     addr[NGX_SOCKADDR_STRLEN];
0150 
0151     static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
0152 #if (NGX_HTTP_GZIP)
0153     static const u_char accept_encoding[12] =
0154         "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
0155 #endif
0156 
0157     static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
0158     static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];
0159 
0160     static size_t nginx_ver_build_len =
0161                                   ngx_http_v2_literal_size(NGINX_VER_BUILD);
0162     static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];
0163 
0164     stream = r->stream;
0165 
0166     if (!stream) {
0167         return ngx_http_next_header_filter(r);
0168     }
0169 
0170     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0171                    "http2 header filter");
0172 
0173     if (r->header_sent) {
0174         return NGX_OK;
0175     }
0176 
0177     r->header_sent = 1;
0178 
0179     if (r != r->main) {
0180         return NGX_OK;
0181     }
0182 
0183     fc = r->connection;
0184 
0185     if (fc->error) {
0186         return NGX_ERROR;
0187     }
0188 
0189     if (r->method == NGX_HTTP_HEAD) {
0190         r->header_only = 1;
0191     }
0192 
0193     switch (r->headers_out.status) {
0194 
0195     case NGX_HTTP_OK:
0196         status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX);
0197         break;
0198 
0199     case NGX_HTTP_NO_CONTENT:
0200         r->header_only = 1;
0201 
0202         ngx_str_null(&r->headers_out.content_type);
0203 
0204         r->headers_out.content_length = NULL;
0205         r->headers_out.content_length_n = -1;
0206 
0207         r->headers_out.last_modified_time = -1;
0208         r->headers_out.last_modified = NULL;
0209 
0210         status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX);
0211         break;
0212 
0213     case NGX_HTTP_PARTIAL_CONTENT:
0214         status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX);
0215         break;
0216 
0217     case NGX_HTTP_NOT_MODIFIED:
0218         r->header_only = 1;
0219         status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX);
0220         break;
0221 
0222     default:
0223         r->headers_out.last_modified_time = -1;
0224         r->headers_out.last_modified = NULL;
0225 
0226         switch (r->headers_out.status) {
0227 
0228         case NGX_HTTP_BAD_REQUEST:
0229             status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX);
0230             break;
0231 
0232         case NGX_HTTP_NOT_FOUND:
0233             status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX);
0234             break;
0235 
0236         case NGX_HTTP_INTERNAL_SERVER_ERROR:
0237             status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX);
0238             break;
0239 
0240         default:
0241             status = 0;
0242         }
0243     }
0244 
0245     h2c = stream->connection;
0246 
0247     if (!h2c->push_disabled && !h2c->goaway
0248         && stream->node->id % 2 == 1
0249         && r->method != NGX_HTTP_HEAD)
0250     {
0251         if (ngx_http_v2_push_resources(r) != NGX_OK) {
0252             return NGX_ERROR;
0253         }
0254     }
0255 
0256     len = h2c->table_update ? 1 : 0;
0257 
0258     len += status ? 1 : 1 + ngx_http_v2_literal_size("418");
0259 
0260     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0261 
0262     if (r->headers_out.server == NULL) {
0263 
0264         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
0265             len += 1 + nginx_ver_len;
0266 
0267         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
0268             len += 1 + nginx_ver_build_len;
0269 
0270         } else {
0271             len += 1 + sizeof(nginx);
0272         }
0273     }
0274 
0275     if (r->headers_out.date == NULL) {
0276         len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
0277     }
0278 
0279     if (r->headers_out.content_type.len) {
0280         len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len;
0281 
0282         if (r->headers_out.content_type_len == r->headers_out.content_type.len
0283             && r->headers_out.charset.len)
0284         {
0285             len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
0286         }
0287     }
0288 
0289     if (r->headers_out.content_length == NULL
0290         && r->headers_out.content_length_n >= 0)
0291     {
0292         len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN;
0293     }
0294 
0295     if (r->headers_out.last_modified == NULL
0296         && r->headers_out.last_modified_time != -1)
0297     {
0298         len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
0299     }
0300 
0301     if (r->headers_out.location && r->headers_out.location->value.len) {
0302 
0303         if (r->headers_out.location->value.data[0] == '/'
0304             && clcf->absolute_redirect)
0305         {
0306             if (clcf->server_name_in_redirect) {
0307                 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
0308                 host = cscf->server_name;
0309 
0310             } else if (r->headers_in.server.len) {
0311                 host = r->headers_in.server;
0312 
0313             } else {
0314                 host.len = NGX_SOCKADDR_STRLEN;
0315                 host.data = addr;
0316 
0317                 if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {
0318                     return NGX_ERROR;
0319                 }
0320             }
0321 
0322             port = ngx_inet_get_port(fc->local_sockaddr);
0323 
0324             location.len = sizeof("https://") - 1 + host.len
0325                            + r->headers_out.location->value.len;
0326 
0327             if (clcf->port_in_redirect) {
0328 
0329 #if (NGX_HTTP_SSL)
0330                 if (fc->ssl)
0331                     port = (port == 443) ? 0 : port;
0332                 else
0333 #endif
0334                     port = (port == 80) ? 0 : port;
0335 
0336             } else {
0337                 port = 0;
0338             }
0339 
0340             if (port) {
0341                 location.len += sizeof(":65535") - 1;
0342             }
0343 
0344             location.data = ngx_pnalloc(r->pool, location.len);
0345             if (location.data == NULL) {
0346                 return NGX_ERROR;
0347             }
0348 
0349             p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
0350 
0351 #if (NGX_HTTP_SSL)
0352             if (fc->ssl) {
0353                 *p++ = 's';
0354             }
0355 #endif
0356 
0357             *p++ = ':'; *p++ = '/'; *p++ = '/';
0358             p = ngx_cpymem(p, host.data, host.len);
0359 
0360             if (port) {
0361                 p = ngx_sprintf(p, ":%ui", port);
0362             }
0363 
0364             p = ngx_cpymem(p, r->headers_out.location->value.data,
0365                               r->headers_out.location->value.len);
0366 
0367             /* update r->headers_out.location->value for possible logging */
0368 
0369             r->headers_out.location->value.len = p - location.data;
0370             r->headers_out.location->value.data = location.data;
0371             ngx_str_set(&r->headers_out.location->key, "Location");
0372         }
0373 
0374         r->headers_out.location->hash = 0;
0375 
0376         len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len;
0377     }
0378 
0379     tmp_len = len;
0380 
0381 #if (NGX_HTTP_GZIP)
0382     if (r->gzip_vary) {
0383         if (clcf->gzip_vary) {
0384             len += 1 + sizeof(accept_encoding);
0385 
0386         } else {
0387             r->gzip_vary = 0;
0388         }
0389     }
0390 #endif
0391 
0392     part = &r->headers_out.headers.part;
0393     header = part->elts;
0394 
0395     for (i = 0; /* void */; i++) {
0396 
0397         if (i >= part->nelts) {
0398             if (part->next == NULL) {
0399                 break;
0400             }
0401 
0402             part = part->next;
0403             header = part->elts;
0404             i = 0;
0405         }
0406 
0407         if (header[i].hash == 0) {
0408             continue;
0409         }
0410 
0411         if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
0412             ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
0413                           "too long response header name: \"%V\"",
0414                           &header[i].key);
0415             return NGX_ERROR;
0416         }
0417 
0418         if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
0419             ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
0420                           "too long response header value: \"%V: %V\"",
0421                           &header[i].key, &header[i].value);
0422             return NGX_ERROR;
0423         }
0424 
0425         len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
0426                  + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
0427 
0428         if (header[i].key.len > tmp_len) {
0429             tmp_len = header[i].key.len;
0430         }
0431 
0432         if (header[i].value.len > tmp_len) {
0433             tmp_len = header[i].value.len;
0434         }
0435     }
0436 
0437     tmp = ngx_palloc(r->pool, tmp_len);
0438     pos = ngx_pnalloc(r->pool, len);
0439 
0440     if (pos == NULL || tmp == NULL) {
0441         return NGX_ERROR;
0442     }
0443 
0444     start = pos;
0445 
0446     if (h2c->table_update) {
0447         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0448                        "http2 table size update: 0");
0449         *pos++ = (1 << 5) | 0;
0450         h2c->table_update = 0;
0451     }
0452 
0453     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0454                    "http2 output header: \":status: %03ui\"",
0455                    r->headers_out.status);
0456 
0457     if (status) {
0458         *pos++ = status;
0459 
0460     } else {
0461         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
0462         *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
0463         pos = ngx_sprintf(pos, "%03ui", r->headers_out.status);
0464     }
0465 
0466     if (r->headers_out.server == NULL) {
0467 
0468         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
0469             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0470                            "http2 output header: \"server: %s\"",
0471                            NGINX_VER);
0472 
0473         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
0474             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0475                            "http2 output header: \"server: %s\"",
0476                            NGINX_VER_BUILD);
0477 
0478         } else {
0479             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0480                            "http2 output header: \"server: nginx\"");
0481         }
0482 
0483         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
0484 
0485         if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
0486             if (nginx_ver[0] == '\0') {
0487                 p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,
0488                                             sizeof(NGINX_VER) - 1, tmp);
0489                 nginx_ver_len = p - nginx_ver;
0490             }
0491 
0492             pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);
0493 
0494         } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
0495             if (nginx_ver_build[0] == '\0') {
0496                 p = ngx_http_v2_write_value(nginx_ver_build,
0497                                             (u_char *) NGINX_VER_BUILD,
0498                                             sizeof(NGINX_VER_BUILD) - 1, tmp);
0499                 nginx_ver_build_len = p - nginx_ver_build;
0500             }
0501 
0502             pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);
0503 
0504         } else {
0505             pos = ngx_cpymem(pos, nginx, sizeof(nginx));
0506         }
0507     }
0508 
0509     if (r->headers_out.date == NULL) {
0510         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0511                        "http2 output header: \"date: %V\"",
0512                        &ngx_cached_http_time);
0513 
0514         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
0515         pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,
0516                                       ngx_cached_http_time.len, tmp);
0517     }
0518 
0519     if (r->headers_out.content_type.len) {
0520         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
0521 
0522         if (r->headers_out.content_type_len == r->headers_out.content_type.len
0523             && r->headers_out.charset.len)
0524         {
0525             len = r->headers_out.content_type.len + sizeof("; charset=") - 1
0526                   + r->headers_out.charset.len;
0527 
0528             p = ngx_pnalloc(r->pool, len);
0529             if (p == NULL) {
0530                 return NGX_ERROR;
0531             }
0532 
0533             p = ngx_cpymem(p, r->headers_out.content_type.data,
0534                            r->headers_out.content_type.len);
0535 
0536             p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1);
0537 
0538             p = ngx_cpymem(p, r->headers_out.charset.data,
0539                            r->headers_out.charset.len);
0540 
0541             /* updated r->headers_out.content_type is also needed for logging */
0542 
0543             r->headers_out.content_type.len = len;
0544             r->headers_out.content_type.data = p - len;
0545         }
0546 
0547         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0548                        "http2 output header: \"content-type: %V\"",
0549                        &r->headers_out.content_type);
0550 
0551         pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,
0552                                       r->headers_out.content_type.len, tmp);
0553     }
0554 
0555     if (r->headers_out.content_length == NULL
0556         && r->headers_out.content_length_n >= 0)
0557     {
0558         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0559                        "http2 output header: \"content-length: %O\"",
0560                        r->headers_out.content_length_n);
0561 
0562         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
0563 
0564         p = pos;
0565         pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n);
0566         *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);
0567     }
0568 
0569     if (r->headers_out.last_modified == NULL
0570         && r->headers_out.last_modified_time != -1)
0571     {
0572         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
0573 
0574         ngx_http_time(pos, r->headers_out.last_modified_time);
0575         len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
0576 
0577         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0578                        "http2 output header: \"last-modified: %*s\"",
0579                        len, pos);
0580 
0581         /*
0582          * Date will always be encoded using huffman in the temporary buffer,
0583          * so it's safe here to use src and dst pointing to the same address.
0584          */
0585         pos = ngx_http_v2_write_value(pos, pos, len, tmp);
0586     }
0587 
0588     if (r->headers_out.location && r->headers_out.location->value.len) {
0589         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0590                        "http2 output header: \"location: %V\"",
0591                        &r->headers_out.location->value);
0592 
0593         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
0594         pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,
0595                                       r->headers_out.location->value.len, tmp);
0596     }
0597 
0598 #if (NGX_HTTP_GZIP)
0599     if (r->gzip_vary) {
0600         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0601                        "http2 output header: \"vary: Accept-Encoding\"");
0602 
0603         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
0604         pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));
0605     }
0606 #endif
0607 
0608     part = &r->headers_out.headers.part;
0609     header = part->elts;
0610 
0611     for (i = 0; /* void */; i++) {
0612 
0613         if (i >= part->nelts) {
0614             if (part->next == NULL) {
0615                 break;
0616             }
0617 
0618             part = part->next;
0619             header = part->elts;
0620             i = 0;
0621         }
0622 
0623         if (header[i].hash == 0) {
0624             continue;
0625         }
0626 
0627 #if (NGX_DEBUG)
0628         if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
0629             ngx_strlow(tmp, header[i].key.data, header[i].key.len);
0630 
0631             ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
0632                            "http2 output header: \"%*s: %V\"",
0633                            header[i].key.len, tmp, &header[i].value);
0634         }
0635 #endif
0636 
0637         *pos++ = 0;
0638 
0639         pos = ngx_http_v2_write_name(pos, header[i].key.data,
0640                                      header[i].key.len, tmp);
0641 
0642         pos = ngx_http_v2_write_value(pos, header[i].value.data,
0643                                       header[i].value.len, tmp);
0644     }
0645 
0646     fin = r->header_only
0647           || (r->headers_out.content_length_n == 0 && !r->expect_trailers);
0648 
0649     frame = ngx_http_v2_create_headers_frame(r, start, pos, fin);
0650     if (frame == NULL) {
0651         return NGX_ERROR;
0652     }
0653 
0654     ngx_http_v2_queue_blocked_frame(h2c, frame);
0655 
0656     stream->queued++;
0657 
0658     cln = ngx_http_cleanup_add(r, 0);
0659     if (cln == NULL) {
0660         return NGX_ERROR;
0661     }
0662 
0663     cln->handler = ngx_http_v2_filter_cleanup;
0664     cln->data = stream;
0665 
0666     fc->send_chain = ngx_http_v2_send_chain;
0667     fc->need_last_buf = 1;
0668 
0669     return ngx_http_v2_filter_send(fc, stream);
0670 }
0671 
0672 
0673 static ngx_int_t
0674 ngx_http_v2_push_resources(ngx_http_request_t *r)
0675 {
0676     u_char                     *start, *end, *last;
0677     ngx_int_t                   rc;
0678     ngx_str_t                   path;
0679     ngx_uint_t                  i, push;
0680     ngx_table_elt_t           **h;
0681     ngx_http_v2_loc_conf_t     *h2lcf;
0682     ngx_http_complex_value_t   *pushes;
0683     ngx_str_t                   binary[NGX_HTTP_V2_PUSH_HEADERS];
0684 
0685     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0686                    "http2 push resources");
0687 
0688     ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));
0689 
0690     h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
0691 
0692     if (h2lcf->pushes) {
0693         pushes = h2lcf->pushes->elts;
0694 
0695         for (i = 0; i < h2lcf->pushes->nelts; i++) {
0696 
0697             if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) {
0698                 return NGX_ERROR;
0699             }
0700 
0701             if (path.len == 0) {
0702                 continue;
0703             }
0704 
0705             if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) {
0706                 continue;
0707             }
0708 
0709             rc = ngx_http_v2_push_resource(r, &path, binary);
0710 
0711             if (rc == NGX_ERROR) {
0712                 return NGX_ERROR;
0713             }
0714 
0715             if (rc == NGX_ABORT) {
0716                 return NGX_OK;
0717             }
0718 
0719             /* NGX_OK, NGX_DECLINED */
0720         }
0721     }
0722 
0723     if (!h2lcf->push_preload) {
0724         return NGX_OK;
0725     }
0726 
0727     h = r->headers_out.link.elts;
0728 
0729     for (i = 0; i < r->headers_out.link.nelts; i++) {
0730 
0731         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0732                        "http2 parse link: \"%V\"", &h[i]->value);
0733 
0734         start = h[i]->value.data;
0735         end = h[i]->value.data + h[i]->value.len;
0736 
0737     next_link:
0738 
0739         while (start < end && *start == ' ') { start++; }
0740 
0741         if (start == end || *start++ != '<') {
0742             continue;
0743         }
0744 
0745         while (start < end && *start == ' ') { start++; }
0746 
0747         for (last = start; last < end && *last != '>'; last++) {
0748             /* void */
0749         }
0750 
0751         if (last == start || last == end) {
0752             continue;
0753         }
0754 
0755         path.len = last - start;
0756         path.data = start;
0757 
0758         start = last + 1;
0759 
0760         while (start < end && *start == ' ') { start++; }
0761 
0762         if (start == end) {
0763             continue;
0764         }
0765 
0766         if (*start == ',') {
0767             start++;
0768             goto next_link;
0769         }
0770 
0771         if (*start++ != ';') {
0772             continue;
0773         }
0774 
0775         last = ngx_strlchr(start, end, ',');
0776 
0777         if (last == NULL) {
0778             last = end;
0779         }
0780 
0781         push = 0;
0782 
0783         for ( ;; ) {
0784 
0785             while (start < last && *start == ' ') { start++; }
0786 
0787             if (last - start >= 6
0788                 && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0)
0789             {
0790                 start += 6;
0791 
0792                 if (start == last || *start == ' ' || *start == ';') {
0793                     push = 0;
0794                     break;
0795                 }
0796 
0797                 goto next_param;
0798             }
0799 
0800             if (last - start >= 11
0801                 && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0)
0802             {
0803                 start += 11;
0804 
0805                 if (start == last || *start == ' ' || *start == ';') {
0806                     push = 1;
0807                 }
0808 
0809                 goto next_param;
0810             }
0811 
0812             if (last - start >= 4
0813                 && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0)
0814             {
0815                 start += 4;
0816 
0817                 while (start < last && *start == ' ') { start++; }
0818 
0819                 if (start == last || *start++ != '"') {
0820                     goto next_param;
0821                 }
0822 
0823                 for ( ;; ) {
0824 
0825                     while (start < last && *start == ' ') { start++; }
0826 
0827                     if (last - start >= 7
0828                         && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0)
0829                     {
0830                         start += 7;
0831 
0832                         if (start < last && (*start == ' ' || *start == '"')) {
0833                             push = 1;
0834                             break;
0835                         }
0836                     }
0837 
0838                     while (start < last && *start != ' ' && *start != '"') {
0839                         start++;
0840                     }
0841 
0842                     if (start == last) {
0843                         break;
0844                     }
0845 
0846                     if (*start == '"') {
0847                         break;
0848                     }
0849 
0850                     start++;
0851                 }
0852             }
0853 
0854         next_param:
0855 
0856             start = ngx_strlchr(start, last, ';');
0857 
0858             if (start == NULL) {
0859                 break;
0860             }
0861 
0862             start++;
0863         }
0864 
0865         if (push) {
0866             while (path.len && path.data[path.len - 1] == ' ') {
0867                 path.len--;
0868             }
0869         }
0870 
0871         if (push && path.len
0872             && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))
0873         {
0874             rc = ngx_http_v2_push_resource(r, &path, binary);
0875 
0876             if (rc == NGX_ERROR) {
0877                 return NGX_ERROR;
0878             }
0879 
0880             if (rc == NGX_ABORT) {
0881                 return NGX_OK;
0882             }
0883 
0884             /* NGX_OK, NGX_DECLINED */
0885         }
0886 
0887         if (last < end) {
0888             start = last + 1;
0889             goto next_link;
0890         }
0891     }
0892 
0893     return NGX_OK;
0894 }
0895 
0896 
0897 static ngx_int_t
0898 ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
0899     ngx_str_t *binary)
0900 {
0901     u_char                      *start, *pos, *tmp;
0902     size_t                       len;
0903     ngx_str_t                   *value;
0904     ngx_uint_t                   i;
0905     ngx_table_elt_t            **h;
0906     ngx_connection_t            *fc;
0907     ngx_http_v2_stream_t        *stream;
0908     ngx_http_v2_out_frame_t     *frame;
0909     ngx_http_v2_connection_t    *h2c;
0910     ngx_http_v2_push_header_t   *ph;
0911 
0912     fc = r->connection;
0913 
0914     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource");
0915 
0916     stream = r->stream;
0917     h2c = stream->connection;
0918 
0919     if (!ngx_path_separator(path->data[0])) {
0920         ngx_log_error(NGX_LOG_WARN, fc->log, 0,
0921                       "non-absolute path \"%V\" not pushed", path);
0922         return NGX_DECLINED;
0923     }
0924 
0925     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0926                    "http2 pushing:%ui limit:%ui",
0927                    h2c->pushing, h2c->concurrent_pushes);
0928 
0929     if (h2c->pushing >= h2c->concurrent_pushes) {
0930         return NGX_ABORT;
0931     }
0932 
0933     if (h2c->last_push == 0x7ffffffe) {
0934         return NGX_ABORT;
0935     }
0936 
0937     if (path->len > NGX_HTTP_V2_MAX_FIELD) {
0938         return NGX_DECLINED;
0939     }
0940 
0941     if (r->headers_in.host == NULL) {
0942         return NGX_ABORT;
0943     }
0944 
0945     ph = ngx_http_v2_push_headers;
0946 
0947     if (binary[0].len) {
0948         tmp = ngx_palloc(r->pool, path->len);
0949         if (tmp == NULL) {
0950             return NGX_ERROR;
0951         }
0952 
0953     } else {
0954         len = path->len;
0955 
0956         for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
0957             h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
0958 
0959             if (*h) {
0960                 len = ngx_max(len, (*h)->value.len);
0961             }
0962         }
0963 
0964         tmp = ngx_palloc(r->pool, len);
0965         if (tmp == NULL) {
0966             return NGX_ERROR;
0967         }
0968 
0969         for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
0970             h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
0971 
0972             if (*h == NULL) {
0973                 continue;
0974             }
0975 
0976             value = &(*h)->value;
0977 
0978             len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;
0979 
0980             pos = ngx_pnalloc(r->pool, len);
0981             if (pos == NULL) {
0982                 return NGX_ERROR;
0983             }
0984 
0985             binary[i].data = pos;
0986 
0987             *pos++ = ngx_http_v2_inc_indexed(ph[i].index);
0988             pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);
0989 
0990             binary[i].len = pos - binary[i].data;
0991         }
0992     }
0993 
0994     len = (h2c->table_update ? 1 : 0)
0995           + 1
0996           + 1 + NGX_HTTP_V2_INT_OCTETS + path->len
0997           + 1;
0998 
0999     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
1000         len += binary[i].len;
1001     }
1002 
1003     pos = ngx_pnalloc(r->pool, len);
1004     if (pos == NULL) {
1005         return NGX_ERROR;
1006     }
1007 
1008     start = pos;
1009 
1010     if (h2c->table_update) {
1011         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1012                        "http2 table size update: 0");
1013         *pos++ = (1 << 5) | 0;
1014         h2c->table_update = 0;
1015     }
1016 
1017     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1018                    "http2 push header: \":method: GET\"");
1019 
1020     *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
1021 
1022     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1023                    "http2 push header: \":path: %V\"", path);
1024 
1025     *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
1026     pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
1027 
1028 #if (NGX_HTTP_SSL)
1029     if (fc->ssl) {
1030         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1031                        "http2 push header: \":scheme: https\"");
1032         *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
1033 
1034     } else
1035 #endif
1036     {
1037         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1038                        "http2 push header: \":scheme: http\"");
1039         *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
1040     }
1041 
1042     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
1043         h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
1044 
1045         if (*h == NULL) {
1046             continue;
1047         }
1048 
1049         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1050                        "http2 push header: \"%V: %V\"",
1051                        &ph[i].name, &(*h)->value);
1052 
1053         pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
1054     }
1055 
1056     frame = ngx_http_v2_create_push_frame(r, start, pos);
1057     if (frame == NULL) {
1058         return NGX_ERROR;
1059     }
1060 
1061     ngx_http_v2_queue_blocked_frame(h2c, frame);
1062 
1063     stream->queued++;
1064 
1065     stream = ngx_http_v2_push_stream(stream, path);
1066 
1067     if (stream) {
1068         stream->request->request_length = pos - start;
1069         return NGX_OK;
1070     }
1071 
1072     return NGX_ERROR;
1073 }
1074 
1075 
1076 static ngx_http_v2_out_frame_t *
1077 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
1078     u_char *end, ngx_uint_t fin)
1079 {
1080     u_char                    type, flags;
1081     size_t                    rest, frame_size;
1082     ngx_buf_t                *b;
1083     ngx_chain_t              *cl, **ll;
1084     ngx_http_v2_stream_t     *stream;
1085     ngx_http_v2_out_frame_t  *frame;
1086 
1087     stream = r->stream;
1088     rest = end - pos;
1089 
1090     frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
1091     if (frame == NULL) {
1092         return NULL;
1093     }
1094 
1095     frame->handler = ngx_http_v2_headers_frame_handler;
1096     frame->stream = stream;
1097     frame->length = rest;
1098     frame->blocked = 1;
1099     frame->fin = fin;
1100 
1101     ll = &frame->first;
1102 
1103     type = NGX_HTTP_V2_HEADERS_FRAME;
1104     flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
1105     frame_size = stream->connection->frame_size;
1106 
1107     for ( ;; ) {
1108         if (rest <= frame_size) {
1109             frame_size = rest;
1110             flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
1111         }
1112 
1113         b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);
1114         if (b == NULL) {
1115             return NULL;
1116         }
1117 
1118         b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
1119         *b->last++ = flags;
1120         b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
1121 
1122         b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1123 
1124         cl = ngx_alloc_chain_link(r->pool);
1125         if (cl == NULL) {
1126             return NULL;
1127         }
1128 
1129         cl->buf = b;
1130 
1131         *ll = cl;
1132         ll = &cl->next;
1133 
1134         b = ngx_calloc_buf(r->pool);
1135         if (b == NULL) {
1136             return NULL;
1137         }
1138 
1139         b->pos = pos;
1140 
1141         pos += frame_size;
1142 
1143         b->last = pos;
1144         b->start = b->pos;
1145         b->end = b->last;
1146         b->temporary = 1;
1147 
1148         cl = ngx_alloc_chain_link(r->pool);
1149         if (cl == NULL) {
1150             return NULL;
1151         }
1152 
1153         cl->buf = b;
1154 
1155         *ll = cl;
1156         ll = &cl->next;
1157 
1158         rest -= frame_size;
1159 
1160         if (rest) {
1161             frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1162 
1163             type = NGX_HTTP_V2_CONTINUATION_FRAME;
1164             flags = NGX_HTTP_V2_NO_FLAG;
1165             continue;
1166         }
1167 
1168         b->last_buf = fin;
1169         cl->next = NULL;
1170         frame->last = cl;
1171 
1172         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1173                        "http2:%ui create HEADERS frame %p: len:%uz fin:%ui",
1174                        stream->node->id, frame, frame->length, fin);
1175 
1176         return frame;
1177     }
1178 }
1179 
1180 
1181 static ngx_http_v2_out_frame_t *
1182 ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)
1183 {
1184     u_char                     type, flags;
1185     size_t                     rest, frame_size, len;
1186     ngx_buf_t                 *b;
1187     ngx_chain_t               *cl, **ll;
1188     ngx_http_v2_stream_t      *stream;
1189     ngx_http_v2_out_frame_t   *frame;
1190     ngx_http_v2_connection_t  *h2c;
1191 
1192     stream = r->stream;
1193     h2c = stream->connection;
1194     rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);
1195 
1196     frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
1197     if (frame == NULL) {
1198         return NULL;
1199     }
1200 
1201     frame->handler = ngx_http_v2_push_frame_handler;
1202     frame->stream = stream;
1203     frame->length = rest;
1204     frame->blocked = 1;
1205     frame->fin = 0;
1206 
1207     ll = &frame->first;
1208 
1209     type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;
1210     flags = NGX_HTTP_V2_NO_FLAG;
1211     frame_size = h2c->frame_size;
1212 
1213     for ( ;; ) {
1214         if (rest <= frame_size) {
1215             frame_size = rest;
1216             flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
1217         }
1218 
1219         b = ngx_create_temp_buf(r->pool,
1220                                 NGX_HTTP_V2_FRAME_HEADER_SIZE
1221                                 + ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)
1222                                    ? NGX_HTTP_V2_STREAM_ID_SIZE : 0));
1223         if (b == NULL) {
1224             return NULL;
1225         }
1226 
1227         b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
1228         *b->last++ = flags;
1229         b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
1230 
1231         b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1232 
1233         if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
1234             h2c->last_push += 2;
1235 
1236             b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);
1237             len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;
1238 
1239         } else {
1240             len = frame_size;
1241         }
1242 
1243         cl = ngx_alloc_chain_link(r->pool);
1244         if (cl == NULL) {
1245             return NULL;
1246         }
1247 
1248         cl->buf = b;
1249 
1250         *ll = cl;
1251         ll = &cl->next;
1252 
1253         b = ngx_calloc_buf(r->pool);
1254         if (b == NULL) {
1255             return NULL;
1256         }
1257 
1258         b->pos = pos;
1259 
1260         pos += len;
1261 
1262         b->last = pos;
1263         b->start = b->pos;
1264         b->end = b->last;
1265         b->temporary = 1;
1266 
1267         cl = ngx_alloc_chain_link(r->pool);
1268         if (cl == NULL) {
1269             return NULL;
1270         }
1271 
1272         cl->buf = b;
1273 
1274         *ll = cl;
1275         ll = &cl->next;
1276 
1277         rest -= frame_size;
1278 
1279         if (rest) {
1280             frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1281 
1282             type = NGX_HTTP_V2_CONTINUATION_FRAME;
1283             continue;
1284         }
1285 
1286         cl->next = NULL;
1287         frame->last = cl;
1288 
1289         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1290                        "http2:%ui create PUSH_PROMISE frame %p: "
1291                        "sid:%ui len:%uz",
1292                        stream->node->id, frame, h2c->last_push,
1293                        frame->length);
1294 
1295         return frame;
1296     }
1297 }
1298 
1299 
1300 static ngx_http_v2_out_frame_t *
1301 ngx_http_v2_create_trailers_frame(ngx_http_request_t *r)
1302 {
1303     u_char            *pos, *start, *tmp;
1304     size_t             len, tmp_len;
1305     ngx_uint_t         i;
1306     ngx_list_part_t   *part;
1307     ngx_table_elt_t   *header;
1308     ngx_connection_t  *fc;
1309 
1310     fc = r->connection;
1311     len = 0;
1312     tmp_len = 0;
1313 
1314     part = &r->headers_out.trailers.part;
1315     header = part->elts;
1316 
1317     for (i = 0; /* void */; i++) {
1318 
1319         if (i >= part->nelts) {
1320             if (part->next == NULL) {
1321                 break;
1322             }
1323 
1324             part = part->next;
1325             header = part->elts;
1326             i = 0;
1327         }
1328 
1329         if (header[i].hash == 0) {
1330             continue;
1331         }
1332 
1333         if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
1334             ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
1335                           "too long response trailer name: \"%V\"",
1336                           &header[i].key);
1337             return NULL;
1338         }
1339 
1340         if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
1341             ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
1342                           "too long response trailer value: \"%V: %V\"",
1343                           &header[i].key, &header[i].value);
1344             return NULL;
1345         }
1346 
1347         len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
1348                  + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
1349 
1350         if (header[i].key.len > tmp_len) {
1351             tmp_len = header[i].key.len;
1352         }
1353 
1354         if (header[i].value.len > tmp_len) {
1355             tmp_len = header[i].value.len;
1356         }
1357     }
1358 
1359     if (len == 0) {
1360         return NGX_HTTP_V2_NO_TRAILERS;
1361     }
1362 
1363     tmp = ngx_palloc(r->pool, tmp_len);
1364     pos = ngx_pnalloc(r->pool, len);
1365 
1366     if (pos == NULL || tmp == NULL) {
1367         return NULL;
1368     }
1369 
1370     start = pos;
1371 
1372     part = &r->headers_out.trailers.part;
1373     header = part->elts;
1374 
1375     for (i = 0; /* void */; i++) {
1376 
1377         if (i >= part->nelts) {
1378             if (part->next == NULL) {
1379                 break;
1380             }
1381 
1382             part = part->next;
1383             header = part->elts;
1384             i = 0;
1385         }
1386 
1387         if (header[i].hash == 0) {
1388             continue;
1389         }
1390 
1391 #if (NGX_DEBUG)
1392         if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
1393             ngx_strlow(tmp, header[i].key.data, header[i].key.len);
1394 
1395             ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1396                            "http2 output trailer: \"%*s: %V\"",
1397                            header[i].key.len, tmp, &header[i].value);
1398         }
1399 #endif
1400 
1401         *pos++ = 0;
1402 
1403         pos = ngx_http_v2_write_name(pos, header[i].key.data,
1404                                      header[i].key.len, tmp);
1405 
1406         pos = ngx_http_v2_write_value(pos, header[i].value.data,
1407                                       header[i].value.len, tmp);
1408     }
1409 
1410     return ngx_http_v2_create_headers_frame(r, start, pos, 1);
1411 }
1412 
1413 
1414 static ngx_chain_t *
1415 ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
1416 {
1417     off_t                      size, offset;
1418     size_t                     rest, frame_size;
1419     ngx_chain_t               *cl, *out, **ln;
1420     ngx_http_request_t        *r;
1421     ngx_http_v2_stream_t      *stream;
1422     ngx_http_v2_loc_conf_t    *h2lcf;
1423     ngx_http_v2_out_frame_t   *frame, *trailers;
1424     ngx_http_v2_connection_t  *h2c;
1425 
1426     r = fc->data;
1427     stream = r->stream;
1428 
1429 #if (NGX_SUPPRESS_WARN)
1430     size = 0;
1431 #endif
1432 
1433     while (in) {
1434         size = ngx_buf_size(in->buf);
1435 
1436         if (size || in->buf->last_buf) {
1437             break;
1438         }
1439 
1440         in = in->next;
1441     }
1442 
1443     if (in == NULL || stream->out_closed) {
1444 
1445         if (stream->queued) {
1446             fc->write->active = 1;
1447             fc->write->ready = 0;
1448 
1449         } else {
1450             fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1451         }
1452 
1453         return NULL;
1454     }
1455 
1456     h2c = stream->connection;
1457 
1458     if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1459         fc->write->active = 1;
1460         fc->write->ready = 0;
1461         return in;
1462     }
1463 
1464     if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1465         cl = ngx_alloc_chain_link(r->pool);
1466         if (cl == NULL) {
1467             return NGX_CHAIN_ERROR;
1468         }
1469 
1470         cl->buf = in->buf;
1471         in->buf = cl->buf->shadow;
1472 
1473         offset = ngx_buf_in_memory(in->buf)
1474                  ? (cl->buf->pos - in->buf->pos)
1475                  : (cl->buf->file_pos - in->buf->file_pos);
1476 
1477         cl->next = stream->free_bufs;
1478         stream->free_bufs = cl;
1479 
1480     } else {
1481         offset = 0;
1482     }
1483 
1484     if (limit == 0 || limit > (off_t) h2c->send_window) {
1485         limit = h2c->send_window;
1486     }
1487 
1488     if (limit > stream->send_window) {
1489         limit = (stream->send_window > 0) ? stream->send_window : 0;
1490     }
1491 
1492     h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
1493 
1494     frame_size = (h2lcf->chunk_size < h2c->frame_size)
1495                  ? h2lcf->chunk_size : h2c->frame_size;
1496 
1497     trailers = NGX_HTTP_V2_NO_TRAILERS;
1498 
1499 #if (NGX_SUPPRESS_WARN)
1500     cl = NULL;
1501 #endif
1502 
1503     for ( ;; ) {
1504         if ((off_t) frame_size > limit) {
1505             frame_size = (size_t) limit;
1506         }
1507 
1508         ln = &out;
1509         rest = frame_size;
1510 
1511         while ((off_t) rest >= size) {
1512 
1513             if (offset) {
1514                 cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
1515                                                    offset, size);
1516                 if (cl == NULL) {
1517                     return NGX_CHAIN_ERROR;
1518                 }
1519 
1520                 offset = 0;
1521 
1522             } else {
1523                 cl = ngx_alloc_chain_link(r->pool);
1524                 if (cl == NULL) {
1525                     return NGX_CHAIN_ERROR;
1526                 }
1527 
1528                 cl->buf = in->buf;
1529             }
1530 
1531             *ln = cl;
1532             ln = &cl->next;
1533 
1534             rest -= (size_t) size;
1535             in = in->next;
1536 
1537             if (in == NULL) {
1538                 frame_size -= rest;
1539                 rest = 0;
1540                 break;
1541             }
1542 
1543             size = ngx_buf_size(in->buf);
1544         }
1545 
1546         if (rest) {
1547             cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
1548             if (cl == NULL) {
1549                 return NGX_CHAIN_ERROR;
1550             }
1551 
1552             cl->buf->flush = 0;
1553             cl->buf->last_buf = 0;
1554 
1555             *ln = cl;
1556 
1557             offset += rest;
1558             size -= rest;
1559         }
1560 
1561         if (cl->buf->last_buf) {
1562             trailers = ngx_http_v2_create_trailers_frame(r);
1563             if (trailers == NULL) {
1564                 return NGX_CHAIN_ERROR;
1565             }
1566 
1567             if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
1568                 cl->buf->last_buf = 0;
1569             }
1570         }
1571 
1572         if (frame_size || cl->buf->last_buf) {
1573             frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,
1574                                                       out, cl);
1575             if (frame == NULL) {
1576                 return NGX_CHAIN_ERROR;
1577             }
1578 
1579             ngx_http_v2_queue_frame(h2c, frame);
1580 
1581             h2c->send_window -= frame_size;
1582 
1583             stream->send_window -= frame_size;
1584             stream->queued++;
1585         }
1586 
1587         if (in == NULL) {
1588 
1589             if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
1590                 ngx_http_v2_queue_frame(h2c, trailers);
1591                 stream->queued++;
1592             }
1593 
1594             break;
1595         }
1596 
1597         limit -= frame_size;
1598 
1599         if (limit == 0) {
1600             break;
1601         }
1602     }
1603 
1604     if (offset) {
1605         cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
1606         if (cl == NULL) {
1607             return NGX_CHAIN_ERROR;
1608         }
1609 
1610         in->buf = cl->buf;
1611         ngx_free_chain(r->pool, cl);
1612     }
1613 
1614     if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
1615         return NGX_CHAIN_ERROR;
1616     }
1617 
1618     if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1619         fc->write->active = 1;
1620         fc->write->ready = 0;
1621     }
1622 
1623     return in;
1624 }
1625 
1626 
1627 static ngx_chain_t *
1628 ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
1629     off_t offset, off_t size)
1630 {
1631     ngx_buf_t    *chunk;
1632     ngx_chain_t  *cl;
1633 
1634     cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
1635     if (cl == NULL) {
1636         return NULL;
1637     }
1638 
1639     chunk = cl->buf;
1640 
1641     ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
1642 
1643     chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
1644     chunk->shadow = buf;
1645 
1646     if (ngx_buf_in_memory(chunk)) {
1647         chunk->pos += offset;
1648         chunk->last = chunk->pos + size;
1649     }
1650 
1651     if (chunk->in_file) {
1652         chunk->file_pos += offset;
1653         chunk->file_last = chunk->file_pos + size;
1654     }
1655 
1656     return cl;
1657 }
1658 
1659 
1660 static ngx_http_v2_out_frame_t *
1661 ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
1662     size_t len, ngx_chain_t *first, ngx_chain_t *last)
1663 {
1664     u_char                    flags;
1665     ngx_buf_t                *buf;
1666     ngx_chain_t              *cl;
1667     ngx_http_v2_out_frame_t  *frame;
1668 
1669     frame = stream->free_frames;
1670 
1671     if (frame) {
1672         stream->free_frames = frame->next;
1673 
1674     } else {
1675         frame = ngx_palloc(stream->request->pool,
1676                            sizeof(ngx_http_v2_out_frame_t));
1677         if (frame == NULL) {
1678             return NULL;
1679         }
1680     }
1681 
1682     flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
1683 
1684     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
1685                    "http2:%ui create DATA frame %p: len:%uz flags:%ui",
1686                    stream->node->id, frame, len, (ngx_uint_t) flags);
1687 
1688     cl = ngx_chain_get_free_buf(stream->request->pool,
1689                                 &stream->free_frame_headers);
1690     if (cl == NULL) {
1691         return NULL;
1692     }
1693 
1694     buf = cl->buf;
1695 
1696     if (buf->start == NULL) {
1697         buf->start = ngx_palloc(stream->request->pool,
1698                                 NGX_HTTP_V2_FRAME_HEADER_SIZE);
1699         if (buf->start == NULL) {
1700             return NULL;
1701         }
1702 
1703         buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
1704         buf->last = buf->end;
1705 
1706         buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1707         buf->memory = 1;
1708     }
1709 
1710     buf->pos = buf->start;
1711     buf->last = buf->pos;
1712 
1713     buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
1714                                                NGX_HTTP_V2_DATA_FRAME);
1715     *buf->last++ = flags;
1716 
1717     buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
1718 
1719     cl->next = first;
1720     first = cl;
1721 
1722     last->buf->flush = 1;
1723 
1724     frame->first = first;
1725     frame->last = last;
1726     frame->handler = ngx_http_v2_data_frame_handler;
1727     frame->stream = stream;
1728     frame->length = len;
1729     frame->blocked = 0;
1730     frame->fin = last->buf->last_buf;
1731 
1732     return frame;
1733 }
1734 
1735 
1736 static ngx_inline ngx_int_t
1737 ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
1738     ngx_http_v2_stream_t *stream)
1739 {
1740     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1741                    "http2:%ui windows: conn:%uz stream:%z",
1742                    stream->node->id, h2c->send_window, stream->send_window);
1743 
1744     if (stream->send_window <= 0) {
1745         stream->exhausted = 1;
1746         return NGX_DECLINED;
1747     }
1748 
1749     if (h2c->send_window == 0) {
1750         ngx_http_v2_waiting_queue(h2c, stream);
1751         return NGX_DECLINED;
1752     }
1753 
1754     return NGX_OK;
1755 }
1756 
1757 
1758 static void
1759 ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
1760     ngx_http_v2_stream_t *stream)
1761 {
1762     ngx_queue_t           *q;
1763     ngx_http_v2_stream_t  *s;
1764 
1765     if (stream->waiting) {
1766         return;
1767     }
1768 
1769     stream->waiting = 1;
1770 
1771     for (q = ngx_queue_last(&h2c->waiting);
1772          q != ngx_queue_sentinel(&h2c->waiting);
1773          q = ngx_queue_prev(q))
1774     {
1775         s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
1776 
1777         if (s->node->rank < stream->node->rank
1778             || (s->node->rank == stream->node->rank
1779                 && s->node->rel_weight >= stream->node->rel_weight))
1780         {
1781             break;
1782         }
1783     }
1784 
1785     ngx_queue_insert_after(q, &stream->queue);
1786 }
1787 
1788 
1789 static ngx_inline ngx_int_t
1790 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
1791 {
1792     stream->blocked = 1;
1793 
1794     if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
1795         fc->error = 1;
1796         return NGX_ERROR;
1797     }
1798 
1799     stream->blocked = 0;
1800 
1801     if (stream->queued) {
1802         fc->buffered |= NGX_HTTP_V2_BUFFERED;
1803         fc->write->active = 1;
1804         fc->write->ready = 0;
1805         return NGX_AGAIN;
1806     }
1807 
1808     fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1809 
1810     return NGX_OK;
1811 }
1812 
1813 
1814 static ngx_int_t
1815 ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
1816     ngx_http_v2_out_frame_t *frame)
1817 {
1818     ngx_chain_t           *cl, *ln;
1819     ngx_http_v2_stream_t  *stream;
1820 
1821     stream = frame->stream;
1822     cl = frame->first;
1823 
1824     for ( ;; ) {
1825         if (cl->buf->pos != cl->buf->last) {
1826             frame->first = cl;
1827 
1828             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1829                            "http2:%ui HEADERS frame %p was sent partially",
1830                            stream->node->id, frame);
1831 
1832             return NGX_AGAIN;
1833         }
1834 
1835         ln = cl->next;
1836 
1837         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1838             cl->next = stream->free_frame_headers;
1839             stream->free_frame_headers = cl;
1840 
1841         } else {
1842             cl->next = stream->free_bufs;
1843             stream->free_bufs = cl;
1844         }
1845 
1846         if (cl == frame->last) {
1847             break;
1848         }
1849 
1850         cl = ln;
1851     }
1852 
1853     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1854                    "http2:%ui HEADERS frame %p was sent",
1855                    stream->node->id, frame);
1856 
1857     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1858                                     + frame->length;
1859 
1860     ngx_http_v2_handle_frame(stream, frame);
1861 
1862     ngx_http_v2_handle_stream(h2c, stream);
1863 
1864     return NGX_OK;
1865 }
1866 
1867 
1868 static ngx_int_t
1869 ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
1870     ngx_http_v2_out_frame_t *frame)
1871 {
1872     ngx_chain_t           *cl, *ln;
1873     ngx_http_v2_stream_t  *stream;
1874 
1875     stream = frame->stream;
1876     cl = frame->first;
1877 
1878     for ( ;; ) {
1879         if (cl->buf->pos != cl->buf->last) {
1880             frame->first = cl;
1881 
1882             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1883                            "http2:%ui PUSH_PROMISE frame %p was sent partially",
1884                            stream->node->id, frame);
1885 
1886             return NGX_AGAIN;
1887         }
1888 
1889         ln = cl->next;
1890 
1891         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1892             cl->next = stream->free_frame_headers;
1893             stream->free_frame_headers = cl;
1894 
1895         } else {
1896             cl->next = stream->free_bufs;
1897             stream->free_bufs = cl;
1898         }
1899 
1900         if (cl == frame->last) {
1901             break;
1902         }
1903 
1904         cl = ln;
1905     }
1906 
1907     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1908                    "http2:%ui PUSH_PROMISE frame %p was sent",
1909                    stream->node->id, frame);
1910 
1911     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1912                                     + frame->length;
1913 
1914     ngx_http_v2_handle_frame(stream, frame);
1915 
1916     ngx_http_v2_handle_stream(h2c, stream);
1917 
1918     return NGX_OK;
1919 }
1920 
1921 
1922 static ngx_int_t
1923 ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
1924     ngx_http_v2_out_frame_t *frame)
1925 {
1926     ngx_buf_t             *buf;
1927     ngx_chain_t           *cl, *ln;
1928     ngx_http_v2_stream_t  *stream;
1929 
1930     stream = frame->stream;
1931     cl = frame->first;
1932 
1933     if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1934 
1935         if (cl->buf->pos != cl->buf->last) {
1936             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1937                            "http2:%ui DATA frame %p was sent partially",
1938                            stream->node->id, frame);
1939 
1940             return NGX_AGAIN;
1941         }
1942 
1943         ln = cl->next;
1944 
1945         cl->next = stream->free_frame_headers;
1946         stream->free_frame_headers = cl;
1947 
1948         if (cl == frame->last) {
1949             goto done;
1950         }
1951 
1952         cl = ln;
1953     }
1954 
1955     for ( ;; ) {
1956         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1957             buf = cl->buf->shadow;
1958 
1959             if (ngx_buf_in_memory(buf)) {
1960                 buf->pos = cl->buf->pos;
1961             }
1962 
1963             if (buf->in_file) {
1964                 buf->file_pos = cl->buf->file_pos;
1965             }
1966         }
1967 
1968         if (ngx_buf_size(cl->buf) != 0) {
1969 
1970             if (cl != frame->first) {
1971                 frame->first = cl;
1972                 ngx_http_v2_handle_stream(h2c, stream);
1973             }
1974 
1975             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1976                            "http2:%ui DATA frame %p was sent partially",
1977                            stream->node->id, frame);
1978 
1979             return NGX_AGAIN;
1980         }
1981 
1982         ln = cl->next;
1983 
1984         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1985             cl->next = stream->free_bufs;
1986             stream->free_bufs = cl;
1987 
1988         } else {
1989             ngx_free_chain(stream->request->pool, cl);
1990         }
1991 
1992         if (cl == frame->last) {
1993             goto done;
1994         }
1995 
1996         cl = ln;
1997     }
1998 
1999 done:
2000 
2001     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
2002                    "http2:%ui DATA frame %p was sent",
2003                    stream->node->id, frame);
2004 
2005     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
2006 
2007     ngx_http_v2_handle_frame(stream, frame);
2008 
2009     ngx_http_v2_handle_stream(h2c, stream);
2010 
2011     return NGX_OK;
2012 }
2013 
2014 
2015 static ngx_inline void
2016 ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
2017     ngx_http_v2_out_frame_t *frame)
2018 {
2019     ngx_http_request_t  *r;
2020 
2021     r = stream->request;
2022 
2023     r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
2024 
2025     if (frame->fin) {
2026         stream->out_closed = 1;
2027     }
2028 
2029     frame->next = stream->free_frames;
2030     stream->free_frames = frame;
2031 
2032     stream->queued--;
2033 }
2034 
2035 
2036 static ngx_inline void
2037 ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
2038     ngx_http_v2_stream_t *stream)
2039 {
2040     ngx_event_t       *wev;
2041     ngx_connection_t  *fc;
2042 
2043     if (stream->waiting || stream->blocked) {
2044         return;
2045     }
2046 
2047     fc = stream->request->connection;
2048 
2049     if (!fc->error && stream->exhausted) {
2050         return;
2051     }
2052 
2053     wev = fc->write;
2054 
2055     wev->active = 0;
2056     wev->ready = 1;
2057 
2058     if (!fc->error && wev->delayed) {
2059         return;
2060     }
2061 
2062     ngx_post_event(wev, &ngx_posted_events);
2063 }
2064 
2065 
2066 static void
2067 ngx_http_v2_filter_cleanup(void *data)
2068 {
2069     ngx_http_v2_stream_t *stream = data;
2070 
2071     size_t                     window;
2072     ngx_event_t               *wev;
2073     ngx_queue_t               *q;
2074     ngx_http_v2_out_frame_t   *frame, **fn;
2075     ngx_http_v2_connection_t  *h2c;
2076 
2077     if (stream->waiting) {
2078         stream->waiting = 0;
2079         ngx_queue_remove(&stream->queue);
2080     }
2081 
2082     if (stream->queued == 0) {
2083         return;
2084     }
2085 
2086     window = 0;
2087     h2c = stream->connection;
2088     fn = &h2c->last_out;
2089 
2090     for ( ;; ) {
2091         frame = *fn;
2092 
2093         if (frame == NULL) {
2094             break;
2095         }
2096 
2097         if (frame->stream == stream && !frame->blocked) {
2098             *fn = frame->next;
2099 
2100             window += frame->length;
2101 
2102             if (--stream->queued == 0) {
2103                 break;
2104             }
2105 
2106             continue;
2107         }
2108 
2109         fn = &frame->next;
2110     }
2111 
2112     if (h2c->send_window == 0 && window) {
2113 
2114         while (!ngx_queue_empty(&h2c->waiting)) {
2115             q = ngx_queue_head(&h2c->waiting);
2116 
2117             ngx_queue_remove(q);
2118 
2119             stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
2120 
2121             stream->waiting = 0;
2122 
2123             wev = stream->request->connection->write;
2124 
2125             wev->active = 0;
2126             wev->ready = 1;
2127 
2128             if (!wev->delayed) {
2129                 ngx_post_event(wev, &ngx_posted_events);
2130             }
2131         }
2132     }
2133 
2134     h2c->send_window += window;
2135 }
2136 
2137 
2138 static ngx_int_t
2139 ngx_http_v2_filter_init(ngx_conf_t *cf)
2140 {
2141     ngx_http_next_header_filter = ngx_http_top_header_filter;
2142     ngx_http_top_header_filter = ngx_http_v2_header_filter;
2143 
2144     return NGX_OK;
2145 }