diff options
Diffstat (limited to 'nginx/src/http/v2/ngx_http_v2_filter_module.c')
-rw-r--r-- | nginx/src/http/v2/ngx_http_v2_filter_module.c | 2145 |
1 files changed, 0 insertions, 2145 deletions
diff --git a/nginx/src/http/v2/ngx_http_v2_filter_module.c b/nginx/src/http/v2/ngx_http_v2_filter_module.c deleted file mode 100644 index 029e8ec..0000000 --- a/nginx/src/http/v2/ngx_http_v2_filter_module.c +++ /dev/null @@ -1,2145 +0,0 @@ - -/* - * Copyright (C) Nginx, Inc. - * Copyright (C) Valentin V. Bartenev - * Copyright (C) Ruslan Ermilov - */ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> -#include <nginx.h> -#include <ngx_http_v2_module.h> - - -/* - * This returns precise number of octets for values in range 0..253 - * and estimate number for the rest, but not smaller than required. - */ - -#define ngx_http_v2_integer_octets(v) (1 + (v) / 127) - -#define ngx_http_v2_literal_size(h) \ - (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1) - - -#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 - - -typedef struct { - ngx_str_t name; - u_char index; - ngx_uint_t offset; -} ngx_http_v2_push_header_t; - - -static ngx_http_v2_push_header_t ngx_http_v2_push_headers[] = { - { ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX, - offsetof(ngx_http_headers_in_t, host) }, - - { ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX, - offsetof(ngx_http_headers_in_t, accept_encoding) }, - - { ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX, - offsetof(ngx_http_headers_in_t, accept_language) }, - - { ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX, - offsetof(ngx_http_headers_in_t, user_agent) }, -}; - -#define NGX_HTTP_V2_PUSH_HEADERS \ - (sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t)) - - -static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r); -static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r, - ngx_str_t *path, ngx_str_t *binary); - -static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); -static ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame( - ngx_http_request_t *r, u_char *pos, u_char *end); -static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( - ngx_http_request_t *r); - -static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, - ngx_chain_t *in, off_t limit); - -static ngx_chain_t *ngx_http_v2_filter_get_shadow( - ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size); -static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame( - ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first, - ngx_chain_t *last); - -static ngx_inline ngx_int_t ngx_http_v2_flow_control( - ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream); -static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c, - ngx_http_v2_stream_t *stream); - -static ngx_inline ngx_int_t ngx_http_v2_filter_send( - ngx_connection_t *fc, ngx_http_v2_stream_t *stream); - -static ngx_int_t ngx_http_v2_headers_frame_handler( - ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); -static ngx_int_t ngx_http_v2_push_frame_handler( - ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); -static ngx_int_t ngx_http_v2_data_frame_handler( - ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); -static ngx_inline void ngx_http_v2_handle_frame( - ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame); -static ngx_inline void ngx_http_v2_handle_stream( - ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream); - -static void ngx_http_v2_filter_cleanup(void *data); - -static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf); - - -static ngx_http_module_t ngx_http_v2_filter_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_v2_filter_init, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - NULL, /* create location configuration */ - NULL /* merge location configuration */ -}; - - -ngx_module_t ngx_http_v2_filter_module = { - NGX_MODULE_V1, - &ngx_http_v2_filter_module_ctx, /* module context */ - NULL, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING -}; - - -static ngx_http_output_header_filter_pt ngx_http_next_header_filter; - - -static ngx_int_t -ngx_http_v2_header_filter(ngx_http_request_t *r) -{ - u_char status, *pos, *start, *p, *tmp; - size_t len, tmp_len; - ngx_str_t host, location; - ngx_uint_t i, port, fin; - ngx_list_part_t *part; - ngx_table_elt_t *header; - ngx_connection_t *fc; - ngx_http_cleanup_t *cln; - ngx_http_v2_stream_t *stream; - ngx_http_v2_out_frame_t *frame; - ngx_http_v2_connection_t *h2c; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; - u_char addr[NGX_SOCKADDR_STRLEN]; - - static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; -#if (NGX_HTTP_GZIP) - static const u_char accept_encoding[12] = - "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f"; -#endif - - static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER); - static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)]; - - static size_t nginx_ver_build_len = - ngx_http_v2_literal_size(NGINX_VER_BUILD); - static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)]; - - stream = r->stream; - - if (!stream) { - return ngx_http_next_header_filter(r); - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2 header filter"); - - if (r->header_sent) { - return NGX_OK; - } - - r->header_sent = 1; - - if (r != r->main) { - return NGX_OK; - } - - fc = r->connection; - - if (fc->error) { - return NGX_ERROR; - } - - if (r->method == NGX_HTTP_HEAD) { - r->header_only = 1; - } - - switch (r->headers_out.status) { - - case NGX_HTTP_OK: - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX); - break; - - case NGX_HTTP_NO_CONTENT: - r->header_only = 1; - - ngx_str_null(&r->headers_out.content_type); - - r->headers_out.content_length = NULL; - r->headers_out.content_length_n = -1; - - r->headers_out.last_modified_time = -1; - r->headers_out.last_modified = NULL; - - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX); - break; - - case NGX_HTTP_PARTIAL_CONTENT: - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX); - break; - - case NGX_HTTP_NOT_MODIFIED: - r->header_only = 1; - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX); - break; - - default: - r->headers_out.last_modified_time = -1; - r->headers_out.last_modified = NULL; - - switch (r->headers_out.status) { - - case NGX_HTTP_BAD_REQUEST: - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX); - break; - - case NGX_HTTP_NOT_FOUND: - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX); - break; - - case NGX_HTTP_INTERNAL_SERVER_ERROR: - status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX); - break; - - default: - status = 0; - } - } - - h2c = stream->connection; - - if (!h2c->push_disabled && !h2c->goaway - && stream->node->id % 2 == 1 - && r->method != NGX_HTTP_HEAD) - { - if (ngx_http_v2_push_resources(r) != NGX_OK) { - return NGX_ERROR; - } - } - - len = h2c->table_update ? 1 : 0; - - len += status ? 1 : 1 + ngx_http_v2_literal_size("418"); - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (r->headers_out.server == NULL) { - - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - len += 1 + nginx_ver_len; - - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - len += 1 + nginx_ver_build_len; - - } else { - len += 1 + sizeof(nginx); - } - } - - if (r->headers_out.date == NULL) { - len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); - } - - if (r->headers_out.content_type.len) { - len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len; - - if (r->headers_out.content_type_len == r->headers_out.content_type.len - && r->headers_out.charset.len) - { - len += sizeof("; charset=") - 1 + r->headers_out.charset.len; - } - } - - if (r->headers_out.content_length == NULL - && r->headers_out.content_length_n >= 0) - { - len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN; - } - - if (r->headers_out.last_modified == NULL - && r->headers_out.last_modified_time != -1) - { - len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); - } - - if (r->headers_out.location && r->headers_out.location->value.len) { - - if (r->headers_out.location->value.data[0] == '/' - && clcf->absolute_redirect) - { - if (clcf->server_name_in_redirect) { - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - host = cscf->server_name; - - } else if (r->headers_in.server.len) { - host = r->headers_in.server; - - } else { - host.len = NGX_SOCKADDR_STRLEN; - host.data = addr; - - if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) { - return NGX_ERROR; - } - } - - port = ngx_inet_get_port(fc->local_sockaddr); - - location.len = sizeof("https://") - 1 + host.len - + r->headers_out.location->value.len; - - if (clcf->port_in_redirect) { - -#if (NGX_HTTP_SSL) - if (fc->ssl) - port = (port == 443) ? 0 : port; - else -#endif - port = (port == 80) ? 0 : port; - - } else { - port = 0; - } - - if (port) { - location.len += sizeof(":65535") - 1; - } - - location.data = ngx_pnalloc(r->pool, location.len); - if (location.data == NULL) { - return NGX_ERROR; - } - - p = ngx_cpymem(location.data, "http", sizeof("http") - 1); - -#if (NGX_HTTP_SSL) - if (fc->ssl) { - *p++ = 's'; - } -#endif - - *p++ = ':'; *p++ = '/'; *p++ = '/'; - p = ngx_cpymem(p, host.data, host.len); - - if (port) { - p = ngx_sprintf(p, ":%ui", port); - } - - p = ngx_cpymem(p, r->headers_out.location->value.data, - r->headers_out.location->value.len); - - /* update r->headers_out.location->value for possible logging */ - - r->headers_out.location->value.len = p - location.data; - r->headers_out.location->value.data = location.data; - ngx_str_set(&r->headers_out.location->key, "Location"); - } - - r->headers_out.location->hash = 0; - - len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len; - } - - tmp_len = len; - -#if (NGX_HTTP_GZIP) - if (r->gzip_vary) { - if (clcf->gzip_vary) { - len += 1 + sizeof(accept_encoding); - - } else { - r->gzip_vary = 0; - } - } -#endif - - part = &r->headers_out.headers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (header[i].hash == 0) { - continue; - } - - if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_CRIT, fc->log, 0, - "too long response header name: \"%V\"", - &header[i].key); - return NGX_ERROR; - } - - if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_CRIT, fc->log, 0, - "too long response header value: \"%V: %V\"", - &header[i].key, &header[i].value); - return NGX_ERROR; - } - - len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len - + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; - - if (header[i].key.len > tmp_len) { - tmp_len = header[i].key.len; - } - - if (header[i].value.len > tmp_len) { - tmp_len = header[i].value.len; - } - } - - tmp = ngx_palloc(r->pool, tmp_len); - pos = ngx_pnalloc(r->pool, len); - - if (pos == NULL || tmp == NULL) { - return NGX_ERROR; - } - - start = pos; - - if (h2c->table_update) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 table size update: 0"); - *pos++ = (1 << 5) | 0; - h2c->table_update = 0; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \":status: %03ui\"", - r->headers_out.status); - - if (status) { - *pos++ = status; - - } else { - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); - *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; - pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); - } - - if (r->headers_out.server == NULL) { - - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"server: %s\"", - NGINX_VER); - - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"server: %s\"", - NGINX_VER_BUILD); - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"server: nginx\""); - } - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); - - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - if (nginx_ver[0] == '\0') { - p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER, - sizeof(NGINX_VER) - 1, tmp); - nginx_ver_len = p - nginx_ver; - } - - pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len); - - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - if (nginx_ver_build[0] == '\0') { - p = ngx_http_v2_write_value(nginx_ver_build, - (u_char *) NGINX_VER_BUILD, - sizeof(NGINX_VER_BUILD) - 1, tmp); - nginx_ver_build_len = p - nginx_ver_build; - } - - pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len); - - } else { - pos = ngx_cpymem(pos, nginx, sizeof(nginx)); - } - } - - if (r->headers_out.date == NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"date: %V\"", - &ngx_cached_http_time); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); - pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data, - ngx_cached_http_time.len, tmp); - } - - if (r->headers_out.content_type.len) { - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); - - if (r->headers_out.content_type_len == r->headers_out.content_type.len - && r->headers_out.charset.len) - { - len = r->headers_out.content_type.len + sizeof("; charset=") - 1 - + r->headers_out.charset.len; - - p = ngx_pnalloc(r->pool, len); - if (p == NULL) { - return NGX_ERROR; - } - - p = ngx_cpymem(p, r->headers_out.content_type.data, - r->headers_out.content_type.len); - - p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1); - - p = ngx_cpymem(p, r->headers_out.charset.data, - r->headers_out.charset.len); - - /* updated r->headers_out.content_type is also needed for logging */ - - r->headers_out.content_type.len = len; - r->headers_out.content_type.data = p - len; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"content-type: %V\"", - &r->headers_out.content_type); - - pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data, - r->headers_out.content_type.len, tmp); - } - - if (r->headers_out.content_length == NULL - && r->headers_out.content_length_n >= 0) - { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"content-length: %O\"", - r->headers_out.content_length_n); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); - - p = pos; - pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); - *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); - } - - if (r->headers_out.last_modified == NULL - && r->headers_out.last_modified_time != -1) - { - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); - - ngx_http_time(pos, r->headers_out.last_modified_time); - len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"last-modified: %*s\"", - len, pos); - - /* - * Date will always be encoded using huffman in the temporary buffer, - * so it's safe here to use src and dst pointing to the same address. - */ - pos = ngx_http_v2_write_value(pos, pos, len, tmp); - } - - if (r->headers_out.location && r->headers_out.location->value.len) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"location: %V\"", - &r->headers_out.location->value); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); - pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data, - r->headers_out.location->value.len, tmp); - } - -#if (NGX_HTTP_GZIP) - if (r->gzip_vary) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"vary: Accept-Encoding\""); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); - pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); - } -#endif - - part = &r->headers_out.headers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (header[i].hash == 0) { - continue; - } - -#if (NGX_DEBUG) - if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { - ngx_strlow(tmp, header[i].key.data, header[i].key.len); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"%*s: %V\"", - header[i].key.len, tmp, &header[i].value); - } -#endif - - *pos++ = 0; - - pos = ngx_http_v2_write_name(pos, header[i].key.data, - header[i].key.len, tmp); - - pos = ngx_http_v2_write_value(pos, header[i].value.data, - header[i].value.len, tmp); - } - - fin = r->header_only - || (r->headers_out.content_length_n == 0 && !r->expect_trailers); - - frame = ngx_http_v2_create_headers_frame(r, start, pos, fin); - if (frame == NULL) { - return NGX_ERROR; - } - - ngx_http_v2_queue_blocked_frame(h2c, frame); - - stream->queued++; - - cln = ngx_http_cleanup_add(r, 0); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_v2_filter_cleanup; - cln->data = stream; - - fc->send_chain = ngx_http_v2_send_chain; - fc->need_last_buf = 1; - - return ngx_http_v2_filter_send(fc, stream); -} - - -static ngx_int_t -ngx_http_v2_push_resources(ngx_http_request_t *r) -{ - u_char *start, *end, *last; - ngx_int_t rc; - ngx_str_t path; - ngx_uint_t i, push; - ngx_table_elt_t **h; - ngx_http_v2_loc_conf_t *h2lcf; - ngx_http_complex_value_t *pushes; - ngx_str_t binary[NGX_HTTP_V2_PUSH_HEADERS]; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2 push resources"); - - ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t)); - - h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); - - if (h2lcf->pushes) { - pushes = h2lcf->pushes->elts; - - for (i = 0; i < h2lcf->pushes->nelts; i++) { - - if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { - return NGX_ERROR; - } - - if (path.len == 0) { - continue; - } - - if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { - continue; - } - - rc = ngx_http_v2_push_resource(r, &path, binary); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_ABORT) { - return NGX_OK; - } - - /* NGX_OK, NGX_DECLINED */ - } - } - - if (!h2lcf->push_preload) { - return NGX_OK; - } - - h = r->headers_out.link.elts; - - for (i = 0; i < r->headers_out.link.nelts; i++) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2 parse link: \"%V\"", &h[i]->value); - - start = h[i]->value.data; - end = h[i]->value.data + h[i]->value.len; - - next_link: - - while (start < end && *start == ' ') { start++; } - - if (start == end || *start++ != '<') { - continue; - } - - while (start < end && *start == ' ') { start++; } - - for (last = start; last < end && *last != '>'; last++) { - /* void */ - } - - if (last == start || last == end) { - continue; - } - - path.len = last - start; - path.data = start; - - start = last + 1; - - while (start < end && *start == ' ') { start++; } - - if (start == end) { - continue; - } - - if (*start == ',') { - start++; - goto next_link; - } - - if (*start++ != ';') { - continue; - } - - last = ngx_strlchr(start, end, ','); - - if (last == NULL) { - last = end; - } - - push = 0; - - for ( ;; ) { - - while (start < last && *start == ' ') { start++; } - - if (last - start >= 6 - && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) - { - start += 6; - - if (start == last || *start == ' ' || *start == ';') { - push = 0; - break; - } - - goto next_param; - } - - if (last - start >= 11 - && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) - { - start += 11; - - if (start == last || *start == ' ' || *start == ';') { - push = 1; - } - - goto next_param; - } - - if (last - start >= 4 - && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) - { - start += 4; - - while (start < last && *start == ' ') { start++; } - - if (start == last || *start++ != '"') { - goto next_param; - } - - for ( ;; ) { - - while (start < last && *start == ' ') { start++; } - - if (last - start >= 7 - && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) - { - start += 7; - - if (start < last && (*start == ' ' || *start == '"')) { - push = 1; - break; - } - } - - while (start < last && *start != ' ' && *start != '"') { - start++; - } - - if (start == last) { - break; - } - - if (*start == '"') { - break; - } - - start++; - } - } - - next_param: - - start = ngx_strlchr(start, last, ';'); - - if (start == NULL) { - break; - } - - start++; - } - - if (push) { - while (path.len && path.data[path.len - 1] == ' ') { - path.len--; - } - } - - if (push && path.len - && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) - { - rc = ngx_http_v2_push_resource(r, &path, binary); - - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc == NGX_ABORT) { - return NGX_OK; - } - - /* NGX_OK, NGX_DECLINED */ - } - - if (last < end) { - start = last + 1; - goto next_link; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path, - ngx_str_t *binary) -{ - u_char *start, *pos, *tmp; - size_t len; - ngx_str_t *value; - ngx_uint_t i; - ngx_table_elt_t **h; - ngx_connection_t *fc; - ngx_http_v2_stream_t *stream; - ngx_http_v2_out_frame_t *frame; - ngx_http_v2_connection_t *h2c; - ngx_http_v2_push_header_t *ph; - - fc = r->connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource"); - - stream = r->stream; - h2c = stream->connection; - - if (!ngx_path_separator(path->data[0])) { - ngx_log_error(NGX_LOG_WARN, fc->log, 0, - "non-absolute path \"%V\" not pushed", path); - return NGX_DECLINED; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 pushing:%ui limit:%ui", - h2c->pushing, h2c->concurrent_pushes); - - if (h2c->pushing >= h2c->concurrent_pushes) { - return NGX_ABORT; - } - - if (h2c->last_push == 0x7ffffffe) { - return NGX_ABORT; - } - - if (path->len > NGX_HTTP_V2_MAX_FIELD) { - return NGX_DECLINED; - } - - if (r->headers_in.host == NULL) { - return NGX_ABORT; - } - - ph = ngx_http_v2_push_headers; - - if (binary[0].len) { - tmp = ngx_palloc(r->pool, path->len); - if (tmp == NULL) { - return NGX_ERROR; - } - - } else { - len = path->len; - - for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) { - h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset); - - if (*h) { - len = ngx_max(len, (*h)->value.len); - } - } - - tmp = ngx_palloc(r->pool, len); - if (tmp == NULL) { - return NGX_ERROR; - } - - for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) { - h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset); - - if (*h == NULL) { - continue; - } - - value = &(*h)->value; - - len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len; - - pos = ngx_pnalloc(r->pool, len); - if (pos == NULL) { - return NGX_ERROR; - } - - binary[i].data = pos; - - *pos++ = ngx_http_v2_inc_indexed(ph[i].index); - pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp); - - binary[i].len = pos - binary[i].data; - } - } - - len = (h2c->table_update ? 1 : 0) - + 1 - + 1 + NGX_HTTP_V2_INT_OCTETS + path->len - + 1; - - for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) { - len += binary[i].len; - } - - pos = ngx_pnalloc(r->pool, len); - if (pos == NULL) { - return NGX_ERROR; - } - - start = pos; - - if (h2c->table_update) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 table size update: 0"); - *pos++ = (1 << 5) | 0; - h2c->table_update = 0; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 push header: \":method: GET\""); - - *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 push header: \":path: %V\"", path); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); - pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp); - -#if (NGX_HTTP_SSL) - if (fc->ssl) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 push header: \":scheme: https\""); - *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX); - - } else -#endif - { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 push header: \":scheme: http\""); - *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); - } - - for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) { - h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset); - - if (*h == NULL) { - continue; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 push header: \"%V: %V\"", - &ph[i].name, &(*h)->value); - - pos = ngx_cpymem(pos, binary[i].data, binary[i].len); - } - - frame = ngx_http_v2_create_push_frame(r, start, pos); - if (frame == NULL) { - return NGX_ERROR; - } - - ngx_http_v2_queue_blocked_frame(h2c, frame); - - stream->queued++; - - stream = ngx_http_v2_push_stream(stream, path); - - if (stream) { - stream->request->request_length = pos - start; - return NGX_OK; - } - - return NGX_ERROR; -} - - -static ngx_http_v2_out_frame_t * -ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, - u_char *end, ngx_uint_t fin) -{ - u_char type, flags; - size_t rest, frame_size; - ngx_buf_t *b; - ngx_chain_t *cl, **ll; - ngx_http_v2_stream_t *stream; - ngx_http_v2_out_frame_t *frame; - - stream = r->stream; - rest = end - pos; - - frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t)); - if (frame == NULL) { - return NULL; - } - - frame->handler = ngx_http_v2_headers_frame_handler; - frame->stream = stream; - frame->length = rest; - frame->blocked = 1; - frame->fin = fin; - - ll = &frame->first; - - type = NGX_HTTP_V2_HEADERS_FRAME; - flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; - frame_size = stream->connection->frame_size; - - for ( ;; ) { - if (rest <= frame_size) { - frame_size = rest; - flags |= NGX_HTTP_V2_END_HEADERS_FLAG; - } - - b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE); - if (b == NULL) { - return NULL; - } - - b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type); - *b->last++ = flags; - b->last = ngx_http_v2_write_sid(b->last, stream->node->id); - - b->tag = (ngx_buf_tag_t) &ngx_http_v2_module; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - - *ll = cl; - ll = &cl->next; - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NULL; - } - - b->pos = pos; - - pos += frame_size; - - b->last = pos; - b->start = b->pos; - b->end = b->last; - b->temporary = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - - *ll = cl; - ll = &cl->next; - - rest -= frame_size; - - if (rest) { - frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE; - - type = NGX_HTTP_V2_CONTINUATION_FRAME; - flags = NGX_HTTP_V2_NO_FLAG; - continue; - } - - b->last_buf = fin; - cl->next = NULL; - frame->last = cl; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2:%ui create HEADERS frame %p: len:%uz fin:%ui", - stream->node->id, frame, frame->length, fin); - - return frame; - } -} - - -static ngx_http_v2_out_frame_t * -ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end) -{ - u_char type, flags; - size_t rest, frame_size, len; - ngx_buf_t *b; - ngx_chain_t *cl, **ll; - ngx_http_v2_stream_t *stream; - ngx_http_v2_out_frame_t *frame; - ngx_http_v2_connection_t *h2c; - - stream = r->stream; - h2c = stream->connection; - rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos); - - frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t)); - if (frame == NULL) { - return NULL; - } - - frame->handler = ngx_http_v2_push_frame_handler; - frame->stream = stream; - frame->length = rest; - frame->blocked = 1; - frame->fin = 0; - - ll = &frame->first; - - type = NGX_HTTP_V2_PUSH_PROMISE_FRAME; - flags = NGX_HTTP_V2_NO_FLAG; - frame_size = h2c->frame_size; - - for ( ;; ) { - if (rest <= frame_size) { - frame_size = rest; - flags |= NGX_HTTP_V2_END_HEADERS_FLAG; - } - - b = ngx_create_temp_buf(r->pool, - NGX_HTTP_V2_FRAME_HEADER_SIZE - + ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) - ? NGX_HTTP_V2_STREAM_ID_SIZE : 0)); - if (b == NULL) { - return NULL; - } - - b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type); - *b->last++ = flags; - b->last = ngx_http_v2_write_sid(b->last, stream->node->id); - - b->tag = (ngx_buf_tag_t) &ngx_http_v2_module; - - if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) { - h2c->last_push += 2; - - b->last = ngx_http_v2_write_sid(b->last, h2c->last_push); - len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE; - - } else { - len = frame_size; - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - - *ll = cl; - ll = &cl->next; - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NULL; - } - - b->pos = pos; - - pos += len; - - b->last = pos; - b->start = b->pos; - b->end = b->last; - b->temporary = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NULL; - } - - cl->buf = b; - - *ll = cl; - ll = &cl->next; - - rest -= frame_size; - - if (rest) { - frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE; - - type = NGX_HTTP_V2_CONTINUATION_FRAME; - continue; - } - - cl->next = NULL; - frame->last = cl; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2:%ui create PUSH_PROMISE frame %p: " - "sid:%ui len:%uz", - stream->node->id, frame, h2c->last_push, - frame->length); - - return frame; - } -} - - -static ngx_http_v2_out_frame_t * -ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) -{ - u_char *pos, *start, *tmp; - size_t len, tmp_len; - ngx_uint_t i; - ngx_list_part_t *part; - ngx_table_elt_t *header; - ngx_connection_t *fc; - - fc = r->connection; - len = 0; - tmp_len = 0; - - part = &r->headers_out.trailers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (header[i].hash == 0) { - continue; - } - - if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_CRIT, fc->log, 0, - "too long response trailer name: \"%V\"", - &header[i].key); - return NULL; - } - - if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_CRIT, fc->log, 0, - "too long response trailer value: \"%V: %V\"", - &header[i].key, &header[i].value); - return NULL; - } - - len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len - + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; - - if (header[i].key.len > tmp_len) { - tmp_len = header[i].key.len; - } - - if (header[i].value.len > tmp_len) { - tmp_len = header[i].value.len; - } - } - - if (len == 0) { - return NGX_HTTP_V2_NO_TRAILERS; - } - - tmp = ngx_palloc(r->pool, tmp_len); - pos = ngx_pnalloc(r->pool, len); - - if (pos == NULL || tmp == NULL) { - return NULL; - } - - start = pos; - - part = &r->headers_out.trailers.part; - header = part->elts; - - for (i = 0; /* void */; i++) { - - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } - - part = part->next; - header = part->elts; - i = 0; - } - - if (header[i].hash == 0) { - continue; - } - -#if (NGX_DEBUG) - if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { - ngx_strlow(tmp, header[i].key.data, header[i].key.len); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output trailer: \"%*s: %V\"", - header[i].key.len, tmp, &header[i].value); - } -#endif - - *pos++ = 0; - - pos = ngx_http_v2_write_name(pos, header[i].key.data, - header[i].key.len, tmp); - - pos = ngx_http_v2_write_value(pos, header[i].value.data, - header[i].value.len, tmp); - } - - return ngx_http_v2_create_headers_frame(r, start, pos, 1); -} - - -static ngx_chain_t * -ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) -{ - off_t size, offset; - size_t rest, frame_size; - ngx_chain_t *cl, *out, **ln; - ngx_http_request_t *r; - ngx_http_v2_stream_t *stream; - ngx_http_v2_loc_conf_t *h2lcf; - ngx_http_v2_out_frame_t *frame, *trailers; - ngx_http_v2_connection_t *h2c; - - r = fc->data; - stream = r->stream; - -#if (NGX_SUPPRESS_WARN) - size = 0; -#endif - - while (in) { - size = ngx_buf_size(in->buf); - - if (size || in->buf->last_buf) { - break; - } - - in = in->next; - } - - if (in == NULL || stream->out_closed) { - - if (stream->queued) { - fc->write->active = 1; - fc->write->ready = 0; - - } else { - fc->buffered &= ~NGX_HTTP_V2_BUFFERED; - } - - return NULL; - } - - h2c = stream->connection; - - if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { - fc->write->active = 1; - fc->write->ready = 0; - return in; - } - - if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_CHAIN_ERROR; - } - - cl->buf = in->buf; - in->buf = cl->buf->shadow; - - offset = ngx_buf_in_memory(in->buf) - ? (cl->buf->pos - in->buf->pos) - : (cl->buf->file_pos - in->buf->file_pos); - - cl->next = stream->free_bufs; - stream->free_bufs = cl; - - } else { - offset = 0; - } - - if (limit == 0 || limit > (off_t) h2c->send_window) { - limit = h2c->send_window; - } - - if (limit > stream->send_window) { - limit = (stream->send_window > 0) ? stream->send_window : 0; - } - - h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); - - frame_size = (h2lcf->chunk_size < h2c->frame_size) - ? h2lcf->chunk_size : h2c->frame_size; - - trailers = NGX_HTTP_V2_NO_TRAILERS; - -#if (NGX_SUPPRESS_WARN) - cl = NULL; -#endif - - for ( ;; ) { - if ((off_t) frame_size > limit) { - frame_size = (size_t) limit; - } - - ln = &out; - rest = frame_size; - - while ((off_t) rest >= size) { - - if (offset) { - cl = ngx_http_v2_filter_get_shadow(stream, in->buf, - offset, size); - if (cl == NULL) { - return NGX_CHAIN_ERROR; - } - - offset = 0; - - } else { - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_CHAIN_ERROR; - } - - cl->buf = in->buf; - } - - *ln = cl; - ln = &cl->next; - - rest -= (size_t) size; - in = in->next; - - if (in == NULL) { - frame_size -= rest; - rest = 0; - break; - } - - size = ngx_buf_size(in->buf); - } - - if (rest) { - cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest); - if (cl == NULL) { - return NGX_CHAIN_ERROR; - } - - cl->buf->flush = 0; - cl->buf->last_buf = 0; - - *ln = cl; - - offset += rest; - size -= rest; - } - - if (cl->buf->last_buf) { - trailers = ngx_http_v2_create_trailers_frame(r); - if (trailers == NULL) { - return NGX_CHAIN_ERROR; - } - - if (trailers != NGX_HTTP_V2_NO_TRAILERS) { - cl->buf->last_buf = 0; - } - } - - if (frame_size || cl->buf->last_buf) { - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, - out, cl); - if (frame == NULL) { - return NGX_CHAIN_ERROR; - } - - ngx_http_v2_queue_frame(h2c, frame); - - h2c->send_window -= frame_size; - - stream->send_window -= frame_size; - stream->queued++; - } - - if (in == NULL) { - - if (trailers != NGX_HTTP_V2_NO_TRAILERS) { - ngx_http_v2_queue_frame(h2c, trailers); - stream->queued++; - } - - break; - } - - limit -= frame_size; - - if (limit == 0) { - break; - } - } - - if (offset) { - cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size); - if (cl == NULL) { - return NGX_CHAIN_ERROR; - } - - in->buf = cl->buf; - ngx_free_chain(r->pool, cl); - } - - if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) { - return NGX_CHAIN_ERROR; - } - - if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { - fc->write->active = 1; - fc->write->ready = 0; - } - - return in; -} - - -static ngx_chain_t * -ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf, - off_t offset, off_t size) -{ - ngx_buf_t *chunk; - ngx_chain_t *cl; - - cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); - if (cl == NULL) { - return NULL; - } - - chunk = cl->buf; - - ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); - - chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow; - chunk->shadow = buf; - - if (ngx_buf_in_memory(chunk)) { - chunk->pos += offset; - chunk->last = chunk->pos + size; - } - - if (chunk->in_file) { - chunk->file_pos += offset; - chunk->file_last = chunk->file_pos + size; - } - - return cl; -} - - -static ngx_http_v2_out_frame_t * -ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream, - size_t len, ngx_chain_t *first, ngx_chain_t *last) -{ - u_char flags; - ngx_buf_t *buf; - ngx_chain_t *cl; - ngx_http_v2_out_frame_t *frame; - - frame = stream->free_frames; - - if (frame) { - stream->free_frames = frame->next; - - } else { - frame = ngx_palloc(stream->request->pool, - sizeof(ngx_http_v2_out_frame_t)); - if (frame == NULL) { - return NULL; - } - } - - flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, - "http2:%ui create DATA frame %p: len:%uz flags:%ui", - stream->node->id, frame, len, (ngx_uint_t) flags); - - cl = ngx_chain_get_free_buf(stream->request->pool, - &stream->free_frame_headers); - if (cl == NULL) { - return NULL; - } - - buf = cl->buf; - - if (buf->start == NULL) { - buf->start = ngx_palloc(stream->request->pool, - NGX_HTTP_V2_FRAME_HEADER_SIZE); - if (buf->start == NULL) { - return NULL; - } - - buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE; - buf->last = buf->end; - - buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module; - buf->memory = 1; - } - - buf->pos = buf->start; - buf->last = buf->pos; - - buf->last = ngx_http_v2_write_len_and_type(buf->last, len, - NGX_HTTP_V2_DATA_FRAME); - *buf->last++ = flags; - - buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id); - - cl->next = first; - first = cl; - - last->buf->flush = 1; - - frame->first = first; - frame->last = last; - frame->handler = ngx_http_v2_data_frame_handler; - frame->stream = stream; - frame->length = len; - frame->blocked = 0; - frame->fin = last->buf->last_buf; - - return frame; -} - - -static ngx_inline ngx_int_t -ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c, - ngx_http_v2_stream_t *stream) -{ - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui windows: conn:%uz stream:%z", - stream->node->id, h2c->send_window, stream->send_window); - - if (stream->send_window <= 0) { - stream->exhausted = 1; - return NGX_DECLINED; - } - - if (h2c->send_window == 0) { - ngx_http_v2_waiting_queue(h2c, stream); - return NGX_DECLINED; - } - - return NGX_OK; -} - - -static void -ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c, - ngx_http_v2_stream_t *stream) -{ - ngx_queue_t *q; - ngx_http_v2_stream_t *s; - - if (stream->waiting) { - return; - } - - stream->waiting = 1; - - for (q = ngx_queue_last(&h2c->waiting); - q != ngx_queue_sentinel(&h2c->waiting); - q = ngx_queue_prev(q)) - { - s = ngx_queue_data(q, ngx_http_v2_stream_t, queue); - - if (s->node->rank < stream->node->rank - || (s->node->rank == stream->node->rank - && s->node->rel_weight >= stream->node->rel_weight)) - { - break; - } - } - - ngx_queue_insert_after(q, &stream->queue); -} - - -static ngx_inline ngx_int_t -ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream) -{ - stream->blocked = 1; - - if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) { - fc->error = 1; - return NGX_ERROR; - } - - stream->blocked = 0; - - if (stream->queued) { - fc->buffered |= NGX_HTTP_V2_BUFFERED; - fc->write->active = 1; - fc->write->ready = 0; - return NGX_AGAIN; - } - - fc->buffered &= ~NGX_HTTP_V2_BUFFERED; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c, - ngx_http_v2_out_frame_t *frame) -{ - ngx_chain_t *cl, *ln; - ngx_http_v2_stream_t *stream; - - stream = frame->stream; - cl = frame->first; - - for ( ;; ) { - if (cl->buf->pos != cl->buf->last) { - frame->first = cl; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui HEADERS frame %p was sent partially", - stream->node->id, frame); - - return NGX_AGAIN; - } - - ln = cl->next; - - if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { - cl->next = stream->free_frame_headers; - stream->free_frame_headers = cl; - - } else { - cl->next = stream->free_bufs; - stream->free_bufs = cl; - } - - if (cl == frame->last) { - break; - } - - cl = ln; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui HEADERS frame %p was sent", - stream->node->id, frame); - - stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE - + frame->length; - - ngx_http_v2_handle_frame(stream, frame); - - ngx_http_v2_handle_stream(h2c, stream); - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c, - ngx_http_v2_out_frame_t *frame) -{ - ngx_chain_t *cl, *ln; - ngx_http_v2_stream_t *stream; - - stream = frame->stream; - cl = frame->first; - - for ( ;; ) { - if (cl->buf->pos != cl->buf->last) { - frame->first = cl; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui PUSH_PROMISE frame %p was sent partially", - stream->node->id, frame); - - return NGX_AGAIN; - } - - ln = cl->next; - - if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { - cl->next = stream->free_frame_headers; - stream->free_frame_headers = cl; - - } else { - cl->next = stream->free_bufs; - stream->free_bufs = cl; - } - - if (cl == frame->last) { - break; - } - - cl = ln; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui PUSH_PROMISE frame %p was sent", - stream->node->id, frame); - - stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE - + frame->length; - - ngx_http_v2_handle_frame(stream, frame); - - ngx_http_v2_handle_stream(h2c, stream); - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c, - ngx_http_v2_out_frame_t *frame) -{ - ngx_buf_t *buf; - ngx_chain_t *cl, *ln; - ngx_http_v2_stream_t *stream; - - stream = frame->stream; - cl = frame->first; - - if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { - - if (cl->buf->pos != cl->buf->last) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui DATA frame %p was sent partially", - stream->node->id, frame); - - return NGX_AGAIN; - } - - ln = cl->next; - - cl->next = stream->free_frame_headers; - stream->free_frame_headers = cl; - - if (cl == frame->last) { - goto done; - } - - cl = ln; - } - - for ( ;; ) { - if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { - buf = cl->buf->shadow; - - if (ngx_buf_in_memory(buf)) { - buf->pos = cl->buf->pos; - } - - if (buf->in_file) { - buf->file_pos = cl->buf->file_pos; - } - } - - if (ngx_buf_size(cl->buf) != 0) { - - if (cl != frame->first) { - frame->first = cl; - ngx_http_v2_handle_stream(h2c, stream); - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui DATA frame %p was sent partially", - stream->node->id, frame); - - return NGX_AGAIN; - } - - ln = cl->next; - - if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { - cl->next = stream->free_bufs; - stream->free_bufs = cl; - - } else { - ngx_free_chain(stream->request->pool, cl); - } - - if (cl == frame->last) { - goto done; - } - - cl = ln; - } - -done: - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2:%ui DATA frame %p was sent", - stream->node->id, frame); - - stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE; - - ngx_http_v2_handle_frame(stream, frame); - - ngx_http_v2_handle_stream(h2c, stream); - - return NGX_OK; -} - - -static ngx_inline void -ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream, - ngx_http_v2_out_frame_t *frame) -{ - ngx_http_request_t *r; - - r = stream->request; - - r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; - - if (frame->fin) { - stream->out_closed = 1; - } - - frame->next = stream->free_frames; - stream->free_frames = frame; - - stream->queued--; -} - - -static ngx_inline void -ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c, - ngx_http_v2_stream_t *stream) -{ - ngx_event_t *wev; - ngx_connection_t *fc; - - if (stream->waiting || stream->blocked) { - return; - } - - fc = stream->request->connection; - - if (!fc->error && stream->exhausted) { - return; - } - - wev = fc->write; - - wev->active = 0; - wev->ready = 1; - - if (!fc->error && wev->delayed) { - return; - } - - ngx_post_event(wev, &ngx_posted_events); -} - - -static void -ngx_http_v2_filter_cleanup(void *data) -{ - ngx_http_v2_stream_t *stream = data; - - size_t window; - ngx_event_t *wev; - ngx_queue_t *q; - ngx_http_v2_out_frame_t *frame, **fn; - ngx_http_v2_connection_t *h2c; - - if (stream->waiting) { - stream->waiting = 0; - ngx_queue_remove(&stream->queue); - } - - if (stream->queued == 0) { - return; - } - - window = 0; - h2c = stream->connection; - fn = &h2c->last_out; - - for ( ;; ) { - frame = *fn; - - if (frame == NULL) { - break; - } - - if (frame->stream == stream && !frame->blocked) { - *fn = frame->next; - - window += frame->length; - - if (--stream->queued == 0) { - break; - } - - continue; - } - - fn = &frame->next; - } - - if (h2c->send_window == 0 && window) { - - while (!ngx_queue_empty(&h2c->waiting)) { - q = ngx_queue_head(&h2c->waiting); - - ngx_queue_remove(q); - - stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); - - stream->waiting = 0; - - wev = stream->request->connection->write; - - wev->active = 0; - wev->ready = 1; - - if (!wev->delayed) { - ngx_post_event(wev, &ngx_posted_events); - } - } - } - - h2c->send_window += window; -} - - -static ngx_int_t -ngx_http_v2_filter_init(ngx_conf_t *cf) -{ - ngx_http_next_header_filter = ngx_http_top_header_filter; - ngx_http_top_header_filter = ngx_http_v2_header_filter; - - return NGX_OK; -} |