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"
-#include "apr_strings.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
apr_file_t *file;
#endif
- char *filename;
+ const char *filename;
apr_finfo_t finfo;
int is_mmapped;
#if APR_HAS_MMAP
{
a_server_config *sconf = apr_palloc(p, sizeof(*sconf));
- sconf->files = apr_make_array(p, 20, sizeof(a_file));
+ sconf->files = apr_array_make(p, 20, sizeof(a_file));
return sconf;
}
-static apr_status_t open_file(apr_file_t **file, const char *filename, int flg1, int flg2,
- apr_pool_t *p)
-{
- apr_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 = apr_put_os_file(file, &hFile, p);
- }
- else {
- rv = GetLastError();
- *file = NULL;
- }
-#else
- rv = apr_open(file, filename, flg1, flg2, p);
-#endif
-
- return rv;
-}
-
static apr_status_t cleanup_file_cache(void *sconfv)
{
a_server_config *sconf = sconfv;
else
#endif
#if APR_HAS_SENDFILE
- apr_close(file->file);
+ apr_file_close(file->file);
#endif
++file;
--n;
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;
/* canonicalize the file name? */
/* os_canonical... */
- if (apr_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 apr_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 = apr_pstrdup(cmd->pool, filename);
sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
- new_file = apr_push_array(sconf->files);
+ new_file = apr_array_push(sconf->files);
*new_file = tmp;
if (sconf->files->nelts == 1) {
/* first one, register the cleanup */
- apr_register_cleanup(cmd->pool, sconf, cleanup_file_cache, apr_null_cleanup);
+ apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null);
}
new_file->is_mmapped = FALSE;
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
a_file *new_file;
a_file tmp;
apr_file_t *fd = NULL;
- char *fspec;
+ apr_status_t rc;
+ const char *fspec;
fspec = ap_os_case_canonical_filename(cmd->pool, filename);
- if (apr_stat(&tmp.finfo, fspec, cmd->temp_pool) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+ 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 (apr_open(&fd, fspec, 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 (apr_mmap_create(&tmp.mm, fd, 0, tmp.finfo.size, cmd->pool) != APR_SUCCESS) {
- int save_errno = errno;
- apr_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;
}
- apr_close(fd);
+ apr_file_close(fd);
tmp.filename = fspec;
sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
- new_file = apr_push_array(sconf->files);
+ new_file = apr_array_push(sconf->files);
*new_file = tmp;
if (sconf->files->nelts == 1) {
/* first one, register the cleanup */
- apr_register_cleanup(cmd->pool, sconf, cleanup_file_cache, apr_null_cleanup);
+ apr_pool_cleanup_register(cmd->pool, sconf, cleanup_file_cache, apr_pool_cleanup_null);
}
new_file->is_mmapped = TRUE;
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;
}
-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 {
- apr_size_t length;
- apr_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
- apr_size_t length, nbytes;
- apr_off_t offset = 0;
+ apr_size_t nbytes;
apr_status_t rv = APR_EINIT;
- if (!rangestatus) {
- rv = ap_send_fd(file->file, r, 0, file->finfo.size, &nbytes);
- }
- else {
- while (ap_each_byterange(r, &offset, &length)) {
- if ((rv = ap_send_fd(file->file, r, offset, length, &nbytes)) != APR_SUCCESS)
- break;
- }
+ /* 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.
+ */
+ if (r->output_filters && r->output_filters->frec) {
+ if (r->output_filters->frec->ftype == AP_FTYPE_CONTENT)
+ return DECLINED;
}
+
+
+ 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;
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;
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;
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 */
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 */
};