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

« back to all changes in this revision

Viewing changes to modules/cache/mod_disk_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
#include "apr_file_io.h"
 
18
#include "apr_strings.h"
 
19
#include "mod_cache.h"
 
20
#include "mod_disk_cache.h"
 
21
#include "ap_provider.h"
 
22
#include "util_filter.h"
 
23
#include "util_script.h"
 
24
#include "util_charset.h"
 
25
 
 
26
/*
 
27
 * mod_disk_cache: Disk Based HTTP 1.1 Cache.
 
28
 *
 
29
 * Flow to Find the .data file:
 
30
 *   Incoming client requests URI /foo/bar/baz
 
31
 *   Generate <hash> off of /foo/bar/baz
 
32
 *   Open <hash>.header
 
33
 *   Read in <hash>.header file (may contain Format #1 or Format #2)
 
34
 *   If format #1 (Contains a list of Vary Headers):
 
35
 *      Use each header name (from .header) with our request values (headers_in) to
 
36
 *      regenerate <hash> using HeaderName+HeaderValue+.../foo/bar/baz
 
37
 *      re-read in <hash>.header (must be format #2)
 
38
 *   read in <hash>.data
 
39
 *
 
40
 * Format #1:
 
41
 *   apr_uint32_t format;
 
42
 *   apr_time_t expire;
 
43
 *   apr_array_t vary_headers (delimited by CRLF)
 
44
 *
 
45
 * Format #2:
 
46
 *   disk_cache_info_t (first sizeof(apr_uint32_t) bytes is the format)
 
47
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
 
48
 *   r->headers_out (delimited by CRLF)
 
49
 *   CRLF
 
50
 *   r->headers_in (delimited by CRLF)
 
51
 *   CRLF
 
52
 */
 
53
 
 
54
module AP_MODULE_DECLARE_DATA disk_cache_module;
 
55
 
 
56
/* Forward declarations */
 
57
static int remove_entity(cache_handle_t *h);
 
58
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *i);
 
59
static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
 
60
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r);
 
61
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
 
62
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
 
63
                               apr_file_t *file);
 
64
 
 
65
/*
 
66
 * Local static functions
 
67
 */
 
68
 
 
69
static char *header_file(apr_pool_t *p, disk_cache_conf *conf,
 
70
                         disk_cache_object_t *dobj, const char *name)
 
71
{
 
72
    if (!dobj->hashfile) {
 
73
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
 
74
                                                conf->dirlength, name);
 
75
    }
 
76
 
 
77
    if (dobj->prefix) {
 
78
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
 
79
                           dobj->hashfile, CACHE_HEADER_SUFFIX, NULL);
 
80
     }
 
81
     else {
 
82
        return apr_pstrcat(p, conf->cache_root, "/", dobj->hashfile,
 
83
                           CACHE_HEADER_SUFFIX, NULL);
 
84
     }
 
85
}
 
86
 
 
87
static char *data_file(apr_pool_t *p, disk_cache_conf *conf,
 
88
                       disk_cache_object_t *dobj, const char *name)
 
89
{
 
90
    if (!dobj->hashfile) {
 
91
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
 
92
                                                conf->dirlength, name);
 
93
    }
 
94
 
 
95
    if (dobj->prefix) {
 
96
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
 
97
                           dobj->hashfile, CACHE_DATA_SUFFIX, NULL);
 
98
     }
 
99
     else {
 
100
        return apr_pstrcat(p, conf->cache_root, "/", dobj->hashfile,
 
101
                           CACHE_DATA_SUFFIX, NULL);
 
102
     }
 
103
}
 
104
 
 
105
static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
 
106
{
 
107
    apr_status_t rv;
 
108
    char *p;
 
109
 
 
110
    for (p = (char*)file + conf->cache_root_len + 1;;) {
 
111
        p = strchr(p, '/');
 
112
        if (!p)
 
113
            break;
 
114
        *p = '\0';
 
115
 
 
116
        rv = apr_dir_make(file,
 
117
                          APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
 
118
        if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
 
119
            /* XXX */
 
120
        }
 
121
        *p = '/';
 
122
        ++p;
 
123
    }
 
124
}
 
125
 
 
126
/* htcacheclean may remove directories underneath us.
 
127
 * So, we'll try renaming three times at a cost of 0.002 seconds.
 
128
 */
 
129
static apr_status_t safe_file_rename(disk_cache_conf *conf,
 
130
                                     const char *src, const char *dest,
 
131
                                     apr_pool_t *pool)
 
132
{
 
133
    apr_status_t rv;
 
134
 
 
135
    rv = apr_file_rename(src, dest, pool);
 
136
 
 
137
    if (rv != APR_SUCCESS) {
 
138
        int i;
 
139
 
 
140
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
 
141
            /* 1000 micro-seconds aka 0.001 seconds. */
 
142
            apr_sleep(1000);
 
143
 
 
144
            mkdir_structure(conf, dest, pool);
 
145
 
 
146
            rv = apr_file_rename(src, dest, pool);
 
147
        }
 
148
    }
 
149
 
 
150
    return rv;
 
151
}
 
152
 
 
153
static apr_status_t file_cache_el_final(disk_cache_object_t *dobj,
 
154
                                        request_rec *r)
 
155
{
 
156
    /* move the data over */
 
157
    if (dobj->tfd) {
 
158
        apr_status_t rv;
 
159
 
 
160
        apr_file_close(dobj->tfd);
 
161
 
 
162
        /* This assumes that the tempfile is on the same file system
 
163
         * as the cache_root. If not, then we need a file copy/move
 
164
         * rather than a rename.
 
165
         */
 
166
        rv = apr_file_rename(dobj->tempfile, dobj->datafile, r->pool);
 
167
        if (rv != APR_SUCCESS) {
 
168
            /* XXX log */
 
169
        }
 
170
 
 
171
        dobj->tfd = NULL;
 
172
    }
 
173
 
 
174
    return APR_SUCCESS;
 
175
}
 
