~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/cache/mod_file_cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#define CORE_PRIVATE
 
90
 
 
91
#include "httpd.h"
 
92
#include "http_config.h"
 
93
#include "http_log.h"
 
94
#include "http_protocol.h"
 
95
#include "http_request.h"
 
96
#include "http_core.h"
 
97
 
 
98
module AP_MODULE_DECLARE_DATA file_cache_module;
 
99
 
 
100
typedef struct {
 
101
#if APR_HAS_SENDFILE
 
102
    apr_file_t *file;
 
103
#endif
 
104
    const char *filename;
 
105
    apr_finfo_t finfo;
 
106
    int is_mmapped;
 
107
#if APR_HAS_MMAP
 
108
    apr_mmap_t *mm;
 
109
#endif
 
110
    char mtimestr[APR_RFC822_DATE_LEN];
 
111
    char sizestr[21];   /* big enough to hold any 64-bit file size + null */
 
112
} a_file;
 
113
 
 
114
typedef struct {
 
115
    apr_hash_t *fileht;
 
116
} a_server_config;
 
117
 
 
118
 
 
119
static void *create_server_config(apr_pool_t *p, server_rec *s)
 
120
{
 
121
    a_server_config *sconf = apr_palloc(p, sizeof(*sconf));
 
122
 
 
123
    sconf->fileht = apr_hash_make(p);
 
124
    return sconf;
 
125
}
 
126
 
 
127
static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap)
 
128
{
 
129
    a_server_config *sconf;
 
130
    a_file *new_file;
 
131
    a_file tmp;
 
132
    apr_file_t *fd = NULL;
 
133
    apr_status_t rc;
 
134
    const char *fspec;
 
135
 
 
136
    fspec = ap_server_root_relative(cmd->pool, filename);
 
137
    if (!fspec) {
 
138
        ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server,
 
139
                     "mod_file_cache: invalid file path "
 
140
                     "%s, skipping", filename);
 
141
        return;
 
142
    }
 
143
    if ((rc = apr_stat(&tmp.finfo, fspec, APR_FINFO_MIN,
 
144
                                 cmd->temp_pool)) != APR_SUCCESS) {
 
145
        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
 
146
            "mod_file_cache: unable to stat(%s), skipping", fspec);
 
147
        return;
 
148
    }
 
149
    if (tmp.finfo.filetype != APR_REG) {
 
150
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
151
            "mod_file_cache: %s isn't a regular file, skipping", fspec);
 
152
        return;
 
153
    }
 
154
    if (tmp.finfo.size > AP_MAX_SENDFILE) {
 
155
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
156
            "mod_file_cache: %s is too large to cache, skipping", fspec);
 
157
        return;
 
158
    }
 
159
 
 
160
    rc = apr_file_open(&fd, fspec, APR_READ | APR_BINARY | APR_XTHREAD,
 
161
                       APR_OS_DEFAULT, cmd->pool);
 
162
    if (rc != APR_SUCCESS) {
 
163
        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
 
164
                     "mod_file_cache: unable to open(%s, O_RDONLY), skipping", fspec);
 
165
        return;
 
166
    }
 
167
    apr_file_inherit_set(fd);
 
168
 
 
169
    /* WooHoo, we have a file to put in the cache */
 
170
    new_file = apr_pcalloc(cmd->pool, sizeof(a_file));
 
171
    new_file->finfo = tmp.finfo;
 
172
 
 
173
#if APR_HAS_MMAP
 
174
    if (mmap) {
 
175
        /* MMAPFile directive. MMAP'ing the file
 
176
         * XXX: APR_HAS_LARGE_FILES issue; need to reject this request if
 
177
         * size is greater than MAX(apr_size_t) (perhaps greater than 1M?).
 
178
         */
 
179
        if ((rc = apr_mmap_create(&new_file->mm, fd, 0,
 
180
                                  (apr_size_t)new_file->finfo.size,
 
181
                                  APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) {
 
182
            apr_file_close(fd);
 
183
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
 
184
                         "mod_file_cache: unable to mmap %s, skipping", filename);
 
185
            return;
 
186
        }
 
187
        apr_file_close(fd);
 
188
        new_file->is_mmapped = TRUE;
 
189
    }
 
190
#endif
 
191
#if APR_HAS_SENDFILE
 
192
    if (!mmap) {
 
193
        /* CacheFile directive. Caching the file handle */
 
194
        new_file->is_mmapped = FALSE;
 
195
        new_file->file = fd;
 
196
    }
 
197
#endif
 
198
 
 
199
    new_file->filename = fspec;
 
200
    apr_rfc822_date(new_file->mtimestr, new_file->finfo.mtime);
 
201
    apr_snprintf(new_file->sizestr, sizeof new_file->sizestr, "%" APR_OFF_T_FMT, new_file->finfo.size);
 
202
 
 
203
    sconf = ap_get_module_config(cmd->server->module_config, &file_cache_module);
 
204
    apr_hash_set(sconf->fileht, new_file->filename, strlen(new_file->filename), new_file);
 
205
 
 
206
}
 
