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;
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;
}
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;
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_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;
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;
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);
}
-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;
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 */
};