|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Author: mod_file_cache by Bill Stoddard <stoddard apache.org>
|
|
Packit |
90a5c9 |
* Based on mod_mmap_static by Dean Gaudet <dgaudet arctic.org>
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* v0.01: initial implementation
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
Documentation:
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
Some sites have a set of static files that are really busy, and
|
|
Packit |
90a5c9 |
change infrequently (or even on a regular schedule). Save time
|
|
Packit |
90a5c9 |
by caching open handles to these files. This module, unlike
|
|
Packit |
90a5c9 |
mod_mmap_static, caches open file handles, not file content.
|
|
Packit |
90a5c9 |
On systems (like Windows) with heavy system call overhead and
|
|
Packit |
90a5c9 |
that have an efficient sendfile implementation, caching file handles
|
|
Packit |
90a5c9 |
offers several advantages over caching content. First, the file system
|
|
Packit |
90a5c9 |
can manage the memory, allowing infrequently hit cached files to
|
|
Packit |
90a5c9 |
be paged out. Second, since caching open handles does not consume
|
|
Packit |
90a5c9 |
significant resources, it will be possible to enable an AutoLoadCache
|
|
Packit |
90a5c9 |
feature where static files are dynamically loaded in the cache
|
|
Packit |
90a5c9 |
as the server runs. On systems that have file change notification,
|
|
Packit |
90a5c9 |
this module can be enhanced to automatically garbage collect
|
|
Packit |
90a5c9 |
cached files that change on disk.
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
This module should work on Unix systems that have sendfile. Place
|
|
Packit |
90a5c9 |
cachefile directives into your configuration to direct files to
|
|
Packit |
90a5c9 |
be cached.
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
cachefile /path/to/file1
|
|
Packit |
90a5c9 |
cachefile /path/to/file2
|
|
Packit |
90a5c9 |
...
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
These files are only cached when the server is restarted, so if you
|
|
Packit |
90a5c9 |
change the list, or if the files are changed, then you'll need to
|
|
Packit |
90a5c9 |
restart the server.
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
To reiterate that point: if the files are modified *in place*
|
|
Packit |
90a5c9 |
without restarting the server you may end up serving requests that
|
|
Packit |
90a5c9 |
are completely bogus. You should update files by unlinking the old
|
|
Packit |
90a5c9 |
copy and putting a new copy in place.
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
There's no such thing as inheriting these files across vhosts or
|
|
Packit |
90a5c9 |
whatever... place the directives in the main server only.
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
Known problems:
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
Don't use Alias or RewriteRule to move these files around... unless
|
|
Packit |
90a5c9 |
you feel like paying for an extra stat() on each request. This is
|
|
Packit |
90a5c9 |
a deficiency in the Apache API that will hopefully be solved some day.
|
|
Packit |
90a5c9 |
The file will be served out of the file handle cache, but there will be
|
|
Packit |
90a5c9 |
an extra stat() that's a waste.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if !(APR_HAS_SENDFILE || APR_HAS_MMAP)
|
|
Packit |
90a5c9 |
#error mod_file_cache only works on systems with APR_HAS_SENDFILE or APR_HAS_MMAP
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr_mmap.h"
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_hash.h"
|
|
Packit |
90a5c9 |
#include "apr_buckets.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAVE_SYS_TYPES_H
|
|
Packit |
90a5c9 |
#include <sys/types.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_log.h"
|
|
Packit |
90a5c9 |
#include "http_protocol.h"
|
|
Packit |
90a5c9 |
#include "http_request.h"
|
|
Packit |
90a5c9 |
#include "http_core.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
module AP_MODULE_DECLARE_DATA file_cache_module;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
apr_file_t *file;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
const char *filename;
|
|
Packit |
90a5c9 |
apr_finfo_t finfo;
|
|
Packit |
90a5c9 |
int is_mmapped;
|
|
Packit |
90a5c9 |
#if APR_HAS_MMAP
|
|
Packit |
90a5c9 |
apr_mmap_t *mm;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
char mtimestr[APR_RFC822_DATE_LEN];
|
|
Packit |
90a5c9 |
char sizestr[21]; /* big enough to hold any 64-bit file size + null */
|
|
Packit |
90a5c9 |
} a_file;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
apr_hash_t *fileht;
|
|
Packit |
90a5c9 |
} a_server_config;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void *create_server_config(apr_pool_t *p, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
a_server_config *sconf = apr_palloc(p, sizeof(*sconf));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
sconf->fileht = apr_hash_make(p);
|
|
Packit |
90a5c9 |
return sconf;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
a_server_config *sconf;
|
|
Packit |
90a5c9 |
a_file *new_file;
|
|
Packit |
90a5c9 |
a_file tmp;
|
|
Packit |
90a5c9 |
apr_file_t *fd = NULL;
|
|
Packit |
90a5c9 |
apr_status_t rc;
|
|
Packit |
90a5c9 |
const char *fspec;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
fspec = ap_server_root_relative(cmd->pool, filename);
|
|
Packit |
90a5c9 |
if (!fspec) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server, APLOGNO(00794)
|
|
Packit |
90a5c9 |
"invalid file path "
|
|
Packit |
90a5c9 |
"%s, skipping", filename);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if ((rc = apr_stat(&tmp.finfo, fspec, APR_FINFO_MIN,
|
|
Packit |
90a5c9 |
cmd->temp_pool)) != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, APLOGNO(00795)
|
|
Packit |
90a5c9 |
"unable to stat(%s), skipping", fspec);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (tmp.finfo.filetype != APR_REG) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00796)
|
|
Packit |
90a5c9 |
"%s isn't a regular file, skipping", fspec);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (tmp.finfo.size > AP_MAX_SENDFILE) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00797)
|
|
Packit |
90a5c9 |
"%s is too large to cache, skipping", fspec);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rc = apr_file_open(&fd, fspec, APR_READ | APR_BINARY | APR_XTHREAD,
|
|
Packit |
90a5c9 |
APR_OS_DEFAULT, cmd->pool);
|
|
Packit |
90a5c9 |
if (rc != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, APLOGNO(00798)
|
|
Packit |
90a5c9 |
"unable to open(%s, O_RDONLY), skipping", fspec);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_file_inherit_set(fd);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* WooHoo, we have a file to put in the cache */
|
|
Packit |
90a5c9 |
new_file = apr_pcalloc(cmd->pool, sizeof(a_file));
|
|
Packit |
90a5c9 |
new_file->finfo = tmp.finfo;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAS_MMAP
|
|
Packit |
90a5c9 |
if (mmap) {
|
|
Packit |
90a5c9 |
/* MMAPFile directive. MMAP'ing the file
|
|
Packit |
90a5c9 |
* XXX: APR_HAS_LARGE_FILES issue; need to reject this request if
|
|
Packit |
90a5c9 |
* size is greater than MAX(apr_size_t) (perhaps greater than 1M?).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((rc = apr_mmap_create(&new_file->mm, fd, 0,
|
|
Packit |
90a5c9 |
(apr_size_t)new_file->finfo.size,
|
|
Packit |
90a5c9 |
APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
apr_file_close(fd);
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, APLOGNO(00799)
|
|
Packit |
90a5c9 |
"unable to mmap %s, skipping", filename);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_file_close(fd);
|
|
Packit |
90a5c9 |
new_file->is_mmapped = TRUE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
if (!mmap) {
|
|
Packit |
90a5c9 |
/* CacheFile directive. Caching the file handle */
|
|
Packit |
90a5c9 |
new_file->is_mmapped = FALSE;
|
|
Packit |
90a5c9 |
new_file->file = fd;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
new_file->filename = fspec;
|
|
Packit |
90a5c9 |
apr_rfc822_date(new_file->mtimestr, new_file->finfo.mtime);
|
|
Packit |
90a5c9 |
apr_snprintf(new_file->sizestr, sizeof new_file->sizestr, "%" APR_OFF_T_FMT, new_file->finfo.size);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
|
|
Packit |
90a5c9 |
apr_hash_set(sconf->fileht, new_file->filename, strlen(new_file->filename), new_file);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *cachefilehandle(cmd_parms *cmd, void *dummy, const char *filename)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
cache_the_file(cmd, filename, 0);
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
/* Sendfile not supported by this OS */
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00800)
|
|
Packit |
90a5c9 |
"unable to cache file: %s. Sendfile is not supported on this OS", filename);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
static const char *cachefilemmap(cmd_parms *cmd, void *dummy, const char *filename)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
#if APR_HAS_MMAP
|
|
Packit |
90a5c9 |
cache_the_file(cmd, filename, 1);
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
/* MMAP not supported by this OS */
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00801)
|
|
Packit |
90a5c9 |
"unable to cache file: %s. MMAP is not supported by this OS", filename);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int file_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* Hummm, anything to do here? */
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If it's one of ours, fill in r->finfo now to avoid extra stat()... this is a
|
|
Packit |
90a5c9 |
* bit of a kludge, because we really want to run after core_translate runs.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int file_cache_xlat(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
a_server_config *sconf;
|
|
Packit |
90a5c9 |
a_file *match;
|
|
Packit |
90a5c9 |
int res;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
sconf = ap_get_module_config(r->server->module_config, &file_cache_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* we only operate when at least one cachefile directive was used */
|
|
Packit |
90a5c9 |
if (!apr_hash_count(sconf->fileht)) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
res = ap_core_translate(r);
|
|
Packit |
90a5c9 |
if (res != OK || !r->filename) {
|
|
Packit |
90a5c9 |
return res;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* search the cache */
|
|
Packit |
90a5c9 |
match = (a_file *) apr_hash_get(sconf->fileht, r->filename, APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
if (match == NULL)
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* pass search results to handler */
|
|
Packit |
90a5c9 |
ap_set_module_config(r->request_config, &file_cache_module, match);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* shortcircuit the get_path_info() stat() calls and stuff */
|
|
Packit |
90a5c9 |
r->finfo = match->finfo;
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int mmap_handler(request_rec *r, a_file *file)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
#if APR_HAS_MMAP
|
|
Packit |
90a5c9 |
conn_rec *c = r->connection;
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
apr_mmap_t *mm;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_mmap_dup(&mm, file->mm, r->pool);
|
|
Packit |
90a5c9 |
b = apr_bucket_mmap_create(mm, 0, (apr_size_t)file->finfo.size,
|
|
Packit |
90a5c9 |
c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(bb, b);
|
|
Packit |
90a5c9 |
b = apr_bucket_eos_create(c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(bb, b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
|
|
Packit |
90a5c9 |
return AP_FILTER_ERROR;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int sendfile_handler(request_rec *r, a_file *file)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
conn_rec *c = r->connection;
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_brigade_insert_file(bb, file->file, 0, file->finfo.size, r->pool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
b = apr_bucket_eos_create(c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(bb, b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
|
|
Packit |
90a5c9 |
return AP_FILTER_ERROR;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int file_cache_handler(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
a_file *match;
|
|
Packit |
90a5c9 |
int errstatus;
|
|
Packit |
90a5c9 |
int rc = OK;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Bail out if r->handler isn't the default value, and doesn't look like a Content-Type
|
|
Packit |
90a5c9 |
* XXX: Even though we made the user explicitly list each path to cache?
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (ap_strcmp_match(r->handler, "*/*") && !AP_IS_DEFAULT_HANDLER_NAME(r->handler)) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* we don't handle anything but GET */
|
|
Packit |
90a5c9 |
if (r->method_number != M_GET) return DECLINED;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* did xlat phase find the file? */
|
|
Packit |
90a5c9 |
match = ap_get_module_config(r->request_config, &file_cache_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (match == NULL) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* note that we would handle GET on this resource */
|
|
Packit |
90a5c9 |
r->allowed |= (AP_METHOD_BIT << M_GET);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* This handler has no use for a request body (yet), but we still
|
|
Packit |
90a5c9 |
* need to read and discard it if the client sent one.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((errstatus = ap_discard_request_body(r)) != OK)
|
|
Packit |
90a5c9 |
return errstatus;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_update_mtime(r, match->finfo.mtime);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* ap_set_last_modified() always converts the file mtime to a string
|
|
Packit |
90a5c9 |
* which is slow. Accelerate the common case.
|
|
Packit |
90a5c9 |
* ap_set_last_modified(r);
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_time_t mod_time;
|
|
Packit |
90a5c9 |
char *datestr;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
mod_time = ap_rationalize_mtime(r, r->mtime);
|
|
Packit |
90a5c9 |
if (mod_time == match->finfo.mtime)
|
|
Packit |
90a5c9 |
datestr = match->mtimestr;
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
datestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
|
|
Packit |
90a5c9 |
apr_rfc822_date(datestr, mod_time);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Last-Modified", datestr);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* ap_set_content_length() always converts the same number and never
|
|
Packit |
90a5c9 |
* returns an error. Accelerate it.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->clength = match->finfo.size;
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Content-Length", match->sizestr);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_set_etag(r);
|
|
Packit |
90a5c9 |
if ((errstatus = ap_meets_conditions(r)) != OK) {
|
|
Packit |
90a5c9 |
return errstatus;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Call appropriate handler */
|
|
Packit |
90a5c9 |
if (!r->header_only) {
|
|
Packit |
90a5c9 |
if (match->is_mmapped == TRUE)
|
|
Packit |
90a5c9 |
rc = mmap_handler(r, match);
|
|
Packit |
90a5c9 |
else
|
|
Packit |
90a5c9 |
rc = sendfile_handler(r, match);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rc;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static command_rec file_cache_cmds[] =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
AP_INIT_ITERATE("cachefile", cachefilehandle, NULL, RSRC_CONF,
|
|
Packit |
90a5c9 |
"A space separated list of files to add to the file handle cache at config time"),
|
|
Packit |
90a5c9 |
AP_INIT_ITERATE("mmapfile", cachefilemmap, NULL, RSRC_CONF,
|
|
Packit |
90a5c9 |
"A space separated list of files to mmap at config time"),
|
|
Packit |
90a5c9 |
{NULL}
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void register_hooks(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_hook_handler(file_cache_handler, NULL, NULL, APR_HOOK_LAST);
|
|
Packit |
90a5c9 |
ap_hook_post_config(file_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_translate_name(file_cache_xlat, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
/* This trick doesn't work apparently because the translate hooks
|
|
Packit |
90a5c9 |
are single shot. If the core_hook returns OK, then our hook is
|
|
Packit |
90a5c9 |
not called.
|
|
Packit |
90a5c9 |
ap_hook_translate_name(file_cache_xlat, aszPre, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_MODULE(file_cache) =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
STANDARD20_MODULE_STUFF,
|
|
Packit |
90a5c9 |
NULL, /* create per-directory config structure */
|
|
Packit |
90a5c9 |
NULL, /* merge per-directory config structures */
|
|
Packit |
90a5c9 |
create_server_config, /* create per-server config structure */
|
|
Packit |
90a5c9 |
NULL, /* merge per-server config structures */
|
|
Packit |
90a5c9 |
file_cache_cmds, /* command handlers */
|
|
Packit |
90a5c9 |
register_hooks /* register hooks */
|
|
Packit |
90a5c9 |
};
|