diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h index 1cfd9fe84..a5886631b 100644 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -25,8 +25,9 @@ #define NGX_HTTP_CACHE_KEY_LEN 16 #define NGX_HTTP_CACHE_ETAG_LEN 42 +#define NGX_HTTP_CACHE_VARY_LEN 42 -#define NGX_HTTP_CACHE_VERSION 2 +#define NGX_HTTP_CACHE_VERSION 3 typedef struct { @@ -71,6 +72,8 @@ struct ngx_http_cache_s { time_t date; ngx_str_t etag; + ngx_str_t vary; + u_char variant[NGX_HTTP_CACHE_KEY_LEN]; size_t header_start; size_t body_start; @@ -112,6 +115,9 @@ typedef struct { u_short body_start; u_char etag_len; u_char etag[NGX_HTTP_CACHE_ETAG_LEN]; + u_char vary_len; + u_char vary[NGX_HTTP_CACHE_VARY_LEN]; + u_char variant[NGX_HTTP_CACHE_KEY_LEN]; } ngx_http_file_cache_header_t; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 2eebc3068..e1b16e9c6 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -29,6 +29,10 @@ static ngx_http_file_cache_node_t * ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key); static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, + size_t len, u_char *hash); +static void ngx_http_file_cache_vary_header(ngx_http_request_t *r, + ngx_md5_t *md5, ngx_str_t *name); static void ngx_http_file_cache_cleanup(void *data); static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache); static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache); @@ -519,6 +523,23 @@ ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c) return NGX_DECLINED; } + if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" has incorrect vary length", + c->file.name.data); + return NGX_DECLINED; + } + + if (h->vary_len) { + ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant); + + if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache vary mismatch"); + return NGX_DECLINED; + } + } + c->buf->last += n; c->valid_sec = h->valid_sec; @@ -870,6 +891,94 @@ ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, } +static void +ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len, + u_char *hash) +{ + u_char *p, *last; + ngx_str_t name; + ngx_md5_t md5; + u_char buf[NGX_HTTP_CACHE_VARY_LEN]; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache vary: \"%*s\"", len, vary); + + ngx_md5_init(&md5); + + ngx_strlow(buf, vary, len); + + p = buf; + last = buf + len; + + while (p < last) { + + while (p < last && (*p == ' ' || *p == ',')) { p++; } + + name.data = p; + + while (p < last && *p != ',' && *p != ' ') { p++; } + + name.len = p - name.data; + + if (name.len == 0) { + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache vary: %V", &name); + + ngx_md5_update(&md5, name.data, name.len); + ngx_md5_update(&md5, (u_char *) ":", sizeof(":") - 1); + + ngx_http_file_cache_vary_header(r, &md5, &name); + + ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1); + } + + ngx_md5_final(hash, &md5); +} + + +static void +ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5, + ngx_str_t *name) +{ + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + part = &r->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; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len != name->len) { + continue; + } + + if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) { + continue; + } + + ngx_md5_update(md5, header[i].value.data, header[i].value.len); + } +} + + void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) { @@ -901,6 +1010,19 @@ ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) ngx_memcpy(h->etag, c->etag.data, c->etag.len); } + if (c->vary.len) { + if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) { + /* should not happen */ + c->vary.len = NGX_HTTP_CACHE_VARY_LEN; + } + + h->vary_len = (u_char) c->vary.len; + ngx_memcpy(h->vary, c->vary.data, c->vary.len); + + ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant); + ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN); + } + p = buf + sizeof(ngx_http_file_cache_header_t); p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key)); @@ -1093,6 +1215,19 @@ ngx_http_file_cache_update_header(ngx_http_request_t *r) ngx_memcpy(h.etag, c->etag.data, c->etag.len); } + if (c->vary.len) { + if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) { + /* should not happen */ + c->vary.len = NGX_HTTP_CACHE_VARY_LEN; + } + + h.vary_len = (u_char) c->vary.len; + ngx_memcpy(h.vary, c->vary.data, c->vary.len); + + ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant); + ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN); + } + (void) ngx_write_file(&file, (u_char *) &h, sizeof(ngx_http_file_cache_header_t), 0); diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 9a70b8c7e..df7e5f4ea 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -4160,7 +4160,17 @@ ngx_http_upstream_process_vary(ngx_http_request_t *r, return NGX_OK; } - u->cacheable = 0; + if (r->cache == NULL) { + return NGX_OK; + } + + if (h->value.len > NGX_HTTP_CACHE_VARY_LEN + || (h->value.len == 1 && h->value.data[0] == '*')) + { + u->cacheable = 0; + } + + r->cache->vary = h->value; #endif