Mercurial > hg > nginx-vendor-current
comparison src/http/ngx_http_file_cache.c @ 464:c8cfb6c462ef NGINX_0_7_44
nginx 0.7.44
*) Feature: the ngx_http_proxy_module preliminary cache support.
*) Feature: the --with-pcre option in the configure.
*) Feature: the "try_files" directive is now allowed on the server
block level.
*) Bugfix: the "try_files" directive handled incorrectly a query string
in a fallback parameter.
*) Bugfix: the "try_files" directive might test incorrectly directories.
*) Bugfix: if there is the single server for given address:port pair,
then captures in regular expressions in a "server_name" directive
did not work.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 23 Mar 2009 00:00:00 +0300 |
parents | 13710a1813ad |
children | 9eda3153223b |
comparison
equal
deleted
inserted
replaced
463:51cb914e6d3a | 464:c8cfb6c462ef |
---|---|
5 | 5 |
6 | 6 |
7 #include <ngx_config.h> | 7 #include <ngx_config.h> |
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_http.h> | 9 #include <ngx_http.h> |
10 | 10 #include <ngx_md5.h> |
11 | 11 |
12 #if (NGX_HAVE_OPENSSL_MD5_H) | 12 |
13 #include <openssl/md5.h> | 13 static ngx_int_t ngx_http_file_cache_exists(ngx_http_request_t *r, |
14 #else | 14 ngx_http_file_cache_t *cache); |
15 #include <md5.h> | 15 static ngx_http_file_cache_node_t * |
16 #endif | 16 ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key); |
17 | 17 static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, |
18 #if (NGX_OPENSSL_MD5) | 18 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); |
19 #define MD5Init MD5_Init | 19 static void ngx_http_file_cache_cleanup(void *data); |
20 #define MD5Update MD5_Update | 20 static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache, |
21 #define MD5Final MD5_Final | 21 ngx_uint_t force); |
22 #endif | 22 static ngx_int_t ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx, |
23 | 23 ngx_str_t *path); |
24 | 24 static ngx_int_t ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx, |
25 ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, | 25 ngx_str_t *path); |
26 ngx_http_cache_ctx_t *ctx) | 26 |
27 { | 27 |
28 static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' }; | |
29 | |
30 | |
31 static ngx_int_t | |
32 ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data) | |
33 { | |
34 ngx_http_file_cache_t *ocache = data; | |
35 | |
36 ngx_rbtree_node_t *sentinel; | |
37 ngx_http_file_cache_t *cache; | |
38 | |
39 cache = shm_zone->data; | |
40 | |
41 if (ocache) { | |
42 if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) { | |
43 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, | |
44 "cache \"%V\" uses the \"%V\" cache path " | |
45 "while previously it used the \"%V\" cache path", | |
46 &shm_zone->name, &cache->path->name, | |
47 &ocache->path->name); | |
48 | |
49 return NGX_ERROR; | |
50 } | |
51 | |
52 cache->rbtree = ocache->rbtree; | |
53 cache->queue = ocache->queue; | |
54 cache->shpool = ocache->shpool; | |
55 cache->created = ocache->created; | |
56 | |
57 return NGX_OK; | |
58 } | |
59 | |
60 cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | |
61 | |
62 cache->rbtree = ngx_slab_alloc(cache->shpool, sizeof(ngx_rbtree_t)); | |
63 if (cache->rbtree == NULL) { | |
64 return NGX_ERROR; | |
65 } | |
66 | |
67 sentinel = ngx_slab_alloc(cache->shpool, sizeof(ngx_rbtree_node_t)); | |
68 if (sentinel == NULL) { | |
69 return NGX_ERROR; | |
70 } | |
71 | |
72 ngx_rbtree_init(cache->rbtree, sentinel, | |
73 ngx_http_file_cache_rbtree_insert_value); | |
74 | |
75 cache->queue = ngx_slab_alloc(cache->shpool, sizeof(ngx_queue_t)); | |
76 if (cache->queue == NULL) { | |
77 return NGX_ERROR; | |
78 } | |
79 | |
80 ngx_queue_init(cache->queue); | |
81 | |
82 cache->created = ngx_time(); | |
83 | |
84 return NGX_OK; | |
85 } | |
86 | |
87 | |
88 void | |
89 ngx_http_file_cache_create_key(ngx_http_request_t *r) | |
90 { | |
91 size_t len; | |
92 ngx_str_t *key; | |
28 ngx_uint_t i; | 93 ngx_uint_t i; |
29 ngx_str_t *key; | 94 ngx_md5_t md5; |
30 ngx_http_cache_t *c; | 95 ngx_http_cache_t *c; |
31 MD5_CTX md5; | |
32 | 96 |
33 c = r->cache; | 97 c = r->cache; |
34 | 98 |
35 c->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32; | 99 len = 0; |
36 if (!(c->file.name.data = ngx_palloc(r->pool, c->file.name.len + 1))) { | 100 |
37 return NGX_ABORT; | 101 ngx_crc32_init(c->crc32); |
38 } | 102 ngx_md5_init(&md5); |
39 | 103 |
40 MD5Init(&md5); | 104 key = c->keys.elts; |
41 | 105 for (i = 0; i < c->keys.nelts; i++) { |
42 key = c->key.elts; | 106 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
43 for (i = 0; i < c->key.nelts; i++) { | 107 "http cache key: \"%V\"", &key[i]); |
44 MD5Update(&md5, key[i].data, key[i].len); | 108 |
45 } | 109 len += key[i].len; |
46 | 110 |
47 MD5Update(&md5, ctx->key.data, ctx->key.len); | 111 ngx_crc32_update(&c->crc32, key[i].data, key[i].len); |
48 | 112 ngx_md5_update(&md5, key[i].data, key[i].len); |
49 MD5Final(c->md5, &md5); | 113 } |
50 | 114 |
51 ngx_memcpy(c->file.name.data, ctx->path->name.data, ctx->path->name.len); | 115 c->header_start = sizeof(ngx_http_file_cache_header_t) |
52 | 116 + sizeof(ngx_http_file_cache_key) + len + 1; |
53 ngx_md5_text(c->file.name.data + ctx->path->name.len + 1 + ctx->path->len, | 117 |
54 c->md5); | 118 ngx_crc32_final(c->crc32); |
55 | 119 ngx_md5_final(c->key, &md5); |
56 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 120 } |
57 "file cache key: %V, md5: %s", &ctx->key, | 121 |
58 c->file.name.data + ctx->path->name.len + 1 + ctx->path->len); | 122 |
59 | 123 ngx_int_t |
60 ngx_create_hashed_filename(&c->file, ctx->path); | 124 ngx_http_file_cache_open(ngx_http_request_t *r) |
125 { | |
126 u_char *p; | |
127 time_t now; | |
128 ssize_t n; | |
129 ngx_int_t rc, rv; | |
130 ngx_uint_t cold, test; | |
131 ngx_path_t *path; | |
132 ngx_http_cache_t *c; | |
133 ngx_pool_cleanup_t *cln; | |
134 ngx_open_file_info_t of; | |
135 ngx_http_file_cache_t *cache; | |
136 ngx_http_core_loc_conf_t *clcf; | |
137 ngx_http_file_cache_header_t *h; | |
138 | |
139 c = r->cache; | |
140 cache = c->file_cache; | |
141 | |
142 cln = ngx_pool_cleanup_add(r->pool, 0); | |
143 if (cln == NULL) { | |
144 return NGX_ERROR; | |
145 } | |
146 | |
147 rc = ngx_http_file_cache_exists(r, cache); | |
148 | |
149 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
150 "http file cache exists: %i u:%ui e:%d", | |
151 rc, c->uses, c->exists); | |
152 | |
153 if (rc == NGX_ERROR) { | |
154 return rc; | |
155 } | |
156 | |
157 cln->handler = ngx_http_file_cache_cleanup; | |
158 cln->data = c; | |
159 | |
160 if (rc == NGX_AGAIN) { | |
161 return rc; | |
162 } | |
163 | |
164 now = ngx_time(); | |
165 | |
166 cold = (now - cache->created < cache->inactive) ? 1 : 0; | |
167 | |
168 if (rc == NGX_OK) { | |
169 | |
170 if (c->error) { | |
171 return c->error; | |
172 } | |
173 | |
174 c->temp_file = 1; | |
175 test = c->exists ? 1 : 0; | |
176 rv = NGX_DECLINED; | |
177 | |
178 } else { /* rc == NGX_DECLINED */ | |
179 | |
180 if (c->min_uses > 1) { | |
181 | |
182 if (!cold) { | |
183 return NGX_AGAIN; | |
184 } | |
185 | |
186 test = 1; | |
187 rv = NGX_AGAIN; | |
188 | |
189 } else { | |
190 c->temp_file = 1; | |
191 test = cold ? 1 : 0; | |
192 rv = NGX_DECLINED; | |
193 } | |
194 } | |
195 | |
196 path = cache->path; | |
197 | |
198 c->file.name.len = path->name.len + 1 + path->len | |
199 + 2 * NGX_HTTP_CACHE_KEY_LEN; | |
200 | |
201 c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1); | |
202 if (c->file.name.data == NULL) { | |
203 return NGX_ERROR; | |
204 } | |
205 | |
206 ngx_memcpy(c->file.name.data, path->name.data, path->name.len); | |
207 | |
208 p = c->file.name.data + path->name.len + 1 + path->len; | |
209 p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN); | |
210 *p = '\0'; | |
211 | |
212 ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len); | |
213 | |
214 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
215 "cache file: \"%s\"", c->file.name.data); | |
216 | |
217 if (!test) { | |
218 return NGX_DECLINED; | |
219 } | |
220 | |
221 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
222 | |
223 ngx_memzero(&of, sizeof(ngx_open_file_info_t)); | |
224 | |
225 of.uniq = c->uniq; | |
226 of.valid = clcf->open_file_cache_valid; | |
227 of.min_uses = clcf->open_file_cache_min_uses; | |
228 of.events = clcf->open_file_cache_events; | |
229 of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; | |
230 | |
231 if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool) | |
232 != NGX_OK) | |
233 { | |
234 switch (of.err) { | |
235 | |
236 case 0: | |
237 return NGX_ERROR; | |
238 | |
239 case NGX_ENOENT: | |
240 case NGX_ENOTDIR: | |
241 return rv; | |
242 | |
243 default: | |
244 ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, | |
245 ngx_open_file_n " \"%s\" failed", c->file.name.data); | |
246 return NGX_ERROR; | |
247 } | |
248 } | |
61 | 249 |
62 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 250 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
63 "file cache name: %s", c->file.name.data); | 251 "http file cache fd: %d", of.fd); |
64 | 252 |
65 return ngx_http_file_cache_open(r->cache); | 253 c->file.fd = of.fd; |
66 } | 254 |
67 | 255 c->buf = ngx_create_temp_buf(r->pool, c->body_start); |
68 | 256 if (c->buf == NULL) { |
69 ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c) | |
70 { | |
71 ssize_t n; | |
72 ngx_err_t err; | |
73 ngx_http_cache_header_t *h; | |
74 | |
75 c->file.fd = ngx_open_file(c->file.name.data, | |
76 NGX_FILE_RDONLY, NGX_FILE_OPEN); | |
77 | |
78 if (c->file.fd == NGX_INVALID_FILE) { | |
79 err = ngx_errno; | |
80 | |
81 if (err == NGX_ENOENT || err == NGX_ENOTDIR) { | |
82 return NGX_DECLINED; | |
83 } | |
84 | |
85 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, | |
86 ngx_open_file_n " \"%s\" failed", c->file.name.data); | |
87 return NGX_ERROR; | 257 return NGX_ERROR; |
88 } | 258 } |
89 | 259 |
90 if (c->uniq) { | 260 n = ngx_read_file(&c->file, c->buf->pos, c->body_start, 0); |
91 if (ngx_fd_info(c->file.fd, &c->file.info) == NGX_FILE_ERROR) { | 261 |
92 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, | 262 if (n == NGX_ERROR) { |
93 ngx_fd_info_n " \"%s\" failed", c->file.name.data); | |
94 | |
95 return NGX_ERROR; | |
96 } | |
97 | |
98 if (ngx_file_uniq(&c->file.info) == c->uniq) { | |
99 if (ngx_close_file(c->file.fd) == NGX_FILE_ERROR) { | |
100 ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, | |
101 ngx_close_file_n " \"%s\" failed", | |
102 c->file.name.data); | |
103 } | |
104 | |
105 return NGX_HTTP_CACHE_THE_SAME; | |
106 } | |
107 } | |
108 | |
109 n = ngx_read_file(&c->file, c->buf->pos, c->buf->end - c->buf->last, 0); | |
110 | |
111 if (n == NGX_ERROR || n == NGX_AGAIN) { | |
112 return n; | 263 return n; |
113 } | 264 } |
114 | 265 |
115 if (n <= c->header_size) { | 266 if ((size_t) n <= c->header_start) { |
116 ngx_log_error(NGX_LOG_CRIT, c->log, 0, | 267 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, |
117 "cache file \"%s\" is too small", c->file.name.data); | 268 "cache file \"%s\" is too small", c->file.name.data); |
118 return NGX_ERROR; | 269 return NGX_ERROR; |
119 } | 270 } |
120 | 271 |
121 h = (ngx_http_cache_header_t *) c->buf->pos; | 272 h = (ngx_http_file_cache_header_t *) c->buf->pos; |
122 c->expires = h->expires; | 273 |
123 c->last_modified= h->last_modified; | 274 if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) { |
275 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, | |
276 "cache file \"%s\" has md5 collision", c->file.name.data); | |
277 return NGX_DECLINED; | |
278 } | |
279 | |
280 c->buf->last += n; | |
281 | |
282 c->valid_sec = h->valid_sec; | |
283 c->last_modified = h->last_modified; | |
124 c->date = h->date; | 284 c->date = h->date; |
125 c->length = h->length; | 285 c->valid_msec = h->valid_msec; |
126 | 286 c->length = of.size; |
127 if (h->key_len > (size_t) (c->buf->end - c->buf->pos)) { | 287 c->body_start = h->body_start; |
128 ngx_log_error(NGX_LOG_ALERT, c->log, 0, | 288 |
129 "cache file \"%s\" is probably invalid", | 289 r->cached = 1; |
130 c->file.name.data); | 290 |
131 return NGX_DECLINED; | 291 if (cold) { |
132 } | 292 |
133 | 293 ngx_shmtx_lock(&cache->shpool->mutex); |
134 #if 0 | 294 |
135 | 295 c->node->uses = c->min_uses; |
136 /* TODO */ | 296 c->node->body_start = c->body_start; |
137 | 297 c->node->exists = 1; |
138 if (c->key_len && h->key_len != c->key_len) { | 298 |
139 | 299 ngx_shmtx_unlock(&cache->shpool->mutex); |
140 ngx_strncmp(h->key, c->key_data, h->key_len) != 0)) | 300 } |
141 | 301 |
142 h->key[h->key_len] = '\0'; | 302 if (c->valid_sec < now) { |
143 ngx_log_error(NGX_LOG_ALERT, c->log, 0, | 303 |
144 "md5 collision: \"%s\" and \"%s\"", | 304 c->uses = c->min_uses; |
145 h->key, c->key.data); | 305 |
146 return NGX_DECLINED; | 306 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
147 } | 307 "http file cache expired: %T %T", c->valid_sec, now); |
148 | 308 |
149 #endif | |
150 | |
151 c->buf->last += n; | |
152 | |
153 if (c->expires < ngx_time()) { | |
154 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
155 "http file cache expired"); | |
156 return NGX_HTTP_CACHE_STALE; | 309 return NGX_HTTP_CACHE_STALE; |
157 } | 310 } |
158 | 311 |
159 /* TODO: NGX_HTTP_CACHE_AGED */ | 312 /* TODO: NGX_HTTP_CACHE_AGED */ |
160 | 313 |
161 /* STUB */ return NGX_DECLINED; | |
162 | |
163 return NGX_OK; | 314 return NGX_OK; |
164 } | 315 } |
165 | 316 |
166 | 317 |
167 #if 0 | 318 static ngx_int_t |
168 | 319 ngx_http_file_cache_exists(ngx_http_request_t *r, ngx_http_file_cache_t *cache) |
169 | 320 { |
170 int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, | 321 ngx_int_t rc; |
171 ngx_str_t *temp_file) | 322 ngx_http_file_cache_node_t *fcn; |
172 { | 323 |
173 int retry; | 324 ngx_shmtx_lock(&cache->shpool->mutex); |
174 ngx_err_t err; | 325 |
175 | 326 fcn = ngx_http_file_cache_lookup(cache, r->cache->key); |
176 retry = 0; | 327 |
328 if (fcn) { | |
329 ngx_queue_remove(&fcn->queue); | |
330 | |
331 if (fcn->error) { | |
332 | |
333 if (fcn->valid_sec < ngx_time()) { | |
334 goto renew; | |
335 } | |
336 | |
337 rc = NGX_OK; | |
338 | |
339 goto done; | |
340 } | |
341 | |
342 fcn->uses++; | |
343 fcn->count++; | |
344 | |
345 if (fcn->exists) { | |
346 | |
347 r->cache->exists = fcn->exists; | |
348 r->cache->body_start = fcn->body_start; | |
349 | |
350 rc = NGX_OK; | |
351 | |
352 goto done; | |
353 } | |
354 | |
355 if (fcn->uses >= r->cache->min_uses) { | |
356 | |
357 r->cache->exists = fcn->exists; | |
358 | |
359 if (fcn->body_start) { | |
360 r->cache->body_start = fcn->body_start; | |
361 } | |
362 | |
363 rc = NGX_OK; | |
364 | |
365 } else { | |
366 rc = NGX_AGAIN; | |
367 } | |
368 | |
369 goto done; | |
370 } | |
371 | |
372 fcn = ngx_slab_alloc_locked(cache->shpool, | |
373 sizeof(ngx_http_file_cache_node_t)); | |
374 if (fcn == NULL) { | |
375 ngx_shmtx_unlock(&cache->shpool->mutex); | |
376 | |
377 (void) ngx_http_file_cache_expire(cache, 1); | |
378 | |
379 ngx_shmtx_lock(&cache->shpool->mutex); | |
380 | |
381 fcn = ngx_slab_alloc_locked(cache->shpool, | |
382 sizeof(ngx_http_file_cache_node_t)); | |
383 if (fcn == NULL) { | |
384 rc = NGX_ERROR; | |
385 goto failed; | |
386 } | |
387 } | |
388 | |
389 ngx_memcpy((u_char *) &fcn->node.key, r->cache->key, | |
390 sizeof(ngx_rbtree_key_t)); | |
391 | |
392 ngx_memcpy(fcn->key, &r->cache->key[sizeof(ngx_rbtree_key_t)], | |
393 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
394 | |
395 ngx_rbtree_insert(cache->rbtree, &fcn->node); | |
396 | |
397 renew: | |
398 | |
399 rc = NGX_DECLINED; | |
400 | |
401 fcn->uses = 1; | |
402 fcn->count = 1; | |
403 fcn->valid_msec = 0; | |
404 fcn->error = 0; | |
405 fcn->exists = 0; | |
406 fcn->valid_sec = 0; | |
407 fcn->uniq = 0; | |
408 fcn->body_start = 0; | |
409 | |
410 done: | |
411 | |
412 fcn->expire = ngx_time() + cache->inactive; | |
413 | |
414 ngx_queue_insert_head(cache->queue, &fcn->queue); | |
415 | |
416 r->cache->uniq = fcn->uniq; | |
417 r->cache->uses = fcn->uses; | |
418 r->cache->error = fcn->error; | |
419 r->cache->node = fcn; | |
420 | |
421 failed: | |
422 | |
423 ngx_shmtx_unlock(&cache->shpool->mutex); | |
424 | |
425 return rc; | |
426 } | |
427 | |
428 | |
429 static ngx_http_file_cache_node_t * | |
430 ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key) | |
431 { | |
432 ngx_int_t rc; | |
433 ngx_rbtree_key_t node_key; | |
434 ngx_rbtree_node_t *node, *sentinel; | |
435 ngx_http_file_cache_node_t *fcn; | |
436 | |
437 ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t)); | |
438 | |
439 node = cache->rbtree->root; | |
440 sentinel = cache->rbtree->sentinel; | |
441 | |
442 while (node != sentinel) { | |
443 | |
444 if (node_key < node->key) { | |
445 node = node->left; | |
446 continue; | |
447 } | |
448 | |
449 if (node_key > node->key) { | |
450 node = node->right; | |
451 continue; | |
452 } | |
453 | |
454 /* node_key == node->key */ | |
455 | |
456 do { | |
457 fcn = (ngx_http_file_cache_node_t *) node; | |
458 | |
459 rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key, | |
460 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
461 | |
462 if (rc == 0) { | |
463 return fcn; | |
464 } | |
465 | |
466 node = (rc < 0) ? node->left : node->right; | |
467 | |
468 } while (node != sentinel && node_key == node->key); | |
469 | |
470 break; | |
471 } | |
472 | |
473 /* not found */ | |
474 | |
475 return NULL; | |
476 } | |
477 | |
478 | |
479 static void | |
480 ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
481 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) | |
482 { | |
483 ngx_rbtree_node_t **p; | |
484 ngx_http_file_cache_node_t *cn, *cnt; | |
177 | 485 |
178 for ( ;; ) { | 486 for ( ;; ) { |
179 if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) { | 487 |
180 return NGX_OK; | 488 if (node->key < temp->key) { |
181 } | 489 |
182 | 490 p = &temp->left; |
183 err = ngx_errno; | 491 |
184 | 492 } else if (node->key > temp->key) { |
185 #if (NGX_WIN32) | 493 |
186 if (err == NGX_EEXIST) { | 494 p = &temp->right; |
187 if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool) | 495 |
188 == NGX_ERROR) | 496 } else { /* node->key == temp->key */ |
189 { | 497 |
190 return NGX_ERROR; | 498 cn = (ngx_http_file_cache_node_t *) node; |
191 } | 499 cnt = (ngx_http_file_cache_node_t *) temp; |
192 } | 500 |
193 #endif | 501 p = (ngx_memcmp(cn->key, cnt->key, |
194 | 502 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)) |
195 if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { | 503 < 0) |
196 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | 504 ? &temp->left : &temp->right; |
197 ngx_rename_file_n "(\"%s\", \"%s\") failed", | 505 } |
198 temp_file->data, ctx->file.name.data); | 506 |
199 | 507 if (*p == sentinel) { |
200 return NGX_ERROR; | 508 break; |
201 } | 509 } |
202 | 510 |
203 if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) { | 511 temp = *p; |
204 return NGX_ERROR; | 512 } |
205 } | 513 |
206 | 514 *p = node; |
207 retry = 1; | 515 node->parent = temp; |
208 } | 516 node->left = sentinel; |
209 } | 517 node->right = sentinel; |
210 | 518 ngx_rbt_red(node); |
211 | 519 } |
212 #endif | 520 |
213 | 521 |
214 | 522 void |
215 ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, | 523 ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) |
216 ngx_dir_t *dir) | 524 { |
217 { | 525 ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf; |
218 int rc; | 526 |
219 ngx_buf_t buf; | 527 u_char *p; |
220 ngx_http_cache_t c; | 528 ngx_str_t *key; |
221 u_char data[sizeof(ngx_http_cache_header_t)]; | 529 ngx_uint_t i; |
222 | 530 ngx_http_cache_t *c; |
223 ngx_memzero(&c, sizeof(ngx_http_cache_t)); | 531 |
224 | 532 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
225 c.file.fd = NGX_INVALID_FILE; | 533 "http file cache set header"); |
226 c.file.name = *name; | 534 |
227 c.file.log = gc->log; | 535 c = r->cache; |
228 | 536 |
229 c.header_size = sizeof(ngx_http_cache_header_t); | 537 h->valid_sec = c->valid_sec; |
230 c.buf = &buf; | 538 h->last_modified = c->last_modified; |
231 c.log = gc->log; | 539 h->date = c->date; |
232 c.key_len = 0; | 540 h->crc32 = c->crc32; |
233 | 541 h->valid_msec = (u_short) c->valid_msec; |
234 buf.memory = 1; | 542 h->header_start = (u_short) c->header_start; |
235 buf.temporary = 1; | 543 h->body_start = (u_short) c->body_start; |
236 buf.pos = data; | 544 |
237 buf.last = data; | 545 p = buf + sizeof(ngx_http_file_cache_header_t); |
238 buf.start = data; | 546 |
239 buf.end = data + sizeof(ngx_http_cache_header_t); | 547 p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key)); |
240 | 548 |
241 rc = ngx_http_file_cache_open(&c); | 549 key = c->keys.elts; |
242 | 550 for (i = 0; i < c->keys.nelts; i++) { |
243 /* TODO: NGX_AGAIN */ | 551 p = ngx_copy(p, key[i].data, key[i].len); |
244 | 552 } |
245 if (rc != NGX_ERROR&& rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) { | 553 |
554 *p = LF; | |
555 } | |
556 | |
557 | |
558 void | |
559 ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) | |
560 { | |
561 ngx_int_t rc; | |
562 ngx_file_uniq_t uniq; | |
563 ngx_file_info_t fi; | |
564 ngx_http_cache_t *c; | |
565 ngx_ext_rename_file_t ext; | |
566 ngx_http_file_cache_t *cache; | |
567 | |
568 c = r->cache; | |
569 | |
570 if (c->updated) { | |
571 return; | |
572 } | |
573 | |
574 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
575 "http file cache update"); | |
576 | |
577 c->updated = 1; | |
578 | |
579 cache = c->file_cache; | |
580 | |
581 uniq = 0; | |
582 | |
583 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
584 "http file cache rename: \"%s\" to \"%s\"", | |
585 tf->file.name.data, c->file.name.data); | |
586 | |
587 ext.access = NGX_FILE_OWNER_ACCESS; | |
588 ext.path_access = NGX_FILE_OWNER_ACCESS; | |
589 ext.time = -1; | |
590 ext.create_path = 1; | |
591 ext.delete_file = 1; | |
592 ext.log_rename_error = 1; | |
593 ext.log = r->connection->log; | |
594 | |
595 rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext); | |
596 | |
597 if (rc == NGX_OK) { | |
598 | |
599 if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) { | |
600 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, | |
601 ngx_fd_info_n " \"%s\" failed", tf->file.name.data); | |
602 | |
603 rc = NGX_ERROR; | |
604 | |
605 } else { | |
606 uniq = ngx_file_uniq(&fi); | |
607 } | |
608 } | |
609 | |
610 ngx_shmtx_lock(&cache->shpool->mutex); | |
611 | |
612 c->node->count--; | |
613 c->node->uniq = uniq; | |
614 c->node->body_start = c->body_start; | |
615 | |
616 if (rc == NGX_OK) { | |
617 c->node->exists = 1; | |
618 } | |
619 | |
620 ngx_shmtx_unlock(&cache->shpool->mutex); | |
621 } | |
622 | |
623 | |
624 ngx_int_t | |
625 ngx_http_cache_send(ngx_http_request_t *r) | |
626 { | |
627 ngx_int_t rc; | |
628 ngx_buf_t *b; | |
629 ngx_chain_t out; | |
630 ngx_http_cache_t *c; | |
631 | |
632 c = r->cache; | |
633 | |
634 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
635 "http file cache send: %s", c->file.name.data); | |
636 | |
637 /* we need to allocate all before the header would be sent */ | |
638 | |
639 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); | |
640 if (b == NULL) { | |
641 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
642 } | |
643 | |
644 b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); | |
645 if (b->file == NULL) { | |
646 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
647 } | |
648 | |
649 rc = ngx_http_send_header(r); | |
650 | |
651 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | |
652 return rc; | |
653 } | |
654 | |
655 b->file_pos = c->body_start; | |
656 b->file_last = c->length; | |
657 | |
658 b->in_file = (c->length - c->body_start) ? 1: 0; | |
659 b->last_buf = (r == r->main) ? 1: 0; | |
660 b->last_in_chain = 1; | |
661 | |
662 b->file->fd = c->file.fd; | |
663 b->file->name = c->file.name; | |
664 b->file->log = r->connection->log; | |
665 | |
666 out.buf = b; | |
667 out.next = NULL; | |
668 | |
669 return ngx_http_output_filter(r, &out); | |
670 } | |
671 | |
672 | |
673 void | |
674 ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf) | |
675 { | |
676 ngx_http_cache_t *c; | |
677 ngx_http_file_cache_t *cache; | |
678 | |
679 c = r->cache; | |
680 | |
681 if (c->updated) { | |
682 return; | |
683 } | |
684 | |
685 c->updated = 1; | |
686 | |
687 cache = c->file_cache; | |
688 | |
689 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
690 "http file cache free"); | |
691 | |
692 ngx_shmtx_lock(&cache->shpool->mutex); | |
693 | |
694 c->node->count--; | |
695 | |
696 if (c->error) { | |
697 c->node->valid_sec = c->valid_sec; | |
698 c->node->valid_msec = c->valid_msec; | |
699 c->node->error = c->error; | |
700 } | |
701 | |
702 ngx_shmtx_unlock(&cache->shpool->mutex); | |
703 | |
704 if (c->temp_file) { | |
705 if (tf && tf->file.fd != NGX_INVALID_FILE) { | |
706 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
707 "http file cache incomplete: \"%s\"", | |
708 tf->file.name.data); | |
709 | |
710 if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { | |
711 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, | |
712 ngx_delete_file_n " \"%s\" failed", | |
713 tf->file.name.data); | |
714 } | |
715 } | |
716 } | |
717 } | |
718 | |
719 | |
720 static void | |
721 ngx_http_file_cache_cleanup(void *data) | |
722 { | |
723 ngx_http_cache_t *c = data; | |
724 | |
725 ngx_http_file_cache_t *cache; | |
726 | |
727 if (c->updated) { | |
728 return; | |
729 } | |
730 | |
731 c->updated = 1; | |
732 | |
733 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0, | |
734 "http file cache cleanup"); | |
735 | |
736 if (c->error) { | |
737 return; | |
738 } | |
739 | |
740 cache = c->file_cache; | |
741 | |
742 ngx_shmtx_lock(&cache->shpool->mutex); | |
743 | |
744 c->node->count--; | |
745 | |
746 ngx_shmtx_unlock(&cache->shpool->mutex); | |
747 } | |
748 | |
749 | |
750 static time_t | |
751 ngx_http_file_cache_expire(ngx_http_file_cache_t *cache, ngx_uint_t forced) | |
752 { | |
753 u_char *name, *p; | |
754 size_t len; | |
755 time_t now, wait; | |
756 ngx_uint_t tries; | |
757 ngx_path_t *path; | |
758 ngx_queue_t *q; | |
759 ngx_http_file_cache_node_t *fcn; | |
760 u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; | |
761 | |
762 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
763 "http file cache expire"); | |
764 | |
765 path = cache->path; | |
766 len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; | |
767 | |
768 now = ngx_time(); | |
769 | |
770 ngx_shmtx_lock(&cache->shpool->mutex); | |
771 | |
772 tries = 0; | |
773 | |
774 for ( ;; ) { | |
775 | |
776 if (ngx_queue_empty(cache->queue)) { | |
777 wait = cache->inactive; | |
778 break; | |
779 } | |
780 | |
781 q = ngx_queue_last(cache->queue); | |
782 | |
783 fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); | |
784 | |
785 if (!forced) { | |
786 wait = fcn->expire - now; | |
787 | |
788 if (wait > 0) { | |
789 break; | |
790 } | |
791 } | |
792 | |
793 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
794 "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", | |
795 fcn->count, fcn->exists, | |
796 fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); | |
797 | |
798 if (fcn->count) { | |
799 | |
800 if (!forced) { | |
801 | |
802 p = ngx_hex_dump(key, (u_char *) &fcn->node.key, | |
803 sizeof(ngx_rbtree_key_t)); | |
804 (void) ngx_hex_dump(p, fcn->key, | |
805 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
806 | |
807 /* | |
808 * abnormally exited workers may leave locked cache entries, | |
809 * and although it may be safe to remove them completely, | |
810 * we prefer to remove them from inactive queue and rbtree | |
811 * only, and to allow other leaks | |
812 */ | |
813 | |
814 ngx_queue_remove(q); | |
815 | |
816 ngx_rbtree_delete(cache->rbtree, &fcn->node); | |
817 | |
818 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, | |
819 "ignore long locked inactive cache entry %*s", | |
820 NGX_HTTP_CACHE_KEY_LEN, key); | |
821 } | |
822 | |
823 if (tries++ < 10) { | |
824 continue; | |
825 } | |
826 | |
827 wait = 1; | |
828 break; | |
829 } | |
830 | |
831 forced = 0; | |
832 | |
833 if (!fcn->exists) { | |
834 | |
835 ngx_queue_remove(q); | |
836 | |
837 ngx_rbtree_delete(cache->rbtree, &fcn->node); | |
838 | |
839 ngx_slab_free_locked(cache->shpool, fcn); | |
840 | |
841 continue; | |
842 } | |
843 | |
844 name = ngx_alloc(len + 1, ngx_cycle->log); | |
845 if (name == NULL) { | |
846 wait = 60; | |
847 break; | |
848 } | |
849 | |
850 ngx_memcpy(name, path->name.data, path->name.len); | |
851 | |
852 p = name + path->name.len + 1 + path->len; | |
853 p = ngx_hex_dump(p, (u_char *) &fcn->node.key, | |
854 sizeof(ngx_rbtree_key_t)); | |
855 p = ngx_hex_dump(p, fcn->key, | |
856 NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
857 *p = '\0'; | |
858 | |
859 ngx_queue_remove(q); | |
860 | |
861 ngx_rbtree_delete(cache->rbtree, &fcn->node); | |
862 | |
863 ngx_slab_free_locked(cache->shpool, fcn); | |
864 | |
865 ngx_shmtx_unlock(&cache->shpool->mutex); | |
866 | |
867 ngx_create_hashed_filename(path, name, len); | |
868 | |
869 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
870 "http file cache expire: \"%s\"", name); | |
871 | |
872 if (ngx_delete_file(name) == NGX_FILE_ERROR) { | |
873 ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, | |
874 ngx_delete_file_n " \"%s\" failed", name); | |
875 } | |
876 | |
877 ngx_free(name); | |
878 | |
879 ngx_shmtx_lock(&cache->shpool->mutex); | |
880 } | |
881 | |
882 ngx_shmtx_unlock(&cache->shpool->mutex); | |
883 | |
884 return wait; | |
885 } | |
886 | |
887 | |
888 time_t | |
889 ngx_http_file_cache_cleaner(void *data) | |
890 { | |
891 ngx_http_file_cache_t *cache = data; | |
892 | |
893 time_t now, next; | |
894 ngx_tree_ctx_t tree; | |
895 | |
896 now = ngx_time(); | |
897 | |
898 if (now >= cache->next_clean_time) { | |
899 | |
900 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, | |
901 "clean unused cache files"); | |
902 | |
903 tree.init_handler = NULL; | |
904 tree.file_handler = ngx_http_file_cache_clean_file; | |
905 tree.pre_tree_handler = ngx_http_file_cache_clean_noop; | |
906 tree.post_tree_handler = ngx_http_file_cache_clean_noop; | |
907 tree.spec_handler = ngx_http_file_cache_clean_file; | |
908 tree.data = cache; | |
909 tree.alloc = 0; | |
910 tree.log = ngx_cycle->log; | |
911 | |
912 (void) ngx_walk_tree(&tree, &cache->path->name); | |
913 | |
914 ngx_time_update(0, 0); | |
915 | |
916 next = ngx_next_time(cache->clean_time); | |
917 | |
918 if (next == -1) { | |
919 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, | |
920 ngx_next_time_n " failed"); | |
921 return 60; | |
922 } | |
923 | |
924 cache->next_clean_time = next; | |
925 } | |
926 | |
927 next = ngx_http_file_cache_expire(cache, 0); | |
928 | |
929 now = ngx_time(); | |
930 | |
931 return (now + next < cache->next_clean_time) ? next: | |
932 cache->next_clean_time - now; | |
933 } | |
934 | |
935 | |
936 static ngx_int_t | |
937 ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) | |
938 { | |
939 return NGX_OK; | |
940 } | |
941 | |
942 | |
943 static ngx_int_t | |
944 ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) | |
945 { | |
946 u_char *p, key[2 * NGX_HTTP_CACHE_KEY_LEN]; | |
947 ngx_int_t n; | |
948 ngx_uint_t i; | |
949 ngx_http_file_cache_t *cache; | |
950 ngx_http_file_cache_node_t *node; | |
951 | |
952 if (path->len < 2 * NGX_HTTP_CACHE_KEY_LEN) { | |
953 goto clean; | |
954 } | |
955 | |
956 p = &path->data[path->len - 2 * NGX_HTTP_CACHE_KEY_LEN]; | |
957 | |
958 for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) { | |
959 n = ngx_hextoi(p, 2); | |
960 | |
961 if (n == NGX_ERROR) { | |
962 goto clean; | |
963 } | |
964 | |
965 p += 2; | |
966 | |
967 key[i] = (u_char) n; | |
968 } | |
969 | |
970 cache = ctx->data; | |
971 | |
972 ngx_shmtx_lock(&cache->shpool->mutex); | |
973 | |
974 node = ngx_http_file_cache_lookup(cache, key); | |
975 | |
976 ngx_shmtx_unlock(&cache->shpool->mutex); | |
977 | |
978 if (node) { | |
246 return NGX_OK; | 979 return NGX_OK; |
247 } | 980 } |
248 | 981 |
249 if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { | 982 clean: |
250 ngx_log_error(NGX_LOG_CRIT, c.log, ngx_errno, | 983 |
251 ngx_delete_file_n " \"%s\" failed", name->data); | 984 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, |
985 "http file cache clean: \"%s\"", path->data); | |
986 | |
987 if (ngx_delete_file(path->data) == NGX_FILE_ERROR) { | |
988 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
989 ngx_delete_file_n " \"%s\" failed", path->data); | |
252 return NGX_ERROR; | 990 return NGX_ERROR; |
253 } | 991 } |
254 | 992 |
255 gc->deleted++; | |
256 gc->freed += ngx_de_size(dir); | |
257 | |
258 return NGX_OK; | 993 return NGX_OK; |
259 } | 994 } |
995 | |
996 | |
997 time_t | |
998 ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status) | |
999 { | |
1000 ngx_uint_t i; | |
1001 ngx_http_cache_valid_t *valid; | |
1002 | |
1003 valid = cache_valid->elts; | |
1004 for (i = 0; i < cache_valid->nelts; i++) { | |
1005 | |
1006 if (valid[i].status == 0) { | |
1007 return valid[i].valid; | |
1008 } | |
1009 | |
1010 if (valid[i].status == status) { | |
1011 return valid[i].valid; | |
1012 } | |
1013 } | |
1014 | |
1015 return 0; | |
1016 } | |
1017 | |
1018 | |
1019 char * | |
1020 ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
1021 { | |
1022 u_char *last, *p; | |
1023 time_t inactive, clean_time, next; | |
1024 ssize_t size; | |
1025 ngx_str_t s, name, *value; | |
1026 ngx_uint_t i, n; | |
1027 ngx_http_file_cache_t *cache; | |
1028 | |
1029 cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t)); | |
1030 if (cache == NULL) { | |
1031 return NGX_CONF_ERROR; | |
1032 } | |
1033 | |
1034 cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t)); | |
1035 if (cache->path == NULL) { | |
1036 return NGX_CONF_ERROR; | |
1037 } | |
1038 | |
1039 inactive = 600; | |
1040 clean_time = 5 * 60 * 60; | |
1041 | |
1042 name.len = 0; | |
1043 size = 0; | |
1044 | |
1045 value = cf->args->elts; | |
1046 | |
1047 cache->path->name = value[1]; | |
1048 | |
1049 if (cache->path->name.data[cache->path->name.len - 1] == '/') { | |
1050 cache->path->name.len--; | |
1051 } | |
1052 | |
1053 if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) { | |
1054 return NGX_CONF_ERROR; | |
1055 } | |
1056 | |
1057 for (i = 2; i < cf->args->nelts; i++) { | |
1058 | |
1059 if (ngx_strncmp(value[i].data, "levels=", 7) == 0) { | |
1060 | |
1061 n = 0; | |
1062 p = value[i].data + 7; | |
1063 last = value[i].data + value[i].len; | |
1064 | |
1065 while (p < last) { | |
1066 | |
1067 if (*p > '0' && *p < '6') { | |
1068 | |
1069 cache->path->level[n] = *p++ - '0'; | |
1070 cache->path->len += cache->path->level[n] + 1; | |
1071 | |
1072 if (p == last) { | |
1073 break; | |
1074 } | |
1075 | |
1076 if (*p++ == ':') { | |
1077 | |
1078 if (n > 2) { | |
1079 goto invalid_levels; | |
1080 } | |
1081 | |
1082 if (cache->path->level[n] == 0) { | |
1083 goto invalid_levels; | |
1084 } | |
1085 | |
1086 n++; | |
1087 | |
1088 continue; | |
1089 } | |
1090 } | |
1091 | |
1092 goto invalid_levels; | |
1093 } | |
1094 | |
1095 if (cache->path->len < 10 + 3) { | |
1096 continue; | |
1097 } | |
1098 | |
1099 invalid_levels: | |
1100 | |
1101 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1102 "invalid \"levels\" \"%V\"", &value[i]); | |
1103 return NGX_CONF_ERROR; | |
1104 } | |
1105 | |
1106 if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) { | |
1107 | |
1108 name.data = value[i].data + 10; | |
1109 | |
1110 p = (u_char *) ngx_strchr(name.data, ':'); | |
1111 | |
1112 if (p) { | |
1113 name.len = p - name.data; | |
1114 | |
1115 p++; | |
1116 | |
1117 s.len = value[i].data + value[i].len - p; | |
1118 s.data = p; | |
1119 | |
1120 size = ngx_parse_size(&s); | |
1121 if (size > 8191) { | |
1122 continue; | |
1123 } | |
1124 } | |
1125 | |
1126 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1127 "invalid keys zone size \"%V\"", &value[i]); | |
1128 return NGX_CONF_ERROR; | |
1129 } | |
1130 | |
1131 if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { | |
1132 | |
1133 s.len = value[i].len - 9; | |
1134 s.data = value[i].data + 9; | |
1135 | |
1136 inactive = ngx_parse_time(&s, 1); | |
1137 if (inactive < 0) { | |
1138 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1139 "invalid inactive value \"%V\"", &value[i]); | |
1140 return NGX_CONF_ERROR; | |
1141 } | |
1142 | |
1143 continue; | |
1144 } | |
1145 | |
1146 if (ngx_strncmp(value[i].data, "clean_time=", 11) == 0) { | |
1147 | |
1148 s.len = value[i].len - 11; | |
1149 s.data = value[i].data + 11; | |
1150 | |
1151 clean_time = ngx_parse_time(&s, 1); | |
1152 if (clean_time < 0 || clean_time > 24 * 60 * 60) { | |
1153 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1154 "invalid clean_time value \"%V\"", &value[i]); | |
1155 return NGX_CONF_ERROR; | |
1156 } | |
1157 | |
1158 continue; | |
1159 } | |
1160 | |
1161 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1162 "invalid parameter \"%V\"", &value[i]); | |
1163 return NGX_CONF_ERROR; | |
1164 } | |
1165 | |
1166 if (name.len == 0 || size == 0) { | |
1167 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1168 "\"%V\" must have \"keys_zone\" parameter", | |
1169 &cmd->name); | |
1170 return NGX_CONF_ERROR; | |
1171 } | |
1172 | |
1173 cache->path->cleaner = ngx_http_file_cache_cleaner; | |
1174 cache->path->data = cache; | |
1175 | |
1176 if (ngx_add_path(cf, &cache->path) != NGX_OK) { | |
1177 return NGX_CONF_ERROR; | |
1178 } | |
1179 | |
1180 cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post); | |
1181 if (cache->shm_zone == NULL) { | |
1182 return NGX_CONF_ERROR; | |
1183 } | |
1184 | |
1185 if (cache->shm_zone->data) { | |
1186 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1187 "duplicate zone \"%V\"", &name); | |
1188 return NGX_CONF_ERROR; | |
1189 } | |
1190 | |
1191 next = ngx_next_time(clean_time); | |
1192 | |
1193 if (next == -1) { | |
1194 ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, | |
1195 ngx_next_time_n " failed"); | |
1196 return NGX_CONF_ERROR; | |
1197 } | |
1198 | |
1199 cache->shm_zone->init = ngx_http_file_cache_init; | |
1200 cache->shm_zone->data = cache; | |
1201 | |
1202 cache->inactive = inactive; | |
1203 cache->clean_time = clean_time; | |
1204 cache->next_clean_time = next; | |
1205 | |
1206 return NGX_CONF_OK; | |
1207 } | |
1208 | |
1209 | |
1210 char * | |
1211 ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
1212 void *conf) | |
1213 { | |
1214 char *p = conf; | |
1215 | |
1216 time_t valid; | |
1217 ngx_str_t *value; | |
1218 ngx_uint_t i, n, status; | |
1219 ngx_array_t **a; | |
1220 ngx_http_cache_valid_t *v; | |
1221 static ngx_uint_t statuses[] = { 200, 301, 302 }; | |
1222 | |
1223 a = (ngx_array_t **) (p + cmd->offset); | |
1224 | |
1225 if (*a == NGX_CONF_UNSET_PTR) { | |
1226 *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t)); | |
1227 if (*a == NULL) { | |
1228 return NGX_CONF_ERROR; | |
1229 } | |
1230 } | |
1231 | |
1232 value = cf->args->elts; | |
1233 n = cf->args->nelts - 1; | |
1234 | |
1235 valid = ngx_parse_time(&value[n], 1); | |
1236 if (valid < 0) { | |
1237 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1238 "invalid time value \"%V\"", &value[n]); | |
1239 return NGX_CONF_ERROR; | |
1240 } | |
1241 | |
1242 if (n == 1) { | |
1243 | |
1244 for (i = 0; i < 3; i++) { | |
1245 v = ngx_array_push(*a); | |
1246 if (v == NULL) { | |
1247 return NGX_CONF_ERROR; | |
1248 } | |
1249 | |
1250 v->status = statuses[i]; | |
1251 v->valid = valid; | |
1252 } | |
1253 | |
1254 return NGX_CONF_OK; | |
1255 } | |
1256 | |
1257 for (i = 1; i < n; i++) { | |
1258 | |
1259 if (ngx_strcmp(value[i].data, "any") == 0) { | |
1260 | |
1261 status = 0; | |
1262 | |
1263 } else { | |
1264 | |
1265 status = ngx_atoi(value[i].data, value[i].len); | |
1266 if (status < 100) { | |
1267 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1268 "invalid status \"%V\"", &value[i]); | |
1269 return NGX_CONF_ERROR; | |
1270 } | |
1271 } | |
1272 | |
1273 v = ngx_array_push(*a); | |
1274 if (v == NULL) { | |
1275 return NGX_CONF_ERROR; | |
1276 } | |
1277 | |
1278 v->status = status; | |
1279 v->valid = valid; | |
1280 } | |
1281 | |
1282 return NGX_CONF_OK; | |
1283 } |