Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.19.2 ]​[ nginx-1.18.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 (size) {
1448             ngx_log_error(NGX_LOG_ERR, fc->log, 0,
1449                           "output on closed stream");
1450             return NGX_CHAIN_ERROR;
1451         }
1452 
1453         if (stream->queued) {
1454             fc->write->active = 1;
1455             fc->write->ready = 0;
1456 
1457         } else {
1458             fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1459         }
1460 
1461         return NULL;
1462     }
1463 
1464     h2c = stream->connection;
1465 
1466     if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1467         fc->write->active = 1;
1468         fc->write->ready = 0;
1469         return in;
1470     }
1471 
1472     if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1473         cl = ngx_alloc_chain_link(r->pool);
1474         if (cl == NULL) {
1475             return NGX_CHAIN_ERROR;
1476         }
1477 
1478         cl->buf = in->buf;
1479         in->buf = cl->buf->shadow;
1480 
1481         offset = ngx_buf_in_memory(in->buf)
1482                  ? (cl->buf->pos - in->buf->pos)
1483                  : (cl->buf->file_pos - in->buf->file_pos);
1484 
1485         cl->next = stream->free_bufs;
1486         stream->free_bufs = cl;
1487 
1488     } else {
1489         offset = 0;
1490     }
1491 
1492     if (limit == 0 || limit > (off_t) h2c->send_window) {
1493         limit = h2c->send_window;
1494     }
1495 
1496     if (limit > stream->send_window) {
1497         limit = (stream->send_window > 0) ? stream->send_window : 0;
1498     }
1499 
1500     h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
1501 
1502     frame_size = (h2lcf->chunk_size < h2c->frame_size)
1503                  ? h2lcf->chunk_size : h2c->frame_size;
1504 
1505     trailers = NGX_HTTP_V2_NO_TRAILERS;
1506 
1507 #if (NGX_SUPPRESS_WARN)
1508     cl = NULL;
1509 #endif
1510 
1511     for ( ;; ) {
1512         if ((off_t) frame_size > limit) {
1513             frame_size = (size_t) limit;
1514         }
1515 
1516         ln = &out;
1517         rest = frame_size;
1518 
1519         while ((off_t) rest >= size) {
1520 
1521             if (offset) {
1522                 cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
1523                                                    offset, size);
1524                 if (cl == NULL) {
1525                     return NGX_CHAIN_ERROR;
1526                 }
1527 
1528                 offset = 0;
1529 
1530             } else {
1531                 cl = ngx_alloc_chain_link(r->pool);
1532                 if (cl == NULL) {
1533                     return NGX_CHAIN_ERROR;
1534                 }
1535 
1536                 cl->buf = in->buf;
1537             }
1538 
1539             *ln = cl;
1540             ln = &cl->next;
1541 
1542             rest -= (size_t) size;
1543             in = in->next;
1544 
1545             if (in == NULL) {
1546                 frame_size -= rest;
1547                 rest = 0;
1548                 break;
1549             }
1550 
1551             size = ngx_buf_size(in->buf);
1552         }
1553 
1554         if (rest) {
1555             cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
1556             if (cl == NULL) {
1557                 return NGX_CHAIN_ERROR;
1558             }
1559 
1560             cl->buf->flush = 0;
1561             cl->buf->last_buf = 0;
1562 
1563             *ln = cl;
1564 
1565             offset += rest;
1566             size -= rest;
1567         }
1568 
1569         if (cl->buf->last_buf) {
1570             trailers = ngx_http_v2_create_trailers_frame(r);
1571             if (trailers == NULL) {
1572                 return NGX_CHAIN_ERROR;
1573             }
1574 
1575             if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
1576                 cl->buf->last_buf = 0;
1577             }
1578         }
1579 
1580         if (frame_size || cl->buf->last_buf) {
1581             frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,
1582                                                       out, cl);
1583             if (frame == NULL) {
1584                 return NGX_CHAIN_ERROR;
1585             }
1586 
1587             ngx_http_v2_queue_frame(h2c, frame);
1588 
1589             h2c->send_window -= frame_size;
1590 
1591             stream->send_window -= frame_size;
1592             stream->queued++;
1593         }
1594 
1595         if (in == NULL) {
1596 
1597             if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
1598                 ngx_http_v2_queue_frame(h2c, trailers);
1599                 stream->queued++;
1600             }
1601 
1602             break;
1603         }
1604 
1605         limit -= frame_size;
1606 
1607         if (limit == 0) {
1608             break;
1609         }
1610     }
1611 
1612     if (offset) {
1613         cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
1614         if (cl == NULL) {
1615             return NGX_CHAIN_ERROR;
1616         }
1617 
1618         in->buf = cl->buf;
1619         ngx_free_chain(r->pool, cl);
1620     }
1621 
1622     if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
1623         return NGX_CHAIN_ERROR;
1624     }
1625 
1626     if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
1627         fc->write->active = 1;
1628         fc->write->ready = 0;
1629     }
1630 
1631     return in;
1632 }
1633 
1634 
1635 static ngx_chain_t *
1636 ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
1637     off_t offset, off_t size)
1638 {
1639     ngx_buf_t    *chunk;
1640     ngx_chain_t  *cl;
1641 
1642     cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
1643     if (cl == NULL) {
1644         return NULL;
1645     }
1646 
1647     chunk = cl->buf;
1648 
1649     ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
1650 
1651     chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
1652     chunk->shadow = buf;
1653 
1654     if (ngx_buf_in_memory(chunk)) {
1655         chunk->pos += offset;
1656         chunk->last = chunk->pos + size;
1657     }
1658 
1659     if (chunk->in_file) {
1660         chunk->file_pos += offset;
1661         chunk->file_last = chunk->file_pos + size;
1662     }
1663 
1664     return cl;
1665 }
1666 
1667 
1668 static ngx_http_v2_out_frame_t *
1669 ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
1670     size_t len, ngx_chain_t *first, ngx_chain_t *last)
1671 {
1672     u_char                     flags;
1673     ngx_buf_t                 *buf;
1674     ngx_chain_t               *cl;
1675     ngx_http_v2_out_frame_t   *frame;
1676     ngx_http_v2_connection_t  *h2c;
1677 
1678     frame = stream->free_frames;
1679     h2c = stream->connection;
1680 
1681     if (frame) {
1682         stream->free_frames = frame->next;
1683 
1684     } else if (h2c->frames < 10000) {
1685         frame = ngx_palloc(stream->request->pool,
1686                            sizeof(ngx_http_v2_out_frame_t));
1687         if (frame == NULL) {
1688             return NULL;
1689         }
1690 
1691         stream->frames++;
1692         h2c->frames++;
1693 
1694     } else {
1695         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
1696                       "http2 flood detected");
1697 
1698         h2c->connection->error = 1;
1699         return NULL;
1700     }
1701 
1702     flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
1703 
1704     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
1705                    "http2:%ui create DATA frame %p: len:%uz flags:%ui",
1706                    stream->node->id, frame, len, (ngx_uint_t) flags);
1707 
1708     cl = ngx_chain_get_free_buf(stream->request->pool,
1709                                 &stream->free_frame_headers);
1710     if (cl == NULL) {
1711         return NULL;
1712     }
1713 
1714     buf = cl->buf;
1715 
1716     if (buf->start == NULL) {
1717         buf->start = ngx_palloc(stream->request->pool,
1718                                 NGX_HTTP_V2_FRAME_HEADER_SIZE);
1719         if (buf->start == NULL) {
1720             return NULL;
1721         }
1722 
1723         buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
1724         buf->last = buf->end;
1725 
1726         buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
1727         buf->memory = 1;
1728     }
1729 
1730     buf->pos = buf->start;
1731     buf->last = buf->pos;
1732 
1733     buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
1734                                                NGX_HTTP_V2_DATA_FRAME);
1735     *buf->last++ = flags;
1736 
1737     buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
1738 
1739     cl->next = first;
1740     first = cl;
1741 
1742     last->buf->flush = 1;
1743 
1744     frame->first = first;
1745     frame->last = last;
1746     frame->handler = ngx_http_v2_data_frame_handler;
1747     frame->stream = stream;
1748     frame->length = len;
1749     frame->blocked = 0;
1750     frame->fin = last->buf->last_buf;
1751 
1752     return frame;
1753 }
1754 
1755 
1756 static ngx_inline ngx_int_t
1757 ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
1758     ngx_http_v2_stream_t *stream)
1759 {
1760     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1761                    "http2:%ui windows: conn:%uz stream:%z",
1762                    stream->node->id, h2c->send_window, stream->send_window);
1763 
1764     if (stream->send_window <= 0) {
1765         stream->exhausted = 1;
1766         return NGX_DECLINED;
1767     }
1768 
1769     if (h2c->send_window == 0) {
1770         ngx_http_v2_waiting_queue(h2c, stream);
1771         return NGX_DECLINED;
1772     }
1773 
1774     return NGX_OK;
1775 }
1776 
1777 
1778 static void
1779 ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
1780     ngx_http_v2_stream_t *stream)
1781 {
1782     ngx_queue_t           *q;
1783     ngx_http_v2_stream_t  *s;
1784 
1785     if (stream->waiting) {
1786         return;
1787     }
1788 
1789     stream->waiting = 1;
1790 
1791     for (q = ngx_queue_last(&h2c->waiting);
1792          q != ngx_queue_sentinel(&h2c->waiting);
1793          q = ngx_queue_prev(q))
1794     {
1795         s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
1796 
1797         if (s->node->rank < stream->node->rank
1798             || (s->node->rank == stream->node->rank
1799                 && s->node->rel_weight >= stream->node->rel_weight))
1800         {
1801             break;
1802         }
1803     }
1804 
1805     ngx_queue_insert_after(q, &stream->queue);
1806 }
1807 
1808 
1809 static ngx_inline ngx_int_t
1810 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
1811 {
1812     stream->blocked = 1;
1813 
1814     if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
1815         fc->error = 1;
1816         return NGX_ERROR;
1817     }
1818 
1819     stream->blocked = 0;
1820 
1821     if (stream->queued) {
1822         fc->buffered |= NGX_HTTP_V2_BUFFERED;
1823         fc->write->active = 1;
1824         fc->write->ready = 0;
1825         return NGX_AGAIN;
1826     }
1827 
1828     fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1829 
1830     return NGX_OK;
1831 }
1832 
1833 
1834 static ngx_int_t
1835 ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
1836     ngx_http_v2_out_frame_t *frame)
1837 {
1838     ngx_chain_t           *cl, *ln;
1839     ngx_http_v2_stream_t  *stream;
1840 
1841     stream = frame->stream;
1842     cl = frame->first;
1843 
1844     for ( ;; ) {
1845         if (cl->buf->pos != cl->buf->last) {
1846             frame->first = cl;
1847 
1848             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1849                            "http2:%ui HEADERS frame %p was sent partially",
1850                            stream->node->id, frame);
1851 
1852             return NGX_AGAIN;
1853         }
1854 
1855         ln = cl->next;
1856 
1857         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1858             cl->next = stream->free_frame_headers;
1859             stream->free_frame_headers = cl;
1860 
1861         } else {
1862             cl->next = stream->free_bufs;
1863             stream->free_bufs = cl;
1864         }
1865 
1866         if (cl == frame->last) {
1867             break;
1868         }
1869 
1870         cl = ln;
1871     }
1872 
1873     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1874                    "http2:%ui HEADERS frame %p was sent",
1875                    stream->node->id, frame);
1876 
1877     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1878                                     + frame->length;
1879 
1880     h2c->payload_bytes += frame->length;
1881 
1882     ngx_http_v2_handle_frame(stream, frame);
1883 
1884     ngx_http_v2_handle_stream(h2c, stream);
1885 
1886     return NGX_OK;
1887 }
1888 
1889 
1890 static ngx_int_t
1891 ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
1892     ngx_http_v2_out_frame_t *frame)
1893 {
1894     ngx_chain_t           *cl, *ln;
1895     ngx_http_v2_stream_t  *stream;
1896 
1897     stream = frame->stream;
1898     cl = frame->first;
1899 
1900     for ( ;; ) {
1901         if (cl->buf->pos != cl->buf->last) {
1902             frame->first = cl;
1903 
1904             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1905                            "http2:%ui PUSH_PROMISE frame %p was sent partially",
1906                            stream->node->id, frame);
1907 
1908             return NGX_AGAIN;
1909         }
1910 
1911         ln = cl->next;
1912 
1913         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1914             cl->next = stream->free_frame_headers;
1915             stream->free_frame_headers = cl;
1916 
1917         } else {
1918             cl->next = stream->free_bufs;
1919             stream->free_bufs = cl;
1920         }
1921 
1922         if (cl == frame->last) {
1923             break;
1924         }
1925 
1926         cl = ln;
1927     }
1928 
1929     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1930                    "http2:%ui PUSH_PROMISE frame %p was sent",
1931                    stream->node->id, frame);
1932 
1933     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
1934                                     + frame->length;
1935 
1936     h2c->payload_bytes += frame->length;
1937 
1938     ngx_http_v2_handle_frame(stream, frame);
1939 
1940     ngx_http_v2_handle_stream(h2c, stream);
1941 
1942     return NGX_OK;
1943 }
1944 
1945 
1946 static ngx_int_t
1947 ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
1948     ngx_http_v2_out_frame_t *frame)
1949 {
1950     ngx_buf_t             *buf;
1951     ngx_chain_t           *cl, *ln;
1952     ngx_http_v2_stream_t  *stream;
1953 
1954     stream = frame->stream;
1955     cl = frame->first;
1956 
1957     if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
1958 
1959         if (cl->buf->pos != cl->buf->last) {
1960             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1961                            "http2:%ui DATA frame %p was sent partially",
1962                            stream->node->id, frame);
1963 
1964             return NGX_AGAIN;
1965         }
1966 
1967         ln = cl->next;
1968 
1969         cl->next = stream->free_frame_headers;
1970         stream->free_frame_headers = cl;
1971 
1972         if (cl == frame->last) {
1973             goto done;
1974         }
1975 
1976         cl = ln;
1977     }
1978 
1979     for ( ;; ) {
1980         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1981             buf = cl->buf->shadow;
1982 
1983             if (ngx_buf_in_memory(buf)) {
1984                 buf->pos = cl->buf->pos;
1985             }
1986 
1987             if (buf->in_file) {
1988                 buf->file_pos = cl->buf->file_pos;
1989             }
1990         }
1991 
1992         if (ngx_buf_size(cl->buf) != 0) {
1993 
1994             if (cl != frame->first) {
1995                 frame->first = cl;
1996                 ngx_http_v2_handle_stream(h2c, stream);
1997             }
1998 
1999             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
2000                            "http2:%ui DATA frame %p was sent partially",
2001                            stream->node->id, frame);
2002 
2003             return NGX_AGAIN;
2004         }
2005 
2006         ln = cl->next;
2007 
2008         if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
2009             cl->next = stream->free_bufs;
2010             stream->free_bufs = cl;
2011 
2012         } else {
2013             ngx_free_chain(stream->request->pool, cl);
2014         }
2015 
2016         if (cl == frame->last) {
2017             goto done;
2018         }
2019 
2020         cl = ln;
2021     }
2022 
2023 done:
2024 
2025     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
2026                    "http2:%ui DATA frame %p was sent",
2027                    stream->node->id, frame);
2028 
2029     stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
2030 
2031     h2c->payload_bytes += frame->length;
2032 
2033     ngx_http_v2_handle_frame(stream, frame);
2034 
2035     ngx_http_v2_handle_stream(h2c, stream);
2036 
2037     return NGX_OK;
2038 }
2039 
2040 
2041 static ngx_inline void
2042 ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
2043     ngx_http_v2_out_frame_t *frame)
2044 {
2045     ngx_http_request_t        *r;
2046     ngx_http_v2_connection_t  *h2c;
2047 
2048     r = stream->request;
2049 
2050     r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
2051 
2052     h2c = stream->connection;
2053 
2054     h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
2055 
2056     if (frame->fin) {
2057         stream->out_closed = 1;
2058     }
2059 
2060     frame->next = stream->free_frames;
2061     stream->free_frames = frame;
2062 
2063     stream->queued--;
2064 }
2065 
2066 
2067 static ngx_inline void
2068 ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
2069     ngx_http_v2_stream_t *stream)
2070 {
2071     ngx_event_t       *wev;
2072     ngx_connection_t  *fc;
2073 
2074     if (stream->waiting || stream->blocked) {
2075         return;
2076     }
2077 
2078     fc = stream->request->connection;
2079 
2080     if (!fc->error && stream->exhausted) {
2081         return;
2082     }
2083 
2084     wev = fc->write;
2085 
2086     wev->active = 0;
2087     wev->ready = 1;
2088 
2089     if (!fc->error && wev->delayed) {
2090         return;
2091     }
2092 
2093     ngx_post_event(wev, &ngx_posted_events);
2094 }
2095 
2096 
2097 static void
2098 ngx_http_v2_filter_cleanup(void *data)
2099 {
2100     ngx_http_v2_stream_t *stream = data;
2101 
2102     size_t                     window;
2103     ngx_event_t               *wev;
2104     ngx_queue_t               *q;
2105     ngx_http_v2_out_frame_t   *frame, **fn;
2106     ngx_http_v2_connection_t  *h2c;
2107 
2108     if (stream->waiting) {
2109         stream->waiting = 0;
2110         ngx_queue_remove(&stream->queue);
2111     }
2112 
2113     if (stream->queued == 0) {
2114         return;
2115     }
2116 
2117     window = 0;
2118     h2c = stream->connection;
2119     fn = &h2c->last_out;
2120 
2121     for ( ;; ) {
2122         frame = *fn;
2123 
2124         if (frame == NULL) {
2125             break;
2126         }
2127 
2128         if (frame->stream == stream && !frame->blocked) {
2129             *fn = frame->next;
2130 
2131             window += frame->length;
2132 
2133             if (--stream->queued == 0) {
2134                 break;
2135             }
2136 
2137             continue;
2138         }
2139 
2140         fn = &frame->next;
2141     }
2142 
2143     if (h2c->send_window == 0 && window) {
2144 
2145         while (!ngx_queue_empty(&h2c->waiting)) {
2146             q = ngx_queue_head(&h2c->waiting);
2147 
2148             ngx_queue_remove(q);
2149 
2150             stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
2151 
2152             stream->waiting = 0;
2153 
2154             wev = stream->request->connection->write;
2155 
2156             wev->active = 0;
2157             wev->ready = 1;
2158 
2159             if (!wev->delayed) {
2160                 ngx_post_event(wev, &ngx_posted_events);
2161             }
2162         }
2163     }
2164 
2165     h2c->send_window += window;
2166 }
2167 
2168 
2169 static ngx_int_t
2170 ngx_http_v2_filter_init(ngx_conf_t *cf)
2171 {
2172     ngx_http_next_header_filter = ngx_http_top_header_filter;
2173     ngx_http_top_header_filter = ngx_http_v2_header_filter;
2174 
2175     return NGX_OK;
2176 }