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