]> granicus.if.org Git - apache/blobdiff - modules/cache/mod_file_cache.c
*) continued header revamping
[apache] / modules / cache / mod_file_cache.c
index 23548d23f4a6d7b105a8ccce7fac36cfb018a009..6667f1ce7fb078ccdf97181a7cb077ec9a8fcda2 100644 (file)
     an extra stat() that's a waste.
 */
 
-#ifdef HAVE_STDIOP_H
-#include <stdio.h>
-#endif
-#ifdef HAVE_SYS_TYPES_H
+#include "apr.h"
+#include "apr_mmap.h"
+#include "apr_strings.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#if APR_HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
 
 #define CORE_PRIVATE
 
 #include "http_protocol.h"
 #include "http_request.h"
 #include "http_core.h"
-#include "apr_mmap.h"
 
-module MODULE_VAR_EXPORT file_cache_module;
-static int once_through = 0;
+module AP_MODULE_DECLARE_DATA file_cache_module;
 
 typedef struct {
 #if APR_HAS_SENDFILE
-    ap_file_t *file;
+    apr_file_t *file;
 #endif
-    char *filename;
-    ap_finfo_t finfo;
+    const char *filename;
+    apr_finfo_t finfo;
     int is_mmapped;
 #if APR_HAS_MMAP
-    ap_mmap_t *mm;
+    apr_mmap_t *mm;
 #endif
 } a_file;
 
 typedef struct {
-    ap_array_header_t *files;
+    apr_array_header_t *files;
 } a_server_config;
 
 
-static void *create_server_config(ap_pool_t *p, server_rec *s)
+static void *create_server_config(apr_pool_t *p, server_rec *s)
 {
-    a_server_config *sconf = ap_palloc(p, sizeof(*sconf));
+    a_server_config *sconf = apr_palloc(p, sizeof(*sconf));
 
-    sconf->files = ap_make_array(p, 20, sizeof(a_file));
+    sconf->files = apr_array_make(p, 20, sizeof(a_file));
     return sconf;
 }
 
-static ap_status_t open_file(ap_file_t **file, const char *filename, int flg1, int flg2, 
-                             ap_pool_t *p)
-{
-    ap_status_t rv;
-#ifdef WIN32
-    /* The Windows file needs to be opened for overlapped i/o, which APR doesn't
-     * support.
-     */
-    HANDLE hFile;
-    hFile = CreateFile(filename,          /* pointer to name of the file */
-                       GENERIC_READ,      /* access (read-write) mode */
-                       FILE_SHARE_READ,   /* share mode */
-                       NULL,              /* pointer to security attributes */
-                       OPEN_EXISTING,     /* how to create */
-                       FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, /* file attributes */
-                       NULL);            /* handle to file with attributes to copy */
-    if (hFile != INVALID_HANDLE_VALUE) {
-        rv = ap_put_os_file(file, &hFile, p);
-    }
-    else {
-        rv = GetLastError();
-        *file = NULL;
-    }
-#else
-    rv = ap_open(file, filename, flg1, flg2, p);
-#endif
-
-    return rv;
-}
-
-static ap_status_t cleanup_file_cache(void *sconfv)
+static apr_status_t cleanup_file_cache(void *sconfv)
 {
     a_server_config *sconf = sconfv;
     size_t n;
@@ -198,11 +167,13 @@ static ap_status_t cleanup_file_cache(void *sconfv)
     while(n) {
 #if APR_HAS_MMAP
         if (file->is_mmapped) { 
-           ap_mmap_delete(file->mm);
+           apr_mmap_delete(file->mm);
         } 
         else 
 #endif 
-            ap_close(file->file); 
+#if APR_HAS_SENDFILE
+            apr_file_close(file->file); 
+#endif
            ++file;
            --n;
     }
@@ -212,44 +183,48 @@ static ap_status_t cleanup_file_cache(void *sconfv)
 static const char *cachefile(cmd_parms *cmd, void *dummy, const char *filename)
 
 {
+    /* ToDo:
+     * Disable the file cache on a Windows 9X box. APR_HAS_SENDFILE will be
+     * defined in an Apache for Windows build, but apr_sendfile returns
+     * APR_ENOTIMPL on Windows 9X because TransmitFile is not available.
+     */
+
 #if APR_HAS_SENDFILE
     a_server_config *sconf;
     a_file *new_file;
     a_file tmp;
-    ap_file_t *fd = NULL;
-    ap_status_t rc;
+    apr_file_t *fd = NULL;
+    apr_status_t rc;
 
     /* canonicalize the file name? */
     /* os_canonical... */
-    if (ap_stat(&tmp.finfo, filename, cmd->temp_pool) != APR_SUCCESS) { 
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-           "file_cache: unable to stat(%s), skipping", filename);
+    /* XXX: uh... yea, or expect them to be -very- accurate typists */
+    if ((rc = apr_stat(&tmp.finfo, filename, 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", filename);
        return NULL;
     }
     if (tmp.finfo.filetype != APR_REG) {
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-           "file_cache: %s isn't a regular file, skipping", filename);
+       ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
+           "mod_file_cache: %s isn't a regular file, skipping", filename);
        return NULL;
     }
 
-    /* Note: open_file should call ap_open for Unix and CreateFile for Windows.
-     * The Windows file needs to be opened for async I/O to allow multiple threads
-     * to serve it up at once.
-     */
-    rc = open_file(&fd, filename, APR_READ, APR_OS_DEFAULT, cmd->pool);
+    rc = apr_file_open(&fd, filename, APR_READ | APR_XTHREAD, APR_OS_DEFAULT, cmd->pool);
     if (rc != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
-                     "file_cache: unable to open(%s, O_RDONLY), skipping", filename);
+                     "mod_file_cache: unable to open(%s, O_RDONLY), skipping", filename);
        return NULL;
     }
     tmp.file = fd;
