src/http/ngx_http_file_cache.c - nginx source code

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.     c->waiting = 1;

  332.     if (c->wait_time == 0) {
  333.         c->wait_time = now + c->lock_timeout;

  334.         c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
  335.         c->wait_event.data = r;
  336.         c->wait_event.log = r->connection->log;
  337.     }

  338.     timer = c->wait_time - now;

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

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

  341.     return NGX_AGAIN;
  342. }


  343. static void
  344. ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
  345. {
  346.     ngx_int_t            rc;
  347.     ngx_connection_t    *c;
  348.     ngx_http_request_t  *r;

  349.     r = ev->data;
  350.     c = r->connection;

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

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

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

  355.     if (rc == NGX_AGAIN) {
  356.         return;
  357.     }

  358.     r->cache->waiting = 0;
  359.     r->main->blocked--;

  360.     if (r->main->terminated) {
  361.         /*
  362.          * trigger connection event handler if the request was
  363.          * terminated
  364.          */

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

  366.     } else {
  367.         r->write_event_handler(r);
  368.         ngx_http_run_posted_requests(c);
  369.     }
  370. }


  371. static ngx_int_t
  372. ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
  373. {
  374.     ngx_uint_t              wait;
  375.     ngx_msec_t              now, timer;
  376.     ngx_http_file_cache_t  *cache;

  377.     now = ngx_current_msec;

  378.     timer = c->wait_time - now;

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

  385.     cache = c->file_cache;
  386.     wait = 0;

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

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

  389.     if (c->node->updating && (ngx_msec_int_t) timer > 0) {
  390.         wait = 1;
  391.     }

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

  393.     if (wait) {
  394.         ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
  395.         return NGX_AGAIN;
  396.     }

  397.     return NGX_OK;
  398. }


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

  410.     n = ngx_http_file_cache_aio_read(r, c);

  411.     if (n < 0) {
  412.         return n;
  413.     }

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

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

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

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

  430.     p = c->buf->pos + sizeof(ngx_http_file_cache_header_t)
  431.         + sizeof(ngx_http_file_cache_key);

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

  440.         p += key[i].len;
  441.     }

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

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

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

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

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

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

  472.     r->cached = 1;

  473.     cache = c->file_cache;

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

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

  476.         if (!c->node->exists) {
  477.             c->node->uses = 1;
  478.             c->node->body_start = c->body_start;
  479.             c->node->exists = 1;
  480.             c->node->uniq = c->uniq;
  481.             c->node->fs_size = c->fs_size;

  482.             cache->sh->size += c->fs_size;
  483.         }

  484.         ngx_shmtx_unlock(&cache->shpool->mutex);
  485.     }

  486.     now = ngx_time();

  487.     if (c->valid_sec < now) {
  488.         c->stale_updating = c->valid_sec + c->updating_sec >= now;
  489.         c->stale_error = c->valid_sec + c->error_sec >= now;

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

  491.         if (c->node->updating) {
  492.             rc = NGX_HTTP_CACHE_UPDATING;

  493.         } else {
  494.             c->node->updating = 1;
  495.             c->updating = 1;
  496.             c->lock_time = c->node->lock_time;
  497.             rc = NGX_HTTP_CACHE_STALE;
  498.         }

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

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

  503.         return rc;
  504.     }

  505.     return NGX_OK;
  506. }


  507. static ssize_t
  508. ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  509. {
  510. #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
  511.     ssize_t                    n;
  512.     ngx_http_core_loc_conf_t  *clcf;

  513.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  514. #endif

  515. #if (NGX_HAVE_FILE_AIO)

  516.     if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio) {
  517.         n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);

  518.         if (n != NGX_AGAIN) {
  519.             c->reading = 0;
  520.             return n;
  521.         }

  522.         c->reading = 1;

  523.         c->file.aio->data = r;
  524.         c->file.aio->handler = ngx_http_cache_aio_event_handler;

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

  526.         r->main->blocked++;
  527.         r->aio = 1;

  528.         return NGX_AGAIN;
  529.     }

  530. #endif

  531. #if (NGX_THREADS)

  532.     if (clcf->aio == NGX_HTTP_AIO_THREADS) {
  533.         c->file.thread_task = c->thread_task;
  534.         c->file.thread_handler = ngx_http_cache_thread_handler;
  535.         c->file.thread_ctx = r;

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

  537.         c->thread_task = c->file.thread_task;
  538.         c->reading = (n == NGX_AGAIN);

  539.         return n;
  540.     }

  541. #endif

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


  544. #if (NGX_HAVE_FILE_AIO)

  545. static void
  546. ngx_http_cache_aio_event_handler(ngx_event_t *ev)
  547. {
  548.     ngx_event_aio_t     *aio;
  549.     ngx_connection_t    *c;
  550.     ngx_http_request_t  *r;

  551.     aio = ev->data;
  552.     r = aio->data;
  553.     c = r->connection;

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

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

  557.     if (ev->timedout) {
  558.         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  559.                       "aio operation took too long");
  560.         ev->timedout = 0;
  561.         return;
  562.     }

  563.     if (ev->timer_set) {
  564.         ngx_del_timer(ev);
  565.     }

  566.     r->main->blocked--;
  567.     r->aio = 0;

  568.     if (r->main->terminated) {
  569.         /*
  570.          * trigger connection event handler if the request was
  571.          * terminated
  572.          */

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

  574.     } else {
  575.         r->write_event_handler(r);
  576.         ngx_http_run_posted_requests(c);
  577.     }
  578. }

  579. #endif


  580. #if (NGX_THREADS)

  581. static ngx_int_t
  582. ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
  583. {
  584.     ngx_str_t                  name;
  585.     ngx_thread_pool_t         *tp;
  586.     ngx_http_request_t        *r;
  587.     ngx_http_core_loc_conf_t  *clcf;

  588.     r = file->thread_ctx;

  589.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  590.     tp = clcf->thread_pool;

  591.     if (tp == NULL) {
  592.         if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
  593.             != NGX_OK)
  594.         {
  595.             return NGX_ERROR;
  596.         }

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

  598.         if (tp == NULL) {
  599.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  600.                           "thread pool \"%V\" not found", &name);
  601.             return NGX_ERROR;
  602.         }
  603.     }

  604.     task->event.data = r;
  605.     task->event.handler = ngx_http_cache_thread_event_handler;

  606.     if (ngx_thread_task_post(tp, task) != NGX_OK) {
  607.         return NGX_ERROR;
  608.     }

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

  610.     r->main->blocked++;
  611.     r->aio = 1;

  612.     return NGX_OK;
  613. }


  614. static void
  615. ngx_http_cache_thread_event_handler(ngx_event_t *ev)
  616. {
  617.     ngx_connection_t    *c;
  618.     ngx_http_request_t  *r;

  619.     r = ev->data;
  620.     c = r->connection;

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

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

  624.     if (ev->timedout) {
  625.         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  626.                       "thread operation took too long");
  627.         ev->timedout = 0;
  628.         return;
  629.     }

  630.     if (ev->timer_set) {
  631.         ngx_del_timer(ev);
  632.     }

  633.     r->main->blocked--;
  634.     r->aio = 0;

  635.     if (r->main->terminated) {
  636.         /*
  637.          * trigger connection event handler if the request was
  638.          * terminated
  639.          */

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

  641.     } else {
  642.         r->write_event_handler(r);
  643.         ngx_http_run_posted_requests(c);
  644.     }
  645. }

  646. #endif


  647. static ngx_int_t
  648. ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  649. {
  650.     ngx_int_t                    rc;
  651.     ngx_http_file_cache_node_t  *fcn;

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

  653.     fcn = c->node;

  654.     if (fcn == NULL) {
  655.         fcn = ngx_http_file_cache_lookup(cache, c->key);
  656.     }

  657.     if (fcn) {
  658.         ngx_queue_remove(&fcn->queue);

  659.         if (c->node == NULL) {
  660.             fcn->uses++;
  661.             fcn->count++;
  662.         }

  663.         if (fcn->error) {

  664.             if (fcn->valid_sec < ngx_time()) {
  665.                 goto renew;
  666.             }

  667.             rc = NGX_OK;

  668.             goto done;
  669.         }

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

  671.             c->exists = fcn->exists;
  672.             if (fcn->body_start && !c->update_variant) {
  673.                 c->body_start = fcn->body_start;
  674.             }

  675.             rc = NGX_OK;

  676.             goto done;
  677.         }

  678.         rc = NGX_AGAIN;

  679.         goto done;
  680.     }

  681.     fcn = ngx_slab_calloc_locked(cache->shpool,
  682.                                  sizeof(ngx_http_file_cache_node_t));
  683.     if (fcn == NULL) {
  684.         ngx_http_file_cache_set_watermark(cache);

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

  686.         (void) ngx_http_file_cache_forced_expire(cache);

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

  688.         fcn = ngx_slab_calloc_locked(cache->shpool,
  689.                                      sizeof(ngx_http_file_cache_node_t));
  690.         if (fcn == NULL) {
  691.             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  692.                           "could not allocate node%s", cache->shpool->log_ctx);
  693.             rc = NGX_ERROR;
  694.             goto failed;
  695.         }
  696.     }

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

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

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

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

  702.     fcn->uses = 1;
  703.     fcn->count = 1;

  704. renew:

  705.     rc = NGX_DECLINED;

  706.     fcn->valid_msec = 0;
  707.     fcn->error = 0;
  708.     fcn->exists = 0;
  709.     fcn->valid_sec = 0;
  710.     fcn->uniq = 0;
  711.     fcn->body_start = 0;
  712.     fcn->fs_size = 0;

  713. done:

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

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

  716.     c->uniq = fcn->uniq;
  717.     c->error = fcn->error;
  718.     c->node = fcn;

  719. failed:

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

  721.     return rc;
  722. }


  723. static ngx_int_t
  724. ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
  725. {
  726.     u_char            *p;
  727.     ngx_http_cache_t  *c;

  728.     c = r->cache;

  729.     if (c->file.name.len) {
  730.         return NGX_OK;
  731.     }

  732.     c->file.name.len = path->name.len + 1 + path->len
  733.                        + 2 * NGX_HTTP_CACHE_KEY_LEN;

  734.     c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
  735.     if (c->file.name.data == NULL) {
  736.         return NGX_ERROR;
  737.     }

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

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

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

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

  745.     return NGX_OK;
  746. }


  747. static ngx_http_file_cache_node_t *
  748. ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
  749. {
  750.     ngx_int_t                    rc;
  751.     ngx_rbtree_key_t             node_key;
  752.     ngx_rbtree_node_t           *node, *sentinel;
  753.     ngx_http_file_cache_node_t  *fcn;

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

  755.     node = cache->sh->rbtree.root;
  756.     sentinel = cache->sh->rbtree.sentinel;

  757.     while (node != sentinel) {

  758.         if (node_key < node->key) {
  759.             node = node->left;
  760.             continue;
  761.         }

  762.         if (node_key > node->key) {
  763.             node = node->right;
  764.             continue;
  765.         }

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

  767.         fcn = (ngx_http_file_cache_node_t *) node;

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

  770.         if (rc == 0) {
  771.             return fcn;
  772.         }

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

  775.     /* not found */

  776.     return NULL;
  777. }


  778. static void
  779. ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  780.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  781. {
  782.     ngx_rbtree_node_t           **p;
  783.     ngx_http_file_cache_node_t   *cn, *cnt;

  784.     for ( ;; ) {

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

  786.             p = &temp->left;

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

  788.             p = &temp->right;

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

  790.             cn = (ngx_http_file_cache_node_t *) node;
  791.             cnt = (ngx_http_file_cache_node_t *) temp;

  792.             p = (ngx_memcmp(cn->key, cnt->key,
  793.                             NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
  794.                  < 0)
  795.                     ? &temp->left : &temp->right;
  796.         }

  797.         if (*p == sentinel) {
  798.             break;
  799.         }

  800.         temp = *p;
  801.     }

  802.     *p = node;
  803.     node->parent = temp;
  804.     node->left = sentinel;
  805.     node->right = sentinel;
  806.     ngx_rbt_red(node);
  807. }


  808. static void
  809. ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,
  810.     u_char *hash)
  811. {
  812.     u_char     *p, *last;
  813.     ngx_str_t   name;
  814.     ngx_md5_t   md5;
  815.     u_char      buf[NGX_HTTP_CACHE_VARY_LEN];

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

  818.     ngx_md5_init(&md5);
  819.     ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);

  820.     ngx_strlow(buf, vary, len);

  821.     p = buf;
  822.     last = buf + len;

  823.     while (p < last) {

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

  825.         name.data = p;

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

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

  828.         if (name.len == 0) {
  829.             break;
  830.         }

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

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

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

  836.         ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);
  837.     }

  838.     ngx_md5_final(hash, &md5);
  839. }


  840. static void
  841. ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,
  842.     ngx_str_t *name)
  843. {
  844.     size_t            len;
  845.     u_char           *p, *start, *last;
  846.     ngx_uint_t        i, multiple, normalize;
  847.     ngx_list_part_t  *part;
  848.     ngx_table_elt_t  *header;

  849.     multiple = 0;
  850.     normalize = 0;

  851.     if (name->len == sizeof("Accept-Charset") - 1
  852.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset",
  853.                            sizeof("Accept-Charset") - 1) == 0)
  854.     {
  855.         normalize = 1;

  856.     } else if (name->len == sizeof("Accept-Encoding") - 1
  857.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding",
  858.                            sizeof("Accept-Encoding") - 1) == 0)
  859.     {
  860.         normalize = 1;

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

  867.     part = &r->headers_in.headers.part;
  868.     header = part->elts;

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

  870.         if (i >= part->nelts) {
  871.             if (part->next == NULL) {
  872.                 break;
  873.             }

  874.             part = part->next;
  875.             header = part->elts;
  876.             i = 0;
  877.         }

  878.         if (header[i].hash == 0) {
  879.             continue;
  880.         }

  881.         if (header[i].key.len != name->len) {
  882.             continue;
  883.         }

  884.         if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {
  885.             continue;
  886.         }

  887.         if (!normalize) {

  888.             if (multiple) {
  889.                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  890.             }

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

  892.             multiple = 1;

  893.             continue;
  894.         }

  895.         /* normalize spaces */

  896.         p = header[i].value.data;
  897.         last = p + header[i].value.len;

  898.         while (p < last) {

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

  900.             start = p;

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

  902.             len = p - start;

  903.             if (len == 0) {
  904.                 break;
  905.             }

  906.             if (multiple) {
  907.                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  908.             }

  909.             ngx_md5_update(md5, start, len);

  910.             multiple = 1;
  911.         }
  912.     }
  913. }


  914. static ngx_int_t
  915. ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)
  916. {
  917.     ngx_http_file_cache_t  *cache;

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

  920.     if (c->secondary) {
  921.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  922.                       "cache file \"%s\" has incorrect vary hash",
  923.                       c->file.name.data);
  924.         return NGX_DECLINED;
  925.     }

  926.     cache = c->file_cache;

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

  928.     c->node->count--;
  929.     c->node = NULL;

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

  931.     c->secondary = 1;
  932.     c->file.name.len = 0;
  933.     c->body_start = c->buffer_size;

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

  935.     return ngx_http_file_cache_open(r);
  936. }


  937. ngx_int_t
  938. ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
  939. {
  940.     ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;

  941.     u_char            *p;
  942.     ngx_str_t         *key;
  943.     ngx_uint_t         i;
  944.     ngx_http_cache_t  *c;

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

  947.     c = r->cache;

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

  949.     h->version = NGX_HTTP_CACHE_VERSION;
  950.     h->valid_sec = c->valid_sec;
  951.     h->updating_sec = c->updating_sec;
  952.     h->error_sec = c->error_sec;
  953.     h->last_modified = c->last_modified;
  954.     h->date = c->date;
  955.     h->crc32 = c->crc32;
  956.     h->valid_msec = (u_short) c->valid_msec;
  957.     h->header_start = (u_short) c->header_start;
  958.     h->body_start = (u_short) c->body_start;

  959.     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  960.         h->etag_len = (u_char) c->etag.len;
  961.         ngx_memcpy(h->etag, c->etag.data, c->etag.len);
  962.     }

  963.     if (c->vary.len) {
  964.         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  965.             /* should not happen */
  966.             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  967.         }

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

  970.         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  971.         ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  972.     }

  973.     if (ngx_http_file_cache_update_variant(r, c) != NGX_OK) {
  974.         return NGX_ERROR;
  975.     }

  976.     p = buf + sizeof(ngx_http_file_cache_header_t);

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

  978.     key = c->keys.elts;
  979.     for (i = 0; i < c->keys.nelts; i++) {
  980.         p = ngx_copy(p, key[i].data, key[i].len);
  981.     }

  982.     *p = LF;

  983.     return NGX_OK;
  984. }


  985. static ngx_int_t
  986. ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)
  987. {
  988.     ngx_http_file_cache_t  *cache;

  989.     if (!c->secondary) {
  990.         return NGX_OK;
  991.     }

  992.     if (c->vary.len
  993.         && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) == 0)
  994.     {
  995.         return NGX_OK;
  996.     }

  997.     /*
  998.      * if the variant hash doesn't match one we used as a secondary
  999.      * cache key, switch back to the original key
  1000.      */

  1001.     cache = c->file_cache;

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

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

  1005.     c->node->count--;
  1006.     c->node->updating = 0;
  1007.     c->node = NULL;

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

  1009.     c->file.name.len = 0;
  1010.     c->update_variant = 1;

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

  1012.     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  1013.         return NGX_ERROR;
  1014.     }

  1015.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  1016.         return NGX_ERROR;
  1017.     }

  1018.     return NGX_OK;
  1019. }


  1020. void
  1021. ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
  1022. {
  1023.     off_t                   fs_size;
  1024.     ngx_int_t               rc;
  1025.     ngx_file_uniq_t         uniq;
  1026.     ngx_file_info_t         fi;
  1027.     ngx_http_cache_t        *c;
  1028.     ngx_ext_rename_file_t   ext;
  1029.     ngx_http_file_cache_t  *cache;

  1030.     c = r->cache;

  1031.     if (c->updated) {
  1032.         return;
  1033.     }

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

  1036.     cache = c->file_cache;

  1037.     c->updated = 1;
  1038.     c->updating = 0;

  1039.     uniq = 0;
  1040.     fs_size = 0;

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

  1044.     ext.access = NGX_FILE_OWNER_ACCESS;
  1045.     ext.path_access = NGX_FILE_OWNER_ACCESS;
  1046.     ext.time = -1;
  1047.     ext.create_path = 1;
  1048.     ext.delete_file = 1;
  1049.     ext.log = r->connection->log;

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

  1051.     if (rc == NGX_OK) {

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

  1055.             rc = NGX_ERROR;

  1056.         } else {
  1057.             uniq = ngx_file_uniq(&fi);
  1058.             fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
  1059.         }
  1060.     }

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

  1062.     c->node->count--;
  1063.     c->node->error = 0;
  1064.     c->node->uniq = uniq;
  1065.     c->node->body_start = c->body_start;

  1066.     cache->sh->size += fs_size - c->node->fs_size;
  1067.     c->node->fs_size = fs_size;

  1068.     if (rc == NGX_OK) {
  1069.         c->node->exists = 1;
  1070.     }

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

  1072.     ngx_shmtx_unlock(&cache->shpool->mutex);
  1073. }


  1074. void
  1075. ngx_http_file_cache_update_header(ngx_http_request_t *r)
  1076. {
  1077.     ssize_t                        n;
  1078.     ngx_err_t                      err;
  1079.     ngx_file_t                     file;
  1080.     ngx_file_info_t                fi;
  1081.     ngx_http_cache_t              *c;
  1082.     ngx_http_file_cache_header_t   h;

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

  1085.     c = r->cache;

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

  1087.     file.name = c->file.name;
  1088.     file.log = r->connection->log;
  1089.     file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);

  1090.     if (file.fd == NGX_INVALID_FILE) {
  1091.         err = ngx_errno;

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

  1093.         if (err == NGX_ENOENT) {
  1094.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1095.                            "http file cache \"%s\" not found",
  1096.                            file.name.data);
  1097.             return;
  1098.         }

  1099.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
  1100.                       ngx_open_file_n " \"%s\" failed", file.name.data);
  1101.         return;
  1102.     }

  1103.     /*
  1104.      * make sure cache file wasn't replaced;
  1105.      * if it was, do nothing
  1106.      */

  1107.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  1108.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  1109.                       ngx_fd_info_n " \"%s\" failed", file.name.data);
  1110.         goto done;
  1111.     }

  1112.     if (c->uniq != ngx_file_uniq(&fi)
  1113.         || c->length != ngx_file_size(&fi))
  1114.     {
  1115.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1116.                        "http file cache \"%s\" changed",
  1117.                        file.name.data);
  1118.         goto done;
  1119.     }

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

  1122.     if (n == NGX_ERROR) {
  1123.         goto done;
  1124.     }

  1125.     if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {
  1126.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  1127.                       ngx_read_file_n " read only %z of %z from \"%s\"",
  1128.                       n, sizeof(ngx_http_file_cache_header_t), file.name.data);
  1129.         goto done;
  1130.     }

  1131.     if (h.version != NGX_HTTP_CACHE_VERSION
  1132.         || h.last_modified != c->last_modified
  1133.         || h.crc32 != c->crc32
  1134.         || (size_t) h.header_start != c->header_start
  1135.         || (size_t) h.body_start != c->body_start)
  1136.     {
  1137.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1138.                        "http file cache \"%s\" content changed",
  1139.                        file.name.data);
  1140.         goto done;
  1141.     }

  1142.     /*
  1143.      * update cache file header with new data,
  1144.      * notably h.valid_sec and h.date
  1145.      */

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

  1147.     h.version = NGX_HTTP_CACHE_VERSION;
  1148.     h.valid_sec = c->valid_sec;
  1149.     h.updating_sec = c->updating_sec;
  1150.     h.error_sec = c->error_sec;
  1151.     h.last_modified = c->last_modified;
  1152.     h.date = c->date;
  1153.     h.crc32 = c->crc32;
  1154.     h.valid_msec = (u_short) c->valid_msec;
  1155.     h.header_start = (u_short) c->header_start;
  1156.     h.body_start = (u_short) c->body_start;

  1157.     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  1158.         h.etag_len = (u_char) c->etag.len;
  1159.         ngx_memcpy(h.etag, c->etag.data, c->etag.len);
  1160.     }

  1161.     if (c->vary.len) {
  1162.         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  1163.             /* should not happen */
  1164.             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  1165.         }

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

  1168.         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  1169.         ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  1170.     }

  1171.     (void) ngx_write_file(&file, (u_char *) &h,
  1172.                           sizeof(ngx_http_file_cache_header_t), 0);

  1173. done:

  1174.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1175.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
  1176.                       ngx_close_file_n " \"%s\" failed", file.name.data);
  1177.     }
  1178. }


  1179. ngx_int_t
  1180. ngx_http_cache_send(ngx_http_request_t *r)
  1181. {
  1182.     ngx_int_t          rc;
  1183.     ngx_buf_t         *b;
  1184.     ngx_chain_t        out;
  1185.     ngx_http_cache_t  *c;

  1186.     c = r->cache;

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

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

  1190.     b = ngx_calloc_buf(r->pool);
  1191.     if (b == NULL) {
  1192.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1193.     }

  1194.     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
  1195.     if (b->file == NULL) {
  1196.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1197.     }

  1198.     rc = ngx_http_send_header(r);

  1199.     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
  1200.         return rc;
  1201.     }

  1202.     b->file_pos = c->body_start;
  1203.     b->file_last = c->length;

  1204.     b->in_file = (c->length - c->body_start) ? 1 : 0;
  1205.     b->last_buf = (r == r->main) ? 1 : 0;
  1206.     b->last_in_chain = 1;
  1207.     b->sync = (b->last_buf || b->in_file) ? 0 : 1;

  1208.     b->file->fd = c->file.fd;
  1209.     b->file->name = c->file.name;
  1210.     b->file->log = r->connection->log;

  1211.     out.buf = b;
  1212.     out.next = NULL;

  1213.     return ngx_http_output_filter(r, &out);
  1214. }


  1215. void
  1216. ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
  1217. {
  1218.     ngx_http_file_cache_t       *cache;
  1219.     ngx_http_file_cache_node_t  *fcn;

  1220.     if (c->updated || c->node == NULL) {
  1221.         return;
  1222.     }

  1223.     cache = c->file_cache;

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

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

  1227.     fcn = c->node;
  1228.     fcn->count--;

  1229.     if (c->updating && fcn->lock_time == c->lock_time) {
  1230.         fcn->updating = 0;
  1231.     }

  1232.     if (c->error) {
  1233.         fcn->error = c->error;

  1234.         if (c->valid_sec) {
  1235.             fcn->valid_sec = c->valid_sec;
  1236.             fcn->valid_msec = c->valid_msec;
  1237.         }

  1238.     } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
  1239.         ngx_queue_remove(&fcn->queue);
  1240.         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1241.         ngx_slab_free_locked(cache->shpool, fcn);
  1242.         cache->sh->count--;
  1243.         c->node = NULL;
  1244.     }

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

  1246.     c->updated = 1;
  1247.     c->updating = 0;

  1248.     if (c->temp_file) {
  1249.         if (tf && tf->file.fd != NGX_INVALID_FILE) {
  1250.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1251.                            "http file cache incomplete: \"%s\"",
  1252.                            tf->file.name.data);

  1253.             if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
  1254.                 ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
  1255.                               ngx_delete_file_n " \"%s\" failed",
  1256.                               tf->file.name.data);
  1257.             }
  1258.         }
  1259.     }

  1260.     if (c->wait_event.timer_set) {
  1261.         ngx_del_timer(&c->wait_event);
  1262.     }
  1263. }


  1264. static void
  1265. ngx_http_file_cache_cleanup(void *data)
  1266. {
  1267.     ngx_http_cache_t  *c = data;

  1268.     if (c->updated) {
  1269.         return;
  1270.     }

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

  1273.     if (c->updating && !c->background) {
  1274.         ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
  1275.                       "stalled cache updating, error:%ui", c->error);
  1276.     }

  1277.     ngx_http_file_cache_free(c, NULL);
  1278. }


  1279. static time_t
  1280. ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
  1281. {
  1282.     u_char                      *name, *p;
  1283.     size_t                       len;
  1284.     time_t                       wait;
  1285.     ngx_uint_t                   tries;
  1286.     ngx_path_t                  *path;
  1287.     ngx_queue_t                 *q, *sentinel;
  1288.     ngx_http_file_cache_node_t  *fcn;
  1289.     u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];

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

  1292.     path = cache->path;
  1293.     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

  1294.     name = ngx_alloc(len + 1, ngx_cycle->log);
  1295.     if (name == NULL) {
  1296.         return 10;
  1297.     }

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

  1299.     wait = 10;
  1300.     tries = 20;
  1301.     sentinel = NULL;

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

  1303.     for ( ;; ) {
  1304.         if (ngx_queue_empty(&cache->sh->queue)) {
  1305.             break;
  1306.         }

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

  1308.         if (q == sentinel) {
  1309.             break;
  1310.         }

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

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

  1316.         if (fcn->count == 0) {
  1317.             ngx_http_file_cache_delete(cache, q, name);
  1318.             wait = 0;
  1319.             break;
  1320.         }

  1321.         if (fcn->deleting) {
  1322.             wait = 1;
  1323.             break;
  1324.         }

  1325.         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1326.                          sizeof(ngx_rbtree_key_t));
  1327.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1328.         (void) ngx_hex_dump(p, fcn->key, len);

  1329.         /*
  1330.          * abnormally exited workers may leave locked cache entries,
  1331.          * and although it may be safe to remove them completely,
  1332.          * we prefer to just move them to the top of the inactive queue
  1333.          */

  1334.         ngx_queue_remove(q);
  1335.         fcn->expire = ngx_time() + cache->inactive;
  1336.         ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

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

  1340.         if (sentinel == NULL) {
  1341.             sentinel = q;
  1342.         }

  1343.         if (--tries) {
  1344.             continue;
  1345.         }

  1346.         wait = 1;
  1347.         break;
  1348.     }

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

  1350.     ngx_free(name);

  1351.     return wait;
  1352. }


  1353. static time_t
  1354. ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
  1355. {
  1356.     u_char                      *name, *p;
  1357.     size_t                       len;
  1358.     time_t                       now, wait;
  1359.     ngx_path_t                  *path;
  1360.     ngx_msec_t                   elapsed;
  1361.     ngx_queue_t                 *q;
  1362.     ngx_http_file_cache_node_t  *fcn;
  1363.     u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];

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

  1366.     path = cache->path;
  1367.     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

  1368.     name = ngx_alloc(len + 1, ngx_cycle->log);
  1369.     if (name == NULL) {
  1370.         return 10;
  1371.     }

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

  1373.     now = ngx_time();

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

  1375.     for ( ;; ) {

  1376.         if (ngx_quit || ngx_terminate) {
  1377.             wait = 1;
  1378.             break;
  1379.         }

  1380.         if (ngx_queue_empty(&cache->sh->queue)) {
  1381.             wait = 10;
  1382.             break;
  1383.         }

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

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

  1386.         wait = fcn->expire - now;

  1387.         if (wait > 0) {
  1388.             wait = wait > 10 ? 10 : wait;
  1389.             break;
  1390.         }

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

  1395.         if (fcn->count == 0) {
  1396.             ngx_http_file_cache_delete(cache, q, name);
  1397.             goto next;
  1398.         }

  1399.         if (fcn->deleting) {
  1400.             wait = 1;
  1401.             break;
  1402.         }

  1403.         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1404.                          sizeof(ngx_rbtree_key_t));
  1405.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1406.         (void) ngx_hex_dump(p, fcn->key, len);

  1407.         /*
  1408.          * abnormally exited workers may leave locked cache entries,
  1409.          * and although it may be safe to remove them completely,
  1410.          * we prefer to just move them to the top of the inactive queue
  1411.          */

  1412.         ngx_queue_remove(q);
  1413.         fcn->expire = ngx_time() + cache->inactive;
  1414.         ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

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

  1418. next:

  1419.         if (++cache->files >= cache->manager_files) {
  1420.             wait = 0;
  1421.             break;
  1422.         }

  1423.         ngx_time_update();

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

  1425.         if (elapsed >= cache->manager_threshold) {
  1426.             wait = 0;
  1427.             break;
  1428.         }
  1429.     }

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

  1431.     ngx_free(name);

  1432.     return wait;
  1433. }


  1434. static void
  1435. ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
  1436.     u_char *name)
  1437. {
  1438.     u_char                      *p;
  1439.     size_t                       len;
  1440.     ngx_path_t                  *path;
  1441.     ngx_http_file_cache_node_t  *fcn;

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

  1443.     if (fcn->exists) {
  1444.         cache->sh->size -= fcn->fs_size;

  1445.         path = cache->path;
  1446.         p = name + path->name.len + 1 + path->len;
  1447.         p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
  1448.                          sizeof(ngx_rbtree_key_t));
  1449.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1450.         p = ngx_hex_dump(p, fcn->key, len);
  1451.         *p = '\0';

  1452.         fcn->count++;
  1453.         fcn->deleting = 1;
  1454.         ngx_shmtx_unlock(&cache->shpool->mutex);

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

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

  1459.         if (ngx_delete_file(name) == NGX_FILE_ERROR) {
  1460.             ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
  1461.                           ngx_delete_file_n " \"%s\" failed", name);
  1462.         }

  1463.         ngx_shmtx_lock(&cache->shpool->mutex);
  1464.         fcn->count--;
  1465.         fcn->deleting = 0;
  1466.     }

  1467.     if (fcn->count == 0) {
  1468.         ngx_queue_remove(q);
  1469.         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1470.         ngx_slab_free_locked(cache->shpool, fcn);
  1471.         cache->sh->count--;
  1472.     }
  1473. }


  1474. static ngx_msec_t
  1475. ngx_http_file_cache_manager(void *data)
  1476. {
  1477.     ngx_http_file_cache_t  *cache = data;

  1478.     off_t       size, free;
  1479.     time_t      wait;
  1480.     ngx_msec_t  elapsed, next;
  1481.     ngx_uint_t  count, watermark;

  1482.     cache->last = ngx_current_msec;
  1483.     cache->files = 0;

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

  1485.     if (next == 0) {
  1486.         next = cache->manager_sleep;
  1487.         goto done;
  1488.     }

  1489.     for ( ;; ) {
  1490.         ngx_shmtx_lock(&cache->shpool->mutex);

  1491.         size = cache->sh->size;
  1492.         count = cache->sh->count;
  1493.         watermark = cache->sh->watermark;

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

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

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

  1499.             if (!cache->min_free) {
  1500.                 break;
  1501.             }

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

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

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

  1509.         wait = ngx_http_file_cache_forced_expire(cache);

  1510.         if (wait > 0) {
  1511.             next = (ngx_msec_t) wait * 1000;
  1512.             break;
  1513.         }

  1514.         if (ngx_quit || ngx_terminate) {
  1515.             break;
  1516.         }

  1517.         if (++cache->files >= cache->manager_files) {
  1518.             next = cache->manager_sleep;
  1519.             break;
  1520.         }

  1521.         ngx_time_update();

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

  1523.         if (elapsed >= cache->manager_threshold) {
  1524.             next = cache->manager_sleep;
  1525.             break;
  1526.         }
  1527.     }

  1528. done:

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

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

  1533.     return next;
  1534. }


  1535. static void
  1536. ngx_http_file_cache_loader(void *data)
  1537. {
  1538.     ngx_http_file_cache_t  *cache = data;

  1539.     ngx_tree_ctx_t  tree;

  1540.     if (!cache->sh->cold || cache->sh->loading) {
  1541.         return;
  1542.     }

  1543.     if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
  1544.         return;
  1545.     }

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

  1548.     tree.init_handler = NULL;
  1549.     tree.file_handler = ngx_http_file_cache_manage_file;
  1550.     tree.pre_tree_handler = ngx_http_file_cache_manage_directory;
  1551.     tree.post_tree_handler = ngx_http_file_cache_noop;
  1552.     tree.spec_handler = ngx_http_file_cache_delete_file;
  1553.     tree.data = cache;
  1554.     tree.alloc = 0;
  1555.     tree.log = ngx_cycle->log;

  1556.     cache->last = ngx_current_msec;
  1557.     cache->files = 0;

  1558.     if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
  1559.         cache->sh->loading = 0;
  1560.         return;
  1561.     }

  1562.     cache->sh->cold = 0;
  1563.     cache->sh->loading = 0;

  1564.     ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
  1565.                   "http file cache: %V %.3fM, bsize: %uz",
  1566.                   &cache->path->name,
  1567.                   ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
  1568.                   cache->bsize);
  1569. }


  1570. static ngx_int_t
  1571. ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1572. {
  1573.     return NGX_OK;
  1574. }


  1575. static ngx_int_t
  1576. ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1577. {
  1578.     ngx_msec_t              elapsed;
  1579.     ngx_http_file_cache_t  *cache;

  1580.     cache = ctx->data;

  1581.     if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
  1582.         (void) ngx_http_file_cache_delete_file(ctx, path);
  1583.     }

  1584.     if (++cache->files >= cache->loader_files) {
  1585.         ngx_http_file_cache_loader_sleep(cache);

  1586.     } else {
  1587.         ngx_time_update();

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

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

  1591.         if (elapsed >= cache->loader_threshold) {
  1592.             ngx_http_file_cache_loader_sleep(cache);
  1593.         }
  1594.     }

  1595.     return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
  1596. }


  1597. static ngx_int_t
  1598. ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1599. {
  1600.     if (path->len >= 5
  1601.         && ngx_strncmp(path->data + path->len - 5, "/temp", 5) == 0)
  1602.     {
  1603.         return NGX_DECLINED;
  1604.     }

  1605.     return NGX_OK;
  1606. }


  1607. static void
  1608. ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
  1609. {
  1610.     ngx_msleep(cache->loader_sleep);

  1611.     ngx_time_update();

  1612.     cache->last = ngx_current_msec;
  1613.     cache->files = 0;
  1614. }


  1615. static ngx_int_t
  1616. ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
  1617. {
  1618.     u_char                 *p;
  1619.     ngx_int_t               n;
  1620.     ngx_uint_t              i;
  1621.     ngx_http_cache_t        c;
  1622.     ngx_http_file_cache_t  *cache;

  1623.     if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
  1624.         return NGX_ERROR;
  1625.     }

  1626.     /*
  1627.      * Temporary files in cache have a suffix consisting of a dot
  1628.      * followed by 10 digits.
  1629.      */

  1630.     if (name->len >= 2 * NGX_HTTP_CACHE_KEY_LEN + 1 + 10
  1631.         && name->data[name->len - 10 - 1] == '.')
  1632.     {
  1633.         return NGX_OK;
  1634.     }

  1635.     if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
  1636.         ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
  1637.                       "cache file \"%s\" is too small", name->data);
  1638.         return NGX_ERROR;
  1639.     }

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

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

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

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

  1647.         if (n == NGX_ERROR) {
  1648.             return NGX_ERROR;
  1649.         }

  1650.         p += 2;

  1651.         c.key[i] = (u_char) n;
  1652.     }

  1653.     return ngx_http_file_cache_add(cache, &c);
  1654. }


  1655. static ngx_int_t
  1656. ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  1657. {
  1658.     ngx_http_file_cache_node_t  *fcn;

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

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

  1661.     if (fcn == NULL) {

  1662.         fcn = ngx_slab_calloc_locked(cache->shpool,
  1663.                                      sizeof(ngx_http_file_cache_node_t));
  1664.         if (fcn == NULL) {
  1665.             ngx_http_file_cache_set_watermark(cache);

  1666.             if (cache->fail_time != ngx_time()) {
  1667.                 cache->fail_time = ngx_time();
  1668.                 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1669.                            "could not allocate node%s", cache->shpool->log_ctx);
  1670.             }

  1671.             ngx_shmtx_unlock(&cache->shpool->mutex);
  1672.             return NGX_ERROR;
  1673.         }

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

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

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

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

  1679.         fcn->uses = 1;
  1680.         fcn->exists = 1;
  1681.         fcn->fs_size = c->fs_size;

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

  1683.     } else {
  1684.         ngx_queue_remove(&fcn->queue);
  1685.     }

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

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

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

  1689.     return NGX_OK;
  1690. }


  1691. static ngx_int_t
  1692. ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1693. {
  1694.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  1695.                    "http file cache delete: \"%s\"", path->data);

  1696.     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
  1697.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  1698.                       ngx_delete_file_n " \"%s\" failed", path->data);
  1699.     }

  1700.     return NGX_OK;
  1701. }


  1702. static void
  1703. ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache)
  1704. {
  1705.     cache->sh->watermark = cache->sh->count - cache->sh->count / 8;

  1706.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1707.                    "http file cache watermark: %ui", cache->sh->watermark);
  1708. }


  1709. time_t
  1710. ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
  1711. {
  1712.     ngx_uint_t               i;
  1713.     ngx_http_cache_valid_t  *valid;

  1714.     if (cache_valid == NULL) {
  1715.         return 0;
  1716.     }

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

  1719.         if (valid[i].status == 0) {
  1720.             return valid[i].valid;
  1721.         }

  1722.         if (valid[i].status == status) {
  1723.             return valid[i].valid;
  1724.         }
  1725.     }

  1726.     return 0;
  1727. }


  1728. char *
  1729. ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1730. {
  1731.     char  *confp = conf;

  1732.     off_t                   max_size, min_free;
  1733.     u_char                 *last, *p;
  1734.     time_t                  inactive;
  1735.     ssize_t                 size;
  1736.     ngx_str_t               s, name, *value;
  1737.     ngx_int_t               loader_files, manager_files;
  1738.     ngx_msec_t              loader_sleep, manager_sleep, loader_threshold,
  1739.                             manager_threshold;
  1740.     ngx_uint_t              i, n, use_temp_path;
  1741.     ngx_array_t            *caches;
  1742.     ngx_http_file_cache_t  *cache, **ce;

  1743.     cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
  1744.     if (cache == NULL) {
  1745.         return NGX_CONF_ERROR;
  1746.     }

  1747.     cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  1748.     if (cache->path == NULL) {
  1749.         return NGX_CONF_ERROR;
  1750.     }

  1751.     use_temp_path = 1;

  1752.     inactive = 600;

  1753.     loader_files = 100;
  1754.     loader_sleep = 50;
  1755.     loader_threshold = 200;

  1756.     manager_files = 100;
  1757.     manager_sleep = 50;
  1758.     manager_threshold = 200;

  1759.     name.len = 0;
  1760.     size = 0;
  1761.     max_size = NGX_MAX_OFF_T_VALUE;
  1762.     min_free = 0;

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

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

  1765.     if (cache->path->name.data[cache->path->name.len - 1] == '/') {
  1766.         cache->path->name.len--;
  1767.     }

  1768.     if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
  1769.         return NGX_CONF_ERROR;
  1770.     }

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

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

  1773.             p = value[i].data + 7;
  1774.             last = value[i].data + value[i].len;

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

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

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

  1779.                     if (p == last) {
  1780.                         break;
  1781.                     }

  1782.                     if (*p++ == ':' && n < NGX_MAX_PATH_LEVEL - 1 && p < last) {
  1783.                         continue;
  1784.                     }

  1785.                     goto invalid_levels;
  1786.                 }

  1787.                 goto invalid_levels;
  1788.             }

  1789.             if (cache->path->len < 10 + NGX_MAX_PATH_LEVEL) {
  1790.                 continue;
  1791.             }

  1792.         invalid_levels:

  1793.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1794.                                "invalid \"levels\" \"%V\"", &value[i]);
  1795.             return NGX_CONF_ERROR;
  1796.         }

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

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

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

  1802.             } else {
  1803.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1804.                                    "invalid use_temp_path value \"%V\", "
  1805.                                    "it must be \"on\" or \"off\"",
  1806.                                    &value[i]);
  1807.                 return NGX_CONF_ERROR;
  1808.             }

  1809.             continue;
  1810.         }

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

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

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

  1814.             if (p == NULL) {
  1815.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1816.                                    "invalid keys zone size \"%V\"", &value[i]);
  1817.                 return NGX_CONF_ERROR;
  1818.             }

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

  1820.             s.data = p + 1;
  1821.             s.len = value[i].data + value[i].len - s.data;

  1822.             size = ngx_parse_size(&s);

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

  1828.             if (size < (ssize_t) (2 * ngx_pagesize)) {
  1829.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1830.                                    "keys zone \"%V\" is too small", &value[i]);
  1831.                 return NGX_CONF_ERROR;
  1832.             }

  1833.             continue;
  1834.         }

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

  1836.             s.len = value[i].len - 9;
  1837.             s.data = value[i].data + 9;

  1838.             inactive = ngx_parse_time(&s, 1);
  1839.             if (inactive == (time_t) NGX_ERROR) {
  1840.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1841.                                    "invalid inactive value \"%V\"", &value[i]);
  1842.                 return NGX_CONF_ERROR;
  1843.             }

  1844.             continue;
  1845.         }

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

  1847.             s.len = value[i].len - 9;
  1848.             s.data = value[i].data + 9;

  1849.             max_size = ngx_parse_offset(&s);
  1850.             if (max_size < 0) {
  1851.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1852.                                    "invalid max_size value \"%V\"", &value[i]);
  1853.                 return NGX_CONF_ERROR;
  1854.             }

  1855.             continue;
  1856.         }

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

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

  1859.             s.len = value[i].len - 9;
  1860.             s.data = value[i].data + 9;

  1861.             min_free = ngx_parse_offset(&s);
  1862.             if (min_free < 0) {
  1863.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1864.                                    "invalid min_free value \"%V\"", &value[i]);
  1865.                 return NGX_CONF_ERROR;
  1866.             }

  1867. #else
  1868.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1869.                                "min_free is not supported "
  1870.                                "on this platform, ignored");
  1871. #endif

  1872.             continue;
  1873.         }

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

  1875.             loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
  1876.             if (loader_files == NGX_ERROR) {
  1877.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1878.                            "invalid loader_files value \"%V\"", &value[i]);
  1879.                 return NGX_CONF_ERROR;
  1880.             }

  1881.             continue;
  1882.         }

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

  1884.             s.len = value[i].len - 13;
  1885.             s.data = value[i].data + 13;

  1886.             loader_sleep = ngx_parse_time(&s, 0);
  1887.             if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
  1888.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1889.                            "invalid loader_sleep value \"%V\"", &value[i]);
  1890.                 return NGX_CONF_ERROR;
  1891.             }

  1892.             continue;
  1893.         }

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

  1895.             s.len = value[i].len - 17;
  1896.             s.data = value[i].data + 17;

  1897.             loader_threshold = ngx_parse_time(&s, 0);
  1898.             if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
  1899.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1900.                            "invalid loader_threshold value \"%V\"", &value[i]);
  1901.                 return NGX_CONF_ERROR;
  1902.             }

  1903.             continue;
  1904.         }

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

  1906.             manager_files = ngx_atoi(value[i].data + 14, value[i].len - 14);
  1907.             if (manager_files == NGX_ERROR) {
  1908.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1909.                            "invalid manager_files value \"%V\"", &value[i]);
  1910.                 return NGX_CONF_ERROR;
  1911.             }

  1912.             continue;
  1913.         }

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

  1915.             s.len = value[i].len - 14;
  1916.             s.data = value[i].data + 14;

  1917.             manager_sleep = ngx_parse_time(&s, 0);
  1918.             if (manager_sleep == (ngx_msec_t) NGX_ERROR) {
  1919.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1920.                            "invalid manager_sleep value \"%V\"", &value[i]);
  1921.                 return NGX_CONF_ERROR;
  1922.             }

  1923.             continue;
  1924.         }

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

  1926.             s.len = value[i].len - 18;
  1927.             s.data = value[i].data + 18;

  1928.             manager_threshold = ngx_parse_time(&s, 0);
  1929.             if (manager_threshold == (ngx_msec_t) NGX_ERROR) {
  1930.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1931.                            "invalid manager_threshold value \"%V\"", &value[i]);
  1932.                 return NGX_CONF_ERROR;
  1933.             }

  1934.             continue;
  1935.         }

  1936.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1937.                            "invalid parameter \"%V\"", &value[i]);
  1938.         return NGX_CONF_ERROR;
  1939.     }

  1940.     if (name.len == 0 || size == 0) {
  1941.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1942.                            "\"%V\" must have \"keys_zone\" parameter",
  1943.                            &cmd->name);
  1944.         return NGX_CONF_ERROR;
  1945.     }

  1946.     cache->path->manager = ngx_http_file_cache_manager;
  1947.     cache->path->loader = ngx_http_file_cache_loader;
  1948.     cache->path->data = cache;
  1949.     cache->path->conf_file = cf->conf_file->file.name.data;
  1950.     cache->path->line = cf->conf_file->line;
  1951.     cache->loader_files = loader_files;
  1952.     cache->loader_sleep = loader_sleep;
  1953.     cache->loader_threshold = loader_threshold;
  1954.     cache->manager_files = manager_files;
  1955.     cache->manager_sleep = manager_sleep;
  1956.     cache->manager_threshold = manager_threshold;

  1957.     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
  1958.         return NGX_CONF_ERROR;
  1959.     }

  1960.     cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
  1961.     if (cache->shm_zone == NULL) {
  1962.         return NGX_CONF_ERROR;
  1963.     }

  1964.     if (cache->shm_zone->data) {
  1965.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1966.                            "duplicate zone \"%V\"", &name);
  1967.         return NGX_CONF_ERROR;
  1968.     }


  1969.     cache->shm_zone->init = ngx_http_file_cache_init;
  1970.     cache->shm_zone->data = cache;

  1971.     cache->use_temp_path = use_temp_path;

  1972.     cache->inactive = inactive;
  1973.     cache->max_size = max_size;
  1974.     cache->min_free = min_free;

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

  1976.     ce = ngx_array_push(caches);
  1977.     if (ce == NULL) {
  1978.         return NGX_CONF_ERROR;
  1979.     }

  1980.     *ce = cache;

  1981.     return NGX_CONF_OK;
  1982. }


  1983. char *
  1984. ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
  1985.     void *conf)
  1986. {
  1987.     char  *p = conf;

  1988.     time_t                    valid;
  1989.     ngx_str_t                *value;
  1990.     ngx_int_t                 status;
  1991.     ngx_uint_t                i, n;
  1992.     ngx_array_t             **a;
  1993.     ngx_http_cache_valid_t   *v;
  1994.     static ngx_uint_t         statuses[] = { 200, 301, 302 };

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

  1996.     if (*a == NGX_CONF_UNSET_PTR) {
  1997.         *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
  1998.         if (*a == NULL) {
  1999.             return NGX_CONF_ERROR;
  2000.         }
  2001.     }

  2002.     value = cf->args->elts;
  2003.     n = cf->args->nelts - 1;

  2004.     valid = ngx_parse_time(&value[n], 1);
  2005.     if (valid == (time_t) NGX_ERROR) {
  2006.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2007.                            "invalid time value \"%V\"", &value[n]);
  2008.         return NGX_CONF_ERROR;
  2009.     }

  2010.     if (n == 1) {

  2011.         for (i = 0; i < 3; i++) {
  2012.             v = ngx_array_push(*a);
  2013.             if (v == NULL) {
  2014.                 return NGX_CONF_ERROR;
  2015.             }

  2016.             v->status = statuses[i];
  2017.             v->valid = valid;
  2018.         }

  2019.         return NGX_CONF_OK;
  2020.     }

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

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

  2023.             status = 0;

  2024.         } else {

  2025.             status = ngx_atoi(value[i].data, value[i].len);
  2026.             if (status < 100 || status > 599) {
  2027.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  2028.                                    "invalid status \"%V\"", &value[i]);
  2029.                 return NGX_CONF_ERROR;
  2030.             }
  2031.         }

  2032.         v = ngx_array_push(*a);
  2033.         if (v == NULL) {
  2034.             return NGX_CONF_ERROR;
  2035.         }

  2036.         v->status = status;
  2037.         v->valid = valid;
  2038.     }

  2039.     return NGX_CONF_OK;
  2040. }