207
 
 
208
static const char *cachefilehandle(cmd_parms *cmd, void *dummy, const char *filename)
 
209
{
 
210
#if APR_HAS_SENDFILE
 
211
    cache_the_file(cmd, filename, 0);
 
212
#else
 
213
    /* Sendfile not supported by this OS */
 
214
    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
215
                 "mod_file_cache: unable to cache file: %s. Sendfile is not supported on this OS", filename);
 
216
#endif
 
217
    return NULL;
 
218
}
 
219
static const char *cachefilemmap(cmd_parms *cmd, void *dummy, const char *filename)
 
220
{
 
221
#if APR_HAS_MMAP
 
222
    cache_the_file(cmd, filename, 1);
 
223
#else
 
224
    /* MMAP not supported by this OS */
 
225
    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
 
226
                 "mod_file_cache: unable to cache file: %s. MMAP is not supported by this OS", filename);
 
227
#endif
 
228
    return NULL;
 
229
}
 
230
 
 
231
static int file_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
 
232
                                   apr_pool_t *ptemp, server_rec *s)
 
233
{
 
234
    /* Hummm, anything to do here? */
 
235
    return OK;
 
236
}
 
237
 
 
238
/* If it's one of ours, fill in r->finfo now to avoid extra stat()... this is a
 
239
 * bit of a kludge, because we really want to run after core_translate runs.
 
240
 */
 
241
static int file_cache_xlat(request_rec *r)
 
242
{
 
243
    a_server_config *sconf;
 
244
    a_file *match;
 
245
    int res;
 
246
 
 
247
    sconf = ap_get_module_config(r->server->module_config, &file_cache_module);
 
248
 
 
249
    /* we only operate when at least one cachefile directive was used */
 
250
    if (!apr_hash_count(sconf->fileht)) {
 
251
        return DECLINED;
 
252
    }
 
253
 
 
254
    res = ap_core_translate(r);
 
255
    if (res != OK || !r->filename) {
 
256
        return res;
 
257
    }
 
258
 
 
259
    /* search the cache */
 
260
    match = (a_file *) apr_hash_get(sconf->fileht, r->filename, APR_HASH_KEY_STRING);
 
261
    if (match == NULL)
 
262
        return DECLINED;
 
263
 
 
264
    /* pass search results to handler */
 
265
    ap_set_module_config(r->request_config, &file_cache_module, match);
 
266
 
 
267
    /* shortcircuit the get_path_info() stat() calls and stuff */
 
268
    r->finfo = match->finfo;
 
269
    return OK;
 
270
}
 
271
 
 
272
static int mmap_handler(request_rec *r, a_file *file)
 
273
{
 
274
#if APR_HAS_MMAP
 
275
    conn_rec *c = r->connection;
 
276
    apr_bucket *b;
 
277
    apr_mmap_t *mm;
 
278
    apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
 
279
 
 
280
    apr_mmap_dup(&mm, file->mm, r->pool);
 
281
    b = apr_bucket_mmap_create(mm, 0, (apr_size_t)file->finfo.size,
 
282
                               c->bucket_alloc);
 
283
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
284
    b = apr_bucket_eos_create(c->bucket_alloc);
 
285
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
286
 
 
287
    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
 
288
        return HTTP_INTERNAL_SERVER_ERROR;
 
289
#endif
 
290
    return OK;
 
291
}
 
292
 
 
293
static int sendfile_handler(request_rec *r, a_file *file)
 
294
{
 
295
#if APR_HAS_SENDFILE
 
296
    conn_rec *c = r->connection;
 
297
    apr_bucket *b;
 
298
    apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
 
299
 
 
300
    b = apr_bucket_file_create(file->file, 0, (apr_size_t)file->finfo.size,
 
301
                               r->pool, c->bucket_alloc);
 
302
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
303
    b = apr_bucket_eos_create(c->bucket_alloc);
 
304
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
305
 
 
306
    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
 
307
        return HTTP_INTERNAL_SERVER_ERROR;
 
308
#endif
 
309
    return OK;
 
310
}
 
