Mercurial > hg > nginx-vendor-current
comparison src/http/ngx_http_cache.c @ 0:f0b350454894 NGINX_0_1_0
nginx 0.1.0
*) The first public version.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 04 Oct 2004 00:00:00 +0400 |
parents | |
children | 6f8b0dc0f8dd |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f0b350454894 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 | |
12 | |
13 static ngx_http_module_t ngx_http_cache_module_ctx = { | |
14 NULL, /* pre conf */ | |
15 | |
16 NULL, /* create main configuration */ | |
17 NULL, /* init main configuration */ | |
18 | |
19 NULL, /* create server configuration */ | |
20 NULL, /* merge server configuration */ | |
21 | |
22 NULL, /* create location configuration */ | |
23 NULL /* merge location configuration */ | |
24 }; | |
25 | |
26 | |
27 ngx_module_t ngx_http_cache_module = { | |
28 NGX_MODULE, | |
29 &ngx_http_cache_module_ctx, /* module context */ | |
30 NULL, /* module directives */ | |
31 NGX_HTTP_MODULE, /* module type */ | |
32 NULL, /* init module */ | |
33 NULL /* init child */ | |
34 }; | |
35 | |
36 | |
37 ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *hash, | |
38 ngx_http_cleanup_t *cleanup, | |
39 ngx_str_t *key, uint32_t *crc) | |
40 { | |
41 ngx_uint_t i; | |
42 ngx_http_cache_t *c; | |
43 | |
44 *crc = ngx_crc(key->data, key->len); | |
45 | |
46 c = hash->elts + *crc % hash->hash * hash->nelts; | |
47 | |
48 if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
49 return (void *) NGX_ERROR; | |
50 } | |
51 | |
52 for (i = 0; i < hash->nelts; i++) { | |
53 if (c[i].crc == *crc | |
54 && c[i].key.len == key->len | |
55 && ngx_rstrncmp(c[i].key.data, key->data, key->len) == 0) | |
56 { | |
57 #if 0 | |
58 if (c[i].expired) { | |
59 ngx_mutex_unlock(&hash->mutex); | |
60 return (void *) NGX_AGAIN; | |
61 } | |
62 #endif | |
63 | |
64 c[i].refs++; | |
65 | |
66 if ((!(c[i].notify && (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT))) | |
67 && (ngx_cached_time - c[i].updated >= hash->update)) | |
68 { | |
69 c[i].expired = 1; | |
70 } | |
71 | |
72 ngx_mutex_unlock(&hash->mutex); | |
73 | |
74 if (cleanup) { | |
75 cleanup->data.cache.hash = hash; | |
76 cleanup->data.cache.cache = &c[i]; | |
77 cleanup->valid = 1; | |
78 cleanup->cache = 1; | |
79 } | |
80 | |
81 return &c[i]; | |
82 } | |
83 } | |
84 | |
85 ngx_mutex_unlock(&hash->mutex); | |
86 | |
87 return NULL; | |
88 } | |
89 | |
90 | |
91 ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, | |
92 ngx_http_cache_t *cache, | |
93 ngx_http_cleanup_t *cleanup, | |
94 ngx_str_t *key, uint32_t crc, | |
95 ngx_str_t *value, ngx_log_t *log) | |
96 { | |
97 time_t old; | |
98 ngx_uint_t i; | |
99 ngx_http_cache_t *c; | |
100 | |
101 old = ngx_cached_time + 1; | |
102 | |
103 c = hash->elts + crc % hash->hash * hash->nelts; | |
104 | |
105 if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
106 return (void *) NGX_ERROR; | |
107 } | |
108 | |
109 if (cache == NULL) { | |
110 | |
111 /* allocate a new entry */ | |
112 | |
113 for (i = 0; i < hash->nelts; i++) { | |
114 if (c[i].refs > 0) { | |
115 /* a busy entry */ | |
116 continue; | |
117 } | |
118 | |
119 if (c[i].key.len == 0) { | |
120 /* a free entry is found */ | |
121 cache = &c[i]; | |
122 break; | |
123 } | |
124 | |
125 /* looking for the oldest cache entry */ | |
126 | |
127 if (old > c[i].accessed) { | |
128 | |
129 old = c[i].accessed; | |
130 cache = &c[i]; | |
131 } | |
132 } | |
133 | |
134 if (cache == NULL) { | |
135 ngx_mutex_unlock(&hash->mutex); | |
136 return NULL; | |
137 } | |
138 | |
139 ngx_http_cache_free(cache, key, value, log); | |
140 | |
141 if (cache->key.data == NULL) { | |
142 cache->key.data = ngx_alloc(key->len, log); | |
143 if (cache->key.data == NULL) { | |
144 ngx_http_cache_free(cache, NULL, NULL, log); | |
145 ngx_mutex_unlock(&hash->mutex); | |
146 return NULL; | |
147 } | |
148 } | |
149 | |
150 cache->key.len = key->len; | |
151 ngx_memcpy(cache->key.data, key->data, key->len); | |
152 | |
153 } else if (value) { | |
154 ngx_http_cache_free(cache, key, value, log); | |
155 } | |
156 | |
157 if (value) { | |
158 if (cache->data.value.data == NULL) { | |
159 cache->data.value.data = ngx_alloc(value->len, log); | |
160 if (cache->data.value.data == NULL) { | |
161 ngx_http_cache_free(cache, NULL, NULL, log); | |
162 ngx_mutex_unlock(&hash->mutex); | |
163 return NULL; | |
164 } | |
165 } | |
166 | |
167 cache->data.value.len = value->len; | |
168 ngx_memcpy(cache->data.value.data, value->data, value->len); | |
169 } | |
170 | |
171 cache->crc = crc; | |
172 cache->key.len = key->len; | |
173 | |
174 cache->refs = 1; | |
175 cache->count = 0; | |
176 | |
177 cache->deleted = 0; | |
178 cache->expired = 0; | |
179 cache->memory = 0; | |
180 cache->mmap = 0; | |
181 cache->notify = 0; | |
182 | |
183 if (cleanup) { | |
184 cleanup->data.cache.hash = hash; | |
185 cleanup->data.cache.cache = cache; | |
186 cleanup->valid = 1; | |
187 cleanup->cache = 1; | |
188 } | |
189 | |
190 ngx_mutex_unlock(&hash->mutex); | |
191 | |
192 return cache; | |
193 } | |
194 | |
195 | |
196 void ngx_http_cache_free(ngx_http_cache_t *cache, | |
197 ngx_str_t *key, ngx_str_t *value, ngx_log_t *log) | |
198 { | |
199 if (cache->memory) { | |
200 if (cache->data.value.data | |
201 && (value == NULL || value->len > cache->data.value.len)) | |
202 { | |
203 ngx_free(cache->data.value.data); | |
204 cache->data.value.data = NULL; | |
205 } | |
206 } | |
207 | |
208 /* TODO: mmap */ | |
209 | |
210 cache->data.value.len = 0; | |
211 | |
212 if (cache->fd != NGX_INVALID_FILE) { | |
213 | |
214 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, | |
215 "http cache close fd: %d", cache->fd); | |
216 | |
217 if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) { | |
218 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
219 ngx_close_file_n " \"%s\" failed", | |
220 cache->key.data); | |
221 } | |
222 | |
223 cache->fd = NGX_INVALID_FILE; | |
224 } | |
225 | |
226 if (cache->key.data && (key == NULL || key->len > cache->key.len)) { | |
227 ngx_free(cache->key.data); | |
228 cache->key.data = NULL; | |
229 } | |
230 | |
231 cache->key.len = 0; | |
232 | |
233 cache->refs = 0; | |
234 } | |
235 | |
236 | |
237 void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache) | |
238 { | |
239 if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
240 return; | |
241 } | |
242 } | |
243 | |
244 | |
245 void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, | |
246 ngx_http_cache_t *cache, ngx_log_t *log) | |
247 { | |
248 if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
249 return; | |
250 } | |
251 | |
252 cache->refs--; | |
253 | |
254 if (cache->refs == 0 && cache->deleted) { | |
255 ngx_http_cache_free(cache, NULL, NULL, log); | |
256 } | |
257 | |
258 ngx_mutex_unlock(&hash->mutex); | |
259 } | |
260 | |
261 | |
262 #if 0 | |
263 | |
264 ngx_http_cache_add_file_event(ngx_http_cache_hash_t *hash, | |
265 ngx_http_cache_t *cache) | |
266 { | |
267 ngx_event_t *ev; | |
268 ngx_http_cache_event_ctx_t *ctx; | |
269 | |
270 ev = &ngx_cycle->read_events[fd]; | |
271 ngx_memzero(ev, sizeof(ngx_event_t); | |
272 | |
273 ev->data = data; | |
274 ev->event_handler = ngx_http_cache_invalidate; | |
275 | |
276 return ngx_add_event(ev, NGX_VNODE_EVENT, 0); | |
277 } | |
278 | |
279 | |
280 void ngx_http_cache_invalidate(ngx_event_t *ev) | |
281 { | |
282 ngx_http_cache_event_ctx_t *ctx; | |
283 | |
284 ctx = ev->data; | |
285 | |
286 ngx_http_cache_lock(&ctx->hash->mutex); | |
287 | |
288 if (ctx->cache->refs == 0) | |
289 ngx_http_cache_free(ctx->cache, NULL, NULL, ctx->log); | |
290 | |
291 } else { | |
292 ctx->cache->deleted = 1; | |
293 } | |
294 | |
295 ngx_http_cache_unlock(&ctx->hash->mutex); | |
296 } | |
297 | |
298 #endif | |
299 | |
300 | |
301 /* TODO: currently fd only */ | |
302 | |
303 ngx_int_t ngx_http_send_cached(ngx_http_request_t *r) | |
304 { | |
305 ngx_int_t rc; | |
306 ngx_hunk_t *h; | |
307 ngx_chain_t out; | |
308 ngx_http_log_ctx_t *ctx; | |
309 | |
310 ctx = r->connection->log->data; | |
311 ctx->action = "sending response to client"; | |
312 | |
313 r->headers_out.status = NGX_HTTP_OK; | |
314 r->headers_out.content_length_n = r->cache->data.size; | |
315 r->headers_out.last_modified_time = r->cache->last_modified; | |
316 | |
317 if (ngx_http_set_content_type(r) != NGX_OK) { | |
318 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
319 } | |
320 | |
321 /* we need to allocate all before the header would be sent */ | |
322 | |
323 if (!(h = ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)))) { | |
324 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
325 } | |
326 | |
327 if (!(h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { | |
328 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
329 } | |
330 | |
331 rc = ngx_http_send_header(r); | |
332 | |
333 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | |
334 return rc; | |
335 } | |
336 | |
337 h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST; | |
338 | |
339 h->file_pos = 0; | |
340 h->file_last = r->cache->data.size; | |
341 | |
342 h->file->fd = r->cache->fd; | |
343 h->file->log = r->connection->log; | |
344 | |
345 out.hunk = h; | |
346 out.next = NULL; | |
347 | |
348 return ngx_http_output_filter(r, &out); | |
349 } | |
350 | |
351 | |
352 char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
353 { | |
354 char *p = conf; | |
355 | |
356 ngx_int_t i, j, dup, invalid; | |
357 ngx_str_t *value, line; | |
358 ngx_http_cache_t *c; | |
359 ngx_http_cache_hash_t *ch, **chp; | |
360 | |
361 chp = (ngx_http_cache_hash_t **) (p + cmd->offset); | |
362 if (*chp) { | |
363 return "is duplicate"; | |
364 } | |
365 | |
366 if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) { | |
367 return NGX_CONF_ERROR; | |
368 } | |
369 *chp = ch; | |
370 | |
371 dup = 0; | |
372 invalid = 0; | |
373 | |
374 value = cf->args->elts; | |
375 | |
376 for (i = 1; i < cf->args->nelts; i++) { | |
377 | |
378 if (value[i].data[1] != '=') { | |
379 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
380 "invalid value \"%s\"", value[i].data); | |
381 return NGX_CONF_ERROR; | |
382 } | |
383 | |
384 switch (value[i].data[0]) { | |
385 | |
386 case 'h': | |
387 if (ch->hash) { | |
388 dup = 1; | |
389 break; | |
390 } | |
391 | |
392 ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2); | |
393 if (ch->hash == (size_t) NGX_ERROR || ch->hash == 0) { | |
394 invalid = 1; | |
395 break; | |
396 } | |
397 | |
398 continue; | |
399 | |
400 case 'n': | |
401 if (ch->nelts) { | |
402 dup = 1; | |
403 break; | |
404 } | |
405 | |
406 ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2); | |
407 if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) { | |
408 invalid = 1; | |
409 break; | |
410 } | |
411 | |
412 continue; | |
413 | |
414 case 'l': | |
415 if (ch->life) { | |
416 dup = 1; | |
417 break; | |
418 } | |
419 | |
420 line.len = value[i].len - 2; | |
421 line.data = value[i].data + 2; | |
422 | |
423 ch->life = ngx_parse_time(&line, 1); | |
424 if (ch->life == NGX_ERROR || ch->life == 0) { | |
425 invalid = 1; | |
426 break; | |
427 } | |
428 | |
429 continue; | |
430 | |
431 case 'u': | |
432 if (ch->update) { | |
433 dup = 1; | |
434 break; | |
435 } | |
436 | |
437 line.len = value[i].len - 2; | |
438 line.data = value[i].data + 2; | |
439 | |
440 ch->update = ngx_parse_time(&line, 1); | |
441 if (ch->update == NGX_ERROR || ch->update == 0) { | |
442 invalid = 1; | |
443 break; | |
444 } | |
445 | |
446 continue; | |
447 | |
448 default: | |
449 invalid = 1; | |
450 } | |
451 | |
452 if (dup) { | |
453 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
454 "duplicate value \"%s\"", value[i].data); | |
455 return NGX_CONF_ERROR; | |
456 } | |
457 | |
458 if (invalid) { | |
459 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
460 "invalid value \"%s\"", value[i].data); | |
461 return NGX_CONF_ERROR; | |
462 } | |
463 } | |
464 | |
465 ch->elts = ngx_pcalloc(cf->pool, | |
466 ch->hash * ch->nelts * sizeof(ngx_http_cache_t)); | |
467 if (ch->elts == NULL) { | |
468 return NGX_CONF_ERROR; | |
469 } | |
470 | |
471 for (i = 0; i < (ngx_int_t) ch->hash; i++) { | |
472 c = ch->elts + i * ch->nelts; | |
473 | |
474 for (j = 0; j < (ngx_int_t) ch->nelts; j++) { | |
475 c[j].fd = NGX_INVALID_FILE; | |
476 } | |
477 } | |
478 | |
479 return NGX_CONF_OK; | |
480 } |