-    tmp.filename = ap_pstrdup(cmd->pool, filename);
+    tmp.filename = apr_pstrdup(cmd->pool, filename);
     sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
-    new_file = ap_push_array(sconf->files);
+    new_file = apr_array_push(sconf->files);
     *new_file = tmp;
     if (sconf->files->nelts == 1) {
        /* first one, register the cleanup */
-       ap_register_cleanup(cmd->pool, sconf, cleanup_file_cache, ap_null_cleanup);
+       apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null);
     }
 
     new_file->is_mmapped = FALSE;
@@ -257,7 +232,7 @@ static const char *cachefile(cmd_parms *cmd, void *dummy, const char *filename)
     return NULL;
 #else
     /* Sendfile not supported on this platform */
-    ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+    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);
     return NULL;
 #endif
@@ -269,40 +244,43 @@ static const char *mmapfile(cmd_parms *cmd, void *dummy, const char *filename)
     a_server_config *sconf;
     a_file *new_file;
     a_file tmp;
-    ap_file_t *fd = NULL;
-
-    if (ap_stat(&tmp.finfo, filename, cmd->temp_pool) != APR_SUCCESS) { 
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+    apr_file_t *fd = NULL;
+    apr_status_t rc;
+    const char *fspec;
+
+    fspec = ap_os_case_canonical_filename(cmd->pool, filename);
+    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", filename);
        return NULL;
     }
     if ((tmp.finfo.filetype) != APR_REG) {
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+       ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, cmd->server,
            "mod_file_cache: %s isn't a regular file, skipping", filename);
        return NULL;
     }
-    if (ap_open(&fd, filename, APR_READ, APR_OS_DEFAULT, cmd->temp_pool) 
-                != APR_SUCCESS) { 
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-           "mod_file_cache: unable to open(%s, O_RDONLY), skipping", filename);
+    if ((rc = apr_file_open(&fd, fspec, APR_READ, APR_OS_DEFAULT, 
+                       cmd->temp_pool)) != APR_SUCCESS) { 
+       ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
+                     "mod_file_cache: unable to open %s, skipping", 
+                     filename);
        return NULL;
     }
-    if (ap_mmap_create(&tmp.mm, fd, 0, tmp.finfo.size, cmd->pool) != APR_SUCCESS) { 
-       int save_errno = errno;
-       ap_close(fd);
-       errno = save_errno;
-       ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+    if ((rc = apr_mmap_create(&tmp.mm, fd, 0, tmp.finfo.size, APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) { 
+       apr_file_close(fd);
+       ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
            "mod_file_cache: unable to mmap %s, skipping", filename);
        return NULL;
     }
-    ap_close(fd);
-    tmp.filename = ap_pstrdup(cmd->pool, filename);
+    apr_file_close(fd);
+    tmp.filename = fspec;
     sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
-    new_file = ap_push_array(sconf->files);
+    new_file = apr_array_push(sconf->files);
     *new_file = tmp;
     if (sconf->files->nelts == 1) {
        /* first one, register the cleanup */
-       ap_register_cleanup(cmd->pool, sconf, cleanup_file_cache, ap_null_cleanup); 
+       apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null); 
     }
 
     new_file->is_mmapped = TRUE;
@@ -323,15 +301,13 @@ static int file_compare(const void *av, const void *bv)
     return strcmp(a->filename, b->filename);
 }
 
