comparison src/http/ngx_http_upstream.c @ 8041:0784ab86ad08

Upstream: fixed X-Accel-Expires/Cache-Control/Expires handling. Previously, if caching was disabled due to Expires in the past, nginx failed to cache the response even if it was cacheable as per subsequently parsed Cache-Control header (ticket #964). Similarly, if caching was disabled due to Expires in the past, "Cache-Control: no-cache" or "Cache-Control: max-age=0", caching was not used if it was cacheable as per subsequently parsed X-Accel-Expires header. Fix is to avoid disabling caching immediately after parsing Expires in the past or Cache-Control, but rather set flags which are later checked by ngx_http_upstream_process_headers() (and cleared by "Cache-Control: max-age" and X-Accel-Expires). Additionally, now X-Accel-Expires does not prevent parsing of cache control extensions, notably stale-while-revalidate and stale-if-error. This ensures that order of the X-Accel-Expires and Cache-Control headers is not important. Prodded by Vadim Fedorenko and Yugo Horie.
author Maxim Dounin <mdounin@mdounin.ru>
date Tue, 07 Jun 2022 00:07:12 +0300
parents e0cfab501dd1
children c7e25324be11
comparison
equal deleted inserted replaced
8040:e0cfab501dd1 8041:0784ab86ad08
2700 2700
2701 #if (NGX_HTTP_CACHE) 2701 #if (NGX_HTTP_CACHE)
2702 2702
2703 if (r->cache) { 2703 if (r->cache) {
2704 2704
2705 if (u->headers_in.no_cache || u->headers_in.expired) {
2706 u->cacheable = 0;
2707 }
2708
2705 if (u->cacheable) { 2709 if (u->cacheable) {
2706 time_t valid; 2710 time_t valid;
2707 2711
2708 valid = r->cache->valid_sec; 2712 valid = r->cache->valid_sec;
2709 2713
2793 ngx_table_elt_t *h; 2797 ngx_table_elt_t *h;
2794 ngx_http_upstream_header_t *hh; 2798 ngx_http_upstream_header_t *hh;
2795 ngx_http_upstream_main_conf_t *umcf; 2799 ngx_http_upstream_main_conf_t *umcf;
2796 2800
2797 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); 2801 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
2802
2803 if (u->headers_in.no_cache || u->headers_in.expired) {
2804 u->cacheable = 0;
2805 }
2798 2806
2799 if (u->headers_in.x_accel_redirect 2807 if (u->headers_in.x_accel_redirect
2800 && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT)) 2808 && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))
2801 { 2809 {
2802 ngx_http_upstream_finalize_request(r, u, NGX_DECLINED); 2810 ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
4789 4797
4790 if (r->cache == NULL) { 4798 if (r->cache == NULL) {
4791 return NGX_OK; 4799 return NGX_OK;
4792 } 4800 }
4793 4801
4794 if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {
4795 return NGX_OK;
4796 }
4797
4798 start = h->value.data; 4802 start = h->value.data;
4799 last = start + h->value.len; 4803 last = start + h->value.len;
4804
4805 if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {
4806 goto extensions;
4807 }
4800 4808
4801 if (ngx_strlcasestrn(start, last, (u_char *) "no-cache", 8 - 1) != NULL 4809 if (ngx_strlcasestrn(start, last, (u_char *) "no-cache", 8 - 1) != NULL
4802 || ngx_strlcasestrn(start, last, (u_char *) "no-store", 8 - 1) != NULL 4810 || ngx_strlcasestrn(start, last, (u_char *) "no-store", 8 - 1) != NULL
4803 || ngx_strlcasestrn(start, last, (u_char *) "private", 7 - 1) != NULL) 4811 || ngx_strlcasestrn(start, last, (u_char *) "private", 7 - 1) != NULL)
4804 { 4812 {
4805 u->cacheable = 0; 4813 u->headers_in.no_cache = 1;
4806 return NGX_OK; 4814 return NGX_OK;
4807 } 4815 }
4808 4816
4809 p = ngx_strlcasestrn(start, last, (u_char *) "s-maxage=", 9 - 1); 4817 p = ngx_strlcasestrn(start, last, (u_char *) "s-maxage=", 9 - 1);
4810 offset = 9; 4818 offset = 9;
4830 u->cacheable = 0; 4838 u->cacheable = 0;
4831 return NGX_OK; 4839 return NGX_OK;
4832 } 4840 }
4833 4841
4834 if (n == 0) { 4842 if (n == 0) {
4835 u->cacheable = 0; 4843 u->headers_in.no_cache = 1;
4836 return NGX_OK; 4844 return NGX_OK;
4837 } 4845 }
4838 4846
4839 r->cache->valid_sec = ngx_time() + n; 4847 r->cache->valid_sec = ngx_time() + n;
4840 } 4848 u->headers_in.expired = 0;
4849 }
4850
4851 extensions:
4841 4852
4842 p = ngx_strlcasestrn(start, last, (u_char *) "stale-while-revalidate=", 4853 p = ngx_strlcasestrn(start, last, (u_char *) "stale-while-revalidate=",
4843 23 - 1); 4854 23 - 1);
4844 4855
4845 if (p) { 4856 if (p) {
4930 } 4941 }
4931 4942
4932 expires = ngx_parse_http_time(h->value.data, h->value.len); 4943 expires = ngx_parse_http_time(h->value.data, h->value.len);
4933 4944
4934 if (expires == NGX_ERROR || expires < ngx_time()) { 4945 if (expires == NGX_ERROR || expires < ngx_time()) {
4935 u->cacheable = 0; 4946 u->headers_in.expired = 1;
4936 return NGX_OK; 4947 return NGX_OK;
4937 } 4948 }
4938 4949
4939 r->cache->valid_sec = expires; 4950 r->cache->valid_sec = expires;
4940 } 4951 }
4994 case NGX_ERROR: 5005 case NGX_ERROR:
4995 return NGX_OK; 5006 return NGX_OK;
4996 5007
4997 default: 5008 default:
4998 r->cache->valid_sec = ngx_time() + n; 5009 r->cache->valid_sec = ngx_time() + n;
5010 u->headers_in.no_cache = 0;
5011 u->headers_in.expired = 0;
4999 return NGX_OK; 5012 return NGX_OK;
5000 } 5013 }
5001 } 5014 }
5002 5015
5003 p++; 5016 p++;
5005 5018
5006 n = ngx_atoi(p, len); 5019 n = ngx_atoi(p, len);
5007 5020
5008 if (n != NGX_ERROR) { 5021 if (n != NGX_ERROR) {
5009 r->cache->valid_sec = n; 5022 r->cache->valid_sec = n;
5023 u->headers_in.no_cache = 0;
5024 u->headers_in.expired = 0;
5010 } 5025 }
5011 } 5026 }
5012 #endif 5027 #endif
5013 5028
5014 return NGX_OK; 5029 return NGX_OK;