Annotation of embedaddon/nginx/src/http/ngx_http_file_cache.c, revision 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>