176
 
 
177
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, request_rec *r)
 
178
{
 
179
    /* Remove the header file and the body file. */
 
180
    apr_file_remove(dobj->hdrsfile, r->pool);
 
181
    apr_file_remove(dobj->datafile, r->pool);
 
182
 
 
183
    /* If we opened the temporary data file, close and remove it. */
 
184
    if (dobj->tfd) {
 
185
        apr_file_close(dobj->tfd);
 
186
        apr_file_remove(dobj->tempfile, r->pool);
 
187
        dobj->tfd = NULL;
 
188
    }
 
189
 
 
190
    return APR_SUCCESS;
 
191
}
 
192
 
 
193
 
 
194
/* These two functions get and put state information into the data
 
195
 * file for an ap_cache_el, this state information will be read
 
196
 * and written transparent to clients of this module
 
197
 */
 
198
static int file_cache_recall_mydata(apr_file_t *fd, cache_info *info,
 
199
                                    disk_cache_object_t *dobj, request_rec *r)
 
200
{
 
201
    apr_status_t rv;
 
202
    char *urlbuff;
 
203
    disk_cache_info_t disk_info;
 
204
    apr_size_t len;
 
205
 
 
206
    /* read the data from the cache file */
 
207
    len = sizeof(disk_cache_info_t);
 
208
    rv = apr_file_read_full(fd, &disk_info, len, &len);
 
209
    if (rv != APR_SUCCESS) {
 
210
        return rv;
 
211
    }
 
212
 
 
213
    /* Store it away so we can get it later. */
 
214
    dobj->disk_info = disk_info;
 
215
 
 
216
    info->status = disk_info.status;
 
217
    info->date = disk_info.date;
 
218
    info->expire = disk_info.expire;
 
219
    info->request_time = disk_info.request_time;
 
220
    info->response_time = disk_info.response_time;
 
221
 
 
222
    /* Note that we could optimize this by conditionally doing the palloc
 
223
     * depending upon the size. */
 
224
    urlbuff = apr_palloc(r->pool, disk_info.name_len + 1);
 
225
    len = disk_info.name_len;
 
226
    rv = apr_file_read_full(fd, urlbuff, len, &len);
 
227
    if (rv != APR_SUCCESS) {
 
228
        return rv;
 
229
    }
 
230
    urlbuff[disk_info.name_len] = '\0';
 
231
 
 
232
    /* check that we have the same URL */
 
233
    /* Would strncmp be correct? */
 
234
    if (strcmp(urlbuff, dobj->name) != 0) {
 
235
        return APR_EGENERAL;
 
236
    }
 
237
 
 
238
    return APR_SUCCESS;
 
239
}
 
240
 
 
241
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
 
242
                             apr_array_header_t *varray, const char *oldkey)
 
243
{
 
244
    struct iovec *iov;
 
245
    int i, k;
 
246
    int nvec;
 
247
    const char *header;
 
248
    const char **elts;
 
249
 
 
250
    nvec = (varray->nelts * 2) + 1;
 
251
    iov = apr_palloc(p, sizeof(struct iovec) * nvec);
 
252
    elts = (const char **) varray->elts;
 
253
 
 
254
    /* TODO:
 
255
     *    - Handle multiple-value headers better. (sort them?)
 
256
     *    - Handle Case in-sensitive Values better.
 
257
     *        This isn't the end of the world, since it just lowers the cache
 
258
     *        hit rate, but it would be nice to fix.
 
259
     *
 
260
     * The majority are case insenstive if they are values (encoding etc).
 
261
     * Most of rfc2616 is case insensitive on header contents.
 
262
     *
 
263
     * So the better solution may be to identify headers which should be
 
264
     * treated case-sensitive?
 
265
     *  HTTP URI's (3.2.3) [host and scheme are insensitive]
 
266
     *  HTTP method (5.1.1)
 
267
     *  HTTP-date values (3.3.1)
 
268
     *  3.7 Media Types [exerpt]
 
269
     *     The type, subtype, and parameter attribute names are case-
 
270
     *     insensitive. Parameter values might or might not be case-sensitive,
 
271
     *     depending on the semantics of the parameter name.
 
272
     *  4.20 Except [exerpt]
 
273
     *     Comparison of expectation values is case-insensitive for unquoted
 
274
     *     tokens (including the 100-continue token), and is case-sensitive for
 
275
     *     quoted-string expectation-extensions.
 
276
     */
 
277
 
 
278
    for(i=0, k=0; i < varray->nelts; i++) {
 
279
        header = apr_table_get(headers, elts[i]);
 
280
        if (!header) {
 
281
            header = "";
 
282
        }
 
283
        iov[k].iov_base = (char*) elts[i];
 
284
        iov[k].iov_len = strlen(elts[i]);
 
285
        k++;
 
286
        iov[k].iov_base = (char*) header;
 
287
        iov[k].iov_len = strlen(header);
 
288
        k++;
 
289
    }
 
290
    iov[k].iov_base = (char*) oldkey;
 
291
    iov[k].iov_len = strlen(oldkey);
 
292
    k++;
 
293
 
 
294
    return apr_pstrcatv(p, iov, k, NULL);
 
295
}
 
296
 
 
297
static int array_alphasort(const void *fn1, const void *fn2)
 
