Apache HTTPD
mod_file_cache.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Author: mod_file_cache by Bill Stoddard <stoddard apache.org>
19 * Based on mod_mmap_static by Dean Gaudet <dgaudet arctic.org>
20 *
21 * v0.01: initial implementation
22 */
23
24/*
25 Documentation:
26
27 Some sites have a set of static files that are really busy, and
28 change infrequently (or even on a regular schedule). Save time
29 by caching open handles to these files. This module, unlike
30 mod_mmap_static, caches open file handles, not file content.
31 On systems (like Windows) with heavy system call overhead and
32 that have an efficient sendfile implementation, caching file handles
33 offers several advantages over caching content. First, the file system
34 can manage the memory, allowing infrequently hit cached files to
35 be paged out. Second, since caching open handles does not consume
36 significant resources, it will be possible to enable an AutoLoadCache
37 feature where static files are dynamically loaded in the cache
38 as the server runs. On systems that have file change notification,
39 this module can be enhanced to automatically garbage collect
40 cached files that change on disk.
41
42 This module should work on Unix systems that have sendfile. Place
43 cachefile directives into your configuration to direct files to
44 be cached.
45
46 cachefile /path/to/file1
47 cachefile /path/to/file2
48 ...
49
50 These files are only cached when the server is restarted, so if you
51 change the list, or if the files are changed, then you'll need to
52 restart the server.
53
54 To reiterate that point: if the files are modified *in place*
55 without restarting the server you may end up serving requests that
56 are completely bogus. You should update files by unlinking the old
57 copy and putting a new copy in place.
58
59 There's no such thing as inheriting these files across vhosts or
60 whatever... place the directives in the main server only.
61
62 Known problems:
63
64 Don't use Alias or RewriteRule to move these files around... unless
65 you feel like paying for an extra stat() on each request. This is
66 a deficiency in the Apache API that will hopefully be solved some day.
67 The file will be served out of the file handle cache, but there will be
68 an extra stat() that's a waste.
69*/
70
71#include "apr.h"
72
73#if !(APR_HAS_SENDFILE || APR_HAS_MMAP)
74#error mod_file_cache only works on systems with APR_HAS_SENDFILE or APR_HAS_MMAP
75#endif
76
77#include "apr_mmap.h"
78#include "apr_strings.h"
79#include "apr_hash.h"
80#include "apr_buckets.h"
81
82#define APR_WANT_STRFUNC
83#include "apr_want.h"
84
85#if APR_HAVE_SYS_TYPES_H
86#include <sys/types.h>
87#endif
88
89#include "httpd.h"
90#include "http_config.h"
91#include "http_log.h"
92#include "http_protocol.h"
93#include "http_request.h"
94#include "http_core.h"
95
96module AP_MODULE_DECLARE_DATA file_cache_module;
97
98typedef struct {
99#if APR_HAS_SENDFILE
101#endif
102 const char *filename;
105#if APR_HAS_MMAP
106 apr_mmap_t *mm;
107#endif
108 char mtimestr[APR_RFC822_DATE_LEN];
109 char sizestr[21]; /* big enough to hold any 64-bit file size + null */
110} a_file;
111
112typedef struct {
115
116
118{
119 a_server_config *sconf = apr_palloc(p, sizeof(*sconf));
120
121 sconf->fileht = apr_hash_make(p);
122 return sconf;
123}
124
125static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap)
126{
129 a_file tmp;
130 apr_file_t *fd = NULL;
132 const char *fspec;
133
135 if (!fspec) {
137 "invalid file path "
138 "%s, skipping", filename);
139 return;
140 }
141 if ((rc = apr_stat(&tmp.finfo, fspec, APR_FINFO_MIN,
142 cmd->temp_pool)) != APR_SUCCESS) {
144 "unable to stat(%s), skipping", fspec);
145 return;
146 }
147 if (tmp.finfo.filetype != APR_REG) {
148 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00796)
149 "%s isn't a regular file, skipping", fspec);
150 return;
151 }
152 if (tmp.finfo.size > AP_MAX_SENDFILE) {
153 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00797)
154 "%s is too large to cache, skipping", fspec);
155 return;
156 }
157
160 if (rc != APR_SUCCESS) {
162 "unable to open(%s, O_RDONLY), skipping", fspec);
163 return;
164 }
166
167 /* WooHoo, we have a file to put in the cache */
168 new_file = apr_pcalloc(cmd->pool, sizeof(a_file));
169 new_file->finfo = tmp.finfo;
170
171#if APR_HAS_MMAP
172 if (mmap) {
173 /* MMAPFile directive. MMAP'ing the file
174 * XXX: APR_HAS_LARGE_FILES issue; need to reject this request if
175 * size is greater than MAX(apr_size_t) (perhaps greater than 1M?).
176 */
177 if ((rc = apr_mmap_create(&new_file->mm, fd, 0,
179 APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) {
182 "unable to mmap %s, skipping", filename);
183 return;
184 }
186 new_file->is_mmapped = TRUE;
187 }
188#endif
189#if APR_HAS_SENDFILE
190 if (!mmap) {
191 /* CacheFile directive. Caching the file handle */
192 new_file->is_mmapped = FALSE;
193 new_file->file = fd;
194 }
195#endif
196
197 new_file->filename = fspec;
198 apr_rfc822_date(new_file->mtimestr, new_file->finfo.mtime);
199 apr_snprintf(new_file->sizestr, sizeof new_file->sizestr, "%" APR_OFF_T_FMT, new_file->finfo.size);
200
201 sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
202 apr_hash_set(sconf->fileht, new_file->filename, strlen(new_file->filename), new_file);
203
204}
205
206static const char *cachefilehandle(cmd_parms *cmd, void *dummy, const char *filename)
207{
208#if APR_HAS_SENDFILE
210#else
211 /* Sendfile not supported by this OS */
212 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00800)
213 "unable to cache file: %s. Sendfile is not supported on this OS", filename);
214#endif
215 return NULL;
216}
217static const char *cachefilemmap(cmd_parms *cmd, void *dummy, const char *filename)
218{
219#if APR_HAS_MMAP
221#else
222 /* MMAP not supported by this OS */
223 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00801)
224 "unable to cache file: %s. MMAP is not supported by this OS", filename);
225#endif
226 return NULL;
227}
228
230 apr_pool_t *ptemp, server_rec *s)
231{
232 /* Hummm, anything to do here? */
233 return OK;
234}
235
236/* If it's one of ours, fill in r->finfo now to avoid extra stat()... this is a
237 * bit of a kludge, because we really want to run after core_translate runs.
238 */
240{
242 a_file *match;
243 int res;
244
245 sconf = ap_get_module_config(r->server->module_config, &file_cache_module);
246
247 /* we only operate when at least one cachefile directive was used */
248 if (!apr_hash_count(sconf->fileht)) {
249 return DECLINED;
250 }
251
253 if (res != OK || !r->filename) {
254 return res;
255 }
256
257 /* search the cache */
259 if (match == NULL)
260 return DECLINED;
261
262 /* pass search results to handler */
263 ap_set_module_config(r->request_config, &file_cache_module, match);
264
265 /* shortcircuit the get_path_info() stat() calls and stuff */
266 r->finfo = match->finfo;
267 return OK;
268}
269
271{
272#if APR_HAS_MMAP
274 apr_bucket *b;
275 apr_mmap_t *mm;
276 apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
277
278 apr_mmap_dup(&mm, file->mm, r->pool);
280 c->bucket_alloc);
282 b = apr_bucket_eos_create(c->bucket_alloc);
284
286 return AP_FILTER_ERROR;
287#endif
288 return OK;
289}
290
292{
293#if APR_HAS_SENDFILE
295 apr_bucket *b;
296 apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
297
298 apr_brigade_insert_file(bb, file->file, 0, file->finfo.size, r->pool);
299
300 b = apr_bucket_eos_create(c->bucket_alloc);
302
304 return AP_FILTER_ERROR;
305#endif
306 return OK;
307}
308
310{
311 a_file *match;
312 int errstatus;
313 int rc = OK;
314
315 /* Bail out if r->handler isn't the default value, and doesn't look like a Content-Type
316 * XXX: Even though we made the user explicitly list each path to cache?
317 */
319 return DECLINED;
320 }
321
322 /* we don't handle anything but GET */
323 if (r->method_number != M_GET) return DECLINED;
324
325 /* did xlat phase find the file? */
326 match = ap_get_module_config(r->request_config, &file_cache_module);
327
328 if (match == NULL) {
329 return DECLINED;
330 }
331
332 /* note that we would handle GET on this resource */
333 r->allowed |= (AP_METHOD_BIT << M_GET);
334
335 /* This handler has no use for a request body (yet), but we still
336 * need to read and discard it if the client sent one.
337 */
339 return errstatus;
340
342
343 /* ap_set_last_modified() always converts the file mtime to a string
344 * which is slow. Accelerate the common case.
345 * ap_set_last_modified(r);
346 */
347 {
349 char *datestr;
350
352 if (mod_time == match->finfo.mtime)
353 datestr = match->mtimestr;
354 else {
357 }
358 apr_table_setn(r->headers_out, "Last-Modified", datestr);
359 }
360
361 /* ap_set_content_length() always converts the same number and never
362 * returns an error. Accelerate it.
363 */
364 r->clength = match->finfo.size;
365 apr_table_setn(r->headers_out, "Content-Length", match->sizestr);
366
367 ap_set_etag(r);
368 if ((errstatus = ap_meets_conditions(r)) != OK) {
369 return errstatus;
370 }
371
372 /* Call appropriate handler */
373 if (!r->header_only) {
374 if (match->is_mmapped == TRUE)
376 else
378 }
379
380 return rc;
381}
382
384{
386 "A space separated list of files to add to the file handle cache at config time"),
388 "A space separated list of files to mmap at config time"),
389 {NULL}
390};
391
393{
397 /* This trick doesn't work apparently because the translate hooks
398 are single shot. If the core_hook returns OK, then our hook is
399 not called.
400 ap_hook_translate_name(file_cache_xlat, aszPre, NULL, APR_HOOK_MIDDLE);
401 */
402
403}
404
406{
408 NULL, /* create per-directory config structure */
409 NULL, /* merge per-directory config structures */
410 create_server_config, /* create per-server config structure */
411 NULL, /* merge per-server config structures */
412 file_cache_cmds, /* command handlers */
413 register_hooks /* register hooks */
414};
int int const char ** match
Definition ap_regex.h:279
#define TRUE
Definition abts.h:38
#define FALSE
Definition abts.h:35
APR-UTIL Buckets/Bucket Brigades.
APR Hash Tables.
APR MMAP routines.
APR Strings library.
APR Standard Headers Support.
#define ap_get_module_config(v, m)
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_ITERATE(directive, func, mconfig, where, help)
char * ap_server_root_relative(apr_pool_t *p, const char *fname)
Definition config.c:1594
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
#define ap_set_module_config(v, m, val)
request_rec * r
#define AP_FILTER_ERROR
Definition httpd.h:473
#define AP_MAX_SENDFILE
Definition httpd.h:317
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
int ap_core_translate(request_rec *r)
Definition core.c:4756
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
void ap_set_etag(request_rec *r)
Definition http_etag.c:367
int ap_meets_conditions(request_rec *r)
apr_time_t ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
Definition protocol.c:174
int ap_discard_request_body(request_rec *r)
void ap_hook_translate_name(ap_HOOK_translate_name_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:81
void ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
Definition request.c:2557
void * dummy
Definition http_vhost.h:62
#define APR_EBADPATH
Definition apr_errno.h:332
#define APR_BRIGADE_INSERT_TAIL(b, e)
apr_file_t * fd
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
#define APR_HOOK_LAST
Definition apr_hooks.h:305
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
apr_redis_t * rc
Definition apr_redis.h:173
#define RSRC_CONF
#define AP_METHOD_BIT
Definition httpd.h:629
#define M_GET
Definition httpd.h:592
#define STANDARD20_MODULE_STUFF
int ap_strcmp_match(const char *str, const char *expected)
Definition util.c:175
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
@ APR_REG
const char apr_file_t * file
#define APR_READ
Definition apr_file_io.h:93
#define APR_BINARY
Definition apr_file_io.h:98
#define APR_XTHREAD
#define APR_OS_DEFAULT
#define APR_FINFO_MIN
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_vformatter_buff_t * c
Definition apr_lib.h:175
#define APR_MMAP_READ
Definition apr_mmap.h:46
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_size_t const char * filename
Definition apr_shm.h:72
const char * s
Definition apr_strings.h:95
apr_cmdtype_e cmd
#define APR_RFC822_DATE_LEN
Definition apr_time.h:186
apr_int64_t apr_time_t
Definition apr_time.h:45
#define AP_IS_DEFAULT_HANDLER_NAME(x)
Definition httpd.h:714
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static void * create_server_config(apr_pool_t *p, server_rec *s)
static int file_cache_handler(request_rec *r)
static const char * cachefilehandle(cmd_parms *cmd, void *dummy, const char *filename)
static const command_rec file_cache_cmds[]
static int mmap_handler(request_rec *r, a_file *file)
static void register_hooks(apr_pool_t *p)
static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap)
static const char * cachefilemmap(cmd_parms *cmd, void *dummy, const char *filename)
static int sendfile_handler(request_rec *r, a_file *file)
static int file_cache_xlat(request_rec *r)
static int file_cache_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
return NULL
Definition mod_so.c:359
sconf
Definition mod_so.c:349
apr_finfo_t finfo
const char * filename
apr_hash_t * fileht
apr_pool_t * pool
apr_finfo_t * finfo
apr_filetype_e filetype
apr_off_t size
apr_time_t mtime
Structure to store things which are per connection.
Definition httpd.h:1152
A structure that represents the current request.
Definition httpd.h:845
struct ap_filter_t * output_filters
Definition httpd.h:1070
int header_only
Definition httpd.h:875
apr_time_t mtime
Definition httpd.h:933
const char * handler
Definition httpd.h:994
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
conn_rec * connection
Definition httpd.h:849
apr_finfo_t finfo
Definition httpd.h:1094
struct ap_conf_vector_t * request_config
Definition httpd.h:1049
apr_int64_t allowed
Definition httpd.h:922
server_rec * server
Definition httpd.h:851
apr_off_t clength
Definition httpd.h:940
apr_table_t * headers_out
Definition httpd.h:978
A structure to store information for each virtual server.
Definition httpd.h:1322
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
apr_status_t apr_rfc822_date(char *date_str, apr_time_t t)
Definition timestr.c:42