# HG changeset patch # User Piotr Sikora <[email protected]> # Date 1491954701 25200 # Tue Apr 11 16:51:41 2017 -0700 # Node ID 432abcf285745ec6b6ee14d6487a4a42b51b7bce # Parent cde1f42da7b26b7d2b788f916685e736b919138e Cache: add HTTP/2 support.
Signed-off-by: Piotr Sikora <[email protected]> diff -r cde1f42da7b2 -r 432abcf28574 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -132,6 +132,14 @@ static ngx_int_t ngx_http_proxy_reinit_r static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in); static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_proxy_process_trailer(ngx_http_request_t *r, + ngx_buf_t *buf); +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) +static ngx_int_t ngx_http_proxy_serialize_headers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_chain_t *ngx_http_proxy_serialize_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +#endif static ngx_int_t ngx_http_proxy_input_filter_init(void *data); static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf); @@ -917,6 +925,7 @@ ngx_http_proxy_handler(ngx_http_request_ u->create_request = ngx_http_proxy_create_request; u->reinit_request = ngx_http_proxy_reinit_request; u->process_header = ngx_http_proxy_process_status_line; + u->process_trailer = ngx_http_proxy_process_trailer; u->abort_request = ngx_http_proxy_abort_request; u->finalize_request = ngx_http_proxy_finalize_request; r->state = 0; @@ -929,6 +938,14 @@ ngx_http_proxy_handler(ngx_http_request_ u->create_request = ngx_http_proxy_create_v2_request; u->output.output_filter = ngx_http_v2_upstream_output_filter; u->output.filter_ctx = r; + +#if (NGX_HTTP_CACHE) + + u->serialize_headers = ngx_http_proxy_serialize_headers; + u->serialize_trailers = ngx_http_proxy_serialize_trailers; + +#endif + } #endif @@ -2482,6 +2499,227 @@ ngx_http_proxy_process_header(ngx_http_r static ngx_int_t +ngx_http_proxy_process_trailer(ngx_http_request_t *r, ngx_buf_t *buf) +{ + ngx_int_t rc; + ngx_table_elt_t *h; + + for ( ;; ) { + + rc = ngx_http_parse_header_line(r, buf, 1); + + if (rc == NGX_OK) { + h = ngx_list_push(&r->upstream->headers_in.trailers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = r->header_hash; + + h->key.len = r->header_name_end - r->header_name_start; + h->value.len = r->header_end - r->header_start; + + h->key.data = ngx_pnalloc(r->pool, + h->key.len + 1 + h->value.len + 1 + h->key.len); + if (h->key.data == NULL) { + return NGX_ERROR; + } + + h->value.data = h->key.data + h->key.len + 1; + h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1; + + ngx_memcpy(h->key.data, r->header_name_start, h->key.len); + h->key.data[h->key.len] = '\0'; + ngx_memcpy(h->value.data, r->header_start, h->value.len); + h->value.data[h->value.len] = '\0'; + + if (h->key.len == r->lowcase_index) { + ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); + + } else { + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy trailer: \"%V: %V\"", + &h->key, &h->value); + + continue; + } + + if (rc == NGX_HTTP_PARSE_HEADER_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy trailer done"); + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid trailer"); + + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } +} + + +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_proxy_serialize_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = sizeof("HTTP/1.1 " CRLF) - 1 + u->headers_in.status_line.len + + sizeof(CRLF) - 1; + + part = &u->headers_in.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; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + b = &u->buffer; + + if (len > (size_t) (b->end - b->last)) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent headers too big to serialize for cache, " + "need:%uz available:%uz", len, b->end - b->last); + + return NGX_ERROR; + } + + b->last = ngx_copy(b->last, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1); + b->last = ngx_copy(b->last, u->headers_in.status_line.data, + u->headers_in.status_line.len); + *b->last++ = CR; *b->last++ = LF; + + part = &u->headers_in.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; + } + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = CR; *b->last++ = LF; + b->pos = b->last; + + return NGX_OK; +} + + +static ngx_chain_t * +ngx_http_proxy_serialize_trailers(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = 0; + + part = &u->headers_in.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; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + if (len == 0) { + return NULL; + } + + len += sizeof(CRLF) - 1; + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_CHAIN_ERROR; + } + + *b->last++ = CR; *b->last++ = LF; + + part = &u->headers_in.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; + } + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + return cl; +} + +#endif + + +static ngx_int_t ngx_http_proxy_input_filter_init(void *data) { ngx_http_request_t *r = data; @@ -3995,18 +4233,6 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - -#if (NGX_HTTP_V2) - - if (conf->http_version == NGX_HTTP_VERSION_20) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_cache\" doesn't work with " - "\"proxy_http_version 2.0\""); - return NGX_CONF_ERROR; - } - -#endif - rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers_cache, ngx_http_proxy_cache_headers); if (rc != NGX_OK) { @@ -4030,26 +4256,13 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t #endif } - if (conf->upstream.pass_trailers) { - - if (conf->http_version != NGX_HTTP_VERSION_20) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_pass_trailers\" requires " - "\"proxy_http_version 2.0\""); - return NGX_CONF_ERROR; - } - -#if (NGX_HTTP_CACHE) - - if (conf->upstream.cache) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_pass_trailers\" doesn't work with " - "\"proxy_cache\""); - return NGX_CONF_ERROR; - } - -#endif - + if (conf->upstream.pass_trailers + && conf->http_version != NGX_HTTP_VERSION_20) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_pass_trailers\" requires " + "\"proxy_http_version 2.0\""); + return NGX_CONF_ERROR; } return NGX_CONF_OK; diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_cache.h --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -27,7 +27,7 @@ #define NGX_HTTP_CACHE_ETAG_LEN 128 #define NGX_HTTP_CACHE_VARY_LEN 128 -#define NGX_HTTP_CACHE_VERSION 5 +#define NGX_HTTP_CACHE_VERSION 6 typedef struct { @@ -82,6 +82,7 @@ struct ngx_http_cache_s { size_t header_start; size_t body_start; + off_t body_length; off_t length; off_t fs_size; @@ -130,6 +131,7 @@ typedef struct { time_t error_sec; time_t last_modified; time_t date; + off_t body_length; uint32_t crc32; u_short valid_msec; u_short header_start; @@ -192,6 +194,8 @@ ngx_int_t ngx_http_file_cache_set_header void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf); void ngx_http_file_cache_update_header(ngx_http_request_t *r); ngx_int_t ngx_http_cache_send(ngx_http_request_t *); +ngx_buf_t *ngx_http_cache_get_trailers(ngx_http_request_t *r, + ngx_http_cache_t *c); void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf); time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status); diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_file_cache.c --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -605,6 +605,7 @@ ngx_http_file_cache_read(ngx_http_reques c->error_sec = h->error_sec; c->last_modified = h->last_modified; c->date = h->date; + c->body_length = h->body_length; c->valid_msec = h->valid_msec; c->body_start = h->body_start; c->etag.len = h->etag_len; @@ -1260,6 +1261,7 @@ ngx_http_file_cache_set_header(ngx_http_ h->error_sec = c->error_sec; h->last_modified = c->last_modified; h->date = c->date; + h->body_length = c->body_length; h->crc32 = c->crc32; h->valid_msec = (u_short) c->valid_msec; h->header_start = (u_short) c->header_start; @@ -1500,6 +1502,7 @@ ngx_http_file_cache_update_header(ngx_ht if (h.version != NGX_HTTP_CACHE_VERSION || h.last_modified != c->last_modified + || (h.body_length != -1 && h.body_length != c->body_length) || h.crc32 != c->crc32 || (size_t) h.header_start != c->header_start || (size_t) h.body_start != c->body_start) @@ -1523,6 +1526,7 @@ ngx_http_file_cache_update_header(ngx_ht h.error_sec = c->error_sec; h.last_modified = c->last_modified; h.date = c->date; + h.body_length = c->body_length; h.crc32 = c->crc32; h.valid_msec = (u_short) c->valid_msec; h.header_start = (u_short) c->header_start; @@ -1561,6 +1565,7 @@ done: ngx_int_t ngx_http_cache_send(ngx_http_request_t *r) { + off_t body_len; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; @@ -1571,7 +1576,14 @@ ngx_http_cache_send(ngx_http_request_t * ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache send: %s", c->file.name.data); - if (r != r->main && c->length - c->body_start == 0) { + if (c->body_length != -1) { + body_len = c->body_length; + + } else { + body_len = c->length - c->body_start; + } + + if (r != r->main && body_len == 0) { return ngx_http_send_header(r); } @@ -1594,9 +1606,9 @@ ngx_http_cache_send(ngx_http_request_t * } b->file_pos = c->body_start; - b->file_last = c->length; - - b->in_file = (c->length - c->body_start) ? 1: 0; + b->file_last = c->body_start + body_len; + + b->in_file = body_len ? 1 : 0; b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1; @@ -1611,6 +1623,42 @@ ngx_http_cache_send(ngx_http_request_t * } +ngx_buf_t * +ngx_http_cache_get_trailers(ngx_http_request_t *r, ngx_http_cache_t *c) +{ + off_t offset; + size_t len; + ssize_t n; + ngx_buf_t *b; + + offset = c->body_start + c->body_length + sizeof(CRLF) - 1; + len = c->length - offset; + + b = ngx_create_temp_buf(r->pool, len + sizeof(CRLF) - 1); + if (b == NULL) { + return NULL; + } + + n = ngx_read_file(&c->file, b->pos, len, offset); + + if (n == NGX_ERROR) { + return NULL; + } + + if ((size_t) n != len) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + ngx_read_file_n " read only %z of %z from \"%s\"", + n, len, c->file.name.data); + return NULL; + } + + b->last += n; + *b->last++ = CR; *b->last++ = LF; + + return b; +} + + void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf) { diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -58,6 +58,10 @@ static ngx_int_t ngx_http_upstream_proce ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r, ngx_http_upstream_t *u); +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) +static ngx_int_t ngx_http_upstream_serialize_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +#endif static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_send_response(ngx_http_request_t *r, @@ -1035,6 +1039,7 @@ static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) { ngx_int_t rc; + ngx_buf_t *trailers; ngx_http_cache_t *c; r->cached = 1; @@ -1076,6 +1081,24 @@ ngx_http_upstream_cache_send(ngx_http_re return NGX_DONE; } + if (c->body_length != -1 && u->conf->pass_trailers) { + + trailers = ngx_http_cache_get_trailers(r, c); + if (trailers == NULL) { + return NGX_ERROR; + } + + if (u->process_trailer(r, trailers) != NGX_OK) { + return NGX_ERROR; + } + + r->expect_trailers = 1; + + if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) { + return NGX_ERROR; + } + } + return ngx_http_cache_send(r); } @@ -2955,6 +2978,49 @@ ngx_http_upstream_process_trailers(ngx_h } +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_upstream_serialize_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + off_t body_len; + ngx_chain_t *cl; + + if (u->serialize_trailers == NULL) { + return NGX_OK; + } + + cl = u->serialize_trailers(r, u); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_ERROR; + } + + if (cl == NULL) { + return NGX_OK; + } + + body_len = u->pipe->temp_file->offset - (off_t) r->cache->body_start; + + if (ngx_write_chain_to_temp_file(u->pipe->temp_file, cl) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_write_file(&u->pipe->temp_file->file, + (u_char *) &body_len, sizeof(off_t), + offsetof(ngx_http_file_cache_header_t, body_length)) + != sizeof(off_t)) + { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ngx_http_upstream_t *u) @@ -3201,9 +3267,16 @@ ngx_http_upstream_send_response(ngx_http } } + if (valid && u->serialize_headers) { + if (u->serialize_headers(r, u) != NGX_OK) { + valid = 0; + } + } + if (valid) { r->cache->date = now; r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); + r->cache->body_length = -1; if (u->headers_in.status_n == NGX_HTTP_OK || u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT) @@ -4107,6 +4180,13 @@ ngx_http_upstream_process_request(ngx_ht if (u->cacheable) { if (p->upstream_done) { + + if (ngx_http_upstream_serialize_trailers(r, u) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ngx_http_file_cache_update(r, p->temp_file); } else if (p->upstream_eof) { @@ -4118,6 +4198,12 @@ ngx_http_upstream_process_request(ngx_ht || u->headers_in.content_length_n == tf->offset - (off_t) r->cache->body_start)) { + if (ngx_http_upstream_serialize_trailers(r, u) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ngx_http_file_cache_update(r, tf); } else { diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -359,6 +359,12 @@ struct ngx_http_upstream_s { ngx_int_t (*create_request)(ngx_http_request_t *r); ngx_int_t (*reinit_request)(ngx_http_request_t *r); ngx_int_t (*process_header)(ngx_http_request_t *r); + ngx_int_t (*process_trailer)(ngx_http_request_t *r, + ngx_buf_t *buf); + ngx_int_t (*serialize_headers)(ngx_http_request_t *r, + ngx_http_upstream_t *u); + ngx_chain_t *(*serialize_trailers)(ngx_http_request_t *r, + ngx_http_upstream_t *u); void (*abort_request)(ngx_http_request_t *r); void (*finalize_request)(ngx_http_request_t *r, ngx_int_t rc); _______________________________________________ nginx-devel mailing list [email protected] http://mailman.nginx.org/mailman/listinfo/nginx-devel