298
{
 
299
    return strcmp(*(char**)fn1, *(char**)fn2);
 
300
}
 
301
 
 
302
static void tokens_to_array(apr_pool_t *p, const char *data,
 
303
                            apr_array_header_t *arr)
 
304
{
 
305
    char *token;
 
306
 
 
307
    while ((token = ap_get_list_item(p, &data)) != NULL) {
 
308
        *((const char **) apr_array_push(arr)) = token;
 
309
    }
 
310
 
 
311
    /* Sort it so that "Vary: A, B" and "Vary: B, A" are stored the same. */
 
312
    qsort((void *) arr->elts, arr->nelts,
 
313
         sizeof(char *), array_alphasort);
 
314
}
 
315
 
 
316
/*
 
317
 * Hook and mod_cache callback functions
 
318
 */
 
319
static int create_entity(cache_handle_t *h, request_rec *r, const char *key, apr_off_t len)
 
320
{
 
321
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
 
322
                                                 &disk_cache_module);
 
323
    cache_object_t *obj;
 
324
    disk_cache_object_t *dobj;
 
325
 
 
326
    if (conf->cache_root == NULL) {
 
327
        return DECLINED;
 
328
    }
 
329
 
 
330
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
 
331
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
 
332
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
 
333
 
 
334
    obj->key = apr_pstrdup(r->pool, key);
 
335
 
 
336
    dobj->name = obj->key;
 
337
    dobj->prefix = NULL;
 
338
    /* Save the cache root */
 
339
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
 
340
    dobj->root_len = conf->cache_root_len;
 
341
    dobj->datafile = data_file(r->pool, conf, dobj, key);
 
342
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
 
343
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
 
344
 
 
345
    return OK;
 
346
}
 
347
 
 
348
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
 
349
{
 
350
    apr_uint32_t format;
 
351
    apr_size_t len;
 
352
    const char *nkey;
 
353
    apr_status_t rc;
 
354
    static int error_logged = 0;
 
355
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
 
356
                                                 &disk_cache_module);
 
357
    apr_finfo_t finfo;
 
358
    cache_object_t *obj;
 
359
    cache_info *info;
 
360
    disk_cache_object_t *dobj;
 
361
    int flags;
 
362
 
 
363
    h->cache_obj = NULL;
 
364
 
 
365
    /* Look up entity keyed to 'url' */
 
366
    if (conf->cache_root == NULL) {
 
367
        if (!error_logged) {
 
368
            error_logged = 1;
 
369
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
 
370
                         "disk_cache: Cannot cache files to disk without a CacheRoot specified.");
 
371
        }
 
372
        return DECLINED;
 
373
    }
 
374
 
 
375
    /* Create and init the cache object */
 
376
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
 
377
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
 
378
 
 
379
    info = &(obj->info);
 
380
 
 
381
    /* Open the headers file */
 
382
    dobj->prefix = NULL;
 
383
 
 
384
    /* Save the cache root */
 
385
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
 
386
    dobj->root_len = conf->cache_root_len;
 
387
 
 
388
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
 
389
    flags = APR_READ|APR_BINARY|APR_BUFFERED;
 
390
    rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
 
391
    if (rc != APR_SUCCESS) {
 
392
        return DECLINED;
 
393
    }
 
394
 
 
395
    /* read the format from the cache file */
 
396
    len = sizeof(format);
 
397
    apr_file_read_full(dobj->hfd, &format, len, &len);
 
398
 
 
399
    if (format == VARY_FORMAT_VERSION) {
 
400
        apr_array_header_t* varray;
 
401
        apr_time_t expire;
 
402
 
 
403
        len = sizeof(expire);
 
404
        apr_file_read_full(dobj->hfd, &expire, len, &len);
 
405
 
 
406
        if (expire < r->request_time) {
 
407
            return DECLINED;
 
408
        }
 
409
 
 
410
        varray = apr_array_make(r->pool, 5, sizeof(char*));
 
411
        rc = read_array(r, varray, dobj->hfd);
 
412
        if (rc != APR_SUCCESS) {
 
413
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
 
414
                         "disk_cache: Cannot parse vary header file: %s",
 
415
                         dobj->hdrsfile);
 
416
            return DECLINED;
 
417
        }
 
418
        apr_file_close(dobj->hfd);
 
419
 
 
420
        nkey = regen_key(r->pool, r->headers_in, varray, key);
 
421
 
 
422
        dobj->hashfile = NULL;
 
423
        dobj->prefix = dobj->hdrsfile;
 
424
        dobj->hdrsfile = header_file(r->pool, conf, dobj, nkey);
 
425
 
 
426
        flags = APR_READ|APR_BINARY|APR_BUFFERED;
 
427
        rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
 
428
        if (rc != APR_SUCCESS) {
 
429
            return DECLINED;
 
430
        }
 
431
    }
 
432
    else if (format != DISK_FORMAT_VERSION) {
 
433
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
 
434
                     "cache_disk: File '%s' has a version mismatch. File had version: %d.",
 
435
                     dobj->hdrsfile, format);
 
436
        return DECLINED;
 
437
    }
 
438
    else {
 
439
        apr_off_t offset = 0;
 
440
        /* This wasn't a Vary Format file, so we must seek to the
 
441
         * start of the file again, so that later reads work.
 
442
         */
 
443
        apr_file_seek(dobj->hfd, APR_SET, &offset);
 
444
        nkey = key;
 
445
    }
 
446
 
 
447
    obj->key = nkey;
 
448
    dobj->key = nkey;
 
449
    dobj->name = key;
 
450
    dobj->datafile = data_file(r->pool, conf, dobj, nkey);
 
451
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
 
452
 
 
453
    /* Open the data file */
 
