一 概述 ngx_srcache 是基于子请求的透明缓存层,能够为各种 location 提供缓存服务。通常,memc-nginx-module 与此模块一起使用以提供具体的缓存服务。 但是任何提供 REST 接口的模块都可以用作此模块使用的 fetch 和 store 子请求。
二 指令 1. srcache_fetch 1 2 3 4 syntax: srcache_fetch  <method> <uri> <args>? default: no  context: http, server, location, location if phase: post-access 
在 access 阶段注册处理函数,请求到来时会发出 Nginx 子请求以进行缓存查找。子请求收到非 200 应答时触发 cache-miss,会进行后续阶段的处理;当子请求收到 200 应答时会触发 cache-hit 处理,会使用子请求收到内容进行应答。确定应答头、应答体 
2. srcache_fetch_skip 1 2 3 4 syntax: srcache_fetch_skip  <flag> default: srcache_fetch_skip 0  context: http, server, location, location if phase: post-access 
使用 flag 作为条件,用来判断是否需要继续进行缓存查询。flag 可以是变量,当 flag 不是空字符串并且不为 “0” 时将跳过缓存查找。
3. srcache_store 1 2 3 4 syntax: srcache_store  <method> <uri> <args>? default: no  context: http, server, location, location if phase: output-filter 
该指令注册一个输出过滤器处理函数,该处理函数将发出 Nginx 子请求,将当前主请求的响应保存到缓存中。 子请求的状态代码将被忽略。
默认情况下响应状态码、响应头(部分响应头未存储)、响应包体都会被存储在缓存中。可以通过 srcache_store_pass_header 或 srcache_store_hide_header 指令控制允许缓存的应答头。原始响应数据会被立即响应到客户端,srcache 不会影响主请求。
4. srcache_store_max_size 1 2 3 4 syntax: srcache_store_max_size  <size> default: srcache_store_max_size 0  context: http, server, location, location if phase: output-header-filter 
当响应包体大于 srcache_store_max_size 设置,模块不会将当前应答存储缓存中。
5. srcache_store_skip 1 2 3 4 syntax: srcache_store_skip  <flag> default: srcache_store_skip 0  context: http, server, location, location if phase: output-header-filter 
与 srcache_fetch_skip 指令类似,只不过 store_skip 指令用来控制是否需要进行缓存存储。
6. srcache_store_statuses 1 2 3 4 syntax: srcache_store_statuses  <status1> <status2> .. default: srcache_store_statuses 200  301  302  context: http, server, location, location if phase: output-header-filter 
指令用来实现根据后端状态码来决定是否进行缓存。
7. srcache_store_ranges 1 2 3 4 syntax: srcache_store_ranges  on |off  default: srcache_store_ranges off  context: http, server, location, location if phase: output-body-filter 
store_ranges 用来控制部分应答的存储,当配置为 on 时会对部分内容响应进行缓存存储,此时必须将 $http_range 添加到缓存 key 中。例如:
1 2 3 4 5 location  / {    set  $key  "$uri $args $http_range " ;     srcache_fetch  GET /memc $key ;     srcache_store  PUT /memc $key ; } 
1 2 3 4 syntax: srcache_header_buffer_size  <size> default: srcache_header_buffer_size 4k /8k  context: http, server, location, location if phase: output-header-filter 
控制用来接收单个响应头的缓冲区大小。
1 2 3 4 syntax: srcache_store_hide_header  <header> default: no  context: http, server, location, location if phase: output-header-filter 
用来设置不需要进行缓存的应答头。在缓存存储时,默认已经将以下响应头过滤:
1 2 3 4 5 6 7 8 9 Connection Keep-Alive Proxy-Authenticate Proxy-Authorization TE Trailers Transfer-Encoding Upgrade Set-Cookie 
使用以下配置可以额外过滤掉 X-Foo、Last-Modified 响应头:
1 2 srcache_store_hide_header  X-Foo;srcache_store_hide_header  Last-Modified;
1 2 3 4 syntax: srcache_store_pass_header  <header> default: no  context: http, server, location, location if phase: output-header-filter 
与 store_hide_header 指令相反,store_pass_header 用来控制必须存储的响应头。
11. srcache_methods 1 2 3 4 syntax: srcache_methods  <method>... default: srcache_methods GET HEAD context: http, server, location phase: post-access, output-header-filter 
srcache_methods 用来控制能够进行缓存 fetch、store 的请求方法。如果请求方法未在此列表中将跳过缓存处理。method 取值范围:GET, HEAD, POST, PUT, DELETE。
12. srcache_ignore_content_encoding 1 2 3 4 syntax: srcache_ignore_content_encoding  on |off  default: srcache_ignore_content_encoding off  context: http, server, location, location if phase: output-header-filter 
当关闭此指令(默认设置)时,非空的 Content-Encoding 响应标头将导致 srcache_store 跳过将整个响应的缓存存储,并向 nginx 的 error.log 文件写入 warn 日志,如下所示
1 2 [warn] 12500#0: *1 srcache_store skipped due to response header "Content-Encoding: gzip"             (maybe you forgot to disable compression on the backend?) 
启用此指令将忽略 Content-Encoding 响应标头,并将其存储在缓存中(并且也不会写 warn 日志)。
13. srcache_request_cache_control 1 2 3 4 syntax: srcache_request_cache_control  on |off  default: srcache_request_cache_control off  context: http, server, location phase: post-access, output-header-filter 
request_cache_control 指令用来控制是否根据请求头来执行不同的缓存策略,例如是否跳过缓存查询、是否跳过缓存存储。指令开启时,在缓存获取阶段如果请求头中有 Cache-Control: no-cache 或者 Pragma: no-cache,会跳过缓存处理;在缓存存储阶段,如果请求头中有 Cache-Control: no-store,会跳过缓存存储处理。
关闭此指令将不关心请求头中的缓存控制指令。
14. srcache_response_cache_control 1 2 3 4 syntax: srcache_response_cache_control  on |off  default: srcache_response_cache_control on  context: http, server, location phase: output-header-filter 
与 request_cache_control 类似,response_cache_control 用来控制业务端应答头中缓存控制指令是否启用。当指令开启时,如果应答头中有 Cache-Control: private|no-store|no-cache|max-age=0 或 Expires: 小于当前时间 将跳过缓存处理。
该指令优先于 srcache_store_no_store,srcache_store_no_cache 和 srcache_store_private 指令。 
15. srcache_store_no_store 1 2 3 4 syntax: srcache_store_no_store  on |off  default: srcache_store_no_store off  context: http, server, location phase: output-header-filter 
启用此指令,在满足其他缓存存储条件下,将强制具有 Cache-Control:no-store 应答头的响应存储在缓存中。 默认为关闭。
16. srcache_store_no_cache 1 2 3 4 syntax: srcache_store_no_cache  on |off  default: srcache_store_no_cache off  context: http, server, location phase: output-header-filter 
启用此指令,在满足其他缓存存储条件下,将强制具有 Cache-Control:no-cache 应答头的响应存储在缓存中。 默认为关闭。
17. srcache_store_private 1 2 3 4 syntax: srcache_store_private  on |off  default: srcache_store_private off  context: http, server, location phase: output-header-filter 
启用此指令,在满足其他缓存存储条件下,将强制具有 Cache-Control:private 应答头的响应存储在缓存中。 默认为关闭。
18. srcache_default_expire 1 2 3 4 syntax: srcache_default_expire  <time> default: srcache_default_expire 60s  context: http, server, location, location if phase: output-header-filter 
设置缓存的默认过期时间,如果响应头中有 Cache-Control: max-age=N 或 Expires 将根据响应头设置缓存有效期。
19. srcache_max_expire 1 2 3 4 syntax: srcache_max_expire  <time> default: srcache_max_expire 0  context: http, server, location, location if phase: output-header-filter 
该伪指令控制 $srcache_expire 变量值所允许的最大到期时间。 此设置优先于其他计算方法。
20. $srcache_expire srcache_expire 变量是当前响应存储在缓存中的有效时间段(以秒为单位)。 计算值的算法如下:
当响应头中有 Cache-Control: max-age=N 头时,将使用 N 作为缓存有效期; 
当响应头中有 Expires 时,会使用 Expires 指定时间与当前时间相减获得缓存有效期; 
否则使用 srcache_default_expire 指令指定的有效期。 
 