-static void file_cache_post_config(ap_pool_t *p, ap_pool_t *plog,
-                                   ap_pool_t *ptemp, server_rec *s)
+static void file_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
+                                   apr_pool_t *ptemp, server_rec *s)
 {
     a_server_config *sconf;
     a_file *elts;
     int nelts;
 
-    once_through++;
-
     /* sort the elements of the main_server, by filename */
     sconf = ap_get_module_config(s->module_config, &file_cache_module);
     elts = (a_file *)sconf->files->elts;
@@ -357,7 +333,7 @@ static int file_cache_xlat(request_rec *r)
     sconf = ap_get_module_config(r->server->module_config, &file_cache_module);
 
     /* we only operate when at least one cachefile directive was used */
-    if (ap_is_empty_table(sconf->files))
+    if (apr_is_empty_table(sconf->files))
        return DECLINED;
 
     res = ap_core_translate(r);
@@ -381,88 +357,46 @@ static int file_cache_xlat(request_rec *r)
 }
 
 
-static int mmap_handler(request_rec *r, a_file *file, int rangestatus)
+static int mmap_handler(request_rec *r, a_file *file)
 {
 #if APR_HAS_MMAP
-    if (!rangestatus) {
-        ap_send_mmap (file->mm, r, 0, file->finfo.size);
-    }
-    else {
-        long length;
-        ap_off_t offset;
-        while (ap_each_byterange(r, &offset, &length)) {
-            ap_send_mmap(file->mm, r, offset, length);
-        }
-    }
+    ap_send_mmap (file->mm, r, 0, file->finfo.size);
 #endif
     return OK;
 }
 