454
    flags = APR_READ|APR_BINARY;
 
455
#ifdef APR_SENDFILE_ENABLED
 
456
    flags |= APR_SENDFILE_ENABLED;
 
457
#endif
 
458
    rc = apr_file_open(&dobj->fd, dobj->datafile, flags, 0, r->pool);
 
459
    if (rc != APR_SUCCESS) {
 
460
        /* XXX: Log message */
 
461
        return DECLINED;
 
462
    }
 
463
 
 
464
    rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, dobj->fd);
 
465
    if (rc == APR_SUCCESS) {
 
466
        dobj->file_size = finfo.size;
 
467
    }
 
468
 
 
469
    /* Read the bytes to setup the cache_info fields */
 
470
    rc = file_cache_recall_mydata(dobj->hfd, info, dobj, r);
 
471
    if (rc != APR_SUCCESS) {
 
472
        /* XXX log message */
 
473
        return DECLINED;
 
474
    }
 
475
 
 
476
    /* Initialize the cache_handle callback functions */
 
477
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
 
478
                 "disk_cache: Recalled cached URL info header %s",  dobj->name);
 
479
    return OK;
 
480
}
 
481
 
 
482
static int remove_entity(cache_handle_t *h)
 
483
{
 
484
    /* Null out the cache object pointer so next time we start from scratch  */
 
485
    h->cache_obj = NULL;
 
486
    return OK;
 
487
}
 
488
 
 
489
static int remove_url(cache_handle_t *h, apr_pool_t *p)
 
490
{
 
491
    apr_status_t rc;
 
492
    disk_cache_object_t *dobj;
 
493
 
 
494
    /* Get disk cache object from cache handle */
 
495
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
 
496
    if (!dobj) {
 
497
        return DECLINED;
 
498
    }
 
499
 
 
500
    /* Delete headers file */
 
501
    if (dobj->hdrsfile) {
 
502
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
 
503
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
 
504
 
 
505
        rc = apr_file_remove(dobj->hdrsfile, p);
 
506
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
 
507
            /* Will only result in an output if httpd is started with -e debug.
 
508
             * For reason see log_error_core for the case s == NULL.
 
509
             */
 
510
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
 
511
                   "disk_cache: Failed to delete headers file %s from cache.",
 
512
                         dobj->hdrsfile);
 
513
            return DECLINED;
 
514
        }
 
515
    }
 
516
 
 
517
     /* Delete data file */
 
518
    if (dobj->datafile) {
 
519
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
 
520
                     "disk_cache: Deleting %s from cache.", dobj->datafile);
 
521
 
 
522
        rc = apr_file_remove(dobj->datafile, p);
 
523
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
 
524
            /* Will only result in an output if httpd is started with -e debug.
 
525
             * For reason see log_error_core for the case s == NULL.
 
526
             */
 
527
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
 
528
                      "disk_cache: Failed to delete data file %s from cache.",
 
529
                         dobj->datafile);
 
530
            return DECLINED;
 
531
        }
 
532
    }
 
533
 
 
534
    /* now delete directories as far as possible up to our cache root */
 
535
    if (dobj->root) {
 
536
        const char *str_to_copy;
 
537
 
 
538
        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->datafile;
 
539
        if (str_to_copy) {
 
540
            char *dir, *slash, *q;
 
541
 
 
542
            dir = apr_pstrdup(p, str_to_copy);
 
543
 
 
544
            /* remove filename */
 
545
            slash = strrchr(dir, '/');
 
546
            *slash = '\0';
 
547
 
 
548
            /*
 
549
             * now walk our way back to the cache root, delete everything
 
550
             * in the way as far as possible
 
551
             *
 
552
             * Note: due to the way we constructed the file names in
 
553
             * header_file and data_file, we are guaranteed that the
 
554
             * cache_root is suffixed by at least one '/' which will be
 
555
             * turned into a terminating null by this loop.  Therefore,
 
556
             * we won't either delete or go above our cache root.
 
557
             */
 
558
            for (q = dir + dobj->root_len; *q ; ) {
 
559
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
 
560
                              "disk_cache: Deleting directory %s from cache",
 
561
                              dir);
 
562
 
 
563
                 rc = apr_dir_remove(dir, p);
 
564
                 if (rc != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rc)) {
 
565
                    break;
 
566
                 }
 
567
                 slash = strrchr(q, '/');
 
568
                 *slash = '\0';
 
569
            }
 
570
        }
 
571
    }
 
572
 
 
573
    return OK;
 
574
}
 
575
 
 
576
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
 
577
                               apr_file_t *file)
 
578
{
 
579
    char w[MAX_STRING_LEN];
 
580
    int p;
 
581
    apr_status_t rv;
 
582
 
 
583
    while (1) {
 
584
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
 
585
        if (rv != APR_SUCCESS) {
 
586
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
587
                          "Premature end of vary array.");
 
588
            return rv;
 
589
        }
 
590
 
 
591
        p = strlen(w);
 
592
        if (p > 0 && w[p - 1] == '\n') {
 
593
            if (p > 1 && w[p - 2] == CR) {
 
594
                w[p - 2] = '\0';
 
595
            }
 
596
            else {
 
597
                w[p - 1] = '\0';
 
598
            }
 
599
        }
 
600
 
 
601
        /* If we've finished reading the array, break out of the loop. */
 
602
        if (w[0] == '\0') {
 
603
            break;
 
604
        }
 
605
 
 
606
       *((const char **) apr_array_push(arr)) = apr_pstrdup(r->pool, w);
 
607
    }
 
608
 
 
609
    return APR_SUCCESS;
 
610
}
 
