/* * Based on original work found on * http://www.atm.cs.ndsu.nodak.edu/~tatarino/apache */ #include "mod_cache.h" /* for core_dir_config */ #define CORE_PRIVATE #include "http_core.h" #include "ap_md5.h" #include "util_md5.h" #include "http_protocol.h" #include "http_log.h" #include "http_request.h" static void cache_cleanup(void *cf) { cache_conf *cfg = cf; if (cfg->mmap_cache_size > 0) mmap_cache_exit(); } static void cache_init(server_rec * r, pool * p) { cache_conf *cfg = ap_get_module_config(r->module_config, &cache_module); if (cfg->mmap_cache_size > 0) mmap_cache_init(cfg->mmap_cache_size); ap_register_cleanup(p, cfg, cache_cleanup, cache_cleanup); } static void *create_cache_config(pool * p, server_rec * s) { cache_conf *cfg = ap_pcalloc(p, sizeof(cache_conf)); cfg->mmap_cache_size = 0; cfg->mmap_cache_threshold = MMAP_CACHE_DEFAULT_THRESHOLD; return cfg; } static const char *set_mmap_cache_size(cmd_parms * parms, char *ptr, char *arg) { cache_conf *cfg = ap_get_module_config(parms->server->module_config, &cache_module); int val; if (sscanf(arg, "%d", &val) != 1 || val < 0) return "MmapCacheSize must be an integer >= 0"; cfg->mmap_cache_size = val; ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_NOTICE, NULL, "Setting mmap cache size to %d", val); return NULL; } static const char *set_mmap_cache_threshold(cmd_parms * parms, char *ptr, char *arg) { cache_conf *cfg = ap_get_module_config(parms->server->module_config, &cache_module); int val; if (sscanf(arg, "%d", &val) != 1 || val < 0) return "MmapCacheThreshold must be an integer >= 0"; cfg->mmap_cache_threshold = val; ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_NOTICE, NULL, "Setting mmap cache threshold to %d", val); return NULL; } static int cache_handler(request_rec * r) { /* just follow default_handler mostly */ cache_conf *cfg = ap_get_module_config(r->server->module_config, &cache_module); core_dir_config *d = ap_get_module_config(r->per_dir_config, &core_module); int rangestatus, errstatus; caddr_t mmap_addr; /* fprintf(stderr, "Handling %s\n", r->filename); */ if (cfg->mmap_cache_size == 0) return DECLINED; if (r->finfo.st_size < cfg->mmap_cache_threshold || (r->header_only && !(d->content_md5 & 1))) return DECLINED; if ((errstatus = ap_discard_request_body(r)) != OK) return errstatus; r->allowed |= (1 << M_GET); r->allowed |= (1 << M_OPTIONS); if (r->method_number == M_INVALID) { ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r->server, "Invalid method (%s) in request %s", r->method, r->the_request); return NOT_IMPLEMENTED; } if (r->method_number == M_OPTIONS) return ap_send_http_options(r); if (r->method_number == M_PUT) return METHOD_NOT_ALLOWED; if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "File does not exist: %s", r->path_info ? ap_pstrcat(r->pool, r->filename, r->path_info, NULL) : r->filename); return NOT_FOUND; } if (r->method_number != M_GET) return METHOD_NOT_ALLOWED; ap_update_mtime(r, r->finfo.st_mtime); ap_set_last_modified(r); ap_set_etag(r); if (((errstatus = ap_meets_conditions(r)) != OK) || (errstatus = ap_set_content_length(r, r->finfo.st_size))) { return errstatus; } ap_block_alarms(); /* the file will be opened here. We were supposed to do that before setting last modified, etc. Hopefully, it's not a big problem. (occasional "No permission" with last_modified header) */ errstatus = mmap_cache_handle_request(r, &mmap_addr); ap_unblock_alarms(); if (errstatus != OK) return errstatus; if (d->content_md5 & 1) { AP_MD5_CTX context; ap_MD5Init(&context); ap_MD5Update(&context, (void *) mmap_addr, r->finfo.st_size); ap_table_set(r->headers_out, "Content-MD5", ap_md5contextTo64(r->pool, &context)); } rangestatus = ap_set_byterange(r); ap_send_http_header(r); if (!r->header_only) { if (!rangestatus) ap_send_mmap(mmap_addr, r, 0, r->finfo.st_size); else { long offset, length; while (ap_each_byterange(r, &offset, &length)) ap_send_mmap(mmap_addr, r, offset, length); } } return OK; } static handler_rec cache_handlers[] = { { "*/*", cache_handler }, { NULL, NULL } }; static command_rec cache_cmds[] = { { "MmapCacheSize", set_mmap_cache_size, NULL, RSRC_CONF, TAKE1, "mmap cache size in entries" }, { "MmapCacheThreshold", set_mmap_cache_threshold, NULL, RSRC_CONF, TAKE1, "mmap cache threshold in bytes" }, { NULL, NULL } }; module MODULE_VAR_EXPORT cache_module = { STANDARD_MODULE_STUFF, cache_init, /* module initializer */ NULL, /* per-directory config creator */ NULL, /* dir config merger */ create_cache_config, /* server config creator */ NULL, /* server config merger */ cache_cmds, /* command table */ cache_handlers, /* [7] list of handlers */ NULL, /* [2] filename-to-URI translation */ NULL, /* [5] check/validate user_id */ NULL, /* [6] check user_id is valid *here* */ NULL, /* [4] check access by host address */ NULL, /* [7] MIME type checker/setter */ NULL, /* [8] fixups */ NULL, /* [10] logger */ NULL, /* [3] header parser */ NULL, /* process initializer */ NULL, /* process exit/cleanup */ NULL /* [1] post read_request handling */ };