311
 
 
312
static int file_cache_handler(request_rec *r)
 
313
{
 
314
    a_file *match;
 
315
    int errstatus;
 
316
    int rc = OK;
 
317
 
 
318
    /* XXX: not sure if this is right yet
 
319
     * see comment in http_core.c:default_handler
 
320
     */
 
321
    if (ap_strcmp_match(r->handler, "*/*")) {
 
322
        return DECLINED;
 
323
    }
 
324
 
 
325
    /* we don't handle anything but GET */
 
326
    if (r->method_number != M_GET) return DECLINED;
 
327
 
 
328
    /* did xlat phase find the file? */
 
329
    match = ap_get_module_config(r->request_config, &file_cache_module);
 
330
 
 
331
    if (match == NULL) {
 
332
        return DECLINED;
 
333
    }
 
334
 
 
335
    /* note that we would handle GET on this resource */
 
336
    r->allowed |= (AP_METHOD_BIT << M_GET);
 
337
 
 
338
    /* This handler has no use for a request body (yet), but we still
 
339
     * need to read and discard it if the client sent one.
 
340
     */
 
341
    if ((errstatus = ap_discard_request_body(r)) != OK)
 
342
        return errstatus;
 
343
 
 
344
    ap_update_mtime(r, match->finfo.mtime);
 
345
 
 
346
    /* ap_set_last_modified() always converts the file mtime to a string
 
347
     * which is slow.  Accelerate the common case.
 
348
     * ap_set_last_modified(r);
 
349
     */
 
350
    {
 
351
        apr_time_t mod_time;
 
352
        char *datestr;
 
353
 
 
354
        mod_time = ap_rationalize_mtime(r, r->mtime);
 
355
        if (mod_time == match->finfo.mtime)
 
356
            datestr = match->mtimestr;
 
357
        else {
 
358
            datestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
 
359
            apr_rfc822_date(datestr, mod_time);
 
360
        }
 
361
        apr_table_setn(r->headers_out, "Last-Modified", datestr);
 
362
    }
 
363
 
 
364
    ap_set_etag(r);
 
365
    if ((errstatus = ap_meets_conditions(r)) != OK) {
 
366
       return errstatus;
 
367
    }
 
368
 
 
369
    /* ap_set_content_length() always converts the same number and never
 
370
     * returns an error.  Accelerate it.
 
371
     */
 
372
    r->clength = match->finfo.size;
 
373
    apr_table_setn(r->headers_out, "Content-Length", match->sizestr);
 
374
 
 
375
    /* Call appropriate handler */
 
376
    if (!r->header_only) {
 
377
        if (match->is_mmapped == TRUE)
 
378
            rc = mmap_handler(r, match);
 
379
        else
 
380
            rc = sendfile_handler(r, match);
 
381
    }
 
382
 
 
383
    return rc;
 
384
}
 
385
 
 
386
static command_rec file_cache_cmds[] =
 
387
{
 
388
AP_INIT_ITERATE("cachefile", cachefilehandle, NULL, RSRC_CONF,
 
389
     "A space separated list of files to add to the file handle cache at config time"),
 
390
AP_INIT_ITERATE("mmapfile", cachefilemmap, NULL, RSRC_CONF,
 
391
     "A space separated list of files to mmap at config time"),
 
392
    {NULL}
 
393
};
 
394
 
 
395
static void register_hooks(apr_pool_t *p)
 
396
{
 
397
    ap_hook_handler(file_cache_handler, NULL, NULL, APR_HOOK_LAST);
 
398
    ap_hook_post_config(file_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
 
399
    ap_hook_translate_name(file_cache_xlat, NULL, NULL, APR_HOOK_MIDDLE);
 
400
    /* This trick doesn't work apparently because the translate hooks
 
401
       are single shot. If the core_hook returns OK, then our hook is
 
402
       not called.
 
403
    ap_hook_translate_name(file_cache_xlat, aszPre, NULL, APR_HOOK_MIDDLE);
 
404
    */
 
405
 
 
406
}
 
407
 
 
408
module AP_MODULE_DECLARE_DATA file_cache_module =
 
409
{
 
410
    STANDARD20_MODULE_STUFF,
 
411
    NULL,                     /* create per-directory config structure */
 
412
    NULL,                     /* merge per-directory config structures */
 
413
    create_server_config,     /* create per-server config structure */
 
414
    NULL,                     /* merge per-server config structures */
 
415
    file_cache_cmds,          /* command handlers */
 
416
    register_hooks            /* register hooks */
 
417
};