src/http/ngx_http_file_cache.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

Global variables defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>
  8. #include <ngx_md5.h>


  9. static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,
  10.     ngx_http_cache_t *c);
  11. static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);
  12. static ngx_int_t ngx_http_file_cache_lock_wait(ngx_http_request_t *r,
  13.     ngx_http_cache_t *c);
  14. static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,
  15.     ngx_http_cache_t *c);
  16. static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,
  17.     ngx_http_cache_t *c);
  18. #if (NGX_HAVE_FILE_AIO)
  19. static void ngx_http_cache_aio_event_handler(ngx_event_t *ev);
  20. #endif
  21. #if (NGX_THREADS)
  22. static ngx_int_t ngx_http_cache_thread_handler(ngx_thread_task_t *task,
  23.     ngx_file_t *file);
  24. static void ngx_http_cache_thread_event_handler(ngx_event_t *ev);
  25. #endif
  26. static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
  27.     ngx_http_cache_t *c);
  28. static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,
  29.     ngx_path_t *path);
  30. static ngx_http_file_cache_node_t *
  31.     ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
  32. static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  33.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
  34. static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary,
  35.     size_t len, u_char *hash);
  36. static void ngx_http_file_cache_vary_header(ngx_http_request_t *r,
  37.     ngx_md5_t *md5, ngx_str_t *name);
  38. static ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r,
  39.     ngx_http_cache_t *c);
  40. static ngx_int_t ngx_http_file_cache_update_variant(ngx_http_request_t *r,
  41.     ngx_http_cache_t *c);
  42. static void ngx_http_file_cache_cleanup(void *data);
  43. static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
  44. static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
  45. static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
  46.     ngx_queue_t *q, u_char *name);
  47. static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
  48. static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
  49.     ngx_str_t *path);
  50. static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
  51.     ngx_str_t *path);
  52. static ngx_int_t ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx,
  53.     ngx_str_t *path);
  54. static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
  55.     ngx_str_t *path);
  56. static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
  57.     ngx_http_cache_t *c);
  58. static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
  59.     ngx_str_t *path);
  60. static void ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache);


  61. ‌ngx_str_t  ngx_http_cache_status[] = {
  62.     ngx_string("MISS"),
  63.     ngx_string("BYPASS"),
  64.     ngx_string("EXPIRED"),
  65.     ngx_string("STALE"),
  66.     ngx_string("UPDATING"),
  67.     ngx_string("REVALIDATED"),
  68.     ngx_string("HIT")
  69. };


  70. ‌static u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };


  71. static ngx_int_t
  72. ‌ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
  73. {
  74.     ngx_http_file_cache_t  *ocache = data;

  75.     size_t                  len;
  76.     ngx_uint_t              n;
  77.     ngx_http_file_cache_t  *cache;

  78.     cache = shm_zone->data;

  79.     if (ocache) {
  80.         if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
  81.             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  82.                           "cache \"%V\" uses the \"%V\" cache path "
  83.                           "while previously it used the \"%V\" cache path",
  84.                           &shm_zone->shm.name, &cache->path->name,
  85.                           &ocache->path->name);

  86.             return NGX_ERROR;
  87.         }

  88.         for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {
  89.             if (cache->path->level[n] != ocache->path->level[n]) {
  90.                 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  91.                               "cache \"%V\" had previously different levels",
  92.                               &shm_zone->shm.name);
  93.                 return NGX_ERROR;
  94.             }
  95.         }

  96.         cache->sh = ocache->sh;

  97.         cache->shpool = ocache->shpool;
  98.         cache->bsize = ocache->bsize;

  99.         cache->max_size /= cache->bsize;

  100.         if (!cache->sh->cold || cache->sh->loading) {
  101.             cache->path->loader = NULL;
  102.         }

  103.         return NGX_OK;
  104.     }

  105.     cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

  106.     if (shm_zone->shm.exists) {
  107.         cache->sh = cache->shpool->data;
  108.         cache->bsize = ngx_fs_bsize(cache->path->name.data);
  109.         cache->max_size /= cache->bsize;

  110.         return NGX_OK;
  111.     }

  112.     cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
  113.     if (cache->sh == NULL) {
  114.         return NGX_ERROR;
  115.     }

  116.     cache->shpool->data = cache->sh;

  117.     ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
  118.                     ngx_http_file_cache_rbtree_insert_value);

  119.     ngx_queue_init(&cache->sh->queue);

  120.     cache->sh->cold = 1;
  121.     cache->sh->loading = 0;
  122.     cache->sh->size = 0;
  123.     cache->sh->count = 0;
  124.     cache->sh->watermark = (ngx_uint_t) -1;

  125.     cache->bsize = ngx_fs_bsize(cache->path->name.data);

  126.     cache->max_size /= cache->bsize;

  127.     len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;

  128.     cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
  129.     if (cache->shpool->log_ctx == NULL) {
  130.         return NGX_ERROR;
  131.     }

  132.     ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
  133.                 &shm_zone->shm.name);

  134.     cache->shpool->log_nomem = 0;

  135.     return NGX_OK;
  136. }


  137. ngx_int_t
  138. ‌ngx_http_file_cache_new(ngx_http_request_t *r)
  139. {
  140.     ngx_http_cache_t  *c;

  141.     c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
  142.     if (c == NULL) {
  143.         return NGX_ERROR;
  144.     }

  145.     if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
  146.         return NGX_ERROR;
  147.     }

  148.     r->cache = c;
  149.     c->file.log = r->connection->log;
  150.     c->file.fd = NGX_INVALID_FILE;

  151.     return NGX_OK;
  152. }


  153. ngx_int_t
  154. ‌ngx_http_file_cache_create(ngx_http_request_t *r)
  155. {
  156.     ngx_http_cache_t       *c;
  157.     ngx_pool_cleanup_t     *cln;
  158.     ngx_http_file_cache_t  *cache;

  159.     c = r->cache;
  160.     cache = c->file_cache;

  161.     cln = ngx_pool_cleanup_add(r->pool, 0);
  162.     if (cln == NULL) {
  163.         return NGX_ERROR;
  164.     }

  165.     cln->handler = ngx_http_file_cache_cleanup;
  166.     cln->data = c;

  167.     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  168.         return NGX_ERROR;
  169.     }

  170.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  171.         return NGX_ERROR;
  172.     }

  173.     return NGX_OK;
  174. }


  175. void
  176. ‌ngx_http_file_cache_create_key(ngx_http_request_t *r)
  177. {
  178.     size_t             len;
  179.     ngx_str_t         *key;
  180.     ngx_uint_t         i;
  181.     ngx_md5_t          md5;
  182.     ngx_http_cache_t  *c;

  183.     c = r->cache;

  184.     len = 0;

  185.     ngx_crc32_init(c->crc32);
  186.     ngx_md5_init(&md5);

  187.     key = c->keys.elts;
  188.     for (i = 0; i < c->keys.nelts; i++) {
  189.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  190.                        "http cache key: \"%V\"", &key[i]);

  191.         len += key[i].len;

  192.         ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
  193.         ngx_md5_update(&md5, key[i].data, key[i].len);
  194.     }

  195.     c->header_start = sizeof(ngx_http_file_cache_header_t)
  196.                       + sizeof(ngx_http_file_cache_key) + len + 1;

  197.     ngx_crc32_final(c->crc32);
  198.     ngx_md5_final(c->key, &md5);

  199.     ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN);
  200. }


  201. ngx_int_t
  202. ‌ngx_http_file_cache_open(ngx_http_request_t *r)
  203. {
  204.     ngx_int_t                  rc, rv;
  205.     ngx_uint_t                 test;
  206.     ngx_http_cache_t          *c;
  207.     ngx_pool_cleanup_t        *cln;
  208.     ngx_open_file_info_t       of;
  209.     ngx_http_file_cache_t     *cache;
  210.     ngx_http_core_loc_conf_t  *clcf;

  211.     c = r->cache;

  212.     if (c->waiting) {
  213.         return NGX_AGAIN;
  214.     }

  215.     if (c->reading) {
  216.         return ngx_http_file_cache_read(r, c);
  217.     }

  218.     cache = c->file_cache;

  219.     if (c->node == NULL) {
  220.         cln = ngx_pool_cleanup_add(r->pool, 0);
  221.         if (cln == NULL) {
  222.             return NGX_ERROR;
  223.         }

  224.         cln->handler = ngx_http_file_cache_cleanup;
  225.         cln->data = c;
  226.     }

  227.     c->buffer_size = c->body_start;

  228.     rc = ngx_http_file_cache_exists(cache, c);

  229.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  230.                    "http file cache exists: %i e:%d", rc, c->exists);

  231.     if (rc == NGX_ERROR) {
  232.         return rc;
  233.     }

  234.     if (rc == NGX_AGAIN) {
  235.         return NGX_HTTP_CACHE_SCARCE;
  236.     }

  237.     if (rc == NGX_OK) {

  238.         if (c->error) {
  239.             return c->error;
  240.         }

  241.         c->temp_file = 1;
  242.         test = c->exists ? 1 : 0;
  243.         rv = NGX_DECLINED;

  244.     } else { /* rc == NGX_DECLINED */

  245.         test = cache->sh->cold ? 1 : 0;

  246.         if (c->min_uses > 1) {

  247.             if (!test) {
  248.                 return NGX_HTTP_CACHE_SCARCE;
  249.             }

  250.             rv = NGX_HTTP_CACHE_SCARCE;

  251.         } else {
  252.             c->temp_file = 1;
  253.             rv = NGX_DECLINED;
  254.         }
  255.     }

  256.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  257.         return NGX_ERROR;
  258.     }

  259.     if (!test) {
  260.         goto done;
  261.     }

  262.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

  263.     ngx_memzero(&of, sizeof(ngx_open_file_info_t));

  264.     of.uniq = c->uniq;
  265.     of.valid = clcf->open_file_cache_valid;
  266.     of.min_uses = clcf->open_file_cache_min_uses;
  267.     of.events = clcf->open_file_cache_events;
  268.     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
  269.     of.read_ahead = clcf->read_ahead;

  270.     if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
  271.         != NGX_OK)
  272.     {
  273.         switch (of.err) {

  274.         case 0:
  275.             return NGX_ERROR;

  276.         case NGX_ENOENT:
  277.         case NGX_ENOTDIR:
  278.             goto done;

  279.         default:
  280.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
  281.                           ngx_open_file_n " \"%s\" failed", c->file.name.data);
  282.             return NGX_ERROR;
  283.         }
  284.     }

  285.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  286.                    "http file cache fd: %d", of.fd);

  287.     c->file.fd = of.fd;
  288.     c->file.log = r->connection->log;
  289.     c->uniq = of.uniq;
  290.     c->length = of.size;
  291.     c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;

  292.     c->buf = ngx_create_temp_buf(r->pool, c->body_start);
  293.     if (c->buf == NULL) {
  294.         return NGX_ERROR;
  295.     }

  296.     return ngx_http_file_cache_read(r, c);

  297. done:

  298.     if (rv == NGX_DECLINED) {
  299.         return ngx_http_file_cache_lock(r, c);
  300.     }

  301.     return rv;
  302. }


  303. static ngx_int_t
  304. ‌ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
  305. {
  306.     ngx_msec_t                 now, timer;
  307.     ngx_http_file_cache_t     *cache;

  308.     if (!c->lock) {
  309.         return NGX_DECLINED;
  310.     }

  311.     now = ngx_current_msec;

  312.     cache = c->file_cache;

  313.     ngx_shmtx_lock(&cache->shpool->mutex);

  314.     timer = c->node->lock_time - now;

  315.     if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {
  316.         c->node->updating = 1;
  317.         c->node->lock_time = now + c->lock_age;
  318.         c->updating = 1;
  319.         c->lock_time = c->node->lock_time;
  320.     }

  321.     ngx_shmtx_unlock(&cache->shpool->mutex);

  322.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  323.                    "http file cache lock u:%d wt:%M",
  324.                    c->updating, c->wait_time);

  325.     if (c->updating) {
  326.         return NGX_DECLINED;
  327.     }

  328.     if (c->lock_timeout == 0) {
  329.         return NGX_HTTP_CACHE_SCARCE;
  330.     }

  331.     if (r->main->blocked >= NGX_HTTP_MAX_BLOCKED) {
  332.         return NGX_HTTP_CACHE_SCARCE;
  333.     }

  334.     c->waiting = 1;

  335.     if (c->wait_time == 0) {
  336.         c->wait_time = now + c->lock_timeout;

  337.         c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
  338.         c->wait_event.data = r;
  339.         c->wait_event.log = r->connection->log;
  340.     }

  341.     timer = c->wait_time - now;

  342.     ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);

  343.     r->main->blocked++;

  344.     return NGX_AGAIN;
  345. }


  346. static void
  347. ‌ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
  348. {
  349.     ngx_int_t            rc;
  350.     ngx_connection_t    *c;
  351.     ngx_http_request_t  *r;

  352.     r = ev->data;
  353.     c = r->connection;

  354.     ngx_http_set_log_request(c->log, r);

  355.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  356.                    "http file cache wait: \"%V?%V\"", &r->uri, &r->args);

  357.     rc = ngx_http_file_cache_lock_wait(r, r->cache);

  358.     if (rc == NGX_AGAIN) {
  359.         return;
  360.     }

  361.     r->cache->waiting = 0;
  362.     r->main->blocked--;

  363.     if (r->main->terminated) {
  364.         /*
  365.          * trigger connection event handler if the request was
  366.          * terminated
  367.          */

  368.         c->write->handler(c->write);

  369.     } else {
  370.         r->write_event_handler(r);
  371.         ngx_http_run_posted_requests(c);
  372.     }
  373. }


  374. static ngx_int_t
  375. ‌ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
  376. {
  377.     ngx_uint_t              wait;
  378.     ngx_msec_t              now, timer;
  379.     ngx_http_file_cache_t  *cache;

  380.     now = ngx_current_msec;

  381.     timer = c->wait_time - now;

  382.     if ((ngx_msec_int_t) timer <= 0) {
  383.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  384.                       "cache lock timeout");
  385.         c->lock_timeout = 0;
  386.         return NGX_OK;
  387.     }

  388.     cache = c->file_cache;
  389.     wait = 0;

  390.     ngx_shmtx_lock(&cache->shpool->mutex);

  391.     timer = c->node->lock_time - now;

  392.     if (c->node->updating && (ngx_msec_int_t) timer > 0) {
  393.         wait = 1;
  394.     }

  395.     ngx_shmtx_unlock(&cache->shpool->mutex);

  396.     if (wait) {
  397.         ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
  398.         return NGX_AGAIN;
  399.     }

  400.     return NGX_OK;
  401. }


  402. static ngx_int_t
  403. ‌ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  404. {
  405.     u_char                        *p;
  406.     time_t                         now;
  407.     ssize_t                        n;
  408.     ngx_str_t                     *key;
  409.     ngx_int_t                      rc;
  410.     ngx_uint_t                     i;
  411.     ngx_http_file_cache_t         *cache;
  412.     ngx_http_file_cache_header_t  *h;

  413.     n = ngx_http_file_cache_aio_read(r, c);

  414.     if (n < 0) {
  415.         return n;
  416.     }

  417.     if ((size_t) n < c->header_start) {
  418.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  419.                       "cache file \"%s\" is too small", c->file.name.data);
  420.         return NGX_DECLINED;
  421.     }

  422.     h = (ngx_http_file_cache_header_t *) c->buf->pos;

  423.     if (h->version != NGX_HTTP_CACHE_VERSION) {
  424.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  425.                       "cache file \"%s\" version mismatch", c->file.name.data);
  426.         return NGX_DECLINED;
  427.     }

  428.     if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {
  429.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  430.                       "cache file \"%s\" has md5 collision", c->file.name.data);
  431.         return NGX_DECLINED;
  432.     }

  433.     p = c->buf->pos + sizeof(ngx_http_file_cache_header_t)
  434.         + sizeof(ngx_http_file_cache_key);

  435.     key = c->keys.elts;
  436.     for (i = 0; i < c->keys.nelts; i++) {
  437.         if (ngx_memcmp(p, key[i].data, key[i].len) != 0) {
  438.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  439.                           "cache file \"%s\" has md5 collision",
  440.                           c->file.name.data);
  441.             return NGX_DECLINED;
  442.         }

  443.         p += key[i].len;
  444.     }

  445.     if ((size_t) h->body_start > c->body_start) {
  446.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  447.                       "cache file \"%s\" has too long header",
  448.                       c->file.name.data);
  449.         return NGX_DECLINED;
  450.     }

  451.     if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) {
  452.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  453.                       "cache file \"%s\" has incorrect vary length",
  454.                       c->file.name.data);
  455.         return NGX_DECLINED;
  456.     }

  457.     if (h->vary_len) {
  458.         ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant);

  459.         if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) {
  460.             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  461.                            "http file cache vary mismatch");
  462.             return ngx_http_file_cache_reopen(r, c);
  463.         }
  464.     }

  465.     c->buf->last += n;

  466.     c->valid_sec = h->valid_sec;
  467.     c->updating_sec = h->updating_sec;
  468.     c->error_sec = h->error_sec;
  469.     c->last_modified = h->last_modified;
  470.     c->date = h->date;
  471.     c->valid_msec = h->valid_msec;
  472.     c->body_start = h->body_start;
  473.     c->etag.len = h->etag_len;
  474.     c->etag.data = h->etag;

  475.     r->cached = 1;

  476.     cache = c->file_cache;

  477.     if (cache->sh->cold) {

  478.         ngx_shmtx_lock(&cache->shpool->mutex);

  479.         if (!c->node->exists) {
  480.             c->node->uses = 1;
  481.             c->node->body_start = c->body_start;
  482.             c->node->exists = 1;
  483.             c->node->uniq = c->uniq;
  484.             c->node->fs_size = c->fs_size;

  485.             cache->sh->size += c->fs_size;
  486.         }

  487.         ngx_shmtx_unlock(&cache->shpool->mutex);
  488.     }

  489.     now = ngx_time();

  490.     if (c->valid_sec < now) {
  491.         c->stale_updating = c->valid_sec + c->updating_sec >= now;
  492.         c->stale_error = c->valid_sec + c->error_sec >= now;

  493.         ngx_shmtx_lock(&cache->shpool->mutex);

  494.         if (c->node->updating) {
  495.             rc = NGX_HTTP_CACHE_UPDATING;

  496.         } else {
  497.             c->node->updating = 1;
  498.             c->updating = 1;
  499.             c->lock_time = c->node->lock_time;
  500.             rc = NGX_HTTP_CACHE_STALE;
  501.         }

  502.         ngx_shmtx_unlock(&cache->shpool->mutex);

  503.         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  504.                        "http file cache expired: %i %T %T",
  505.                        rc, c->valid_sec, now);

  506.         return rc;
  507.     }

  508.     return NGX_OK;
  509. }


  510. static ssize_t
  511. ‌ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  512. {
  513. #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
  514.     ssize_t                    n;
  515.     ngx_http_core_loc_conf_t  *clcf;

  516.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  517. #endif

  518. #if (NGX_HAVE_FILE_AIO)

  519.     if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio
  520.         && r->main->blocked < NGX_HTTP_MAX_BLOCKED)
  521.     {
  522.         n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);

  523.         if (n != NGX_AGAIN) {
  524.             c->reading = 0;
  525.             return n;
  526.         }

  527.         c->reading = 1;

  528.         c->file.aio->data = r;
  529.         c->file.aio->handler = ngx_http_cache_aio_event_handler;

  530.         ngx_add_timer(&c->file.aio->event, 60000);

  531.         r->main->blocked++;
  532.         r->aio = 1;

  533.         return NGX_AGAIN;
  534.     }

  535. #endif

  536. #if (NGX_THREADS)

  537.     if (clcf->aio == NGX_HTTP_AIO_THREADS
  538.         && r->main->blocked < NGX_HTTP_MAX_BLOCKED)
  539.     {
  540.         c->file.thread_task = c->thread_task;
  541.         c->file.thread_handler = ngx_http_cache_thread_handler;
  542.         c->file.thread_ctx = r;

  543.         n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);

  544.         c->thread_task = c->file.thread_task;
  545.         c->reading = (n == NGX_AGAIN);

  546.         return n;
  547.     }

  548. #endif

  549.     return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
  550. }


  551. #if (NGX_HAVE_FILE_AIO)

  552. static void
  553. ‌ngx_http_cache_aio_event_handler(ngx_event_t *ev)
  554. {
  555.     ngx_event_aio_t     *aio;
  556.     ngx_connection_t    *c;
  557.     ngx_http_request_t  *r;

  558.     aio = ev->data;
  559.     r = aio->data;
  560.     c = r->connection;

  561.     ngx_http_set_log_request(c->log, r);

  562.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  563.                    "http file cache aio: \"%V?%V\"", &r->uri, &r->args);

  564.     if (ev->timedout) {
  565.         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  566.                       "aio operation took too long");
  567.         ev->timedout = 0;
  568.         return;
  569.     }

  570.     if (ev->timer_set) {
  571.         ngx_del_timer(ev);
  572.     }

  573.     r->main->blocked--;
  574.     r->aio = 0;

  575.     if (r->main->terminated) {
  576.         /*
  577.          * trigger connection event handler if the request was
  578.          * terminated
  579.          */

  580.         c->write->handler(c->write);

  581.     } else {
  582.         r->write_event_handler(r);
  583.         ngx_http_run_posted_requests(c);
  584.     }
  585. }

  586. #endif


  587. #if (NGX_THREADS)

  588. static ngx_int_t
  589. ‌ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
  590. {
  591.     ngx_str_t                  name;
  592.     ngx_thread_pool_t         *tp;
  593.     ngx_http_request_t        *r;
  594.     ngx_http_core_loc_conf_t  *clcf;

  595.     r = file->thread_ctx;

  596.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  597.     tp = clcf->thread_pool;

  598.     if (tp == NULL) {
  599.         if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
  600.             != NGX_OK)
  601.         {
  602.             return NGX_ERROR;
  603.         }

  604.         tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);

  605.         if (tp == NULL) {
  606.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  607.                           "thread pool \"%V\" not found", &name);
  608.             return NGX_ERROR;
  609.         }
  610.     }

  611.     task->event.data = r;
  612.     task->event.handler = ngx_http_cache_thread_event_handler;

  613.     if (ngx_thread_task_post(tp, task) != NGX_OK) {
  614.         return NGX_ERROR;
  615.     }

  616.     ngx_add_timer(&task->event, 60000);

  617.     r->main->blocked++;
  618.     r->aio = 1;

  619.     return NGX_OK;
  620. }


  621. static void
  622. ‌ngx_http_cache_thread_event_handler(ngx_event_t *ev)
  623. {
  624.     ngx_connection_t    *c;
  625.     ngx_http_request_t  *r;

  626.     r = ev->data;
  627.     c = r->connection;

  628.     ngx_http_set_log_request(c->log, r);

  629.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  630.                    "http file cache thread: \"%V?%V\"", &r->uri, &r->args);

  631.     if (ev->timedout) {
  632.         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  633.                       "thread operation took too long");
  634.         ev->timedout = 0;
  635.         return;
  636.     }

  637.     if (ev->timer_set) {
  638.         ngx_del_timer(ev);
  639.     }

  640.     r->main->blocked--;
  641.     r->aio = 0;

  642.     if (r->main->terminated) {
  643.         /*
  644.          * trigger connection event handler if the request was
  645.          * terminated
  646.          */

  647.         c->write->handler(c->write);

  648.     } else {
  649.         r->write_event_handler(r);
  650.         ngx_http_run_posted_requests(c);
  651.     }
  652. }

  653. #endif


  654. static ngx_int_t
  655. ‌ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  656. {
  657.     ngx_int_t                    rc;
  658.     ngx_http_file_cache_node_t  *fcn;

  659.     ngx_shmtx_lock(&cache->shpool->mutex);

  660.     fcn = c->node;

  661.     if (fcn == NULL) {
  662.         fcn = ngx_http_file_cache_lookup(cache, c->key);
  663.     }

  664.     if (fcn) {
  665.         ngx_queue_remove(&fcn->queue);

  666.         if (c->node == NULL) {
  667.             fcn->uses++;
  668.             fcn->count++;
  669.         }

  670.         if (fcn->error) {

  671.             if (fcn->valid_sec < ngx_time()) {
  672.                 goto renew;
  673.             }

  674.             rc = NGX_OK;

  675.             goto done;
  676.         }

  677.         if (fcn->exists || fcn->uses >= c->min_uses) {

  678.             c->exists = fcn->exists;
  679.             if (fcn->body_start && !c->update_variant) {
  680.                 c->body_start = fcn->body_start;
  681.             }

  682.             rc = NGX_OK;

  683.             goto done;
  684.         }

  685.         rc = NGX_AGAIN;

  686.         goto done;
  687.     }

  688.     fcn = ngx_slab_calloc_locked(cache->shpool,
  689.                                  sizeof(ngx_http_file_cache_node_t));
  690.     if (fcn == NULL) {
  691.         ngx_http_file_cache_set_watermark(cache);

  692.         ngx_shmtx_unlock(&cache->shpool->mutex);

  693.         (void) ngx_http_file_cache_forced_expire(cache);

  694.         ngx_shmtx_lock(&cache->shpool->mutex);

  695.         fcn = ngx_slab_calloc_locked(cache->shpool,
  696.                                      sizeof(ngx_http_file_cache_node_t));
  697.         if (fcn == NULL) {
  698.             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  699.                           "could not allocate node%s", cache->shpool->log_ctx);
  700.             rc = NGX_ERROR;
  701.             goto failed;
  702.         }
  703.     }

  704.     cache->sh->count++;

  705.     ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

  706.     ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
  707.                NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

  708.     ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

  709.     fcn->uses = 1;
  710.     fcn->count = 1;

  711. renew:

  712.     rc = NGX_DECLINED;

  713.     fcn->valid_msec = 0;
  714.     fcn->error = 0;
  715.     fcn->exists = 0;
  716.     fcn->valid_sec = 0;
  717.     fcn->uniq = 0;
  718.     fcn->body_start = 0;
  719.     fcn->fs_size = 0;

  720. done:

  721.     fcn->expire = ngx_time() + cache->inactive;

  722.     ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

  723.     c->uniq = fcn->uniq;
  724.     c->error = fcn->error;
  725.     c->node = fcn;

  726. failed:

  727.     ngx_shmtx_unlock(&cache->shpool->mutex);

  728.     return rc;
  729. }


  730. static ngx_int_t
  731. ‌ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
  732. {
  733.     u_char            *p;
  734.     ngx_http_cache_t  *c;

  735.     c = r->cache;

  736.     if (c->file.name.len) {
  737.         return NGX_OK;
  738.     }

  739.     c->file.name.len = path->name.len + 1 + path->len
  740.                        + 2 * NGX_HTTP_CACHE_KEY_LEN;

  741.     c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
  742.     if (c->file.name.data == NULL) {
  743.         return NGX_ERROR;
  744.     }

  745.     ngx_memcpy(c->file.name.data, path->name.data, path->name.len);

  746.     p = c->file.name.data + path->name.len + 1 + path->len;
  747.     p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
  748.     *p = '\0';

  749.     ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);

  750.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  751.                    "cache file: \"%s\"", c->file.name.data);

  752.     return NGX_OK;
  753. }


  754. static ngx_http_file_cache_node_t *
  755. ‌ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
  756. {
  757.     ngx_int_t                    rc;
  758.     ngx_rbtree_key_t             node_key;
  759.     ngx_rbtree_node_t           *node, *sentinel;
  760.     ngx_http_file_cache_node_t  *fcn;

  761.     ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));

  762.     node = cache->sh->rbtree.root;
  763.     sentinel = cache->sh->rbtree.sentinel;

  764.     while (node != sentinel) {

  765.         if (node_key < node->key) {
  766.             node = node->left;
  767.             continue;
  768.         }

  769.         if (node_key > node->key) {
  770.             node = node->right;
  771.             continue;
  772.         }

  773.         /* node_key == node->key */

  774.         fcn = (ngx_http_file_cache_node_t *) node;

  775.         rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
  776.                         NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

  777.         if (rc == 0) {
  778.             return fcn;
  779.         }

  780.         node = (rc < 0) ? node->left : node->right;
  781.     }

  782.     /* not found */

  783.     return NULL;
  784. }


  785. static void
  786. ‌ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  787.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  788. {
  789.     ngx_rbtree_node_t           **p;
  790.     ngx_http_file_cache_node_t   *cn, *cnt;

  791.     for ( ;; ) {

  792.         if (node->key < temp->key) {

  793.             p = &temp->left;

  794.         } else if (node->key > temp->key) {

  795.             p = &temp->right;

  796.         } else { /* node->key == temp->key */

  797.             cn = (ngx_http_file_cache_node_t *) node;
  798.             cnt = (ngx_http_file_cache_node_t *) temp;

  799.             p = (ngx_memcmp(cn->key, cnt->key,
  800.                             NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
  801.                  < 0)
  802.                     ? &temp->left : &temp->right;
  803.         }

  804.         if (*p == sentinel) {
  805.             break;
  806.         }

  807.         temp = *p;
  808.     }

  809.     *p = node;
  810.     node->parent = temp;
  811.     node->left = sentinel;
  812.     node->right = sentinel;
  813.     ngx_rbt_red(node);
  814. }


  815. static void
  816. ‌ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,
  817.     u_char *hash)
  818. {
  819.     u_char     *p, *last;
  820.     ngx_str_t   name;
  821.     ngx_md5_t   md5;
  822.     u_char      buf[NGX_HTTP_CACHE_VARY_LEN];

  823.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  824.                    "http file cache vary: \"%*s\"", len, vary);

  825.     ngx_md5_init(&md5);
  826.     ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);

  827.     ngx_strlow(buf, vary, len);

  828.     p = buf;
  829.     last = buf + len;

  830.     while (p < last) {

  831.         while (p < last && (*p == ' ' || *p == ',')) { p++; }

  832.         name.data = p;

  833.         while (p < last && *p != ',' && *p != ' ') { p++; }

  834.         name.len = p - name.data;

  835.         if (name.len == 0) {
  836.             break;
  837.         }

  838.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  839.                        "http file cache vary: %V", &name);

  840.         ngx_md5_update(&md5, name.data, name.len);
  841.         ngx_md5_update(&md5, (u_char *) ":", sizeof(":") - 1);

  842.         ngx_http_file_cache_vary_header(r, &md5, &name);

  843.         ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);
  844.     }

  845.     ngx_md5_final(hash, &md5);
  846. }


  847. static void
  848. ‌ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,
  849.     ngx_str_t *name)
  850. {
  851.     size_t            len;
  852.     u_char           *p, *start, *last;
  853.     ngx_uint_t        i, multiple, normalize;
  854.     ngx_list_part_t  *part;
  855.     ngx_table_elt_t  *header;

  856.     multiple = 0;
  857.     normalize = 0;

  858.     if (name->len == sizeof("Accept-Charset") - 1
  859.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset",
  860.                            sizeof("Accept-Charset") - 1) == 0)
  861.     {
  862.         normalize = 1;

  863.     } else if (name->len == sizeof("Accept-Encoding") - 1
  864.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding",
  865.                            sizeof("Accept-Encoding") - 1) == 0)
  866.     {
  867.         normalize = 1;

  868.     } else if (name->len == sizeof("Accept-Language") - 1
  869.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Language",
  870.                            sizeof("Accept-Language") - 1) == 0)
  871.     {
  872.         normalize = 1;
  873.     }

  874.     part = &r->headers_in.headers.part;
  875.     header = part->elts;

  876.     for (i = 0; /* void */ ; i++) {

  877.         if (i >= part->nelts) {
  878.             if (part->next == NULL) {
  879.                 break;
  880.             }

  881.             part = part->next;
  882.             header = part->elts;
  883.             i = 0;
  884.         }

  885.         if (header[i].hash == 0) {
  886.             continue;
  887.         }

  888.         if (header[i].key.len != name->len) {
  889.             continue;
  890.         }

  891.         if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {
  892.             continue;
  893.         }

  894.         if (!normalize) {

  895.             if (multiple) {
  896.                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  897.             }

  898.             ngx_md5_update(md5, header[i].value.data, header[i].value.len);

  899.             multiple = 1;

  900.             continue;
  901.         }

  902.         /* normalize spaces */

  903.         p = header[i].value.data;
  904.         last = p + header[i].value.len;

  905.         while (p < last) {

  906.             while (p < last && (*p == ' ' || *p == ',')) { p++; }

  907.             start = p;

  908.             while (p < last && *p != ',' && *p != ' ') { p++; }

  909.             len = p - start;

  910.             if (len == 0) {
  911.                 break;
  912.             }

  913.             if (multiple) {
  914.                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  915.             }

  916.             ngx_md5_update(md5, start, len);

  917.             multiple = 1;
  918.         }
  919.     }
  920. }


  921. static ngx_int_t
  922. ‌ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)
  923. {
  924.     ngx_http_file_cache_t  *cache;

  925.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  926.                    "http file cache reopen");

  927.     if (c->secondary) {
  928.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  929.                       "cache file \"%s\" has incorrect vary hash",
  930.                       c->file.name.data);
  931.         return NGX_DECLINED;
  932.     }

  933.     cache = c->file_cache;

  934.     ngx_shmtx_lock(&cache->shpool->mutex);

  935.     c->node->count--;
  936.     c->node = NULL;

  937.     ngx_shmtx_unlock(&cache->shpool->mutex);

  938.     c->secondary = 1;
  939.     c->file.name.len = 0;
  940.     c->body_start = c->buffer_size;

  941.     ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN);

  942.     return ngx_http_file_cache_open(r);
  943. }


  944. ngx_int_t
  945. ‌ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
  946. {
  947.     ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;

  948.     u_char            *p;
  949.     ngx_str_t         *key;
  950.     ngx_uint_t         i;
  951.     ngx_http_cache_t  *c;

  952.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  953.                    "http file cache set header");

  954.     c = r->cache;

  955.     ngx_memzero(h, sizeof(ngx_http_file_cache_header_t));

  956.     h->version = NGX_HTTP_CACHE_VERSION;
  957.     h->valid_sec = c->valid_sec;
  958.     h->updating_sec = c->updating_sec;
  959.     h->error_sec = c->error_sec;
  960.     h->last_modified = c->last_modified;
  961.     h->date = c->date;
  962.     h->crc32 = c->crc32;
  963.     h->valid_msec = (u_short) c->valid_msec;
  964.     h->header_start = (u_short) c->header_start;
  965.     h->body_start = (u_short) c->body_start;

  966.     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  967.         h->etag_len = (u_char) c->etag.len;
  968.         ngx_memcpy(h->etag, c->etag.data, c->etag.len);
  969.     }

  970.     if (c->vary.len) {
  971.         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  972.             /* should not happen */
  973.             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  974.         }

  975.         h->vary_len = (u_char) c->vary.len;
  976.         ngx_memcpy(h->vary, c->vary.data, c->vary.len);

  977.         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  978.         ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  979.     }

  980.     if (ngx_http_file_cache_update_variant(r, c) != NGX_OK) {
  981.         return NGX_ERROR;
  982.     }

  983.     p = buf + sizeof(ngx_http_file_cache_header_t);

  984.     p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));

  985.     key = c->keys.elts;
  986.     for (i = 0; i < c->keys.nelts; i++) {
  987.         p = ngx_copy(p, key[i].data, key[i].len);
  988.     }

  989.     *p = LF;

  990.     return NGX_OK;
  991. }


  992. static ngx_int_t
  993. ‌ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)
  994. {
  995.     ngx_http_file_cache_t  *cache;

  996.     if (!c->secondary) {
  997.         return NGX_OK;
  998.     }

  999.     if (c->vary.len
  1000.         && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) == 0)
  1001.     {
  1002.         return NGX_OK;
  1003.     }

  1004.     /*
  1005.      * if the variant hash doesn't match one we used as a secondary
  1006.      * cache key, switch back to the original key
  1007.      */

  1008.     cache = c->file_cache;

  1009.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1010.                    "http file cache main key");

  1011.     ngx_shmtx_lock(&cache->shpool->mutex);

  1012.     c->node->count--;
  1013.     c->node->updating = 0;
  1014.     c->node = NULL;

  1015.     ngx_shmtx_unlock(&cache->shpool->mutex);

  1016.     c->file.name.len = 0;
  1017.     c->update_variant = 1;

  1018.     ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN);

  1019.     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  1020.         return NGX_ERROR;
  1021.     }

  1022.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  1023.         return NGX_ERROR;
  1024.     }

  1025.     return NGX_OK;
  1026. }


  1027. void
  1028. ‌ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
  1029. {
  1030.     off_t                   fs_size;
  1031.     ngx_int_t               rc;
  1032.     ngx_file_uniq_t         uniq;
  1033.     ngx_file_info_t         fi;
  1034.     ngx_http_cache_t        *c;
  1035.     ngx_ext_rename_file_t   ext;
  1036.     ngx_http_file_cache_t  *cache;

  1037.     c = r->cache;

  1038.     if (c->updated) {
  1039.         return;
  1040.     }

  1041.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1042.                    "http file cache update");

  1043.     cache = c->file_cache;

  1044.     c->updated = 1;
  1045.     c->updating = 0;

  1046.     uniq = 0;
  1047.     fs_size = 0;

  1048.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1049.                    "http file cache rename: \"%s\" to \"%s\"",
  1050.                    tf->file.name.data, c->file.name.data);

  1051.     ext.access = NGX_FILE_OWNER_ACCESS;
  1052.     ext.path_access = NGX_FILE_OWNER_ACCESS;
  1053.     ext.time = -1;
  1054.     ext.create_path = 1;
  1055.     ext.delete_file = 1;
  1056.     ext.log = r->connection->log;

  1057.     rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);

  1058.     if (rc == NGX_OK) {

  1059.         if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
  1060.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  1061.                           ngx_fd_info_n " \"%s\" failed", tf->file.name.data);

  1062.             rc = NGX_ERROR;

  1063.         } else {
  1064.             uniq = ngx_file_uniq(&fi);
  1065.             fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
  1066.         }
  1067.     }

  1068.     ngx_shmtx_lock(&cache->shpool->mutex);

  1069.     c->node->count--;
  1070.     c->node->error = 0;
  1071.     c->node->uniq = uniq;
  1072.     c->node->body_start = c->body_start;

  1073.     cache->sh->size += fs_size - c->node->fs_size;
  1074.     c->node->fs_size = fs_size;

  1075.     if (rc == NGX_OK) {
  1076.         c->node->exists = 1;
  1077.     }

  1078.     c->node->updating = 0;

  1079.     ngx_shmtx_unlock(&cache->shpool->mutex);
  1080. }


  1081. void
  1082. ‌ngx_http_file_cache_update_header(ngx_http_request_t *r)
  1083. {
  1084.     ssize_t                        n;
  1085.     ngx_err_t                      err;
  1086.     ngx_file_t                     file;
  1087.     ngx_file_info_t                fi;
  1088.     ngx_http_cache_t              *c;
  1089.     ngx_http_file_cache_header_t   h;

  1090.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1091.                    "http file cache update header");

  1092.     c = r->cache;

  1093.     ngx_memzero(&file, sizeof(ngx_file_t));

  1094.     file.name = c->file.name;
  1095.     file.log = r->connection->log;
  1096.     file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);

  1097.     if (file.fd == NGX_INVALID_FILE) {
  1098.         err = ngx_errno;

  1099.         /* cache file may have been deleted */

  1100.         if (err == NGX_ENOENT) {
  1101.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1102.                            "http file cache \"%s\" not found",
  1103.                            file.name.data);
  1104.             return;
  1105.         }

  1106.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
  1107.                       ngx_open_file_n " \"%s\" failed", file.name.data);
  1108.         return;
  1109.     }

  1110.     /*
  1111.      * make sure cache file wasn't replaced;
  1112.      * if it was, do nothing
  1113.      */

  1114.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1115.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  1116.                       ngx_fd_info_n " \"%s\" failed", file.name.data);
  1117.         goto done;
  1118.     }

  1119.     if (c->uniq != ngx_file_uniq(&fi)
  1120.         || c->length != ngx_file_size(&fi))
  1121.     {
  1122.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1123.                        "http file cache \"%s\" changed",
  1124.                        file.name.data);
  1125.         goto done;
  1126.     }

  1127.     n = ngx_read_file(&file, (u_char *) &h,
  1128.                       sizeof(ngx_http_file_cache_header_t), 0);

  1129.     if (n == NGX_ERROR) {
  1130.         goto done;
  1131.     }

  1132.     if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {
  1133.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  1134.                       ngx_read_file_n " read only %z of %z from \"%s\"",
  1135.                       n, sizeof(ngx_http_file_cache_header_t), file.name.data);
  1136.         goto done;
  1137.     }

  1138.     if (h.version != NGX_HTTP_CACHE_VERSION
  1139.         || h.last_modified != c->last_modified
  1140.         || h.crc32 != c->crc32
  1141.         || (size_t) h.header_start != c->header_start
  1142.         || (size_t) h.body_start != c->body_start)
  1143.     {
  1144.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1145.                        "http file cache \"%s\" content changed",
  1146.                        file.name.data);
  1147.         goto done;
  1148.     }

  1149.     /*
  1150.      * update cache file header with new data,
  1151.      * notably h.valid_sec and h.date
  1152.      */

  1153.     ngx_memzero(&h, sizeof(ngx_http_file_cache_header_t));

  1154.     h.version = NGX_HTTP_CACHE_VERSION;
  1155.     h.valid_sec = c->valid_sec;
  1156.     h.updating_sec = c->updating_sec;
  1157.     h.error_sec = c->error_sec;
  1158.     h.last_modified = c->last_modified;
  1159.     h.date = c->date;
  1160.     h.crc32 = c->crc32;
  1161.     h.valid_msec = (u_short) c->valid_msec;
  1162.     h.header_start = (u_short) c->header_start;
  1163.     h.body_start = (u_short) c->body_start;

  1164.     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  1165.         h.etag_len = (u_char) c->etag.len;
  1166.         ngx_memcpy(h.etag, c->etag.data, c->etag.len);
  1167.     }

  1168.     if (c->vary.len) {
  1169.         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  1170.             /* should not happen */
  1171.             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  1172.         }

  1173.         h.vary_len = (u_char) c->vary.len;
  1174.         ngx_memcpy(h.vary, c->vary.data, c->vary.len);

  1175.         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  1176.         ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  1177.     }

  1178.     (void) ngx_write_file(&file, (u_char *) &h,
  1179.                           sizeof(ngx_http_file_cache_header_t), 0);

  1180. done:

  1181.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1182.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
  1183.                       ngx_close_file_n " \"%s\" failed", file.name.data);
  1184.     }
  1185. }


  1186. ngx_int_t
  1187. ‌ngx_http_cache_send(ngx_http_request_t *r)
  1188. {
  1189.     ngx_int_t          rc;
  1190.     ngx_buf_t         *b;
  1191.     ngx_chain_t        out;
  1192.     ngx_http_cache_t  *c;

  1193.     c = r->cache;

  1194.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1195.                    "http file cache send: %s", c->file.name.data);

  1196.     /* we need to allocate all before the header would be sent */

  1197.     b = ngx_calloc_buf(r->pool);
  1198.     if (b == NULL) {
  1199.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1200.     }

  1201.     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
  1202.     if (b->file == NULL) {
  1203.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1204.     }

  1205.     rc = ngx_http_send_header(r);

  1206.     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
  1207.         return rc;
  1208.     }

  1209.     b->file_pos = c->body_start;
  1210.     b->file_last = c->length;

  1211.     b->in_file = (c->length - c->body_start) ? 1 : 0;
  1212.     b->last_buf = (r == r->main) ? 1 : 0;
  1213.     b->last_in_chain = 1;
  1214.     b->sync = (b->last_buf || b->in_file) ? 0 : 1;

  1215.     b->file->fd = c->file.fd;
  1216.     b->file->name = c->file.name;
  1217.     b->file->log = r->connection->log;

  1218.     out.buf = b;
  1219.     out.next = NULL;

  1220.     return ngx_http_output_filter(r, &out);
  1221. }


  1222. void
  1223. ‌ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
  1224. {
  1225.     ngx_http_file_cache_t       *cache;
  1226.     ngx_http_file_cache_node_t  *fcn;

  1227.     if (c->updated || c->node == NULL) {
  1228.         return;
  1229.     }

  1230.     cache = c->file_cache;

  1231.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1232.                    "http file cache free, fd: %d", c->file.fd);

  1233.     ngx_shmtx_lock(&cache->shpool->mutex);

  1234.     fcn = c->node;
  1235.     fcn->count--;

  1236.     if (c->updating && fcn->lock_time == c->lock_time) {
  1237.         fcn->updating = 0;
  1238.     }

  1239.     if (c->error) {
  1240.         fcn->error = c->error;

  1241.         if (c->valid_sec) {
  1242.             fcn->valid_sec = c->valid_sec;
  1243.             fcn->valid_msec = c->valid_msec;
  1244.         }

  1245.     } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
  1246.         ngx_queue_remove(&fcn->queue);
  1247.         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1248.         ngx_slab_free_locked(cache->shpool, fcn);
  1249.         cache->sh->count--;
  1250.         c->node = NULL;
  1251.     }

  1252.     ngx_shmtx_unlock(&cache->shpool->mutex);

  1253.     c->updated = 1;
  1254.     c->updating = 0;

  1255.     if (c->temp_file) {
  1256.         if (tf && tf->file.fd != NGX_INVALID_FILE) {
  1257.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1258.                            "http file cache incomplete: \"%s\"",
  1259.                            tf->file.name.data);

  1260.             if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
  1261.                 ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
  1262.                               ngx_delete_file_n " \"%s\" failed",
  1263.                               tf->file.name.data);
  1264.             }
  1265.         }
  1266.     }

  1267.     if (c->wait_event.timer_set) {
  1268.         ngx_del_timer(&c->wait_event);
  1269.     }
  1270. }


  1271. static void
  1272. ‌ngx_http_file_cache_cleanup(void *data)
  1273. {
  1274.     ngx_http_cache_t  *c = data;

  1275.     if (c->updated) {
  1276.         return;
  1277.     }

  1278.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1279.                    "http file cache cleanup");

  1280.     if (c->updating && !c->background) {
  1281.         ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
  1282.                       "stalled cache updating, error:%ui", c->error);
  1283.     }

  1284.     ngx_http_file_cache_free(c, NULL);
  1285. }


  1286. static time_t
  1287. ‌ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
  1288. {
  1289.     u_char                      *name, *p;
  1290.     size_t                       len;
  1291.     time_t                       wait;
  1292.     ngx_uint_t                   tries;
  1293.     ngx_path_t                  *path;
  1294.     ngx_queue_t                 *q, *sentinel;
  1295.     ngx_http_file_cache_node_t  *fcn;
  1296.     u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];

  1297.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1298.                    "http file cache forced expire");

  1299.     path = cache->path;
  1300.     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

  1301.     name = ngx_alloc(len + 1, ngx_cycle->log);
  1302.     if (name == NULL) {
  1303.         return 10;
  1304.     }

  1305.     ngx_memcpy(name, path->name.data, path->name.len);

  1306.     wait = 10;
  1307.     tries = 20;
  1308.     sentinel = NULL;

  1309.     ngx_shmtx_lock(&cache->shpool->mutex);

  1310.     for ( ;; ) {
  1311.         if (ngx_queue_empty(&cache->sh->queue)) {
  1312.             break;
  1313.         }

  1314.         q = ngx_queue_last(&cache->sh->queue);

  1315.         if (q == sentinel) {
  1316.             break;
  1317.         }

  1318.         fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

  1319.         ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1320.                   "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
  1321.                   fcn->count, fcn->exists,
  1322.                   fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);

  1323.         if (fcn->count == 0) {
  1324.             ngx_http_file_cache_delete(cache, q, name);
  1325.             wait = 0;
  1326.             break;
  1327.         }

  1328.         if (fcn->deleting) {
  1329.             wait = 1;
  1330.             break;
  1331.         }

  1332.         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1333.                          sizeof(ngx_rbtree_key_t));
  1334.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1335.         (void) ngx_hex_dump(p, fcn->key, len);

  1336.         /*
  1337.          * abnormally exited workers may leave locked cache entries,
  1338.          * and although it may be safe to remove them completely,
  1339.          * we prefer to just move them to the top of the inactive queue
  1340.          */

  1341.         ngx_queue_remove(q);
  1342.         fcn->expire = ngx_time() + cache->inactive;
  1343.         ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

  1344.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1345.                       "ignore long locked inactive cache entry %*s, count:%d",
  1346.                       (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);

  1347.         if (sentinel == NULL) {
  1348.             sentinel = q;
  1349.         }

  1350.         if (--tries) {
  1351.             continue;
  1352.         }

  1353.         wait = 1;
  1354.         break;
  1355.     }

  1356.     ngx_shmtx_unlock(&cache->shpool->mutex);

  1357.     ngx_free(name);

  1358.     return wait;
  1359. }


  1360. static time_t
  1361. ‌ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
  1362. {
  1363.     u_char                      *name, *p;
  1364.     size_t                       len;
  1365.     time_t                       now, wait;
  1366.     ngx_path_t                  *path;
  1367.     ngx_msec_t                   elapsed;
  1368.     ngx_queue_t                 *q;
  1369.     ngx_http_file_cache_node_t  *fcn;
  1370.     u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];

  1371.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1372.                    "http file cache expire");

  1373.     path = cache->path;
  1374.     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

  1375.     name = ngx_alloc(len + 1, ngx_cycle->log);
  1376.     if (name == NULL) {
  1377.         return 10;
  1378.     }

  1379.     ngx_memcpy(name, path->name.data, path->name.len);

  1380.     now = ngx_time();

  1381.     ngx_shmtx_lock(&cache->shpool->mutex);

  1382.     for ( ;; ) {

  1383.         if (ngx_quit || ngx_terminate) {
  1384.             wait = 1;
  1385.             break;
  1386.         }

  1387.         if (ngx_queue_empty(&cache->sh->queue)) {
  1388.             wait = 10;
  1389.             break;
  1390.         }

  1391.         q = ngx_queue_last(&cache->sh->queue);

  1392.         fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

  1393.         wait = fcn->expire - now;

  1394.         if (wait > 0) {
  1395.             wait = wait > 10 ? 10 : wait;
  1396.             break;
  1397.         }

  1398.         ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1399.                        "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
  1400.                        fcn->count, fcn->exists,
  1401.                        fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);

  1402.         if (fcn->count == 0) {
  1403.             ngx_http_file_cache_delete(cache, q, name);
  1404.             goto next;
  1405.         }

  1406.         if (fcn->deleting) {
  1407.             wait = 1;
  1408.             break;
  1409.         }

  1410.         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1411.                          sizeof(ngx_rbtree_key_t));
  1412.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1413.         (void) ngx_hex_dump(p, fcn->key, len);

  1414.         /*
  1415.          * abnormally exited workers may leave locked cache entries,
  1416.          * and although it may be safe to remove them completely,
  1417.          * we prefer to just move them to the top of the inactive queue
  1418.          */

  1419.         ngx_queue_remove(q);
  1420.         fcn->expire = ngx_time() + cache->inactive;
  1421.         ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

  1422.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1423.                       "ignore long locked inactive cache entry %*s, count:%d",
  1424.                       (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);

  1425. next:

  1426.         if (++cache->files >= cache->manager_files) {
  1427.             wait = 0;
  1428.             break;
  1429.         }

  1430.         ngx_time_update();

  1431.         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));

  1432.         if (elapsed >= cache->manager_threshold) {
  1433.             wait = 0;
  1434.             break;
  1435.         }
  1436.     }

  1437.     ngx_shmtx_unlock(&cache->shpool->mutex);

  1438.     ngx_free(name);

  1439.     return wait;
  1440. }


  1441. static void
  1442. ‌ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
  1443.     u_char *name)
  1444. {
  1445.     u_char                      *p;
  1446.     size_t                       len;
  1447.     ngx_path_t                  *path;
  1448.     ngx_http_file_cache_node_t  *fcn;

  1449.     fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

  1450.     if (fcn->exists) {
  1451.         cache->sh->size -= fcn->fs_size;

  1452.         path = cache->path;
  1453.         p = name + path->name.len + 1 + path->len;
  1454.         p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
  1455.                          sizeof(ngx_rbtree_key_t));
  1456.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1457.         p = ngx_hex_dump(p, fcn->key, len);
  1458.         *p = '\0';

  1459.         fcn->count++;
  1460.         fcn->deleting = 1;
  1461.         ngx_shmtx_unlock(&cache->shpool->mutex);

  1462.         len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
  1463.         ngx_create_hashed_filename(path, name, len);

  1464.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1465.                        "http file cache expire: \"%s\"", name);

  1466.         if (ngx_delete_file(name) == NGX_FILE_ERROR) {
  1467.             ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
  1468.                           ngx_delete_file_n " \"%s\" failed", name);
  1469.         }

  1470.         ngx_shmtx_lock(&cache->shpool->mutex);
  1471.         fcn->count--;
  1472.         fcn->deleting = 0;
  1473.     }

  1474.     if (fcn->count == 0) {
  1475.         ngx_queue_remove(q);
  1476.         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1477.         ngx_slab_free_locked(cache->shpool, fcn);
  1478.         cache->sh->count--;
  1479.     }
  1480. }


  1481. static ngx_msec_t
  1482. ‌ngx_http_file_cache_manager(void *data)
  1483. {
  1484.     ngx_http_file_cache_t  *cache = data;

  1485.     off_t       size, free;
  1486.     time_t      wait;
  1487.     ngx_msec_t  elapsed, next;
  1488.     ngx_uint_t  count, watermark;

  1489.     cache->last = ngx_current_msec;
  1490.     cache->files = 0;

  1491.     next = (ngx_msec_t) ngx_http_file_cache_expire(cache) * 1000;

  1492.     if (next == 0) {
  1493.         next = cache->manager_sleep;
  1494.         goto done;
  1495.     }

  1496.     for ( ;; ) {
  1497.         ngx_shmtx_lock(&cache->shpool->mutex);

  1498.         size = cache->sh->size;
  1499.         count = cache->sh->count;
  1500.         watermark = cache->sh->watermark;

  1501.         ngx_shmtx_unlock(&cache->shpool->mutex);

  1502.         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1503.                        "http file cache size: %O c:%ui w:%i",
  1504.                        size, count, (ngx_int_t) watermark);

  1505.         if (size < cache->max_size && count < watermark) {

  1506.             if (!cache->min_free) {
  1507.                 break;
  1508.             }

  1509.             free = ngx_fs_available(cache->path->name.data);

  1510.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1511.                            "http file cache free: %O", free);

  1512.             if (free > cache->min_free) {
  1513.                 break;
  1514.             }
  1515.         }

  1516.         wait = ngx_http_file_cache_forced_expire(cache);

  1517.         if (wait > 0) {
  1518.             next = (ngx_msec_t) wait * 1000;
  1519.             break;
  1520.         }

  1521.         if (ngx_quit || ngx_terminate) {
  1522.             break;
  1523.         }

  1524.         if (++cache->files >= cache->manager_files) {
  1525.             next = cache->manager_sleep;
  1526.             break;
  1527.         }

  1528.         ngx_time_update();

  1529.         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));

  1530.         if (elapsed >= cache->manager_threshold) {
  1531.             next = cache->manager_sleep;
  1532.             break;
  1533.         }
  1534.     }

  1535. done:

  1536.     elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));

  1537.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1538.                    "http file cache manager: %ui e:%M n:%M",
  1539.                    cache->files, elapsed, next);

  1540.     return next;
  1541. }


  1542. static void
  1543. ‌ngx_http_file_cache_loader(void *data)
  1544. {
  1545.     ngx_http_file_cache_t  *cache = data;

  1546.     ngx_tree_ctx_t  tree;

  1547.     if (!cache->sh->cold || cache->sh->loading) {
  1548.         return;
  1549.     }

  1550.     if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
  1551.         return;
  1552.     }

  1553.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1554.                    "http file cache loader");

  1555.     tree.init_handler = NULL;
  1556.     tree.file_handler = ngx_http_file_cache_manage_file;
  1557.     tree.pre_tree_handler = ngx_http_file_cache_manage_directory;
  1558.     tree.post_tree_handler = ngx_http_file_cache_noop;
  1559.     tree.spec_handler = ngx_http_file_cache_delete_file;
  1560.     tree.data = cache;
  1561.     tree.alloc = 0;
  1562.     tree.log = ngx_cycle->log;

  1563.     cache->last = ngx_current_msec;
  1564.     cache->files = 0;

  1565.     if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
  1566.         cache->sh->loading = 0;
  1567.         return;
  1568.     }

  1569.     cache->sh->cold = 0;
  1570.     cache->sh->loading = 0;

  1571.     ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
  1572.                   "http file cache: %V %.3fM, bsize: %uz",
  1573.                   &cache->path->name,
  1574.                   ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
  1575.                   cache->bsize);
  1576. }


  1577. static ngx_int_t
  1578. ‌ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1579. {
  1580.     return NGX_OK;
  1581. }


  1582. static ngx_int_t
  1583. ‌ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1584. {
  1585.     ngx_msec_t              elapsed;
  1586.     ngx_http_file_cache_t  *cache;

  1587.     cache = ctx->data;

  1588.     if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
  1589.         (void) ngx_http_file_cache_delete_file(ctx, path);
  1590.     }

  1591.     if (++cache->files >= cache->loader_files) {
  1592.         ngx_http_file_cache_loader_sleep(cache);

  1593.     } else {
  1594.         ngx_time_update();

  1595.         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));

  1596.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1597.                        "http file cache loader time elapsed: %M", elapsed);

  1598.         if (elapsed >= cache->loader_threshold) {
  1599.             ngx_http_file_cache_loader_sleep(cache);
  1600.         }
  1601.     }

  1602.     return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
  1603. }


  1604. static ngx_int_t
  1605. ‌ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1606. {
  1607.     if (path->len >= 5
  1608.         && ngx_strncmp(path->data + path->len - 5, "/temp", 5) == 0)
  1609.     {
  1610.         return NGX_DECLINED;
  1611.     }

  1612.     return NGX_OK;
  1613. }


  1614. static void
  1615. ‌ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
  1616. {
  1617.     ngx_msleep(cache->loader_sleep);

  1618.     ngx_time_update();

  1619.     cache->last = ngx_current_msec;
  1620.     cache->files = 0;
  1621. }


  1622. static ngx_int_t
  1623. ‌ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
  1624. {
  1625.     u_char                 *p;
  1626.     ngx_int_t               n;
  1627.     ngx_uint_t              i;
  1628.     ngx_http_cache_t        c;
  1629.     ngx_http_file_cache_t  *cache;

  1630.     if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
  1631.         return NGX_ERROR;
  1632.     }

  1633.     /*
  1634.      * Temporary files in cache have a suffix consisting of a dot
  1635.      * followed by 10 digits.
  1636.      */

  1637.     if (name->len >= 2 * NGX_HTTP_CACHE_KEY_LEN + 1 + 10
  1638.         && name->data[name->len - 10 - 1] == '.')
  1639.     {
  1640.         return NGX_OK;
  1641.     }

  1642.     if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
  1643.         ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
  1644.                       "cache file \"%s\" is too small", name->data);
  1645.         return NGX_ERROR;
  1646.     }

  1647.     ngx_memzero(&c, sizeof(ngx_http_cache_t));
  1648.     cache = ctx->data;

  1649.     c.length = ctx->size;
  1650.     c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;

  1651.     p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];

  1652.     for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
  1653.         n = ngx_hextoi(p, 2);

  1654.         if (n == NGX_ERROR) {
  1655.             return NGX_ERROR;
  1656.         }

  1657.         p += 2;

  1658.         c.key[i] = (u_char) n;
  1659.     }

  1660.     return ngx_http_file_cache_add(cache, &c);
  1661. }


  1662. static ngx_int_t
  1663. ‌ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  1664. {
  1665.     ngx_http_file_cache_node_t  *fcn;

  1666.     ngx_shmtx_lock(&cache->shpool->mutex);

  1667.     fcn = ngx_http_file_cache_lookup(cache, c->key);

  1668.     if (fcn == NULL) {

  1669.         fcn = ngx_slab_calloc_locked(cache->shpool,
  1670.                                      sizeof(ngx_http_file_cache_node_t));
  1671.         if (fcn == NULL) {
  1672.             ngx_http_file_cache_set_watermark(cache);

  1673.             if (cache->fail_time != ngx_time()) {
  1674.                 cache->fail_time = ngx_time();
  1675.                 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1676.                            "could not allocate node%s", cache->shpool->log_ctx);
  1677.             }

  1678.             ngx_shmtx_unlock(&cache->shpool->mutex);
  1679.             return NGX_ERROR;
  1680.         }

  1681.         cache->sh->count++;

  1682.         ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));

  1683.         ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
  1684.                    NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

  1685.         ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);

  1686.         fcn->uses = 1;
  1687.         fcn->exists = 1;
  1688.         fcn->fs_size = c->fs_size;

  1689.         cache->sh->size += c->fs_size;

  1690.     } else {
  1691.         ngx_queue_remove(&fcn->queue);
  1692.     }

  1693.     fcn->expire = ngx_time() + cache->inactive;

  1694.     ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

  1695.     ngx_shmtx_unlock(&cache->shpool->mutex);

  1696.     return NGX_OK;
  1697. }


  1698. static ngx_int_t
  1699. ‌ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1700. {
  1701.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  1702.                    "http file cache delete: \"%s\"", path->data);

  1703.     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
  1704.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  1705.                       ngx_delete_file_n " \"%s\" failed", path->data);
  1706.     }

  1707.     return NGX_OK;
  1708. }


  1709. static void
  1710. ‌ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache)
  1711. {
  1712.     cache->sh->watermark = cache->sh->count - cache->sh->count / 8;

  1713.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1714.                    "http file cache watermark: %ui", cache->sh->watermark);
  1715. }


  1716. time_t
  1717. ‌ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
  1718. {
  1719.     ngx_uint_t               i;
  1720.     ngx_http_cache_valid_t  *valid;

  1721.     if (cache_valid == NULL) {
  1722.         return 0;
  1723.     }

  1724.     valid = cache_valid->elts;
  1725.     for (i = 0; i < cache_valid->nelts; i++) {

  1726.         if (valid[i].status == 0) {
  1727.             return valid[i].valid;
  1728.         }

  1729.         if (valid[i].status == status) {
  1730.             return valid[i].valid;
  1731.         }
  1732.     }

  1733.     return 0;
  1734. }


  1735. char *
  1736. ‌ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1737. {
  1738.     char  *confp = conf;

  1739.     off_t                   max_size, min_free;
  1740.     u_char                 *last, *p;
  1741.     time_t                  inactive;
  1742.     ssize_t                 size;
  1743.     ngx_str_t               s, name, *value;
  1744.     ngx_int_t               loader_files, manager_files;
  1745.     ngx_msec_t              loader_sleep, manager_sleep, loader_threshold,
  1746.                             manager_threshold;
  1747.     ngx_uint_t              i, n, use_temp_path;
  1748.     ngx_array_t            *caches;
  1749.     ngx_http_file_cache_t  *cache, **ce;

  1750.     cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
  1751.     if (cache == NULL) {
  1752.         return NGX_CONF_ERROR;
  1753.     }

  1754.     cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  1755.     if (cache->path == NULL) {
  1756.         return NGX_CONF_ERROR;
  1757.     }

  1758.     use_temp_path = 1;

  1759.     inactive = 600;

  1760.     loader_files = 100;
  1761.     loader_sleep = 50;
  1762.     loader_threshold = 200;

  1763.     manager_files = 100;
  1764.     manager_sleep = 50;
  1765.     manager_threshold = 200;

  1766.     name.len = 0;
  1767.     size = 0;
  1768.     max_size = NGX_MAX_OFF_T_VALUE;
  1769.     min_free = 0;

  1770.     value = cf->args->elts;

  1771.     cache->path->name = value[1];

  1772.     if (cache->path->name.data[cache->path->name.len - 1] == '/') {
  1773.         cache->path->name.len--;
  1774.     }

  1775.     if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
  1776.         return NGX_CONF_ERROR;
  1777.     }

  1778.     for (i = 2; i < cf->args->nelts; i++) {

  1779.         if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {

  1780.             p = value[i].data + 7;
  1781.             last = value[i].data + value[i].len;

  1782.             for (n = 0; n < NGX_MAX_PATH_LEVEL && p < last; n++) {

  1783.                 if (*p > '0' && *p < '3') {

  1784.                     cache->path->level[n] = *p++ - '0';
  1785.                     cache->path->len += cache->path->level[n] + 1;

  1786.                     if (p == last) {
  1787.                         break;
  1788.                     }

  1789.                     if (*p++ == ':' && n < NGX_MAX_PATH_LEVEL - 1 && p < last) {
  1790.                         continue;
  1791.                     }

  1792.                     goto invalid_levels;
  1793.                 }

  1794.                 goto invalid_levels;
  1795.             }

  1796.             if (cache->path->len < 10 + NGX_MAX_PATH_LEVEL) {
  1797.                 continue;
  1798.             }

  1799.         invalid_levels:

  1800.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1801.                                "invalid \"levels\" \"%V\"", &value[i]);
  1802.             return NGX_CONF_ERROR;
  1803.         }

  1804.         if (ngx_strncmp(value[i].data, "use_temp_path=", 14) == 0) {

  1805.             if (ngx_strcmp(&value[i].data[14], "on") == 0) {
  1806.                 use_temp_path = 1;

  1807.             } else if (ngx_strcmp(&value[i].data[14], "off") == 0) {
  1808.                 use_temp_path = 0;

  1809.             } else {
  1810.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1811.                                    "invalid use_temp_path value \"%V\", "
  1812.                                    "it must be \"on\" or \"off\"",
  1813.                                    &value[i]);
  1814.                 return NGX_CONF_ERROR;
  1815.             }

  1816.             continue;
  1817.         }

  1818.         if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {

  1819.             name.data = value[i].data + 10;

  1820.             p = (u_char *) ngx_strchr(name.data, ':');

  1821.             if (p == NULL) {
  1822.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1823.                                    "invalid keys zone size \"%V\"", &value[i]);
  1824.                 return NGX_CONF_ERROR;
  1825.             }

  1826.             name.len = p - name.data;

  1827.             s.data = p + 1;
  1828.             s.len = value[i].data + value[i].len - s.data;

  1829.             size = ngx_parse_size(&s);

  1830.             if (size == NGX_ERROR) {
  1831.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1832.                                    "invalid keys zone size \"%V\"", &value[i]);
  1833.                 return NGX_CONF_ERROR;
  1834.             }

  1835.             if (size < (ssize_t) (2 * ngx_pagesize)) {
  1836.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1837.                                    "keys zone \"%V\" is too small", &value[i]);
  1838.                 return NGX_CONF_ERROR;
  1839.             }

  1840.             continue;
  1841.         }

  1842.         if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {

  1843.             s.len = value[i].len - 9;
  1844.             s.data = value[i].data + 9;

  1845.             inactive = ngx_parse_time(&s, 1);
  1846.             if (inactive == (time_t) NGX_ERROR) {
  1847.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1848.                                    "invalid inactive value \"%V\"", &value[i]);
  1849.                 return NGX_CONF_ERROR;
  1850.             }

  1851.             continue;
  1852.         }

  1853.         if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {

  1854.             s.len = value[i].len - 9;
  1855.             s.data = value[i].data + 9;

  1856.             max_size = ngx_parse_offset(&s);
  1857.             if (max_size < 0) {
  1858.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1859.                                    "invalid max_size value \"%V\"", &value[i]);
  1860.                 return NGX_CONF_ERROR;
  1861.             }

  1862.             continue;
  1863.         }

  1864.         if (ngx_strncmp(value[i].data, "min_free=", 9) == 0) {

  1865. #if (NGX_WIN32 || NGX_HAVE_STATFS || NGX_HAVE_STATVFS)

  1866.             s.len = value[i].len - 9;
  1867.             s.data = value[i].data + 9;

  1868.             min_free = ngx_parse_offset(&s);
  1869.             if (min_free < 0) {
  1870.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1871.                                    "invalid min_free value \"%V\"", &value[i]);
  1872.                 return NGX_CONF_ERROR;
  1873.             }

  1874. #else
  1875.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1876.                                "min_free is not supported "
  1877.                                "on this platform, ignored");
  1878. #endif

  1879.             continue;
  1880.         }

  1881.         if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {

  1882.             loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
  1883.             if (loader_files == NGX_ERROR) {
  1884.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1885.                            "invalid loader_files value \"%V\"", &value[i]);
  1886.                 return NGX_CONF_ERROR;
  1887.             }

  1888.             continue;
  1889.         }

  1890.         if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {

  1891.             s.len = value[i].len - 13;
  1892.             s.data = value[i].data + 13;

  1893.             loader_sleep = ngx_parse_time(&s, 0);
  1894.             if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
  1895.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1896.                            "invalid loader_sleep value \"%V\"", &value[i]);
  1897.                 return NGX_CONF_ERROR;
  1898.             }

  1899.             continue;
  1900.         }

  1901.         if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {

  1902.             s.len = value[i].len - 17;
  1903.             s.data = value[i].data + 17;

  1904.             loader_threshold = ngx_parse_time(&s, 0);
  1905.             if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
  1906.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1907.                            "invalid loader_threshold value \"%V\"", &value[i]);
  1908.                 return NGX_CONF_ERROR;
  1909.             }

  1910.             continue;
  1911.         }

  1912.         if (ngx_strncmp(value[i].data, "manager_files=", 14) == 0) {

  1913.             manager_files = ngx_atoi(value[i].data + 14, value[i].len - 14);
  1914.             if (manager_files == NGX_ERROR) {
  1915.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1916.                            "invalid manager_files value \"%V\"", &value[i]);
  1917.                 return NGX_CONF_ERROR;
  1918.             }

  1919.             continue;
  1920.         }

  1921.         if (ngx_strncmp(value[i].data, "manager_sleep=", 14) == 0) {

  1922.             s.len = value[i].len - 14;
  1923.             s.data = value[i].data + 14;

  1924.             manager_sleep = ngx_parse_time(&s, 0);
  1925.             if (manager_sleep == (ngx_msec_t) NGX_ERROR) {
  1926.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1927.                            "invalid manager_sleep value \"%V\"", &value[i]);
  1928.                 return NGX_CONF_ERROR;
  1929.             }

  1930.             continue;
  1931.         }

  1932.         if (ngx_strncmp(value[i].data, "manager_threshold=", 18) == 0) {

  1933.             s.len = value[i].len - 18;
  1934.             s.data = value[i].data + 18;

  1935.             manager_threshold = ngx_parse_time(&s, 0);
  1936.             if (manager_threshold == (ngx_msec_t) NGX_ERROR) {
  1937.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1938.                            "invalid manager_threshold value \"%V\"", &value[i]);
  1939.                 return NGX_CONF_ERROR;
  1940.             }

  1941.             continue;
  1942.         }

  1943.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1944.                            "invalid parameter \"%V\"", &value[i]);
  1945.         return NGX_CONF_ERROR;
  1946.     }

  1947.     if (name.len == 0 || size == 0) {
  1948.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1949.                            "\"%V\" must have \"keys_zone\" parameter",
  1950.                            &cmd->name);
  1951.         return NGX_CONF_ERROR;
  1952.     }

  1953.     cache->path->manager = ngx_http_file_cache_manager;
  1954.     cache->path->loader = ngx_http_file_cache_loader;
  1955.     cache->path->data = cache;
  1956.     cache->path->conf_file = cf->conf_file->file.name.data;
  1957.     cache->path->line = cf->conf_file->line;
  1958.     cache->loader_files = loader_files;
  1959.     cache->loader_sleep = loader_sleep;
  1960.     cache->loader_threshold = loader_threshold;
  1961.     cache->manager_files = manager_files;
  1962.     cache->manager_sleep = manager_sleep;
  1963.     cache->manager_threshold = manager_threshold;

  1964.     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
  1965.         return NGX_CONF_ERROR;
  1966.     }

  1967.     cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
  1968.     if (cache->shm_zone == NULL) {
  1969.         return NGX_CONF_ERROR;
  1970.     }

  1971.     if (cache->shm_zone->data) {
  1972.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1973.                            "duplicate zone \"%V\"", &name);
  1974.         return NGX_CONF_ERROR;
  1975.     }


  1976.     cache->shm_zone->init = ngx_http_file_cache_init;
  1977.     cache->shm_zone->data = cache;

  1978.     cache->use_temp_path = use_temp_path;

  1979.     cache->inactive = inactive;
  1980.     cache->max_size = max_size;
  1981.     cache->min_free = min_free;

  1982.     caches = (ngx_array_t *) (confp + cmd->offset);

  1983.     ce = ngx_array_push(caches);
  1984.     if (ce == NULL) {
  1985.         return NGX_CONF_ERROR;
  1986.     }

  1987.     *ce = cache;

  1988.     return NGX_CONF_OK;
  1989. }


  1990. char *
  1991. ‌ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
  1992.     void *conf)
  1993. {
  1994.     char  *p = conf;

  1995.     time_t                    valid;
  1996.     ngx_str_t                *value;
  1997.     ngx_int_t                 status;
  1998.     ngx_uint_t                i, n;
  1999.     ngx_array_t             **a;
  2000.     ngx_http_cache_valid_t   *v;
  2001.     static ngx_uint_t         statuses[] = { 200, 301, 302 };

  2002.     a = (ngx_array_t **) (p + cmd->offset);

  2003.     if (*a == NGX_CONF_UNSET_PTR) {
  2004.         *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
  2005.         if (*a == NULL) {
  2006.             return NGX_CONF_ERROR;
  2007.         }
  2008.     }

  2009.     value = cf->args->elts;
  2010.     n = cf->args->nelts - 1;

  2011.     valid = ngx_parse_time(&value[n], 1);
  2012.     if (valid == (time_t) NGX_ERROR) {
  2013.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2014.                            "invalid time value \"%V\"", &value[n]);
  2015.         return NGX_CONF_ERROR;
  2016.     }

  2017.     if (n == 1) {

  2018.         for (i = 0; i < 3; i++) {
  2019.             v = ngx_array_push(*a);
  2020.             if (v == NULL) {
  2021.                 return NGX_CONF_ERROR;
  2022.             }

  2023.             v->status = statuses[i];
  2024.             v->valid = valid;
  2025.         }

  2026.         return NGX_CONF_OK;
  2027.     }

  2028.     for (i = 1; i < n; i++) {

  2029.         if (ngx_strcmp(value[i].data, "any") == 0) {

  2030.             status = 0;

  2031.         } else {

  2032.             status = ngx_atoi(value[i].data, value[i].len);
  2033.             if (status < 100 || status > 599) {
  2034.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2035.                                    "invalid status \"%V\"", &value[i]);
  2036.                 return NGX_CONF_ERROR;
  2037.             }
  2038.         }

  2039.         v = ngx_array_push(*a);
  2040.         if (v == NULL) {
  2041.             return NGX_CONF_ERROR;
  2042.         }

  2043.         v->status = status;
  2044.         v->valid = valid;
  2045.     }

  2046.     return NGX_CONF_OK;
  2047. }