Mercurial > hg > nginx
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; |