在以上三步计算完成后,会将有效期与 srcache_max_expire 设置值相比较,如果超过 srcache_max_expire 设置值,则设置为 srcache_max_expire 值。
三 实现 srcache 模块同时介入 NGX_HTTP_ACCESS_PHASE 、 HEADER_FILTER、BODY_FILTER 处理阶段。缓存的读取需要 NGX_HTTP_ACCESS_PHASE 阶段与 HEADER|BODY_FILTER阶段配合实现;缓存的设置在 BODY_FILTER 阶段实现。缓存的读取、设置是在一个状态循环中实现(有些过于复杂),HEADER|BODY_FILTER 除了给主请求使用外还给子请求使用,在其中判断缓存查找是否成功、将查找缓存拷贝到 ctx 中。
1. 注册处理函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 static  ngx_int_t ngx_http_srcache_post_config (ngx_conf_t  *cf) {     int                               multi_http_blocks;     ngx_int_t                         rc;     ngx_http_handler_pt             *h;     ngx_http_core_main_conf_t        *cmcf;     ngx_http_srcache_main_conf_t     *smcf;     rc = ngx_http_srcache_add_variables(cf);     if  (rc != NGX_OK) {         return  rc;     }     smcf = ngx_http_conf_get_module_main_conf(cf,                                               ngx_http_srcache_filter_module);     if  (ngx_http_srcache_prev_cycle != ngx_cycle) {         ngx_http_srcache_prev_cycle = ngx_cycle;         multi_http_blocks = 0 ;     } else  {         multi_http_blocks = 1 ;     }     if  (multi_http_blocks || smcf->module_used) {         dd("using ngx-srcache" );                  rc = ngx_http_srcache_filter_init(cf);         if  (rc != NGX_OK) {             return  rc;         }         cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);                  h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);         if  (h == NULL ) {             return  NGX_ERROR;         }         *h = ngx_http_srcache_access_handler;     }     return  NGX_OK; } 
2. 读缓存上半部 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 ngx_int_t ngx_http_srcache_access_handler (ngx_http_request_t  *r) {     ngx_str_t                        skip;     ngx_int_t                        rc;     ngx_http_srcache_loc_conf_t     *conf;     ngx_http_srcache_main_conf_t    *smcf;     ngx_http_srcache_ctx_t          *ctx;     ngx_chain_t                     *cl;     size_t                           len;     unsigned                         no_store;          conf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module);          if  (conf->fetch == NULL  && conf->store == NULL ) {         dd("bypass: %.*s" , (int ) r->uri.len, r->uri.data);         return  NGX_DECLINED;     }     dd("store defined? %p" , conf->store);     dd("req method: %lu" , (unsigned  long ) r->method);     dd("cache methods: %lu" , (unsigned  long ) conf->cache_methods);          if  (!(r->method & conf->cache_methods)) {         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log , 0 ,                        "srcache_fetch and srcache_store skipped due to request "                         "method %V" , &r->method_name);         return  NGX_DECLINED;     }          if  (conf->req_cache_control         && ngx_http_srcache_request_no_cache(r, &no_store) == NGX_OK)     {         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log , 0 ,                        "srcache_fetch skipped due to request headers "                         "\"Cache-Control: no-cache\" or \"Pragma: no-cache\"" );         if  (!no_store) {                          ctx = ngx_pcalloc(r->pool,                               sizeof (ngx_http_srcache_filter_module));             if  (ctx == NULL ) {                 return  NGX_ERROR;             }             ngx_http_set_ctx(r, ctx, ngx_http_srcache_filter_module);         } else  {             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log , 0 ,                            "srcache_store skipped due to request header "                             "\"Cache-Control: no-store\"" );         }         return  NGX_DECLINED;     }          if  (conf->fetch_skip != NULL          && ngx_http_complex_value(r, conf->fetch_skip, &skip) == NGX_OK         && skip.len         && (skip.len != 1  || skip.data[0 ] != '0' ))     {         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log , 0 ,                        "srcache_fetch skipped due to the true value fed into "                         "srcache_fetch_skip: \"%V\"" , &skip);                  ctx = ngx_pcalloc(r->pool, sizeof (ngx_http_srcache_filter_module));         if  (ctx == NULL ) {             return  NGX_ERROR;         }         ngx_http_set_ctx(r, ctx, ngx_http_srcache_filter_module);         return  NGX_DECLINED;     }          ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module);     if  (ctx != NULL ) {                  if  (ctx->waiting_subrequest) {             dd("waiting subrequest" );             return  NGX_AGAIN;         }         if  (ctx->waiting_request_body) {             return  NGX_AGAIN;         }                  if  (ctx->request_body_done == 1 ) {             ctx->request_body_done = 0 ;             goto  do_fetch_subrequest;         }                  if  (ctx->request_done) {             dd("request done" );             if  (ngx_http_post_request(r, NULL ) != NGX_OK) {                 return  NGX_ERROR;             }             if  (!ctx->from_cache) {                 return  NGX_DECLINED;             }             dd("sending header" );                                       if  (ctx->body_from_cache) {                 len = 0 ;                 for  (cl = ctx->body_from_cache; cl->next; cl = cl->next) {                     len += ngx_buf_size(cl->buf);                 }                 len += ngx_buf_size(cl->buf);                 cl->buf->last_buf = 1 ;                 r->headers_out.content_length_n = len;                 rc = ngx_http_send_header(r);                 dd("srcache fetch header returned %d" , (int ) rc);                 if  (rc == NGX_ERROR || rc > NGX_OK) {                     return  rc;                 } #if  1                 if  (r->header_only) {                     return  NGX_HTTP_OK;                 } #endif                  if  (!r->filter_finalize) {                     rc = ngx_http_output_filter(r, ctx->body_from_cache);                     if  (rc == NGX_ERROR || rc > NGX_OK) {                         return  rc;                     }                 }                 dd("sent body from cache: %d" , (int ) rc);                 dd("finalize from here..." );                 ngx_http_finalize_request(r, rc);                                  return  NGX_DONE;             }                          return  NGX_DECLINED;         }     } else  {         ctx = ngx_pcalloc(r->pool, sizeof (ngx_http_srcache_filter_module));         if  (ctx == NULL ) {             return  NGX_ERROR;         }         ngx_http_set_ctx(r, ctx, ngx_http_srcache_filter_module);     }     smcf = ngx_http_get_module_main_conf(r, ngx_http_srcache_filter_module);     if  (!smcf->postponed_to_access_phase_end) {         ngx_http_core_main_conf_t        *cmcf;         ngx_http_phase_handler_t          tmp;         ngx_http_phase_handler_t         *ph;         ngx_http_phase_handler_t         *cur_ph;         ngx_http_phase_handler_t         *last_ph;         smcf->postponed_to_access_phase_end = 1 ;         cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);         ph = cmcf->phase_engine.handlers;         cur_ph = &ph[r->phase_handler];                  last_ph = &ph[cur_ph->next - 2 ];         if  (cur_ph < last_ph) {             dd("swaping the contents of cur_ph and last_ph..." );             tmp = *cur_ph;             memmove(cur_ph, cur_ph + 1 ,                     (last_ph - cur_ph) * sizeof  (ngx_http_phase_handler_t ));             *last_ph = tmp;             r->phase_handler--;              return  NGX_DECLINED;         }     }          if  (conf->fetch == NULL ) {         dd("fetch is not defined" );         return  NGX_DECLINED;     }     dd("running phase handler..." );          if  (!r->request_body) {         dd("reading request body: ctx = %p" , ctx);         rc = ngx_http_read_client_request_body(r,                                                ngx_http_srcache_post_read_body);         if  (rc == NGX_ERROR || rc > NGX_OK) { #if  (nginx_version < 1002006)                                               \     || (nginx_version >= 1003000 && nginx_version < 1003009)             r->main->count--; #endif              return  rc;         }                  if  (rc == NGX_AGAIN) {             ctx->waiting_request_body = 1 ;             return  NGX_AGAIN;         }              } do_fetch_subrequest:               rc = ngx_http_srcache_fetch_subrequest(r, conf, ctx);     if  (rc != NGX_OK) {         return  rc;     }     ctx->waiting_subrequest = 1 ;     dd("quit" );     return  NGX_AGAIN; } 
3. 读缓存下半部与设置缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 static  ngx_int_t ngx_http_srcache_body_filter (ngx_http_request_t  *r, ngx_chain_t  *in) {     ngx_http_srcache_ctx_t       *ctx, *pr_ctx;     ngx_int_t                     rc;     ngx_str_t                     skip;     ngx_chain_t                  *cl;     ngx_http_srcache_loc_conf_t  *slcf;     size_t                        len;     unsigned                      last;     dd_enter();     if  (in == NULL ) {         return  ngx_http_srcache_next_body_filter(r, NULL );     }     ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module);     if  (ctx == NULL  || ctx->from_cache || ctx->store_skip) {         dd("bypass: %.*s" , (int ) r->uri.len, r->uri.data);         return  ngx_http_srcache_next_body_filter(r, in);     }     if  (ctx->ignore_body || ctx->in_store_subrequest) {         dd("ignore body: ignore body %d, in store sr %d" ,            (int ) ctx->ignore_body, (int ) ctx->in_store_subrequest);         ngx_http_srcache_discard_bufs(r->pool, in);         return  NGX_OK;     }               if  (ctx->in_fetch_subrequest) {                           if  (ctx->parsing_cached_headers) {                          if  (ctx->process_header == NULL ) {                 dd("restore parent request header" );                 ctx->process_header = ngx_http_srcache_process_status_line;                 r->state = 0 ;              }             for  (cl = in; cl; cl = cl->next) {                 if  (ngx_buf_in_memory(cl->buf)) {                     dd("old pos %p, last %p" , cl->buf->pos, cl->buf->last);                     rc = ctx->process_header(r, cl->buf);                     if  (rc == NGX_AGAIN) {                         dd("AGAIN/OK: new pos %p, last %p" ,                            cl->buf->pos, cl->buf->last);                         continue ;                     }                                          if  (rc == NGX_ERROR) {                         r->state = 0 ;                          ctx->parsing_cached_headers = 0 ;                         ctx->ignore_body = 1 ;                         ngx_http_srcache_discard_bufs(r->pool, cl);                         pr_ctx = ngx_http_get_module_ctx(r->parent,                                               ngx_http_srcache_filter_module);                         if  (pr_ctx == NULL ) {                             return  NGX_ERROR;                         }                         pr_ctx->from_cache = 0 ;                         return  NGX_OK;                     }                                          dd("OK: new pos %p, last %p" , cl->buf->pos, cl->buf->last);                     dd("buf left: %.*s" , (int ) (cl->buf->last - cl->buf->pos),                        cl->buf->pos);                     ctx->parsing_cached_headers = 0 ;                     break ;                 }             }             if  (cl == NULL ) {                 return  NGX_OK;             }             if  (cl->buf->pos == cl->buf->last) {                 cl = cl->next;             }             if  (cl == NULL ) {                 return  NGX_OK;             }                          in = cl;         }         dd("save the cached response body for parent" );         pr_ctx = ngx_http_get_module_ctx(r->parent,                                          ngx_http_srcache_filter_module);         if  (pr_ctx == NULL ) {             return  NGX_ERROR;         }                  rc = ngx_http_srcache_add_copy_chain(r->pool,                                              &pr_ctx->body_from_cache, in,                                              &last);         if  (rc != NGX_OK) {             return  NGX_ERROR;         }         if  (last) {             ctx->seen_subreq_eof = 1 ;         }                  ngx_http_srcache_discard_bufs(r->pool, in);         return  NGX_OK;     }          if  (ctx->store_response) {         dd("storing the response: %p" , in);         slcf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module);         if  (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT             && ctx->http_status == NGX_HTTP_OK)         {             u_char *p;             if  (!slcf->store_ranges) {                 ctx->store_response = 0 ;                 goto  done;             }             dd("fix 206 status code" );                          cl = ctx->body_to_cache;             assert(cl && cl->buf && cl->buf->last - cl->buf->pos > 12 );             p = cl->buf->pos + sizeof ("HTTP/1.x 20" ) - 1 ;             *p = '6' ;             ctx->http_status = NGX_HTTP_PARTIAL_CONTENT;         }         for  (cl = in; cl; cl = cl->next) {             if  (ngx_buf_in_memory(cl->buf)) {                 len = ngx_buf_size(cl->buf);                 ctx->response_length += len;                 ctx->response_body_length += len;             }         }         if  (slcf->store_max_size != 0              && ctx->response_length > slcf->store_max_size)         {             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log , 0 ,                            "srcache_store bypassed because response body "                             "exceeded maximum size: %z (limit is: %z)" ,                            ctx->response_length, slcf->store_max_size);             ctx->store_response = 0 ;             goto  done;         }         rc = ngx_http_srcache_add_copy_chain(r->pool, &ctx->body_to_cache,                                              in, &last);         if  (rc != NGX_OK) {             ctx->store_response = 0 ;             goto  done;         }         if  (last && r == r->main) { #if  1             if  (r->headers_out.content_length_n >                 (off_t ) ctx->response_body_length)             {                 ngx_log_error(NGX_LOG_ERR, r->connection->log , 0 ,                               "srcache_store: skipped because response body "                                "truncated: %O > %uz" ,                               r->headers_out.content_length_n,                               ctx->response_body_length);                 ctx->store_response = 0 ;                 goto  done;             }             if  (r->headers_out.status >= NGX_HTTP_SPECIAL_RESPONSE                 && r->headers_out.status != ctx->http_status)             {                                  ngx_log_error(NGX_LOG_ERR, r->connection->log , 0 ,                               "srcache_store: skipped due to new error status "                                "code %ui (old: %ui)" ,                               r->headers_out.status, ctx->http_status);                 ctx->store_response = 0 ;                 goto  done;             } #endif              if  (slcf->store_skip != NULL                  && ngx_http_complex_value(r, slcf->store_skip, &skip) == NGX_OK                 && skip.len                 && (skip.len != 1  || skip.data[0 ] != '0' ))             {                 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log , 0 ,                                "srcache_store skipped due to the true value in "                                 "srcache_store_skip: \"%V\"" , &skip);                 ctx->store_response = 0 ;                 goto  done;             }             rc = ngx_http_srcache_store_subrequest(r, ctx);             if  (rc != NGX_OK) {                 ctx->store_response = 0 ;                 goto  done;             }         }     } else  {         dd("NO store response" );     } done:     return  ngx_http_srcache_next_body_filter(r, in); }