Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 #include <ngx_md5.h>
0012 
0013 
0014 static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,
0015     ngx_http_cache_t *c);
0016 static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);
0017 static void ngx_http_file_cache_lock_wait(ngx_http_request_t *r,
0018     ngx_http_cache_t *c);
0019 static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,
0020     ngx_http_cache_t *c);
0021 static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,
0022     ngx_http_cache_t *c);
0023 #if (NGX_HAVE_FILE_AIO)
0024 static void ngx_http_cache_aio_event_handler(ngx_event_t *ev);
0025 #endif
0026 #if (NGX_THREADS)
0027 static ngx_int_t ngx_http_cache_thread_handler(ngx_thread_task_t *task,
0028     ngx_file_t *file);
0029 static void ngx_http_cache_thread_event_handler(ngx_event_t *ev);
0030 #endif
0031 static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
0032     ngx_http_cache_t *c);
0033 static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,
0034     ngx_path_t *path);
0035 static ngx_http_file_cache_node_t *
0036     ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
0037 static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
0038     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
0039 static void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary,
0040     size_t len, u_char *hash);
0041 static void ngx_http_file_cache_vary_header(ngx_http_request_t *r,
0042     ngx_md5_t *md5, ngx_str_t *name);
0043 static ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r,
0044     ngx_http_cache_t *c);
0045 static ngx_int_t ngx_http_file_cache_update_variant(ngx_http_request_t *r,
0046     ngx_http_cache_t *c);
0047 static void ngx_http_file_cache_cleanup(void *data);
0048 static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
0049 static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
0050 static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
0051     ngx_queue_t *q, u_char *name);
0052 static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
0053 static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
0054     ngx_str_t *path);
0055 static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
0056     ngx_str_t *path);
0057 static ngx_int_t ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx,
0058     ngx_str_t *path);
0059 static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
0060     ngx_str_t *path);
0061 static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
0062     ngx_http_cache_t *c);
0063 static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
0064     ngx_str_t *path);
0065 static void ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache);
0066 
0067 
0068 ngx_str_t  ngx_http_cache_status[] = {
0069     ngx_string("MISS"),
0070     ngx_string("BYPASS"),
0071     ngx_string("EXPIRED"),
0072     ngx_string("STALE"),
0073     ngx_string("UPDATING"),
0074     ngx_string("REVALIDATED"),
0075     ngx_string("HIT")
0076 };
0077 
0078 
0079 static u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
0080 
0081 
0082 static ngx_int_t
0083 ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
0084 {
0085     ngx_http_file_cache_t  *ocache = data;
0086 
0087     size_t                  len;
0088     ngx_uint_t              n;
0089     ngx_http_file_cache_t  *cache;
0090 
0091     cache = shm_zone->data;
0092 
0093     if (ocache) {
0094         if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
0095             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
0096                           "cache \"%V\" uses the \"%V\" cache path "
0097                           "while previously it used the \"%V\" cache path",
0098                           &shm_zone->shm.name, &cache->path->name,
0099                           &ocache->path->name);
0100 
0101             return NGX_ERROR;
0102         }
0103 
0104         for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {
0105             if (cache->path->level[n] != ocache->path->level[n]) {
0106                 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
0107                               "cache \"%V\" had previously different levels",
0108                               &shm_zone->shm.name);
0109                 return NGX_ERROR;
0110             }
0111         }
0112 
0113         cache->sh = ocache->sh;
0114 
0115         cache->shpool = ocache->shpool;
0116         cache->bsize = ocache->bsize;
0117 
0118         cache->max_size /= cache->bsize;
0119 
0120         if (!cache->sh->cold || cache->sh->loading) {
0121             cache->path->loader = NULL;
0122         }
0123 
0124         return NGX_OK;
0125     }
0126 
0127     cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
0128 
0129     if (shm_zone->shm.exists) {
0130         cache->sh = cache->shpool->data;
0131         cache->bsize = ngx_fs_bsize(cache->path->name.data);
0132 
0133         return NGX_OK;
0134     }
0135 
0136     cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
0137     if (cache->sh == NULL) {
0138         return NGX_ERROR;
0139     }
0140 
0141     cache->shpool->data = cache->sh;
0142 
0143     ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
0144                     ngx_http_file_cache_rbtree_insert_value);
0145 
0146     ngx_queue_init(&cache->sh->queue);
0147 
0148     cache->sh->cold = 1;
0149     cache->sh->loading = 0;
0150     cache->sh->size = 0;
0151     cache->sh->count = 0;
0152     cache->sh->watermark = (ngx_uint_t) -1;
0153 
0154     cache->bsize = ngx_fs_bsize(cache->path->name.data);
0155 
0156     cache->max_size /= cache->bsize;
0157 
0158     len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
0159 
0160     cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
0161     if (cache->shpool->log_ctx == NULL) {
0162         return NGX_ERROR;
0163     }
0164 
0165     ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
0166                 &shm_zone->shm.name);
0167 
0168     cache->shpool->log_nomem = 0;
0169 
0170     return NGX_OK;
0171 }
0172 
0173 
0174 ngx_int_t
0175 ngx_http_file_cache_new(ngx_http_request_t *r)
0176 {
0177     ngx_http_cache_t  *c;
0178 
0179     c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
0180     if (c == NULL) {
0181         return NGX_ERROR;
0182     }
0183 
0184     if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
0185         return NGX_ERROR;
0186     }
0187 
0188     r->cache = c;
0189     c->file.log = r->connection->log;
0190     c->file.fd = NGX_INVALID_FILE;
0191 
0192     return NGX_OK;
0193 }
0194 
0195 
0196 ngx_int_t
0197 ngx_http_file_cache_create(ngx_http_request_t *r)
0198 {
0199     ngx_http_cache_t       *c;
0200     ngx_pool_cleanup_t     *cln;
0201     ngx_http_file_cache_t  *cache;
0202 
0203     c = r->cache;
0204     cache = c->file_cache;
0205 
0206     cln = ngx_pool_cleanup_add(r->pool, 0);
0207     if (cln == NULL) {
0208         return NGX_ERROR;
0209     }
0210 
0211     cln->handler = ngx_http_file_cache_cleanup;
0212     cln->data = c;
0213 
0214     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
0215         return NGX_ERROR;
0216     }
0217 
0218     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
0219         return NGX_ERROR;
0220     }
0221 
0222     return NGX_OK;
0223 }
0224 
0225 
0226 void
0227 ngx_http_file_cache_create_key(ngx_http_request_t *r)
0228 {
0229     size_t             len;
0230     ngx_str_t         *key;
0231     ngx_uint_t         i;
0232     ngx_md5_t          md5;
0233     ngx_http_cache_t  *c;
0234 
0235     c = r->cache;
0236 
0237     len = 0;
0238 
0239     ngx_crc32_init(c->crc32);
0240     ngx_md5_init(&md5);
0241 
0242     key = c->keys.elts;
0243     for (i = 0; i < c->keys.nelts; i++) {
0244         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0245                        "http cache key: \"%V\"", &key[i]);
0246 
0247         len += key[i].len;
0248 
0249         ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
0250         ngx_md5_update(&md5, key[i].data, key[i].len);
0251     }
0252 
0253     c->header_start = sizeof(ngx_http_file_cache_header_t)
0254                       + sizeof(ngx_http_file_cache_key) + len + 1;
0255 
0256     ngx_crc32_final(c->crc32);
0257     ngx_md5_final(c->key, &md5);
0258 
0259     ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN);
0260 }
0261 
0262 
0263 ngx_int_t
0264 ngx_http_file_cache_open(ngx_http_request_t *r)
0265 {
0266     ngx_int_t                  rc, rv;
0267     ngx_uint_t                 test;
0268     ngx_http_cache_t          *c;
0269     ngx_pool_cleanup_t        *cln;
0270     ngx_open_file_info_t       of;
0271     ngx_http_file_cache_t     *cache;
0272     ngx_http_core_loc_conf_t  *clcf;
0273 
0274     c = r->cache;
0275 
0276     if (c->waiting) {
0277         return NGX_AGAIN;
0278     }
0279 
0280     if (c->reading) {
0281         return ngx_http_file_cache_read(r, c);
0282     }
0283 
0284     cache = c->file_cache;
0285 
0286     if (c->node == NULL) {
0287         cln = ngx_pool_cleanup_add(r->pool, 0);
0288         if (cln == NULL) {
0289             return NGX_ERROR;
0290         }
0291 
0292         cln->handler = ngx_http_file_cache_cleanup;
0293         cln->data = c;
0294     }
0295 
0296     rc = ngx_http_file_cache_exists(cache, c);
0297 
0298     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0299                    "http file cache exists: %i e:%d", rc, c->exists);
0300 
0301     if (rc == NGX_ERROR) {
0302         return rc;
0303     }
0304 
0305     if (rc == NGX_AGAIN) {
0306         return NGX_HTTP_CACHE_SCARCE;
0307     }
0308 
0309     if (rc == NGX_OK) {
0310 
0311         if (c->error) {
0312             return c->error;
0313         }
0314 
0315         c->temp_file = 1;
0316         test = c->exists ? 1 : 0;
0317         rv = NGX_DECLINED;
0318 
0319     } else { /* rc == NGX_DECLINED */
0320 
0321         test = cache->sh->cold ? 1 : 0;
0322 
0323         if (c->min_uses > 1) {
0324 
0325             if (!test) {
0326                 return NGX_HTTP_CACHE_SCARCE;
0327             }
0328 
0329             rv = NGX_HTTP_CACHE_SCARCE;
0330 
0331         } else {
0332             c->temp_file = 1;
0333             rv = NGX_DECLINED;
0334         }
0335     }
0336 
0337     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
0338         return NGX_ERROR;
0339     }
0340 
0341     if (!test) {
0342         goto done;
0343     }
0344 
0345     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0346 
0347     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
0348 
0349     of.uniq = c->uniq;
0350     of.valid = clcf->open_file_cache_valid;
0351     of.min_uses = clcf->open_file_cache_min_uses;
0352     of.events = clcf->open_file_cache_events;
0353     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
0354     of.read_ahead = clcf->read_ahead;
0355 
0356     if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
0357         != NGX_OK)
0358     {
0359         switch (of.err) {
0360 
0361         case 0:
0362             return NGX_ERROR;
0363 
0364         case NGX_ENOENT:
0365         case NGX_ENOTDIR:
0366             goto done;
0367 
0368         default:
0369             ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
0370                           ngx_open_file_n " \"%s\" failed", c->file.name.data);
0371             return NGX_ERROR;
0372         }
0373     }
0374 
0375     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0376                    "http file cache fd: %d", of.fd);
0377 
0378     c->file.fd = of.fd;
0379     c->file.log = r->connection->log;
0380     c->uniq = of.uniq;
0381     c->length = of.size;
0382     c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;
0383 
0384     c->buf = ngx_create_temp_buf(r->pool, c->body_start);
0385     if (c->buf == NULL) {
0386         return NGX_ERROR;
0387     }
0388 
0389     return ngx_http_file_cache_read(r, c);
0390 
0391 done:
0392 
0393     if (rv == NGX_DECLINED) {
0394         return ngx_http_file_cache_lock(r, c);
0395     }
0396 
0397     return rv;
0398 }
0399 
0400 
0401 static ngx_int_t
0402 ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
0403 {
0404     ngx_msec_t                 now, timer;
0405     ngx_http_file_cache_t     *cache;
0406 
0407     if (!c->lock) {
0408         return NGX_DECLINED;
0409     }
0410 
0411     now = ngx_current_msec;
0412 
0413     cache = c->file_cache;
0414 
0415     ngx_shmtx_lock(&cache->shpool->mutex);
0416 
0417     timer = c->node->lock_time - now;
0418 
0419     if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {
0420         c->node->updating = 1;
0421         c->node->lock_time = now + c->lock_age;
0422         c->updating = 1;
0423         c->lock_time = c->node->lock_time;
0424     }
0425 
0426     ngx_shmtx_unlock(&cache->shpool->mutex);
0427 
0428     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0429                    "http file cache lock u:%d wt:%M",
0430                    c->updating, c->wait_time);
0431 
0432     if (c->updating) {
0433         return NGX_DECLINED;
0434     }
0435 
0436     if (c->lock_timeout == 0) {
0437         return NGX_HTTP_CACHE_SCARCE;
0438     }
0439 
0440     c->waiting = 1;
0441 
0442     if (c->wait_time == 0) {
0443         c->wait_time = now + c->lock_timeout;
0444 
0445         c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
0446         c->wait_event.data = r;
0447         c->wait_event.log = r->connection->log;
0448     }
0449 
0450     timer = c->wait_time - now;
0451 
0452     ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
0453 
0454     r->main->blocked++;
0455 
0456     return NGX_AGAIN;
0457 }
0458 
0459 
0460 static void
0461 ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
0462 {
0463     ngx_connection_t    *c;
0464     ngx_http_request_t  *r;
0465 
0466     r = ev->data;
0467     c = r->connection;
0468 
0469     ngx_http_set_log_request(c->log, r);
0470 
0471     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
0472                    "http file cache wait: \"%V?%V\"", &r->uri, &r->args);
0473 
0474     ngx_http_file_cache_lock_wait(r, r->cache);
0475 
0476     ngx_http_run_posted_requests(c);
0477 }
0478 
0479 
0480 static void
0481 ngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)
0482 {
0483     ngx_uint_t              wait;
0484     ngx_msec_t              now, timer;
0485     ngx_http_file_cache_t  *cache;
0486 
0487     now = ngx_current_msec;
0488 
0489     timer = c->wait_time - now;
0490 
0491     if ((ngx_msec_int_t) timer <= 0) {
0492         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
0493                       "cache lock timeout");
0494         c->lock_timeout = 0;
0495         goto wakeup;
0496     }
0497 
0498     cache = c->file_cache;
0499     wait = 0;
0500 
0501     ngx_shmtx_lock(&cache->shpool->mutex);
0502 
0503     timer = c->node->lock_time - now;
0504 
0505     if (c->node->updating && (ngx_msec_int_t) timer > 0) {
0506         wait = 1;
0507     }
0508 
0509     ngx_shmtx_unlock(&cache->shpool->mutex);
0510 
0511     if (wait) {
0512         ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
0513         return;
0514     }
0515 
0516 wakeup:
0517 
0518     c->waiting = 0;
0519     r->main->blocked--;
0520     r->write_event_handler(r);
0521 }
0522 
0523 
0524 static ngx_int_t
0525 ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)
0526 {
0527     u_char                        *p;
0528     time_t                         now;
0529     ssize_t                        n;
0530     ngx_str_t                     *key;
0531     ngx_int_t                      rc;
0532     ngx_uint_t                     i;
0533     ngx_http_file_cache_t         *cache;
0534     ngx_http_file_cache_header_t  *h;
0535 
0536     n = ngx_http_file_cache_aio_read(r, c);
0537 
0538     if (n < 0) {
0539         return n;
0540     }
0541 
0542     if ((size_t) n < c->header_start) {
0543         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
0544                       "cache file \"%s\" is too small", c->file.name.data);
0545         return NGX_DECLINED;
0546     }
0547 
0548     h = (ngx_http_file_cache_header_t *) c->buf->pos;
0549 
0550     if (h->version != NGX_HTTP_CACHE_VERSION) {
0551         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
0552                       "cache file \"%s\" version mismatch", c->file.name.data);
0553         return NGX_DECLINED;
0554     }
0555 
0556     if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {
0557         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
0558                       "cache file \"%s\" has md5 collision", c->file.name.data);
0559         return NGX_DECLINED;
0560     }
0561 
0562     p = c->buf->pos + sizeof(ngx_http_file_cache_header_t)
0563         + sizeof(ngx_http_file_cache_key);
0564 
0565     key = c->keys.elts;
0566     for (i = 0; i < c->keys.nelts; i++) {
0567         if (ngx_memcmp(p, key[i].data, key[i].len) != 0) {
0568             ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
0569                           "cache file \"%s\" has md5 collision",
0570                           c->file.name.data);
0571             return NGX_DECLINED;
0572         }
0573 
0574         p += key[i].len;
0575     }
0576 
0577     if ((size_t) h->body_start > c->body_start) {
0578         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
0579                       "cache file \"%s\" has too long header",
0580                       c->file.name.data);
0581         return NGX_DECLINED;
0582     }
0583 
0584     if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) {
0585         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
0586                       "cache file \"%s\" has incorrect vary length",
0587                       c->file.name.data);
0588         return NGX_DECLINED;
0589     }
0590 
0591     if (h->vary_len) {
0592         ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant);
0593 
0594         if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) {
0595             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0596                            "http file cache vary mismatch");
0597             return ngx_http_file_cache_reopen(r, c);
0598         }
0599     }
0600 
0601     c->buf->last += n;
0602 
0603     c->valid_sec = h->valid_sec;
0604     c->updating_sec = h->updating_sec;
0605     c->error_sec = h->error_sec;
0606     c->last_modified = h->last_modified;
0607     c->date = h->date;
0608     c->valid_msec = h->valid_msec;
0609     c->body_start = h->body_start;
0610     c->etag.len = h->etag_len;
0611     c->etag.data = h->etag;
0612 
0613     r->cached = 1;
0614 
0615     cache = c->file_cache;
0616 
0617     if (cache->sh->cold) {
0618 
0619         ngx_shmtx_lock(&cache->shpool->mutex);
0620 
0621         if (!c->node->exists) {
0622             c->node->uses = 1;
0623             c->node->body_start = c->body_start;
0624             c->node->exists = 1;
0625             c->node->uniq = c->uniq;
0626             c->node->fs_size = c->fs_size;
0627 
0628             cache->sh->size += c->fs_size;
0629         }
0630 
0631         ngx_shmtx_unlock(&cache->shpool->mutex);
0632     }
0633 
0634     now = ngx_time();
0635 
0636     if (c->valid_sec < now) {
0637         c->stale_updating = c->valid_sec + c->updating_sec >= now;
0638         c->stale_error = c->valid_sec + c->error_sec >= now;
0639 
0640         ngx_shmtx_lock(&cache->shpool->mutex);
0641 
0642         if (c->node->updating) {
0643             rc = NGX_HTTP_CACHE_UPDATING;
0644 
0645         } else {
0646             c->node->updating = 1;
0647             c->updating = 1;
0648             c->lock_time = c->node->lock_time;
0649             rc = NGX_HTTP_CACHE_STALE;
0650         }
0651 
0652         ngx_shmtx_unlock(&cache->shpool->mutex);
0653 
0654         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0655                        "http file cache expired: %i %T %T",
0656                        rc, c->valid_sec, now);
0657 
0658         return rc;
0659     }
0660 
0661     return NGX_OK;
0662 }
0663 
0664 
0665 static ssize_t
0666 ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
0667 {
0668 #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
0669     ssize_t                    n;
0670     ngx_http_core_loc_conf_t  *clcf;
0671 
0672     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0673 #endif
0674 
0675 #if (NGX_HAVE_FILE_AIO)
0676 
0677     if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio) {
0678         n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
0679 
0680         if (n != NGX_AGAIN) {
0681             c->reading = 0;
0682             return n;
0683         }
0684 
0685         c->reading = 1;
0686 
0687         c->file.aio->data = r;
0688         c->file.aio->handler = ngx_http_cache_aio_event_handler;
0689 
0690         r->main->blocked++;
0691         r->aio = 1;
0692 
0693         return NGX_AGAIN;
0694     }
0695 
0696 #endif
0697 
0698 #if (NGX_THREADS)
0699 
0700     if (clcf->aio == NGX_HTTP_AIO_THREADS) {
0701         c->file.thread_task = c->thread_task;
0702         c->file.thread_handler = ngx_http_cache_thread_handler;
0703         c->file.thread_ctx = r;
0704 
0705         n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
0706 
0707         c->thread_task = c->file.thread_task;
0708         c->reading = (n == NGX_AGAIN);
0709 
0710         return n;
0711     }
0712 
0713 #endif
0714 
0715     return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
0716 }
0717 
0718 
0719 #if (NGX_HAVE_FILE_AIO)
0720 
0721 static void
0722 ngx_http_cache_aio_event_handler(ngx_event_t *ev)
0723 {
0724     ngx_event_aio_t     *aio;
0725     ngx_connection_t    *c;
0726     ngx_http_request_t  *r;
0727 
0728     aio = ev->data;
0729     r = aio->data;
0730     c = r->connection;
0731 
0732     ngx_http_set_log_request(c->log, r);
0733 
0734     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
0735                    "http file cache aio: \"%V?%V\"", &r->uri, &r->args);
0736 
0737     r->main->blocked--;
0738     r->aio = 0;
0739 
0740     r->write_event_handler(r);
0741 
0742     ngx_http_run_posted_requests(c);
0743 }
0744 
0745 #endif
0746 
0747 
0748 #if (NGX_THREADS)
0749 
0750 static ngx_int_t
0751 ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
0752 {
0753     ngx_str_t                  name;
0754     ngx_thread_pool_t         *tp;
0755     ngx_http_request_t        *r;
0756     ngx_http_core_loc_conf_t  *clcf;
0757 
0758     r = file->thread_ctx;
0759 
0760     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0761     tp = clcf->thread_pool;
0762 
0763     if (tp == NULL) {
0764         if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
0765             != NGX_OK)
0766         {
0767             return NGX_ERROR;
0768         }
0769 
0770         tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);
0771 
0772         if (tp == NULL) {
0773             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0774                           "thread pool \"%V\" not found", &name);
0775             return NGX_ERROR;
0776         }
0777     }
0778 
0779     task->event.data = r;
0780     task->event.handler = ngx_http_cache_thread_event_handler;
0781 
0782     if (ngx_thread_task_post(tp, task) != NGX_OK) {
0783         return NGX_ERROR;
0784     }
0785 
0786     r->main->blocked++;
0787     r->aio = 1;
0788 
0789     return NGX_OK;
0790 }
0791 
0792 
0793 static void
0794 ngx_http_cache_thread_event_handler(ngx_event_t *ev)
0795 {
0796     ngx_connection_t    *c;
0797     ngx_http_request_t  *r;
0798 
0799     r = ev->data;
0800     c = r->connection;
0801 
0802     ngx_http_set_log_request(c->log, r);
0803 
0804     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
0805                    "http file cache thread: \"%V?%V\"", &r->uri, &r->args);
0806 
0807     r->main->blocked--;
0808     r->aio = 0;
0809 
0810     r->write_event_handler(r);
0811 
0812     ngx_http_run_posted_requests(c);
0813 }
0814 
0815 #endif
0816 
0817 
0818 static ngx_int_t
0819 ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
0820 {
0821     ngx_int_t                    rc;
0822     ngx_http_file_cache_node_t  *fcn;
0823 
0824     ngx_shmtx_lock(&cache->shpool->mutex);
0825 
0826     fcn = c->node;
0827 
0828     if (fcn == NULL) {
0829         fcn = ngx_http_file_cache_lookup(cache, c->key);
0830     }
0831 
0832     if (fcn) {
0833         ngx_queue_remove(&fcn->queue);
0834 
0835         if (c->node == NULL) {
0836             fcn->uses++;
0837             fcn->count++;
0838         }
0839 
0840         if (fcn->error) {
0841 
0842             if (fcn->valid_sec < ngx_time()) {
0843                 goto renew;
0844             }
0845 
0846             rc = NGX_OK;
0847 
0848             goto done;
0849         }
0850 
0851         if (fcn->exists || fcn->uses >= c->min_uses) {
0852 
0853             c->exists = fcn->exists;
0854             if (fcn->body_start) {
0855                 c->body_start = fcn->body_start;
0856             }
0857 
0858             rc = NGX_OK;
0859 
0860             goto done;
0861         }
0862 
0863         rc = NGX_AGAIN;
0864 
0865         goto done;
0866     }
0867 
0868     fcn = ngx_slab_calloc_locked(cache->shpool,
0869                                  sizeof(ngx_http_file_cache_node_t));
0870     if (fcn == NULL) {
0871         ngx_http_file_cache_set_watermark(cache);
0872 
0873         ngx_shmtx_unlock(&cache->shpool->mutex);
0874 
0875         (void) ngx_http_file_cache_forced_expire(cache);
0876 
0877         ngx_shmtx_lock(&cache->shpool->mutex);
0878 
0879         fcn = ngx_slab_calloc_locked(cache->shpool,
0880                                      sizeof(ngx_http_file_cache_node_t));
0881         if (fcn == NULL) {
0882             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
0883                           "could not allocate node%s", cache->shpool->log_ctx);
0884             rc = NGX_ERROR;
0885             goto failed;
0886         }
0887     }
0888 
0889     cache->sh->count++;
0890 
0891     ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
0892 
0893     ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
0894                NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
0895 
0896     ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
0897 
0898     fcn->uses = 1;
0899     fcn->count = 1;
0900 
0901 renew:
0902 
0903     rc = NGX_DECLINED;
0904 
0905     fcn->valid_msec = 0;
0906     fcn->error = 0;
0907     fcn->exists = 0;
0908     fcn->valid_sec = 0;
0909     fcn->uniq = 0;
0910     fcn->body_start = 0;
0911     fcn->fs_size = 0;
0912 
0913 done:
0914 
0915     fcn->expire = ngx_time() + cache->inactive;
0916 
0917     ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
0918 
0919     c->uniq = fcn->uniq;
0920     c->error = fcn->error;
0921     c->node = fcn;
0922 
0923 failed:
0924 
0925     ngx_shmtx_unlock(&cache->shpool->mutex);
0926 
0927     return rc;
0928 }
0929 
0930 
0931 static ngx_int_t
0932 ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
0933 {
0934     u_char            *p;
0935     ngx_http_cache_t  *c;
0936 
0937     c = r->cache;
0938 
0939     if (c->file.name.len) {
0940         return NGX_OK;
0941     }
0942 
0943     c->file.name.len = path->name.len + 1 + path->len
0944                        + 2 * NGX_HTTP_CACHE_KEY_LEN;
0945 
0946     c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
0947     if (c->file.name.data == NULL) {
0948         return NGX_ERROR;
0949     }
0950 
0951     ngx_memcpy(c->file.name.data, path->name.data, path->name.len);
0952 
0953     p = c->file.name.data + path->name.len + 1 + path->len;
0954     p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
0955     *p = '\0';
0956 
0957     ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);
0958 
0959     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0960                    "cache file: \"%s\"", c->file.name.data);
0961 
0962     return NGX_OK;
0963 }
0964 
0965 
0966 static ngx_http_file_cache_node_t *
0967 ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
0968 {
0969     ngx_int_t                    rc;
0970     ngx_rbtree_key_t             node_key;
0971     ngx_rbtree_node_t           *node, *sentinel;
0972     ngx_http_file_cache_node_t  *fcn;
0973 
0974     ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));
0975 
0976     node = cache->sh->rbtree.root;
0977     sentinel = cache->sh->rbtree.sentinel;
0978 
0979     while (node != sentinel) {
0980 
0981         if (node_key < node->key) {
0982             node = node->left;
0983             continue;
0984         }
0985 
0986         if (node_key > node->key) {
0987             node = node->right;
0988             continue;
0989         }
0990 
0991         /* node_key == node->key */
0992 
0993         fcn = (ngx_http_file_cache_node_t *) node;
0994 
0995         rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
0996                         NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
0997 
0998         if (rc == 0) {
0999             return fcn;
1000         }
1001 
1002         node = (rc < 0) ? node->left : node->right;
1003     }
1004 
1005     /* not found */
1006 
1007     return NULL;
1008 }
1009 
1010 
1011 static void
1012 ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
1013     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
1014 {
1015     ngx_rbtree_node_t           **p;
1016     ngx_http_file_cache_node_t   *cn, *cnt;
1017 
1018     for ( ;; ) {
1019 
1020         if (node->key < temp->key) {
1021 
1022             p = &temp->left;
1023 
1024         } else if (node->key > temp->key) {
1025 
1026             p = &temp->right;
1027 
1028         } else { /* node->key == temp->key */
1029 
1030             cn = (ngx_http_file_cache_node_t *) node;
1031             cnt = (ngx_http_file_cache_node_t *) temp;
1032 
1033             p = (ngx_memcmp(cn->key, cnt->key,
1034                             NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
1035                  < 0)
1036                     ? &temp->left : &temp->right;
1037         }
1038 
1039         if (*p == sentinel) {
1040             break;
1041         }
1042 
1043         temp = *p;
1044     }
1045 
1046     *p = node;
1047     node->parent = temp;
1048     node->left = sentinel;
1049     node->right = sentinel;
1050     ngx_rbt_red(node);
1051 }
1052 
1053 
1054 static void
1055 ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,
1056     u_char *hash)
1057 {
1058     u_char     *p, *last;
1059     ngx_str_t   name;
1060     ngx_md5_t   md5;
1061     u_char      buf[NGX_HTTP_CACHE_VARY_LEN];
1062 
1063     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1064                    "http file cache vary: \"%*s\"", len, vary);
1065 
1066     ngx_md5_init(&md5);
1067     ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);
1068 
1069     ngx_strlow(buf, vary, len);
1070 
1071     p = buf;
1072     last = buf + len;
1073 
1074     while (p < last) {
1075 
1076         while (p < last && (*p == ' ' || *p == ',')) { p++; }
1077 
1078         name.data = p;
1079 
1080         while (p < last && *p != ',' && *p != ' ') { p++; }
1081 
1082         name.len = p - name.data;
1083 
1084         if (name.len == 0) {
1085             break;
1086         }
1087 
1088         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1089                        "http file cache vary: %V", &name);
1090 
1091         ngx_md5_update(&md5, name.data, name.len);
1092         ngx_md5_update(&md5, (u_char *) ":", sizeof(":") - 1);
1093 
1094         ngx_http_file_cache_vary_header(r, &md5, &name);
1095 
1096         ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);
1097     }
1098 
1099     ngx_md5_final(hash, &md5);
1100 }
1101 
1102 
1103 static void
1104 ngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,
1105     ngx_str_t *name)
1106 {
1107     size_t            len;
1108     u_char           *p, *start, *last;
1109     ngx_uint_t        i, multiple, normalize;
1110     ngx_list_part_t  *part;
1111     ngx_table_elt_t  *header;
1112 
1113     multiple = 0;
1114     normalize = 0;
1115 
1116     if (name->len == sizeof("Accept-Charset") - 1
1117         && ngx_strncasecmp(name->data, (u_char *) "Accept-Charset",
1118                            sizeof("Accept-Charset") - 1) == 0)
1119     {
1120         normalize = 1;
1121 
1122     } else if (name->len == sizeof("Accept-Encoding") - 1
1123         && ngx_strncasecmp(name->data, (u_char *) "Accept-Encoding",
1124                            sizeof("Accept-Encoding") - 1) == 0)
1125     {
1126         normalize = 1;
1127 
1128     } else if (name->len == sizeof("Accept-Language") - 1
1129         && ngx_strncasecmp(name->data, (u_char *) "Accept-Language",
1130                            sizeof("Accept-Language") - 1) == 0)
1131     {
1132         normalize = 1;
1133     }
1134 
1135     part = &r->headers_in.headers.part;
1136     header = part->elts;
1137 
1138     for (i = 0; /* void */ ; i++) {
1139 
1140         if (i >= part->nelts) {
1141             if (part->next == NULL) {
1142                 break;
1143             }
1144 
1145             part = part->next;
1146             header = part->elts;
1147             i = 0;
1148         }
1149 
1150         if (header[i].hash == 0) {
1151             continue;
1152         }
1153 
1154         if (header[i].key.len != name->len) {
1155             continue;
1156         }
1157 
1158         if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {
1159             continue;
1160         }
1161 
1162         if (!normalize) {
1163 
1164             if (multiple) {
1165                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
1166             }
1167 
1168             ngx_md5_update(md5, header[i].value.data, header[i].value.len);
1169 
1170             multiple = 1;
1171 
1172             continue;
1173         }
1174 
1175         /* normalize spaces */
1176 
1177         p = header[i].value.data;
1178         last = p + header[i].value.len;
1179 
1180         while (p < last) {
1181 
1182             while (p < last && (*p == ' ' || *p == ',')) { p++; }
1183 
1184             start = p;
1185 
1186             while (p < last && *p != ',' && *p != ' ') { p++; }
1187 
1188             len = p - start;
1189 
1190             if (len == 0) {
1191                 break;
1192             }
1193 
1194             if (multiple) {
1195                 ngx_md5_update(md5, (u_char *) ",", sizeof(",") - 1);
1196             }
1197 
1198             ngx_md5_update(md5, start, len);
1199 
1200             multiple = 1;
1201         }
1202     }
1203 }
1204 
1205 
1206 static ngx_int_t
1207 ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)
1208 {
1209     ngx_http_file_cache_t  *cache;
1210 
1211     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
1212                    "http file cache reopen");
1213 
1214     if (c->secondary) {
1215         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
1216                       "cache file \"%s\" has incorrect vary hash",
1217                       c->file.name.data);
1218         return NGX_DECLINED;
1219     }
1220 
1221     cache = c->file_cache;
1222 
1223     ngx_shmtx_lock(&cache->shpool->mutex);
1224 
1225     c->node->count--;
1226     c->node = NULL;
1227 
1228     ngx_shmtx_unlock(&cache->shpool->mutex);
1229 
1230     c->secondary = 1;
1231     c->file.name.len = 0;
1232     c->body_start = c->buf->end - c->buf->start;
1233 
1234     ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN);
1235 
1236     return ngx_http_file_cache_open(r);
1237 }
1238 
1239 
1240 ngx_int_t
1241 ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
1242 {
1243     ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;
1244 
1245     u_char            *p;
1246     ngx_str_t         *key;
1247     ngx_uint_t         i;
1248     ngx_http_cache_t  *c;
1249 
1250     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1251                    "http file cache set header");
1252 
1253     c = r->cache;
1254 
1255     ngx_memzero(h, sizeof(ngx_http_file_cache_header_t));
1256 
1257     h->version = NGX_HTTP_CACHE_VERSION;
1258     h->valid_sec = c->valid_sec;
1259     h->updating_sec = c->updating_sec;
1260     h->error_sec = c->error_sec;
1261     h->last_modified = c->last_modified;
1262     h->date = c->date;
1263     h->crc32 = c->crc32;
1264     h->valid_msec = (u_short) c->valid_msec;
1265     h->header_start = (u_short) c->header_start;
1266     h->body_start = (u_short) c->body_start;
1267 
1268     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
1269         h->etag_len = (u_char) c->etag.len;
1270         ngx_memcpy(h->etag, c->etag.data, c->etag.len);
1271     }
1272 
1273     if (c->vary.len) {
1274         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
1275             /* should not happen */
1276             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
1277         }
1278 
1279         h->vary_len = (u_char) c->vary.len;
1280         ngx_memcpy(h->vary, c->vary.data, c->vary.len);
1281 
1282         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
1283         ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
1284     }
1285 
1286     if (ngx_http_file_cache_update_variant(r, c) != NGX_OK) {
1287         return NGX_ERROR;
1288     }
1289 
1290     p = buf + sizeof(ngx_http_file_cache_header_t);
1291 
1292     p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));
1293 
1294     key = c->keys.elts;
1295     for (i = 0; i < c->keys.nelts; i++) {
1296         p = ngx_copy(p, key[i].data, key[i].len);
1297     }
1298 
1299     *p = LF;
1300 
1301     return NGX_OK;
1302 }
1303 
1304 
1305 static ngx_int_t
1306 ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)
1307 {
1308     ngx_http_file_cache_t  *cache;
1309 
1310     if (!c->secondary) {
1311         return NGX_OK;
1312     }
1313 
1314     if (c->vary.len
1315         && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) == 0)
1316     {
1317         return NGX_OK;
1318     }
1319 
1320     /*
1321      * if the variant hash doesn't match one we used as a secondary
1322      * cache key, switch back to the original key
1323      */
1324 
1325     cache = c->file_cache;
1326 
1327     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1328                    "http file cache main key");
1329 
1330     ngx_shmtx_lock(&cache->shpool->mutex);
1331 
1332     c->node->count--;
1333     c->node->updating = 0;
1334     c->node = NULL;
1335 
1336     ngx_shmtx_unlock(&cache->shpool->mutex);
1337 
1338     c->file.name.len = 0;
1339 
1340     ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN);
1341 
1342     if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
1343         return NGX_ERROR;
1344     }
1345 
1346     if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
1347         return NGX_ERROR;
1348     }
1349 
1350     return NGX_OK;
1351 }
1352 
1353 
1354 void
1355 ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
1356 {
1357     off_t                   fs_size;
1358     ngx_int_t               rc;
1359     ngx_file_uniq_t         uniq;
1360     ngx_file_info_t         fi;
1361     ngx_http_cache_t        *c;
1362     ngx_ext_rename_file_t   ext;
1363     ngx_http_file_cache_t  *cache;
1364 
1365     c = r->cache;
1366 
1367     if (c->updated) {
1368         return;
1369     }
1370 
1371     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1372                    "http file cache update");
1373 
1374     cache = c->file_cache;
1375 
1376     c->updated = 1;
1377     c->updating = 0;
1378 
1379     uniq = 0;
1380     fs_size = 0;
1381 
1382     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1383                    "http file cache rename: \"%s\" to \"%s\"",
1384                    tf->file.name.data, c->file.name.data);
1385 
1386     ext.access = NGX_FILE_OWNER_ACCESS;
1387     ext.path_access = NGX_FILE_OWNER_ACCESS;
1388     ext.time = -1;
1389     ext.create_path = 1;
1390     ext.delete_file = 1;
1391     ext.log = r->connection->log;
1392 
1393     rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
1394 
1395     if (rc == NGX_OK) {
1396 
1397         if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
1398             ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
1399                           ngx_fd_info_n " \"%s\" failed", tf->file.name.data);
1400 
1401             rc = NGX_ERROR;
1402 
1403         } else {
1404             uniq = ngx_file_uniq(&fi);
1405             fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
1406         }
1407     }
1408 
1409     ngx_shmtx_lock(&cache->shpool->mutex);
1410 
1411     c->node->count--;
1412     c->node->error = 0;
1413     c->node->uniq = uniq;
1414     c->node->body_start = c->body_start;
1415 
1416     cache->sh->size += fs_size - c->node->fs_size;
1417     c->node->fs_size = fs_size;
1418 
1419     if (rc == NGX_OK) {
1420         c->node->exists = 1;
1421     }
1422 
1423     c->node->updating = 0;
1424 
1425     ngx_shmtx_unlock(&cache->shpool->mutex);
1426 }
1427 
1428 
1429 void
1430 ngx_http_file_cache_update_header(ngx_http_request_t *r)
1431 {
1432     ssize_t                        n;
1433     ngx_err_t                      err;
1434     ngx_file_t                     file;
1435     ngx_file_info_t                fi;
1436     ngx_http_cache_t              *c;
1437     ngx_http_file_cache_header_t   h;
1438 
1439     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1440                    "http file cache update header");
1441 
1442     c = r->cache;
1443 
1444     ngx_memzero(&file, sizeof(ngx_file_t));
1445 
1446     file.name = c->file.name;
1447     file.log = r->connection->log;
1448     file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
1449 
1450     if (file.fd == NGX_INVALID_FILE) {
1451         err = ngx_errno;
1452 
1453         /* cache file may have been deleted */
1454 
1455         if (err == NGX_ENOENT) {
1456             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1457                            "http file cache \"%s\" not found",
1458                            file.name.data);
1459             return;
1460         }
1461 
1462         ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
1463                       ngx_open_file_n " \"%s\" failed", file.name.data);
1464         return;
1465     }
1466 
1467     /*
1468      * make sure cache file wasn't replaced;
1469      * if it was, do nothing
1470      */
1471 
1472     if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
1473         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
1474                       ngx_fd_info_n " \"%s\" failed", file.name.data);
1475         goto done;
1476     }
1477 
1478     if (c->uniq != ngx_file_uniq(&fi)
1479         || c->length != ngx_file_size(&fi))
1480     {
1481         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1482                        "http file cache \"%s\" changed",
1483                        file.name.data);
1484         goto done;
1485     }
1486 
1487     n = ngx_read_file(&file, (u_char *) &h,
1488                       sizeof(ngx_http_file_cache_header_t), 0);
1489 
1490     if (n == NGX_ERROR) {
1491         goto done;
1492     }
1493 
1494     if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {
1495         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
1496                       ngx_read_file_n " read only %z of %z from \"%s\"",
1497                       n, sizeof(ngx_http_file_cache_header_t), file.name.data);
1498         goto done;
1499     }
1500 
1501     if (h.version != NGX_HTTP_CACHE_VERSION
1502         || h.last_modified != c->last_modified
1503         || h.crc32 != c->crc32
1504         || (size_t) h.header_start != c->header_start
1505         || (size_t) h.body_start != c->body_start)
1506     {
1507         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1508                        "http file cache \"%s\" content changed",
1509                        file.name.data);
1510         goto done;
1511     }
1512 
1513     /*
1514      * update cache file header with new data,
1515      * notably h.valid_sec and h.date
1516      */
1517 
1518     ngx_memzero(&h, sizeof(ngx_http_file_cache_header_t));
1519 
1520     h.version = NGX_HTTP_CACHE_VERSION;
1521     h.valid_sec = c->valid_sec;
1522     h.updating_sec = c->updating_sec;
1523     h.error_sec = c->error_sec;
1524     h.last_modified = c->last_modified;
1525     h.date = c->date;
1526     h.crc32 = c->crc32;
1527     h.valid_msec = (u_short) c->valid_msec;
1528     h.header_start = (u_short) c->header_start;
1529     h.body_start = (u_short) c->body_start;
1530 
1531     if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
1532         h.etag_len = (u_char) c->etag.len;
1533         ngx_memcpy(h.etag, c->etag.data, c->etag.len);
1534     }
1535 
1536     if (c->vary.len) {
1537         if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {
1538             /* should not happen */
1539             c->vary.len = NGX_HTTP_CACHE_VARY_LEN;
1540         }
1541 
1542         h.vary_len = (u_char) c->vary.len;
1543         ngx_memcpy(h.vary, c->vary.data, c->vary.len);
1544 
1545         ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);
1546         ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);
1547     }
1548 
1549     (void) ngx_write_file(&file, (u_char *) &h,
1550                           sizeof(ngx_http_file_cache_header_t), 0);
1551 
1552 done:
1553 
1554     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
1555         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
1556                       ngx_close_file_n " \"%s\" failed", file.name.data);
1557     }
1558 }
1559 
1560 
1561 ngx_int_t
1562 ngx_http_cache_send(ngx_http_request_t *r)
1563 {
1564     ngx_int_t          rc;
1565     ngx_buf_t         *b;
1566     ngx_chain_t        out;
1567     ngx_http_cache_t  *c;
1568 
1569     c = r->cache;
1570 
1571     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1572                    "http file cache send: %s", c->file.name.data);
1573 
1574     if (r != r->main && c->length - c->body_start == 0) {
1575         return ngx_http_send_header(r);
1576     }
1577 
1578     /* we need to allocate all before the header would be sent */
1579 
1580     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
1581     if (b == NULL) {
1582         return NGX_HTTP_INTERNAL_SERVER_ERROR;
1583     }
1584 
1585     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
1586     if (b->file == NULL) {
1587         return NGX_HTTP_INTERNAL_SERVER_ERROR;
1588     }
1589 
1590     rc = ngx_http_send_header(r);
1591 
1592     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
1593         return rc;
1594     }
1595 
1596     b->file_pos = c->body_start;
1597     b->file_last = c->length;
1598 
1599     b->in_file = (c->length - c->body_start) ? 1: 0;
1600     b->last_buf = (r == r->main) ? 1: 0;
1601     b->last_in_chain = 1;
1602 
1603     b->file->fd = c->file.fd;
1604     b->file->name = c->file.name;
1605     b->file->log = r->connection->log;
1606 
1607     out.buf = b;
1608     out.next = NULL;
1609 
1610     return ngx_http_output_filter(r, &out);
1611 }
1612 
1613 
1614 void
1615 ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
1616 {
1617     ngx_http_file_cache_t       *cache;
1618     ngx_http_file_cache_node_t  *fcn;
1619 
1620     if (c->updated || c->node == NULL) {
1621         return;
1622     }
1623 
1624     cache = c->file_cache;
1625 
1626     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
1627                    "http file cache free, fd: %d", c->file.fd);
1628 
1629     ngx_shmtx_lock(&cache->shpool->mutex);
1630 
1631     fcn = c->node;
1632     fcn->count--;
1633 
1634     if (c->updating && fcn->lock_time == c->lock_time) {
1635         fcn->updating = 0;
1636     }
1637 
1638     if (c->error) {
1639         fcn->error = c->error;
1640 
1641         if (c->valid_sec) {
1642             fcn->valid_sec = c->valid_sec;
1643             fcn->valid_msec = c->valid_msec;
1644         }
1645 
1646     } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
1647         ngx_queue_remove(&fcn->queue);
1648         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
1649         ngx_slab_free_locked(cache->shpool, fcn);
1650         cache->sh->count--;
1651         c->node = NULL;
1652     }
1653 
1654     ngx_shmtx_unlock(&cache->shpool->mutex);
1655 
1656     c->updated = 1;
1657     c->updating = 0;
1658 
1659     if (c->temp_file) {
1660         if (tf && tf->file.fd != NGX_INVALID_FILE) {
1661             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
1662                            "http file cache incomplete: \"%s\"",
1663                            tf->file.name.data);
1664 
1665             if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
1666                 ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
1667                               ngx_delete_file_n " \"%s\" failed",
1668                               tf->file.name.data);
1669             }
1670         }
1671     }
1672 
1673     if (c->wait_event.timer_set) {
1674         ngx_del_timer(&c->wait_event);
1675     }
1676 }
1677 
1678 
1679 static void
1680 ngx_http_file_cache_cleanup(void *data)
1681 {
1682     ngx_http_cache_t  *c = data;
1683 
1684     if (c->updated) {
1685         return;
1686     }
1687 
1688     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
1689                    "http file cache cleanup");
1690 
1691     if (c->updating && !c->background) {
1692         ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
1693                       "stalled cache updating, error:%ui", c->error);
1694     }
1695 
1696     ngx_http_file_cache_free(c, NULL);
1697 }
1698 
1699 
1700 static time_t
1701 ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
1702 {
1703     u_char                      *name;
1704     size_t                       len;
1705     time_t                       wait;
1706     ngx_uint_t                   tries;
1707     ngx_path_t                  *path;
1708     ngx_queue_t                 *q;
1709     ngx_http_file_cache_node_t  *fcn;
1710 
1711     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1712                    "http file cache forced expire");
1713 
1714     path = cache->path;
1715     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
1716 
1717     name = ngx_alloc(len + 1, ngx_cycle->log);
1718     if (name == NULL) {
1719         return 10;
1720     }
1721 
1722     ngx_memcpy(name, path->name.data, path->name.len);
1723 
1724     wait = 10;
1725     tries = 20;
1726 
1727     ngx_shmtx_lock(&cache->shpool->mutex);
1728 
1729     for (q = ngx_queue_last(&cache->sh->queue);
1730          q != ngx_queue_sentinel(&cache->sh->queue);
1731          q = ngx_queue_prev(q))
1732     {
1733         fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
1734 
1735         ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1736                   "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
1737                   fcn->count, fcn->exists,
1738                   fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
1739 
1740         if (fcn->count == 0) {
1741             ngx_http_file_cache_delete(cache, q, name);
1742             wait = 0;
1743 
1744         } else {
1745             if (--tries) {
1746                 continue;
1747             }
1748 
1749             wait = 1;
1750         }
1751 
1752         break;
1753     }
1754 
1755     ngx_shmtx_unlock(&cache->shpool->mutex);
1756 
1757     ngx_free(name);
1758 
1759     return wait;
1760 }
1761 
1762 
1763 static time_t
1764 ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
1765 {
1766     u_char                      *name, *p;
1767     size_t                       len;
1768     time_t                       now, wait;
1769     ngx_path_t                  *path;
1770     ngx_msec_t                   elapsed;
1771     ngx_queue_t                 *q;
1772     ngx_http_file_cache_node_t  *fcn;
1773     u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];
1774 
1775     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1776                    "http file cache expire");
1777 
1778     path = cache->path;
1779     len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
1780 
1781     name = ngx_alloc(len + 1, ngx_cycle->log);
1782     if (name == NULL) {
1783         return 10;
1784     }
1785 
1786     ngx_memcpy(name, path->name.data, path->name.len);
1787 
1788     now = ngx_time();
1789 
1790     ngx_shmtx_lock(&cache->shpool->mutex);
1791 
1792     for ( ;; ) {
1793 
1794         if (ngx_quit || ngx_terminate) {
1795             wait = 1;
1796             break;
1797         }
1798 
1799         if (ngx_queue_empty(&cache->sh->queue)) {
1800             wait = 10;
1801             break;
1802         }
1803 
1804         q = ngx_queue_last(&cache->sh->queue);
1805 
1806         fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
1807 
1808         wait = fcn->expire - now;
1809 
1810         if (wait > 0) {
1811             wait = wait > 10 ? 10 : wait;
1812             break;
1813         }
1814 
1815         ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1816                        "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
1817                        fcn->count, fcn->exists,
1818                        fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
1819 
1820         if (fcn->count == 0) {
1821             ngx_http_file_cache_delete(cache, q, name);
1822             goto next;
1823         }
1824 
1825         if (fcn->deleting) {
1826             wait = 1;
1827             break;
1828         }
1829 
1830         p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
1831                          sizeof(ngx_rbtree_key_t));
1832         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
1833         (void) ngx_hex_dump(p, fcn->key, len);
1834 
1835         /*
1836          * abnormally exited workers may leave locked cache entries,
1837          * and although it may be safe to remove them completely,
1838          * we prefer to just move them to the top of the inactive queue
1839          */
1840 
1841         ngx_queue_remove(q);
1842         fcn->expire = ngx_time() + cache->inactive;
1843         ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
1844 
1845         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
1846                       "ignore long locked inactive cache entry %*s, count:%d",
1847                       (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
1848 
1849 next:
1850 
1851         if (++cache->files >= cache->manager_files) {
1852             wait = 0;
1853             break;
1854         }
1855 
1856         ngx_time_update();
1857 
1858         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
1859 
1860         if (elapsed >= cache->manager_threshold) {
1861             wait = 0;
1862             break;
1863         }
1864     }
1865 
1866     ngx_shmtx_unlock(&cache->shpool->mutex);
1867 
1868     ngx_free(name);
1869 
1870     return wait;
1871 }
1872 
1873 
1874 static void
1875 ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
1876     u_char *name)
1877 {
1878     u_char                      *p;
1879     size_t                       len;
1880     ngx_path_t                  *path;
1881     ngx_http_file_cache_node_t  *fcn;
1882 
1883     fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
1884 
1885     if (fcn->exists) {
1886         cache->sh->size -= fcn->fs_size;
1887 
1888         path = cache->path;
1889         p = name + path->name.len + 1 + path->len;
1890         p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
1891                          sizeof(ngx_rbtree_key_t));
1892         len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
1893         p = ngx_hex_dump(p, fcn->key, len);
1894         *p = '\0';
1895 
1896         fcn->count++;
1897         fcn->deleting = 1;
1898         ngx_shmtx_unlock(&cache->shpool->mutex);
1899 
1900         len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
1901         ngx_create_hashed_filename(path, name, len);
1902 
1903         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1904                        "http file cache expire: \"%s\"", name);
1905 
1906         if (ngx_delete_file(name) == NGX_FILE_ERROR) {
1907             ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
1908                           ngx_delete_file_n " \"%s\" failed", name);
1909         }
1910 
1911         ngx_shmtx_lock(&cache->shpool->mutex);
1912         fcn->count--;
1913         fcn->deleting = 0;
1914     }
1915 
1916     if (fcn->count == 0) {
1917         ngx_queue_remove(q);
1918         ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
1919         ngx_slab_free_locked(cache->shpool, fcn);
1920         cache->sh->count--;
1921     }
1922 }
1923 
1924 
1925 static ngx_msec_t
1926 ngx_http_file_cache_manager(void *data)
1927 {
1928     ngx_http_file_cache_t  *cache = data;
1929 
1930     off_t       size;
1931     time_t      wait;
1932     ngx_msec_t  elapsed, next;
1933     ngx_uint_t  count, watermark;
1934 
1935     cache->last = ngx_current_msec;
1936     cache->files = 0;
1937 
1938     next = (ngx_msec_t) ngx_http_file_cache_expire(cache) * 1000;
1939 
1940     if (next == 0) {
1941         next = cache->manager_sleep;
1942         goto done;
1943     }
1944 
1945     for ( ;; ) {
1946         ngx_shmtx_lock(&cache->shpool->mutex);
1947 
1948         size = cache->sh->size;
1949         count = cache->sh->count;
1950         watermark = cache->sh->watermark;
1951 
1952         ngx_shmtx_unlock(&cache->shpool->mutex);
1953 
1954         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1955                        "http file cache size: %O c:%ui w:%i",
1956                        size, count, (ngx_int_t) watermark);
1957 
1958         if (size < cache->max_size && count < watermark) {
1959             break;
1960         }
1961 
1962         wait = ngx_http_file_cache_forced_expire(cache);
1963 
1964         if (wait > 0) {
1965             next = (ngx_msec_t) wait * 1000;
1966             break;
1967         }
1968 
1969         if (ngx_quit || ngx_terminate) {
1970             break;
1971         }
1972 
1973         if (++cache->files >= cache->manager_files) {
1974             next = cache->manager_sleep;
1975             break;
1976         }
1977 
1978         ngx_time_update();
1979 
1980         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
1981 
1982         if (elapsed >= cache->manager_threshold) {
1983             next = cache->manager_sleep;
1984             break;
1985         }
1986     }
1987 
1988 done:
1989 
1990     elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
1991 
1992     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1993                    "http file cache manager: %ui e:%M n:%M",
1994                    cache->files, elapsed, next);
1995 
1996     return next;
1997 }
1998 
1999 
2000 static void
2001 ngx_http_file_cache_loader(void *data)
2002 {
2003     ngx_http_file_cache_t  *cache = data;
2004 
2005     ngx_tree_ctx_t  tree;
2006 
2007     if (!cache->sh->cold || cache->sh->loading) {
2008         return;
2009     }
2010 
2011     if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
2012         return;
2013     }
2014 
2015     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
2016                    "http file cache loader");
2017 
2018     tree.init_handler = NULL;
2019     tree.file_handler = ngx_http_file_cache_manage_file;
2020     tree.pre_tree_handler = ngx_http_file_cache_manage_directory;
2021     tree.post_tree_handler = ngx_http_file_cache_noop;
2022     tree.spec_handler = ngx_http_file_cache_delete_file;
2023     tree.data = cache;
2024     tree.alloc = 0;
2025     tree.log = ngx_cycle->log;
2026 
2027     cache->last = ngx_current_msec;
2028     cache->files = 0;
2029 
2030     if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
2031         cache->sh->loading = 0;
2032         return;
2033     }
2034 
2035     cache->sh->cold = 0;
2036     cache->sh->loading = 0;
2037 
2038     ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
2039                   "http file cache: %V %.3fM, bsize: %uz",
2040                   &cache->path->name,
2041                   ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
2042                   cache->bsize);
2043 }
2044 
2045 
2046 static ngx_int_t
2047 ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
2048 {
2049     return NGX_OK;
2050 }
2051 
2052 
2053 static ngx_int_t
2054 ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
2055 {
2056     ngx_msec_t              elapsed;
2057     ngx_http_file_cache_t  *cache;
2058 
2059     cache = ctx->data;
2060 
2061     if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
2062         (void) ngx_http_file_cache_delete_file(ctx, path);
2063     }
2064 
2065     if (++cache->files >= cache->loader_files) {
2066         ngx_http_file_cache_loader_sleep(cache);
2067 
2068     } else {
2069         ngx_time_update();
2070 
2071         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
2072 
2073         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
2074                        "http file cache loader time elapsed: %M", elapsed);
2075 
2076         if (elapsed >= cache->loader_threshold) {
2077             ngx_http_file_cache_loader_sleep(cache);
2078         }
2079     }
2080 
2081     return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
2082 }
2083 
2084 
2085 static ngx_int_t
2086 ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx, ngx_str_t *path)
2087 {
2088     if (path->len >= 5
2089         && ngx_strncmp(path->data + path->len - 5, "/temp", 5) == 0)
2090     {
2091         return NGX_DECLINED;
2092     }
2093 
2094     return NGX_OK;
2095 }
2096 
2097 
2098 static void
2099 ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
2100 {
2101     ngx_msleep(cache->loader_sleep);
2102 
2103     ngx_time_update();
2104 
2105     cache->last = ngx_current_msec;
2106     cache->files = 0;
2107 }
2108 
2109 
2110 static ngx_int_t
2111 ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
2112 {
2113     u_char                 *p;
2114     ngx_int_t               n;
2115     ngx_uint_t              i;
2116     ngx_http_cache_t        c;
2117     ngx_http_file_cache_t  *cache;
2118 
2119     if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
2120         return NGX_ERROR;
2121     }
2122 
2123     /*
2124      * Temporary files in cache have a suffix consisting of a dot
2125      * followed by 10 digits.
2126      */
2127 
2128     if (name->len >= 2 * NGX_HTTP_CACHE_KEY_LEN + 1 + 10
2129         && name->data[name->len - 10 - 1] == '.')
2130     {
2131         return NGX_OK;
2132     }
2133 
2134     if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
2135         ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
2136                       "cache file \"%s\" is too small", name->data);
2137         return NGX_ERROR;
2138     }
2139 
2140     ngx_memzero(&c, sizeof(ngx_http_cache_t));
2141     cache = ctx->data;
2142 
2143     c.length = ctx->size;
2144     c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;
2145 
2146     p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
2147 
2148     for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
2149         n = ngx_hextoi(p, 2);
2150 
2151         if (n == NGX_ERROR) {
2152             return NGX_ERROR;
2153         }
2154 
2155         p += 2;
2156 
2157         c.key[i] = (u_char) n;
2158     }
2159 
2160     return ngx_http_file_cache_add(cache, &c);
2161 }
2162 
2163 
2164 static ngx_int_t
2165 ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
2166 {
2167     ngx_http_file_cache_node_t  *fcn;
2168 
2169     ngx_shmtx_lock(&cache->shpool->mutex);
2170 
2171     fcn = ngx_http_file_cache_lookup(cache, c->key);
2172 
2173     if (fcn == NULL) {
2174 
2175         fcn = ngx_slab_calloc_locked(cache->shpool,
2176                                      sizeof(ngx_http_file_cache_node_t));
2177         if (fcn == NULL) {
2178             ngx_http_file_cache_set_watermark(cache);
2179 
2180             if (cache->fail_time != ngx_time()) {
2181                 cache->fail_time = ngx_time();
2182                 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
2183                            "could not allocate node%s", cache->shpool->log_ctx);
2184             }
2185 
2186             ngx_shmtx_unlock(&cache->shpool->mutex);
2187             return NGX_ERROR;
2188         }
2189 
2190         cache->sh->count++;
2191 
2192         ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
2193 
2194         ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
2195                    NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
2196 
2197         ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
2198 
2199         fcn->uses = 1;
2200         fcn->exists = 1;
2201         fcn->fs_size = c->fs_size;
2202 
2203         cache->sh->size += c->fs_size;
2204 
2205     } else {
2206         ngx_queue_remove(&fcn->queue);
2207     }
2208 
2209     fcn->expire = ngx_time() + cache->inactive;
2210 
2211     ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
2212 
2213     ngx_shmtx_unlock(&cache->shpool->mutex);
2214 
2215     return NGX_OK;
2216 }
2217 
2218 
2219 static ngx_int_t
2220 ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
2221 {
2222     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
2223                    "http file cache delete: \"%s\"", path->data);
2224 
2225     if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
2226         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
2227                       ngx_delete_file_n " \"%s\" failed", path->data);
2228     }
2229 
2230     return NGX_OK;
2231 }
2232 
2233 
2234 static void
2235 ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache)
2236 {
2237     cache->sh->watermark = cache->sh->count - cache->sh->count / 8;
2238 
2239     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
2240                    "http file cache watermark: %ui", cache->sh->watermark);
2241 }
2242 
2243 
2244 time_t
2245 ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
2246 {
2247     ngx_uint_t               i;
2248     ngx_http_cache_valid_t  *valid;
2249 
2250     if (cache_valid == NULL) {
2251         return 0;
2252     }
2253 
2254     valid = cache_valid->elts;
2255     for (i = 0; i < cache_valid->nelts; i++) {
2256 
2257         if (valid[i].status == 0) {
2258             return valid[i].valid;
2259         }
2260 
2261         if (valid[i].status == status) {
2262             return valid[i].valid;
2263         }
2264     }
2265 
2266     return 0;
2267 }
2268 
2269 
2270 char *
2271 ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2272 {
2273     char  *confp = conf;
2274 
2275     off_t                   max_size;
2276     u_char                 *last, *p;
2277     time_t                  inactive;
2278     ssize_t                 size;
2279     ngx_str_t               s, name, *value;
2280     ngx_int_t               loader_files, manager_files;
2281     ngx_msec_t              loader_sleep, manager_sleep, loader_threshold,
2282                             manager_threshold;
2283     ngx_uint_t              i, n, use_temp_path;
2284     ngx_array_t            *caches;
2285     ngx_http_file_cache_t  *cache, **ce;
2286 
2287     cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
2288     if (cache == NULL) {
2289         return NGX_CONF_ERROR;
2290     }
2291 
2292     cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
2293     if (cache->path == NULL) {
2294         return NGX_CONF_ERROR;
2295     }
2296 
2297     use_temp_path = 1;
2298 
2299     inactive = 600;
2300 
2301     loader_files = 100;
2302     loader_sleep = 50;
2303     loader_threshold = 200;
2304 
2305     manager_files = 100;
2306     manager_sleep = 50;
2307     manager_threshold = 200;
2308 
2309     name.len = 0;
2310     size = 0;
2311     max_size = NGX_MAX_OFF_T_VALUE;
2312 
2313     value = cf->args->elts;
2314 
2315     cache->path->name = value[1];
2316 
2317     if (cache->path->name.data[cache->path->name.len - 1] == '/') {
2318         cache->path->name.len--;
2319     }
2320 
2321     if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
2322         return NGX_CONF_ERROR;
2323     }
2324 
2325     for (i = 2; i < cf->args->nelts; i++) {
2326 
2327         if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {
2328 
2329             p = value[i].data + 7;
2330             last = value[i].data + value[i].len;
2331 
2332             for (n = 0; n < NGX_MAX_PATH_LEVEL && p < last; n++) {
2333 
2334                 if (*p > '0' && *p < '3') {
2335 
2336                     cache->path->level[n] = *p++ - '0';
2337                     cache->path->len += cache->path->level[n] + 1;
2338 
2339                     if (p == last) {
2340                         break;
2341                     }
2342 
2343                     if (*p++ == ':' && n < NGX_MAX_PATH_LEVEL - 1 && p < last) {
2344                         continue;
2345                     }
2346 
2347                     goto invalid_levels;
2348                 }
2349 
2350                 goto invalid_levels;
2351             }
2352 
2353             if (cache->path->len < 10 + NGX_MAX_PATH_LEVEL) {
2354                 continue;
2355             }
2356 
2357         invalid_levels:
2358 
2359             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2360                                "invalid \"levels\" \"%V\"", &value[i]);
2361             return NGX_CONF_ERROR;
2362         }
2363 
2364         if (ngx_strncmp(value[i].data, "use_temp_path=", 14) == 0) {
2365 
2366             if (ngx_strcmp(&value[i].data[14], "on") == 0) {
2367                 use_temp_path = 1;
2368 
2369             } else if (ngx_strcmp(&value[i].data[14], "off") == 0) {
2370                 use_temp_path = 0;
2371 
2372             } else {
2373                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2374                                    "invalid use_temp_path value \"%V\", "
2375                                    "it must be \"on\" or \"off\"",
2376                                    &value[i]);
2377                 return NGX_CONF_ERROR;
2378             }
2379 
2380             continue;
2381         }
2382 
2383         if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {
2384 
2385             name.data = value[i].data + 10;
2386 
2387             p = (u_char *) ngx_strchr(name.data, ':');
2388 
2389             if (p) {
2390                 name.len = p - name.data;
2391 
2392                 p++;
2393 
2394                 s.len = value[i].data + value[i].len - p;
2395                 s.data = p;
2396 
2397                 size = ngx_parse_size(&s);
2398                 if (size > 8191) {
2399                     continue;
2400                 }
2401             }
2402 
2403             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2404                                "invalid keys zone size \"%V\"", &value[i]);
2405             return NGX_CONF_ERROR;
2406         }
2407 
2408         if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
2409 
2410             s.len = value[i].len - 9;
2411             s.data = value[i].data + 9;
2412 
2413             inactive = ngx_parse_time(&s, 1);
2414             if (inactive == (time_t) NGX_ERROR) {
2415                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2416                                    "invalid inactive value \"%V\"", &value[i]);
2417                 return NGX_CONF_ERROR;
2418             }
2419 
2420             continue;
2421         }
2422 
2423         if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
2424 
2425             s.len = value[i].len - 9;
2426             s.data = value[i].data + 9;
2427 
2428             max_size = ngx_parse_offset(&s);
2429             if (max_size < 0) {
2430                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2431                                    "invalid max_size value \"%V\"", &value[i]);
2432                 return NGX_CONF_ERROR;
2433             }
2434 
2435             continue;
2436         }
2437 
2438         if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {
2439 
2440             loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
2441             if (loader_files == NGX_ERROR) {
2442                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2443                            "invalid loader_files value \"%V\"", &value[i]);
2444                 return NGX_CONF_ERROR;
2445             }
2446 
2447             continue;
2448         }
2449 
2450         if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {
2451 
2452             s.len = value[i].len - 13;
2453             s.data = value[i].data + 13;
2454 
2455             loader_sleep = ngx_parse_time(&s, 0);
2456             if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
2457                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2458                            "invalid loader_sleep value \"%V\"", &value[i]);
2459                 return NGX_CONF_ERROR;
2460             }
2461 
2462             continue;
2463         }
2464 
2465         if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {
2466 
2467             s.len = value[i].len - 17;
2468             s.data = value[i].data + 17;
2469 
2470             loader_threshold = ngx_parse_time(&s, 0);
2471             if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
2472                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2473                            "invalid loader_threshold value \"%V\"", &value[i]);
2474                 return NGX_CONF_ERROR;
2475             }
2476 
2477             continue;
2478         }
2479 
2480         if (ngx_strncmp(value[i].data, "manager_files=", 14) == 0) {
2481 
2482             manager_files = ngx_atoi(value[i].data + 14, value[i].len - 14);
2483             if (manager_files == NGX_ERROR) {
2484                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2485                            "invalid manager_files value \"%V\"", &value[i]);
2486                 return NGX_CONF_ERROR;
2487             }
2488 
2489             continue;
2490         }
2491 
2492         if (ngx_strncmp(value[i].data, "manager_sleep=", 14) == 0) {
2493 
2494             s.len = value[i].len - 14;
2495             s.data = value[i].data + 14;
2496 
2497             manager_sleep = ngx_parse_time(&s, 0);
2498             if (manager_sleep == (ngx_msec_t) NGX_ERROR) {
2499                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2500                            "invalid manager_sleep value \"%V\"", &value[i]);
2501                 return NGX_CONF_ERROR;
2502             }
2503 
2504             continue;
2505         }
2506 
2507         if (ngx_strncmp(value[i].data, "manager_threshold=", 18) == 0) {
2508 
2509             s.len = value[i].len - 18;
2510             s.data = value[i].data + 18;
2511 
2512             manager_threshold = ngx_parse_time(&s, 0);
2513             if (manager_threshold == (ngx_msec_t) NGX_ERROR) {
2514                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2515                            "invalid manager_threshold value \"%V\"", &value[i]);
2516                 return NGX_CONF_ERROR;
2517             }
2518 
2519             continue;
2520         }
2521 
2522         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2523                            "invalid parameter \"%V\"", &value[i]);
2524         return NGX_CONF_ERROR;
2525     }
2526 
2527     if (name.len == 0 || size == 0) {
2528         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2529                            "\"%V\" must have \"keys_zone\" parameter",
2530                            &cmd->name);
2531         return NGX_CONF_ERROR;
2532     }
2533 
2534     cache->path->manager = ngx_http_file_cache_manager;
2535     cache->path->loader = ngx_http_file_cache_loader;
2536     cache->path->data = cache;
2537     cache->path->conf_file = cf->conf_file->file.name.data;
2538     cache->path->line = cf->conf_file->line;
2539     cache->loader_files = loader_files;
2540     cache->loader_sleep = loader_sleep;
2541     cache->loader_threshold = loader_threshold;
2542     cache->manager_files = manager_files;
2543     cache->manager_sleep = manager_sleep;
2544     cache->manager_threshold = manager_threshold;
2545 
2546     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
2547         return NGX_CONF_ERROR;
2548     }
2549 
2550     cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
2551     if (cache->shm_zone == NULL) {
2552         return NGX_CONF_ERROR;
2553     }
2554 
2555     if (cache->shm_zone->data) {
2556         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2557                            "duplicate zone \"%V\"", &name);
2558         return NGX_CONF_ERROR;
2559     }
2560 
2561 
2562     cache->shm_zone->init = ngx_http_file_cache_init;
2563     cache->shm_zone->data = cache;
2564 
2565     cache->use_temp_path = use_temp_path;
2566 
2567     cache->inactive = inactive;
2568     cache->max_size = max_size;
2569 
2570     caches = (ngx_array_t *) (confp + cmd->offset);
2571 
2572     ce = ngx_array_push(caches);
2573     if (ce == NULL) {
2574         return NGX_CONF_ERROR;
2575     }
2576 
2577     *ce = cache;
2578 
2579     return NGX_CONF_OK;
2580 }
2581 
2582 
2583 char *
2584 ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
2585     void *conf)
2586 {
2587     char  *p = conf;
2588 
2589     time_t                    valid;
2590     ngx_str_t                *value;
2591     ngx_uint_t                i, n, status;
2592     ngx_array_t             **a;
2593     ngx_http_cache_valid_t   *v;
2594     static ngx_uint_t         statuses[] = { 200, 301, 302 };
2595 
2596     a = (ngx_array_t **) (p + cmd->offset);
2597 
2598     if (*a == NGX_CONF_UNSET_PTR) {
2599         *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
2600         if (*a == NULL) {
2601             return NGX_CONF_ERROR;
2602         }
2603     }
2604 
2605     value = cf->args->elts;
2606     n = cf->args->nelts - 1;
2607 
2608     valid = ngx_parse_time(&value[n], 1);
2609     if (valid == (time_t) NGX_ERROR) {
2610         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2611                            "invalid time value \"%V\"", &value[n]);
2612         return NGX_CONF_ERROR;
2613     }
2614 
2615     if (n == 1) {
2616 
2617         for (i = 0; i < 3; i++) {
2618             v = ngx_array_push(*a);
2619             if (v == NULL) {
2620                 return NGX_CONF_ERROR;
2621             }
2622 
2623             v->status = statuses[i];
2624             v->valid = valid;
2625         }
2626 
2627         return NGX_CONF_OK;
2628     }
2629 
2630     for (i = 1; i < n; i++) {
2631 
2632         if (ngx_strcmp(value[i].data, "any") == 0) {
2633 
2634             status = 0;
2635 
2636         } else {
2637 
2638             status = ngx_atoi(value[i].data, value[i].len);
2639             if (status < 100) {
2640                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2641                                    "invalid status \"%V\"", &value[i]);
2642                 return NGX_CONF_ERROR;
2643             }
2644         }
2645 
2646         v = ngx_array_push(*a);
2647         if (v == NULL) {
2648             return NGX_CONF_ERROR;
2649         }
2650 
2651         v->status = status;
2652         v->valid = valid;
2653     }
2654 
2655     return NGX_CONF_OK;
2656 }