-
-static int sendfile_handler(request_rec *r, a_file *file, int rangestatus)
+static int sendfile_handler(request_rec *r, a_file *file)
 {
 #if APR_HAS_SENDFILE
-    long length;
-    ap_off_t offset = 0;
-    struct iovec iov;
-    ap_hdtr_t hdtr;
-    ap_hdtr_t *phdtr = &hdtr;
-    ap_status_t rv; 
-    ap_int32_t flags = 0;
-
-    /* 
-     * We want to send any data held in the client buffer on the
-     * call to iol_sendfile. So hijack it then set outcnt to 0
-     * to prevent the data from being sent to the client again
-     * when the buffer is flushed to the client at the end of the 
-     * request.
+    apr_size_t nbytes;
+    apr_status_t rv = APR_EINIT;
+
+    /* A cached file handle (more importantly, its file pointer) is 
+     * shared by all threads in the process. The file pointer will 
+     * be corrupted if multiple threads attempt to read from the 
+     * cached file handle. The sendfile API does not rely on the position 
+     * of the file pointer instead taking explicit file offset and 
+     * length arguments. 
+     *
+     * We should call ap_send_fd with a cached file handle IFF 
+     * we are CERTAIN the file will be served with apr_sendfile(). 
+     * The presense of an AP_FTYPE_FILTER in the filter chain nearly
+     * guarantees that apr_sendfile will NOT be used to send the file.
+     * Furthermore, AP_FTYPE_CONTENT filters will be at the beginning
+     * of the chain, so it should suffice to just check the first 
+     * filter in the chain. If the first filter is not a content filter, 
+     * assume apr_sendfile() will be used to send the content.
      */
-    iov.iov_base = r->connection->client->outbase;
-    iov.iov_len =  r->connection->client->outcnt;
-    r->connection->client->outcnt = 0;
-
-    /* initialize the ap_hdtr_t struct */
-    phdtr->headers = &iov;
-    phdtr->numheaders = 1;
-    phdtr->trailers = NULL;
-    phdtr->numtrailers = 0;
-
-    if (!rangestatus) {
-        length = file->finfo.size;
-
-        if (!r->connection->keepalive) {
-            /* Disconnect the socket after the send completes. This
-             * should leave the accept socket in a state ready to be
-             * reused for the next connection.
-             */
-            flags |= APR_SENDFILE_DISCONNECT_SOCKET;
-        }
-
-        rv = iol_sendfile(r->connection->client->iol, 
-                     file->file,
-                     phdtr,
-                     &offset,
-                     &length,
-                     flags);
-        if (rv != APR_SUCCESS) { 
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 
-                          "mod_file_cache: iol_sendfile failed."); 
+    if (r->output_filters && r->output_filters->frec) {
+        if (r->output_filters->frec->ftype == AP_FTYPE_CONTENT)
+            return DECLINED;
     }
-    } 
-    else {
-        while (ap_each_byterange(r, &offset, &length)) {
-            rv =iol_sendfile(r->connection->client->iol, 
-                         file->file,
-                         phdtr,
-                         &offset,
-                         &length,
-                             0); 
-            if (rv != APR_SUCCESS) { 
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 
-                              "mod_file_cache: iol_sendfile failed."); 
-            } 
-            phdtr = NULL;
-        }
+
+
+    rv = ap_send_fd(file->file, r, 0, file->finfo.size, &nbytes);
+    if (rv != APR_SUCCESS) {
+        /* ap_send_fd will log the error */
+        return HTTP_INTERNAL_SERVER_ERROR;
     }
 #endif
     return OK;
@@ -471,9 +405,16 @@ static int sendfile_handler(request_rec *r, a_file *file, int rangestatus)
 static int file_cache_handler(request_rec *r) 
 {
     a_file *match;
-    int rangestatus, errstatus;
+    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;
 
@@ -496,20 +437,19 @@ static int file_cache_handler(request_rec *r)
     ap_update_mtime(r, match->finfo.mtime);
     ap_set_last_modified(r);
     ap_set_etag(r);
-    if (((errstatus = ap_meets_conditions(r)) != OK)
-       || (errstatus = ap_set_content_length (r, match->finfo.size))) {
-           return errstatus;
+    if ((errstatus = ap_meets_conditions(r)) != OK) {
+       return errstatus;
     }
+    ap_set_content_length(r, match->finfo.size);
 
-    rangestatus = ap_set_byterange(r);
     ap_send_http_header(r);
 
     /* Call appropriate handler */
     if (!r->header_only) {    
         if (match->is_mmapped == TRUE)
-            rc = mmap_handler(r, match, rangestatus);
+            rc = mmap_handler(r, match);
         else
-            rc = sendfile_handler(r, match, rangestatus);
+            rc = sendfile_handler(r, match);
     }
 
     return rc;
@@ -518,31 +458,26 @@ static int file_cache_handler(request_rec *r)
 static command_rec file_cache_cmds[] =
 {
 AP_INIT_ITERATE("cachefile", cachefile, NULL, RSRC_CONF,
-     "A space seperated list of files to add to the file handle cache at config time"),
+     "A space separated list of files to add to the file handle cache at config time"),
 AP_INIT_ITERATE("mmapfile", mmapfile, NULL, RSRC_CONF,
-     "A space seperated list of files to mmap at config time"),
+     "A space separated list of files to mmap at config time"),
     {NULL}
 };
 
-static void register_hooks(void)
+static void register_hooks(apr_pool_t *p)
 {
-    ap_hook_post_config(file_cache_post_config, NULL, NULL, AP_HOOK_MIDDLE);
-    ap_hook_translate_name(file_cache_xlat, NULL, NULL, AP_HOOK_MIDDLE);
+    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, AP_HOOK_MIDDLE); 
+    ap_hook_translate_name(file_cache_xlat, aszPre, NULL, APR_HOOK_MIDDLE); 
     */
 
-};
-
-static const handler_rec file_cache_handlers[] =
-{
-    { "*/*", file_cache_handler },
-    { NULL }
-};
+}
 
-module MODULE_VAR_EXPORT file_cache_module =
+module AP_MODULE_DECLARE_DATA file_cache_module =
 {
     STANDARD20_MODULE_STUFF,
     NULL,                     /* create per-directory config structure */
@@ -550,6 +485,5 @@ module MODULE_VAR_EXPORT file_cache_module =
     create_server_config,     /* create per-server config structure */
     NULL,                     /* merge per-server config structures */
     file_cache_cmds,          /* command handlers */
-    file_cache_handlers,      /* handlers */
     register_hooks            /* register hooks */
 };