Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_file_cache.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 #include <ngx_md5.h>
12 
13 
14 static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,
15  ngx_http_cache_t *c);
16 static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);
17 static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,
18  ngx_http_cache_t *c);
19 static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,
20  ngx_http_cache_t *c);
21 #if (NGX_HAVE_FILE_AIO)
22 static void ngx_http_cache_aio_event_handler(ngx_event_t *ev);
23 #endif
24 static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
25  ngx_http_cache_t *c);
26 static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,
27  ngx_path_t *path);
29  ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
30 static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
32 static void ngx_http_file_cache_cleanup(void *data);
33 static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
34 static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
35 static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
36  ngx_queue_t *q, u_char *name);
37 static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
38 static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
39  ngx_str_t *path);
40 static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
41  ngx_str_t *path);
42 static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
43  ngx_str_t *path);
44 static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
45  ngx_http_cache_t *c);
46 static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
47  ngx_str_t *path);
48 
49 
51  ngx_string("MISS"),
52  ngx_string("BYPASS"),
53  ngx_string("EXPIRED"),
54  ngx_string("STALE"),
55  ngx_string("UPDATING"),
56  ngx_string("HIT")
57 };
58 
59 
60 static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
61 
62 
63 static ngx_int_t
64 ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
65 {
66  ngx_http_file_cache_t *ocache = data;
67 
68  size_t len;
69  ngx_uint_t n;
70  ngx_http_file_cache_t *cache;
71 
72  cache = shm_zone->data;
73 
74  if (ocache) {
75  if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
76  ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
77  "cache \"%V\" uses the \"%V\" cache path "
78  "while previously it used the \"%V\" cache path",
79  &shm_zone->shm.name, &cache->path->name,
80  &ocache->path->name);
81 
82  return NGX_ERROR;
83  }
84 
85  for (n = 0; n < 3; n++) {
86  if (cache->path->level[n] != ocache->path->level[n]) {
87  ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
88  "cache \"%V\" had previously different levels",
89  &shm_zone->shm.name);
90  return NGX_ERROR;
91  }
92  }
93 
94  cache->sh = ocache->sh;
95 
96  cache->shpool = ocache->shpool;
97  cache->bsize = ocache->bsize;
98 
99  cache->max_size /= cache->bsize;
100 
101  if (!cache->sh->cold || cache->sh->loading) {
102  cache->path->loader = NULL;
103  }
104 
105  return NGX_OK;
106  }
107 
108  cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
109 
110  if (shm_zone->shm.exists) {
111  cache->sh = cache->shpool->data;
112  cache->bsize = ngx_fs_bsize(cache->path->name.data);
113 
114  return NGX_OK;
115  }
116 
117  cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
118  if (cache->sh == NULL) {
119  return NGX_ERROR;
120  }
121 
122  cache->shpool->data = cache->sh;
123 
124  ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
125  ngx_http_file_cache_rbtree_insert_value);
126 
127  ngx_queue_init(&cache->sh->queue);
128 
129  cache->sh->cold = 1;
130  cache->sh->loading = 0;
131  cache->sh->size = 0;
132 
133  cache->bsize = ngx_fs_bsize(cache->path->name.data);
134 
135  cache->max_size /= cache->bsize;
136 
137  len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
138 
139  cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
140  if (cache->shpool->log_ctx == NULL) {
141  return NGX_ERROR;
142  }
143 
144  ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
145  &shm_zone->shm.name);
146 
147  return NGX_OK;
148 }
149 
150 
151 ngx_int_t
153 {
154  ngx_http_cache_t *c;
155 
156  c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
157  if (c == NULL) {
158  return NGX_ERROR;
159  }
160 
161  if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
162  return NGX_ERROR;
163  }
164 
165  r->cache = c;
166  c->file.log = r->connection->log;
167  c->file.fd = NGX_INVALID_FILE;
168 
169  return NGX_OK;
170 }
171 
172 
173 ngx_int_t
175 {
176  ngx_http_cache_t *c;
177  ngx_pool_cleanup_t *cln;
178  ngx_http_file_cache_t *cache;
179 
180  c = r->cache;
181  cache = c->file_cache;
182 
183  cln = ngx_pool_cleanup_add(r->pool, 0);
184  if (cln == NULL) {
185  return NGX_ERROR;
186  }
187 
188  cln->handler = ngx_http_file_cache_cleanup;
189  cln->data = c;
190 
191  if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
192  return NGX_ERROR;
193  }
194 
195  if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
196  return NGX_ERROR;
197  }
198 
199  return NGX_OK;
200 }
201 
202 
203 void
205 {
206  size_t len;
207  ngx_str_t *key;
208  ngx_uint_t i;
209  ngx_md5_t md5;
210  ngx_http_cache_t *c;
211 
212  c = r->cache;
213 
214  len = 0;
215 
216  ngx_crc32_init(c->crc32);
217  ngx_md5_init(&md5);
218 
219  key = c->keys.elts;
220  for (i = 0; i < c->keys.nelts; i++) {
222  "http cache key: \"%V\"", &key[i]);
223 
224  len += key[i].len;
225 
226  ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
227  ngx_md5_update(&md5, key[i].data, key[i].len);
228  }
229 
231  + sizeof(ngx_http_file_cache_key) + len + 1;
232 
234  ngx_md5_final(c->key, &md5);
235 }
236 
237 
238 ngx_int_t
240 {
241  ngx_int_t rc, rv;
242  ngx_uint_t cold, test;
243  ngx_http_cache_t *c;
244  ngx_pool_cleanup_t *cln;
246  ngx_http_file_cache_t *cache;
248 
249  c = r->cache;
250 
251  if (c->waiting) {
252  return NGX_AGAIN;
253  }
254 
255  if (c->buf) {
256  return ngx_http_file_cache_read(r, c);
257  }
258 
259  cache = c->file_cache;
260 
261  if (c->node == NULL) {
262  cln = ngx_pool_cleanup_add(r->pool, 0);
263  if (cln == NULL) {
264  return NGX_ERROR;
265  }
266 
267  cln->handler = ngx_http_file_cache_cleanup;
268  cln->data = c;
269  }
270 
271  rc = ngx_http_file_cache_exists(cache, c);
272 
274  "http file cache exists: %i e:%d", rc, c->exists);
275 
276  if (rc == NGX_ERROR) {
277  return rc;
278  }
279 
280  if (rc == NGX_AGAIN) {
281  return NGX_HTTP_CACHE_SCARCE;
282  }
283 
284  cold = cache->sh->cold;
285 
286  if (rc == NGX_OK) {
287 
288  if (c->error) {
289  return c->error;
290  }
291 
292  c->temp_file = 1;
293  test = c->exists ? 1 : 0;
294  rv = NGX_DECLINED;
295 
296  } else { /* rc == NGX_DECLINED */
297 
298  if (c->min_uses > 1) {
299 
300  if (!cold) {
301  return NGX_HTTP_CACHE_SCARCE;
302  }
303 
304  test = 1;
306 
307  } else {
308  c->temp_file = 1;
309  test = cold ? 1 : 0;
310  rv = NGX_DECLINED;
311  }
312  }
313 
314  if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
315  return NGX_ERROR;
316  }
317 
318  if (!test) {
319  goto done;
320  }
321 
323 
324  ngx_memzero(&of, sizeof(ngx_open_file_info_t));
325 
326  of.uniq = c->uniq;
327  of.valid = clcf->open_file_cache_valid;
329  of.events = clcf->open_file_cache_events;
331  of.read_ahead = clcf->read_ahead;
332 
333  if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
334  != NGX_OK)
335  {
336  switch (of.err) {
337 
338  case 0:
339  return NGX_ERROR;
340 
341  case NGX_ENOENT:
342  case NGX_ENOTDIR:
343  goto done;
344 
345  default:
347  ngx_open_file_n " \"%s\" failed", c->file.name.data);
348  return NGX_ERROR;
349  }
350  }
351 
353  "http file cache fd: %d", of.fd);
354 
355  c->file.fd = of.fd;
356  c->file.log = r->connection->log;
357  c->uniq = of.uniq;
358  c->length = of.size;
359  c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;
360 
361  c->buf = ngx_create_temp_buf(r->pool, c->body_start);
362  if (c->buf == NULL) {
363  return NGX_ERROR;
364  }
365 
366  return ngx_http_file_cache_read(r, c);
367 
368 done:
369 
370  if (rv == NGX_DECLINED) {
371  return ngx_http_file_cache_lock(r, c);
372  }
373 
374  return rv;
375 }
376 
377 
378 static ngx_int_t
379 ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
380 {
381  ngx_msec_t now, timer;
382  ngx_http_file_cache_t *cache;
383 
384  if (!c->lock) {
385  return NGX_DECLINED;
386  }
387 
388  cache = c->file_cache;
389 
390  ngx_shmtx_lock(&cache->shpool->mutex);
391 
392  if (!c->node->updating) {
393  c->node->updating = 1;
394  c->updating = 1;
395  }
396 
397  ngx_shmtx_unlock(&cache->shpool->mutex);
398 
400  "http file cache lock u:%d wt:%M",
401  c->updating, c->wait_time);
402 
403  if (c->updating) {
404  return NGX_DECLINED;
405  }
406 
407  c->waiting = 1;
408 
409  now = ngx_current_msec;
410 
411  if (c->wait_time == 0) {
412  c->wait_time = now + c->lock_timeout;
413 
414  c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
415  c->wait_event.data = r;
416  c->wait_event.log = r->connection->log;
417  }
418 
419  timer = c->wait_time - now;
420 
421  ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
422 
423  r->main->blocked++;
424 
425  return NGX_AGAIN;
426 }
427 
428 
429 static void
430 ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
431 {
432  ngx_uint_t wait;
433  ngx_msec_t timer;
434  ngx_http_cache_t *c;
436  ngx_http_file_cache_t *cache;
437 
438  r = ev->data;
439  c = r->cache;
440 
442  "http file cache wait handler wt:%M cur:%M",
444 
445  timer = c->wait_time - ngx_current_msec;
446 
447  if ((ngx_msec_int_t) timer <= 0) {
449  "http file cache lock timeout");
450  c->lock = 0;
451  goto wakeup;
452  }
453 
454  cache = c->file_cache;
455  wait = 0;
456 
457  ngx_shmtx_lock(&cache->shpool->mutex);
458 
459  if (c->node->updating) {
460  wait = 1;
461  }
462 
463  ngx_shmtx_unlock(&cache->shpool->mutex);
464 
465  if (wait) {
466  ngx_add_timer(ev, (timer > 500) ? 500 : timer);
467  return;
468  }
469 
470 wakeup:
471 
472  c->waiting = 0;
473  r->main->blocked--;
475 }
476 
477 
478 static ngx_int_t
479 ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)
480 {
481  time_t now;
482  ssize_t n;
483  ngx_int_t rc;
484  ngx_http_file_cache_t *cache;
486 
487  n = ngx_http_file_cache_aio_read(r, c);
488 
489  if (n < 0) {
490  return n;
491  }
492 
493  if ((size_t) n < c->header_start) {
495  "cache file \"%s\" is too small", c->file.name.data);
496  return NGX_DECLINED;
497  }
498 
500 
501  if (h->crc32 != c->crc32) {
503  "cache file \"%s\" has md5 collision", c->file.name.data);
504  return NGX_DECLINED;
505  }
506 
507  if (h->body_start > c->body_start) {
509  "cache file \"%s\" has too long header",
510  c->file.name.data);
511  return NGX_DECLINED;
512  }
513 
514  c->buf->last += n;
515 
516  c->valid_sec = h->valid_sec;
518  c->date = h->date;
519  c->valid_msec = h->valid_msec;
520  c->header_start = h->header_start;
521  c->body_start = h->body_start;
522 
523  r->cached = 1;
524 
525  cache = c->file_cache;
526 
527  if (cache->sh->cold) {
528 
529  ngx_shmtx_lock(&cache->shpool->mutex);
530 
531  if (!c->node->exists) {
532  c->node->uses = 1;
533  c->node->body_start = c->body_start;
534  c->node->exists = 1;
535  c->node->uniq = c->uniq;
536  c->node->fs_size = c->fs_size;
537 
538  cache->sh->size += c->fs_size;
539  }
540 
541  ngx_shmtx_unlock(&cache->shpool->mutex);
542  }
543 
544  now = ngx_time();
545 
546  if (c->valid_sec < now) {
547 
548  ngx_shmtx_lock(&cache->shpool->mutex);
549 
550  if (c->node->updating) {
552 
553  } else {
554  c->node->updating = 1;
555  c->updating = 1;
557  }
558 
559  ngx_shmtx_unlock(&cache->shpool->mutex);
560 
562  "http file cache expired: %i %T %T",
563  rc, c->valid_sec, now);
564 
565  return rc;
566  }
567 
568  return NGX_OK;
569 }
570 
571 
572 static ssize_t
573 ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
574 {
575 #if (NGX_HAVE_FILE_AIO)
576  ssize_t n;
578 
579  if (!ngx_file_aio) {
580  goto noaio;
581  }
582 
584 
585  if (!clcf->aio) {
586  goto noaio;
587  }
588 
589  n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
590 
591  if (n != NGX_AGAIN) {
592  return n;
593  }
594 
595  c->file.aio->data = r;
596  c->file.aio->handler = ngx_http_cache_aio_event_handler;
597 
598  r->main->blocked++;
599  r->aio = 1;
600 
601  return NGX_AGAIN;
602 
603 noaio:
604 
605 #endif
606 
607  return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
608 }
609 
610 
611 #if (NGX_HAVE_FILE_AIO)
612 
613 static void
614 ngx_http_cache_aio_event_handler(ngx_event_t *ev)
615 {
616  ngx_event_aio_t *aio;
618 
619  aio = ev->data;
620  r = aio->data;
621 
622  r->main->blocked--;
623  r->aio = 0;
624 
626 }
627 
628 #endif
629 
630 
631 static ngx_int_t
632 ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
633 {
634  ngx_int_t rc;
636 
637  ngx_shmtx_lock(&cache->shpool->mutex);
638 
639  fcn = c->node;
640 
641  if (fcn == NULL) {
642  fcn = ngx_http_file_cache_lookup(cache, c->key);
643  }
644 
645  if (fcn) {
646  ngx_queue_remove(&fcn->queue);
647 
648  if (c->node == NULL) {
649  fcn->uses++;
650  fcn->count++;
651  }
652 
653  if (fcn->error) {
654 
655  if (fcn->valid_sec < ngx_time()) {
656  goto renew;
657  }
658 
659  rc = NGX_OK;
660 
661  goto done;
662  }
663 
664  if (fcn->exists || fcn->uses >= c->min_uses) {
665 
666  c->exists = fcn->exists;
667  if (fcn->body_start) {
668  c->body_start = fcn->body_start;
669  }
670 
671  rc = NGX_OK;
672 
673  goto done;
674  }
675 
676  rc = NGX_AGAIN;
677 
678  goto done;
679  }
680 
681  fcn = ngx_slab_alloc_locked(cache->shpool,
683  if (fcn == NULL) {
684  ngx_shmtx_unlock(&cache->shpool->mutex);
685 
686  (void) ngx_http_file_cache_forced_expire(cache);
687 
688  ngx_shmtx_lock(&cache->shpool->mutex);
689 
690  fcn = ngx_slab_alloc_locked(cache->shpool,
692  if (fcn == NULL) {
693  rc = NGX_ERROR;
694  goto failed;
695  }
696  }
697 
698  ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
699 
700  ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
702 
703  ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
704 
705  fcn->uses = 1;
706  fcn->count = 1;
707  fcn->updating = 0;
708  fcn->deleting = 0;
709 
710 renew:
711 
712  rc = NGX_DECLINED;
713 
714  fcn->valid_msec = 0;
715  fcn->error = 0;
716  fcn->exists = 0;
717  fcn->valid_sec = 0;
718  fcn->uniq = 0;
719  fcn->body_start = 0;
720  fcn->fs_size = 0;
721 
722 done:
723 
724  fcn->expire = ngx_time() + cache->inactive;
725 
726  ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
727 
728  c->uniq = fcn->uniq;
729  c->error = fcn->error;
730  c->node = fcn;
731 
732 failed:
733 
734  ngx_shmtx_unlock(&cache->shpool->mutex);
735 
736  return rc;
737 }
738 
739 
740 static ngx_int_t
741 ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
742 {
743  u_char *p;
744  ngx_http_cache_t *c;
745 
746  c = r->cache;
747 
748  if (c->file.name.len) {
749  return NGX_OK;
750  }
751 
752  c->file.name.len = path->name.len + 1 + path->len
754 
755  c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
756  if (c->file.name.data == NULL) {
757  return NGX_ERROR;
758  }
759 
760  ngx_memcpy(c->file.name.data, path->name.data, path->name.len);
761 
762  p = c->file.name.data + path->name.len + 1 + path->len;
764  *p = '\0';
765 
767 
769  "cache file: \"%s\"", c->file.name.data);
770 
771  return NGX_OK;
772 }
773 
774 
776 ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
777 {
778  ngx_int_t rc;
779  ngx_rbtree_key_t node_key;
780  ngx_rbtree_node_t *node, *sentinel;
782 
783  ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));
784 
785  node = cache->sh->rbtree.root;
786  sentinel = cache->sh->rbtree.sentinel;
787 
788  while (node != sentinel) {
789 
790  if (node_key < node->key) {
791  node = node->left;
792  continue;
793  }
794 
795  if (node_key > node->key) {
796  node = node->right;
797  continue;
798  }
799 
800  /* node_key == node->key */
801 
802  fcn = (ngx_http_file_cache_node_t *) node;
803 
804  rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
806 
807  if (rc == 0) {
808  return fcn;
809  }
810 
811  node = (rc < 0) ? node->left : node->right;
812  }
813 
814  /* not found */
815 
816  return NULL;
817 }
818 
819 
820 static void
821 ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
822  ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
823 {
824  ngx_rbtree_node_t **p;
825  ngx_http_file_cache_node_t *cn, *cnt;
826 
827  for ( ;; ) {
828 
829  if (node->key < temp->key) {
830 
831  p = &temp->left;
832 
833  } else if (node->key > temp->key) {
834 
835  p = &temp->right;
836 
837  } else { /* node->key == temp->key */
838 
839  cn = (ngx_http_file_cache_node_t *) node;
840  cnt = (ngx_http_file_cache_node_t *) temp;
841 
842  p = (ngx_memcmp(cn->key, cnt->key,
844  < 0)
845  ? &temp->left : &temp->right;
846  }
847 
848  if (*p == sentinel) {
849  break;
850  }
851 
852  temp = *p;
853  }
854 
855  *p = node;
856  node->parent = temp;
857  node->left = sentinel;
858  node->right = sentinel;
859  ngx_rbt_red(node);
860 }
861 
862 
863 void
865 {
867 
868  u_char *p;
869  ngx_str_t *key;
870  ngx_uint_t i;
871  ngx_http_cache_t *c;
872 
874  "http file cache set header");
875 
876  c = r->cache;
877 
878  h->valid_sec = c->valid_sec;
880  h->date = c->date;
881  h->crc32 = c->crc32;
882  h->valid_msec = (u_short) c->valid_msec;
883  h->header_start = (u_short) c->header_start;
884  h->body_start = (u_short) c->body_start;
885 
886  p = buf + sizeof(ngx_http_file_cache_header_t);
887 
888  p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));
889 
890  key = c->keys.elts;
891  for (i = 0; i < c->keys.nelts; i++) {
892  p = ngx_copy(p, key[i].data, key[i].len);
893  }
894 
895  *p = LF;
896 }
897 
898 
899 void
901 {
902  off_t fs_size;
903  ngx_int_t rc;
904  ngx_file_uniq_t uniq;
905  ngx_file_info_t fi;
906  ngx_http_cache_t *c;
908  ngx_http_file_cache_t *cache;
909 
910  c = r->cache;
911 
912  if (c->updated) {
913  return;
914  }
915 
917  "http file cache update");
918 
919  c->updated = 1;
920  c->updating = 0;
921 
922  cache = c->file_cache;
923 
924  uniq = 0;
925  fs_size = 0;
926 
928  "http file cache rename: \"%s\" to \"%s\"",
929  tf->file.name.data, c->file.name.data);
930 
933  ext.time = -1;
934  ext.create_path = 1;
935  ext.delete_file = 1;
936  ext.log = r->connection->log;
937 
938  rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
939 
940  if (rc == NGX_OK) {
941 
942  if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
944  ngx_fd_info_n " \"%s\" failed", tf->file.name.data);
945 
946  rc = NGX_ERROR;
947 
948  } else {
949  uniq = ngx_file_uniq(&fi);
950  fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
951  }
952  }
953 
954  ngx_shmtx_lock(&cache->shpool->mutex);
955 
956  c->node->count--;
957  c->node->uniq = uniq;
958  c->node->body_start = c->body_start;
959 
960  cache->sh->size += fs_size - c->node->fs_size;
961  c->node->fs_size = fs_size;
962 
963  if (rc == NGX_OK) {
964  c->node->exists = 1;
965  }
966 
967  c->node->updating = 0;
968 
969  ngx_shmtx_unlock(&cache->shpool->mutex);
970 }
971 
972 
973 ngx_int_t
975 {
976  ngx_int_t rc;
977  ngx_buf_t *b;
978  ngx_chain_t out;
979  ngx_http_cache_t *c;
980 
981  c = r->cache;
982 
984  "http file cache send: %s", c->file.name.data);
985 
986  if (r != r->main && c->length - c->body_start == 0) {
987  return ngx_http_send_header(r);
988  }
989 
990  /* we need to allocate all before the header would be sent */
991 
992  b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
993  if (b == NULL) {
995  }
996 
997  b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
998  if (b->file == NULL) {
1000  }
1001 
1002  rc = ngx_http_send_header(r);
1003 
1004  if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
1005  return rc;
1006  }
1007 
1008  b->file_pos = c->body_start;
1009  b->file_last = c->length;
1010 
1011  b->in_file = (c->length - c->body_start) ? 1: 0;
1012  b->last_buf = (r == r->main) ? 1: 0;
1013  b->last_in_chain = 1;
1014 
1015  b->file->fd = c->file.fd;
1016  b->file->name = c->file.name;
1017  b->file->log = r->connection->log;
1018 
1019  out.buf = b;
1020  out.next = NULL;
1021 
1022  return ngx_http_output_filter(r, &out);
1023 }
1024 
1025 
1026 void
1028 {
1029  ngx_http_file_cache_t *cache;
1031 
1032  if (c->updated || c->node == NULL) {
1033  return;
1034  }
1035 
1036  cache = c->file_cache;
1037 
1039  "http file cache free, fd: %d", c->file.fd);
1040 
1041  ngx_shmtx_lock(&cache->shpool->mutex);
1042 
1043  fcn = c->node;
1044  fcn->count--;
1045 
1046  if (c->updating) {
1047  fcn->updating = 0;
1048  }
1049 
1050  if (c->error) {
1051  fcn->error = c->error;
1052 
1053  if (c->valid_sec) {
1054  fcn->valid_sec = c->valid_sec;
1055  fcn->valid_msec = c->valid_msec;
1056  }
1057 
1058  } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
1059  ngx_queue_remove(&fcn->queue);
1060  ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
1061  ngx_slab_free_locked(cache->shpool, fcn);
1062  c->node = NULL;
1063  }
1064 
1065  ngx_shmtx_unlock(&cache->shpool->mutex);
1066 
1067  c->updated = 1;
1068  c->updating = 0;
1069 
1070  if (c->temp_file) {
1071  if (tf && tf->file.fd != NGX_INVALID_FILE) {
1073  "http file cache incomplete: \"%s\"",
1074  tf->file.name.data);
1075 
1078  ngx_delete_file_n " \"%s\" failed",
1079  tf->file.name.data);
1080  }
1081  }
1082  }
1083 
1084  if (c->wait_event.timer_set) {
1086  }
1087 }
1088 
1089 
1090 static void
1091 ngx_http_file_cache_cleanup(void *data)
1092 {
1093  ngx_http_cache_t *c = data;
1094 
1095  if (c->updated) {
1096  return;
1097  }
1098 
1100  "http file cache cleanup");
1101 
1102  if (c->updating) {
1104  "stalled cache updating, error:%ui", c->error);
1105  }
1106 
1107  ngx_http_file_cache_free(c, NULL);
1108 }
1109 
1110 
1111 static time_t
1112 ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
1113 {
1114  u_char *name;
1115  size_t len;
1116  time_t wait;
1117  ngx_uint_t tries;
1118  ngx_path_t *path;
1119  ngx_queue_t *q;
1121 
1123  "http file cache forced expire");
1124 
1125  path = cache->path;
1126  len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
1127 
1128  name = ngx_alloc(len + 1, ngx_cycle->log);
1129  if (name == NULL) {
1130  return 10;
1131  }
1132 
1133  ngx_memcpy(name, path->name.data, path->name.len);
1134 
1135  wait = 10;
1136  tries = 20;
1137 
1138  ngx_shmtx_lock(&cache->shpool->mutex);
1139 
1140  for (q = ngx_queue_last(&cache->sh->queue);
1141  q != ngx_queue_sentinel(&cache->sh->queue);
1142  q = ngx_queue_prev(q))
1143  {
1144  fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
1145 
1147  "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
1148  fcn->count, fcn->exists,
1149  fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
1150 
1151  if (fcn->count == 0) {
1152  ngx_http_file_cache_delete(cache, q, name);
1153  wait = 0;
1154 
1155  } else {
1156  if (--tries) {
1157  continue;
1158  }
1159 
1160  wait = 1;
1161  }
1162 
1163  break;
1164  }
1165 
1166  ngx_shmtx_unlock(&cache->shpool->mutex);
1167 
1168  ngx_free(name);
1169 
1170  return wait;
1171 }
1172 
1173 
1174 static time_t
1175 ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
1176 {
1177  u_char *name, *p;
1178  size_t len;
1179  time_t now, wait;
1180  ngx_path_t *path;
1181  ngx_queue_t *q;
1183  u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
1184 
1186  "http file cache expire");
1187 
1188  path = cache->path;
1189  len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
1190 
1191  name = ngx_alloc(len + 1, ngx_cycle->log);
1192  if (name == NULL) {
1193  return 10;
1194  }
1195 
1196  ngx_memcpy(name, path->name.data, path->name.len);
1197 
1198  now = ngx_time();
1199 
1200  ngx_shmtx_lock(&cache->shpool->mutex);
1201 
1202  for ( ;; ) {
1203 
1204  if (ngx_queue_empty(&cache->sh->queue)) {
1205  wait = 10;
1206  break;
1207  }
1208 
1209  q = ngx_queue_last(&cache->sh->queue);
1210 
1211  fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
1212 
1213  wait = fcn->expire - now;
1214 
1215  if (wait > 0) {
1216  wait = wait > 10 ? 10 : wait;
1217  break;
1218  }
1219 
1221  "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
1222  fcn->count, fcn->exists,
1223  fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
1224 
1225  if (fcn->count == 0) {
1226  ngx_http_file_cache_delete(cache, q, name);
1227  continue;
1228  }
1229 
1230  if (fcn->deleting) {
1231  wait = 1;
1232  break;
1233  }
1234 
1235  p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
1236  sizeof(ngx_rbtree_key_t));
1237  len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
1238  (void) ngx_hex_dump(p, fcn->key, len);
1239 
1240  /*
1241  * abnormally exited workers may leave locked cache entries,
1242  * and although it may be safe to remove them completely,
1243  * we prefer to just move them to the top of the inactive queue
1244  */
1245 
1246  ngx_queue_remove(q);
1247  fcn->expire = ngx_time() + cache->inactive;
1248  ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
1249 
1251  "ignore long locked inactive cache entry %*s, count:%d",
1252  2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
1253  }
1254 
1255  ngx_shmtx_unlock(&cache->shpool->mutex);
1256 
1257  ngx_free(name);
1258 
1259  return wait;
1260 }
1261 
1262 
1263 static void
1264 ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
1265  u_char *name)
1266 {
1267  u_char *p;
1268  size_t len;
1269  ngx_path_t *path;
1271 
1272  fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
1273 
1274  if (fcn->exists) {
1275  cache->sh->size -= fcn->fs_size;
1276 
1277  path = cache->path;
1278  p = name + path->name.len + 1 + path->len;
1279  p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
1280  sizeof(ngx_rbtree_key_t));
1281  len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
1282  p = ngx_hex_dump(p, fcn->key, len);
1283  *p = '\0';
1284 
1285  fcn->count++;
1286  fcn->deleting = 1;
1287  ngx_shmtx_unlock(&cache->shpool->mutex);
1288 
1289  len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
1290  ngx_create_hashed_filename(path, name, len);
1291 
1293  "http file cache expire: \"%s\"", name);
1294 
1295  if (ngx_delete_file(name) == NGX_FILE_ERROR) {
1297  ngx_delete_file_n " \"%s\" failed", name);
1298  }
1299 
1300  ngx_shmtx_lock(&cache->shpool->mutex);
1301  fcn->count--;
1302  fcn->deleting = 0;
1303  }
1304 
1305  if (fcn->count == 0) {
1306  ngx_queue_remove(q);
1307  ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
1308  ngx_slab_free_locked(cache->shpool, fcn);
1309  }
1310 }
1311 
1312 
1313 static time_t
1314 ngx_http_file_cache_manager(void *data)
1315 {
1316  ngx_http_file_cache_t *cache = data;
1317 
1318  off_t size;
1319  time_t next, wait;
1320 
1321  next = ngx_http_file_cache_expire(cache);
1322 
1323  cache->last = ngx_current_msec;
1324  cache->files = 0;
1325 
1326  for ( ;; ) {
1327  ngx_shmtx_lock(&cache->shpool->mutex);
1328 
1329  size = cache->sh->size;
1330 
1331  ngx_shmtx_unlock(&cache->shpool->mutex);
1332 
1334  "http file cache size: %O", size);
1335 
1336  if (size < cache->max_size) {
1337  return next;
1338  }
1339 
1340  wait = ngx_http_file_cache_forced_expire(cache);
1341 
1342  if (wait > 0) {
1343  return wait;
1344  }
1345 
1346  if (ngx_quit || ngx_terminate) {
1347  return next;
1348  }
1349  }
1350 }
1351 
1352 
1353 static void
1354 ngx_http_file_cache_loader(void *data)
1355 {
1356  ngx_http_file_cache_t *cache = data;
1357 
1358  ngx_tree_ctx_t tree;
1359 
1360  if (!cache->sh->cold || cache->sh->loading) {
1361  return;
1362  }
1363 
1364  if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
1365  return;
1366  }
1367 
1369  "http file cache loader");
1370 
1371  tree.init_handler = NULL;
1372  tree.file_handler = ngx_http_file_cache_manage_file;
1373  tree.pre_tree_handler = ngx_http_file_cache_noop;
1374  tree.post_tree_handler = ngx_http_file_cache_noop;
1375  tree.spec_handler = ngx_http_file_cache_delete_file;
1376  tree.data = cache;
1377  tree.alloc = 0;
1378  tree.log = ngx_cycle->log;
1379 
1380  cache->last = ngx_current_msec;
1381  cache->files = 0;
1382 
1383  if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
1384  cache->sh->loading = 0;
1385  return;
1386  }
1387 
1388  cache->sh->cold = 0;
1389  cache->sh->loading = 0;
1390 
1392  "http file cache: %V %.3fM, bsize: %uz",
1393  &cache->path->name,
1394  ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
1395  cache->bsize);
1396 }
1397 
1398 
1399 static ngx_int_t
1400 ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
1401 {
1402  return NGX_OK;
1403 }
1404 
1405 
1406 static ngx_int_t
1407 ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
1408 {
1409  ngx_msec_t elapsed;
1410  ngx_http_file_cache_t *cache;
1411 
1412  cache = ctx->data;
1413 
1414  if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
1415  (void) ngx_http_file_cache_delete_file(ctx, path);
1416  }
1417 
1418  if (++cache->files >= cache->loader_files) {
1419  ngx_http_file_cache_loader_sleep(cache);
1420 
1421  } else {
1422  ngx_time_update();
1423 
1424  elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
1425 
1427  "http file cache loader time elapsed: %M", elapsed);
1428 
1429  if (elapsed >= cache->loader_threshold) {
1430  ngx_http_file_cache_loader_sleep(cache);
1431  }
1432  }
1433 
1434  return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
1435 }
1436 
1437 
1438 static void
1439 ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
1440 {
1441  ngx_msleep(cache->loader_sleep);
1442 
1443  ngx_time_update();
1444 
1445  cache->last = ngx_current_msec;
1446  cache->files = 0;
1447 }
1448 
1449 
1450 static ngx_int_t
1451 ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
1452 {
1453  u_char *p;
1454  ngx_int_t n;
1455  ngx_uint_t i;
1456  ngx_http_cache_t c;
1457  ngx_http_file_cache_t *cache;
1458 
1459  if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
1460  return NGX_ERROR;
1461  }
1462 
1463  if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
1464  ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
1465  "cache file \"%s\" is too small", name->data);
1466  return NGX_ERROR;
1467  }
1468 
1469  ngx_memzero(&c, sizeof(ngx_http_cache_t));
1470  cache = ctx->data;
1471 
1472  c.length = ctx->size;
1473  c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;
1474 
1475  p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
1476 
1477  for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
1478  n = ngx_hextoi(p, 2);
1479 
1480  if (n == NGX_ERROR) {
1481  return NGX_ERROR;
1482  }
1483 
1484  p += 2;
1485 
1486  c.key[i] = (u_char) n;
1487  }
1488 
1489  return ngx_http_file_cache_add(cache, &c);
1490 }
1491 
1492 
1493 static ngx_int_t
1494 ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
1495 {
1497 
1498  ngx_shmtx_lock(&cache->shpool->mutex);
1499 
1500  fcn = ngx_http_file_cache_lookup(cache, c->key);
1501 
1502  if (fcn == NULL) {
1503 
1504  fcn = ngx_slab_alloc_locked(cache->shpool,
1505  sizeof(ngx_http_file_cache_node_t));
1506  if (fcn == NULL) {
1507  ngx_shmtx_unlock(&cache->shpool->mutex);
1508  return NGX_ERROR;
1509  }
1510 
1511  ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
1512 
1513  ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
1514  NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
1515 
1516  ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
1517 
1518  fcn->uses = 1;
1519  fcn->count = 0;
1520  fcn->valid_msec = 0;
1521  fcn->error = 0;
1522  fcn->exists = 1;
1523  fcn->updating = 0;
1524  fcn->deleting = 0;
1525  fcn->uniq = 0;
1526  fcn->valid_sec = 0;
1527  fcn->body_start = 0;
1528  fcn->fs_size = c->fs_size;
1529 
1530  cache->sh->size += c->fs_size;
1531 
1532  } else {
1533  ngx_queue_remove(&fcn->queue);
1534  }
1535 
1536  fcn->expire = ngx_time() + cache->inactive;
1537 
1538  ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
1539 
1540  ngx_shmtx_unlock(&cache->shpool->mutex);
1541 
1542  return NGX_OK;
1543 }
1544 
1545 
1546 static ngx_int_t
1547 ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
1548 {
1550  "http file cache delete: \"%s\"", path->data);
1551 
1552  if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
1554  ngx_delete_file_n " \"%s\" failed", path->data);
1555  }
1556 
1557  return NGX_OK;
1558 }
1559 
1560 
1561 time_t
1563 {
1564  ngx_uint_t i;
1565  ngx_http_cache_valid_t *valid;
1566 
1567  if (cache_valid == NULL) {
1568  return 0;
1569  }
1570 
1571  valid = cache_valid->elts;
1572  for (i = 0; i < cache_valid->nelts; i++) {
1573 
1574  if (valid[i].status == 0) {
1575  return valid[i].valid;
1576  }
1577 
1578  if (valid[i].status == status) {
1579  return valid[i].valid;
1580  }
1581  }
1582 
1583  return 0;
1584 }
1585 
1586 
1587 char *
1589 {
1590  off_t max_size;
1591  u_char *last, *p;
1592  time_t inactive;
1593  ssize_t size;
1594  ngx_str_t s, name, *value;
1595  ngx_int_t loader_files;
1596  ngx_msec_t loader_sleep, loader_threshold;
1597  ngx_uint_t i, n;
1598  ngx_http_file_cache_t *cache;
1599 
1600  cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
1601  if (cache == NULL) {
1602  return NGX_CONF_ERROR;
1603  }
1604 
1605  cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
1606  if (cache->path == NULL) {
1607  return NGX_CONF_ERROR;
1608  }
1609 
1610  inactive = 600;
1611  loader_files = 100;
1612  loader_sleep = 50;
1613  loader_threshold = 200;
1614 
1615  name.len = 0;
1616  size = 0;
1617  max_size = NGX_MAX_OFF_T_VALUE;
1618 
1619  value = cf->args->elts;
1620 
1621  cache->path->name = value[1];
1622 
1623  if (cache->path->name.data[cache->path->name.len - 1] == '/') {
1624  cache->path->name.len--;
1625  }
1626 
1627  if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
1628  return NGX_CONF_ERROR;
1629  }
1630 
1631  for (i = 2; i < cf->args->nelts; i++) {
1632 
1633  if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {
1634 
1635  p = value[i].data + 7;
1636  last = value[i].data + value[i].len;
1637 
1638  for (n = 0; n < 3 && p < last; n++) {
1639 
1640  if (*p > '0' && *p < '3') {
1641 
1642  cache->path->level[n] = *p++ - '0';
1643  cache->path->len += cache->path->level[n] + 1;
1644 
1645  if (p == last) {
1646  break;
1647  }
1648 
1649  if (*p++ == ':' && n < 2 && p != last) {
1650  continue;
1651  }
1652 
1653  goto invalid_levels;
1654  }
1655 
1656  goto invalid_levels;
1657  }
1658 
1659  if (cache->path->len < 10 + 3) {
1660  continue;
1661  }
1662 
1663  invalid_levels:
1664 
1666  "invalid \"levels\" \"%V\"", &value[i]);
1667  return NGX_CONF_ERROR;
1668  }
1669 
1670  if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {
1671 
1672  name.data = value[i].data + 10;
1673 
1674  p = (u_char *) ngx_strchr(name.data, ':');
1675 
1676  if (p) {
1677  name.len = p - name.data;
1678 
1679  p++;
1680 
1681  s.len = value[i].data + value[i].len - p;
1682  s.data = p;
1683 
1684  size = ngx_parse_size(&s);
1685  if (size > 8191) {
1686  continue;
1687  }
1688  }
1689 
1691  "invalid keys zone size \"%V\"", &value[i]);
1692  return NGX_CONF_ERROR;
1693  }
1694 
1695  if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
1696 
1697  s.len = value[i].len - 9;
1698  s.data = value[i].data + 9;
1699 
1700  inactive = ngx_parse_time(&s, 1);
1701  if (inactive == (time_t) NGX_ERROR) {
1703  "invalid inactive value \"%V\"", &value[i]);
1704  return NGX_CONF_ERROR;
1705  }
1706 
1707  continue;
1708  }
1709 
1710  if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
1711 
1712  s.len = value[i].len - 9;
1713  s.data = value[i].data + 9;
1714 
1715  max_size = ngx_parse_offset(&s);
1716  if (max_size < 0) {
1718  "invalid max_size value \"%V\"", &value[i]);
1719  return NGX_CONF_ERROR;
1720  }
1721 
1722  continue;
1723  }
1724 
1725  if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {
1726 
1727  loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
1728  if (loader_files == NGX_ERROR) {
1730  "invalid loader_files value \"%V\"", &value[i]);
1731  return NGX_CONF_ERROR;
1732  }
1733 
1734  continue;
1735  }
1736 
1737  if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {
1738 
1739  s.len = value[i].len - 13;
1740  s.data = value[i].data + 13;
1741 
1742  loader_sleep = ngx_parse_time(&s, 0);
1743  if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
1745  "invalid loader_sleep value \"%V\"", &value[i]);
1746  return NGX_CONF_ERROR;
1747  }
1748 
1749  continue;
1750  }
1751 
1752  if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {
1753 
1754  s.len = value[i].len - 17;
1755  s.data = value[i].data + 17;
1756 
1757  loader_threshold = ngx_parse_time(&s, 0);
1758  if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
1760  "invalid loader_threshold value \"%V\"", &value[i]);
1761  return NGX_CONF_ERROR;
1762  }
1763 
1764  continue;
1765  }
1766 
1768  "invalid parameter \"%V\"", &value[i]);
1769  return NGX_CONF_ERROR;
1770  }
1771 
1772  if (name.len == 0 || size == 0) {
1774  "\"%V\" must have \"keys_zone\" parameter",
1775  &cmd->name);
1776  return NGX_CONF_ERROR;
1777  }
1778 
1779  cache->path->manager = ngx_http_file_cache_manager;
1780  cache->path->loader = ngx_http_file_cache_loader;
1781  cache->path->data = cache;
1782  cache->path->conf_file = cf->conf_file->file.name.data;
1783  cache->path->line = cf->conf_file->line;
1784  cache->loader_files = loader_files;
1785  cache->loader_sleep = loader_sleep;
1786  cache->loader_threshold = loader_threshold;
1787 
1788  if (ngx_add_path(cf, &cache->path) != NGX_OK) {
1789  return NGX_CONF_ERROR;
1790  }
1791 
1792  cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
1793  if (cache->shm_zone == NULL) {
1794  return NGX_CONF_ERROR;
1795  }
1796 
1797  if (cache->shm_zone->data) {
1799  "duplicate zone \"%V\"", &name);
1800  return NGX_CONF_ERROR;
1801  }
1802 
1803 
1804  cache->shm_zone->init = ngx_http_file_cache_init;
1805  cache->shm_zone->data = cache;
1806 
1807  cache->inactive = inactive;
1808  cache->max_size = max_size;
1809 
1810  return NGX_CONF_OK;
1811 }
1812 
1813 
1814 char *
1816  void *conf)
1817 {
1818  char *p = conf;
1819 
1820  time_t valid;
1821  ngx_str_t *value;
1822  ngx_uint_t i, n, status;
1823  ngx_array_t **a;
1825  static ngx_uint_t statuses[] = { 200, 301, 302 };
1826 
1827  a = (ngx_array_t **) (p + cmd->offset);
1828 
1829  if (*a == NGX_CONF_UNSET_PTR) {
1830  *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
1831  if (*a == NULL) {
1832  return NGX_CONF_ERROR;
1833  }
1834  }
1835 
1836  value = cf->args->elts;
1837  n = cf->args->nelts - 1;
1838 
1839  valid = ngx_parse_time(&value[n], 1);
1840  if (valid == (time_t) NGX_ERROR) {
1842  "invalid time value \"%V\"", &value[n]);
1843  return NGX_CONF_ERROR;
1844  }
1845 
1846  if (n == 1) {
1847 
1848  for (i = 0; i < 3; i++) {
1849  v = ngx_array_push(*a);
1850  if (v == NULL) {
1851  return NGX_CONF_ERROR;
1852  }
1853 
1854  v->status = statuses[i];
1855  v->valid = valid;
1856  }
1857 
1858  return NGX_CONF_OK;
1859  }
1860 
1861  for (i = 1; i < n; i++) {
1862 
1863  if (ngx_strcmp(value[i].data, "any") == 0) {
1864 
1865  status = 0;
1866 
1867  } else {
1868 
1869  status = ngx_atoi(value[i].data, value[i].len);
1870  if (status < 100) {
1872  "invalid status \"%V\"", &value[i]);
1873  return NGX_CONF_ERROR;
1874  }
1875  }
1876 
1877  v = ngx_array_push(*a);
1878  if (v == NULL) {
1879  return NGX_CONF_ERROR;
1880  }
1881 
1882  v->status = status;
1883  v->valid = valid;
1884  }
1885 
1886  return NGX_CONF_OK;
1887 }