src/http/ngx_http_file_cache.c - nginx-1.7.10

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 void 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. static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
  22.     ngx_http_cache_t *c);
  23. static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,
  24.     ngx_path_t *path);
  25. static ngx_http_file_cache_node_t *
  26.     ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
  27. static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  28.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
  29. static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary,
  30.     size_t len, u_char *hash);
  31. static void ngx_http_file_cache_vary_header(ngx_http_request_t *r,
  32.     ngx_md5_t *md5, ngx_str_t *name);
  33. static ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r,
  34.     ngx_http_cache_t *c);
  35. static ngx_int_t ngx_http_file_cache_update_variant(ngx_http_request_t *r,
  36.     ngx_http_cache_t *c);
  37. static void ngx_http_file_cache_cleanup(void *data);
  38. static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
  39. static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
  40. static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
  41.     ngx_queue_t *q, u_char *name);
  42. static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
  43. static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
  44.     ngx_str_t *path);
  45. static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
  46.     ngx_str_t *path);
  47. static ngx_int_t ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx,
  48.     ngx_str_t *path);
  49. static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
  50.     ngx_str_t *path);
  51. static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
  52.     ngx_http_cache_t *c);
  53. static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
  54.     ngx_str_t *path);


  55. ngx_str_t  ngx_http_cache_status[] = {
  56.     ngx_string("MISS"),
  57.     ngx_string("BYPASS"),
  58.     ngx_string("EXPIRED"),
  59.     ngx_string("STALE"),
  60.     ngx_string("UPDATING"),
  61.     ngx_string("REVALIDATED"),
  62.     ngx_string("HIT")
  63. };


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


  65. static ngx_int_t
  66. ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
  67. {
  68.     ngx_http_file_cache_t  *ocache = data;

  69.     size_t                  len;
  70.     ngx_uint_t              n;
  71.     ngx_http_file_cache_t  *cache;

  72.     cache = shm_zone->data;

  73.     if (ocache) {
  74.         if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
  75.             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  76.                           "cache \"%V\" uses the \"%V\" cache path "
  77.                           "while previously it used the \"%V\" cache path",
  78.                           &shm_zone->shm.name, &cache->path->name,
  79.                           &ocache->path->name);

  80.             return NGX_ERROR;
  81.         }

  82.         for (n = 0; n < 3; n++) {
  83.             if (cache->path->level[n] != ocache->path->level[n]) {
  84.                 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
  85.                               "cache \"%V\" had previously different levels",
  86.                               &shm_zone->shm.name);
  87.                 return NGX_ERROR;
  88.             }
  89.         }

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

  91.         cache->shpool = ocache->shpool;
  92.         cache->bsize = ocache->bsize;

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

  94.         if (!cache->sh->cold || cache->sh->loading) {
  95.             cache->path->loader = NULL;
  96.         }

  97.         return NGX_OK;
  98.     }

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

  100.     if (shm_zone->shm.exists) {
  101.         cache->sh = cache->shpool->data;
  102.         cache->bsize = ngx_fs_bsize(cache->path->name.data);

  103.         return NGX_OK;
  104.     }

  105.     cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
  106.     if (cache->sh == NULL) {
  107.         return NGX_ERROR;
  108.     }

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

  110.     ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
  111.                     ngx_http_file_cache_rbtree_insert_value);

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

  113.     cache->sh->cold = 1;
  114.     cache->sh->loading = 0;
  115.     cache->sh->size = 0;

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

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

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

  119.     cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
  120.     if (cache->shpool->log_ctx == NULL) {
  121.         return NGX_ERROR;
  122.     }

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

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

  126.     return NGX_OK;
  127. }


  128. ngx_int_t
  129. ngx_http_file_cache_new(ngx_http_request_t *r)
  130. {
  131.     ngx_http_cache_t  *c;

  132.     c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
  133.     if (c == NULL) {
  134.         return NGX_ERROR;
  135.     }

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

  139.     r->cache = c;
  140.     c->file.log = r->connection->log;
  141.     c->file.fd = NGX_INVALID_FILE;

  142.     c->last_modified = -1;

  143.     return NGX_OK;
  144. }


  145. ngx_int_t
  146. ngx_http_file_cache_create(ngx_http_request_t *r)
  147. {
  148.     ngx_http_cache_t       *c;
  149.     ngx_pool_cleanup_t     *cln;
  150.     ngx_http_file_cache_t  *cache;

  151.     c = r->cache;
  152.     cache = c->file_cache;

  153.     cln = ngx_pool_cleanup_add(r->pool, 0);
  154.     if (cln == NULL) {
  155.         return NGX_ERROR;
  156.     }

  157.     cln->handler = ngx_http_file_cache_cleanup;
  158.     cln->data = c;

  159.     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  160.         return NGX_ERROR;
  161.     }

  162.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  163.         return NGX_ERROR;
  164.     }

  165.     return NGX_OK;
  166. }


  167. void
  168. ngx_http_file_cache_create_key(ngx_http_request_t *r)
  169. {
  170.     size_t             len;
  171.     ngx_str_t         *key;
  172.     ngx_uint_t         i;
  173.     ngx_md5_t          md5;
  174.     ngx_http_cache_t  *c;

  175.     c = r->cache;

  176.     len = 0;

  177.     ngx_crc32_init(c->crc32);
  178.     ngx_md5_init(&md5);

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

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

  184.         ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
  185.         ngx_md5_update(&md5, key[i].data, key[i].len);
  186.     }

  187.     c->header_start = sizeof(ngx_http_file_cache_header_t)
  188.                       + sizeof(ngx_http_file_cache_key) + len + 1;

  189.     ngx_crc32_final(c->crc32);
  190.     ngx_md5_final(c->key, &md5);

  191.     ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN);
  192. }


  193. ngx_int_t
  194. ngx_http_file_cache_open(ngx_http_request_t *r)
  195. {
  196.     ngx_int_t                  rc, rv;
  197.     ngx_uint_t                 cold, test;
  198.     ngx_http_cache_t          *c;
  199.     ngx_pool_cleanup_t        *cln;
  200.     ngx_open_file_info_t       of;
  201.     ngx_http_file_cache_t     *cache;
  202.     ngx_http_core_loc_conf_t  *clcf;

  203.     c = r->cache;

  204.     if (c->waiting) {
  205.         return NGX_AGAIN;
  206.     }

  207.     if (c->reading) {
  208.         return ngx_http_file_cache_read(r, c);
  209.     }

  210.     cache = c->file_cache;

  211.     if (c->node == NULL) {
  212.         cln = ngx_pool_cleanup_add(r->pool, 0);
  213.         if (cln == NULL) {
  214.             return NGX_ERROR;
  215.         }

  216.         cln->handler = ngx_http_file_cache_cleanup;
  217.         cln->data = c;
  218.     }

  219.     rc = ngx_http_file_cache_exists(cache, c);

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

  222.     if (rc == NGX_ERROR) {
  223.         return rc;
  224.     }

  225.     if (rc == NGX_AGAIN) {
  226.         return NGX_HTTP_CACHE_SCARCE;
  227.     }

  228.     cold = cache->sh->cold;

  229.     if (rc == NGX_OK) {

  230.         if (c->error) {
  231.             return c->error;
  232.         }

  233.         c->temp_file = 1;
  234.         test = c->exists ? 1 : 0;
  235.         rv = NGX_DECLINED;

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

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

  238.             if (!cold) {
  239.                 return NGX_HTTP_CACHE_SCARCE;
  240.             }

  241.             test = 1;
  242.             rv = NGX_HTTP_CACHE_SCARCE;

  243.         } else {
  244.             c->temp_file = 1;
  245.             test = cold ? 1 : 0;
  246.             rv = NGX_DECLINED;
  247.         }
  248.     }

  249.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  250.         return NGX_ERROR;
  251.     }

  252.     if (!test) {
  253.         goto done;
  254.     }

  255.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

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

  257.     of.uniq = c->uniq;
  258.     of.valid = clcf->open_file_cache_valid;
  259.     of.min_uses = clcf->open_file_cache_min_uses;
  260.     of.events = clcf->open_file_cache_events;
  261.     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
  262.     of.read_ahead = clcf->read_ahead;

  263.     if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
  264.         != NGX_OK)
  265.     {
  266.         switch (of.err) {

  267.         case 0:
  268.             return NGX_ERROR;

  269.         case NGX_ENOENT:
  270.         case NGX_ENOTDIR:
  271.             goto done;

  272.         default:
  273.             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
  274.                           ngx_open_file_n " \"%s\" failed", c->file.name.data);
  275.             return NGX_ERROR;
  276.         }
  277.     }

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

  280.     c->file.fd = of.fd;
  281.     c->file.log = r->connection->log;
  282.     c->uniq = of.uniq;
  283.     c->length = of.size;
  284.     c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;

  285.     c->buf = ngx_create_temp_buf(r->pool, c->body_start);
  286.     if (c->buf == NULL) {
  287.         return NGX_ERROR;
  288.     }

  289.     return ngx_http_file_cache_read(r, c);

  290. done:

  291.     if (rv == NGX_DECLINED) {
  292.         return ngx_http_file_cache_lock(r, c);
  293.     }

  294.     return rv;
  295. }


  296. static ngx_int_t
  297. ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
  298. {
  299.     ngx_msec_t                 now, timer;
  300.     ngx_http_file_cache_t     *cache;

  301.     if (!c->lock) {
  302.         return NGX_DECLINED;
  303.     }

  304.     now = ngx_current_msec;

  305.     cache = c->file_cache;

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

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

  308.     if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {
  309.         c->node->updating = 1;
  310.         c->node->lock_time = now + c->lock_age;
  311.         c->updating = 1;
  312.         c->lock_time = c->node->lock_time;
  313.     }

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

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

  318.     if (c->updating) {
  319.         return NGX_DECLINED;
  320.     }

  321.     if (c->lock_timeout == 0) {
  322.         return NGX_HTTP_CACHE_SCARCE;
  323.     }

  324.     c->waiting = 1;

  325.     if (c->wait_time == 0) {
  326.         c->wait_time = now + c->lock_timeout;

  327.         c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
  328.         c->wait_event.data = r;
  329.         c->wait_event.log = r->connection->log;
  330.     }

  331.     timer = c->wait_time - now;

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

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

  334.     return NGX_AGAIN;
  335. }


  336. static void
  337. ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
  338. {
  339.     ngx_connection_t    *c;
  340.     ngx_http_request_t  *r;

  341.     r = ev->data;
  342.     c = r->connection;

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

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

  346.     ngx_http_file_cache_lock_wait(r, r->cache);

  347.     ngx_http_run_posted_requests(c);
  348. }


  349. static void
  350. ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
  351. {
  352.     ngx_uint_t              wait;
  353.     ngx_msec_t              now, timer;
  354.     ngx_http_file_cache_t  *cache;

  355.     now = ngx_current_msec;

  356.     timer = c->wait_time - now;

  357.     if ((ngx_msec_int_t) timer <= 0) {
  358.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  359.                       "cache lock timeout");
  360.         c->lock_timeout = 0;
  361.         goto wakeup;
  362.     }

  363.     cache = c->file_cache;
  364.     wait = 0;

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

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

  367.     if (c->node->updating && (ngx_msec_int_t) timer > 0) {
  368.         wait = 1;
  369.     }

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

  371.     if (wait) {
  372.         ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
  373.         return;
  374.     }

  375. wakeup:

  376.     c->waiting = 0;
  377.     r->main->blocked--;
  378.     r->write_event_handler(r);
  379. }


  380. static ngx_int_t
  381. ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  382. {
  383.     time_t                         now;
  384.     ssize_t                        n;
  385.     ngx_int_t                      rc;
  386.     ngx_http_file_cache_t         *cache;
  387.     ngx_http_file_cache_header_t  *h;

  388.     n = ngx_http_file_cache_aio_read(r, c);

  389.     if (n < 0) {
  390.         return n;
  391.     }

  392.     if ((size_t) n < c->header_start) {
  393.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  394.                       "cache file \"%s\" is too small", c->file.name.data);
  395.         return NGX_DECLINED;
  396.     }

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

  398.     if (h->version != NGX_HTTP_CACHE_VERSION) {
  399.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  400.                       "cache file \"%s\" version mismatch", c->file.name.data);
  401.         return NGX_DECLINED;
  402.     }

  403.     if (h->crc32 != c->crc32) {
  404.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  405.                       "cache file \"%s\" has md5 collision", c->file.name.data);
  406.         return NGX_DECLINED;
  407.     }

  408.     if ((size_t) h->body_start > c->body_start) {
  409.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  410.                       "cache file \"%s\" has too long header",
  411.                       c->file.name.data);
  412.         return NGX_DECLINED;
  413.     }

  414.     if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) {
  415.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  416.                       "cache file \"%s\" has incorrect vary length",
  417.                       c->file.name.data);
  418.         return NGX_DECLINED;
  419.     }

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

  422.         if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) {
  423.             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  424.                            "http file cache vary mismatch");
  425.             return ngx_http_file_cache_reopen(r, c);
  426.         }
  427.     }

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

  429.     c->valid_sec = h->valid_sec;
  430.     c->last_modified = h->last_modified;
  431.     c->date = h->date;
  432.     c->valid_msec = h->valid_msec;
  433.     c->header_start = h->header_start;
  434.     c->body_start = h->body_start;
  435.     c->etag.len = h->etag_len;
  436.     c->etag.data = h->etag;

  437.     r->cached = 1;

  438.     cache = c->file_cache;

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

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

  441.         if (!c->node->exists) {
  442.             c->node->uses = 1;
  443.             c->node->body_start = c->body_start;
  444.             c->node->exists = 1;
  445.             c->node->uniq = c->uniq;
  446.             c->node->fs_size = c->fs_size;

  447.             cache->sh->size += c->fs_size;
  448.         }

  449.         ngx_shmtx_unlock(&cache->shpool->mutex);
  450.     }

  451.     now = ngx_time();

  452.     if (c->valid_sec < now) {

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

  454.         if (c->node->updating) {
  455.             rc = NGX_HTTP_CACHE_UPDATING;

  456.         } else {
  457.             c->node->updating = 1;
  458.             c->updating = 1;
  459.             c->lock_time = c->node->lock_time;
  460.             rc = NGX_HTTP_CACHE_STALE;
  461.         }

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

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

  466.         return rc;
  467.     }

  468.     return NGX_OK;
  469. }


  470. static ssize_t
  471. ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
  472. {
  473. #if (NGX_HAVE_FILE_AIO)
  474.     ssize_t                    n;
  475.     ngx_http_core_loc_conf_t  *clcf;

  476.     if (!ngx_file_aio) {
  477.         goto noaio;
  478.     }

  479.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

  480.     if (!clcf->aio) {
  481.         goto noaio;
  482.     }

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

  484.     if (n != NGX_AGAIN) {
  485.         c->reading = 0;
  486.         return n;
  487.     }

  488.     c->reading = 1;

  489.     c->file.aio->data = r;
  490.     c->file.aio->handler = ngx_http_cache_aio_event_handler;

  491.     r->main->blocked++;
  492.     r->aio = 1;

  493.     return NGX_AGAIN;

  494. noaio:

  495. #endif

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


  498. #if (NGX_HAVE_FILE_AIO)

  499. static void
  500. ngx_http_cache_aio_event_handler(ngx_event_t *ev)
  501. {
  502.     ngx_event_aio_t     *aio;
  503.     ngx_connection_t    *c;
  504.     ngx_http_request_t  *r;

  505.     aio = ev->data;
  506.     r = aio->data;
  507.     c = r->connection;

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

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

  511.     r->main->blocked--;
  512.     r->aio = 0;

  513.     r->write_event_handler(r);

  514.     ngx_http_run_posted_requests(c);
  515. }

  516. #endif


  517. static ngx_int_t
  518. ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  519. {
  520.     ngx_int_t                    rc;
  521.     ngx_http_file_cache_node_t  *fcn;

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

  523.     fcn = c->node;

  524.     if (fcn == NULL) {
  525.         fcn = ngx_http_file_cache_lookup(cache, c->key);
  526.     }

  527.     if (fcn) {
  528.         ngx_queue_remove(&fcn->queue);

  529.         if (c->node == NULL) {
  530.             fcn->uses++;
  531.             fcn->count++;
  532.         }

  533.         if (fcn->error) {

  534.             if (fcn->valid_sec < ngx_time()) {
  535.                 goto renew;
  536.             }

  537.             rc = NGX_OK;

  538.             goto done;
  539.         }

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

  541.             c->exists = fcn->exists;
  542.             if (fcn->body_start) {
  543.                 c->body_start = fcn->body_start;
  544.             }

  545.             rc = NGX_OK;

  546.             goto done;
  547.         }

  548.         rc = NGX_AGAIN;

  549.         goto done;
  550.     }

  551.     fcn = ngx_slab_calloc_locked(cache->shpool,
  552.                                  sizeof(ngx_http_file_cache_node_t));
  553.     if (fcn == NULL) {
  554.         ngx_shmtx_unlock(&cache->shpool->mutex);

  555.         (void) ngx_http_file_cache_forced_expire(cache);

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

  557.         fcn = ngx_slab_calloc_locked(cache->shpool,
  558.                                      sizeof(ngx_http_file_cache_node_t));
  559.         if (fcn == NULL) {
  560.             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  561.                           "could not allocate node%s", cache->shpool->log_ctx);
  562.             rc = NGX_ERROR;
  563.             goto failed;
  564.         }
  565.     }

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

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

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

  570.     fcn->uses = 1;
  571.     fcn->count = 1;

  572. renew:

  573.     rc = NGX_DECLINED;

  574.     fcn->valid_msec = 0;
  575.     fcn->error = 0;
  576.     fcn->exists = 0;
  577.     fcn->valid_sec = 0;
  578.     fcn->uniq = 0;
  579.     fcn->body_start = 0;
  580.     fcn->fs_size = 0;

  581. done:

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

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

  584.     c->uniq = fcn->uniq;
  585.     c->error = fcn->error;
  586.     c->node = fcn;

  587. failed:

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

  589.     return rc;
  590. }


  591. static ngx_int_t
  592. ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
  593. {
  594.     u_char            *p;
  595.     ngx_http_cache_t  *c;

  596.     c = r->cache;

  597.     if (c->file.name.len) {
  598.         return NGX_OK;
  599.     }

  600.     c->file.name.len = path->name.len + 1 + path->len
  601.                        + 2 * NGX_HTTP_CACHE_KEY_LEN;

  602.     c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
  603.     if (c->file.name.data == NULL) {
  604.         return NGX_ERROR;
  605.     }

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

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

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

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

  613.     return NGX_OK;
  614. }


  615. static ngx_http_file_cache_node_t *
  616. ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
  617. {
  618.     ngx_int_t                    rc;
  619.     ngx_rbtree_key_t             node_key;
  620.     ngx_rbtree_node_t           *node, *sentinel;
  621.     ngx_http_file_cache_node_t  *fcn;

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

  623.     node = cache->sh->rbtree.root;
  624.     sentinel = cache->sh->rbtree.sentinel;

  625.     while (node != sentinel) {

  626.         if (node_key < node->key) {
  627.             node = node->left;
  628.             continue;
  629.         }

  630.         if (node_key > node->key) {
  631.             node = node->right;
  632.             continue;
  633.         }

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

  635.         fcn = (ngx_http_file_cache_node_t *) node;

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

  638.         if (rc == 0) {
  639.             return fcn;
  640.         }

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

  643.     /* not found */

  644.     return NULL;
  645. }


  646. static void
  647. ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  648.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  649. {
  650.     ngx_rbtree_node_t           **p;
  651.     ngx_http_file_cache_node_t   *cn, *cnt;

  652.     for ( ;; ) {

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

  654.             p = &temp->left;

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

  656.             p = &temp->right;

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

  658.             cn = (ngx_http_file_cache_node_t *) node;
  659.             cnt = (ngx_http_file_cache_node_t *) temp;

  660.             p = (ngx_memcmp(cn->key, cnt->key,
  661.                             NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
  662.                  < 0)
  663.                     ? &temp->left : &temp->right;
  664.         }

  665.         if (*p == sentinel) {
  666.             break;
  667.         }

  668.         temp = *p;
  669.     }

  670.     *p = node;
  671.     node->parent = temp;
  672.     node->left = sentinel;
  673.     node->right = sentinel;
  674.     ngx_rbt_red(node);
  675. }


  676. static void
  677. ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,
  678.     u_char *hash)
  679. {
  680.     u_char     *p, *last;
  681.     ngx_str_t   name;
  682.     ngx_md5_t   md5;
  683.     u_char      buf[NGX_HTTP_CACHE_VARY_LEN];

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

  686.     ngx_md5_init(&md5);
  687.     ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);

  688.     ngx_strlow(buf, vary, len);

  689.     p = buf;
  690.     last = buf + len;

  691.     while (p < last) {

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

  693.         name.data = p;

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

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

  696.         if (name.len == 0) {
  697.             break;
  698.         }

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

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

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

  704.         ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);
  705.     }

  706.     ngx_md5_final(hash, &md5);
  707. }


  708. static void
  709. ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,
  710.     ngx_str_t *name)
  711. {
  712.     size_t            len;
  713.     u_char           *p, *start, *last;
  714.     ngx_uint_t        i, multiple, normalize;
  715.     ngx_list_part_t  *part;
  716.     ngx_table_elt_t  *header;

  717.     multiple = 0;
  718.     normalize = 0;

  719.     if (name->len == sizeof("Accept-Charset") - 1
  720.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset",
  721.                            sizeof("Accept-Charset") - 1) == 0)
  722.     {
  723.         normalize = 1;

  724.     } else if (name->len == sizeof("Accept-Encoding") - 1
  725.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding",
  726.                            sizeof("Accept-Encoding") - 1) == 0)
  727.     {
  728.         normalize = 1;

  729.     } else if (name->len == sizeof("Accept-Language") - 1
  730.         && ngx_strncasecmp(name->data, (u_char *) "Accept-Language",
  731.                            sizeof("Accept-Language") - 1) == 0)
  732.     {
  733.         normalize = 1;
  734.     }

  735.     part = &r->headers_in.headers.part;
  736.     header = part->elts;

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

  738.         if (i >= part->nelts) {
  739.             if (part->next == NULL) {
  740.                 break;
  741.             }

  742.             part = part->next;
  743.             header = part->elts;
  744.             i = 0;
  745.         }

  746.         if (header[i].hash == 0) {
  747.             continue;
  748.         }

  749.         if (header[i].key.len != name->len) {
  750.             continue;
  751.         }

  752.         if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {
  753.             continue;
  754.         }

  755.         if (!normalize) {

  756.             if (multiple) {
  757.                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  758.             }

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

  760.             multiple = 1;

  761.             continue;
  762.         }

  763.         /* normalize spaces */

  764.         p = header[i].value.data;
  765.         last = p + header[i].value.len;

  766.         while (p < last) {

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

  768.             start = p;

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

  770.             len = p - start;

  771.             if (len == 0) {
  772.                 break;
  773.             }

  774.             if (multiple) {
  775.                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
  776.             }

  777.             ngx_md5_update(md5, start, len);

  778.             multiple = 1;
  779.         }
  780.     }
  781. }


  782. static ngx_int_t
  783. ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)
  784. {
  785.     ngx_http_file_cache_t  *cache;

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

  788.     if (c->secondary) {
  789.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  790.                       "cache file \"%s\" has incorrect vary hash",
  791.                       c->file.name.data);
  792.         return NGX_DECLINED;
  793.     }

  794.     cache = c->file_cache;

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

  796.     c->node->count--;
  797.     c->node = NULL;

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

  799.     c->secondary = 1;
  800.     c->file.name.len = 0;
  801.     c->body_start = c->buf->end - c->buf->start;

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

  803.     return ngx_http_file_cache_open(r);
  804. }


  805. ngx_int_t
  806. ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
  807. {
  808.     ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;

  809.     u_char            *p;
  810.     ngx_str_t         *key;
  811.     ngx_uint_t         i;
  812.     ngx_http_cache_t  *c;

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

  815.     c = r->cache;

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

  817.     h->version = NGX_HTTP_CACHE_VERSION;
  818.     h->valid_sec = c->valid_sec;
  819.     h->last_modified = c->last_modified;
  820.     h->date = c->date;
  821.     h->crc32 = c->crc32;
  822.     h->valid_msec = (u_short) c->valid_msec;
  823.     h->header_start = (u_short) c->header_start;
  824.     h->body_start = (u_short) c->body_start;

  825.     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  826.         h->etag_len = (u_char) c->etag.len;
  827.         ngx_memcpy(h->etag, c->etag.data, c->etag.len);
  828.     }

  829.     if (c->vary.len) {
  830.         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  831.             /* should not happen */
  832.             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  833.         }

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

  836.         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  837.         ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  838.     }

  839.     if (ngx_http_file_cache_update_variant(r, c) != NGX_OK) {
  840.         return NGX_ERROR;
  841.     }

  842.     p = buf + sizeof(ngx_http_file_cache_header_t);

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

  844.     key = c->keys.elts;
  845.     for (i = 0; i < c->keys.nelts; i++) {
  846.         p = ngx_copy(p, key[i].data, key[i].len);
  847.     }

  848.     *p = LF;

  849.     return NGX_OK;
  850. }


  851. static ngx_int_t
  852. ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)
  853. {
  854.     ngx_http_file_cache_t  *cache;

  855.     if (!c->secondary) {
  856.         return NGX_OK;
  857.     }

  858.     if (c->vary.len
  859.         && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) == 0)
  860.     {
  861.         return NGX_OK;
  862.     }

  863.     /*
  864.      * if the variant hash doesn't match one we used as a secondary
  865.      * cache key, switch back to the original key
  866.      */

  867.     cache = c->file_cache;

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

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

  871.     c->node->count--;
  872.     c->node->updating = 0;
  873.     c->node = NULL;

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

  875.     c->file.name.len = 0;

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

  877.     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
  878.         return NGX_ERROR;
  879.     }

  880.     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
  881.         return NGX_ERROR;
  882.     }

  883.     return NGX_OK;
  884. }


  885. void
  886. ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
  887. {
  888.     off_t                   fs_size;
  889.     ngx_int_t               rc;
  890.     ngx_file_uniq_t         uniq;
  891.     ngx_file_info_t         fi;
  892.     ngx_http_cache_t        *c;
  893.     ngx_ext_rename_file_t   ext;
  894.     ngx_http_file_cache_t  *cache;

  895.     c = r->cache;

  896.     if (c->updated) {
  897.         return;
  898.     }

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

  901.     cache = c->file_cache;

  902.     c->updated = 1;
  903.     c->updating = 0;

  904.     uniq = 0;
  905.     fs_size = 0;

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

  909.     ext.access = NGX_FILE_OWNER_ACCESS;
  910.     ext.path_access = NGX_FILE_OWNER_ACCESS;
  911.     ext.time = -1;
  912.     ext.create_path = 1;
  913.     ext.delete_file = 1;
  914.     ext.log = r->connection->log;

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

  916.     if (rc == NGX_OK) {

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

  920.             rc = NGX_ERROR;

  921.         } else {
  922.             uniq = ngx_file_uniq(&fi);
  923.             fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
  924.         }
  925.     }

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

  927.     c->node->count--;
  928.     c->node->uniq = uniq;
  929.     c->node->body_start = c->body_start;

  930.     cache->sh->size += fs_size - c->node->fs_size;
  931.     c->node->fs_size = fs_size;

  932.     if (rc == NGX_OK) {
  933.         c->node->exists = 1;
  934.     }

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

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


  938. void
  939. ngx_http_file_cache_update_header(ngx_http_request_t *r)
  940. {
  941.     ssize_t                        n;
  942.     ngx_err_t                      err;
  943.     ngx_file_t                     file;
  944.     ngx_file_info_t                fi;
  945.     ngx_http_cache_t              *c;
  946.     ngx_http_file_cache_header_t   h;

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

  949.     c = r->cache;

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

  951.     file.name = c->file.name;
  952.     file.log = r->connection->log;
  953.     file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);

  954.     if (file.fd == NGX_INVALID_FILE) {
  955.         err = ngx_errno;

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

  957.         if (err == NGX_ENOENT) {
  958.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  959.                            "http file cache \"%s\" not found",
  960.                            file.name.data);
  961.             return;
  962.         }

  963.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
  964.                       ngx_open_file_n " \"%s\" failed", file.name.data);
  965.         return;
  966.     }

  967.     /*
  968.      * make sure cache file wasn't replaced;
  969.      * if it was, do nothing
  970.      */

  971.     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
  972.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
  973.                       ngx_fd_info_n " \"%s\" failed", file.name.data);
  974.         goto done;
  975.     }

  976.     if (c->uniq != ngx_file_uniq(&fi)
  977.         || c->length != ngx_file_size(&fi))
  978.     {
  979.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  980.                        "http file cache \"%s\" changed",
  981.                        file.name.data);
  982.         goto done;
  983.     }

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

  986.     if (n == NGX_ERROR) {
  987.         goto done;
  988.     }

  989.     if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {
  990.         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
  991.                       ngx_read_file_n " read only %z of %z from \"%s\"",
  992.                       n, sizeof(ngx_http_file_cache_header_t), file.name.data);
  993.         goto done;
  994.     }

  995.     if (h.version != NGX_HTTP_CACHE_VERSION
  996.         || h.last_modified != c->last_modified
  997.         || h.crc32 != c->crc32
  998.         || h.header_start != c->header_start
  999.         || h.body_start != c->body_start)
  1000.     {
  1001.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1002.                        "http file cache \"%s\" content changed",
  1003.                        file.name.data);
  1004.         goto done;
  1005.     }

  1006.     /*
  1007.      * update cache file header with new data,
  1008.      * notably h.valid_sec and h.date
  1009.      */

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

  1011.     h.version = NGX_HTTP_CACHE_VERSION;
  1012.     h.valid_sec = c->valid_sec;
  1013.     h.last_modified = c->last_modified;
  1014.     h.date = c->date;
  1015.     h.crc32 = c->crc32;
  1016.     h.valid_msec = (u_short) c->valid_msec;
  1017.     h.header_start = (u_short) c->header_start;
  1018.     h.body_start = (u_short) c->body_start;

  1019.     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
  1020.         h.etag_len = (u_char) c->etag.len;
  1021.         ngx_memcpy(h.etag, c->etag.data, c->etag.len);
  1022.     }

  1023.     if (c->vary.len) {
  1024.         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
  1025.             /* should not happen */
  1026.             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
  1027.         }

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

  1030.         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
  1031.         ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
  1032.     }

  1033.     (void) ngx_write_file(&file, (u_char *) &h,
  1034.                           sizeof(ngx_http_file_cache_header_t), 0);

  1035. done:

  1036.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  1037.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
  1038.                       ngx_close_file_n " \"%s\" failed", file.name.data);
  1039.     }
  1040. }


  1041. ngx_int_t
  1042. ngx_http_cache_send(ngx_http_request_t *r)
  1043. {
  1044.     ngx_int_t          rc;
  1045.     ngx_buf_t         *b;
  1046.     ngx_chain_t        out;
  1047.     ngx_http_cache_t  *c;

  1048.     c = r->cache;

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

  1051.     if (r != r->main && c->length - c->body_start == 0) {
  1052.         return ngx_http_send_header(r);
  1053.     }

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

  1055.     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
  1056.     if (b == NULL) {
  1057.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1058.     }

  1059.     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
  1060.     if (b->file == NULL) {
  1061.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  1062.     }

  1063.     rc = ngx_http_send_header(r);

  1064.     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
  1065.         return rc;
  1066.     }

  1067.     b->file_pos = c->body_start;
  1068.     b->file_last = c->length;

  1069.     b->in_file = (c->length - c->body_start) ? 1: 0;
  1070.     b->last_buf = (r == r->main) ? 1: 0;
  1071.     b->last_in_chain = 1;

  1072.     b->file->fd = c->file.fd;
  1073.     b->file->name = c->file.name;
  1074.     b->file->log = r->connection->log;

  1075.     out.buf = b;
  1076.     out.next = NULL;

  1077.     return ngx_http_output_filter(r, &out);
  1078. }


  1079. void
  1080. ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
  1081. {
  1082.     ngx_http_file_cache_t       *cache;
  1083.     ngx_http_file_cache_node_t  *fcn;

  1084.     if (c->updated || c->node == NULL) {
  1085.         return;
  1086.     }

  1087.     cache = c->file_cache;

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

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

  1091.     fcn = c->node;
  1092.     fcn->count--;

  1093.     if (c->updating && fcn->lock_time == c->lock_time) {
  1094.         fcn->updating = 0;
  1095.     }

  1096.     if (c->error) {
  1097.         fcn->error = c->error;

  1098.         if (c->valid_sec) {
  1099.             fcn->valid_sec = c->valid_sec;
  1100.             fcn->valid_msec = c->valid_msec;
  1101.         }

  1102.     } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
  1103.         ngx_queue_remove(&fcn->queue);
  1104.         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1105.         ngx_slab_free_locked(cache->shpool, fcn);
  1106.         c->node = NULL;
  1107.     }

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

  1109.     c->updated = 1;
  1110.     c->updating = 0;

  1111.     if (c->temp_file) {
  1112.         if (tf && tf->file.fd != NGX_INVALID_FILE) {
  1113.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
  1114.                            "http file cache incomplete: \"%s\"",
  1115.                            tf->file.name.data);

  1116.             if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
  1117.                 ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
  1118.                               ngx_delete_file_n " \"%s\" failed",
  1119.                               tf->file.name.data);
  1120.             }
  1121.         }
  1122.     }

  1123.     if (c->wait_event.timer_set) {
  1124.         ngx_del_timer(&c->wait_event);
  1125.     }
  1126. }


  1127. static void
  1128. ngx_http_file_cache_cleanup(void *data)
  1129. {
  1130.     ngx_http_cache_t  *c = data;

  1131.     if (c->updated) {
  1132.         return;
  1133.     }

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

  1136.     if (c->updating) {
  1137.         ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
  1138.                       "stalled cache updating, error:%ui", c->error);
  1139.     }

  1140.     ngx_http_file_cache_free(c, NULL);
  1141. }


  1142. static time_t
  1143. ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
  1144. {
  1145.     u_char                      *name;
  1146.     size_t                       len;
  1147.     time_t                       wait;
  1148.     ngx_uint_t                   tries;
  1149.     ngx_path_t                  *path;
  1150.     ngx_queue_t                 *q;
  1151.     ngx_http_file_cache_node_t  *fcn;

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

  1154.     path = cache->path;
  1155.     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

  1156.     name = ngx_alloc(len + 1, ngx_cycle->log);
  1157.     if (name == NULL) {
  1158.         return 10;
  1159.     }

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

  1161.     wait = 10;
  1162.     tries = 20;

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

  1164.     for (q = ngx_queue_last(&cache->sh->queue);
  1165.          q != ngx_queue_sentinel(&cache->sh->queue);
  1166.          q = ngx_queue_prev(q))
  1167.     {
  1168.         fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);

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

  1173.         if (fcn->count == 0) {
  1174.             ngx_http_file_cache_delete(cache, q, name);
  1175.             wait = 0;

  1176.         } else {
  1177.             if (--tries) {
  1178.                 continue;
  1179.             }

  1180.             wait = 1;
  1181.         }

  1182.         break;
  1183.     }

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

  1185.     ngx_free(name);

  1186.     return wait;
  1187. }


  1188. static time_t
  1189. ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
  1190. {
  1191.     u_char                      *name, *p;
  1192.     size_t                       len;
  1193.     time_t                       now, wait;
  1194.     ngx_path_t                  *path;
  1195.     ngx_queue_t                 *q;
  1196.     ngx_http_file_cache_node_t  *fcn;
  1197.     u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];

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

  1200.     path = cache->path;
  1201.     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;

  1202.     name = ngx_alloc(len + 1, ngx_cycle->log);
  1203.     if (name == NULL) {
  1204.         return 10;
  1205.     }

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

  1207.     now = ngx_time();

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

  1209.     for ( ;; ) {

  1210.         if (ngx_quit || ngx_terminate) {
  1211.             wait = 1;
  1212.             break;
  1213.         }

  1214.         if (ngx_queue_empty(&cache->sh->queue)) {
  1215.             wait = 10;
  1216.             break;
  1217.         }

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

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

  1220.         wait = fcn->expire - now;

  1221.         if (wait > 0) {
  1222.             wait = wait > 10 ? 10 : wait;
  1223.             break;
  1224.         }

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

  1229.         if (fcn->count == 0) {
  1230.             ngx_http_file_cache_delete(cache, q, name);
  1231.             continue;
  1232.         }

  1233.         if (fcn->deleting) {
  1234.             wait = 1;
  1235.             break;
  1236.         }

  1237.         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
  1238.                          sizeof(ngx_rbtree_key_t));
  1239.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1240.         (void) ngx_hex_dump(p, fcn->key, len);

  1241.         /*
  1242.          * abnormally exited workers may leave locked cache entries,
  1243.          * and although it may be safe to remove them completely,
  1244.          * we prefer to just move them to the top of the inactive queue
  1245.          */

  1246.         ngx_queue_remove(q);
  1247.         fcn->expire = ngx_time() + cache->inactive;
  1248.         ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);

  1249.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  1250.                       "ignore long locked inactive cache entry %*s, count:%d",
  1251.                       2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
  1252.     }

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

  1254.     ngx_free(name);

  1255.     return wait;
  1256. }


  1257. static void
  1258. ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
  1259.     u_char *name)
  1260. {
  1261.     u_char                      *p;
  1262.     size_t                       len;
  1263.     ngx_path_t                  *path;
  1264.     ngx_http_file_cache_node_t  *fcn;

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

  1266.     if (fcn->exists) {
  1267.         cache->sh->size -= fcn->fs_size;

  1268.         path = cache->path;
  1269.         p = name + path->name.len + 1 + path->len;
  1270.         p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
  1271.                          sizeof(ngx_rbtree_key_t));
  1272.         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
  1273.         p = ngx_hex_dump(p, fcn->key, len);
  1274.         *p = '\0';

  1275.         fcn->count++;
  1276.         fcn->deleting = 1;
  1277.         ngx_shmtx_unlock(&cache->shpool->mutex);

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

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

  1282.         if (ngx_delete_file(name) == NGX_FILE_ERROR) {
  1283.             ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
  1284.                           ngx_delete_file_n " \"%s\" failed", name);
  1285.         }

  1286.         ngx_shmtx_lock(&cache->shpool->mutex);
  1287.         fcn->count--;
  1288.         fcn->deleting = 0;
  1289.     }

  1290.     if (fcn->count == 0) {
  1291.         ngx_queue_remove(q);
  1292.         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
  1293.         ngx_slab_free_locked(cache->shpool, fcn);
  1294.     }
  1295. }


  1296. static time_t
  1297. ngx_http_file_cache_manager(void *data)
  1298. {
  1299.     ngx_http_file_cache_t  *cache = data;

  1300.     off_t   size;
  1301.     time_t  next, wait;

  1302.     next = ngx_http_file_cache_expire(cache);

  1303.     cache->last = ngx_current_msec;
  1304.     cache->files = 0;

  1305.     for ( ;; ) {
  1306.         ngx_shmtx_lock(&cache->shpool->mutex);

  1307.         size = cache->sh->size;

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

  1309.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
  1310.                        "http file cache size: %O", size);

  1311.         if (size < cache->max_size) {
  1312.             return next;
  1313.         }

  1314.         wait = ngx_http_file_cache_forced_expire(cache);

  1315.         if (wait > 0) {
  1316.             return wait;
  1317.         }

  1318.         if (ngx_quit || ngx_terminate) {
  1319.             return next;
  1320.         }
  1321.     }
  1322. }


  1323. static void
  1324. ngx_http_file_cache_loader(void *data)
  1325. {
  1326.     ngx_http_file_cache_t  *cache = data;

  1327.     ngx_tree_ctx_t  tree;

  1328.     if (!cache->sh->cold || cache->sh->loading) {
  1329.         return;
  1330.     }

  1331.     if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
  1332.         return;
  1333.     }

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

  1336.     tree.init_handler = NULL;
  1337.     tree.file_handler = ngx_http_file_cache_manage_file;
  1338.     tree.pre_tree_handler = ngx_http_file_cache_manage_directory;
  1339.     tree.post_tree_handler = ngx_http_file_cache_noop;
  1340.     tree.spec_handler = ngx_http_file_cache_delete_file;
  1341.     tree.data = cache;
  1342.     tree.alloc = 0;
  1343.     tree.log = ngx_cycle->log;

  1344.     cache->last = ngx_current_msec;
  1345.     cache->files = 0;

  1346.     if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
  1347.         cache->sh->loading = 0;
  1348.         return;
  1349.     }

  1350.     cache->sh->cold = 0;
  1351.     cache->sh->loading = 0;

  1352.     ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
  1353.                   "http file cache: %V %.3fM, bsize: %uz",
  1354.                   &cache->path->name,
  1355.                   ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
  1356.                   cache->bsize);
  1357. }


  1358. static ngx_int_t
  1359. ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1360. {
  1361.     return NGX_OK;
  1362. }


  1363. static ngx_int_t
  1364. ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1365. {
  1366.     ngx_msec_t              elapsed;
  1367.     ngx_http_file_cache_t  *cache;

  1368.     cache = ctx->data;

  1369.     if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
  1370.         (void) ngx_http_file_cache_delete_file(ctx, path);
  1371.     }

  1372.     if (++cache->files >= cache->loader_files) {
  1373.         ngx_http_file_cache_loader_sleep(cache);

  1374.     } else {
  1375.         ngx_time_update();

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

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

  1379.         if (elapsed >= cache->loader_threshold) {
  1380.             ngx_http_file_cache_loader_sleep(cache);
  1381.         }
  1382.     }

  1383.     return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
  1384. }


  1385. static ngx_int_t
  1386. ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1387. {
  1388.     if (path->len >= 5
  1389.         && ngx_strncmp(path->data + path->len - 5, "/temp", 5) == 0)
  1390.     {
  1391.         return NGX_DECLINED;
  1392.     }

  1393.     return NGX_OK;
  1394. }


  1395. static void
  1396. ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
  1397. {
  1398.     ngx_msleep(cache->loader_sleep);

  1399.     ngx_time_update();

  1400.     cache->last = ngx_current_msec;
  1401.     cache->files = 0;
  1402. }


  1403. static ngx_int_t
  1404. ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
  1405. {
  1406.     u_char                 *p;
  1407.     ngx_int_t               n;
  1408.     ngx_uint_t              i;
  1409.     ngx_http_cache_t        c;
  1410.     ngx_http_file_cache_t  *cache;

  1411.     if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
  1412.         return NGX_ERROR;
  1413.     }

  1414.     if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
  1415.         ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
  1416.                       "cache file \"%s\" is too small", name->data);
  1417.         return NGX_ERROR;
  1418.     }

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

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

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

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

  1426.         if (n == NGX_ERROR) {
  1427.             return NGX_ERROR;
  1428.         }

  1429.         p += 2;

  1430.         c.key[i] = (u_char) n;
  1431.     }

  1432.     return ngx_http_file_cache_add(cache, &c);
  1433. }


  1434. static ngx_int_t
  1435. ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
  1436. {
  1437.     ngx_http_file_cache_node_t  *fcn;

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

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

  1440.     if (fcn == NULL) {

  1441.         fcn = ngx_slab_calloc_locked(cache->shpool,
  1442.                                      sizeof(ngx_http_file_cache_node_t));
  1443.         if (fcn == NULL) {
  1444.             ngx_shmtx_unlock(&cache->shpool->mutex);
  1445.             return NGX_ERROR;
  1446.         }

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

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

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

  1451.         fcn->uses = 1;
  1452.         fcn->exists = 1;
  1453.         fcn->fs_size = c->fs_size;

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

  1455.     } else {
  1456.         ngx_queue_remove(&fcn->queue);
  1457.     }

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

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

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

  1461.     return NGX_OK;
  1462. }


  1463. static ngx_int_t
  1464. ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
  1465. {
  1466.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
  1467.                    "http file cache delete: \"%s\"", path->data);

  1468.     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
  1469.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  1470.                       ngx_delete_file_n " \"%s\" failed", path->data);
  1471.     }

  1472.     return NGX_OK;
  1473. }


  1474. time_t
  1475. ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
  1476. {
  1477.     ngx_uint_t               i;
  1478.     ngx_http_cache_valid_t  *valid;

  1479.     if (cache_valid == NULL) {
  1480.         return 0;
  1481.     }

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

  1484.         if (valid[i].status == 0) {
  1485.             return valid[i].valid;
  1486.         }

  1487.         if (valid[i].status == status) {
  1488.             return valid[i].valid;
  1489.         }
  1490.     }

  1491.     return 0;
  1492. }


  1493. char *
  1494. ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1495. {
  1496.     char  *confp = conf;

  1497.     off_t                   max_size;
  1498.     u_char                 *last, *p;
  1499.     time_t                  inactive;
  1500.     size_t                  len;
  1501.     ssize_t                 size;
  1502.     ngx_str_t               s, name, *value;
  1503.     ngx_int_t               loader_files;
  1504.     ngx_msec_t              loader_sleep, loader_threshold;
  1505.     ngx_uint_t              i, n, use_temp_path;
  1506.     ngx_array_t            *caches;
  1507.     ngx_http_file_cache_t  *cache, **ce;

  1508.     cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
  1509.     if (cache == NULL) {
  1510.         return NGX_CONF_ERROR;
  1511.     }

  1512.     cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  1513.     if (cache->path == NULL) {
  1514.         return NGX_CONF_ERROR;
  1515.     }

  1516.     use_temp_path = 1;

  1517.     inactive = 600;
  1518.     loader_files = 100;
  1519.     loader_sleep = 50;
  1520.     loader_threshold = 200;

  1521.     name.len = 0;
  1522.     size = 0;
  1523.     max_size = NGX_MAX_OFF_T_VALUE;

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

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

  1526.     if (cache->path->name.data[cache->path->name.len - 1] == '/') {
  1527.         cache->path->name.len--;
  1528.     }

  1529.     if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
  1530.         return NGX_CONF_ERROR;
  1531.     }

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

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

  1534.             p = value[i].data + 7;
  1535.             last = value[i].data + value[i].len;

  1536.             for (n = 0; n < 3 && p < last; n++) {

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

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

  1540.                     if (p == last) {
  1541.                         break;
  1542.                     }

  1543.                     if (*p++ == ':' && n < 2 && p != last) {
  1544.                         continue;
  1545.                     }

  1546.                     goto invalid_levels;
  1547.                 }

  1548.                 goto invalid_levels;
  1549.             }

  1550.             if (cache->path->len < 10 + 3) {
  1551.                 continue;
  1552.             }

  1553.         invalid_levels:

  1554.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1555.                                "invalid \"levels\" \"%V\"", &value[i]);
  1556.             return NGX_CONF_ERROR;
  1557.         }

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

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

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

  1563.             } else {
  1564.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1565.                                    "invalid use_temp_path value \"%V\", "
  1566.                                    "it must be \"on\" or \"off\"",
  1567.                                    &value[i]);
  1568.                 return NGX_CONF_ERROR;
  1569.             }

  1570.             continue;
  1571.         }

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

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

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

  1575.             if (p) {
  1576.                 name.len = p - name.data;

  1577.                 p++;

  1578.                 s.len = value[i].data + value[i].len - p;
  1579.                 s.data = p;

  1580.                 size = ngx_parse_size(&s);
  1581.                 if (size > 8191) {
  1582.                     continue;
  1583.                 }
  1584.             }

  1585.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1586.                                "invalid keys zone size \"%V\"", &value[i]);
  1587.             return NGX_CONF_ERROR;
  1588.         }

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

  1590.             s.len = value[i].len - 9;
  1591.             s.data = value[i].data + 9;

  1592.             inactive = ngx_parse_time(&s, 1);
  1593.             if (inactive == (time_t) NGX_ERROR) {
  1594.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1595.                                    "invalid inactive value \"%V\"", &value[i]);
  1596.                 return NGX_CONF_ERROR;
  1597.             }

  1598.             continue;
  1599.         }

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

  1601.             s.len = value[i].len - 9;
  1602.             s.data = value[i].data + 9;

  1603.             max_size = ngx_parse_offset(&s);
  1604.             if (max_size < 0) {
  1605.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1606.                                    "invalid max_size value \"%V\"", &value[i]);
  1607.                 return NGX_CONF_ERROR;
  1608.             }

  1609.             continue;
  1610.         }

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

  1612.             loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
  1613.             if (loader_files == NGX_ERROR) {
  1614.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1615.                            "invalid loader_files value \"%V\"", &value[i]);
  1616.                 return NGX_CONF_ERROR;
  1617.             }

  1618.             continue;
  1619.         }

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

  1621.             s.len = value[i].len - 13;
  1622.             s.data = value[i].data + 13;

  1623.             loader_sleep = ngx_parse_time(&s, 0);
  1624.             if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
  1625.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1626.                            "invalid loader_sleep value \"%V\"", &value[i]);
  1627.                 return NGX_CONF_ERROR;
  1628.             }

  1629.             continue;
  1630.         }

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

  1632.             s.len = value[i].len - 17;
  1633.             s.data = value[i].data + 17;

  1634.             loader_threshold = ngx_parse_time(&s, 0);
  1635.             if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
  1636.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1637.                            "invalid loader_threshold value \"%V\"", &value[i]);
  1638.                 return NGX_CONF_ERROR;
  1639.             }

  1640.             continue;
  1641.         }

  1642.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1643.                            "invalid parameter \"%V\"", &value[i]);
  1644.         return NGX_CONF_ERROR;
  1645.     }

  1646.     if (name.len == 0 || size == 0) {
  1647.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1648.                            "\"%V\" must have \"keys_zone\" parameter",
  1649.                            &cmd->name);
  1650.         return NGX_CONF_ERROR;
  1651.     }

  1652.     cache->path->manager = ngx_http_file_cache_manager;
  1653.     cache->path->loader = ngx_http_file_cache_loader;
  1654.     cache->path->data = cache;
  1655.     cache->path->conf_file = cf->conf_file->file.name.data;
  1656.     cache->path->line = cf->conf_file->line;
  1657.     cache->loader_files = loader_files;
  1658.     cache->loader_sleep = loader_sleep;
  1659.     cache->loader_threshold = loader_threshold;

  1660.     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
  1661.         return NGX_CONF_ERROR;
  1662.     }

  1663.     if (!use_temp_path) {
  1664.         cache->temp_path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  1665.         if (cache->temp_path == NULL) {
  1666.             return NGX_CONF_ERROR;
  1667.         }

  1668.         len = cache->path->name.len + sizeof("/temp") - 1;

  1669.         p = ngx_pnalloc(cf->pool, len + 1);
  1670.         if (p == NULL) {
  1671.             return NGX_CONF_ERROR;
  1672.         }

  1673.         cache->temp_path->name.len = len;
  1674.         cache->temp_path->name.data = p;

  1675.         p = ngx_cpymem(p, cache->path->name.data, cache->path->name.len);
  1676.         ngx_memcpy(p, "/temp", sizeof("/temp"));

  1677.         ngx_memcpy(&cache->temp_path->level, &cache->path->level,
  1678.                    3 * sizeof(size_t));

  1679.         cache->temp_path->len = cache->path->len;
  1680.         cache->temp_path->conf_file = cf->conf_file->file.name.data;
  1681.         cache->temp_path->line = cf->conf_file->line;

  1682.         if (ngx_add_path(cf, &cache->temp_path) != NGX_OK) {
  1683.             return NGX_CONF_ERROR;
  1684.         }
  1685.     }

  1686.     cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
  1687.     if (cache->shm_zone == NULL) {
  1688.         return NGX_CONF_ERROR;
  1689.     }

  1690.     if (cache->shm_zone->data) {
  1691.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1692.                            "duplicate zone \"%V\"", &name);
  1693.         return NGX_CONF_ERROR;
  1694.     }


  1695.     cache->shm_zone->init = ngx_http_file_cache_init;
  1696.     cache->shm_zone->data = cache;

  1697.     cache->inactive = inactive;
  1698.     cache->max_size = max_size;

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

  1700.     ce = ngx_array_push(caches);
  1701.     if (ce == NULL) {
  1702.         return NGX_CONF_ERROR;
  1703.     }

  1704.     *ce = cache;

  1705.     return NGX_CONF_OK;
  1706. }


  1707. char *
  1708. ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
  1709.     void *conf)
  1710. {
  1711.     char  *p = conf;

  1712.     time_t                    valid;
  1713.     ngx_str_t                *value;
  1714.     ngx_uint_t                i, n, status;
  1715.     ngx_array_t             **a;
  1716.     ngx_http_cache_valid_t   *v;
  1717.     static ngx_uint_t         statuses[] = { 200, 301, 302 };

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

  1719.     if (*a == NGX_CONF_UNSET_PTR) {
  1720.         *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
  1721.         if (*a == NULL) {
  1722.             return NGX_CONF_ERROR;
  1723.         }
  1724.     }

  1725.     value = cf->args->elts;
  1726.     n = cf->args->nelts - 1;

  1727.     valid = ngx_parse_time(&value[n], 1);
  1728.     if (valid == (time_t) NGX_ERROR) {
  1729.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1730.                            "invalid time value \"%V\"", &value[n]);
  1731.         return NGX_CONF_ERROR;
  1732.     }

  1733.     if (n == 1) {

  1734.         for (i = 0; i < 3; i++) {
  1735.             v = ngx_array_push(*a);
  1736.             if (v == NULL) {
  1737.                 return NGX_CONF_ERROR;
  1738.             }

  1739.             v->status = statuses[i];
  1740.             v->valid = valid;
  1741.         }

  1742.         return NGX_CONF_OK;
  1743.     }

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

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

  1746.             status = 0;

  1747.         } else {

  1748.             status = ngx_atoi(value[i].data, value[i].len);
  1749.             if (status < 100) {
  1750.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1751.                                    "invalid status \"%V\"", &value[i]);
  1752.                 return NGX_CONF_ERROR;
  1753.             }
  1754.         }

  1755.         v = ngx_array_push(*a);
  1756.         if (v == NULL) {
  1757.             return NGX_CONF_ERROR;
  1758.         }

  1759.         v->status = status;
  1760.         v->valid = valid;
  1761.     }

  1762.     return NGX_CONF_OK;
  1763. }