Annotation of embedaddon/nginx/src/http/ngx_http_file_cache.c, revision 1.1.1.1
1.1 misho 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);
28: static ngx_http_file_cache_node_t *
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,
31: ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
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:
50: ngx_str_t ngx_http_cache_status[] = {
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
152: ngx_http_file_cache_new(ngx_http_request_t *r)
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
174: ngx_http_file_cache_create(ngx_http_request_t *r)
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
204: ngx_http_file_cache_create_key(ngx_http_request_t *r)
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++) {
221: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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:
230: c->header_start = sizeof(ngx_http_file_cache_header_t)
231: + sizeof(ngx_http_file_cache_key) + len + 1;
232:
233: ngx_crc32_final(c->crc32);
234: ngx_md5_final(c->key, &md5);
235: }
236:
237:
238: ngx_int_t
239: ngx_http_file_cache_open(ngx_http_request_t *r)
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;
245: ngx_open_file_info_t of;
246: ngx_http_file_cache_t *cache;
247: ngx_http_core_loc_conf_t *clcf;
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:
273: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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;
305: rv = NGX_HTTP_CACHE_SCARCE;
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:
322: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
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;
328: of.min_uses = clcf->open_file_cache_min_uses;
329: of.events = clcf->open_file_cache_events;
330: of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
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:
346: ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
347: ngx_open_file_n " \"%s\" failed", c->file.name.data);
348: return NGX_ERROR;
349: }
350: }
351:
352: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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:
399: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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;
435: ngx_http_request_t *r;
436: ngx_http_file_cache_t *cache;
437:
438: r = ev->data;
439: c = r->cache;
440:
441: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
442: "http file cache wait handler wt:%M cur:%M",
443: c->wait_time, ngx_current_msec);
444:
445: timer = c->wait_time - ngx_current_msec;
446:
447: if ((ngx_msec_int_t) timer <= 0) {
448: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 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--;
474: r->connection->write->handler(r->connection->write);
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;
485: ngx_http_file_cache_header_t *h;
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) {
494: ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
495: "cache file \"%s\" is too small", c->file.name.data);
496: return NGX_DECLINED;
497: }
498:
499: h = (ngx_http_file_cache_header_t *) c->buf->pos;
500:
501: if (h->crc32 != c->crc32) {
502: ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
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) {
508: ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
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;
517: c->last_modified = h->last_modified;
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) {
551: rc = NGX_HTTP_CACHE_UPDATING;
552:
553: } else {
554: c->node->updating = 1;
555: c->updating = 1;
556: rc = NGX_HTTP_CACHE_STALE;
557: }
558:
559: ngx_shmtx_unlock(&cache->shpool->mutex);
560:
561: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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;
577: ngx_http_core_loc_conf_t *clcf;
578:
579: if (!ngx_file_aio) {
580: goto noaio;
581: }
582:
583: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
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;
617: ngx_http_request_t *r;
618:
619: aio = ev->data;
620: r = aio->data;
621:
622: r->main->blocked--;
623: r->aio = 0;
624:
625: r->connection->write->handler(r->connection->write);
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;
635: ngx_http_file_cache_node_t *fcn;
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,
682: sizeof(ngx_http_file_cache_node_t));
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,
691: sizeof(ngx_http_file_cache_node_t));
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)],
701: NGX_HTTP_CACHE_KEY_LEN - 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
753: + 2 * NGX_HTTP_CACHE_KEY_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;
763: p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
764: *p = '\0';
765:
766: ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);
767:
768: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
769: "cache file: \"%s\"", c->file.name.data);
770:
771: return NGX_OK;
772: }
773:
774:
775: static ngx_http_file_cache_node_t *
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;
781: ngx_http_file_cache_node_t *fcn;
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,
805: NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
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,
843: NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
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
864: ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
865: {
866: ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf;
867:
868: u_char *p;
869: ngx_str_t *key;
870: ngx_uint_t i;
871: ngx_http_cache_t *c;
872:
873: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
874: "http file cache set header");
875:
876: c = r->cache;
877:
878: h->valid_sec = c->valid_sec;
879: h->last_modified = c->last_modified;
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
900: ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
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;
907: ngx_ext_rename_file_t ext;
908: ngx_http_file_cache_t *cache;
909:
910: c = r->cache;
911:
912: if (c->updated) {
913: return;
914: }
915:
916: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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:
927: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
928: "http file cache rename: \"%s\" to \"%s\"",
929: tf->file.name.data, c->file.name.data);
930:
931: ext.access = NGX_FILE_OWNER_ACCESS;
932: ext.path_access = NGX_FILE_OWNER_ACCESS;
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) {
943: ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
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
974: ngx_http_cache_send(ngx_http_request_t *r)
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:
983: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
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) {
994: return NGX_HTTP_INTERNAL_SERVER_ERROR;
995: }
996:
997: b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
998: if (b->file == NULL) {
999: return NGX_HTTP_INTERNAL_SERVER_ERROR;
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
1027: ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
1028: {
1029: ngx_http_file_cache_t *cache;
1030: ngx_http_file_cache_node_t *fcn;
1031:
1032: if (c->updated || c->node == NULL) {
1033: return;
1034: }
1035:
1036: cache = c->file_cache;
1037:
1038: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
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) {
1072: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
1073: "http file cache incomplete: \"%s\"",
1074: tf->file.name.data);
1075:
1076: if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
1077: ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
1078: ngx_delete_file_n " \"%s\" failed",
1079: tf->file.name.data);
1080: }
1081: }
1082: }
1083:
1084: if (c->wait_event.timer_set) {
1085: ngx_del_timer(&c->wait_event);
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:
1099: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
1100: "http file cache cleanup");
1101:
1102: if (c->updating) {
1103: ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
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;
1120: ngx_http_file_cache_node_t *fcn;
1121:
1122: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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:
1146: ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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;
1182: ngx_http_file_cache_node_t *fcn;
1183: u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
1184:
1185: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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:
1220: ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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:
1250: ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
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;
1270: ngx_http_file_cache_node_t *fcn;
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:
1292: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1293: "http file cache expire: \"%s\"", name);
1294:
1295: if (ngx_delete_file(name) == NGX_FILE_ERROR) {
1296: ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
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:
1333: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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:
1368: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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:
1391: ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
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:
1426: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
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: {
1496: ngx_http_file_cache_node_t *fcn;
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: {
1549: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
1550: "http file cache delete: \"%s\"", path->data);
1551:
1552: if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
1553: ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
1554: ngx_delete_file_n " \"%s\" failed", path->data);
1555: }
1556:
1557: return NGX_OK;
1558: }
1559:
1560:
1561: time_t
1562: ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
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 *
1588: ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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:
1665: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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:
1690: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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) {
1702: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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) {
1717: ngx_conf_log_error(NGX_LOG_EMERG, cf, 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) {
1729: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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) {
1744: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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) {
1759: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1760: "invalid loader_threshold value \"%V\"", &value[i]);
1761: return NGX_CONF_ERROR;
1762: }
1763:
1764: continue;
1765: }
1766:
1767: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1768: "invalid parameter \"%V\"", &value[i]);
1769: return NGX_CONF_ERROR;
1770: }
1771:
1772: if (name.len == 0 || size == 0) {
1773: ngx_conf_log_error(NGX_LOG_EMERG, cf, 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) {
1798: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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 *
1815: ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
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;
1824: ngx_http_cache_valid_t *v;
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) {
1841: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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) {
1871: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>