Annotation of embedaddon/nginx/src/http/ngx_http_file_cache.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>