Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

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     len = ngx_max(r->schema.len, path->len);
0948 
0949     if (binary[0].len) {
0950         tmp = ngx_palloc(r->pool, len);
0951         if (tmp == NULL) {
0952             return NGX_ERROR;
0953         }
0954 
0955     } else {
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 + NGX_HTTP_V2_INT_OCTETS + r->schema.len;
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     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1029                    "http2 push header: \":scheme: %V\"", &r->schema);
1030 
1031     if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) {
1032         *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
1033 
1034     } else if (r->schema.len == 4
1035                && ngx_strncmp(r->schema.data, "http", 4) == 0)
1036     {
1037         *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
1038 
1039     } else {
1040         *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
1041         pos = ngx_http_v2_write_value(pos, r->schema.data, r->schema.len, tmp);
1042     }
1043 
1044     for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
1045         h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
1046 
1047         if (*h == NULL) {
1048             continue;
1049         }
1050 
1051         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1052                        "http2 push header: \"%V: %V\"",
1053                        &ph[i].name, &(*h)->value);
1054 
1055         pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
1056     }
1057 
1058     frame = ngx_http_v2_create_push_frame(r, start, pos);
1059     if (frame == NULL) {
1060         return NGX_ERROR;
1061     }
1062 
1063     ngx_http_v2_queue_blocked_frame(h2c, frame);
1064 
1065     stream->queued++;
1066 
1067     stream = ngx_http_v2_push_stream(stream, path);
1068 
1069     if (stream) {
1070         stream->request->request_length = pos - start;
1071         return NGX_OK;
1072     }
1073 
1074     return NGX_ERROR;
1075 }
1076 
1077 
1078 static ngx_http_v2_out_frame_t *
1079 ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
1080     u_char *end, ngx_uint_t fin)
1081 {
1082     u_char                    type, flags;
1083     size_t                    rest, frame_size;
1084     ngx_buf_t                *b;
1085     ngx_chain_t              *cl, **ll;
1086     ngx_http_v2_stream_t     *stream;
1087     ngx_http_v2_out_frame_t  *frame;
1088 
1089     stream = r->stream;
1090     rest = end - pos;
1091 
1092     frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
1093     if (frame == NULL) {
1094         return NULL;
1095     }
1096 
1097     frame->handler = ngx_http_v2_headers_frame_handler;
1098     frame->stream = stream;
1099     frame->length = rest;
1100     frame->blocked = 1;
1101     frame->fin = fin;
1102 
1103     ll = &frame->first;
1104 
1105     type = NGX_HTTP_V2_HEADERS_FRAME;
1106     flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
1107     frame_size = stream->connection->frame_size;
1108 
1109     for ( ;; ) {
1110         if (rest <= frame_size) {
1111             frame_size = rest;
1112             flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
1113         }
1114 
1115         b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);
1116         if (b == NULL) {
1117             return NULL;
1118         }
1119 
1120         b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
1121         *b->last++ = flags;
1122         b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
1123 
1124         b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1125 
1126         cl = ngx_alloc_chain_link(r->pool);
1127         if (cl == NULL) {
1128             return NULL;
1129         }
1130 
1131         cl->buf = b;
1132 
1133         *ll = cl;
1134         ll = &cl->next;
1135 
1136         b = ngx_calloc_buf(r->pool);
1137         if (b == NULL) {
1138             return NULL;
1139         }
1140 
1141         b->pos = pos;
1142 
1143         pos += frame_size;
1144 
1145         b->last = pos;
1146         b->start = b->pos;
1147         b->end = b->last;
1148         b->temporary = 1;
1149 
1150         cl = ngx_alloc_chain_link(r->pool);
1151         if (cl == NULL) {
1152             return NULL;
1153         }
1154 
1155         cl->buf = b;
1156 
1157         *ll = cl;
1158         ll = &cl->next;
1159 
1160         rest -= frame_size;
1161 
1162         if (rest) {
1163             frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1164 
1165             type = NGX_HTTP_V2_CONTINUATION_FRAME;
1166             flags = NGX_HTTP_V2_NO_FLAG;
1167             continue;
1168         }
1169 
1170         b->last_buf = fin;
1171         cl->next = NULL;
1172         frame->last = cl;
1173 
1174         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1175                        "http2:%ui create HEADERS frame %p: len:%uz fin:%ui",
1176                        stream->node->id, frame, frame->length, fin);
1177 
1178         return frame;
1179     }
1180 }
1181 
1182 
1183 static ngx_http_v2_out_frame_t *
1184 ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)
1185 {
1186     u_char                     type, flags;
1187     size_t                     rest, frame_size, len;
1188     ngx_buf_t                 *b;
1189     ngx_chain_t               *cl, **ll;
1190     ngx_http_v2_stream_t      *stream;
1191     ngx_http_v2_out_frame_t   *frame;
1192     ngx_http_v2_connection_t  *h2c;
1193 
1194     stream = r->stream;
1195     h2c = stream->connection;
1196     rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);
1197 
1198     frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
1199     if (frame == NULL) {
1200         return NULL;
1201     }
1202 
1203     frame->handler = ngx_http_v2_push_frame_handler;
1204     frame->stream = stream;
1205     frame->length = rest;
1206     frame->blocked = 1;
1207     frame->fin = 0;
1208 
1209     ll = &frame->first;
1210 
1211     type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;
1212     flags = NGX_HTTP_V2_NO_FLAG;
1213     frame_size = h2c->frame_size;
1214 
1215     for ( ;; ) {
1216         if (rest <= frame_size) {
1217             frame_size = rest;
1218             flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
1219         }
1220 
1221         b = ngx_create_temp_buf(r->pool,
1222                                 NGX_HTTP_V2_FRAME_HEADER_SIZE
1223                                 + ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)
1224                                    ? NGX_HTTP_V2_STREAM_ID_SIZE : 0));
1225         if (b == NULL) {
1226             return NULL;
1227         }
1228 
1229         b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
1230         *b->last++ = flags;
1231         b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
1232 
1233         b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1234 
1235         if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
1236             h2c->last_push += 2;
1237 
1238             b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);
1239             len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;
1240 
1241         } else {
1242             len = frame_size;
1243         }
1244 
1245         cl = ngx_alloc_chain_link(r->pool);
1246         if (cl == NULL) {
1247             return NULL;
1248         }
1249 
1250         cl->buf = b;
1251 
1252         *ll = cl;
1253         ll = &cl->next;
1254 
1255         b = ngx_calloc_buf(r->pool);
1256         if (b == NULL) {
1257             return NULL;
1258         }
1259 
1260         b->pos = pos;
1261 
1262         pos += len;
1263 
1264         b->last = pos;
1265         b->start = b->pos;
1266         b->end = b->last;
1267         b->temporary = 1;
1268 
1269         cl = ngx_alloc_chain_link(r->pool);
1270         if (cl == NULL) {
1271             return NULL;
1272         }
1273 
1274         cl->buf = b;
1275 
1276         *ll = cl;
1277         ll = &cl->next;
1278 
1279         rest -= frame_size;
1280 
1281         if (rest) {
1282             frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1283 
1284             type = NGX_HTTP_V2_CONTINUATION_FRAME;
1285             continue;
1286         }
1287 
1288         cl->next = NULL;
1289         frame->last = cl;
1290 
1291         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1292                        "http2:%ui create PUSH_PROMISE frame %p: "
1293                        "sid:%ui len:%uz",
1294                        stream->node->id, frame, h2c->last_push,
1295                        frame->length);
1296 
1297         return frame;
1298     }
1299 }
1300 
1301 
1302 static ngx_http_v2_out_frame_t *
1303 ngx_http_v2_create_trailers_frame(ngx_http_request_t *r)
1304 {
1305     u_char            *pos, *start, *tmp;
1306     size_t             len, tmp_len;
1307     ngx_uint_t         i;
1308     ngx_list_part_t   *part;
1309     ngx_table_elt_t   *header;
1310     ngx_connection_t  *fc;
1311 
1312     fc = r->connection;
1313     len = 0;
1314     tmp_len = 0;
1315 
1316     part = &r->headers_out.trailers.part;
1317     header = part->elts;
1318 
1319     for (i = 0; /* void */; i++) {
1320 
1321         if (i >= part->nelts) {
1322             if (part->next == NULL) {
1323                 break;
1324             }
1325 
1326             part = part->next;
1327             header = part->elts;
1328             i = 0;
1329         }
1330 
1331         if (header[i].hash == 0) {
1332             continue;
1333         }
1334 
1335         if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
1336             ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
1337                           "too long response trailer name: \"%V\"",
1338                           &header[i].key);
1339             return NULL;
1340         }
1341 
1342         if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
1343             ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
1344                           "too long response trailer value: \"%V: %V\"",
1345                           &header[i].key, &header[i].value);
1346             return NULL;
1347         }
1348 
1349         len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
1350                  + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
1351 
1352         if (header[i].key.len > tmp_len) {
1353             tmp_len = header[i].key.len;
1354         }
1355 
1356         if (header[i].value.len > tmp_len) {
1357             tmp_len = header[i].value.len;
1358         }
1359     }
1360 
1361     if (len == 0) {
1362         return NGX_HTTP_V2_NO_TRAILERS;
1363     }
1364 
1365     tmp = ngx_palloc(r->pool, tmp_len);
1366     pos = ngx_pnalloc(r->pool, len);
1367 
1368     if (pos == NULL || tmp == NULL) {
1369         return NULL;
1370     }
1371 
1372     start = pos;
1373 
1374     part = &r->headers_out.trailers.part;
1375     header = part->elts;
1376 
1377     for (i = 0; /* void */; i++) {
1378 
1379         if (i >= part->nelts) {
1380             if (part->next == NULL) {
1381                 break;
1382             }
1383 
1384             part = part->next;
1385             header = part->elts;
1386             i = 0;
1387         }
1388 
1389         if (header[i].hash == 0) {
1390             continue;
1391         }
1392 
1393 #if (NGX_DEBUG)
1394         if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
1395             ngx_strlow(tmp, header[i].key.data, header[i].key.len);
1396 
1397             ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1398                            "http2 output trailer: \"%*s: %V\"",
1399                            header[i].key.len, tmp, &header[i].value);
1400         }
1401 #endif
1402 
1403         *pos++ = 0;
1404 
1405         pos = ngx_http_v2_write_name(pos, header[i].key.data,
1406                                      header[i].key.len, tmp);
1407 
1408         pos = ngx_http_v2_write_value(pos, header[i].value.data,
1409                                       header[i].value.len, tmp);
1410     }
1411 
1412     return ngx_http_v2_create_headers_frame(r, start, pos, 1);
1413 }
1414 
1415 
1416 static ngx_chain_t *
1417 ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
1418 {
1419     off_t                      size, offset;
1420     size_t                     rest, frame_size;
1421     ngx_chain_t               *cl, *out, **ln;
1422     ngx_http_request_t        *r;
1423     ngx_http_v2_stream_t      *stream;
1424     ngx_http_v2_loc_conf_t    *h2lcf;
1425     ngx_http_v2_out_frame_t   *frame, *trailers;
1426     ngx_http_v2_connection_t  *h2c;
1427 
1428     r = fc->data;
1429     stream = r->stream;
1430 
1431 #if (NGX_SUPPRESS_WARN)
1432     size = 0;
1433 #endif
1434 
1435     while (in) {
1436         size = ngx_buf_size(in->buf);
1437 
1438         if (size || in->buf->last_buf) {
1439             break;
1440         }
1441 
1442         in = in->next;
1443     }
1444 
1445     if (in == NULL || stream->out_closed) {
1446 
1447         if (stream->queued) {
1448             fc->write->active = 1;
1449             fc->write->ready = 0;
1450 
1451         } else {
1452             fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1453         }
1454 
1455         return NULL;
1456     }
1457 
1458     h2c = stream->connection;
1459 
1460     if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1461         fc->write->active = 1;
1462         fc->write->ready = 0;
1463         return in;
1464     }
1465 
1466     if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1467         cl = ngx_alloc_chain_link(r->pool);
1468         if (cl == NULL) {
1469             return NGX_CHAIN_ERROR;
1470         }
1471 
1472         cl->buf = in->buf;
1473         in->buf = cl->buf->shadow;
1474 
1475         offset = ngx_buf_in_memory(in->buf)
1476                  ? (cl->buf->pos - in->buf->pos)
1477                  : (cl->buf->file_pos - in->buf->file_pos);
1478 
1479         cl->next = stream->free_bufs;
1480         stream->free_bufs = cl;
1481 
1482     } else {
1483         offset = 0;
1484     }
1485 
1486     if (limit == 0 || limit > (off_t) h2c->send_window) {
1487         limit = h2c->send_window;
1488     }
1489 
1490     if (limit > stream->send_window) {
1491         limit = (stream->send_window > 0) ? stream->send_window : 0;
1492     }
1493 
1494     h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
1495 
1496     frame_size = (h2lcf->chunk_size < h2c->frame_size)
1497                  ? h2lcf->chunk_size : h2c->frame_size;
1498 
1499     trailers = NGX_HTTP_V2_NO_TRAILERS;
1500 
1501 #if (NGX_SUPPRESS_WARN)
1502     cl = NULL;
1503 #endif
1504 
1505     for ( ;; ) {
1506         if ((off_t) frame_size > limit) {
1507             frame_size = (size_t) limit;
1508         }
1509 
1510         ln = &out;
1511         rest = frame_size;
1512 
1513         while ((off_t) rest >= size) {
1514 
1515             if (offset) {
1516                 cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
1517                                                    offset, size);
1518                 if (cl == NULL) {
1519                     return NGX_CHAIN_ERROR;
1520                 }
1521 
1522                 offset = 0;
1523 
1524             } else {
1525                 cl = ngx_alloc_chain_link(r->pool);
1526                 if (cl == NULL) {
1527                     return NGX_CHAIN_ERROR;
1528                 }
1529 
1530                 cl->buf = in->buf;
1531             }
1532 
1533             *ln = cl;
1534             ln = &cl->next;
1535 
1536             rest -= (size_t) size;
1537             in = in->next;
1538 
1539             if (in == NULL) {
1540                 frame_size -= rest;
1541                 rest = 0;
1542                 break;
1543             }
1544 
1545             size = ngx_buf_size(in->buf);
1546         }
1547 
1548         if (rest) {
1549             cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
1550             if (cl == NULL) {
1551                 return NGX_CHAIN_ERROR;
1552             }
1553 
1554             cl->buf->flush = 0;
1555             cl->buf->last_buf = 0;
1556 
1557             *ln = cl;
1558 
1559             offset += rest;
1560             size -= rest;
1561         }
1562 
1563         if (cl->buf->last_buf) {
1564             trailers = ngx_http_v2_create_trailers_frame(r);
1565             if (trailers == NULL) {
1566                 return NGX_CHAIN_ERROR;
1567             }
1568 
1569             if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
1570                 cl->buf->last_buf = 0;
1571             }
1572         }
1573 
1574         if (frame_size || cl->buf->last_buf) {
1575             frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,
1576                                                       out, cl);
1577             if (frame == NULL) {
1578                 return NGX_CHAIN_ERROR;
1579             }
1580 
1581             ngx_http_v2_queue_frame(h2c, frame);
1582 
1583             h2c->send_window -= frame_size;
1584 
1585             stream->send_window -= frame_size;
1586             stream->queued++;
1587         }
1588 
1589         if (in == NULL) {
1590 
1591             if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
1592                 ngx_http_v2_queue_frame(h2c, trailers);
1593                 stream->queued++;
1594             }
1595 
1596             break;
1597         }
1598 
1599         limit -= frame_size;
1600 
1601         if (limit == 0) {
1602             break;
1603         }
1604     }
1605 
1606     if (offset) {
1607         cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
1608         if (cl == NULL) {
1609             return NGX_CHAIN_ERROR;
1610         }
1611 
1612         in->buf = cl->buf;
1613         ngx_free_chain(r->pool, cl);
1614     }
1615 
1616     if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
1617         return NGX_CHAIN_ERROR;
1618     }
1619 
1620     if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1621         fc->write->active = 1;
1622         fc->write->ready = 0;
1623     }
1624 
1625     return in;
1626 }
1627 
1628 
1629 static ngx_chain_t *
1630 ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
1631     off_t offset, off_t size)
1632 {
1633     ngx_buf_t    *chunk;
1634     ngx_chain_t  *cl;
1635 
1636     cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
1637     if (cl == NULL) {
1638         return NULL;
1639     }
1640 
1641     chunk = cl->buf;
1642 
1643     ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
1644 
1645     chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
1646     chunk->shadow = buf;
1647 
1648     if (ngx_buf_in_memory(chunk)) {
1649         chunk->pos += offset;
1650         chunk->last = chunk->pos + size;
1651     }
1652 
1653     if (chunk->in_file) {
1654         chunk->file_pos += offset;
1655         chunk->file_last = chunk->file_pos + size;
1656     }
1657 
1658     return cl;
1659 }
1660 
1661 
1662 static ngx_http_v2_out_frame_t *
1663 ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
1664     size_t len, ngx_chain_t *first, ngx_chain_t *last)
1665 {
1666     u_char                    flags;
1667     ngx_buf_t                *buf;
1668     ngx_chain_t              *cl;
1669     ngx_http_v2_out_frame_t  *frame;
1670 
1671     frame = stream->free_frames;
1672 
1673     if (frame) {
1674         stream->free_frames = frame->next;
1675 
1676     } else {
1677         frame = ngx_palloc(stream->request->pool,
1678                            sizeof(ngx_http_v2_out_frame_t));
1679         if (frame == NULL) {
1680             return NULL;
1681         }
1682     }
1683 
1684     flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
1685 
1686     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
1687                    "http2:%ui create DATA frame %p: len:%uz flags:%ui",
1688                    stream->node->id, frame, len, (ngx_uint_t) flags);
1689 
1690     cl = ngx_chain_get_free_buf(stream->request->pool,
1691                                 &stream->free_frame_headers);
1692     if (cl == NULL) {
1693         return NULL;
1694     }
1695 
1696     buf = cl->buf;
1697 
1698     if (buf->start == NULL) {
1699         buf->start = ngx_palloc(stream->request->pool,
1700                                 NGX_HTTP_V2_FRAME_HEADER_SIZE);
1701         if (buf->start == NULL) {
1702             return NULL;
1703         }
1704 
1705         buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
1706         buf->last = buf->end;
1707 
1708         buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1709         buf->memory = 1;
1710     }
1711 
1712     buf->pos = buf->start;
1713     buf->last = buf->pos;
1714 
1715     buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
1716                                                NGX_HTTP_V2_DATA_FRAME);
1717     *buf->last++ = flags;
1718 
1719     buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
1720 
1721     cl->next = first;
1722     first = cl;
1723 
1724     last->buf->flush = 1;
1725 
1726     frame->first = first;
1727     frame->last = last;
1728     frame->handler = ngx_http_v2_data_frame_handler;
1729     frame->stream = stream;
1730     frame->length = len;
1731     frame->blocked = 0;
1732     frame->fin = last->buf->last_buf;
1733 
1734     return frame;
1735 }
1736 
1737 
1738 static ngx_inline ngx_int_t
1739 ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
1740     ngx_http_v2_stream_t *stream)
1741 {
1742     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1743                    "http2:%ui windows: conn:%uz stream:%z",
1744                    stream->node->id, h2c->send_window, stream->send_window);
1745 
1746     if (stream->send_window <= 0) {
1747         stream->exhausted = 1;
1748         return NGX_DECLINED;
1749     }
1750 
1751     if (h2c->send_window == 0) {
1752         ngx_http_v2_waiting_queue(h2c, stream);
1753         return NGX_DECLINED;
1754     }
1755 
1756     return NGX_OK;
1757 }
1758 
1759 
1760 static void
1761 ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
1762     ngx_http_v2_stream_t *stream)
1763 {
1764     ngx_queue_t           *q;
1765     ngx_http_v2_stream_t  *s;
1766 
1767     if (stream->waiting) {
1768         return;
1769     }
1770 
1771     stream->waiting = 1;
1772 
1773     for (q = ngx_queue_last(&h2c->waiting);
1774          q != ngx_queue_sentinel(&h2c->waiting);
1775          q = ngx_queue_prev(q))
1776     {
1777         s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
1778 
1779         if (s->node->rank < stream->node->rank
1780             || (s->node->rank == stream->node->rank
1781                 && s->node->rel_weight >= stream->node->rel_weight))
1782         {
1783             break;
1784         }
1785     }
1786 
1787     ngx_queue_insert_after(q, &stream->queue);
1788 }
1789 
1790 
1791 static ngx_inline ngx_int_t
1792 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
1793 {
1794     stream->blocked = 1;
1795 
1796     if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
1797         fc->error = 1;
1798         return NGX_ERROR;
1799     }
1800 
1801     stream->blocked = 0;
1802 
1803     if (stream->queued) {
1804         fc->buffered |= NGX_HTTP_V2_BUFFERED;
1805         fc->write->active = 1;
1806         fc->write->ready = 0;
1807         return NGX_AGAIN;
1808     }
1809 
1810     fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1811 
1812     return NGX_OK;
1813 }
1814 
1815 
1816 static ngx_int_t
1817 ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
1818     ngx_http_v2_out_frame_t *frame)
1819 {
1820     ngx_chain_t           *cl, *ln;
1821     ngx_http_v2_stream_t  *stream;
1822 
1823     stream = frame->stream;
1824     cl = frame->first;
1825 
1826     for ( ;; ) {
1827         if (cl->buf->pos != cl->buf->last) {
1828             frame->first = cl;
1829 
1830             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1831                            "http2:%ui HEADERS frame %p was sent partially",
1832                            stream->node->id, frame);
1833 
1834             return NGX_AGAIN;
1835         }
1836 
1837         ln = cl->next;
1838 
1839         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1840             cl->next = stream->free_frame_headers;
1841             stream->free_frame_headers = cl;
1842 
1843         } else {
1844             cl->next = stream->free_bufs;
1845             stream->free_bufs = cl;
1846         }
1847 
1848         if (cl == frame->last) {
1849             break;
1850         }
1851 
1852         cl = ln;
1853     }
1854 
1855     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1856                    "http2:%ui HEADERS frame %p was sent",
1857                    stream->node->id, frame);
1858 
1859     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1860                                     + frame->length;
1861 
1862     ngx_http_v2_handle_frame(stream, frame);
1863 
1864     ngx_http_v2_handle_stream(h2c, stream);
1865 
1866     return NGX_OK;
1867 }
1868 
1869 
1870 static ngx_int_t
1871 ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
1872     ngx_http_v2_out_frame_t *frame)
1873 {
1874     ngx_chain_t           *cl, *ln;
1875     ngx_http_v2_stream_t  *stream;
1876 
1877     stream = frame->stream;
1878     cl = frame->first;
1879 
1880     for ( ;; ) {
1881         if (cl->buf->pos != cl->buf->last) {
1882             frame->first = cl;
1883 
1884             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1885                            "http2:%ui PUSH_PROMISE frame %p was sent partially",
1886                            stream->node->id, frame);
1887 
1888             return NGX_AGAIN;
1889         }
1890 
1891         ln = cl->next;
1892 
1893         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1894             cl->next = stream->free_frame_headers;
1895             stream->free_frame_headers = cl;
1896 
1897         } else {
1898             cl->next = stream->free_bufs;
1899             stream->free_bufs = cl;
1900         }
1901 
1902         if (cl == frame->last) {
1903             break;
1904         }
1905 
1906         cl = ln;
1907     }
1908 
1909     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1910                    "http2:%ui PUSH_PROMISE frame %p was sent",
1911                    stream->node->id, frame);
1912 
1913     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1914                                     + frame->length;
1915 
1916     ngx_http_v2_handle_frame(stream, frame);
1917 
1918     ngx_http_v2_handle_stream(h2c, stream);
1919 
1920     return NGX_OK;
1921 }
1922 
1923 
1924 static ngx_int_t
1925 ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
1926     ngx_http_v2_out_frame_t *frame)
1927 {
1928     ngx_buf_t             *buf;
1929     ngx_chain_t           *cl, *ln;
1930     ngx_http_v2_stream_t  *stream;
1931 
1932     stream = frame->stream;
1933     cl = frame->first;
1934 
1935     if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1936 
1937         if (cl->buf->pos != cl->buf->last) {
1938             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1939                            "http2:%ui DATA frame %p was sent partially",
1940                            stream->node->id, frame);
1941 
1942             return NGX_AGAIN;
1943         }
1944 
1945         ln = cl->next;
1946 
1947         cl->next = stream->free_frame_headers;
1948         stream->free_frame_headers = cl;
1949 
1950         if (cl == frame->last) {
1951             goto done;
1952         }
1953 
1954         cl = ln;
1955     }
1956 
1957     for ( ;; ) {
1958         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1959             buf = cl->buf->shadow;
1960 
1961             if (ngx_buf_in_memory(buf)) {
1962                 buf->pos = cl->buf->pos;
1963             }
1964 
1965             if (buf->in_file) {
1966                 buf->file_pos = cl->buf->file_pos;
1967             }
1968         }
1969 
1970         if (ngx_buf_size(cl->buf) != 0) {
1971 
1972             if (cl != frame->first) {
1973                 frame->first = cl;
1974                 ngx_http_v2_handle_stream(h2c, stream);
1975             }
1976 
1977             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1978                            "http2:%ui DATA frame %p was sent partially",
1979                            stream->node->id, frame);
1980 
1981             return NGX_AGAIN;
1982         }
1983 
1984         ln = cl->next;
1985 
1986         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1987             cl->next = stream->free_bufs;
1988             stream->free_bufs = cl;
1989 
1990         } else {
1991             ngx_free_chain(stream->request->pool, cl);
1992         }
1993 
1994         if (cl == frame->last) {
1995             goto done;
1996         }
1997 
1998         cl = ln;
1999     }
2000 
2001 done:
2002 
2003     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
2004                    "http2:%ui DATA frame %p was sent",
2005                    stream->node->id, frame);
2006 
2007     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
2008 
2009     ngx_http_v2_handle_frame(stream, frame);
2010 
2011     ngx_http_v2_handle_stream(h2c, stream);
2012 
2013     return NGX_OK;
2014 }
2015 
2016 
2017 static ngx_inline void
2018 ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
2019     ngx_http_v2_out_frame_t *frame)
2020 {
2021     ngx_http_request_t  *r;
2022 
2023     r = stream->request;
2024 
2025     r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
2026 
2027     if (frame->fin) {
2028         stream->out_closed = 1;
2029     }
2030 
2031     frame->next = stream->free_frames;
2032     stream->free_frames = frame;
2033 
2034     stream->queued--;
2035 }
2036 
2037 
2038 static ngx_inline void
2039 ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
2040     ngx_http_v2_stream_t *stream)
2041 {
2042     ngx_event_t       *wev;
2043     ngx_connection_t  *fc;
2044 
2045     if (stream->waiting || stream->blocked) {
2046         return;
2047     }
2048 
2049     fc = stream->request->connection;
2050 
2051     if (!fc->error && stream->exhausted) {
2052         return;
2053     }
2054 
2055     wev = fc->write;
2056 
2057     wev->active = 0;
2058     wev->ready = 1;
2059 
2060     if (!fc->error && wev->delayed) {
2061         return;
2062     }
2063 
2064     ngx_post_event(wev, &ngx_posted_events);
2065 }
2066 
2067 
2068 static void
2069 ngx_http_v2_filter_cleanup(void *data)
2070 {
2071     ngx_http_v2_stream_t *stream = data;
2072 
2073     size_t                     window;
2074     ngx_event_t               *wev;
2075     ngx_queue_t               *q;
2076     ngx_http_v2_out_frame_t   *frame, **fn;
2077     ngx_http_v2_connection_t  *h2c;
2078 
2079     if (stream->waiting) {
2080         stream->waiting = 0;
2081         ngx_queue_remove(&stream->queue);
2082     }
2083 
2084     if (stream->queued == 0) {
2085         return;
2086     }
2087 
2088     window = 0;
2089     h2c = stream->connection;
2090     fn = &h2c->last_out;
2091 
2092     for ( ;; ) {
2093         frame = *fn;
2094 
2095         if (frame == NULL) {
2096             break;
2097         }
2098 
2099         if (frame->stream == stream && !frame->blocked) {
2100             *fn = frame->next;
2101 
2102             window += frame->length;
2103 
2104             if (--stream->queued == 0) {
2105                 break;
2106             }
2107 
2108             continue;
2109         }
2110 
2111         fn = &frame->next;
2112     }
2113 
2114     if (h2c->send_window == 0 && window) {
2115 
2116         while (!ngx_queue_empty(&h2c->waiting)) {
2117             q = ngx_queue_head(&h2c->waiting);
2118 
2119             ngx_queue_remove(q);
2120 
2121             stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
2122 
2123             stream->waiting = 0;
2124 
2125             wev = stream->request->connection->write;
2126 
2127             wev->active = 0;
2128             wev->ready = 1;
2129 
2130             if (!wev->delayed) {
2131                 ngx_post_event(wev, &ngx_posted_events);
2132             }
2133         }
2134     }
2135 
2136     h2c->send_window += window;
2137 }
2138 
2139 
2140 static ngx_int_t
2141 ngx_http_v2_filter_init(ngx_conf_t *cf)
2142 {
2143     ngx_http_next_header_filter = ngx_http_top_header_filter;
2144     ngx_http_top_header_filter = ngx_http_v2_header_filter;
2145 
2146     return NGX_OK;
2147 }