Mercurial > hg > nginx-ranges
diff 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 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_cache.c @@ -0,0 +1,480 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + + +static ngx_http_module_t ngx_http_cache_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_cache_module = { + NGX_MODULE, + &ngx_http_cache_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init child */ +}; + + +ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *hash, + ngx_http_cleanup_t *cleanup, + ngx_str_t *key, uint32_t *crc) +{ + ngx_uint_t i; + ngx_http_cache_t *c; + + *crc = ngx_crc(key->data, key->len); + + c = hash->elts + *crc % hash->hash * hash->nelts; + + if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { + return (void *) NGX_ERROR; + } + + for (i = 0; i < hash->nelts; i++) { + if (c[i].crc == *crc + && c[i].key.len == key->len + && ngx_rstrncmp(c[i].key.data, key->data, key->len) == 0) + { +#if 0 + if (c[i].expired) { + ngx_mutex_unlock(&hash->mutex); + return (void *) NGX_AGAIN; + } +#endif + + c[i].refs++; + + if ((!(c[i].notify && (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT))) + && (ngx_cached_time - c[i].updated >= hash->update)) + { + c[i].expired = 1; + } + + ngx_mutex_unlock(&hash->mutex); + + if (cleanup) { + cleanup->data.cache.hash = hash; + cleanup->data.cache.cache = &c[i]; + cleanup->valid = 1; + cleanup->cache = 1; + } + + return &c[i]; + } + } + + ngx_mutex_unlock(&hash->mutex); + + return NULL; +} + + +ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, + ngx_http_cache_t *cache, + ngx_http_cleanup_t *cleanup, + ngx_str_t *key, uint32_t crc, + ngx_str_t *value, ngx_log_t *log) +{ + time_t old; + ngx_uint_t i; + ngx_http_cache_t *c; + + old = ngx_cached_time + 1; + + c = hash->elts + crc % hash->hash * hash->nelts; + + if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { + return (void *) NGX_ERROR; + } + + if (cache == NULL) { + + /* allocate a new entry */ + + for (i = 0; i < hash->nelts; i++) { + if (c[i].refs > 0) { + /* a busy entry */ + continue; + } + + if (c[i].key.len == 0) { + /* a free entry is found */ + cache = &c[i]; + break; + } + + /* looking for the oldest cache entry */ + + if (old > c[i].accessed) { + + old = c[i].accessed; + cache = &c[i]; + } + } + + if (cache == NULL) { + ngx_mutex_unlock(&hash->mutex); + return NULL; + } + + ngx_http_cache_free(cache, key, value, log); + + if (cache->key.data == NULL) { + cache->key.data = ngx_alloc(key->len, log); + if (cache->key.data == NULL) { + ngx_http_cache_free(cache, NULL, NULL, log); + ngx_mutex_unlock(&hash->mutex); + return NULL; + } + } + + cache->key.len = key->len; + ngx_memcpy(cache->key.data, key->data, key->len); + + } else if (value) { + ngx_http_cache_free(cache, key, value, log); + } + + if (value) { + if (cache->data.value.data == NULL) { + cache->data.value.data = ngx_alloc(value->len, log); + if (cache->data.value.data == NULL) { + ngx_http_cache_free(cache, NULL, NULL, log); + ngx_mutex_unlock(&hash->mutex); + return NULL; + } + } + + cache->data.value.len = value->len; + ngx_memcpy(cache->data.value.data, value->data, value->len); + } + + cache->crc = crc; + cache->key.len = key->len; + + cache->refs = 1; + cache->count = 0; + + cache->deleted = 0; + cache->expired = 0; + cache->memory = 0; + cache->mmap = 0; + cache->notify = 0; + + if (cleanup) { + cleanup->data.cache.hash = hash; + cleanup->data.cache.cache = cache; + cleanup->valid = 1; + cleanup->cache = 1; + } + + ngx_mutex_unlock(&hash->mutex); + + return cache; +} + + +void ngx_http_cache_free(ngx_http_cache_t *cache, + ngx_str_t *key, ngx_str_t *value, ngx_log_t *log) +{ + if (cache->memory) { + if (cache->data.value.data + && (value == NULL || value->len > cache->data.value.len)) + { + ngx_free(cache->data.value.data); + cache->data.value.data = NULL; + } + } + + /* TODO: mmap */ + + cache->data.value.len = 0; + + if (cache->fd != NGX_INVALID_FILE) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http cache close fd: %d", cache->fd); + + if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + cache->key.data); + } + + cache->fd = NGX_INVALID_FILE; + } + + if (cache->key.data && (key == NULL || key->len > cache->key.len)) { + ngx_free(cache->key.data); + cache->key.data = NULL; + } + + cache->key.len = 0; + + cache->refs = 0; +} + + +void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache) +{ + if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { + return; + } +} + + +void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, + ngx_http_cache_t *cache, ngx_log_t *log) +{ + if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { + return; + } + + cache->refs--; + + if (cache->refs == 0 && cache->deleted) { + ngx_http_cache_free(cache, NULL, NULL, log); + } + + ngx_mutex_unlock(&hash->mutex); +} + + +#if 0 + +ngx_http_cache_add_file_event(ngx_http_cache_hash_t *hash, + ngx_http_cache_t *cache) +{ + ngx_event_t *ev; + ngx_http_cache_event_ctx_t *ctx; + + ev = &ngx_cycle->read_events[fd]; + ngx_memzero(ev, sizeof(ngx_event_t); + + ev->data = data; + ev->event_handler = ngx_http_cache_invalidate; + + return ngx_add_event(ev, NGX_VNODE_EVENT, 0); +} + + +void ngx_http_cache_invalidate(ngx_event_t *ev) +{ + ngx_http_cache_event_ctx_t *ctx; + + ctx = ev->data; + + ngx_http_cache_lock(&ctx->hash->mutex); + + if (ctx->cache->refs == 0) + ngx_http_cache_free(ctx->cache, NULL, NULL, ctx->log); + + } else { + ctx->cache->deleted = 1; + } + + ngx_http_cache_unlock(&ctx->hash->mutex); +} + +#endif + + +/* TODO: currently fd only */ + +ngx_int_t ngx_http_send_cached(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_hunk_t *h; + ngx_chain_t out; + ngx_http_log_ctx_t *ctx; + + ctx = r->connection->log->data; + ctx->action = "sending response to client"; + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_length_n = r->cache->data.size; + r->headers_out.last_modified_time = r->cache->last_modified; + + if (ngx_http_set_content_type(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* we need to allocate all before the header would be sent */ + + if (!(h = ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!(h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST; + + h->file_pos = 0; + h->file_last = r->cache->data.size; + + h->file->fd = r->cache->fd; + h->file->log = r->connection->log; + + out.hunk = h; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} + + +char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_int_t i, j, dup, invalid; + ngx_str_t *value, line; + ngx_http_cache_t *c; + ngx_http_cache_hash_t *ch, **chp; + + chp = (ngx_http_cache_hash_t **) (p + cmd->offset); + if (*chp) { + return "is duplicate"; + } + + if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) { + return NGX_CONF_ERROR; + } + *chp = ch; + + dup = 0; + invalid = 0; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (value[i].data[1] != '=') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + switch (value[i].data[0]) { + + case 'h': + if (ch->hash) { + dup = 1; + break; + } + + ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2); + if (ch->hash == (size_t) NGX_ERROR || ch->hash == 0) { + invalid = 1; + break; + } + + continue; + + case 'n': + if (ch->nelts) { + dup = 1; + break; + } + + ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2); + if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) { + invalid = 1; + break; + } + + continue; + + case 'l': + if (ch->life) { + dup = 1; + break; + } + + line.len = value[i].len - 2; + line.data = value[i].data + 2; + + ch->life = ngx_parse_time(&line, 1); + if (ch->life == NGX_ERROR || ch->life == 0) { + invalid = 1; + break; + } + + continue; + + case 'u': + if (ch->update) { + dup = 1; + break; + } + + line.len = value[i].len - 2; + line.data = value[i].data + 2; + + ch->update = ngx_parse_time(&line, 1); + if (ch->update == NGX_ERROR || ch->update == 0) { + invalid = 1; + break; + } + + continue; + + default: + invalid = 1; + } + + if (dup) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + if (invalid) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + } + + ch->elts = ngx_pcalloc(cf->pool, + ch->hash * ch->nelts * sizeof(ngx_http_cache_t)); + if (ch->elts == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < (ngx_int_t) ch->hash; i++) { + c = ch->elts + i * ch->nelts; + + for (j = 0; j < (ngx_int_t) ch->nelts; j++) { + c[j].fd = NGX_INVALID_FILE; + } + } + + return NGX_CONF_OK; +}