[PATCH 14 of 14] Cache: add HTTP/2 support
Piotr Sikora
piotrsikora at google.com
Thu Jun 22 20:33:18 UTC 2017
# HG changeset patch
# User Piotr Sikora <piotrsikora at google.com>
# 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 <piotrsikora at google.com>
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);
More information about the nginx-devel
mailing list