1 
2 /*
3  * Copyright (C) Yichun Zhang (agentzh)
4  */
5 
6 
7 #ifndef DDEBUG
8 #define DDEBUG 0
9 #endif
10 #include "ddebug.h"
11 
12 
13 #include "ngx_http_srcache_filter_module.h"
14 #include "ngx_http_srcache_util.h"
15 #include "ngx_http_srcache_var.h"
16 #include "ngx_http_srcache_fetch.h"
17 #include "ngx_http_srcache_store.h"
18 #include "ngx_http_srcache_headers.h"
19 
20 
21 static void *ngx_http_srcache_create_loc_conf(ngx_conf_t *cf);
22 static char *ngx_http_srcache_merge_loc_conf(ngx_conf_t *cf, void *parent,
23     void *child);
24 static ngx_int_t ngx_http_srcache_post_config(ngx_conf_t *cf);
25 static char *ngx_http_srcache_conf_set_request(ngx_conf_t *cf,
26     ngx_command_t *cmd, void *conf);
27 static void *ngx_http_srcache_create_main_conf(ngx_conf_t *cf);
28 static char *ngx_http_srcache_init_main_conf(ngx_conf_t *cf, void *conf);
29 static char *ngx_http_srcache_store_statuses(ngx_conf_t *cf,
30     ngx_command_t *cmd, void *conf);
31 
32 
33 static volatile ngx_cycle_t  *ngx_http_srcache_prev_cycle = NULL;
34 
35 
36 static ngx_str_t  ngx_http_srcache_hide_headers[] = {
37     ngx_string("Connection"),
38     ngx_string("Keep-Alive"),
39     ngx_string("Proxy-Authenticate"),
40     ngx_string("Proxy-Authorization"),
41     ngx_string("TE"),
42     ngx_string("Trailers"),
43     ngx_string("Transfer-Encoding"),
44     ngx_string("Upgrade"),
45     ngx_string("Set-Cookie"),
46     ngx_null_string
47 };
48 
49 
50 static ngx_conf_bitmask_t  ngx_http_srcache_cache_method_mask[] = {
51     { ngx_string("GET"),  NGX_HTTP_GET},
52     { ngx_string("HEAD"), NGX_HTTP_HEAD },
53     { ngx_string("POST"), NGX_HTTP_POST },
54     { ngx_string("PUT"), NGX_HTTP_PUT },
55     { ngx_string("DELETE"), NGX_HTTP_DELETE },
56     { ngx_null_string, 0 }
57 };
58 
59 
60 static ngx_command_t  ngx_http_srcache_commands[] = {
61 
62     { ngx_string("srcache_buffer"),
63       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
64           |NGX_CONF_TAKE1,
65       ngx_conf_set_size_slot,
66       NGX_HTTP_LOC_CONF_OFFSET,
67       offsetof(ngx_http_srcache_loc_conf_t, buf_size),
68       NULL },
69 
70     { ngx_string("srcache_fetch"),
71       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
72           |NGX_CONF_TAKE23,
73       ngx_http_srcache_conf_set_request,
74       NGX_HTTP_LOC_CONF_OFFSET,
75       offsetof(ngx_http_srcache_loc_conf_t, fetch),
76       NULL },
77 
78     { ngx_string("srcache_store"),
79       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
80           |NGX_CONF_TAKE23,
81       ngx_http_srcache_conf_set_request,
82       NGX_HTTP_LOC_CONF_OFFSET,
83       offsetof(ngx_http_srcache_loc_conf_t, store),
84       NULL },
85 
86     { ngx_string("srcache_store_max_size"),
87       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
88           |NGX_CONF_TAKE1,
89       ngx_conf_set_size_slot,
90       NGX_HTTP_LOC_CONF_OFFSET,
91       offsetof(ngx_http_srcache_loc_conf_t, store_max_size),
92       NULL },
93 
94     { ngx_string("srcache_fetch_skip"),
95       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
96           |NGX_CONF_TAKE1,
97       ngx_http_set_complex_value_slot,
98       NGX_HTTP_LOC_CONF_OFFSET,
99       offsetof(ngx_http_srcache_loc_conf_t, fetch_skip),
100       NULL },
101 
102     { ngx_string("srcache_store_skip"),
103       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
104           |NGX_CONF_TAKE1,
105       ngx_http_set_complex_value_slot,
106       NGX_HTTP_LOC_CONF_OFFSET,
107       offsetof(ngx_http_srcache_loc_conf_t, store_skip),
108       NULL },
109 
110     { ngx_string("srcache_store_statuses"),
111       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
112           |NGX_CONF_1MORE,
113       ngx_http_srcache_store_statuses,
114       NGX_HTTP_LOC_CONF_OFFSET,
115       0,
116       NULL },
117 
118     { ngx_string("srcache_methods"),
119       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
120       ngx_conf_set_bitmask_slot,
121       NGX_HTTP_LOC_CONF_OFFSET,
122       offsetof(ngx_http_srcache_loc_conf_t, cache_methods),
123       &ngx_http_srcache_cache_method_mask },
124 
125     { ngx_string("srcache_request_cache_control"),
126       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
127       ngx_conf_set_flag_slot,
128       NGX_HTTP_LOC_CONF_OFFSET,
129       offsetof(ngx_http_srcache_loc_conf_t, req_cache_control),
130       NULL },
131 
132     { ngx_string("srcache_store_private"),
133       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
134       ngx_conf_set_flag_slot,
135       NGX_HTTP_LOC_CONF_OFFSET,
136       offsetof(ngx_http_srcache_loc_conf_t, store_private),
137       NULL },
138 
139     { ngx_string("srcache_store_no_store"),
140       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
141       ngx_conf_set_flag_slot,
142       NGX_HTTP_LOC_CONF_OFFSET,
143       offsetof(ngx_http_srcache_loc_conf_t, store_no_store),
144       NULL },
145 
146     { ngx_string("srcache_store_no_cache"),
147       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
148       ngx_conf_set_flag_slot,
149       NGX_HTTP_LOC_CONF_OFFSET,
150       offsetof(ngx_http_srcache_loc_conf_t, store_no_cache),
151       NULL },
152 
153     { ngx_string("srcache_response_cache_control"),
154       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
155       ngx_conf_set_flag_slot,
156       NGX_HTTP_LOC_CONF_OFFSET,
157       offsetof(ngx_http_srcache_loc_conf_t, resp_cache_control),
158       NULL },
159 
160     { ngx_string("srcache_store_hide_header"),
161       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
162       ngx_conf_set_str_array_slot,
163       NGX_HTTP_LOC_CONF_OFFSET,
164       offsetof(ngx_http_srcache_loc_conf_t, hide_headers),
165       NULL },
166 
167     { ngx_string("srcache_store_pass_header"),
168       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
169       ngx_conf_set_str_array_slot,
170       NGX_HTTP_LOC_CONF_OFFSET,
171       offsetof(ngx_http_srcache_loc_conf_t, pass_headers),
172       NULL },
173 
174     { ngx_string("srcache_store_ranges"),
175       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
176       ngx_conf_set_flag_slot,
177       NGX_HTTP_LOC_CONF_OFFSET,
178       offsetof(ngx_http_srcache_loc_conf_t, store_ranges),
179       NULL },
180 
181     { ngx_string("srcache_ignore_content_encoding"),
182       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
183       ngx_conf_set_flag_slot,
184       NGX_HTTP_LOC_CONF_OFFSET,
185       offsetof(ngx_http_srcache_loc_conf_t, ignore_content_encoding),
186       NULL },
187 
188     { ngx_string("srcache_header_buffer_size"),
189       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
190           |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
191       ngx_conf_set_size_slot,
192       NGX_HTTP_LOC_CONF_OFFSET,
193       offsetof(ngx_http_srcache_loc_conf_t, header_buf_size),
194       NULL },
195 
196     { ngx_string("srcache_max_expire"),
197       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
198           |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
199       ngx_conf_set_sec_slot,
200       NGX_HTTP_LOC_CONF_OFFSET,
201       offsetof(ngx_http_srcache_loc_conf_t, max_expire),
202       NULL },
203 
204     { ngx_string("srcache_default_expire"),
205       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
206           |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
207       ngx_conf_set_sec_slot,
208       NGX_HTTP_LOC_CONF_OFFSET,
209       offsetof(ngx_http_srcache_loc_conf_t, default_expire),
210       NULL },
211 
212       ngx_null_command
213 };
214 
215 
216 static ngx_http_module_t  ngx_http_srcache_filter_module_ctx = {
217     NULL,                                  /* preconfiguration */
218     ngx_http_srcache_post_config,          /* postconfiguration */
219 
220     ngx_http_srcache_create_main_conf,     /* create main configuration */
221     ngx_http_srcache_init_main_conf,       /* init main configuration */
222 
223     NULL,                                  /* create server configuration */
224     NULL,                                  /* merge server configuration */
225 
226     ngx_http_srcache_create_loc_conf,      /* create location configuration */
227     ngx_http_srcache_merge_loc_conf        /* merge location configuration */
228 };
229 
230 
231 ngx_module_t  ngx_http_srcache_filter_module = {
232     NGX_MODULE_V1,
233     &ngx_http_srcache_filter_module_ctx,   /* module context */
234     ngx_http_srcache_commands,             /* module directives */
235     NGX_HTTP_MODULE,                       /* module type */
236     NULL,                                  /* init master */
237     NULL,                                  /* init module */
238     NULL,                                  /* init process */
239     NULL,                                  /* init thread */
240     NULL,                                  /* exit thread */
241     NULL,                                  /* exit process */
242     NULL,                                  /* exit master */
243     NGX_MODULE_V1_PADDING
244 };
245 
246 
247 static void *
ngx_http_srcache_create_loc_conf(ngx_conf_t * cf)248 ngx_http_srcache_create_loc_conf(ngx_conf_t *cf)
249 {
250     ngx_http_srcache_loc_conf_t  *conf;
251 
252     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_srcache_loc_conf_t));
253     if (conf == NULL) {
254         return NULL;
255     }
256 
257     /*
258      * set by ngx_pcalloc():
259      *
260      *      conf->fetch_skip = NULL;
261      *      conf->store_skip = NULL;
262      *      conf->cache_methods = 0;
263      *      conf->hide_headers_hash = { NULL, 0 };
264      *      conf->skip_content_type = 0;
265      *      conf->store_statuses = NULL;
266      */
267 
268     conf->fetch = NGX_CONF_UNSET_PTR;
269     conf->store = NGX_CONF_UNSET_PTR;
270 
271     conf->buf_size = NGX_CONF_UNSET_SIZE;
272     conf->store_max_size = NGX_CONF_UNSET_SIZE;
273     conf->header_buf_size = NGX_CONF_UNSET_SIZE;
274 
275     conf->req_cache_control = NGX_CONF_UNSET;
276     conf->resp_cache_control = NGX_CONF_UNSET;
277 
278     conf->store_private = NGX_CONF_UNSET;
279     conf->store_no_store = NGX_CONF_UNSET;
280     conf->store_no_cache = NGX_CONF_UNSET;
281     conf->store_ranges = NGX_CONF_UNSET;
282 
283     conf->max_expire = NGX_CONF_UNSET;
284     conf->default_expire = NGX_CONF_UNSET;
285 
286     conf->ignore_content_encoding = NGX_CONF_UNSET;
287 
288     conf->hide_headers = NGX_CONF_UNSET_PTR;
289     conf->pass_headers = NGX_CONF_UNSET_PTR;
290 
291     return conf;
292 }
293 
294 
295 static char *
ngx_http_srcache_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)296 ngx_http_srcache_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
297 {
298     ngx_http_srcache_loc_conf_t     *prev = parent;
299     ngx_http_srcache_loc_conf_t     *conf = child;
300     ngx_hash_init_t                  hash;
301 
302     ngx_conf_merge_ptr_value(conf->fetch, prev->fetch, NULL);
303     ngx_conf_merge_ptr_value(conf->store, prev->store, NULL);
304 
305     ngx_conf_merge_size_value(conf->buf_size, prev->buf_size,
306                               (size_t) ngx_pagesize);
307 
308     ngx_conf_merge_size_value(conf->store_max_size, prev->store_max_size, 0);
309 
310     ngx_conf_merge_size_value(conf->header_buf_size, prev->header_buf_size,
311                               (size_t) ngx_pagesize);
312 
313     if (conf->fetch_skip == NULL) {
314         conf->fetch_skip = prev->fetch_skip;
315     }
316 
317     if (conf->store_skip == NULL) {
318         conf->store_skip = prev->store_skip;
319     }
320 
321     if (conf->store_statuses == NULL) {
322         conf->store_statuses = prev->store_statuses;
323     }
324 
325     if (conf->cache_methods == 0) {
326         conf->cache_methods = prev->cache_methods;
327     }
328 
329     conf->cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
330 
331     ngx_conf_merge_value(conf->req_cache_control, prev->req_cache_control, 0);
332     ngx_conf_merge_value(conf->resp_cache_control, prev->resp_cache_control, 1);
333 
334     ngx_conf_merge_value(conf->store_private, prev->store_private, 0);
335     ngx_conf_merge_value(conf->store_no_store, prev->store_no_store, 0);
336     ngx_conf_merge_value(conf->store_no_cache, prev->store_no_cache, 0);
337     ngx_conf_merge_value(conf->store_ranges, prev->store_ranges, 0);
338 
339     ngx_conf_merge_value(conf->max_expire, prev->max_expire, 0);
340     ngx_conf_merge_value(conf->default_expire, prev->default_expire, 60);
341 
342     ngx_conf_merge_value(conf->ignore_content_encoding,
343                          prev->ignore_content_encoding, 0);
344 
345     hash.max_size = 512;
346     hash.bucket_size = ngx_align(64, ngx_cacheline_size);
347     hash.name = "srcache_store_hide_headers_hash";
348 
349     if (ngx_http_srcache_hide_headers_hash(cf, conf,
350              prev, ngx_http_srcache_hide_headers, &hash)
351         != NGX_OK)
352     {
353         return NGX_CONF_ERROR;
354     }
355 
356     return NGX_CONF_OK;
357 }
358 
359 
360 static char *
ngx_http_srcache_conf_set_request(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)361 ngx_http_srcache_conf_set_request(ngx_conf_t *cf, ngx_command_t *cmd,
362     void *conf)
363 {
364     char  *p = conf;
365 
366     ngx_http_srcache_request_t      **rpp;
367     ngx_http_srcache_request_t       *rp;
368     ngx_str_t                        *value;
369     ngx_str_t                        *method_name;
370     ngx_http_compile_complex_value_t  ccv;
371     ngx_http_srcache_main_conf_t     *smcf;
372 
373     rpp = (ngx_http_srcache_request_t **) (p + cmd->offset);
374 
375     if (*rpp != NGX_CONF_UNSET_PTR) {
376         return "is duplicate";
377     }
378 
379     smcf = ngx_http_conf_get_module_main_conf(cf,
380                                               ngx_http_srcache_filter_module);
381 
382     smcf->module_used = 1;
383 
384     value = cf->args->elts;
385 
386     *rpp = ngx_pcalloc(cf->pool, sizeof(ngx_http_srcache_request_t));
387     if (*rpp == NULL) {
388         return NGX_CONF_ERROR;
389     }
390 
391     rp = *rpp;
392 
393     method_name = &value[1];
394 
395     rp->method = ngx_http_srcache_parse_method_name(&method_name);
396 
397     if (rp->method == NGX_HTTP_UNKNOWN) {
398         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
399                            "%V specifies bad HTTP method %V",
400                            &cmd->name, method_name);
401 
402         return NGX_CONF_ERROR;
403     }
404 
405     rp->method_name = *method_name;
406 
407     /* compile the location arg */
408 
409     if (value[2].len == 0) {
410         ngx_memzero(&rp->location, sizeof(ngx_http_complex_value_t));
411 
412     } else {
413         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
414 
415         ccv.cf = cf;
416         ccv.value = &value[2];
417         ccv.complex_value = &rp->location;
418 
419         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
420             return NGX_CONF_ERROR;
421         }
422     }
423 
424     if (cf->args->nelts == 2 + 1) {
425         return NGX_CONF_OK;
426     }
427 
428     /* compile the args arg */
429 
430     if (value[3].len == 0) {
431         ngx_memzero(&rp->location, sizeof(ngx_http_complex_value_t));
432         return NGX_CONF_OK;
433     }
434 
435     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
436 
437     ccv.cf = cf;
438     ccv.value = &value[3];
439     ccv.complex_value = &rp->args;
440 
441     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
442         return NGX_CONF_ERROR;
443     }
444 
445     return NGX_CONF_OK;
446 }
447 
448 
449 static void *
ngx_http_srcache_create_main_conf(ngx_conf_t * cf)450 ngx_http_srcache_create_main_conf(ngx_conf_t *cf)
451 {
452     ngx_http_srcache_main_conf_t *smcf;
453 
454     smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_srcache_main_conf_t));
455     if (smcf == NULL) {
456         return NULL;
457     }
458 
459     /* set by ngx_pcalloc:
460      *      smcf->postponed_to_access_phase_end = 0;
461      *      smcf->module_used = 0;
462      *      smcf->headers_in_hash = { NULL, 0 };
463      */
464 
465     return smcf;
466 }
467 
468 
469 static char *
ngx_http_srcache_init_main_conf(ngx_conf_t * cf,void * conf)470 ngx_http_srcache_init_main_conf(ngx_conf_t *cf, void *conf)
471 {
472     ngx_http_srcache_main_conf_t *smcf = conf;
473 
474     ngx_array_t                     headers_in;
475     ngx_hash_key_t                 *hk;
476     ngx_hash_init_t                 hash;
477     ngx_http_srcache_header_t      *header;
478 
479     /* srcache_headers_in_hash */
480 
481     if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
482         != NGX_OK)
483     {
484         return NGX_CONF_ERROR;
485     }
486 
487     for (header = ngx_http_srcache_headers_in; header->name.len; header++) {
488         hk = ngx_array_push(&headers_in);
489         if (hk == NULL) {
490             return NGX_CONF_ERROR;
491         }
492 
493         hk->key = header->name;
494         hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
495         hk->value = header;
496     }
497 
498     hash.hash = &smcf->headers_in_hash;
499     hash.key = ngx_hash_key_lc;
500     hash.max_size = 512;
501     hash.bucket_size = ngx_align(64, ngx_cacheline_size);
502     hash.name = "srcache_headers_in_hash";
503     hash.pool = cf->pool;
504     hash.temp_pool = NULL;
505 
506     if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
507         return NGX_CONF_ERROR;
508     }
509 
510     return NGX_CONF_OK;
511 }
512 
513 
514 static ngx_int_t
ngx_http_srcache_post_config(ngx_conf_t * cf)515 ngx_http_srcache_post_config(ngx_conf_t *cf)
516 {
517     int                              multi_http_blocks;
518     ngx_int_t                        rc;
519     ngx_http_handler_pt             *h;
520     ngx_http_core_main_conf_t       *cmcf;
521     ngx_http_srcache_main_conf_t    *smcf;
522 
523     rc = ngx_http_srcache_add_variables(cf);
524     if (rc != NGX_OK) {
525         return rc;
526     }
527 
528     smcf = ngx_http_conf_get_module_main_conf(cf,
529                                               ngx_http_srcache_filter_module);
530 
531     if (ngx_http_srcache_prev_cycle != ngx_cycle) {
532         ngx_http_srcache_prev_cycle = ngx_cycle;
533         multi_http_blocks = 0;
534 
535     } else {
536         multi_http_blocks = 1;
537     }
538 
539     if (multi_http_blocks || smcf->module_used) {
540 
541         dd("using ngx-srcache");
542 
543         /* register our output filters */
544         rc = ngx_http_srcache_filter_init(cf);
545         if (rc != NGX_OK) {
546             return rc;
547         }
548 
549         cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
550 
551         /* register our access phase handler */
552 
553         h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
554         if (h == NULL) {
555             return NGX_ERROR;
556         }
557 
558         *h = ngx_http_srcache_access_handler;
559     }
560 
561     return NGX_OK;
562 }
563 
564 
565 static char *
ngx_http_srcache_store_statuses(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)566 ngx_http_srcache_store_statuses(ngx_conf_t *cf, ngx_command_t *cmd,
567     void *conf)
568 {
569     ngx_http_srcache_loc_conf_t     *slcf = conf;
570 
571     ngx_uint_t       i, n;
572     ngx_int_t        status;
573     ngx_str_t       *value;
574 
575     value = cf->args->elts;
576 
577     if (slcf->store_statuses) {
578         return "is duplicate";
579     }
580 
581     n = cf->args->nelts - 1;
582 
583     slcf->store_statuses = ngx_pnalloc(cf->pool, (n + 1) * sizeof(ngx_int_t));
584     if (slcf->store_statuses == NULL) {
585         return NGX_CONF_ERROR;
586     }
587 
588     for (i = 1; i <= n; i++) {
589         status = ngx_atoi(value[i].data, value[i].len);
590         if (status == NGX_ERROR) {
591             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
592                                "status code \"%V\" is an invalid number",
593                                &value[i]);
594 
595             return NGX_CONF_ERROR;
596         }
597 
598         if (status < 0) {
599             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
600                                "status code \"%V\" is not a positive number",
601                                &value[i]);
602 
603             return NGX_CONF_ERROR;
604         }
605 
606         slcf->store_statuses[i - 1] = status;
607     }
608 
609     slcf->store_statuses[i - 1] = 0;
610 
611     ngx_sort(slcf->store_statuses, n, sizeof(ngx_int_t),
612              ngx_http_srcache_cmp_int);
613 
614 #if 0
615     for (i = 0; i < n; i++) {
616         dd("status: %d", (int) slcf->store_statuses[i]);
617     }
618 #endif
619 
620     return NGX_CONF_OK;
621 }
622 
623 /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
624