611
 
 
612
static apr_status_t store_array(apr_file_t *fd, apr_array_header_t* arr)
 
613
{
 
614
    int i;
 
615
    apr_status_t rv;
 
616
    struct iovec iov[2];
 
617
    apr_size_t amt;
 
618
    const char **elts;
 
619
 
 
620
    elts = (const char **) arr->elts;
 
621
 
 
622
    for (i = 0; i < arr->nelts; i++) {
 
623
        iov[0].iov_base = (char*) elts[i];
 
624
        iov[0].iov_len = strlen(elts[i]);
 
625
        iov[1].iov_base = CRLF;
 
626
        iov[1].iov_len = sizeof(CRLF) - 1;
 
627
 
 
628
        rv = apr_file_writev(fd, (const struct iovec *) &iov, 2,
 
629
                             &amt);
 
630
        if (rv != APR_SUCCESS) {
 
631
            return rv;
 
632
        }
 
633
    }
 
634
 
 
635
    iov[0].iov_base = CRLF;
 
636
    iov[0].iov_len = sizeof(CRLF) - 1;
 
637
 
 
638
    return apr_file_writev(fd, (const struct iovec *) &iov, 1,
 
639
                         &amt);
 
640
}
 
641
 
 
642
static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
 
643
                               apr_table_t *table, apr_file_t *file)
 
644
{
 
645
    char w[MAX_STRING_LEN];
 
646
    char *l;
 
647
    int p;
 
648
    apr_status_t rv;
 
649
 
 
650
    while (1) {
 
651
 
 
652
        /* ### What about APR_EOF? */
 
653
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
 
654
        if (rv != APR_SUCCESS) {
 
655
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
656
                          "Premature end of cache headers.");
 
657
            return rv;
 
658
        }
 
659
 
 
660
        /* Delete terminal (CR?)LF */
 
661
 
 
662
        p = strlen(w);
 
663
        /* Indeed, the host's '\n':
 
664
           '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
 
665
           -- whatever the script generates.
 
666
        */
 
667
        if (p > 0 && w[p - 1] == '\n') {
 
668
            if (p > 1 && w[p - 2] == CR) {
 
669
                w[p - 2] = '\0';
 
670
            }
 
671
            else {
 
672
                w[p - 1] = '\0';
 
673
            }
 
674
        }
 
675
 
 
676
        /* If we've finished reading the headers, break out of the loop. */
 
677
        if (w[0] == '\0') {
 
678
            break;
 
679
        }
 
680
 
 
681
#if APR_CHARSET_EBCDIC
 
682
        /* Chances are that we received an ASCII header text instead of
 
683
         * the expected EBCDIC header lines. Try to auto-detect:
 
684
         */
 
685
        if (!(l = strchr(w, ':'))) {
 
686
            int maybeASCII = 0, maybeEBCDIC = 0;
 
687
            unsigned char *cp, native;
 
688
            apr_size_t inbytes_left, outbytes_left;
 
689
 
 
690
            for (cp = w; *cp != '\0'; ++cp) {
 
691
                native = apr_xlate_conv_byte(ap_hdrs_from_ascii, *cp);
 
692
                if (apr_isprint(*cp) && !apr_isprint(native))
 
693
                    ++maybeEBCDIC;
 
694
                if (!apr_isprint(*cp) && apr_isprint(native))
 
695
                    ++maybeASCII;
 
696
            }
 
697
            if (maybeASCII > maybeEBCDIC) {
 
698
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
 
699
                             "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
 
700
                             r->filename);
 
701
                inbytes_left = outbytes_left = cp - w;
 
702
                apr_xlate_conv_buffer(ap_hdrs_from_ascii,
 
703
                                      w, &inbytes_left, w, &outbytes_left);
 
704
            }
 
705
        }
 
706
#endif /*APR_CHARSET_EBCDIC*/
 
707
 
 
708
        /* if we see a bogus header don't ignore it. Shout and scream */
 
709
        if (!(l = strchr(w, ':'))) {
 
710
            return APR_EGENERAL;
 
711
        }
 
712
 
 
713
        *l++ = '\0';
 
714
        while (*l && apr_isspace(*l)) {
 
715
            ++l;
 
716
        }
 
717
 
 
718
        apr_table_add(table, w, l);
 
719
    }
 
720
 
 
721
    return APR_SUCCESS;
 
722
}
 
723
 
 
724
/*
 
725
 * Reads headers from a buffer and returns an array of headers.
 
726
 * Returns NULL on file error
 
727
 * This routine tries to deal with too long lines and continuation lines.
 
728
 * @@@: XXX: FIXME: currently the headers are passed thru un-merged.
 
729
 * Is that okay, or should they be collapsed where possible?
 
730
 */
 
731
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
 
732
{
 
733
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
 
734
 
 
735
    /* This case should not happen... */
 
736
    if (!dobj->hfd) {
 
737
        /* XXX log message */
 
738
        return APR_NOTFOUND;
 
739
    }
 
740
 
 
741
    h->req_hdrs = apr_table_make(r->pool, 20);
 
742
    h->resp_hdrs = apr_table_make(r->pool, 20);
 
743
 
 
744
    /* Call routine to read the header lines/status line */
 
745
    read_table(h, r, h->resp_hdrs, dobj->hfd);
 
746
    read_table(h, r, h->req_hdrs, dobj->hfd);
 
747
 
 
748
    apr_file_close(dobj->hfd);
 
749
 
 
750
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
 
751
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
 
752
    return APR_SUCCESS;
 
753
}
 
754
 
 
755
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
 
