#include "http_request.h"
#include "http_core.h"
#include "apr_mmap.h"
+#include "apr_strings.h"
-module MODULE_VAR_EXPORT file_cache_module;
-static ap_pool_t *pconf;
-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_make_array(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)
+#if APR_HAS_SENDFILE
+static apr_status_t open_file(apr_file_t **file, const char *filename, int flg1, int flg2,
+ apr_pool_t *p)
{
- ap_status_t rv;
+ apr_status_t rv;
#ifdef WIN32
/* The Windows file needs to be opened for overlapped i/o, which APR doesn't
* support.
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);
+ rv = apr_put_os_file(file, &hFile, p);
}
else {
rv = GetLastError();
*file = NULL;
}
#else
- rv = ap_open(file, filename, flg1, flg2, p);
+ rv = apr_open(file, filename, flg1, flg2, p);
#endif
return rv;
}
+#endif /* APR_HAS_SENDFILE */
-#if APR_HAS_SENDFILE
-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;
n = sconf->files->nelts;
file = (a_file *)sconf->files->elts;
while(n) {
- ap_close(file->file);
- ++file;
- --n;
- }
- return APR_SUCCESS;
-}
-#endif
#if APR_HAS_MMAP
-static ap_status_t cleanup_mmap(void *sconfv)
-{
- a_server_config *sconf = sconfv;
- size_t n;
- a_file *file;
-
- n = sconf->files->nelts;
- file = (a_file *)sconf->files->elts;
- while(n) {
- ap_mmap_delete(file->mm);
+ if (file->is_mmapped) {
+ apr_mmap_delete(file->mm);
+ }
+ else
+#endif
+#if APR_HAS_SENDFILE
+ apr_close(file->file);
+#endif
++file;
--n;
}
return APR_SUCCESS;
}
-#endif
static const char *cachefile(cmd_parms *cmd, void *dummy, const char *filename)
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, NULL) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
- "file_cache: unable to stat(%s), skipping", filename);
+ if ((rc = apr_stat(&tmp.finfo, filename, 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.
+ /* 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);
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_push_array(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_register_cleanup(cmd->pool, sconf, cleanup_file_cache, apr_null_cleanup);
}
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;
+ apr_file_t *fd = NULL;
+ apr_status_t rc;
+ const char *fspec;
- if (ap_stat(&tmp.finfo, filename, pconf) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
+ fspec = ap_os_case_canonical_filename(cmd->pool, filename);
+ if ((rc = apr_stat(&tmp.finfo, fspec, 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, pconf) != 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_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, pconf) != 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_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_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_push_array(sconf->files);
*new_file = tmp;
if (sconf->files->nelts == 1) {
/* first one, register the cleanup */
- ap_register_cleanup(cmd->pool, sconf, cleanup_mmap, ap_null_cleanup);
+ apr_register_cleanup(cmd->pool, sconf, cleanup_file_cache, apr_null_cleanup);
}
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++;
- pconf = p;
-
/* 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_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.
- */
- 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;
- }
-
- iol_sendfile(r->connection->client->iol,
- file->file,
- phdtr,
- &offset,
- &length,
- flags);
+ apr_size_t nbytes;
+ apr_status_t rv = APR_EINIT;
+ apr_off_t offset = 0;
+
+ rv = apr_seek(file->file, APR_SET, &offset);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "seek failed");
+ return HTTP_INTERNAL_SERVER_ERROR;
}
- else {
- while (ap_each_byterange(r, &offset, &length)) {
- iol_sendfile(r->connection->client->iol,
- file->file,
- phdtr,
- &offset,
- &length,
- flags);
- 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;
+ if (strcmp(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)
{
+ ap_hook_handler(file_cache_handler, NULL, NULL, AP_HOOK_MIDDLE);
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);
/* This trick doesn't work apparently because the translate hooks
ap_hook_translate_name(file_cache_xlat, aszPre, NULL, AP_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 */
};