# HG changeset patch # User Maxim Dounin # Date 1721320785 -10800 # Node ID e46e1ea89ccd3cf2443ecce05b0e377d4f34ca89 # Parent 8cdab3d89c44c26a91562eac5d421a727e80489e Upstream: $upstream_cache_age variable. The variable reflects response age, including the time spent in the cache and the upstream response age as obtained from the "Age" header. If the response wasn't cached, the variable reflects the "Age" header of the upstream response. If the intended use case is to cache responses as per HTTP/1.1 caching model, the $upstream_cache_age variable can be used to provide the "Age" header with the "add_header" directive, such as: add_header Age $upstream_cache_age; This now removes the "Age" header if it was present. Further, the "expires" directives now removes the "Age" header if it was present in the response, as the "expires" directive assumes zero age when it adds "Expires" and "Cache-Control" headers. diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -93,6 +93,10 @@ static ngx_http_set_header_t ngx_http_s offsetof(ngx_http_headers_out_t, etag), ngx_http_set_response_header }, + { ngx_string("Age"), + offsetof(ngx_http_headers_out_t, age), + ngx_http_set_response_header }, + { ngx_null_string, 0, NULL } }; @@ -352,6 +356,11 @@ ngx_http_set_expires(ngx_http_request_t } } + if (r->headers_out.age) { + r->headers_out.age->hash = 0; + r->headers_out.age = NULL; + } + e = r->headers_out.expires; if (e == NULL) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -278,6 +278,7 @@ typedef struct { ngx_table_elt_t *cache_control; ngx_table_elt_t *link; + ngx_table_elt_t *age; ngx_str_t *override_charset; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -31,6 +31,8 @@ static ngx_int_t ngx_http_upstream_cache ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_cache_age(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); #endif static void ngx_http_upstream_init_request(ngx_http_request_t *r); @@ -297,7 +299,8 @@ static ngx_http_upstream_header_t ngx_h { ngx_string("Age"), ngx_http_upstream_process_age, 0, - ngx_http_upstream_copy_header_line, 0, 0 }, + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, age), 0 }, { ngx_string("X-Accel-Expires"), ngx_http_upstream_process_accel_expires, 0, @@ -436,6 +439,10 @@ static ngx_http_variable_t ngx_http_ups ngx_http_upstream_cache_etag, 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + { ngx_string("upstream_cache_age"), NULL, + ngx_http_upstream_cache_age, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + #endif { ngx_string("upstream_http_"), NULL, ngx_http_upstream_header_variable, @@ -6211,6 +6218,60 @@ ngx_http_upstream_cache_etag(ngx_http_re return NGX_OK; } + +static ngx_int_t +ngx_http_upstream_cache_age(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + time_t now, age; + + if (r->upstream == NULL) { + v->not_found = 1; + return NGX_OK; + } + + if (!r->cached + || r->cache == NULL + || r->upstream->cache_status == NGX_HTTP_CACHE_REVALIDATED) + { + if (r->upstream->headers_in.age == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = r->upstream->headers_in.age->value.len; + v->data = r->upstream->headers_in.age->value.data; + + return NGX_OK; + } + + p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + now = ngx_time(); + age = now - r->cache->date; + + if (r->cache->date > now) { + age = 0; + } + + age += r->upstream->headers_in.age_n; + + v->len = ngx_sprintf(p, "%T", age) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + #endif