756
{
 
757
    apr_bucket *e;
 
758
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
 
759
 
 
760
    e = apr_bucket_file_create(dobj->fd, 0, (apr_size_t) dobj->file_size, p,
 
761
                               bb->bucket_alloc);
 
762
    APR_BRIGADE_INSERT_HEAD(bb, e);
 
763
    e = apr_bucket_eos_create(bb->bucket_alloc);
 
764
    APR_BRIGADE_INSERT_TAIL(bb, e);
 
765
 
 
766
    return APR_SUCCESS;
 
767
}
 
768
 
 
769
static apr_status_t store_table(apr_file_t *fd, apr_table_t *table)
 
770
{
 
771
    int i;
 
772
    apr_status_t rv;
 
773
    struct iovec iov[4];
 
774
    apr_size_t amt;
 
775
    apr_table_entry_t *elts;
 
776
 
 
777
    elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
 
778
    for (i = 0; i < apr_table_elts(table)->nelts; ++i) {
 
779
        if (elts[i].key != NULL) {
 
780
            iov[0].iov_base = elts[i].key;
 
781
            iov[0].iov_len = strlen(elts[i].key);
 
782
            iov[1].iov_base = ": ";
 
783
            iov[1].iov_len = sizeof(": ") - 1;
 
784
            iov[2].iov_base = elts[i].val;
 
785
            iov[2].iov_len = strlen(elts[i].val);
 
786
            iov[3].iov_base = CRLF;
 
787
            iov[3].iov_len = sizeof(CRLF) - 1;
 
788
 
 
789
            rv = apr_file_writev(fd, (const struct iovec *) &iov, 4,
 
790
                                 &amt);
 
791
            if (rv != APR_SUCCESS) {
 
792
                return rv;
 
793
            }
 
794
        }
 
795
    }
 
796
    iov[0].iov_base = CRLF;
 
797
    iov[0].iov_len = sizeof(CRLF) - 1;
 
798
    rv = apr_file_writev(fd, (const struct iovec *) &iov, 1,
 
799
                         &amt);
 
800
    return rv;
 
801
}
 
802
 
 
803
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
 
804
{
 
805
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
 
806
                                                 &disk_cache_module);
 
807
    apr_status_t rv;
 
808
    apr_size_t amt;
 
809
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
 
810
 
 
811
    disk_cache_info_t disk_info;
 
812
    struct iovec iov[2];
 
813
 
 
814
    /* This is flaky... we need to manage the cache_info differently */
 
815
    h->cache_obj->info = *info;
 
816
 
 
817
    if (r->headers_out) {
 
818
        const char *tmp;
 
819
 
 
820
        tmp = apr_table_get(r->headers_out, "Vary");
 
821
 
 
822
        if (tmp) {
 
823
            apr_array_header_t* varray;
 
824
            apr_uint32_t format = VARY_FORMAT_VERSION;
 
825
 
 
826
            mkdir_structure(conf, dobj->hdrsfile, r->pool);
 
827
 
 
828
            rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
 
829
                                 APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL,
 
830
                                 r->pool);
 
831
 
 
832
            if (rv != APR_SUCCESS) {
 
833
                return rv;
 
834
            }
 
835
 
 
836
            amt = sizeof(format);
 
837
            apr_file_write(dobj->tfd, &format, &amt);
 
838
 
 
839
            amt = sizeof(info->expire);
 
840
            apr_file_write(dobj->tfd, &info->expire, &amt);
 
841
 
 
842
            varray = apr_array_make(r->pool, 6, sizeof(char*));
 
843
            tokens_to_array(r->pool, tmp, varray);
 
844
 
 
845
            store_array(dobj->tfd, varray);
 
846
 
 
847
            apr_file_close(dobj->tfd);
 
848
 
 
849
            dobj->tfd = NULL;
 
850
 
 
851
            rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile,
 
852
                                  r->pool);
 
853
            if (rv != APR_SUCCESS) {
 
854
                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
 
855
                    "disk_cache: rename tempfile to varyfile failed: %s -> %s",
 
856
                    dobj->tempfile, dobj->hdrsfile);
 
857
                return rv;
 
858
            }
 
859
 
 
860
            dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
 
861
            tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
 
862
            dobj->prefix = dobj->hdrsfile;
 
863
            dobj->hashfile = NULL;
 
864
            dobj->datafile = data_file(r->pool, conf, dobj, tmp);
 
865
            dobj->hdrsfile = header_file(r->pool, conf, dobj, tmp);
 
866
        }
 
867
    }
 
868
 
 
869
 
 
870
    rv = apr_file_mktemp(&dobj->hfd, dobj->tempfile,
 
871
                         APR_CREATE | APR_WRITE | APR_BINARY |
 
872
                         APR_BUFFERED | APR_EXCL, r->pool);
 
873
 
 
874
    if (rv != APR_SUCCESS) {
 
875
        return rv;
 
876
    }
 
877
 
 
878
    dobj->name = h->cache_obj->key;
 
879
 
 
880
    disk_info.format = DISK_FORMAT_VERSION;
 
881
    disk_info.date = info->date;
 
882
    disk_info.expire = info->expire;
 
883
    disk_info.entity_version = dobj->disk_info.entity_version++;
 
884
    disk_info.request_time = info->request_time;
 
885
    disk_info.response_time = info->response_time;
 
886
    disk_info.status = info->status;
 
887
 
 
888
    disk_info.name_len = strlen(dobj->name);
 
889
 
 
890
    iov[0].iov_base = (void*)&disk_info;
 
891
    iov[0].iov_len = sizeof(disk_cache_info_t);
 
892
    iov[1].iov_base = (void*)dobj->name;
 
893
    iov[1].iov_len = disk_info.name_len;
 
