blob: 23e3503e0f0cc621e4bd7403de250b593c80cdc6 [file] [log] [blame]
Igor Sysoev4fc368f2003-12-01 16:28:14 +00001
Igor Sysoevd90282d2004-09-28 08:34:51 +00002/*
Igor Sysoevff8da912004-09-29 16:00:49 +00003 * Copyright (C) Igor Sysoev
Igor Sysoevd90282d2004-09-28 08:34:51 +00004 */
5
6
Igor Sysoev4fc368f2003-12-01 16:28:14 +00007#include <ngx_config.h>
8#include <ngx_core.h>
9#include <ngx_http.h>
Igor Sysoev52859f22009-03-23 13:14:51 +000010#include <ngx_md5.h>
Igor Sysoev4fc368f2003-12-01 16:28:14 +000011
12
Igor Sysoev19298ec2009-03-30 07:45:55 +000013static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
14 ngx_http_cache_t *c);
Igor Sysoev52859f22009-03-23 13:14:51 +000015static ngx_http_file_cache_node_t *
16 ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
17static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
18 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
19static void ngx_http_file_cache_cleanup(void *data);
Igor Sysoev19298ec2009-03-30 07:45:55 +000020static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
21static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
22static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
23 ngx_queue_t *q, u_char *name);
24static ngx_int_t
25 ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache);
26static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
Igor Sysoev52859f22009-03-23 13:14:51 +000027 ngx_str_t *path);
Igor Sysoev19298ec2009-03-30 07:45:55 +000028static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
29 ngx_str_t *path);
30static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
31 ngx_str_t *path);
32static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
33 ngx_http_cache_t *c);
34static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
Igor Sysoev52859f22009-03-23 13:14:51 +000035 ngx_str_t *path);
Igor Sysoev4fc368f2003-12-01 16:28:14 +000036
37
Igor Sysoev52859f22009-03-23 13:14:51 +000038static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
39
40
41static ngx_int_t
42ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
Igor Sysoev4fc368f2003-12-01 16:28:14 +000043{
Igor Sysoev52859f22009-03-23 13:14:51 +000044 ngx_http_file_cache_t *ocache = data;
Igor Sysoev4fc368f2003-12-01 16:28:14 +000045
Igor Sysoeva2c8d9a2009-03-27 17:00:42 +000046 size_t len;
Igor Sysoev52859f22009-03-23 13:14:51 +000047 ngx_http_file_cache_t *cache;
Igor Sysoev4fc368f2003-12-01 16:28:14 +000048
Igor Sysoev52859f22009-03-23 13:14:51 +000049 cache = shm_zone->data;
Igor Sysoev4fc368f2003-12-01 16:28:14 +000050
Igor Sysoev52859f22009-03-23 13:14:51 +000051 if (ocache) {
52 if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
53 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
54 "cache \"%V\" uses the \"%V\" cache path "
55 "while previously it used the \"%V\" cache path",
Igor Sysoevc7f876b2009-04-16 19:25:09 +000056 &shm_zone->shm.name, &cache->path->name,
Igor Sysoev52859f22009-03-23 13:14:51 +000057 &ocache->path->name);
Igor Sysoev4fc368f2003-12-01 16:28:14 +000058
59 return NGX_ERROR;
60 }
61
Igor Sysoevf7a08d52009-04-18 19:27:28 +000062 cache->sh = ocache->sh;
63
Igor Sysoev52859f22009-03-23 13:14:51 +000064 cache->shpool = ocache->shpool;
Igor Sysoev19298ec2009-03-30 07:45:55 +000065 cache->bsize = ocache->bsize;
66
67 cache->max_size /= cache->bsize;
Igor Sysoev52859f22009-03-23 13:14:51 +000068
69 return NGX_OK;
70 }
71
72 cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
73
Igor Sysoevf7a08d52009-04-18 19:27:28 +000074 if (shm_zone->shm.exists) {
75 cache->sh = cache->shpool->data;
76 cache->bsize = ngx_fs_bsize(cache->path->name.data);
77
78 return NGX_OK;
79 }
80
81 cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
82 if (cache->sh == NULL) {
Igor Sysoev52859f22009-03-23 13:14:51 +000083 return NGX_ERROR;
84 }
85
Igor Sysoevf7a08d52009-04-18 19:27:28 +000086 cache->shpool->data = cache->sh;
Igor Sysoev52859f22009-03-23 13:14:51 +000087
Igor Sysoevf7a08d52009-04-18 19:27:28 +000088 ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
Igor Sysoev52859f22009-03-23 13:14:51 +000089 ngx_http_file_cache_rbtree_insert_value);
90
Igor Sysoevf7a08d52009-04-18 19:27:28 +000091 ngx_queue_init(&cache->sh->queue);
Igor Sysoev52859f22009-03-23 13:14:51 +000092
Igor Sysoevf7a08d52009-04-18 19:27:28 +000093 cache->sh->cold = 1;
94 cache->sh->size = 0;
Igor Sysoev19298ec2009-03-30 07:45:55 +000095
96 cache->bsize = ngx_fs_bsize(cache->path->name.data);
97
98 cache->max_size /= cache->bsize;
99
Igor Sysoevc7f876b2009-04-16 19:25:09 +0000100 len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
Igor Sysoeva2c8d9a2009-03-27 17:00:42 +0000101
102 cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
103 if (cache->shpool->log_ctx == NULL) {
104 return NGX_ERROR;
105 }
106
107 ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
Igor Sysoevc7f876b2009-04-16 19:25:09 +0000108 &shm_zone->shm.name);
Igor Sysoeva2c8d9a2009-03-27 17:00:42 +0000109
Igor Sysoev52859f22009-03-23 13:14:51 +0000110 return NGX_OK;
111}
112
113
114void
115ngx_http_file_cache_create_key(ngx_http_request_t *r)
116{
117 size_t len;
118 ngx_str_t *key;
119 ngx_uint_t i;
120 ngx_md5_t md5;
121 ngx_http_cache_t *c;
122
123 c = r->cache;
124
125 len = 0;
126
127 ngx_crc32_init(c->crc32);
128 ngx_md5_init(&md5);
129
130 key = c->keys.elts;
131 for (i = 0; i < c->keys.nelts; i++) {
132 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
133 "http cache key: \"%V\"", &key[i]);
134
135 len += key[i].len;
136
137 ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
138 ngx_md5_update(&md5, key[i].data, key[i].len);
139 }
140
141 c->header_start = sizeof(ngx_http_file_cache_header_t)
142 + sizeof(ngx_http_file_cache_key) + len + 1;
143
144 ngx_crc32_final(c->crc32);
145 ngx_md5_final(c->key, &md5);
146}
147
148
149ngx_int_t
150ngx_http_file_cache_open(ngx_http_request_t *r)
151{
152 u_char *p;
153 time_t now;
154 ssize_t n;
155 ngx_int_t rc, rv;
156 ngx_uint_t cold, test;
157 ngx_path_t *path;
158 ngx_http_cache_t *c;
159 ngx_pool_cleanup_t *cln;
160 ngx_open_file_info_t of;
161 ngx_http_file_cache_t *cache;
162 ngx_http_core_loc_conf_t *clcf;
163 ngx_http_file_cache_header_t *h;
164
165 c = r->cache;
166 cache = c->file_cache;
167
168 cln = ngx_pool_cleanup_add(r->pool, 0);
169 if (cln == NULL) {
170 return NGX_ERROR;
171 }
172
Igor Sysoev19298ec2009-03-30 07:45:55 +0000173 rc = ngx_http_file_cache_exists(cache, c);
Igor Sysoev52859f22009-03-23 13:14:51 +0000174
175 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
176 "http file cache exists: %i u:%ui e:%d",
177 rc, c->uses, c->exists);
178
179 if (rc == NGX_ERROR) {
180 return rc;
181 }
182
183 cln->handler = ngx_http_file_cache_cleanup;
184 cln->data = c;
185
186 if (rc == NGX_AGAIN) {
187 return rc;
188 }
189
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000190 cold = cache->sh->cold;
Igor Sysoev52859f22009-03-23 13:14:51 +0000191
192 if (rc == NGX_OK) {
193
194 if (c->error) {
195 return c->error;
196 }
197
198 c->temp_file = 1;
199 test = c->exists ? 1 : 0;
200 rv = NGX_DECLINED;
201
202 } else { /* rc == NGX_DECLINED */
203
204 if (c->min_uses > 1) {
205
206 if (!cold) {
207 return NGX_AGAIN;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000208 }
209
Igor Sysoev52859f22009-03-23 13:14:51 +0000210 test = 1;
211 rv = NGX_AGAIN;
212
213 } else {
214 c->temp_file = 1;
215 test = cold ? 1 : 0;
216 rv = NGX_DECLINED;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000217 }
218 }
219
Igor Sysoev52859f22009-03-23 13:14:51 +0000220 path = cache->path;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000221
Igor Sysoev52859f22009-03-23 13:14:51 +0000222 c->file.name.len = path->name.len + 1 + path->len
223 + 2 * NGX_HTTP_CACHE_KEY_LEN;
224
225 c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
226 if (c->file.name.data == NULL) {
227 return NGX_ERROR;
228 }
229
230 ngx_memcpy(c->file.name.data, path->name.data, path->name.len);
231
232 p = c->file.name.data + path->name.len + 1 + path->len;
233 p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
234 *p = '\0';
235
236 ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);
237
238 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
239 "cache file: \"%s\"", c->file.name.data);
240
241 if (!test) {
242 return NGX_DECLINED;
243 }
244
245 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
246
247 ngx_memzero(&of, sizeof(ngx_open_file_info_t));
248
249 of.uniq = c->uniq;
250 of.valid = clcf->open_file_cache_valid;
251 of.min_uses = clcf->open_file_cache_min_uses;
252 of.events = clcf->open_file_cache_events;
253 of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
254
255 if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
256 != NGX_OK)
257 {
258 switch (of.err) {
259
260 case 0:
261 return NGX_ERROR;
262
263 case NGX_ENOENT:
264 case NGX_ENOTDIR:
265 return rv;
266
267 default:
268 ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
269 ngx_open_file_n " \"%s\" failed", c->file.name.data);
270 return NGX_ERROR;
271 }
272 }
273
274 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
275 "http file cache fd: %d", of.fd);
276
277 c->file.fd = of.fd;
Igor Sysoev3afc10f2009-03-24 15:03:38 +0000278 c->file.log = r->connection->log;
Igor Sysoev52859f22009-03-23 13:14:51 +0000279
280 c->buf = ngx_create_temp_buf(r->pool, c->body_start);
281 if (c->buf == NULL) {
282 return NGX_ERROR;
283 }
284
285 n = ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
286
287 if (n == NGX_ERROR) {
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000288 return n;
289 }
290
Igor Sysoev52859f22009-03-23 13:14:51 +0000291 if ((size_t) n <= c->header_start) {
292 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
Igor Sysoevb1dfe472004-12-21 12:30:30 +0000293 "cache file \"%s\" is too small", c->file.name.data);
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000294 return NGX_ERROR;
295 }
296
Igor Sysoev52859f22009-03-23 13:14:51 +0000297 h = (ngx_http_file_cache_header_t *) c->buf->pos;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000298
Igor Sysoev52859f22009-03-23 13:14:51 +0000299 if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {
300 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
301 "cache file \"%s\" has md5 collision", c->file.name.data);
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000302 return NGX_DECLINED;
303 }
304
Igor Sysoevb1dfe472004-12-21 12:30:30 +0000305 c->buf->last += n;
Igor Sysoev54498db2004-02-11 17:08:49 +0000306
Igor Sysoev52859f22009-03-23 13:14:51 +0000307 c->valid_sec = h->valid_sec;
308 c->last_modified = h->last_modified;
309 c->date = h->date;
310 c->valid_msec = h->valid_msec;
311 c->length = of.size;
312 c->body_start = h->body_start;
313
314 r->cached = 1;
315
316 if (cold) {
317
318 ngx_shmtx_lock(&cache->shpool->mutex);
319
Igor Sysoev19298ec2009-03-30 07:45:55 +0000320 if (!c->node->exists) {
321 c->node->uses = 1;
322 c->node->body_start = c->body_start;
323 c->node->exists = 1;
324
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000325 cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000326 }
Igor Sysoev52859f22009-03-23 13:14:51 +0000327
328 ngx_shmtx_unlock(&cache->shpool->mutex);
329 }
330
Igor Sysoev19298ec2009-03-30 07:45:55 +0000331 now = ngx_time();
332
Igor Sysoev52859f22009-03-23 13:14:51 +0000333 if (c->valid_sec < now) {
334
335 c->uses = c->min_uses;
336
337 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
338 "http file cache expired: %T %T", c->valid_sec, now);
339
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000340 return NGX_HTTP_CACHE_STALE;
341 }
342
343 /* TODO: NGX_HTTP_CACHE_AGED */
344
345 return NGX_OK;
346}
347
348
Igor Sysoev52859f22009-03-23 13:14:51 +0000349static ngx_int_t
Igor Sysoev19298ec2009-03-30 07:45:55 +0000350ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000351{
Igor Sysoev52859f22009-03-23 13:14:51 +0000352 ngx_int_t rc;
353 ngx_http_file_cache_node_t *fcn;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000354
Igor Sysoev52859f22009-03-23 13:14:51 +0000355 ngx_shmtx_lock(&cache->shpool->mutex);
356
Igor Sysoev19298ec2009-03-30 07:45:55 +0000357 fcn = ngx_http_file_cache_lookup(cache, c->key);
Igor Sysoev52859f22009-03-23 13:14:51 +0000358
359 if (fcn) {
360 ngx_queue_remove(&fcn->queue);
361
362 if (fcn->error) {
363
364 if (fcn->valid_sec < ngx_time()) {
365 goto renew;
366 }
367
368 rc = NGX_OK;
369
370 goto done;
371 }
372
373 fcn->uses++;
374 fcn->count++;
375
376 if (fcn->exists) {
377
Igor Sysoev19298ec2009-03-30 07:45:55 +0000378 c->exists = fcn->exists;
379 c->body_start = fcn->body_start;
Igor Sysoev52859f22009-03-23 13:14:51 +0000380
381 rc = NGX_OK;
382
383 goto done;
384 }
385
Igor Sysoev19298ec2009-03-30 07:45:55 +0000386 if (fcn->uses >= c->min_uses) {
Igor Sysoev52859f22009-03-23 13:14:51 +0000387
Igor Sysoev19298ec2009-03-30 07:45:55 +0000388 c->exists = fcn->exists;
389 c->body_start = fcn->body_start;
Igor Sysoev52859f22009-03-23 13:14:51 +0000390
391 rc = NGX_OK;
392
393 } else {
394 rc = NGX_AGAIN;
395 }
396
397 goto done;
398 }
399
400 fcn = ngx_slab_alloc_locked(cache->shpool,
401 sizeof(ngx_http_file_cache_node_t));
402 if (fcn == NULL) {
403 ngx_shmtx_unlock(&cache->shpool->mutex);
404
Igor Sysoevfb5f5042009-04-10 17:45:07 +0000405 (void) ngx_http_file_cache_forced_expire(cache);
Igor Sysoev52859f22009-03-23 13:14:51 +0000406
407 ngx_shmtx_lock(&cache->shpool->mutex);
408
409 fcn = ngx_slab_alloc_locked(cache->shpool,
410 sizeof(ngx_http_file_cache_node_t));
411 if (fcn == NULL) {
412 rc = NGX_ERROR;
413 goto failed;
414 }
415 }
416
Igor Sysoev19298ec2009-03-30 07:45:55 +0000417 ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
Igor Sysoev52859f22009-03-23 13:14:51 +0000418
Igor Sysoev19298ec2009-03-30 07:45:55 +0000419 ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
Igor Sysoev52859f22009-03-23 13:14:51 +0000420 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
421
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000422 ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
Igor Sysoev52859f22009-03-23 13:14:51 +0000423
424renew:
425
426 rc = NGX_DECLINED;
427
428 fcn->uses = 1;
429 fcn->count = 1;
430 fcn->valid_msec = 0;
431 fcn->error = 0;
432 fcn->exists = 0;
433 fcn->valid_sec = 0;
434 fcn->uniq = 0;
435 fcn->body_start = 0;
Igor Sysoev931d3132009-04-01 13:05:38 +0000436 fcn->length = 0;
Igor Sysoev52859f22009-03-23 13:14:51 +0000437
438done:
439
440 fcn->expire = ngx_time() + cache->inactive;
441
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000442 ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
Igor Sysoev52859f22009-03-23 13:14:51 +0000443
Igor Sysoev19298ec2009-03-30 07:45:55 +0000444 c->uniq = fcn->uniq;
445 c->uses = fcn->uses;
446 c->error = fcn->error;
447 c->node = fcn;
Igor Sysoev52859f22009-03-23 13:14:51 +0000448
449failed:
450
451 ngx_shmtx_unlock(&cache->shpool->mutex);
452
453 return rc;
454}
455
456
457static ngx_http_file_cache_node_t *
458ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
459{
460 ngx_int_t rc;
461 ngx_rbtree_key_t node_key;
462 ngx_rbtree_node_t *node, *sentinel;
463 ngx_http_file_cache_node_t *fcn;
464
465 ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));
466
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000467 node = cache->sh->rbtree.root;
468 sentinel = cache->sh->rbtree.sentinel;
Igor Sysoev52859f22009-03-23 13:14:51 +0000469
470 while (node != sentinel) {
471
472 if (node_key < node->key) {
473 node = node->left;
474 continue;
475 }
476
477 if (node_key > node->key) {
478 node = node->right;
479 continue;
480 }
481
482 /* node_key == node->key */
483
484 do {
485 fcn = (ngx_http_file_cache_node_t *) node;
486
487 rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
488 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
489
490 if (rc == 0) {
491 return fcn;
492 }
493
494 node = (rc < 0) ? node->left : node->right;
495
496 } while (node != sentinel && node_key == node->key);
497
498 break;
499 }
500
501 /* not found */
502
503 return NULL;
504}
505
506
507static void
508ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
509 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
510{
511 ngx_rbtree_node_t **p;
512 ngx_http_file_cache_node_t *cn, *cnt;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000513
514 for ( ;; ) {
Igor Sysoev52859f22009-03-23 13:14:51 +0000515
516 if (node->key < temp->key) {
517
518 p = &temp->left;
519
520 } else if (node->key > temp->key) {
521
522 p = &temp->right;
523
524 } else { /* node->key == temp->key */
525
526 cn = (ngx_http_file_cache_node_t *) node;
527 cnt = (ngx_http_file_cache_node_t *) temp;
528
529 p = (ngx_memcmp(cn->key, cnt->key,
530 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
531 < 0)
532 ? &temp->left : &temp->right;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000533 }
534
Igor Sysoev52859f22009-03-23 13:14:51 +0000535 if (*p == sentinel) {
536 break;
537 }
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000538
Igor Sysoev52859f22009-03-23 13:14:51 +0000539 temp = *p;
540 }
541
542 *p = node;
543 node->parent = temp;
544 node->left = sentinel;
545 node->right = sentinel;
546 ngx_rbt_red(node);
547}
548
549
550void
551ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
552{
553 ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf;
554
555 u_char *p;
556 ngx_str_t *key;
557 ngx_uint_t i;
558 ngx_http_cache_t *c;
559
560 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
561 "http file cache set header");
562
563 c = r->cache;
564
565 h->valid_sec = c->valid_sec;
566 h->last_modified = c->last_modified;
567 h->date = c->date;
568 h->crc32 = c->crc32;
569 h->valid_msec = (u_short) c->valid_msec;
570 h->header_start = (u_short) c->header_start;
571 h->body_start = (u_short) c->body_start;
572
573 p = buf + sizeof(ngx_http_file_cache_header_t);
574
575 p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));
576
577 key = c->keys.elts;
578 for (i = 0; i < c->keys.nelts; i++) {
579 p = ngx_copy(p, key[i].data, key[i].len);
580 }
581
582 *p = LF;
583}
584
585
586void
587ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
588{
Igor Sysoev19298ec2009-03-30 07:45:55 +0000589 off_t size;
Igor Sysoev52859f22009-03-23 13:14:51 +0000590 ngx_int_t rc;
591 ngx_file_uniq_t uniq;
592 ngx_file_info_t fi;
593 ngx_http_cache_t *c;
594 ngx_ext_rename_file_t ext;
595 ngx_http_file_cache_t *cache;
596
597 c = r->cache;
598
599 if (c->updated) {
600 return;
601 }
602
603 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
604 "http file cache update");
605
606 c->updated = 1;
607
608 cache = c->file_cache;
609
610 uniq = 0;
611
612 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
613 "http file cache rename: \"%s\" to \"%s\"",
614 tf->file.name.data, c->file.name.data);
615
616 ext.access = NGX_FILE_OWNER_ACCESS;
617 ext.path_access = NGX_FILE_OWNER_ACCESS;
618 ext.time = -1;
619 ext.create_path = 1;
620 ext.delete_file = 1;
621 ext.log_rename_error = 1;
622 ext.log = r->connection->log;
623
624 rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
625
626 if (rc == NGX_OK) {
627
628 if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
629 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
630 ngx_fd_info_n " \"%s\" failed", tf->file.name.data);
631
632 rc = NGX_ERROR;
633
634 } else {
635 uniq = ngx_file_uniq(&fi);
636 }
637 }
638
Igor Sysoev19298ec2009-03-30 07:45:55 +0000639 size = (c->length + cache->bsize - 1) / cache->bsize;
640
Igor Sysoev52859f22009-03-23 13:14:51 +0000641 ngx_shmtx_lock(&cache->shpool->mutex);
642
643 c->node->count--;
644 c->node->uniq = uniq;
645 c->node->body_start = c->body_start;
646
Igor Sysoev19298ec2009-03-30 07:45:55 +0000647 size = size - (c->node->length + cache->bsize - 1) / cache->bsize;
648
649 c->node->length = c->length;
650
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000651 cache->sh->size += size;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000652
Igor Sysoev52859f22009-03-23 13:14:51 +0000653 if (rc == NGX_OK) {
654 c->node->exists = 1;
655 }
656
657 ngx_shmtx_unlock(&cache->shpool->mutex);
658}
659
660
661ngx_int_t
662ngx_http_cache_send(ngx_http_request_t *r)
663{
664 ngx_int_t rc;
665 ngx_buf_t *b;
666 ngx_chain_t out;
667 ngx_http_cache_t *c;
668
669 c = r->cache;
670
671 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
672 "http file cache send: %s", c->file.name.data);
673
674 /* we need to allocate all before the header would be sent */
675
676 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
677 if (b == NULL) {
678 return NGX_HTTP_INTERNAL_SERVER_ERROR;
679 }
680
681 b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
682 if (b->file == NULL) {
683 return NGX_HTTP_INTERNAL_SERVER_ERROR;
684 }
685
686 rc = ngx_http_send_header(r);
687
688 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
689 return rc;
690 }
691
692 b->file_pos = c->body_start;
693 b->file_last = c->length;
694
695 b->in_file = (c->length - c->body_start) ? 1: 0;
696 b->last_buf = (r == r->main) ? 1: 0;
697 b->last_in_chain = 1;
698
699 b->file->fd = c->file.fd;
700 b->file->name = c->file.name;
701 b->file->log = r->connection->log;
702
703 out.buf = b;
704 out.next = NULL;
705
706 return ngx_http_output_filter(r, &out);
707}
708
709
710void
711ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf)
712{
713 ngx_http_cache_t *c;
714 ngx_http_file_cache_t *cache;
715
716 c = r->cache;
717
718 if (c->updated) {
719 return;
720 }
721
722 c->updated = 1;
723
724 cache = c->file_cache;
725
726 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
727 "http file cache free");
728
729 ngx_shmtx_lock(&cache->shpool->mutex);
730
731 c->node->count--;
732
733 if (c->error) {
734 c->node->valid_sec = c->valid_sec;
735 c->node->valid_msec = c->valid_msec;
736 c->node->error = c->error;
737 }
738
739 ngx_shmtx_unlock(&cache->shpool->mutex);
740
741 if (c->temp_file) {
742 if (tf && tf->file.fd != NGX_INVALID_FILE) {
743 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
744 "http file cache incomplete: \"%s\"",
745 tf->file.name.data);
746
747 if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
748 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
749 ngx_delete_file_n " \"%s\" failed",
750 tf->file.name.data);
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000751 }
752 }
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000753 }
754}
755
756
Igor Sysoev52859f22009-03-23 13:14:51 +0000757static void
758ngx_http_file_cache_cleanup(void *data)
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000759{
Igor Sysoev52859f22009-03-23 13:14:51 +0000760 ngx_http_cache_t *c = data;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000761
Igor Sysoev52859f22009-03-23 13:14:51 +0000762 ngx_http_file_cache_t *cache;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000763
Igor Sysoev52859f22009-03-23 13:14:51 +0000764 if (c->updated) {
765 return;
766 }
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000767
Igor Sysoev52859f22009-03-23 13:14:51 +0000768 c->updated = 1;
Igor Sysoevb1dfe472004-12-21 12:30:30 +0000769
Igor Sysoev52859f22009-03-23 13:14:51 +0000770 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
771 "http file cache cleanup");
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000772
Igor Sysoev52859f22009-03-23 13:14:51 +0000773 if (c->error) {
774 return;
775 }
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000776
Igor Sysoev52859f22009-03-23 13:14:51 +0000777 cache = c->file_cache;
Igor Sysoev4fc368f2003-12-01 16:28:14 +0000778
Igor Sysoev52859f22009-03-23 13:14:51 +0000779 ngx_shmtx_lock(&cache->shpool->mutex);
780
781 c->node->count--;
782
783 ngx_shmtx_unlock(&cache->shpool->mutex);
784}
785
786
787static time_t
Igor Sysoev19298ec2009-03-30 07:45:55 +0000788ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
789{
790 u_char *name;
791 size_t len;
792 time_t wait;
793 ngx_uint_t tries;
794 ngx_path_t *path;
795 ngx_queue_t *q;
796 ngx_http_file_cache_node_t *fcn;
797
798 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
799 "http file cache forced expire");
800
801 path = cache->path;
802 len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
803
804 name = ngx_alloc(len + 1, ngx_cycle->log);
805 if (name == NULL) {
Igor Sysoev9ce893b2009-04-10 17:46:25 +0000806 return 10;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000807 }
808
809 ngx_memcpy(name, path->name.data, path->name.len);
810
Igor Sysoev9ce893b2009-04-10 17:46:25 +0000811 wait = 10;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000812 tries = 0;
813
814 ngx_shmtx_lock(&cache->shpool->mutex);
815
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000816 for (q = ngx_queue_last(&cache->sh->queue);
817 q != ngx_queue_sentinel(&cache->sh->queue);
Igor Sysoev19298ec2009-03-30 07:45:55 +0000818 q = ngx_queue_prev(q))
819 {
820 fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
821
822 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
823 "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
824 fcn->count, fcn->exists,
825 fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
826
827 if (fcn->count) {
828
829 if (tries++ < 20) {
830 continue;
831 }
832
833 wait = 1;
834
835 break;
836 }
837
838 if (!fcn->exists) {
839
840 ngx_queue_remove(q);
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000841 ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
Igor Sysoev19298ec2009-03-30 07:45:55 +0000842 ngx_slab_free_locked(cache->shpool, fcn);
843
844 break;
845 }
846
847 ngx_http_file_cache_delete(cache, q, name);
848
849 break;
850 }
851
852 ngx_shmtx_unlock(&cache->shpool->mutex);
853
854 ngx_free(name);
855
856 return wait;
857}
858
859
860static time_t
861ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
Igor Sysoev52859f22009-03-23 13:14:51 +0000862{
863 u_char *name, *p;
864 size_t len;
865 time_t now, wait;
Igor Sysoev52859f22009-03-23 13:14:51 +0000866 ngx_path_t *path;
867 ngx_queue_t *q;
868 ngx_http_file_cache_node_t *fcn;
869 u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
870
871 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
872 "http file cache expire");
873
874 path = cache->path;
875 len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
876
Igor Sysoev19298ec2009-03-30 07:45:55 +0000877 name = ngx_alloc(len + 1, ngx_cycle->log);
878 if (name == NULL) {
Igor Sysoev9ce893b2009-04-10 17:46:25 +0000879 return 10;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000880 }
881
882 ngx_memcpy(name, path->name.data, path->name.len);
883
Igor Sysoev52859f22009-03-23 13:14:51 +0000884 now = ngx_time();
885
886 ngx_shmtx_lock(&cache->shpool->mutex);
887
Igor Sysoev52859f22009-03-23 13:14:51 +0000888 for ( ;; ) {
889
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000890 if (ngx_queue_empty(&cache->sh->queue)) {
Igor Sysoev9ce893b2009-04-10 17:46:25 +0000891 wait = 10;
Igor Sysoev52859f22009-03-23 13:14:51 +0000892 break;
893 }
894
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000895 q = ngx_queue_last(&cache->sh->queue);
Igor Sysoev52859f22009-03-23 13:14:51 +0000896
897 fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
898
Igor Sysoev19298ec2009-03-30 07:45:55 +0000899 wait = fcn->expire - now;
Igor Sysoev52859f22009-03-23 13:14:51 +0000900
Igor Sysoev19298ec2009-03-30 07:45:55 +0000901 if (wait > 0) {
Igor Sysoev9ce893b2009-04-10 17:46:25 +0000902 wait = wait > 10 ? 10 : wait;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000903 break;
Igor Sysoev52859f22009-03-23 13:14:51 +0000904 }
905
906 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
907 "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
908 fcn->count, fcn->exists,
909 fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
910
911 if (fcn->count) {
912
Igor Sysoev19298ec2009-03-30 07:45:55 +0000913 p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
914 sizeof(ngx_rbtree_key_t));
Igor Sysoev52859f22009-03-23 13:14:51 +0000915
Igor Sysoev19298ec2009-03-30 07:45:55 +0000916 len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
917 (void) ngx_hex_dump(p, fcn->key, len);
Igor Sysoev52859f22009-03-23 13:14:51 +0000918
Igor Sysoev19298ec2009-03-30 07:45:55 +0000919 /*
920 * abnormally exited workers may leave locked cache entries,
921 * and although it may be safe to remove them completely,
922 * we prefer to remove them from inactive queue and rbtree
923 * only, and to allow other leaks
924 */
Igor Sysoev52859f22009-03-23 13:14:51 +0000925
Igor Sysoev19298ec2009-03-30 07:45:55 +0000926 ngx_queue_remove(q);
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000927 ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
Igor Sysoev52859f22009-03-23 13:14:51 +0000928
Igor Sysoev19298ec2009-03-30 07:45:55 +0000929 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
Igor Sysoev2f1a2f32009-03-26 09:50:04 +0000930 "ignore long locked inactive cache entry %*s, count:%d",
931 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
Igor Sysoev52859f22009-03-23 13:14:51 +0000932
Igor Sysoev19298ec2009-03-30 07:45:55 +0000933 continue;
Igor Sysoev52859f22009-03-23 13:14:51 +0000934 }
935
Igor Sysoev52859f22009-03-23 13:14:51 +0000936 if (!fcn->exists) {
937
938 ngx_queue_remove(q);
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000939 ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
Igor Sysoev52859f22009-03-23 13:14:51 +0000940 ngx_slab_free_locked(cache->shpool, fcn);
941
942 continue;
943 }
944
Igor Sysoev19298ec2009-03-30 07:45:55 +0000945 ngx_http_file_cache_delete(cache, q, name);
Igor Sysoev52859f22009-03-23 13:14:51 +0000946 }
947
948 ngx_shmtx_unlock(&cache->shpool->mutex);
949
Igor Sysoev19298ec2009-03-30 07:45:55 +0000950 ngx_free(name);
951
Igor Sysoev52859f22009-03-23 13:14:51 +0000952 return wait;
953}
954
955
Igor Sysoev19298ec2009-03-30 07:45:55 +0000956static void
957ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
958 u_char *name)
959{
960 u_char *p;
961 size_t len;
962 ngx_path_t *path;
963 ngx_http_file_cache_node_t *fcn;
964
965 fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
966
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000967 cache->sh->size -= (fcn->length + cache->bsize - 1) / cache->bsize;
Igor Sysoev19298ec2009-03-30 07:45:55 +0000968
969 path = cache->path;
970
971 p = name + path->name.len + 1 + path->len;
972
973 p = ngx_hex_dump(p, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t));
974
975 len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
976 p = ngx_hex_dump(p, fcn->key, len);
977 *p = '\0';
978
979 ngx_queue_remove(q);
980
Igor Sysoevf7a08d52009-04-18 19:27:28 +0000981 ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
Igor Sysoev19298ec2009-03-30 07:45:55 +0000982
983 ngx_slab_free_locked(cache->shpool, fcn);
984
985 ngx_shmtx_unlock(&cache->shpool->mutex);
986
987 len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
988
989 ngx_create_hashed_filename(path, name, len);
990
991 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
992 "http file cache expire: \"%s\"", name);
993
994 if (ngx_delete_file(name) == NGX_FILE_ERROR) {
995 ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
996 ngx_delete_file_n " \"%s\" failed", name);
997 }
998
999 ngx_shmtx_lock(&cache->shpool->mutex);
1000}
1001
1002
1003static time_t
1004ngx_http_file_cache_manager(void *data)
Igor Sysoev52859f22009-03-23 13:14:51 +00001005{
1006 ngx_http_file_cache_t *cache = data;
1007
Igor Sysoev19298ec2009-03-30 07:45:55 +00001008 off_t size;
1009 time_t next;
Igor Sysoev52859f22009-03-23 13:14:51 +00001010 ngx_tree_ctx_t tree;
1011
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001012 if (cache->sh->cold) {
Igor Sysoev52859f22009-03-23 13:14:51 +00001013
Igor Sysoev19298ec2009-03-30 07:45:55 +00001014 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1015 "http file cache manager update");
Igor Sysoev52859f22009-03-23 13:14:51 +00001016
1017 tree.init_handler = NULL;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001018 tree.file_handler = ngx_http_file_cache_manage_file;
1019 tree.pre_tree_handler = ngx_http_file_cache_noop;
1020 tree.post_tree_handler = ngx_http_file_cache_noop;
1021 tree.spec_handler = ngx_http_file_cache_delete_file;
Igor Sysoev52859f22009-03-23 13:14:51 +00001022 tree.data = cache;
1023 tree.alloc = 0;
1024 tree.log = ngx_cycle->log;
1025
Igor Sysoev19298ec2009-03-30 07:45:55 +00001026 cache->last = ngx_current_msec;
1027 cache->files = 0;
Igor Sysoev52859f22009-03-23 13:14:51 +00001028
Igor Sysoev19298ec2009-03-30 07:45:55 +00001029 if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
Igor Sysoev9ce893b2009-04-10 17:46:25 +00001030 return 10;
Igor Sysoev52859f22009-03-23 13:14:51 +00001031 }
1032
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001033 cache->sh->cold = 0;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001034
1035 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
1036 "http file cache: %V %.3fM, bsize: %uz",
1037 &cache->path->name,
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001038 ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
Igor Sysoev19298ec2009-03-30 07:45:55 +00001039 cache->bsize);
Igor Sysoev52859f22009-03-23 13:14:51 +00001040 }
1041
Igor Sysoev19298ec2009-03-30 07:45:55 +00001042 next = ngx_http_file_cache_expire(cache);
Igor Sysoev52859f22009-03-23 13:14:51 +00001043
Igor Sysoev19298ec2009-03-30 07:45:55 +00001044 cache->last = ngx_current_msec;
1045 cache->files = 0;
Igor Sysoev52859f22009-03-23 13:14:51 +00001046
Igor Sysoev19298ec2009-03-30 07:45:55 +00001047 for ( ;; ) {
1048 ngx_shmtx_lock(&cache->shpool->mutex);
1049
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001050 size = cache->sh->size;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001051
1052 ngx_shmtx_unlock(&cache->shpool->mutex);
1053
Igor Sysoeva697c8e2009-04-01 13:01:50 +00001054 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1055 "http file cache size: %O", size);
1056
Igor Sysoev19298ec2009-03-30 07:45:55 +00001057 if (size < cache->max_size) {
1058 return next;
1059 }
1060
1061 next = ngx_http_file_cache_forced_expire(cache);
1062
1063 if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) {
1064 return next;
1065 }
1066 }
Igor Sysoev52859f22009-03-23 13:14:51 +00001067}
1068
1069
1070static ngx_int_t
Igor Sysoev19298ec2009-03-30 07:45:55 +00001071ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
1072{
1073 ngx_msec_t elapsed;
1074
1075 if (cache->files++ > 100) {
1076
1077 ngx_time_update(0, 0);
1078
1079 elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
1080
1081 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1082 "http file cache manager time: %M", elapsed);
1083
1084 if (elapsed > 200) {
1085
1086 /*
1087 * if processing 100 files takes more than 200ms,
1088 * it seems that many operations require disk i/o,
1089 * therefore sleep 200ms
1090 */
1091
1092 ngx_msleep(200);
1093
1094 ngx_time_update(0, 0);
1095 }
1096
1097 cache->last = ngx_current_msec;
1098 cache->files = 0;
1099 }
1100
1101 return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
1102}
1103
1104
1105static ngx_int_t
1106ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
Igor Sysoev52859f22009-03-23 13:14:51 +00001107{
1108 return NGX_OK;
1109}
1110
1111
1112static ngx_int_t
Igor Sysoev19298ec2009-03-30 07:45:55 +00001113ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
Igor Sysoev52859f22009-03-23 13:14:51 +00001114{
Igor Sysoev19298ec2009-03-30 07:45:55 +00001115 ngx_http_file_cache_t *cache;
Igor Sysoev52859f22009-03-23 13:14:51 +00001116
Igor Sysoev19298ec2009-03-30 07:45:55 +00001117 cache = ctx->data;
1118
1119 if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
1120 (void) ngx_http_file_cache_delete_file(ctx, path);
Igor Sysoev52859f22009-03-23 13:14:51 +00001121 }
1122
Igor Sysoev19298ec2009-03-30 07:45:55 +00001123 return ngx_http_file_cache_manager_sleep(cache);
1124}
1125
1126
1127static ngx_int_t
1128ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
1129{
1130 u_char *p;
1131 ngx_fd_t fd;
1132 ngx_int_t n;
1133 ngx_uint_t i;
1134 ngx_file_info_t fi;
1135 ngx_http_cache_t c;
1136 ngx_http_file_cache_t *cache;
1137 ngx_http_file_cache_header_t h;
1138
1139 if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
1140 return NGX_ERROR;
1141 }
1142
1143 ngx_memzero(&c, sizeof(ngx_http_cache_t));
1144
1145 fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
1146
1147 if (fd == NGX_INVALID_FILE) {
1148 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
1149 ngx_open_file_n " \"%s\" failed", name->data);
1150 return NGX_ERROR;
1151 }
1152
1153 c.file.fd = fd;
1154 c.file.name = *name;
1155 c.file.log = ctx->log;
1156
1157 n = ngx_read_file(&c.file, (u_char *) &h,
1158 sizeof(ngx_http_file_cache_header_t), 0);
1159 if (n == NGX_ERROR) {
1160 return NGX_ERROR;
1161 }
1162
1163 if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) {
1164 ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
1165 "cache file \"%s\" is too small", name->data);
1166 return NGX_ERROR;
1167 }
1168
1169 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
1170 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
1171 ngx_fd_info_n " \"%s\" failed", name->data);
1172
1173 } else {
1174 c.uniq = ngx_file_uniq(&fi);
1175 c.valid_sec = h.valid_sec;
1176 c.valid_msec = h.valid_msec;
1177 c.body_start = h.body_start;
1178 c.length = ngx_file_size(&fi);
1179 }
1180
1181 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
1182 ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
1183 ngx_close_file_n " \"%s\" failed", name->data);
1184 }
1185
1186 if (c.body_start == 0) {
1187 return NGX_ERROR;
1188 }
1189
1190 p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
Igor Sysoev52859f22009-03-23 13:14:51 +00001191
1192 for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
1193 n = ngx_hextoi(p, 2);
1194
1195 if (n == NGX_ERROR) {
Igor Sysoev19298ec2009-03-30 07:45:55 +00001196 return NGX_ERROR;
Igor Sysoev52859f22009-03-23 13:14:51 +00001197 }
1198
1199 p += 2;
1200
Igor Sysoev19298ec2009-03-30 07:45:55 +00001201 c.key[i] = (u_char) n;
Igor Sysoev52859f22009-03-23 13:14:51 +00001202 }
1203
1204 cache = ctx->data;
1205
Igor Sysoev19298ec2009-03-30 07:45:55 +00001206 return ngx_http_file_cache_add(cache, &c);
1207}
1208
1209
1210static ngx_int_t
1211ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
1212{
1213 ngx_http_file_cache_node_t *fcn;
1214
Igor Sysoev52859f22009-03-23 13:14:51 +00001215 ngx_shmtx_lock(&cache->shpool->mutex);
1216
Igor Sysoev19298ec2009-03-30 07:45:55 +00001217 fcn = ngx_http_file_cache_lookup(cache, c->key);
1218
1219 if (fcn == NULL) {
1220
1221 fcn = ngx_slab_alloc_locked(cache->shpool,
1222 sizeof(ngx_http_file_cache_node_t));
1223 if (fcn == NULL) {
1224 ngx_shmtx_unlock(&cache->shpool->mutex);
1225 return NGX_ERROR;
1226 }
1227
1228 ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
1229
1230 ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
1231 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
1232
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001233 ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
Igor Sysoev19298ec2009-03-30 07:45:55 +00001234
1235 fcn->uses = 1;
1236 fcn->count = 0;
1237 fcn->valid_msec = c->valid_msec;
1238 fcn->error = 0;
1239 fcn->exists = 1;
1240 fcn->uniq = c->uniq;
1241 fcn->valid_sec = c->valid_sec;
1242 fcn->body_start = c->body_start;
1243 fcn->length = c->length;
1244
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001245 cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001246
1247 } else {
1248 ngx_queue_remove(&fcn->queue);
1249 }
1250
1251 fcn->expire = ngx_time() + cache->inactive;
1252
Igor Sysoevf7a08d52009-04-18 19:27:28 +00001253 ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
Igor Sysoev52859f22009-03-23 13:14:51 +00001254
1255 ngx_shmtx_unlock(&cache->shpool->mutex);
1256
Igor Sysoev19298ec2009-03-30 07:45:55 +00001257 return NGX_OK;
1258}
Igor Sysoev4fc368f2003-12-01 16:28:14 +00001259
Igor Sysoev52859f22009-03-23 13:14:51 +00001260
Igor Sysoev19298ec2009-03-30 07:45:55 +00001261static ngx_int_t
1262ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
1263{
Igor Sysoev52859f22009-03-23 13:14:51 +00001264 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
Igor Sysoev19298ec2009-03-30 07:45:55 +00001265 "http file cache delete: \"%s\"", path->data);
Igor Sysoev52859f22009-03-23 13:14:51 +00001266
1267 if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
1268 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
1269 ngx_delete_file_n " \"%s\" failed", path->data);
Igor Sysoev4fc368f2003-12-01 16:28:14 +00001270 }
1271
Igor Sysoev4fc368f2003-12-01 16:28:14 +00001272 return NGX_OK;
1273}
Igor Sysoev52859f22009-03-23 13:14:51 +00001274
1275
1276time_t
1277ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
1278{
1279 ngx_uint_t i;
1280 ngx_http_cache_valid_t *valid;
1281
Igor Sysoev91db11b2009-03-30 14:23:07 +00001282 if (cache_valid == NULL) {
1283 return 0;
1284 }
1285
Igor Sysoev52859f22009-03-23 13:14:51 +00001286 valid = cache_valid->elts;
1287 for (i = 0; i < cache_valid->nelts; i++) {
1288
1289 if (valid[i].status == 0) {
1290 return valid[i].valid;
1291 }
1292
1293 if (valid[i].status == status) {
1294 return valid[i].valid;
1295 }
1296 }
1297
1298 return 0;
1299}
1300
1301
1302char *
1303ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1304{
Igor Sysoev19298ec2009-03-30 07:45:55 +00001305 off_t max_size;
Igor Sysoev52859f22009-03-23 13:14:51 +00001306 u_char *last, *p;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001307 time_t inactive;
Igor Sysoev52859f22009-03-23 13:14:51 +00001308 ssize_t size;
1309 ngx_str_t s, name, *value;
1310 ngx_uint_t i, n;
1311 ngx_http_file_cache_t *cache;
1312
1313 cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
1314 if (cache == NULL) {
1315 return NGX_CONF_ERROR;
1316 }
1317
1318 cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
1319 if (cache->path == NULL) {
1320 return NGX_CONF_ERROR;
1321 }
1322
1323 inactive = 600;
Igor Sysoev52859f22009-03-23 13:14:51 +00001324
1325 name.len = 0;
1326 size = 0;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001327 max_size = NGX_MAX_OFF_T_VALUE;
Igor Sysoev52859f22009-03-23 13:14:51 +00001328
1329 value = cf->args->elts;
1330
1331 cache->path->name = value[1];
1332
1333 if (cache->path->name.data[cache->path->name.len - 1] == '/') {
1334 cache->path->name.len--;
1335 }
1336
1337 if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
1338 return NGX_CONF_ERROR;
1339 }
1340
1341 for (i = 2; i < cf->args->nelts; i++) {
1342
1343 if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {
1344
Igor Sysoev52859f22009-03-23 13:14:51 +00001345 p = value[i].data + 7;
1346 last = value[i].data + value[i].len;
1347
Igor Sysoev43f3f4a2009-04-06 08:35:34 +00001348 for (n = 0; n < 3 && p < last; n++) {
Igor Sysoev52859f22009-03-23 13:14:51 +00001349
Igor Sysoev43f3f4a2009-04-06 08:35:34 +00001350 if (*p > '0' && *p < '3') {
Igor Sysoev52859f22009-03-23 13:14:51 +00001351
1352 cache->path->level[n] = *p++ - '0';
1353 cache->path->len += cache->path->level[n] + 1;
1354
1355 if (p == last) {
1356 break;
1357 }
1358
Igor Sysoev43f3f4a2009-04-06 08:35:34 +00001359 if (*p++ == ':' && n < 2 && p != last) {
Igor Sysoev52859f22009-03-23 13:14:51 +00001360 continue;
1361 }
Igor Sysoev43f3f4a2009-04-06 08:35:34 +00001362
1363 goto invalid_levels;
Igor Sysoev52859f22009-03-23 13:14:51 +00001364 }
1365
1366 goto invalid_levels;
1367 }
1368
1369 if (cache->path->len < 10 + 3) {
1370 continue;
1371 }
1372
1373 invalid_levels:
1374
1375 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1376 "invalid \"levels\" \"%V\"", &value[i]);
1377 return NGX_CONF_ERROR;
1378 }
1379
1380 if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {
1381
1382 name.data = value[i].data + 10;
1383
1384 p = (u_char *) ngx_strchr(name.data, ':');
1385
1386 if (p) {
Igor Sysoevc7f876b2009-04-16 19:25:09 +00001387 *p = '\0';
1388
Igor Sysoev52859f22009-03-23 13:14:51 +00001389 name.len = p - name.data;
1390
1391 p++;
1392
1393 s.len = value[i].data + value[i].len - p;
1394 s.data = p;
1395
1396 size = ngx_parse_size(&s);
1397 if (size > 8191) {
1398 continue;
1399 }
1400 }
1401
1402 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1403 "invalid keys zone size \"%V\"", &value[i]);
1404 return NGX_CONF_ERROR;
1405 }
1406
1407 if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
1408
1409 s.len = value[i].len - 9;
1410 s.data = value[i].data + 9;
1411
1412 inactive = ngx_parse_time(&s, 1);
1413 if (inactive < 0) {
1414 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1415 "invalid inactive value \"%V\"", &value[i]);
1416 return NGX_CONF_ERROR;
1417 }
1418
1419 continue;
1420 }
1421
Igor Sysoev19298ec2009-03-30 07:45:55 +00001422 if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
Igor Sysoev52859f22009-03-23 13:14:51 +00001423
Igor Sysoev19298ec2009-03-30 07:45:55 +00001424 s.len = value[i].len - 9;
1425 s.data = value[i].data + 9;
Igor Sysoev52859f22009-03-23 13:14:51 +00001426
Igor Sysoev19298ec2009-03-30 07:45:55 +00001427 max_size = ngx_parse_offset(&s);
1428 if (max_size < 0) {
Igor Sysoev52859f22009-03-23 13:14:51 +00001429 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
Igor Sysoev19298ec2009-03-30 07:45:55 +00001430 "invalid max_size value \"%V\"", &value[i]);
Igor Sysoev52859f22009-03-23 13:14:51 +00001431 return NGX_CONF_ERROR;
1432 }
1433
1434 continue;
1435 }
1436
1437 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1438 "invalid parameter \"%V\"", &value[i]);
1439 return NGX_CONF_ERROR;
1440 }
1441
1442 if (name.len == 0 || size == 0) {
1443 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1444 "\"%V\" must have \"keys_zone\" parameter",
1445 &cmd->name);
1446 return NGX_CONF_ERROR;
1447 }
1448
Igor Sysoev19298ec2009-03-30 07:45:55 +00001449 cache->path->manager = ngx_http_file_cache_manager;
Igor Sysoev52859f22009-03-23 13:14:51 +00001450 cache->path->data = cache;
1451
1452 if (ngx_add_path(cf, &cache->path) != NGX_OK) {
1453 return NGX_CONF_ERROR;
1454 }
1455
1456 cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
1457 if (cache->shm_zone == NULL) {
1458 return NGX_CONF_ERROR;
1459 }
1460
1461 if (cache->shm_zone->data) {
1462 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1463 "duplicate zone \"%V\"", &name);
1464 return NGX_CONF_ERROR;
1465 }
1466
Igor Sysoev52859f22009-03-23 13:14:51 +00001467
1468 cache->shm_zone->init = ngx_http_file_cache_init;
1469 cache->shm_zone->data = cache;
1470
1471 cache->inactive = inactive;
Igor Sysoev19298ec2009-03-30 07:45:55 +00001472 cache->max_size = max_size;
Igor Sysoev52859f22009-03-23 13:14:51 +00001473
1474 return NGX_CONF_OK;
1475}
1476
1477
1478char *
1479ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
1480 void *conf)
1481{
1482 char *p = conf;
1483
1484 time_t valid;
1485 ngx_str_t *value;
1486 ngx_uint_t i, n, status;
1487 ngx_array_t **a;
1488 ngx_http_cache_valid_t *v;
1489 static ngx_uint_t statuses[] = { 200, 301, 302 };
1490
1491 a = (ngx_array_t **) (p + cmd->offset);
1492
1493 if (*a == NGX_CONF_UNSET_PTR) {
1494 *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
1495 if (*a == NULL) {
1496 return NGX_CONF_ERROR;
1497 }
1498 }
1499
1500 value = cf->args->elts;
1501 n = cf->args->nelts - 1;
1502
1503 valid = ngx_parse_time(&value[n], 1);
1504 if (valid < 0) {
1505 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1506 "invalid time value \"%V\"", &value[n]);
1507 return NGX_CONF_ERROR;
1508 }
1509
1510 if (n == 1) {
1511
1512 for (i = 0; i < 3; i++) {
1513 v = ngx_array_push(*a);
1514 if (v == NULL) {
1515 return NGX_CONF_ERROR;
1516 }
1517
1518 v->status = statuses[i];
1519 v->valid = valid;
1520 }
1521
1522 return NGX_CONF_OK;
1523 }
1524
1525 for (i = 1; i < n; i++) {
1526
1527 if (ngx_strcmp(value[i].data, "any") == 0) {
1528
1529 status = 0;
1530
1531 } else {
1532
1533 status = ngx_atoi(value[i].data, value[i].len);
1534 if (status < 100) {
1535 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1536 "invalid status \"%V\"", &value[i]);
1537 return NGX_CONF_ERROR;
1538 }
1539 }
1540
1541 v = ngx_array_push(*a);
1542 if (v == NULL) {
1543 return NGX_CONF_ERROR;
1544 }
1545
1546 v->status = status;
1547 v->valid = valid;
1548 }
1549
1550 return NGX_CONF_OK;
1551}