diff options
Diffstat (limited to 'modules/cache/mod_file_cache.c')
-rw-r--r-- | modules/cache/mod_file_cache.c | 489 |
1 files changed, 0 insertions, 489 deletions
diff --git a/modules/cache/mod_file_cache.c b/modules/cache/mod_file_cache.c deleted file mode 100644 index c1682b5c5e..0000000000 --- a/modules/cache/mod_file_cache.c +++ /dev/null @@ -1,489 +0,0 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2000-2002 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, - * if and wherever such third-party acknowledgments normally appear. - * - * 4. The names "Apache" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - */ - -/* - * Author: mod_file_cache by Bill Stoddard <stoddard@apache.org> - * Based on mod_mmap_static by Dean Gaudet <dgaudet@arctic.org> - * - * v0.01: initial implementation - */ - -/* - Documentation: - - Some sites have a set of static files that are really busy, and - change infrequently (or even on a regular schedule). Save time - by caching open handles to these files. This module, unlike - mod_mmap_static, caches open file handles, not file content. - On systems (like Windows) with heavy system call overhead and - that have an efficient sendfile implementation, caching file handles - offers several advantages over caching content. First, the file system - can manage the memory, allowing infrequently hit cached files to - be paged out. Second, since caching open handles does not consume - significant resources, it will be possible to enable an AutoLoadCache - feature where static files are dynamically loaded in the cache - as the server runs. On systems that have file change notification, - this module can be enhanced to automatically garbage collect - cached files that change on disk. - - This module should work on Unix systems that have sendfile. Place - cachefile directives into your configuration to direct files to - be cached. - - cachefile /path/to/file1 - cachefile /path/to/file2 - ... - - These files are only cached when the server is restarted, so if you - change the list, or if the files are changed, then you'll need to - restart the server. - - To reiterate that point: if the files are modified *in place* - without restarting the server you may end up serving requests that - are completely bogus. You should update files by unlinking the old - copy and putting a new copy in place. - - There's no such thing as inheriting these files across vhosts or - whatever... place the directives in the main server only. - - Known problems: - - Don't use Alias or RewriteRule to move these files around... unless - you feel like paying for an extra stat() on each request. This is - a deficiency in the Apache API that will hopefully be solved some day. - The file will be served out of the file handle cache, but there will be - an extra stat() that's a waste. -*/ - -#include "apr.h" - -#if !(APR_HAS_SENDFILE || APR_HAS_MMAP) -#error mod_file_cache only works on systems with APR_HAS_SENDFILE or APR_HAS_MMAP -#endif - -#include "apr_mmap.h" -#include "apr_strings.h" -#include "apr_hash.h" -#include "apr_buckets.h" - -#define APR_WANT_STRFUNC -#include "apr_want.h" - -#if APR_HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif - -#define CORE_PRIVATE - -#include "httpd.h" -#include "http_config.h" -#include "http_log.h" -#include "http_protocol.h" -#include "http_request.h" -#include "http_core.h" - -module AP_MODULE_DECLARE_DATA file_cache_module; - -typedef struct { -#if APR_HAS_SENDFILE - apr_file_t *file; -#endif - const char *filename; - apr_finfo_t finfo; - int is_mmapped; -#if APR_HAS_MMAP - apr_mmap_t *mm; -#endif - char mtimestr[APR_RFC822_DATE_LEN]; - char sizestr[21]; /* big enough to hold any 64-bit file size + null */ -} a_file; - -typedef struct { - apr_hash_t *fileht; -} a_server_config; - - -static void *create_server_config(apr_pool_t *p, server_rec *s) -{ - a_server_config *sconf = apr_palloc(p, sizeof(*sconf)); - - sconf->fileht = apr_hash_make(p); - return sconf; -} - -static apr_status_t cleanup_file_cache(void *sconfv) -{ - a_server_config *sconf = sconfv; - apr_pool_t *p = apr_hash_pool_get(sconf->fileht); - a_file *file; - apr_hash_index_t *hi; - - /* Iterate over the file hash table and clean up each entry */ - for (hi = apr_hash_first(p, sconf->fileht); hi; hi=apr_hash_next(hi)) { - apr_hash_this(hi, NULL, NULL, (void **)&file); -#if APR_HAS_MMAP - if (file->is_mmapped) { - apr_mmap_delete(file->mm); - } -#endif -#if APR_HAS_SENDFILE - if (!file->is_mmapped) { - apr_file_close(file->file); - } -#endif - } - return APR_SUCCESS; -} - -static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap) -{ - a_server_config *sconf; - a_file *new_file; - a_file tmp; - apr_file_t *fd = NULL; - apr_status_t rc; - const char *fspec; - - fspec = ap_server_root_relative(cmd->pool, filename); - if (!fspec) { - ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server, - "mod_file_cache: invalid file path " - "%s, skipping", filename); - return; - } - if ((rc = apr_stat(&tmp.finfo, fspec, APR_FINFO_MIN, - cmd->temp_pool)) != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, - "mod_file_cache: unable to stat(%s), skipping", fspec); - return; - } - if (tmp.finfo.filetype != APR_REG) { - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server, - "mod_file_cache: %s isn't a regular file, skipping", fspec); - return; - } - if (tmp.finfo.size > AP_MAX_SENDFILE) { - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server, - "mod_file_cache: %s is too large to cache, skipping", fspec); - return; - } - - rc = apr_file_open(&fd, fspec, APR_READ | APR_BINARY | APR_XTHREAD, - APR_OS_DEFAULT, cmd->pool); - if (rc != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, - "mod_file_cache: unable to open(%s, O_RDONLY), skipping", fspec); - return; - } - apr_file_set_inherit(fd); - - /* WooHoo, we have a file to put in the cache */ - new_file = apr_pcalloc(cmd->pool, sizeof(a_file)); - new_file->finfo = tmp.finfo; - -#if APR_HAS_MMAP - if (mmap) { - apr_mmap_t *mm; - - /* MMAPFile directive. MMAP'ing the file - * XXX: APR_HAS_LARGE_FILES issue; need to reject this request if - * size is greater than MAX(apr_size_t) (perhaps greater than 1M?). - */ - if ((rc = apr_mmap_create(&mm, fd, 0, - (apr_size_t)new_file->finfo.size, - APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) { - apr_file_close(fd); - /* We want to cache an apr_mmap_t that's marked as "non-owner" - * to pass to each request so that mmap_setaside()'s call to - * apr_mmap_dup() will never try to move the apr_mmap_t to a - * different pool. This apr_mmap_t is already going to live - * longer than any request, but mmap_setaside() has no way to - * know that because it's allocated out of cmd->pool, - * which is disjoint from r->pool. - */ - apr_mmap_dup(&new_file->mm, mm, cmd->pool, 0); - ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, - "mod_file_cache: unable to mmap %s, skipping", filename); - return; - } - apr_file_close(fd); - new_file->is_mmapped = TRUE; - } -#endif -#if APR_HAS_SENDFILE - if (!mmap) { - /* CacheFile directive. Caching the file handle */ - new_file->is_mmapped = FALSE; - new_file->file = fd; - } -#endif - - new_file->filename = fspec; - apr_rfc822_date(new_file->mtimestr, new_file->finfo.mtime); - apr_snprintf(new_file->sizestr, sizeof new_file->sizestr, "%" APR_OFF_T_FMT, new_file->finfo.size); - - sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module); - apr_hash_set(sconf->fileht, new_file->filename, strlen(new_file->filename), new_file); - - if (apr_hash_count(sconf->fileht) == 1) { - /* first one, register the cleanup */ - apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null); - } -} - -static const char *cachefilehandle(cmd_parms *cmd, void *dummy, const char *filename) -{ -#if APR_HAS_SENDFILE - cache_the_file(cmd, filename, 0); -#else - /* Sendfile not supported by this OS */ - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server, - "mod_file_cache: unable to cache file: %s. Sendfile is not supported on this OS", filename); -#endif - return NULL; -} -static const char *cachefilemmap(cmd_parms *cmd, void *dummy, const char *filename) -{ -#if APR_HAS_MMAP - cache_the_file(cmd, filename, 1); -#else - /* MMAP not supported by this OS */ - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server, - "mod_file_cache: unable to cache file: %s. MMAP is not supported by this OS", filename); -#endif - return NULL; -} - -static int file_cache_post_config(apr_pool_t *p, apr_pool_t *plog, - apr_pool_t *ptemp, server_rec *s) -{ - /* Hummm, anything to do here? */ - return OK; -} - -/* If it's one of ours, fill in r->finfo now to avoid extra stat()... this is a - * bit of a kludge, because we really want to run after core_translate runs. - */ -static int file_cache_xlat(request_rec *r) -{ - a_server_config *sconf; - a_file *match; - int res; - - sconf = ap_get_module_config(r->server->module_config, &file_cache_module); - - /* we only operate when at least one cachefile directive was used */ - if (!apr_hash_count(sconf->fileht)) { - return DECLINED; - } - - res = ap_core_translate(r); - if (res != OK || !r->filename) { - return res; - } - - /* search the cache */ - match = (a_file *) apr_hash_get(sconf->fileht, r->filename, APR_HASH_KEY_STRING); - if (match == NULL) - return DECLINED; - - /* pass search results to handler */ - ap_set_module_config(r->request_config, &file_cache_module, match); - - /* shortcircuit the get_path_info() stat() calls and stuff */ - r->finfo = match->finfo; - return OK; -} - -static int mmap_handler(request_rec *r, a_file *file) -{ -#if APR_HAS_MMAP - apr_bucket *b; - apr_bucket_brigade *bb = apr_brigade_create(r->pool); - - b = apr_bucket_mmap_create(file->mm, 0, (apr_size_t)file->finfo.size); - APR_BRIGADE_INSERT_TAIL(bb, b); - b = apr_bucket_eos_create(); - APR_BRIGADE_INSERT_TAIL(bb, b); - - if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) - return HTTP_INTERNAL_SERVER_ERROR; -#endif - return OK; -} - -static int sendfile_handler(request_rec *r, a_file *file) -{ -#if APR_HAS_SENDFILE - apr_bucket *b; - apr_bucket_brigade *bb = apr_brigade_create(r->pool); - - b = apr_bucket_file_create(file->file, 0, - (apr_size_t)file->finfo.size, r->pool); - APR_BRIGADE_INSERT_TAIL(bb, b); - b = apr_bucket_eos_create(); - APR_BRIGADE_INSERT_TAIL(bb, b); - - if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) - return HTTP_INTERNAL_SERVER_ERROR; -#endif - return OK; -} - -static int file_cache_handler(request_rec *r) -{ - a_file *match; - int errstatus; - int rc = OK; - - /* XXX: not sure if this is right yet - * see comment in http_core.c:default_handler - */ - if (ap_strcmp_match(r->handler, "*/*")) { - return DECLINED; - } - - /* we don't handle anything but GET */ - if (r->method_number != M_GET) return DECLINED; - - /* did xlat phase find the file? */ - match = ap_get_module_config(r->request_config, &file_cache_module); - - if (match == NULL) { - return DECLINED; - } - - /* note that we would handle GET on this resource */ - r->allowed |= (AP_METHOD_BIT << M_GET); - - /* This handler has no use for a request body (yet), but we still - * need to read and discard it if the client sent one. - */ - if ((errstatus = ap_discard_request_body(r)) != OK) - return errstatus; - - ap_update_mtime(r, match->finfo.mtime); - - /* ap_set_last_modified() always converts the file mtime to a string - * which is slow. Accelerate the common case. - * ap_set_last_modified(r); - */ - { - apr_time_t mod_time; - char *datestr; - - mod_time = ap_rationalize_mtime(r, r->mtime); - if (mod_time == match->finfo.mtime) - datestr = match->mtimestr; - else { - datestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN); - apr_rfc822_date(datestr, mod_time); - } - apr_table_setn(r->headers_out, "Last-Modified", datestr); - } - - ap_set_etag(r); - if ((errstatus = ap_meets_conditions(r)) != OK) { - return errstatus; - } - - /* ap_set_content_length() always converts the same number and never - * returns an error. Accelerate it. - */ - r->clength = match->finfo.size; - apr_table_setn(r->headers_out, "Content-Length", match->sizestr); - - /* Call appropriate handler */ - if (!r->header_only) { - if (match->is_mmapped == TRUE) - rc = mmap_handler(r, match); - else - rc = sendfile_handler(r, match); - } - - return rc; -} - -static command_rec file_cache_cmds[] = -{ -AP_INIT_ITERATE("cachefile", cachefilehandle, NULL, RSRC_CONF, - "A space separated list of files to add to the file handle cache at config time"), -AP_INIT_ITERATE("mmapfile", cachefilemmap, NULL, RSRC_CONF, - "A space separated list of files to mmap at config time"), - {NULL} -}; - -static void register_hooks(apr_pool_t *p) -{ - ap_hook_handler(file_cache_handler, NULL, NULL, APR_HOOK_LAST); - ap_hook_post_config(file_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_translate_name(file_cache_xlat, NULL, NULL, APR_HOOK_MIDDLE); - /* This trick doesn't work apparently because the translate hooks - are single shot. If the core_hook returns OK, then our hook is - not called. - ap_hook_translate_name(file_cache_xlat, aszPre, NULL, APR_HOOK_MIDDLE); - */ - -} - -module AP_MODULE_DECLARE_DATA file_cache_module = -{ - STANDARD20_MODULE_STUFF, - NULL, /* create per-directory config structure */ - NULL, /* merge per-directory config structures */ - create_server_config, /* create per-server config structure */ - NULL, /* merge per-server config structures */ - file_cache_cmds, /* command handlers */ - register_hooks /* register hooks */ -}; |