894
 
 
895
    rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, 2, &amt);
 
896
    if (rv != APR_SUCCESS) {
 
897
        return rv;
 
898
    }
 
899
 
 
900
    if (r->headers_out) {
 
901
        apr_table_t *headers_out;
 
902
 
 
903
        headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
 
904
                                                  r->server);
 
905
 
 
906
        if (!apr_table_get(headers_out, "Content-Type")
 
907
            && r->content_type) {
 
908
            apr_table_setn(headers_out, "Content-Type",
 
909
                           ap_make_content_type(r, r->content_type));
 
910
        }
 
911
 
 
912
        headers_out = apr_table_overlay(r->pool, headers_out,
 
913
                                        r->err_headers_out);
 
914
        rv = store_table(dobj->hfd, headers_out);
 
915
        if (rv != APR_SUCCESS) {
 
916
            return rv;
 
917
        }
 
918
    }
 
919
 
 
920
    /* Parse the vary header and dump those fields from the headers_in. */
 
921
    /* FIXME: Make call to the same thing cache_select calls to crack Vary. */
 
922
    if (r->headers_in) {
 
923
        apr_table_t *headers_in;
 
924
 
 
925
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
 
926
                                                 r->server);
 
927
        rv = store_table(dobj->hfd, headers_in);
 
928
        if (rv != APR_SUCCESS) {
 
929
            return rv;
 
930
        }
 
931
    }
 
932
 
 
933
    apr_file_close(dobj->hfd); /* flush and close */
 
934
 
 
935
    /* Remove old file with the same name. If remove fails, then
 
936
     * perhaps we need to create the directory tree where we are
 
937
     * about to write the new headers file.
 
938
     */
 
939
    rv = apr_file_remove(dobj->hdrsfile, r->pool);
 
940
    if (rv != APR_SUCCESS) {
 
941
        mkdir_structure(conf, dobj->hdrsfile, r->pool);
 
942
    }
 
943
 
 
944
    rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile, r->pool);
 
945
    if (rv != APR_SUCCESS) {
 
946
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
 
947
                     "disk_cache: rename tempfile to hdrsfile failed: %s -> %s",
 
948
                     dobj->tempfile, dobj->hdrsfile);
 
949
        return rv;
 
950
    }
 
951
 
 
952
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
 
953
 
 
954
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
 
955
                 "disk_cache: Stored headers for URL %s",  dobj->name);
 
956
    return APR_SUCCESS;
 
957
}
 
958
 
 
959
static apr_status_t store_body(cache_handle_t *h, request_rec *r,
 
960
                               apr_bucket_brigade *bb)
 
961
{
 
962
    apr_bucket *e;
 
963
    apr_status_t rv;
 
964
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
 
965
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
 
966
                                                 &disk_cache_module);
 
967
 
 
968
    /* We write to a temp file and then atomically rename the file over
 
969
     * in file_cache_el_final().
 
970
     */
 
971
    if (!dobj->tfd) {
 
972
        rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
 
973
                             APR_CREATE | APR_WRITE | APR_BINARY |
 
974
                             APR_BUFFERED | APR_EXCL, r->pool);
 
975
        if (rv != APR_SUCCESS) {
 
976
            return rv;
 
977
        }
 
978
        dobj->file_size = 0;
 
979
    }
 
980
 
 
981
    for (e = APR_BRIGADE_FIRST(bb);
 
982
         e != APR_BRIGADE_SENTINEL(bb);
 
983
         e = APR_BUCKET_NEXT(e))
 
984
    {
 
985
        const char *str;
 
986
        apr_size_t length, written;
 
987
        rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
 
988
        if (rv != APR_SUCCESS) {
 
989
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
 
990
                         "cache_disk: Error when reading bucket for URL %s",
 
991
                         h->cache_obj->key);
 
992
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
 
993
            file_cache_errorcleanup(dobj, r);
 
994
            return rv;
 
995
        }
 
996
        rv = apr_file_write_full(dobj->tfd, str, length, &written);
 
997
        if (rv != APR_SUCCESS) {
 
998
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
 
999
                         "cache_disk: Error when writing cache file for URL %s",
 
1000
                         h->cache_obj->key);
 
1001
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
 
1002
            file_cache_errorcleanup(dobj, r);
 
1003
            return rv;
 
1004
        }
 
1005
        dobj->file_size += written;
 
1006
        if (dobj->file_size > conf->maxfs) {
 
1007
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
 
1008
                         "cache_disk: URL %s failed the size check "
 
1009
                         "(%" APR_OFF_T_FMT ">%" APR_SIZE_T_FMT ")",
 
1010
                         h->cache_obj->key, dobj->file_size, conf->maxfs);
 
1011
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
 
1012
            file_cache_errorcleanup(dobj, r);
 
1013
            return APR_EGENERAL;
 
1014
        }
 
1015
    }
 
1016
 
 
1017
    /* Was this the final bucket? If yes, close the temp file and perform
 
1018
     * sanity checks.
 
1019
     */
 
1020
    if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
 
1021
        if (r->connection->aborted || r->no_cache) {
 
1022
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
 
1023
                         "disk_cache: Discarding body for URL %s "
 
1024
                         "because connection has been aborted.",
 
1025
                         h->cache_obj->key);
 
1026
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
 
1027
            file_cache_errorcleanup(dobj, r);
 
1028
            return APR_EGENERAL;
 
1029
        }
 
1030
        if (dobj->file_size < conf->minfs) {
 
1031
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
 
1032
                         "cache_disk: URL %s failed the size check "
 
1033
                         "(%" APR_OFF_T_FMT "<%" APR_SIZE_T_FMT ")",
 
1034
                         h->cache_obj->key, dobj->file_size, conf->minfs);
 
