/* * Copyright 2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * $Id: mod_lenya.c,v 1.13 2004/10/04 12:20:24 imboden Exp $ */ /* * Version: 0.3 */ /* * Documentation: * * This module caches http requests from the lenya cache directory, * if nothing is found in the cache directory it just passes the * request over to the next module. */ #include "httpd.h" #include "http_log.h" #include "http_config.h" #include "http_protocol.h" #include "ap_compat.h" #include #include #include #define PATH_LEN 255 #ifdef DDEBUG static inline void DEBUG(char *str, ...) { va_list vl; va_start(vl, str); vprintf(str,vl); va_end(vl); fflush(stdout); } #else static inline void DEBUG(char *str, ...) { } #endif /* prototypes */ static int lenya_url_handler(request_rec *r, int lookup); static void *lenya_create_server_config(apr_pool_t *p, server_rec *s); static void *lenya_merge_config(apr_pool_t *p, server_rec *s); static void register_hooks(apr_pool_t *p); static const char *conf_lcachedir(cmd_parms *parms, void *mconfig, const char *w); static const char *conf_lprefix(cmd_parms *parms, void *mconfig, const char *w); static const char *conf_lenablelog(cmd_parms *parms, void *mconfig, const char *w); typedef struct lenya_config { char *cachedir; int cachedir_slen; /* we precalc constant string lenght's */ char *prefix; int prefix_slen; char *dirindex; int dirindex_slen; int logging; int active; } LenyaConfig; /* globals */ module AP_MODULE_DECLARE_DATA lenya_module; static int lenya_url_handler(request_rec *r, int lookup) { char *curi,*dtype,*path; apr_status_t ret; apr_finfo_t fi; apr_size_t sb; apr_file_t *fl; LenyaConfig *cfg; cfg = ap_get_module_config(r->server->module_config, &lenya_module); DEBUG("ACTIVE: %i\n", cfg->active); if (!cfg->active) return DECLINED; if (r->method_number != M_GET) return DECLINED; DEBUG("LENYA MODULE: %i %p %p %p %s\n",r->proxyreq,r->prev,r->next,r->main,r->uri); /* cache only html documents above the prefix */ if ((curi = strstr(r->uri, cfg->prefix)) == NULL) return DECLINED; /* check if the reuest is for DirectoryIndex */ if (curi[1] == 0) { path = apr_palloc(r->pool, cfg->prefix_slen + cfg->dirindex_slen + 1 + 1); sprintf(path, "%s/%s", cfg->cachedir, cfg->dirindex); DEBUG("DEBUG: dirindex lookup: %s\n",path); } else { curi = &curi[cfg->prefix_slen]; if ((dtype = strrchr(curi, '.')) == NULL) return DECLINED; if (strlen(dtype) < 5) return DECLINED; /* only accept requests for .html */ if (strncmp(dtype, ".html", 5)) return DECLINED; DEBUG("dtype: %s\ncuri:%s\n",dtype,curi); path = apr_palloc(r->pool, strlen(curi) + cfg->cachedir_slen + 1 + 1); sprintf(path, "%s/%s", cfg->cachedir, curi); DEBUG("lookup: %s\n",path); } ret = apr_stat(&fi, path, APR_FINFO_SIZE , r->pool); if (ret != APR_SUCCESS) { DEBUG("DEBUG: COULD NOT stat file\n"); return DECLINED; } DEBUG("DEBUG: Reading from cache: %s\n", path); ret = apr_file_open(&fl, path, APR_READ, 0, r->pool); if (ret != APR_SUCCESS) { DEBUG("DEBUG: COULD NOT open file\n"); return DECLINED; } ap_set_content_length(r, fi.size); ap_set_content_type(r, "text/html"); if (ap_send_fd(fl, r, 0, (apr_size_t)fi.size, &sb) != APR_SUCCESS) { DEBUG("DEBUG: %i bytesd sent\n",sb); return DECLINED; } apr_file_close(fl); DEBUG("DEBUG: Sucessfully read from cache\n"); if (cfg->logging) ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "mod_lenya: cached %s",curi); return OK; } /* allocate and initialize per server config structures */ static void *create_lenya_server_config(apr_pool_t *p, server_rec *s) { LenyaConfig *cfg = apr_pcalloc(p, sizeof(LenyaConfig)); cfg->cachedir = NULL; cfg->cachedir_slen = 0; cfg->prefix = NULL; cfg->prefix_slen = 0; cfg->dirindex = NULL; cfg->dirindex_slen = 0; cfg->logging = 0; cfg->active = 0; return (LenyaConfig*)cfg; } /* register the callback function, it's a so called quick handler that gets called very early in the request processing cycle */ static void register_hooks(apr_pool_t *p) { ap_hook_quick_handler(lenya_url_handler, NULL, NULL, APR_HOOK_LAST); } static const char *conf_cachedir(cmd_parms *cmd, void *dummy, const char *arg) { LenyaConfig *cfg = ap_get_module_config(cmd->server->module_config, &lenya_module); cfg->cachedir = (char*)apr_pstrdup(cmd->pool, arg); cfg->cachedir_slen = strlen(cfg->cachedir); /* remove the final slash */ if (cfg->cachedir[cfg->cachedir_slen-1] == '/') { cfg->cachedir[cfg->cachedir_slen-1] = 0; cfg->cachedir_slen -= 1; } DEBUG("-> cache directory: %s\n", cfg->cachedir); DEBUG("-> active\n"); cfg->active = 1; return NULL; } static const char *conf_prefix(cmd_parms *cmd, void *dummy, const char *arg) { LenyaConfig *cfg = ap_get_module_config(cmd->server->module_config, &lenya_module); cfg->prefix = (char*)apr_pstrdup(cmd->pool, arg); cfg->prefix_slen = strlen(cfg->prefix); DEBUG("-> prefix: %s\n", cfg->prefix); return NULL; } static const char *conf_dirindex(cmd_parms *cmd, void *dummy, const char *arg) { LenyaConfig *cfg = ap_get_module_config(cmd->server->module_config, &lenya_module); cfg->dirindex = (char*)apr_pstrdup(cmd->pool, arg); cfg->dirindex_slen = strlen(cfg->dirindex); DEBUG("-> directory index: %s\n", cfg->dirindex); return NULL; } static const char *conf_logging(cmd_parms *cmd, void *dummy, int arg) { LenyaConfig *cfg = ap_get_module_config(cmd->server->module_config, &lenya_module); cfg->logging = arg; DEBUG("-> logging: %i\n", cfg->logging); return NULL; } static const command_rec lenya_cmds[] = { AP_INIT_TAKE1("LenyaCacheDir", conf_cachedir, NULL, RSRC_CONF, "Lenya's cache directory"), AP_INIT_TAKE1("Prefix", conf_prefix, NULL, RSRC_CONF, "Prefix"), AP_INIT_FLAG("Logging", conf_logging, NULL, RSRC_CONF, "Enable logging of Cache requests"), AP_INIT_TAKE1("Index", conf_dirindex, NULL, RSRC_CONF, "Specify the Directory Index"), {NULL} }; module AP_MODULE_DECLARE_DATA lenya_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-directory config structure */ NULL, /* merge per-directory config structures */ create_lenya_server_config, /* create per-server config structure */ NULL, /* merge per-server config structures */ lenya_cmds, /* command apr_table_t */ register_hooks /* register hooks */ };