1035
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
 
1036
            file_cache_errorcleanup(dobj, r);
 
1037
            return APR_EGENERAL;
 
1038
        }
 
1039
 
 
1040
        /* All checks were fine. Move tempfile to final destination */
 
1041
        /* Link to the perm file, and close the descriptor */
 
1042
        file_cache_el_final(dobj, r);
 
1043
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
 
1044
                     "disk_cache: Body for URL %s cached.",  dobj->name);
 
1045
    }
 
1046
 
 
1047
    return APR_SUCCESS;
 
1048
}
 
1049
 
 
1050
static void *create_config(apr_pool_t *p, server_rec *s)
 
1051
{
 
1052
    disk_cache_conf *conf = apr_pcalloc(p, sizeof(disk_cache_conf));
 
1053
 
 
1054
    /* XXX: Set default values */
 
1055
    conf->dirlevels = DEFAULT_DIRLEVELS;
 
1056
    conf->dirlength = DEFAULT_DIRLENGTH;
 
1057
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
 
1058
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
 
1059
 
 
1060
    conf->cache_root = NULL;
 
1061
    conf->cache_root_len = 0;
 
1062
 
 
1063
    return conf;
 
1064
}
 
1065
 
 
1066
/*
 
1067
 * mod_disk_cache configuration directives handlers.
 
1068
 */
 
1069
static const char
 
1070
*set_cache_root(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
1071
{
 
1072
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
 
1073
                                                 &disk_cache_module);
 
1074
    conf->cache_root = arg;
 
1075
    conf->cache_root_len = strlen(arg);
 
1076
    /* TODO: canonicalize cache_root and strip off any trailing slashes */
 
1077
 
 
1078
    return NULL;
 
1079
}
 
1080
 
 
1081
/*
 
1082
 * Consider eliminating the next two directives in favor of
 
1083
 * Ian's prime number hash...
 
1084
 * key = hash_fn( r->uri)
 
1085
 * filename = "/key % prime1 /key %prime2/key %prime3"
 
1086
 */
 
1087
static const char
 
1088
*set_cache_dirlevels(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
1089
{
 
1090
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
 
1091
                                                 &disk_cache_module);
 
1092
    int val = atoi(arg);
 
1093
    if (val < 1)
 
1094
        return "CacheDirLevels value must be an integer greater than 0";
 
1095
    if (val * conf->dirlength > CACHEFILE_LEN)
 
1096
        return "CacheDirLevels*CacheDirLength value must not be higher than 20";
 
1097
    conf->dirlevels = val;
 
1098
    return NULL;
 
1099
}
 
1100
static const char
 
1101
*set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
1102
{
 
1103
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
 
1104
                                                 &disk_cache_module);
 
1105
    int val = atoi(arg);
 
1106
    if (val < 1)
 
1107
        return "CacheDirLength value must be an integer greater than 0";
 
1108
    if (val * conf->dirlevels > CACHEFILE_LEN)
 
1109
        return "CacheDirLevels*CacheDirLength value must not be higher than 20";
 
1110
 
 
1111
    conf->dirlength = val;
 
1112
    return NULL;
 
1113
}
 
1114
 
 
1115
static const char
 
1116
*set_cache_minfs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
1117
{
 
1118
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
 
1119
                                                 &disk_cache_module);
 
1120
    conf->minfs = atoi(arg);
 
1121
    return NULL;
 
1122
}
 
1123
static const char
 
1124
*set_cache_maxfs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
1125
{
 
1126
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
 
1127
                                                 &disk_cache_module);
 
1128
    conf->maxfs = atoi(arg);
 
1129
    return NULL;
 
1130
}
 
1131
 
 
1132
static const command_rec disk_cache_cmds[] =
 
1133
{
 
1134
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
 
1135
                 "The directory to store cache files"),
 
1136
    AP_INIT_TAKE1("CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF,
 
1137
                  "The number of levels of subdirectories in the cache"),
 
1138
    AP_INIT_TAKE1("CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF,
 
1139
                  "The number of characters in subdirectory names"),
 
1140
    AP_INIT_TAKE1("CacheMinFileSize", set_cache_minfs, NULL, RSRC_CONF,
 
1141
                  "The minimum file size to cache a document"),
 
1142
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
 
1143
                  "The maximum file size to cache a document"),
 
1144
    {NULL}
 
1145
};
 
1146
 
 
1147
static const cache_provider cache_disk_provider =
 
1148
{
 
1149
    &remove_entity,
 
1150
    &store_headers,
 
1151
    &store_body,
 
1152
    &recall_headers,
 
1153
    &recall_body,
 
1154
    &create_entity,
 
1155
    &open_entity,
 
1156
    &remove_url,
 
1157
};
 
1158
 
 
1159
static void disk_cache_register_hook(apr_pool_t *p)
 
1160
{
 
1161
    /* cache initializer */
 
1162
    ap_register_provider(p, CACHE_PROVIDER_GROUP, "disk", "0",
 
1163
                         &cache_disk_provider);
 
1164
}
 
1165
 
 
1166
module AP_MODULE_DECLARE_DATA disk_cache_module = {
 
1167
    STANDARD20_MODULE_STUFF,
 
1168
    NULL,                       /* create per-directory config structure */
 
1169
    NULL,                       /* merge per-directory config structures */
 
1170
    create_config,              /* create per-server config structure */
 
1171
    NULL,                       /* merge per-server config structures */
 
1172
    disk_cache_cmds,            /* command apr_table_t */
 
1173
    disk_cache_register_hook    /* register hooks */
 
1174
};