View | Details | Raw Unified | Return to bug 39380
Collapse All | Expand All

(-)dist/modules/cache/cache_storage.c (+30 lines)
Lines 331-340 Link Here
331
apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
331
apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
332
                                        char**key)
332
                                        char**key)
333
{
333
{
334
    cache_request_rec *cache;
334
    char *port_str, *hn, *lcs;
335
    char *port_str, *hn, *lcs;
335
    const char *hostname, *scheme;
336
    const char *hostname, *scheme;
336
    int i;
337
    int i;
337
338
339
    cache = (cache_request_rec *) ap_get_module_config(r->request_config,
340
                                                       &cache_module);
341
    if (!cache) {
342
        /* This should never happen */
343
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
344
                     "cache: No cache request information available for key"
345
                     " generation");
346
        *key = "";
347
        return APR_EGENERAL;
348
    }
349
    if (cache->key) {
350
        /*
351
         * We have been here before during the processing of this request.
352
         * So return the key we already have.
353
         */
354
        *key = apr_pstrdup(p, cache->key);
355
        return APR_SUCCESS;
356
    }
357
338
    /*
358
    /*
339
     * Use the canonical name to improve cache hit rate, but only if this is
359
     * Use the canonical name to improve cache hit rate, but only if this is
340
     * not a proxy request or if this is a reverse proxy request.
360
     * not a proxy request or if this is a reverse proxy request.
Lines 429-433 Link Here
429
    *key = apr_pstrcat(p, scheme, "://", hostname, port_str,
449
    *key = apr_pstrcat(p, scheme, "://", hostname, port_str,
430
                       r->parsed_uri.path, "?", r->args, NULL);
450
                       r->parsed_uri.path, "?", r->args, NULL);
431
451
452
    /*
453
     * Store the key in the request_config for the cache as r->parsed_uri
454
     * might have changed in the time from our first visit here triggered by the
455
     * quick handler and our possible second visit triggered by the CACHE_SAVE
456
     * filter (e.g. r->parsed_uri got unescaped). In this case we would save the
457
     * resource in the cache under a key where it is never found by the quick
458
     * handler during following requests.
459
     */
460
    cache->key = apr_pstrdup(r->pool, *key);
461
432
    return APR_SUCCESS;
462
    return APR_SUCCESS;
433
}
463
}
(-)dist/modules/cache/cache_util.c (-5 / +21 lines)
Lines 243-249 Link Here
243
    age = ap_cache_current_age(info, age_c, r->request_time);
243
    age = ap_cache_current_age(info, age_c, r->request_time);
244
244
245
    /* extract s-maxage */
245
    /* extract s-maxage */
246
    if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "s-maxage", &val)) {
246
    if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "s-maxage", &val)
247
        && val != NULL) {
247
        smaxage = apr_atoi64(val);
248
        smaxage = apr_atoi64(val);
248
    }
249
    }
249
    else {
250
    else {
Lines 252-258 Link Here
252
253
253
    /* extract max-age from request */
254
    /* extract max-age from request */
254
    if (!conf->ignorecachecontrol
255
    if (!conf->ignorecachecontrol
255
        && cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val)) {
256
        && cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val)
257
        && val != NULL) {
256
        maxage_req = apr_atoi64(val);
258
        maxage_req = apr_atoi64(val);
257
    }
259
    }
258
    else {
260
    else {
Lines 260-266 Link Here
260
    }
262
    }
261
263
262
    /* extract max-age from response */
264
    /* extract max-age from response */
263
    if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "max-age", &val)) {
265
    if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "max-age", &val)
266
        && val != NULL) {
264
        maxage_cresp = apr_atoi64(val);
267
        maxage_cresp = apr_atoi64(val);
265
    }
268
    }
266
    else {
269
    else {
Lines 282-288 Link Here
282
285
283
    /* extract max-stale */
286
    /* extract max-stale */
284
    if (cc_req && ap_cache_liststr(r->pool, cc_req, "max-stale", &val)) {
287
    if (cc_req && ap_cache_liststr(r->pool, cc_req, "max-stale", &val)) {
285
        maxstale = apr_atoi64(val);
288
        if(val != NULL) {
289
            maxstale = apr_atoi64(val);
290
        }
291
        else {
292
            /* If no value is assigned to max-stale, then the client is willing
293
             * to accept a stale response of any age */
294
            /* Let's pretend 100 years is enough, need margin some marging here
295
             * or the freshness calculation later will overflow */
296
            maxstale = APR_INT64_C(86400*365*100);
297
        }
286
    }
298
    }
287
    else {
299
    else {
288
        maxstale = 0;
300
        maxstale = 0;
Lines 290-296 Link Here
290
302
291
    /* extract min-fresh */
303
    /* extract min-fresh */
292
    if (!conf->ignorecachecontrol
304
    if (!conf->ignorecachecontrol
293
        && cc_req && ap_cache_liststr(r->pool, cc_req, "min-fresh", &val)) {
305
        && cc_req && ap_cache_liststr(r->pool, cc_req, "min-fresh", &val)
306
        && val != NULL) {
294
        minfresh = apr_atoi64(val);
307
        minfresh = apr_atoi64(val);
295
    }
308
    }
296
    else {
309
    else {
Lines 419-424 Link Here
419
                                                  next - val_start);
432
                                                  next - val_start);
420
                        }
433
                        }
421
                    }
434
                    }
435
                    else {
436
                        *val = NULL;
437
                    }
422
                }
438
                }
423
                return 1;
439
                return 1;
424
            }
440
            }
(-)dist/modules/cache/mod_cache.c (-3 / +4 lines)
Lines 456-462 Link Here
456
         */
456
         */
457
        reason = "No Last-Modified, Etag, or Expires headers";
457
        reason = "No Last-Modified, Etag, or Expires headers";
458
    }
458
    }
459
    else if (r->header_only) {
459
    else if (r->header_only && !cache->stale_handle) {
460
        /* HEAD requests */
460
        /* HEAD requests */
461
        reason = "HTTP HEAD request";
461
        reason = "HTTP HEAD request";
462
    }
462
    }
Lines 589-599 Link Here
589
            cache->provider->remove_entity(cache->stale_handle);
589
            cache->provider->remove_entity(cache->stale_handle);
590
            /* Treat the request as if it wasn't conditional. */
590
            /* Treat the request as if it wasn't conditional. */
591
            cache->stale_handle = NULL;
591
            cache->stale_handle = NULL;
592
            rv = !OK;
592
        }
593
        }
593
    }
594
    }
594
595
595
    /* no cache handle, create a new entity */
596
    /* no cache handle, create a new entity only for non-HEAD request */
596
    if (!cache->handle) {
597
    if (!cache->handle && !r->header_only) {
597
        rv = cache_create_entity(r, size);
598
        rv = cache_create_entity(r, size);
598
        info = apr_pcalloc(r->pool, sizeof(cache_info));
599
        info = apr_pcalloc(r->pool, sizeof(cache_info));
599
        /* We only set info->status upon the initial creation. */
600
        /* We only set info->status upon the initial creation. */
(-)dist/modules/cache/mod_cache.h (+3 lines)
Lines 231-236 Link Here
231
    apr_time_t lastmod;                 /* last-modified time */
231
    apr_time_t lastmod;                 /* last-modified time */
232
    cache_info *info;                   /* current cache info */
232
    cache_info *info;                   /* current cache info */
233
    ap_filter_t *remove_url_filter;     /* Enable us to remove the filter */
233
    ap_filter_t *remove_url_filter;     /* Enable us to remove the filter */
234
    char *key;                          /* The cache key created for this
235
                                         * request
236
                                         */
234
} cache_request_rec;
237
} cache_request_rec;
235
238
236
239
(-)dist/modules/cache/mod_disk_cache.c (-473 / +2264 lines)
Lines 22-54 Link Here
22
#include "util_filter.h"
22
#include "util_filter.h"
23
#include "util_script.h"
23
#include "util_script.h"
24
#include "util_charset.h"
24
#include "util_charset.h"
25
#include "ap_mpm.h"
26
#include "mpm_common.h"
25
27
26
/*
28
/*
27
 * mod_disk_cache: Disk Based HTTP 1.1 Cache.
29
 * mod_disk_cache: Disk Based HTTP 1.1 Cache.
28
 *
30
 *
29
 * Flow to Find the .data file:
31
 * Flow to Find the right cache file:
30
 *   Incoming client requests URI /foo/bar/baz
32
 *   Incoming client requests an URL
31
 *   Generate <hash> off of /foo/bar/baz
33
 *   Generate <hash>.header from URL
32
 *   Open <hash>.header
34
 *   Open <hash>.header
33
 *   Read in <hash>.header file (may contain Format #1 or Format #2)
35
 *   Read in <hash>.header file format identifier, which might be:
34
 *   If format #1 (Contains a list of Vary Headers):
36
 *      VARY_FORMAT_VERSION - Vary headers
35
 *      Use each header name (from .header) with our request values (headers_in) to
37
 *      DISK_FORMAT_VERSION - Metadata and headers for a cached file
36
 *      regenerate <hash> using HeaderName+HeaderValue+.../foo/bar/baz
38
 *      Anything else       - Unknown header format, remove and return.
37
 *      re-read in <hash>.header (must be format #2)
38
 *   read in <hash>.data
39
 *
39
 *
40
 * Format #1:
40
 *   If VARY_FORMAT_VERSION (Contains a list of Vary Headers):
41
 *      Use each header name with our request values (headers_in) to
42
 *      regenerate <hash>.header using HeaderName+HeaderValue+URL,
43
 *      open it, read format (must be DISK_FORMAT_VERSION).
44
 *
45
 * VARY_FORMAT_VERSION:
41
 *   apr_uint32_t format;
46
 *   apr_uint32_t format;
42
 *   apr_time_t expire;
47
 *   apr_time_t expire;
43
 *   apr_array_t vary_headers (delimited by CRLF)
48
 *   apr_array_t vary_headers (delimited by CRLF)
44
 *
49
 *
45
 * Format #2:
50
 * DISK_FORMAT_VERSION:
46
 *   disk_cache_info_t (first sizeof(apr_uint32_t) bytes is the format)
51
 *   disk_cache_info_t
47
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
52
 *   entity name (dobj->name) [length is in disk_cache_info_t->name_len]
48
 *   r->headers_out (delimited by CRLF)
53
 *   bodyfile (dobj->bodyfile) [length is in disk_cache_info_t->bodyname_len]
49
 *   CRLF
54
 *   optional filename (r->filename)
50
 *   r->headers_in (delimited by CRLF)
55
 *                      [length is in disk_cache_info_t->filename_len]
51
 *   CRLF
56
 *   r->headers_out (see on disk header format below)
57
 *   r->headers_in
58
 *
59
 * On disk headers are stored in the following format:
60
 *   apr_uint32_t totsize; - size of headers to follow
61
 *   totsize amount of headers, HeaderA\0ValueA\0...HeaderN\0ValueN\0
52
 */
62
 */
53
63
54
module AP_MODULE_DECLARE_DATA disk_cache_module;
64
module AP_MODULE_DECLARE_DATA disk_cache_module;
Lines 62-243 Link Here
62
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
72
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
63
                               apr_file_t *file);
73
                               apr_file_t *file);
64
74
65
/*
66
 * Local static functions
67
 */
68
75
69
static char *header_file(apr_pool_t *p, disk_cache_conf *conf,
76
#define CACHE_LOOP_INCTIME(x) x <<= 1
70
                         disk_cache_object_t *dobj, const char *name)
77
#define CACHE_LOOP_DECTIME(x) x >>= 1
71
{
78
72
    if (!dobj->hashfile) {
79
static void cache_loop_sleep(apr_interval_time_t *t) {
73
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
80
74
                                                conf->dirlength, name);
81
    if(*t < CACHE_LOOP_MINSLEEP) {
82
        *t = CACHE_LOOP_MINSLEEP;
83
    }
84
    else if(*t > CACHE_LOOP_MAXSLEEP) {
85
        *t = CACHE_LOOP_MAXSLEEP;
75
    }
86
    }
76
87
77
    if (dobj->prefix) {
88
    apr_sleep(*t);
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
}
89
}
86
90
87
static char *data_file(apr_pool_t *p, disk_cache_conf *conf,
91
88
                       disk_cache_object_t *dobj, const char *name)
92
/*
93
 * Modified file bucket implementation to be able to deliver files
94
 * while caching.
95
 */
96
97
/* Derived from apr_buckets_file.c */
98
99
#define BUCKET_IS_DISKCACHE(e)        ((e)->type == &bucket_type_diskcache)
100
static const apr_bucket_type_t bucket_type_diskcache;
101
102
static void diskcache_bucket_destroy(void *data)
89
{
103
{
90
    if (!dobj->hashfile) {
104
    diskcache_bucket_data *f = data;
91
        dobj->hashfile = ap_cache_generate_name(p, conf->dirlevels,
92
                                                conf->dirlength, name);
93
    }
94
105
95
    if (dobj->prefix) {
106
    if (apr_bucket_shared_destroy(f)) {
96
        return apr_pstrcat(p, dobj->prefix, CACHE_VDIR_SUFFIX, "/",
107
        /* no need to close files here; it will get
97
                           dobj->hashfile, CACHE_DATA_SUFFIX, NULL);
108
         * done automatically when the pool gets cleaned up */
98
     }
109
        apr_bucket_free(f);
99
     else {
110
    }
100
        return apr_pstrcat(p, conf->cache_root, "/", dobj->hashfile,
101
                           CACHE_DATA_SUFFIX, NULL);
102
     }
103
}
111
}
104
112
105
static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
113
114
/* The idea here is to convert diskcache buckets to regular file buckets
115
   as data becomes available */
116
static apr_status_t diskcache_bucket_read(apr_bucket *e, const char **str,
117
                                          apr_size_t *len, 
118
                                          apr_read_type_e block)
106
{
119
{
120
    diskcache_bucket_data *a = e->data;
121
    apr_file_t *f = a->fd;
122
    apr_bucket *b = NULL;
123
    char *buf;
107
    apr_status_t rv;
124
    apr_status_t rv;
108
    char *p;
125
    apr_finfo_t finfo;
126
    apr_size_t filelength = e->length; /* bytes remaining in file past offset */
127
    apr_off_t fileoffset = e->start;
128
    apr_size_t available;
129
    apr_time_t start = apr_time_now();
130
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
131
    apr_int32_t flags;
132
#endif
133
134
#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
135
    if ((flags = apr_file_flags_get(f)) & APR_XTHREAD) {
136
        /* this file descriptor is shared across multiple threads and
137
         * this OS doesn't support that natively, so as a workaround
138
         * we must reopen the file into a->readpool */
139
        const char *fname;
140
        apr_file_name_get(&fname, f);
141
142
        rv = apr_file_open(&f, fname, (flags & ~APR_XTHREAD), 0, a->readpool);
143
        if (rv != APR_SUCCESS)
144
            return rv;
145
146
        a->fd = f;
147
    }
148
#endif
149
150
    /* in case we die prematurely */
151
    *str = NULL;
152
    *len = 0;
153
154
    /* DEBUG
155
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
156
            "disk_cache: Called diskcache_bucket_read");
157
     */
158
159
    while(1) {
160
        /* Figure out how big the file is right now, sit here until
161
           it's grown enough or we get bored */
162
        rv = apr_file_info_get(&finfo, 
163
                        APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_NLINK, f);
164
        if(rv != APR_SUCCESS) {
165
            return rv;
166
        }
109
167
110
    for (p = (char*)file + conf->cache_root_len + 1;;) {
168
        if(finfo.size >= fileoffset + MIN(filelength, CACHE_BUCKET_MINCHUNK)) {
111
        p = strchr(p, '/');
112
        if (!p)
113
            break;
169
            break;
114
        *p = '\0';
170
        }
171
172
        /* No use to even wait for a deleted file */
173
        if(finfo.nlink == 0) {
174
            return APR_EGENERAL;
175
        }
176
177
        if(block == APR_NONBLOCK_READ) {
178
            return APR_EAGAIN;
179
        }
115
180
116
        rv = apr_dir_make(file,
181
        /* Check for timeout */
117
                          APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
182
        if(finfo.mtime < (apr_time_now() - a->updtimeout) ) {
118
        if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
183
            return APR_EGENERAL;
119
            /* XXX */
184
        }
185
        /* If we have progress within half the timeout period, return what
186
           we have so far */
187
        if(finfo.size > fileoffset &&
188
                start < (apr_time_now() - a->updtimeout/2) ) 
189
        {
190
            break;
120
        }
191
        }
121
        *p = '/';
192
122
        ++p;
193
        /* Increase loop delay on each pass */
194
        cache_loop_sleep(&(a->polldelay));
195
        CACHE_LOOP_INCTIME(a->polldelay);
196
    }
197
    /* Decrease the loop delay a notch so the stored value is the actual
198
       delay needed */
199
    CACHE_LOOP_DECTIME(a->polldelay);
200
201
    /* Convert this bucket to a zero-length heap bucket so we won't be called
202
       again */
203
    buf = apr_bucket_alloc(0, e->list);
204
    apr_bucket_heap_make(e, buf, 0, apr_bucket_free);
205
206
    /* Wrap as much as possible into a regular file bucket */
207
    available = MIN(filelength, finfo.size-fileoffset);
208
    b = apr_bucket_file_create(f, fileoffset, available, a->readpool, e->list);
209
    APR_BUCKET_INSERT_AFTER(e, b);
210
211
    /* DEBUG
212
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
213
            "disk_cache: diskcache_bucket_read: Converted to regular file"
214
            " off %" APR_OFF_T_FMT " len %" APR_SIZE_T_FMT,
215
            fileoffset, available);
216
     */
217
218
219
    /* Put any remains in yet another bucket */
220
    if(available < filelength) {
221
        e=b;
222
        /* for efficiency, we can just build a new apr_bucket struct
223
         * to wrap around the existing bucket */
224
        b = apr_bucket_alloc(sizeof(*b), e->list);
225
        b->start  = fileoffset + available;
226
        b->length = filelength - available;
227
        b->data   = a;
228
        b->type   = &bucket_type_diskcache;
229
        b->free   = apr_bucket_free;
230
        b->list   = e->list;
231
        APR_BUCKET_INSERT_AFTER(e, b);
232
    }
233
    else {
234
        diskcache_bucket_destroy(a);
123
    }
235
    }
236
237
    *str = buf;
238
    return APR_SUCCESS;
124
}
239
}
125
240
126
/* htcacheclean may remove directories underneath us.
241
static apr_bucket * diskcache_bucket_make(apr_bucket *b,
127
 * So, we'll try renaming three times at a cost of 0.002 seconds.
242
                                                apr_file_t *fd,
128
 */
243
                                                apr_off_t offset,
129
static apr_status_t safe_file_rename(disk_cache_conf *conf,
244
                                                apr_size_t len, 
130
                                     const char *src, const char *dest,
245
                                                apr_interval_time_t timeout,
131
                                     apr_pool_t *pool)
246
                                                apr_pool_t *p)
132
{
247
{
133
    apr_status_t rv;
248
    diskcache_bucket_data *f;
134
249
135
    rv = apr_file_rename(src, dest, pool);
250
    f = apr_bucket_alloc(sizeof(*f), b->list);
251
    f->fd = fd;
252
    f->readpool = p;
253
    f->updtimeout = timeout;
254
    f->polldelay = 0;
136
255
137
    if (rv != APR_SUCCESS) {
256
    b = apr_bucket_shared_make(b, f, offset, len);
138
        int i;
257
    b->type = &bucket_type_diskcache;
139
258
140
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
259
    return b;
141
            /* 1000 micro-seconds aka 0.001 seconds. */
260
}
142
            apr_sleep(1000);
143
261
144
            mkdir_structure(conf, dest, pool);
262
static apr_bucket * diskcache_bucket_create(apr_file_t *fd,
263
                                                  apr_off_t offset,
264
                                                  apr_size_t len, 
265
                                                  apr_interval_time_t timeout,
266
                                                  apr_pool_t *p,
267
                                                  apr_bucket_alloc_t *list)
268
{
269
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
145
270
146
            rv = apr_file_rename(src, dest, pool);
271
    APR_BUCKET_INIT(b);
272
    b->free = apr_bucket_free;
273
    b->list = list;
274
    return diskcache_bucket_make(b, fd, offset, len, timeout, p);
275
}
276
277
278
/* FIXME: This is probably only correct for the first case, that seems
279
   to be the one that occurs all the time... */
280
static apr_status_t diskcache_bucket_setaside(apr_bucket *data, 
281
                                              apr_pool_t *reqpool)
282
{
283
    diskcache_bucket_data *a = data->data;
284
    apr_file_t *fd = NULL;
285
    apr_file_t *f = a->fd;
286
    apr_pool_t *curpool = apr_file_pool_get(f);
287
288
    if (apr_pool_is_ancestor(curpool, reqpool)) {
289
        return APR_SUCCESS;
290
    }
291
292
    if (!apr_pool_is_ancestor(a->readpool, reqpool)) {
293
        /* FIXME: Figure out what needs to be done here */
294
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
295
                "disk_cache: diskcache_bucket_setaside: FIXME1");
296
        a->readpool = reqpool;
297
    }
298
299
    /* FIXME: Figure out what needs to be done here */
300
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
301
            "disk_cache: diskcache_bucket_setaside: FIXME2");
302
303
    apr_file_setaside(&fd, f, reqpool);
304
    a->fd = fd;
305
    return APR_SUCCESS;
306
}
307
308
static const apr_bucket_type_t bucket_type_diskcache = {
309
    "DISKCACHE", 5, APR_BUCKET_DATA,
310
    diskcache_bucket_destroy,
311
    diskcache_bucket_read,
312
    diskcache_bucket_setaside,
313
    apr_bucket_shared_split,
314
    apr_bucket_shared_copy
315
};
316
317
/* From apr_brigade.c */
318
319
/* A "safe" maximum bucket size, 1Gb */
320
#define MAX_BUCKET_SIZE (0x40000000)
321
322
static apr_bucket * diskcache_brigade_insert(apr_bucket_brigade *bb,
323
                                                   apr_file_t *f, apr_off_t
324
                                                   start, apr_off_t length,
325
                                                   apr_interval_time_t timeout,
326
                                                   apr_pool_t *p)
327
{
328
    apr_bucket *e;
329
330
    if (length < MAX_BUCKET_SIZE) {
331
        e = diskcache_bucket_create(f, start, (apr_size_t)length, timeout, p, 
332
                bb->bucket_alloc);
333
    }
334
    else {
335
        /* Several buckets are needed. */        
336
        e = diskcache_bucket_create(f, start, MAX_BUCKET_SIZE, timeout, p, 
337
                bb->bucket_alloc);
338
339
        while (length > MAX_BUCKET_SIZE) {
340
            apr_bucket *ce;
341
            apr_bucket_copy(e, &ce);
342
            APR_BRIGADE_INSERT_TAIL(bb, ce);
343
            e->start += MAX_BUCKET_SIZE;
344
            length -= MAX_BUCKET_SIZE;
147
        }
345
        }
346
        e->length = (apr_size_t)length; /* Resize just the last bucket */
148
    }
347
    }
149
348
150
    return rv;
349
    APR_BRIGADE_INSERT_TAIL(bb, e);
350
    return e;
151
}
351
}
152
352
153
static apr_status_t file_cache_el_final(disk_cache_object_t *dobj,
353
/* --------------------------------------------------------------- */
154
                                        request_rec *r)
354
355
/*
356
 * Local static functions
357
 */
358
359
static char *cache_file(apr_pool_t *p, disk_cache_conf *conf,
360
                        const char *prefix, const char *name, 
361
                        const char *suffix)
155
{
362
{
156
    /* move the data over */
157
    if (dobj->tfd) {
158
        apr_status_t rv;
159
363
160
        apr_file_close(dobj->tfd);
364
    char *hashfile;
161
365
162
        /* This assumes that the tempfile is on the same file system
366
    hashfile = ap_cache_generate_name(p, conf->dirlevels,
163
         * as the cache_root. If not, then we need a file copy/move
367
                                                conf->dirlength, name);
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
368
171
        dobj->tfd = NULL;
369
    /* This assumes that we always deal with Vary-stuff if there's a prefix */
370
    if (prefix) {
371
        return apr_pstrcat(p, prefix, CACHE_VDIR_SUFFIX, "/",
372
                hashfile, suffix, NULL);
373
    }
374
    else {
375
        return apr_pstrcat(p, conf->cache_root, "/", hashfile, suffix, NULL);
172
    }
376
    }
173
174
    return APR_SUCCESS;
175
}
377
}
176
378
177
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, request_rec *r)
379
380
static apr_status_t mkdir_structure(const char *file, apr_pool_t *pool)
178
{
381
{
179
    /* Remove the header file and the body file. */
382
    apr_status_t rv;
180
    apr_file_remove(dobj->hdrsfile, r->pool);
383
    char *p;
181
    apr_file_remove(dobj->datafile, r->pool);
384
    int i;
182
385
183
    /* If we opened the temporary data file, close and remove it. */
386
    p = strrchr((char *)file, '/');
184
    if (dobj->tfd) {
387
    if(!p) {
185
        apr_file_close(dobj->tfd);
388
        return APR_EGENERAL;
186
        apr_file_remove(dobj->tempfile, r->pool);
187
        dobj->tfd = NULL;
188
    }
389
    }
189
390
190
    return APR_SUCCESS;
391
    *p = '\0';
191
}
392
393
    /* Be stubborn to overcome racyness when others deletes directories
394
       while we're trying to create them */
395
    for(i=0; i < 10; i++) {
396
        rv = apr_dir_make_recursive(file, 
397
                                    APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
398
        if(rv == APR_SUCCESS) {
399
            break;
400
        }
401
    }
402
    *p = '/';
192
403
404
    return rv;
405
}
193
406
194
/* These two functions get and put state information into the data
407
/* htcacheclean may remove directories underneath us.
195
 * file for an ap_cache_el, this state information will be read
408
 * So, we'll try renaming three times at a cost of 0.002 seconds.
196
 * and written transparent to clients of this module
197
 */
409
 */
198
static int file_cache_recall_mydata(apr_file_t *fd, cache_info *info,
410
static apr_status_t safe_file_rename(const char *src, const char *dest,
199
                                    disk_cache_object_t *dobj, request_rec *r)
411
                                     apr_pool_t *pool)
200
{
412
{
201
    apr_status_t rv;
413
    apr_status_t rv;
202
    char *urlbuff;
203
    disk_cache_info_t disk_info;
204
    apr_size_t len;
205
414
206
    /* read the data from the cache file */
415
    rv = apr_file_rename(src, dest, pool);
207
    len = sizeof(disk_cache_info_t);
416
208
    rv = apr_file_read_full(fd, &disk_info, len, &len);
209
    if (rv != APR_SUCCESS) {
417
    if (rv != APR_SUCCESS) {
210
        return rv;
418
        int i;
419
420
        for (i = 0; i < 2 && rv != APR_SUCCESS; i++) {
421
            mkdir_structure(dest, pool);
422
423
            rv = apr_file_rename(src, dest, pool);
424
425
            if(rv != APR_SUCCESS) {
426
                /* 1000 micro-seconds aka 0.001 seconds. */
427
                apr_sleep(1000);
428
            }
429
        }
211
    }
430
    }
212
431
213
    /* Store it away so we can get it later. */
432
    return rv;
214
    dobj->disk_info = disk_info;
433
}
215
434
216
    info->status = disk_info.status;
435
/* Close fd, remove file if it was opened for writing */
217
    info->date = disk_info.date;
436
static void close_and_rm(apr_file_t *fd, const char *file, request_rec *r)
218
    info->expire = disk_info.expire;
437
{
219
    info->request_time = disk_info.request_time;
438
    apr_int32_t flags = apr_file_flags_get(fd);
220
    info->response_time = disk_info.response_time;
439
221
440
    apr_file_close(fd);
222
    /* Note that we could optimize this by conditionally doing the palloc
441
    if(flags & APR_FOPEN_WRITE) {
223
     * depending upon the size. */
442
        apr_file_remove(file, r->pool);
224
    urlbuff = apr_palloc(r->pool, disk_info.name_len + 1);
443
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
225
    len = disk_info.name_len;
444
                     "disk_cache: close_and_rm: Removed %s",
226
    rv = apr_file_read_full(fd, urlbuff, len, &len);
445
                     file);
227
    if (rv != APR_SUCCESS) {
228
        return rv;
229
    }
446
    }
230
    urlbuff[disk_info.name_len] = '\0';
447
}
231
448
232
    /* check that we have the same URL */
449
233
    /* Would strncmp be correct? */
450
static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, 
234
    if (strcmp(urlbuff, dobj->name) != 0) {
451
                                            request_rec *r)
235
        return APR_EGENERAL;
452
{
453
    /* Only remove files that are opened for write when called, files
454
       opened only for reading must be explicitly removed */
455
    if(dobj->hfd) {
456
        close_and_rm(dobj->hfd, dobj->hdrsfile, r);
457
        dobj->hfd = NULL;
458
    }
459
    if(dobj->bfd) {
460
        close_and_rm(dobj->bfd, dobj->bodyfile, r);
461
        dobj->bfd = NULL;
462
    }
463
    if (dobj->tfd) {
464
        close_and_rm(dobj->tfd, dobj->tempfile, r);
465
        dobj->tfd = NULL;
236
    }
466
    }
237
467
238
    return APR_SUCCESS;
468
    return APR_SUCCESS;
239
}
469
}
240
470
471
241
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
472
static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
242
                             apr_array_header_t *varray, const char *oldkey)
473
                             apr_array_header_t *varray, const char *oldkey)
243
{
474
{
Lines 316-322 Link Here
316
/*
547
/*
317
 * Hook and mod_cache callback functions
548
 * Hook and mod_cache callback functions
318
 */
549
 */
319
static int create_entity(cache_handle_t *h, request_rec *r, const char *key, apr_off_t len)
550
static int create_entity(cache_handle_t *h, request_rec *r, const char *key, 
551
                         apr_off_t len)
320
{
552
{
321
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
553
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
322
                                                 &disk_cache_module);
554
                                                 &disk_cache_module);
Lines 327-332 Link Here
327
        return DECLINED;
559
        return DECLINED;
328
    }
560
    }
329
561
562
    /* Note, len is -1 if unknown so don't trust it too hard */
563
    if (len > conf->maxfs) {
564
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
565
                     "disk_cache: URL %s failed the size check "
566
                     "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
567
                     key, len, conf->maxfs);
568
        return DECLINED;
569
    }
570
    if (len >= 0 && len < conf->minfs) {
571
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
572
                     "disk_cache: URL %s failed the size check "
573
                     "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
574
                     key, len, conf->minfs);
575
        return DECLINED;
576
    }
577
330
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
578
    /* Allocate and initialize cache_object_t and disk_cache_object_t */
331
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
579
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
332
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
580
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
Lines 334-410 Link Here
334
    obj->key = apr_pstrdup(r->pool, key);
582
    obj->key = apr_pstrdup(r->pool, key);
335
583
336
    dobj->name = obj->key;
584
    dobj->name = obj->key;
337
    dobj->prefix = NULL;
338
    /* Save the cache root */
585
    /* Save the cache root */
339
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
586
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
340
    dobj->root_len = conf->cache_root_len;
587
    dobj->root_len = conf->cache_root_len;
341
    dobj->datafile = data_file(r->pool, conf, dobj, key);
588
    dobj->hdrsfile = cache_file(r->pool, conf, NULL, key, CACHE_HEADER_SUFFIX);
342
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
343
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
589
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
590
    dobj->initial_size = len;
591
    dobj->file_size = -1;
592
    dobj->lastmod = APR_DATE_BAD;
593
    dobj->updtimeout = conf->updtimeout;
594
    dobj->removedirs = conf->removedirs;
595
    dobj->header_only = r->header_only;
596
597
    if(r->filename != NULL && strlen(r->filename) > 0) {
598
        char buf[34];
599
        char *str;
600
601
        /* When possible, hash the body on dev:inode to minimize file
602
           duplication. */
603
        if( (r->finfo.valid & APR_FINFO_IDENT) == APR_FINFO_IDENT) {
604
            apr_uint64_t device = r->finfo.device; /* Avoid ifdef ... */
605
            apr_uint64_t inode  = r->finfo.inode;  /* ... type-mess */
606
607
            apr_snprintf(buf, sizeof(buf), "%016" APR_UINT64_T_HEX_FMT ":%016" 
608
                         APR_UINT64_T_HEX_FMT, device, inode);
609
            str = buf;
610
        }
611
        else {
612
            str = r->filename;
613
        }
614
        dobj->bodyfile = cache_file(r->pool, conf, NULL, str, 
615
                                    CACHE_BODY_SUFFIX);
616
        dobj->filename = r->filename;
617
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
618
                     "disk_cache: File %s was hashed using %s into %s",
619
                     r->filename, str, dobj->bodyfile);
620
    }
621
    else {
622
        dobj->bodyfile = cache_file(r->pool, conf, NULL, key, 
623
                                    CACHE_BODY_SUFFIX);
624
    }
344
625
345
    return OK;
626
    return OK;
346
}
627
}
347
628
348
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
629
630
static apr_status_t file_read_timeout(apr_file_t *file, char * buf,
631
                                      apr_size_t len, apr_time_t timeout)
349
{
632
{
350
    apr_uint32_t format;
633
    apr_size_t left, done;
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;
634
    apr_finfo_t finfo;
358
    cache_object_t *obj;
635
    apr_status_t rc;
359
    cache_info *info;
636
    apr_interval_time_t delay=0;
360
    disk_cache_object_t *dobj;
361
    int flags;
362
637
363
    h->cache_obj = NULL;
638
    done = 0;
639
    left = len;
364
640
365
    /* Look up entity keyed to 'url' */
641
    while(1) {
366
    if (conf->cache_root == NULL) {
642
        rc = apr_file_read_full(file, buf+done, left, &len);
367
        if (!error_logged) {
643
        if (rc == APR_SUCCESS) {
368
            error_logged = 1;
644
           break;
369
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
370
                         "disk_cache: Cannot cache files to disk without a CacheRoot specified.");
371
        }
645
        }
372
        return DECLINED;
646
        done += len;
373
    }
647
        left -= len;
374
648
375
    /* Create and init the cache object */
649
        if(!APR_STATUS_IS_EOF(rc)) {
376
    h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
650
            return rc;
377
    obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
651
        }
652
        rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, file);
653
        if(rc != APR_SUCCESS) {
654
           return rc;
655
        }
656
        if(finfo.mtime < (apr_time_now() - timeout) ) {
657
            return APR_ETIMEDOUT;
658
        }
659
        cache_loop_sleep(&delay);
660
        CACHE_LOOP_INCTIME(delay);
661
    }
378
662
379
    info = &(obj->info);
663
    return APR_SUCCESS;
664
}
380
665
381
    /* Open the headers file */
382
    dobj->prefix = NULL;
383
666
384
    /* Save the cache root */
667
static apr_status_t open_header(cache_handle_t *h, request_rec *r, 
385
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
668
                                const char *key, disk_cache_conf *conf)
386
    dobj->root_len = conf->cache_root_len;
669
{
670
    int flags = APR_READ | APR_WRITE | APR_BINARY;
671
    disk_cache_format_t format;
672
    apr_status_t rc;
673
    const char *nkey = key;
674
    disk_cache_info_t disk_info;
675
    cache_object_t *obj = h->cache_obj;
676
    disk_cache_object_t *dobj = obj->vobj;
387
677
388
    dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
678
    /* Open header read/write so it's easy to rewrite it when needed */
389
    flags = APR_READ|APR_BINARY|APR_BUFFERED;
390
    rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
679
    rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
391
    if (rc != APR_SUCCESS) {
680
    if (rc != APR_SUCCESS) {
392
        return DECLINED;
681
        return CACHE_EDECLINED;
393
    }
682
    }
394
683
395
    /* read the format from the cache file */
684
    /* read the format from the cache file */
396
    len = sizeof(format);
685
    rc = apr_file_read_full(dobj->hfd, &format, sizeof(format), NULL);
397
    apr_file_read_full(dobj->hfd, &format, len, &len);
686
    if(APR_STATUS_IS_EOF(rc)) {
687
        return CACHE_ENODATA;
688
    }
689
    else if(rc != APR_SUCCESS) {
690
        return rc;
691
    }
398
692
693
    /* Vary-files are being written to tmpfile and moved in place, so
694
       the should always be complete */
399
    if (format == VARY_FORMAT_VERSION) {
695
    if (format == VARY_FORMAT_VERSION) {
400
        apr_array_header_t* varray;
696
        apr_array_header_t* varray;
401
        apr_time_t expire;
697
        apr_time_t expire;
698
        char *p;
402
699
403
        len = sizeof(expire);
700
        rc = apr_file_read_full(dobj->hfd, &expire, sizeof(expire), NULL);
404
        apr_file_read_full(dobj->hfd, &expire, len, &len);
701
        if(rc != APR_SUCCESS) {
702
            return rc;
703
        }
405
704
406
        if (expire < r->request_time) {
705
        if (expire < r->request_time) {
407
            return DECLINED;
706
            return CACHE_EDECLINED;
408
        }
707
        }
409
708
410
        varray = apr_array_make(r->pool, 5, sizeof(char*));
709
        varray = apr_array_make(r->pool, 5, sizeof(char*));
Lines 413-504 Link Here
413
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
712
            ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
414
                         "disk_cache: Cannot parse vary header file: %s",
713
                         "disk_cache: Cannot parse vary header file: %s",
415
                         dobj->hdrsfile);
714
                         dobj->hdrsfile);
416
            return DECLINED;
715
            return CACHE_EDECLINED;
417
        }
716
        }
418
        apr_file_close(dobj->hfd);
717
        apr_file_close(dobj->hfd);
419
718
420
        nkey = regen_key(r->pool, r->headers_in, varray, key);
719
        nkey = regen_key(r->pool, r->headers_in, varray, key);
421
720
422
        dobj->hashfile = NULL;
423
        dobj->prefix = dobj->hdrsfile;
721
        dobj->prefix = dobj->hdrsfile;
424
        dobj->hdrsfile = header_file(r->pool, conf, dobj, nkey);
722
        p = strrchr((char *)dobj->prefix, '.');
723
        if(p) {
724
            /* Cut away the suffix */
725
            *p = '\0';
726
        }
727
        dobj->hdrsfile = cache_file(r->pool, conf, dobj->prefix, nkey,
728
                                    CACHE_HEADER_SUFFIX);
425
729
426
        flags = APR_READ|APR_BINARY|APR_BUFFERED;
427
        rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
730
        rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
428
        if (rc != APR_SUCCESS) {
731
        if (rc != APR_SUCCESS) {
429
            return DECLINED;
732
            dobj->hfd = NULL;
733
            return CACHE_EDECLINED;
734
        }
735
        rc = apr_file_read_full(dobj->hfd, &format, sizeof(format), NULL);
736
        if(APR_STATUS_IS_EOF(rc)) {
737
            return CACHE_ENODATA;
738
        }
739
        else if(rc != APR_SUCCESS) {
740
            return rc;
430
        }
741
        }
431
    }
742
    }
432
    else if (format != DISK_FORMAT_VERSION) {
743
433
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
744
    if(format != DISK_FORMAT_VERSION) {
434
                     "cache_disk: File '%s' has a version mismatch. File had version: %d.",
745
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
435
                     dobj->hdrsfile, format);
746
                     "disk_cache: File '%s' had a version mismatch. File had "
436
        return DECLINED;
747
                     "version: %d (current is %d). Deleted.", dobj->hdrsfile,
437
    }
748
                     format, DISK_FORMAT_VERSION);
438
    else {
749
        file_cache_errorcleanup(dobj, r);
439
        apr_off_t offset = 0;
750
        apr_file_remove(dobj->hdrsfile, r->pool);
440
        /* This wasn't a Vary Format file, so we must seek to the
751
        return CACHE_EDECLINED;
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
    }
752
    }
446
753
447
    obj->key = nkey;
754
    obj->key = nkey;
448
    dobj->key = nkey;
449
    dobj->name = key;
755
    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
756
453
    /* Open the data file */
757
    /* read the data from the header file */
454
    flags = APR_READ|APR_BINARY;
758
    rc = apr_file_read_full(dobj->hfd, &disk_info, sizeof(disk_info), NULL);
455
#ifdef APR_SENDFILE_ENABLED
759
    if(APR_STATUS_IS_EOF(rc)) {
456
    flags |= APR_SENDFILE_ENABLED;
760
        return CACHE_ENODATA;
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
    }
761
    }
463
762
    else if(rc != APR_SUCCESS) {
464
    rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, dobj->fd);
763
        return rc;
465
    if (rc == APR_SUCCESS) {
466
        dobj->file_size = finfo.size;
467
    }
764
    }
468
765
469
    /* Read the bytes to setup the cache_info fields */
766
    /* Store it away so we can get it later. */
470
    rc = file_cache_recall_mydata(dobj->hfd, info, dobj, r);
767
    dobj->disk_info = disk_info;
471
    if (rc != APR_SUCCESS) {
472
        /* XXX log message */
473
        return DECLINED;
474
    }
475
768
476
    /* Initialize the cache_handle callback functions */
769
    return APR_SUCCESS;
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
}
770
}
481
771
482
static int remove_entity(cache_handle_t *h)
772
773
static apr_status_t open_header_timeout(cache_handle_t *h, request_rec *r, 
774
                                const char *key, disk_cache_conf *conf)
483
{
775
{
484
    /* Null out the cache object pointer so next time we start from scratch  */
776
    apr_status_t rc;
485
    h->cache_obj = NULL;
777
    apr_finfo_t finfo;
486
    return OK;
778
    apr_interval_time_t delay = 0;
779
    cache_object_t *obj = h->cache_obj;
780
    disk_cache_object_t *dobj = obj->vobj;
781
782
    while(1) {
783
        if(dobj->hfd) {
784
            apr_file_close(dobj->hfd);
785
            dobj->hfd = NULL;
786
        }
787
        rc = open_header(h, r, key, conf);
788
        if(rc != APR_SUCCESS && rc != CACHE_ENODATA) {
789
            if(rc != CACHE_EDECLINED) {
790
                ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
791
                             "disk_cache: Cannot load header file: %s",
792
                             dobj->hdrsfile);
793
            }
794
            return rc;
795
        }
796
797
        /* Objects with unknown body size will have file_size == -1 until the
798
           entire body is written and the header updated with the actual size.
799
           And since we depend on knowing the body size we wait until the size
800
           is written */
801
        if(rc == APR_SUCCESS && dobj->disk_info.file_size >= 0) {
802
            break;
803
        }
804
        rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->hfd);
805
        if(rc != APR_SUCCESS) {
806
            return rc;
807
        }
808
        if(finfo.mtime < (apr_time_now() - dobj->updtimeout)) {
809
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
810
                         "disk_cache: Timed out waiting for header file %s for "
811
                         "URL %s - caching the body failed?", 
812
                         dobj->hdrsfile, key);
813
            return CACHE_EDECLINED;
814
        }
815
        cache_loop_sleep(&delay);
816
        CACHE_LOOP_INCTIME(delay);
817
    }
818
819
    return APR_SUCCESS;
487
}
820
}
488
821
489
static int remove_url(cache_handle_t *h, apr_pool_t *p)
822
823
static apr_status_t load_header_strings(request_rec *r,
824
                                        disk_cache_object_t *dobj)
490
{
825
{
826
    apr_size_t len;
491
    apr_status_t rc;
827
    apr_status_t rc;
492
    disk_cache_object_t *dobj;
828
    char *urlbuff;
493
829
494
    /* Get disk cache object from cache handle */
830
    if(dobj->disk_info.name_len > MAX_STRING_LEN ||
495
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
831
            dobj->disk_info.bodyname_len > MAX_STRING_LEN ||
496
    if (!dobj) {
832
            dobj->disk_info.filename_len > MAX_STRING_LEN) 
497
        return DECLINED;
833
    {
834
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
835
                "disk_cache: Corrupt cache header for URL %s, deleting: %s",
836
                dobj->name, dobj->hdrsfile);
837
        file_cache_errorcleanup(dobj, r);
838
        apr_file_remove(dobj->hdrsfile, r->pool);
839
        return CACHE_EDECLINED;
498
    }
840
    }
499
841
500
    /* Delete headers file */
842
    /* FIXME: Enforce that url and bodyname is present */
501
    if (dobj->hdrsfile) {
843
844
845
    len = dobj->disk_info.name_len;
846
    urlbuff = apr_palloc(r->pool, len+1);
847
    if(urlbuff == NULL) {
848
        return APR_ENOMEM;
849
    }
850
851
    rc = file_read_timeout(dobj->hfd, urlbuff, len, dobj->updtimeout);
852
    if (rc == APR_ETIMEDOUT) {
853
        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
854
                     "disk_cache: Timed out waiting for urlbuff for "
855
                     "URL %s - caching failed?",  dobj->name);
856
        return CACHE_EDECLINED;
857
    }
858
    else if(rc != APR_SUCCESS) {
859
        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
860
                     "disk_cache: Error reading urlbuff for URL %s",
861
                     dobj->name);
862
        return CACHE_EDECLINED;
863
    }
864
    urlbuff[len] = '\0';
865
866
    /* check that we have the same URL */
867
    if (strcmp(urlbuff, dobj->name) != 0) {
868
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
869
                     "disk_cache: Cached URL %s didn't match requested "
870
                     "URL %s", urlbuff, dobj->name);
871
        file_cache_errorcleanup(dobj, r);
872
        apr_file_remove(dobj->hdrsfile, r->pool);
873
        apr_file_remove(dobj->bodyfile, r->pool);
874
        return CACHE_EDECLINED;
875
    }
876
877
    /* Read in the file the body is stored in */
878
    len = dobj->disk_info.bodyname_len;
879
    if(len > 0) {
880
        char *bodyfile = apr_palloc(r->pool, len+1);
881
882
        if(bodyfile == NULL) {
883
            return APR_ENOMEM;
884
        }
885
886
        rc = file_read_timeout(dobj->hfd, bodyfile, len, dobj->updtimeout);
887
        if (rc == APR_ETIMEDOUT) {
888
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
889
                         "disk_cache: Timed out waiting for body cache "
890
                         "filename for URL %s - caching failed?", dobj->name);
891
            return CACHE_EDECLINED;
892
        }
893
        else if(rc != APR_SUCCESS) {
894
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
895
                         "disk_cache: Error reading body cache filename for "
896
                         "URL %s", dobj->name);
897
            return CACHE_EDECLINED;
898
        }
899
        bodyfile[len] = '\0';
900
        dobj->bodyfile = apr_pstrcat(r->pool, dobj->root, "/", bodyfile, NULL);
901
    }
902
903
    /* Read in the filename */
904
    len = dobj->disk_info.filename_len;
905
    if(len > 0) {
906
        char *fnamebuf = apr_palloc(r->pool, len+1);
907
908
        if(fnamebuf == NULL) {
909
            return APR_ENOMEM;
910
        }
911
912
        rc = file_read_timeout(dobj->hfd, fnamebuf, len, dobj->updtimeout);
913
        if (rc == APR_ETIMEDOUT) {
914
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
915
                         "disk_cache: Timed out waiting for filename for "
916
                         "URL %s - caching failed?", dobj->name);
917
            return CACHE_EDECLINED;
918
        }
919
        else if(rc != APR_SUCCESS) {
920
            ap_log_error(APLOG_MARK, APLOG_WARNING, rc, r->server,
921
                         "disk_cache: Error reading filename for URL %s",
922
                         dobj->name);
923
            return CACHE_EDECLINED;
924
        }
925
        fnamebuf[len] = '\0';
926
927
        dobj->filename = fnamebuf;
928
        /* We can't set r->filename here because for example mod_rewrite
929
           will exhibit different behaviour compared to a completely
930
           uncached entity (will happen if entity is revalidated for 
931
           example). */
932
        /* Save a pointer to r->filename so we can set it later on in
933
           recall_body which doesn't get r as an argument */
934
        dobj->rfilename = &(r->filename);
935
    }
936
937
    return APR_SUCCESS;
938
}
939
940
941
static apr_status_t open_body_timeout(request_rec *r, cache_object_t *cache_obj)
942
{
943
    apr_status_t rc;
944
    apr_finfo_t finfo;
945
    int flags = APR_READ|APR_BINARY;
946
    apr_interval_time_t delay = 0;
947
    disk_cache_object_t *dobj = (disk_cache_object_t *) cache_obj->vobj;
948
    cache_info *info = &(cache_obj->info);
949
    
950
#if APR_HAS_SENDFILE
951
    core_dir_config *pdconf = ap_get_module_config(r->per_dir_config,
952
                                                   &core_module);
953
    flags |= ((pdconf->enable_sendfile == ENABLE_SENDFILE_OFF)
954
             ? 0 : APR_SENDFILE_ENABLED);
955
#endif  
956
957
    if(dobj->bodyfile == NULL || strlen(dobj->bodyfile) == 0) {
958
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
959
                     "disk_cache: open_body_timeout called with NULL "
960
                     "bodyfile for URL %s",
961
                     dobj->name);
962
        return APR_EGENERAL;
963
    }
964
965
    /* Wait here until we get a body cachefile, data in it, and do quick sanity
966
     * check */
967
968
    while(1) {
969
        if(dobj->bfd == NULL) {
970
            rc = apr_file_open(&dobj->bfd, dobj->bodyfile, flags, 0, r->pool);
971
            if(rc != APR_SUCCESS) {
972
                if(info->response_time < (apr_time_now() - dobj->updtimeout) ) {
973
                    /* This usually means that the body simply wasn't cached,
974
                       due to HEAD requests for example */
975
                    ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, r->server,
976
                                 "disk_cache: Timed out waiting for bodyfile "
977
                                 "%s for URL %s - caching failed?", 
978
                                 dobj->bodyfile, dobj->name);
979
                    return CACHE_EDECLINED;
980
                }
981
                cache_loop_sleep(&delay);
982
                CACHE_LOOP_INCTIME(delay);
983
                continue;
984
            }
985
        }
986
987
        rc = apr_file_info_get(&finfo, APR_FINFO_SIZE | APR_FINFO_CSIZE | 
988
                                        APR_FINFO_MTIME | APR_FINFO_NLINK, 
989
                               dobj->bfd);
990
        if(rc != APR_SUCCESS && !APR_STATUS_IS_INCOMPLETE(rc)) {
991
            return rc;
992
        }
993
        if(finfo.valid & APR_FINFO_NLINK && finfo.nlink == 0) {
994
            /* This file has been deleted, close it and try again */
995
            apr_file_close(dobj->bfd);
996
            dobj->bfd = NULL;
997
            continue;
998
        }
999
1000
        /* Argh!  Why have APR_FINFO_CSIZE when it's not even
1001
           implemented on Unix, which is the simple platform to do it on?
1002
         */
1003
        if(finfo.valid & APR_FINFO_CSIZE) {
1004
            /* Assume that files consuming less storage than size are broken.
1005
               It really shouldn't happen, but for example XFS on Linux can
1006
               leave such files after a system crash. We simply use the smaller
1007
               of the sizes and let error handling do the rest. */
1008
            dobj->file_size = MIN(finfo.size, finfo.csize);
1009
        }
1010
        else {
1011
            dobj->file_size = finfo.size;
1012
        }
1013
1014
        /* Note that the body might have been updated by another entity
1015
           that uses the same body, which usually means that we should
1016
           revalidate too. Don't freak out completely when this happens.
1017
           We might have:
1018
           - Body in sync with this header.
1019
           - Body being cached.
1020
           - Body that failed caching.
1021
           - Body newer than this header. 
1022
         */
1023
1024
        if(dobj->initial_size < dobj->file_size) {
1025
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1026
                         "disk_cache: Cached body for too large for URL %s"
1027
                         " - revalidating.", dobj->name);
1028
            apr_file_remove(dobj->hdrsfile, r->pool);
1029
            return CACHE_EDECLINED;
1030
        }
1031
        else if(dobj->initial_size > dobj->file_size) {
1032
            /* Still caching or failed? */
1033
            if(finfo.mtime < (apr_time_now() - dobj->updtimeout) ) {
1034
                ap_log_error(APLOG_MARK, APLOG_INFO, rc, r->server,
1035
                             "disk_cache: Cached body too small for URL %s"
1036
                             " - revalidating.", dobj->name);
1037
                apr_file_remove(dobj->hdrsfile, r->pool);
1038
                return CACHE_EDECLINED;
1039
            }
1040
        }
1041
        else {
1042
            /* If right size, file has either the correct mtime or an mtime
1043
               within the update timeout (in the window between finished
1044
               copying and setting mtime) 
1045
             */
1046
            if(dobj->lastmod != APR_DATE_BAD &&
1047
                    finfo.mtime != dobj->lastmod &&
1048
                    finfo.mtime < (apr_time_now() - dobj->updtimeout) ) 
1049
            {
1050
                ap_log_error(APLOG_MARK, APLOG_INFO, rc, r->server,
1051
                             "disk_cache: Cached body Last-Modified mismatch "
1052
                             "for URL %s - revalidating.", dobj->name);
1053
                apr_file_remove(dobj->hdrsfile, r->pool);
1054
                return CACHE_EDECLINED;
1055
            }
1056
        }
1057
1058
        if(dobj->file_size > 0) {
1059
            break;
1060
        }
1061
        cache_loop_sleep(&delay);
1062
        CACHE_LOOP_INCTIME(delay);
1063
    }
1064
1065
    return APR_SUCCESS;
1066
}
1067
1068
1069
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
1070
{
1071
    apr_status_t rc;
1072
    disk_cache_object_t *dobj;
1073
    cache_info *info;
1074
    static int error_logged = 0;
1075
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1076
                                                 &disk_cache_module);
1077
1078
    h->cache_obj = NULL;
1079
1080
    /* Look up entity keyed to 'url' */
1081
    if (conf->cache_root == NULL) {
1082
        if (!error_logged) {
1083
            error_logged = 1;
1084
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1085
                         "disk_cache: Cannot cache files to disk without a "
1086
                         "CacheRoot specified.");
1087
        }
1088
        return DECLINED;
1089
    }
1090
1091
    /* Create and init the cache object */
1092
    h->cache_obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
1093
    h->cache_obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
1094
    info = &(h->cache_obj->info);
1095
1096
    /* Save the cache root */
1097
    dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
1098
    dobj->root_len = conf->cache_root_len;
1099
1100
    dobj->hdrsfile = cache_file(r->pool, conf, NULL, key, CACHE_HEADER_SUFFIX);
1101
1102
    dobj->updtimeout = conf->updtimeout;
1103
    dobj->removedirs = conf->removedirs;
1104
    dobj->header_only = r->header_only;
1105
1106
    /* Open header and read basic info, wait until header contains
1107
       valid size information for the body */
1108
    rc = open_header_timeout(h, r, key, conf);
1109
    if(rc != APR_SUCCESS) {
1110
        if(dobj->hfd != NULL) {
1111
            apr_file_close(dobj->hfd);
1112
            dobj->hfd = NULL;
1113
        }
1114
        return DECLINED;
1115
    }
1116
1117
    info->status = dobj->disk_info.status;
1118
    info->date = dobj->disk_info.date;
1119
    info->expire = dobj->disk_info.expire;
1120
    info->request_time = dobj->disk_info.request_time;
1121
    info->response_time = dobj->disk_info.response_time;
1122
1123
    dobj->lastmod = dobj->disk_info.lastmod;
1124
    dobj->initial_size = (apr_off_t) dobj->disk_info.file_size;
1125
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1126
1127
    /* Load and check strings (URL, bodyfile, filename) */
1128
    rc = load_header_strings(r, dobj);
1129
    if(rc != APR_SUCCESS) {
1130
        if(dobj->hfd != NULL) {
1131
            apr_file_close(dobj->hfd);
1132
            dobj->hfd = NULL;
1133
        }
1134
        return DECLINED;
1135
    }
1136
1137
    /* Only need body cachefile if we have a body and this isn't a HEAD
1138
       request */
1139
    if(dobj->initial_size > 0 && !dobj->header_only) {
1140
        rc = open_body_timeout(r, h->cache_obj);
1141
        if(rc != APR_SUCCESS) {
1142
            if(dobj->hfd != NULL) {
1143
                apr_file_close(dobj->hfd);
1144
                dobj->hfd = NULL;
1145
            }
1146
            if(dobj->bfd != NULL) {
1147
                apr_file_close(dobj->bfd);
1148
                dobj->bfd = NULL;
1149
            }
1150
            return DECLINED;
1151
        }
1152
    }
1153
    else {
1154
        dobj->file_size = 0;
1155
    }
1156
1157
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1158
                 "disk_cache: Recalled status for cached URL %s from file %s",
1159
                 dobj->name, dobj->hdrsfile);
1160
    return OK;
1161
}
1162
1163
1164
static int remove_entity(cache_handle_t *h)
1165
{
1166
    disk_cache_object_t *dobj;
1167
    apr_finfo_t finfo;
1168
    apr_pool_t *p;
1169
    apr_status_t rv;
1170
1171
    /* Get disk cache object from cache handle */
1172
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1173
1174
    /* Null out the cache object pointer so next time we start from scratch */
1175
    h->cache_obj = NULL;
1176
1177
    if(!dobj) {
1178
        return OK;
1179
    }
1180
1181
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1182
            "disk_cache: remove_entity: %s", dobj->name);
1183
1184
    /* We really want to remove the cache files  here since mod_cache has
1185
       deemed it stale, but it seems like an API miss that we don't
1186
       have a pool? And why is this function separate from remove_url?
1187
       Oh well, beware of kludge ;) */
1188
1189
    if(dobj->hfd != NULL) {
1190
        /* Only remove file if fd isn't already unlinked. Not atomic, but
1191
           the best we can do? */
1192
        rv = apr_file_info_get(&finfo, APR_FINFO_NLINK, dobj->hfd);
1193
        if(rv == APR_SUCCESS && finfo.nlink != 0) {
1194
            p = apr_file_pool_get(dobj->hfd);
1195
            apr_file_remove(dobj->hdrsfile, p);
1196
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1197
                         "disk_cache: remove_entity: Deleted %s from cache.",
1198
                         dobj->hdrsfile);
1199
        }
1200
        apr_file_close(dobj->hfd);
1201
        dobj->hfd = NULL;
1202
    }
1203
    if(dobj->bfd != NULL) {
1204
        /* Only remove file if fd isn't already unlinked. Not atomic, but
1205
           the best we can do? */
1206
        rv = apr_file_info_get(&finfo, APR_FINFO_NLINK, dobj->bfd);
1207
        if(rv == APR_SUCCESS && finfo.nlink != 0) {
1208
            p = apr_file_pool_get(dobj->bfd);
1209
            apr_file_remove(dobj->bodyfile, p);
1210
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1211
                    "disk_cache: remove_entity: Deleted %s from cache.",
1212
                    dobj->bodyfile);
1213
        }
1214
        apr_file_close(dobj->bfd);
1215
        dobj->bfd = NULL;
1216
    }
1217
1218
    return OK;
1219
}
1220
1221
1222
/* FIXME: It would make sense to have the errorcleanup and this function
1223
   to be the same */
1224
static int remove_url(cache_handle_t *h, apr_pool_t *p)
1225
{
1226
    apr_status_t rc;
1227
    disk_cache_object_t *dobj;
1228
1229
    /* Get disk cache object from cache handle */
1230
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1231
    if (!dobj) {
1232
        return DECLINED;
1233
    }
1234
1235
    /* Delete headers file */
1236
    if (dobj->hdrsfile) {
502
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1237
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
503
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
1238
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
504
1239
Lines 508-541 Link Here
508
             * For reason see log_error_core for the case s == NULL.
1243
             * For reason see log_error_core for the case s == NULL.
509
             */
1244
             */
510
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
1245
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
511
                   "disk_cache: Failed to delete headers file %s from cache.",
1246
                         "disk_cache: Failed to delete headers file %s "
512
                         dobj->hdrsfile);
1247
                         "from cache.", dobj->hdrsfile);
513
            return DECLINED;
1248
            return DECLINED;
514
        }
1249
        }
515
    }
1250
    }
516
1251
517
     /* Delete data file */
1252
    /* Only delete body cache file if it isn't backed by a real file */
518
    if (dobj->datafile) {
1253
    if(!dobj->filename && dobj->bodyfile) {
519
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1254
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
520
                     "disk_cache: Deleting %s from cache.", dobj->datafile);
1255
                     "disk_cache: Deleting %s from cache.", dobj->bodyfile);
521
1256
522
        rc = apr_file_remove(dobj->datafile, p);
1257
        rc = apr_file_remove(dobj->bodyfile, p);
523
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
1258
        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,
1259
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
528
                      "disk_cache: Failed to delete data file %s from cache.",
1260
                         "disk_cache: Failed to delete body file %s "
529
                         dobj->datafile);
1261
                         "from cache.", dobj->bodyfile);
530
            return DECLINED;
1262
            return DECLINED;
531
        }
1263
        }
532
    }
1264
    }
533
1265
1266
    if(!dobj->removedirs) {
1267
        return OK;
1268
    }
1269
534
    /* now delete directories as far as possible up to our cache root */
1270
    /* now delete directories as far as possible up to our cache root */
535
    if (dobj->root) {
1271
    if (dobj->root) {
536
        const char *str_to_copy;
1272
        const char *str_to_copy;
537
1273
538
        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->datafile;
1274
        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->bodyfile;
539
        if (str_to_copy) {
1275
        if (str_to_copy) {
540
            char *dir, *slash, *q;
1276
            char *dir, *slash, *q;
541
1277
Lines 550-559 Link Here
550
             * in the way as far as possible
1286
             * in the way as far as possible
551
             *
1287
             *
552
             * Note: due to the way we constructed the file names in
1288
             * Note: due to the way we constructed the file names in
553
             * header_file and data_file, we are guaranteed that the
1289
             * cache_file, we are guaranteed that the cache_root is suffixed by
554
             * cache_root is suffixed by at least one '/' which will be
1290
             * at least one '/' which will be turned into a terminating null by
555
             * turned into a terminating null by this loop.  Therefore,
1291
             * this loop.  Therefore, we won't either delete or go above our
556
             * we won't either delete or go above our cache root.
1292
             * cache root.
557
             */
1293
             */
558
            for (q = dir + dobj->root_len; *q ; ) {
1294
            for (q = dir + dobj->root_len; *q ; ) {
559
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1295
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
Lines 584-590 Link Here
584
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
1320
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
585
        if (rv != APR_SUCCESS) {
1321
        if (rv != APR_SUCCESS) {
586
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1322
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
587
                          "Premature end of vary array.");
1323
                          "disk_cache: Premature end of vary array.");
588
            return rv;
1324
            return rv;
589
        }
1325
        }
590
1326
Lines 639-726 Link Here
639
                         &amt);
1375
                         &amt);
640
}
1376
}
641
1377
642
static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
1378
/* Load table stored by store_table */
1379
static apr_status_t read_table(request_rec *r,
643
                               apr_table_t *table, apr_file_t *file)
1380
                               apr_table_t *table, apr_file_t *file)
644
{
1381
{
645
    char w[MAX_STRING_LEN];
1382
    char *s, *k, *v;
646
    char *l;
1383
    apr_uint32_t totsize = 0;
647
    int p;
648
    apr_status_t rv;
1384
    apr_status_t rv;
649
1385
650
    while (1) {
1386
    rv = apr_file_read_full(file, &totsize, sizeof(totsize), NULL);
651
1387
    if(rv != APR_SUCCESS) {
652
        /* ### What about APR_EOF? */
1388
        return rv;
653
        rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
1389
    }
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
1390
662
        p = strlen(w);
1391
    s = apr_palloc(r->pool, totsize);
663
        /* Indeed, the host's '\n':
1392
    if(s == NULL) {
664
           '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
1393
        return APR_ENOMEM;
665
           -- whatever the script generates.
1394
    }
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
1395
676
        /* If we've finished reading the headers, break out of the loop. */
1396
    rv = apr_file_read_full(file, s, totsize, NULL);
677
        if (w[0] == '\0') {
1397
    if(rv != APR_SUCCESS) {
678
            break;
1398
        return rv;
679
        }
1399
    }
680
1400
681
#if APR_CHARSET_EBCDIC
1401
    k=s;
682
        /* Chances are that we received an ASCII header text instead of
1402
    while(k < s + totsize) {
683
         * the expected EBCDIC header lines. Try to auto-detect:
1403
        /* FIXME: Do a pointer-loop instead of strlen to make sure we don't
1404
                  walk outside of allocated memory if on-disk data has been
1405
                  corrupted
684
         */
1406
         */
685
        if (!(l = strchr(w, ':'))) {
1407
        v = k + strlen(k) + 1;
686
            int maybeASCII = 0, maybeEBCDIC = 0;
1408
        apr_table_addn(table, k, v);
687
            unsigned char *cp, native;
1409
        k = v + strlen(v) + 1;
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
    }
1410
    }
720
1411
721
    return APR_SUCCESS;
1412
    return APR_SUCCESS;
722
}
1413
}
723
1414
1415
724
/*
1416
/*
725
 * Reads headers from a buffer and returns an array of headers.
1417
 * Reads headers from a buffer and returns an array of headers.
726
 * Returns NULL on file error
1418
 * Returns NULL on file error
Lines 731-751 Link Here
731
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
1423
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
732
{
1424
{
733
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1425
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
1426
    apr_status_t rv;
1427
    apr_off_t off;
1428
    apr_finfo_t finfo;
1429
    apr_interval_time_t delay = 0;
734
1430
735
    /* This case should not happen... */
1431
    /* This case should not happen... */
736
    if (!dobj->hfd) {
1432
    if (!dobj->hfd) {
737
        /* XXX log message */
1433
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1434
                     "disk_cache: recall_headers called without fd for URL %s",
1435
                     dobj->name);
738
        return APR_NOTFOUND;
1436
        return APR_NOTFOUND;
739
    }
1437
    }
740
1438
741
    h->req_hdrs = apr_table_make(r->pool, 20);
1439
    off = 0;
1440
    rv = apr_file_seek(dobj->hfd, APR_CUR, &off);
1441
    if(rv != APR_SUCCESS) {
1442
        return rv;
1443
    }
1444
742
    h->resp_hdrs = apr_table_make(r->pool, 20);
1445
    h->resp_hdrs = apr_table_make(r->pool, 20);
1446
    h->req_hdrs = apr_table_make(r->pool, 20);
743
1447
744
    /* Call routine to read the header lines/status line */
1448
    while(1) {
745
    read_table(h, r, h->resp_hdrs, dobj->hfd);
1449
        rv = read_table(r, h->resp_hdrs, dobj->hfd);
746
    read_table(h, r, h->req_hdrs, dobj->hfd);
1450
        if(rv != APR_SUCCESS) {
1451
            apr_table_clear(h->resp_hdrs);
1452
        }
1453
        else {
1454
            rv = read_table(r, h->req_hdrs, dobj->hfd);
1455
            if(rv != APR_SUCCESS) {
1456
                apr_table_clear(h->req_hdrs);
1457
            }
1458
        }
1459
        if(rv == APR_SUCCESS) {
1460
            break;
1461
        }
1462
        if(!APR_STATUS_IS_EOF(rv)) {
1463
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
1464
                          "disk_cache: Error reading cache headers "
1465
                          "URL %s", dobj->name);
1466
            if(dobj->hfd != NULL) {
1467
                apr_file_close(dobj->hfd);
1468
                dobj->hfd = NULL;
1469
            }
1470
            if(dobj->bfd != NULL) {
1471
                apr_file_close(dobj->bfd);
1472
                dobj->bfd = NULL;
1473
            }
1474
            return rv;
1475
        }
747
1476
748
    apr_file_close(dobj->hfd);
1477
        /* FIXME: Check if header file deleted (nlinks==0) and reopen it if
1478
         * that's the case */
1479
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->hfd);
1480
        if(rv != APR_SUCCESS ||
1481
                finfo.mtime < (apr_time_now() - dobj->updtimeout) ) 
1482
        {
1483
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1484
                          "disk_cache: Timed out waiting for cache headers "
1485
                          "URL %s", dobj->name);
1486
            if(dobj->hfd != NULL) {
1487
                apr_file_close(dobj->hfd);
1488
                dobj->hfd = NULL;
1489
            }
1490
            if(dobj->bfd != NULL) {
1491
                apr_file_close(dobj->bfd);
1492
                dobj->bfd = NULL;
1493
            }
1494
            return APR_EGENERAL;
1495
        }
1496
        rv = apr_file_seek(dobj->hfd, APR_SET, &off);
1497
        if(rv != APR_SUCCESS) {
1498
            return rv;
1499
        }
1500
        cache_loop_sleep(&delay);
1501
        CACHE_LOOP_INCTIME(delay);
1502
    }
749
1503
750
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1504
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
751
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
1505
                 "disk_cache: Recalled headers for URL %s",  dobj->name);
Lines 757-899 Link Here
757
    apr_bucket *e;
1511
    apr_bucket *e;
758
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1512
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
759
1513
760
    e = apr_bucket_file_create(dobj->fd, 0, (apr_size_t) dobj->file_size, p,
1514
    if(dobj->hfd != NULL) {
761
                               bb->bucket_alloc);
1515
        /* Close header cache file, it won't be needed anymore */
762
    APR_BRIGADE_INSERT_HEAD(bb, e);
1516
        apr_file_close(dobj->hfd);
1517
        dobj->hfd = NULL;
1518
    }
1519
1520
    if(dobj->initial_size > 0 && !dobj->header_only && dobj->bfd == NULL) {
1521
        /* This should never happen, really... */
1522
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
1523
                     "disk_cache: recall_body: Called but no fd open, URL %s "
1524
                     "from file %s", dobj->name, dobj->bodyfile);
1525
        return APR_EGENERAL;
1526
    }
1527
1528
    /* Restore r->filename if not present */
1529
    if(dobj->filename != NULL && dobj->rfilename != NULL && 
1530
            *(dobj->rfilename) == NULL) 
1531
    {
1532
        *(dobj->rfilename) = dobj->filename;
1533
    }
1534
1535
    /* Insert as much as possible as regular file (ie. sendfile():able) */
1536
    if(dobj->file_size > 0) {
1537
        if(apr_brigade_insert_file(bb, dobj->bfd, 0, 
1538
                                   dobj->file_size, p) == NULL) 
1539
        {
1540
            return APR_ENOMEM;
1541
        }
1542
    }
1543
1544
    /* Insert any remainder as read-while-caching bucket */
1545
    if(dobj->file_size < dobj->initial_size) {
1546
        if(diskcache_brigade_insert(bb, dobj->bfd, dobj->file_size, 
1547
                                    dobj->initial_size - dobj->file_size,
1548
                                    dobj->updtimeout, p
1549
                    ) == NULL) 
1550
        {
1551
            return APR_ENOMEM;
1552
        }
1553
    }
1554
763
    e = apr_bucket_eos_create(bb->bucket_alloc);
1555
    e = apr_bucket_eos_create(bb->bucket_alloc);
764
    APR_BRIGADE_INSERT_TAIL(bb, e);
1556
    APR_BRIGADE_INSERT_TAIL(bb, e);
765
1557
1558
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1559
                 "disk_cache: recall_body: Succeeded for URL %s from file %s",
1560
                 dobj->name, dobj->bodyfile);
1561
766
    return APR_SUCCESS;
1562
    return APR_SUCCESS;
767
}
1563
}
768
1564
769
static apr_status_t store_table(apr_file_t *fd, apr_table_t *table)
1565
/* Store table on disk.
1566
 * Format on disk: apr_uint32_t totsize - total size of data following totsize
1567
 *                 totsize of data, consisting of key\0value\0...key\0value\0
1568
 */
1569
static apr_status_t store_table(apr_file_t *fd, apr_table_t *table,
1570
                                request_rec *r)
770
{
1571
{
771
    int i;
1572
    int i, nelts, niov;
772
    apr_status_t rv;
1573
    apr_status_t rv;
773
    struct iovec iov[4];
774
    apr_size_t amt;
1574
    apr_size_t amt;
1575
    apr_uint32_t totsize = 0;
775
    apr_table_entry_t *elts;
1576
    apr_table_entry_t *elts;
1577
    /* FIXME: Is this too big for putting on the stack? */
1578
    struct iovec iov[APR_MAX_IOVEC_SIZE];
1579
1580
    nelts = apr_table_elts(table)->nelts;
1581
    if(nelts > (APR_MAX_IOVEC_SIZE-1)/2) {
1582
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1583
                     "disk_cache: store_table: %d header entries would "
1584
                     "overflow iovec", nelts);
1585
        return APR_EINVAL;
1586
    }
776
1587
777
    elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
1588
    elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
778
    for (i = 0; i < apr_table_elts(table)->nelts; ++i) {
1589
    niov = 1;
1590
    for (i = 0; i < nelts; ++i) {
779
        if (elts[i].key != NULL) {
1591
        if (elts[i].key != NULL) {
780
            iov[0].iov_base = elts[i].key;
1592
            iov[niov].iov_base = elts[i].key;
781
            iov[0].iov_len = strlen(elts[i].key);
1593
            iov[niov].iov_len = strlen(elts[i].key)+1;
782
            iov[1].iov_base = ": ";
1594
            totsize += iov[niov++].iov_len;
783
            iov[1].iov_len = sizeof(": ") - 1;
1595
            iov[niov].iov_base = elts[i].val;
784
            iov[2].iov_base = elts[i].val;
1596
            iov[niov].iov_len = strlen(elts[i].val)+1;
785
            iov[2].iov_len = strlen(elts[i].val);
1597
            totsize += iov[niov++].iov_len;
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
        }
1598
        }
795
    }
1599
    }
796
    iov[0].iov_base = CRLF;
1600
    iov[0].iov_base = &totsize;
797
    iov[0].iov_len = sizeof(CRLF) - 1;
1601
    iov[0].iov_len = sizeof(totsize);
798
    rv = apr_file_writev(fd, (const struct iovec *) &iov, 1,
1602
    rv = apr_file_writev(fd, (const struct iovec *) &iov, niov,
799
                         &amt);
1603
                         &amt);
800
    return rv;
1604
    return rv;
801
}
1605
}
802
1606
803
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
1607
1608
static apr_status_t open_new_file(request_rec *r, const char *filename,
1609
                                  apr_file_t **fd, disk_cache_conf *conf)
804
{
1610
{
805
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1611
    int flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL;
806
                                                 &disk_cache_module);
807
    apr_status_t rv;
1612
    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
1613
822
        if (tmp) {
1614
    while(1) {
823
            apr_array_header_t* varray;
1615
        rv = apr_file_open(fd, filename, flags, 
824
            apr_uint32_t format = VARY_FORMAT_VERSION;
1616
                           APR_FPROT_UREAD | APR_FPROT_UWRITE, r->pool);
1617
1618
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1619
                     "disk_cache: open_new_file: Opening %s", filename);
1620
1621
        if(APR_STATUS_IS_EEXIST(rv)) {
1622
            apr_finfo_t finfo;
1623
1624
            rv = apr_stat(&finfo, filename, APR_FINFO_MTIME, r->pool);
1625
            if(APR_STATUS_IS_ENOENT(rv)) {
1626
                /* Someone else has already removed it, try again */
1627
                continue;
1628
            }
1629
            else if(rv != APR_SUCCESS) {
1630
                return rv;
1631
            }
825
1632
826
            mkdir_structure(conf, dobj->hdrsfile, r->pool);
1633
            /* FIXME: We should really check for size and mtime that matches
1634
               the source file too if available */
1635
            if(finfo.mtime < (apr_time_now() - conf->updtimeout) ) {
1636
                /* Something stale that's left around */
1637
1638
                rv = apr_file_remove(filename, r->pool);
1639
                if(rv != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rv)) {
1640
                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1641
                                 "disk_cache: open_new_file: Failed to "
1642
                                 "remove old %s", filename);
1643
                    return rv;
1644
                }
1645
                continue;
1646
            }
1647
            else {
1648
                /* Someone else has just created the file, return identifiable
1649
                   status so calling function can do the right thing */
827
1650
828
            rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
1651
                return CACHE_EEXIST;
829
                                 APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL,
1652
            }
830
                                 r->pool);
1653
        }
1654
        else if(APR_STATUS_IS_ENOENT(rv)) {
1655
            /* The directory for the file didn't exist */
831
1656
832
            if (rv != APR_SUCCESS) {
1657
            rv = mkdir_structure(filename, r->pool);
1658
            if(rv != APR_SUCCESS) {
1659
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1660
                             "disk_cache: open_new_file: Failed to make "
1661
                             "directory for %s", filename);
833
                return rv;
1662
                return rv;
834
            }
1663
            }
1664
            continue;
1665
        }
1666
        else if(rv == APR_SUCCESS) {
1667
            return APR_SUCCESS;
1668
        }
1669
        else {
1670
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1671
                         "disk_cache: open_new_file: Failed to open %s",
1672
                         filename);
1673
            return rv;
1674
        }
1675
    }
835
1676
836
            amt = sizeof(format);
1677
    /* We should never get here, so */
837
            apr_file_write(dobj->tfd, &format, &amt);
1678
    return APR_EGENERAL;
1679
}
838
1680
839
            amt = sizeof(info->expire);
840
            apr_file_write(dobj->tfd, &info->expire, &amt);
841
1681
842
            varray = apr_array_make(r->pool, 6, sizeof(char*));
1682
static apr_status_t store_vary_header(cache_handle_t *h, disk_cache_conf *conf,
843
            tokens_to_array(r->pool, tmp, varray);
1683
                                       request_rec *r, cache_info *info,
1684
                                       const char *varyhdr)
1685
{
1686
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1687
    apr_array_header_t* varray;
1688
    const char *vfile;
1689
    apr_status_t rv;
1690
    int flags;
1691
    disk_cache_format_t format = VARY_FORMAT_VERSION;
1692
    struct iovec iov[2];
1693
    apr_size_t amt;
844
1694
845
            store_array(dobj->tfd, varray);
1695
    if(dobj->prefix != NULL) {
1696
        vfile = dobj->prefix;
1697
    }
1698
    else {
1699
        vfile = dobj->hdrsfile;
1700
    }
846
1701
847
            apr_file_close(dobj->tfd);
1702
    flags = APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL | APR_BUFFERED;
1703
    rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile, flags, r->pool);
1704
    if (rv != APR_SUCCESS) {
1705
        return rv;
1706
    }
848
1707
849
            dobj->tfd = NULL;
1708
    iov[0].iov_base = (void*)&format;
1709
    iov[0].iov_len = sizeof(format);
850
1710
851
            rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile,
1711
    iov[1].iov_base = (void*)&info->expire;
852
                                  r->pool);
1712
    iov[1].iov_len = sizeof(info->expire);
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
1713
860
            dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1714
    rv = apr_file_writev(dobj->tfd, (const struct iovec *) &iov, 2, &amt);
861
            tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
1715
    if (rv != APR_SUCCESS) {
862
            dobj->prefix = dobj->hdrsfile;
1716
        file_cache_errorcleanup(dobj, r);
863
            dobj->hashfile = NULL;
1717
        return rv;
864
            dobj->datafile = data_file(r->pool, conf, dobj, tmp);
865
            dobj->hdrsfile = header_file(r->pool, conf, dobj, tmp);
866
        }
867
    }
1718
    }
868
1719
1720
    varray = apr_array_make(r->pool, 6, sizeof(char*));
1721
    tokens_to_array(r->pool, varyhdr, varray);
869
1722
870
    rv = apr_file_mktemp(&dobj->hfd, dobj->tempfile,
1723
    rv = store_array(dobj->tfd, varray);
871
                         APR_CREATE | APR_WRITE | APR_BINARY |
1724
    if (rv != APR_SUCCESS) {
872
                         APR_BUFFERED | APR_EXCL, r->pool);
1725
        file_cache_errorcleanup(dobj, r);
1726
        return rv;
1727
    }
873
1728
1729
    rv = apr_file_close(dobj->tfd);
1730
    dobj->tfd = NULL;
874
    if (rv != APR_SUCCESS) {
1731
    if (rv != APR_SUCCESS) {
1732
        file_cache_errorcleanup(dobj, r);
1733
        apr_file_remove(dobj->tempfile, r->pool);
1734
        return rv;
1735
    }
1736
1737
    rv = safe_file_rename(dobj->tempfile, vfile, r->pool);
1738
    if (rv != APR_SUCCESS) {
1739
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
1740
                     "disk_cache: rename tempfile to varyfile failed: "
1741
                     "%s -> %s", dobj->tempfile, vfile);
1742
        file_cache_errorcleanup(dobj, r);
1743
        apr_file_remove(dobj->tempfile, r->pool);
875
        return rv;
1744
        return rv;
876
    }
1745
    }
877
1746
878
    dobj->name = h->cache_obj->key;
1747
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
1748
1749
    if(dobj->prefix == NULL) {
1750
        const char *tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
1751
        char *p;
1752
1753
        dobj->prefix = dobj->hdrsfile;
1754
        p = strrchr((char *)dobj->prefix, '.');
1755
        if(p) {
1756
            /* Cut away the suffix */
1757
            *p = '\0';
1758
        }
1759
        dobj->hdrsfile = cache_file(r->pool, conf, dobj->prefix, tmp, 
1760
                                    CACHE_HEADER_SUFFIX);
1761
        dobj->bodyfile = cache_file(r->pool, conf, dobj->prefix, tmp, 
1762
                                    CACHE_BODY_SUFFIX);
1763
    }
1764
1765
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1766
                 "disk_cache: Stored vary header for URL %s", dobj->name);
1767
1768
    return APR_SUCCESS;
1769
}
1770
1771
1772
static apr_status_t store_disk_header(disk_cache_object_t *dobj,
1773
                                       request_rec *r, cache_info *info)
1774
{
1775
    disk_cache_format_t format = DISK_FORMAT_VERSION;
1776
    struct iovec iov[5];
1777
    int niov;
1778
    disk_cache_info_t disk_info;
1779
    apr_size_t amt;
1780
    apr_status_t rv;
879
1781
880
    disk_info.format = DISK_FORMAT_VERSION;
881
    disk_info.date = info->date;
1782
    disk_info.date = info->date;
882
    disk_info.expire = info->expire;
1783
    disk_info.expire = info->expire;
883
    disk_info.entity_version = dobj->disk_info.entity_version++;
1784
    disk_info.entity_version = dobj->disk_info.entity_version++;
884
    disk_info.request_time = info->request_time;
1785
    disk_info.request_time = info->request_time;
885
    disk_info.response_time = info->response_time;
1786
    disk_info.response_time = info->response_time;
886
    disk_info.status = info->status;
1787
    disk_info.status = info->status;
1788
    disk_info.file_size = dobj->initial_size;
1789
    disk_info.lastmod = dobj->lastmod;
1790
1791
    niov = 0;
1792
    iov[niov].iov_base = (void*)&format;
1793
    iov[niov++].iov_len = sizeof(format);
1794
    iov[niov].iov_base = (void*)&disk_info;
1795
    iov[niov++].iov_len = sizeof(disk_cache_info_t);
887
1796
888
    disk_info.name_len = strlen(dobj->name);
1797
    disk_info.name_len = strlen(dobj->name);
1798
    iov[niov].iov_base = (void*)dobj->name;
1799
    iov[niov++].iov_len = disk_info.name_len;
889
1800
890
    iov[0].iov_base = (void*)&disk_info;
1801
    if(dobj->initial_size > 0) {
891
    iov[0].iov_len = sizeof(disk_cache_info_t);
1802
        /* We know the bodyfile is root/bodyname ... */
892
    iov[1].iov_base = (void*)dobj->name;
1803
        char *bodyname = (char *) dobj->bodyfile + dobj->root_len + 1;
893
    iov[1].iov_len = disk_info.name_len;
1804
        disk_info.bodyname_len = strlen(bodyname);
1805
        iov[niov].iov_base = (void*)bodyname;
1806
        iov[niov++].iov_len = disk_info.bodyname_len;
1807
    }
1808
    else {
1809
        disk_info.bodyname_len = 0;
1810
    }
894
1811
895
    rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, 2, &amt);
1812
    if(r->filename != NULL && strlen(r->filename) > 0) {
1813
        disk_info.filename_len = strlen(r->filename);
1814
        iov[niov].iov_base = (void*)r->filename;
1815
        iov[niov++].iov_len = disk_info.filename_len;
1816
    }
1817
    else {
1818
        disk_info.filename_len = 0;
1819
    }
1820
1821
    rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, niov, &amt);
896
    if (rv != APR_SUCCESS) {
1822
    if (rv != APR_SUCCESS) {
1823
        file_cache_errorcleanup(dobj, r);
897
        return rv;
1824
        return rv;
898
    }
1825
    }
899
1826
Lines 911-918 Link Here
911
1838
912
        headers_out = apr_table_overlay(r->pool, headers_out,
1839
        headers_out = apr_table_overlay(r->pool, headers_out,
913
                                        r->err_headers_out);
1840
                                        r->err_headers_out);
914
        rv = store_table(dobj->hfd, headers_out);
1841
        rv = store_table(dobj->hfd, headers_out, r);
915
        if (rv != APR_SUCCESS) {
1842
        if (rv != APR_SUCCESS) {
1843
            file_cache_errorcleanup(dobj, r);
916
            return rv;
1844
            return rv;
917
        }
1845
        }
918
    }
1846
    }
Lines 924-1047 Link Here
924
1852
925
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
1853
        headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
926
                                                 r->server);
1854
                                                 r->server);
927
        rv = store_table(dobj->hfd, headers_in);
1855
        rv = store_table(dobj->hfd, headers_in, r);
928
        if (rv != APR_SUCCESS) {
1856
        if (rv != APR_SUCCESS) {
1857
            file_cache_errorcleanup(dobj, r);
1858
            return rv;
1859
        }
1860
    }
1861
1862
    /* Store it away so we can get it later. */
1863
    dobj->disk_info = disk_info;
1864
1865
    return APR_SUCCESS;
1866
}
1867
1868
1869
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, 
1870
                                  cache_info *info)
1871
{
1872
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
1873
                                                 &disk_cache_module);
1874
    apr_status_t rv;
1875
    int rewriting;
1876
    disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
1877
    const char *lastmods;
1878
1879
1880
    /* This is flaky... we need to manage the cache_info differently */
1881
    h->cache_obj->info = *info;
1882
1883
    /* Get last-modified timestamp */
1884
    lastmods = apr_table_get(r->err_headers_out, "Last-Modified");
1885
    if (lastmods == NULL) {
1886
        lastmods = apr_table_get(r->headers_out, "Last-Modified");
1887
    }
1888
    if (lastmods != NULL) {
1889
        dobj->lastmod = apr_date_parse_http(lastmods);
1890
    }
1891
1892
    if(dobj->hfd) {
1893
        rewriting = TRUE;
1894
1895
        /* Don't update header on disk if the following is met:
1896
           - The body size is known.
1897
           - If Last-Modified is known, it has to be identical.
1898
           - It's not expired.
1899
           - Date in cached header isn't older than updtimeout.
1900
         */
1901
        if( dobj->disk_info.file_size >= 0 && (dobj->lastmod == APR_DATE_BAD || 
1902
                dobj->lastmod == dobj->disk_info.lastmod) &&
1903
                dobj->disk_info.expire > r->request_time &&
1904
                dobj->disk_info.date > info->date - dobj->updtimeout) 
1905
        {
1906
            dobj->skipstore = TRUE;
1907
1908
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1909
                         "disk_cache: store_headers: Headers current for URL "
1910
                         "%s", dobj->name);
1911
        }
1912
        else {
1913
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1914
                         "disk_cache: Rewriting headers for URL %s", 
1915
                         dobj->name);
1916
        }
1917
    }
1918
    else {
1919
        rewriting = FALSE;
1920
1921
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1922
                     "disk_cache: Storing new headers for URL %s", dobj->name);
1923
    }
1924
1925
    if (r->headers_out) {
1926
        const char *tmp;
1927
1928
        tmp = apr_table_get(r->headers_out, "Vary");
1929
1930
        if (tmp) {
1931
            rv = store_vary_header(h, conf, r, info, tmp);
1932
            if(rv != APR_SUCCESS) {
1933
                return rv;
1934
            }
1935
        }
1936
    } 
1937
1938
    if(dobj->skipstore) {
1939
        apr_file_close(dobj->hfd);
1940
        dobj->hfd = NULL;
1941
        return APR_SUCCESS;
1942
    }
1943
1944
    if(rewriting) {
1945
        apr_finfo_t     finfo;
1946
1947
        rv = apr_file_info_get(&finfo, APR_FINFO_MTIME, dobj->hfd);
1948
        if(rv != APR_SUCCESS) {
1949
            return rv;
1950
        }
1951
1952
        /* FIXME: Isn't this a bit redundant? It probably causes more
1953
           trouble than it's fixing, especially since we handle it above
1954
           except for looking at mtime */
1955
        /* Don't store disk headers more often than updtimeout */
1956
        if(dobj->disk_info.file_size >= 0 &&
1957
                dobj->disk_info.expire > r->request_time &&
1958
                r->request_time < finfo.mtime + dobj->updtimeout) 
1959
        {
1960
            dobj->skipstore = TRUE;
1961
        }
1962
        else {
1963
            /* This triggers bugs in APR when using APR_BUFFERED */
1964
            apr_off_t off=0;
1965
            rv = apr_file_seek(dobj->hfd, APR_SET, &off);
1966
            if (rv != APR_SUCCESS) {
1967
                return rv;
1968
            }
1969
            rv = apr_file_trunc(dobj->hfd, 0);
1970
            if(rv != APR_SUCCESS) {
1971
                return rv;
1972
            }
1973
        }
1974
1975
    }
1976
    else {
1977
        rv = open_new_file(r, dobj->hdrsfile, &(dobj->hfd), conf);
1978
        if(rv == CACHE_EEXIST) {
1979
            dobj->skipstore = TRUE;
1980
        }
1981
        else if(rv != APR_SUCCESS) {
929
            return rv;
1982
            return rv;
930
        }
1983
        }
931
    }
1984
    }
932
1985
933
    apr_file_close(dobj->hfd); /* flush and close */
1986
    if(dobj->skipstore) {
1987
        if(dobj->hfd) {
1988
            apr_file_close(dobj->hfd);
1989
            dobj->hfd = NULL;
1990
        }
1991
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1992
                     "disk_cache: Skipping store for URL %s: Someone else "
1993
                     "beat us to it",  dobj->name);
1994
        return APR_SUCCESS;
1995
    }
1996
1997
    rv = store_disk_header(dobj, r, info);
1998
    if(rv != APR_SUCCESS) {
1999
        return rv;
2000
    }
2001
2002
    /* If the body size is unknown, the header file will be rewritten later
2003
       so we can't close it */
2004
    if(dobj->initial_size >= 0) {
2005
        rv = apr_file_close(dobj->hfd);
2006
        dobj->hfd = NULL;
2007
        if(rv != APR_SUCCESS) {
2008
            apr_file_remove(dobj->hdrsfile, r->pool);
2009
            return rv;
2010
        }
2011
    }
2012
2013
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2014
                 "disk_cache: Stored headers for URL %s",  dobj->name);
2015
    return APR_SUCCESS;
2016
}
2017
2018
2019
static apr_status_t check_destfd_timeout(apr_file_t *fd, apr_time_t now,
2020
                                         apr_time_t updtimeout) 
2021
{
2022
    apr_status_t rc;
2023
    apr_finfo_t finfo;
2024
2025
    /* Get mtime and nlink for our opened destfile */
2026
    rc = apr_file_info_get(&finfo, APR_FINFO_MTIME | APR_FINFO_NLINK, fd);
2027
    if(rc != APR_SUCCESS) {
2028
        return rc;
2029
    }
2030
2031
    /* If link count is zero, file is deleted */
2032
    if(finfo.nlink == 0) {
2033
        return APR_ETIMEDOUT;
2034
    }
2035
2036
    /* Check if mtime on destfile shows us having timed out */
2037
    if(now - finfo.mtime > updtimeout) {
2038
        return APR_ETIMEDOUT;
2039
    }
2040
2041
    return APR_SUCCESS;
2042
}
2043
2044
2045
static apr_status_t copy_body(apr_pool_t *p,
2046
                              apr_file_t *srcfd, apr_off_t srcoff, 
2047
                              apr_file_t *destfd, apr_off_t destoff, 
2048
                              apr_off_t len, apr_interval_time_t updtimeout)
2049
{
2050
    apr_status_t rc;
2051
    apr_size_t size;
2052
    apr_finfo_t finfo;
2053
    apr_time_t starttime = apr_time_now();
2054
    apr_time_t last = starttime;
2055
    apr_time_t lastcheck = 0;
2056
    unsigned int i=0, freq=1;
2057
    apr_interval_time_t minintvl = updtimeout/10;
2058
    apr_interval_time_t maxintvl = minintvl*3;
2059
2060
    char *buf = apr_palloc(p, MIN(len, CACHE_BUF_SIZE));
2061
    if (!buf) {
2062
        return APR_ENOMEM;
2063
    }
2064
2065
    if(srcoff != 0) {
2066
        rc = apr_file_seek(srcfd, APR_SET, &srcoff);
2067
        if(rc != APR_SUCCESS) {
2068
            return rc;
2069
        }
2070
    }
934
2071
935
    /* Remove old file with the same name. If remove fails, then
2072
    if(destoff != 0) {
936
     * perhaps we need to create the directory tree where we are
2073
        rc = apr_file_seek(destfd, APR_SET, &destoff);
937
     * about to write the new headers file.
2074
        if(rc != APR_SUCCESS) {
2075
            return rc;
2076
        }
2077
    }
2078
2079
    /* Tried doing this with mmap, but sendfile on Linux got confused when
2080
       sending a file while it was being written to from an mmapped area.
2081
       The traditional way seems to be good enough, and less complex.
938
     */
2082
     */
939
    rv = apr_file_remove(dobj->hdrsfile, r->pool);
2083
    while(len > 0) {
940
    if (rv != APR_SUCCESS) {
2084
        size=MIN(len, CACHE_BUF_SIZE);
941
        mkdir_structure(conf, dobj->hdrsfile, r->pool);
2085
2086
        rc = apr_file_read_full (srcfd, buf, size, NULL);
2087
        if(rc != APR_SUCCESS) {
2088
            return rc;
2089
        }
2090
2091
        /* Do timeout checks before we do the write, this is what other clients
2092
           will see. Don't waste resources by calling apr_time_now() on each
2093
           iteration. */
2094
        if(i++ % freq == 0) {
2095
            apr_time_t now = apr_time_now();
2096
            apr_time_t elapsed = now-last;
2097
2098
            /* Do closer inspection at updtimeout intervals */
2099
            if(now-lastcheck > updtimeout) {
2100
                rc = check_destfd_timeout(destfd, now, updtimeout);
2101
                if(rc != APR_SUCCESS) {
2102
                    return rc;
2103
                }
2104
                lastcheck = now;
2105
            }
2106
2107
            if(elapsed > updtimeout) {
2108
                if(freq > 1) {
2109
                    /* The close inspection above will catch a timeout. 
2110
                       If we get here, make sure we recalibrate at which
2111
                       frequency we should check stuff */
2112
                    freq = 1;
2113
                }
2114
            }
2115
            else if(elapsed < minintvl) {
2116
                freq <<= 1;
2117
                freq |= 1;
2118
            }
2119
            else if(elapsed > maxintvl && freq > 1) {
2120
                freq >>= 1;
2121
            }
2122
            last = now;
2123
        }
2124
2125
        rc = apr_file_write_full(destfd, buf, size, NULL);
2126
        if(rc != APR_SUCCESS) {
2127
            return rc;
2128
        }
2129
        len -= size;
2130
    }
2131
2132
    /* Make sure we are the one having cached the destfile */
2133
    rc = check_destfd_timeout(destfd, apr_time_now(), updtimeout);
2134
    if(rc != APR_SUCCESS) {
2135
        return rc;
2136
    }
2137
2138
    /* Check if file has changed during copying. This is not 100% foolproof
2139
       due to NFS attribute caching when on NFS etc. */
2140
    /* FIXME: Can we assume that we're always copying an entire file? In that
2141
              case we can check if the current filesize matches the length
2142
              we think it is */
2143
    rc = apr_file_info_get(&finfo, APR_FINFO_MTIME, srcfd);
2144
    if(rc != APR_SUCCESS) {
2145
        return rc;
2146
    }
2147
    if(starttime < finfo.mtime) {
2148
        return APR_EGENERAL;
2149
    }
2150
2151
    return APR_SUCCESS;
2152
}
2153
2154
2155
/* Provide srcfile and srcinfo containing
2156
   APR_FINFO_INODE|APR_FINFO_MTIME to make sure we have opened the right file
2157
   (someone might have just replaced it which messes up things).
2158
*/
2159
static apr_status_t copy_body_nofd(apr_pool_t *p, const char *srcfile, 
2160
                                   apr_off_t srcoff, apr_finfo_t *srcinfo,
2161
                                   const char *destfile, apr_off_t destoff, 
2162
                                   apr_off_t len, 
2163
                                   apr_interval_time_t updtimeout)
2164
{
2165
    apr_status_t rc;
2166
    apr_file_t *srcfd, *destfd;
2167
    apr_finfo_t finfo;
2168
2169
    rc = apr_file_open(&srcfd, srcfile, APR_READ | APR_BINARY, 0, p);
2170
    if(rc != APR_SUCCESS) {
2171
        return rc;
2172
    }
2173
    rc = apr_file_info_get(&finfo, APR_FINFO_INODE|APR_FINFO_MTIME, srcfd);
2174
    if(rc != APR_SUCCESS) {
2175
        return rc;
2176
    }
2177
    /* FIXME: Should probably check device too */
2178
    if(srcinfo->inode != finfo.inode || srcinfo->mtime < finfo.mtime) {
2179
        return APR_EGENERAL;
2180
    }
2181
2182
    rc = apr_file_open(&destfd, destfile, APR_WRITE | APR_BINARY, 0, p);
2183
    if(rc != APR_SUCCESS) {
2184
        return rc;
2185
    }
2186
2187
    rc = copy_body(p, srcfd, srcoff, destfd, destoff, len, updtimeout);
2188
    apr_file_close(srcfd);
2189
    if(rc != APR_SUCCESS) {
2190
        apr_file_close(destfd);
2191
        return rc;
2192
    }
2193
2194
    rc = apr_file_close(destfd);
2195
2196
    /* Set mtime on file */
2197
    apr_file_mtime_set(destfile, finfo.mtime, p);
2198
2199
    return rc;
2200
}
2201
2202
2203
#if APR_HAS_THREADS
2204
static apr_status_t bgcopy_thread_cleanup(void *data)
2205
{
2206
    copyinfo *ci = data;
2207
    apr_status_t rc, ret;
2208
    apr_pool_t *p;
2209
2210
    /* FIXME: Debug */
2211
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2212
                 "disk_cache: bgcopy_thread_cleanup: %s -> %s",
2213
                 ci->srcfile, ci->destfile);
2214
2215
    rc = apr_thread_join(&ret, ci->t);
2216
    if(rc != APR_SUCCESS) {
2217
        ap_log_error(APLOG_MARK, APLOG_ERR, rc, ci->s,
2218
                     "disk_cache: bgcopy_thread_cleanup: apr_thread_join "
2219
                     "failed %s -> %s", ci->srcfile, ci->destfile);
2220
        return rc;
2221
    }
2222
    if(ret != APR_SUCCESS) {
2223
        ap_log_error(APLOG_MARK, APLOG_ERR, ret, ci->s,
2224
                     "disk_cache: Background caching body %s -> %s failed",
2225
                     ci->srcfile, ci->destfile);
942
    }
2226
    }
943
2227
944
    rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile, r->pool);
2228
    /* FIXME: Debug */
2229
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2230
                 "disk_cache: bgcopy_thread_cleanup: SUCCESS %s -> %s",
2231
                 ci->srcfile, ci->destfile);
2232
2233
    /* Destroy our private pool */
2234
    p = ci->pool;
2235
    apr_pool_destroy(p);
2236
2237
    return APR_SUCCESS;
2238
}
2239
2240
2241
static void *bgcopy_thread(apr_thread_t *t, void *data)
2242
{
2243
    copyinfo *ci = data;
2244
    apr_pool_t *p;
2245
    apr_status_t rc;
2246
2247
    p = apr_thread_pool_get(t);
2248
2249
    /* FIXME: Debug */
2250
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2251
                 "disk_cache: bgcopy_thread: start %s -> %s",
2252
                 ci->srcfile, ci->destfile);
2253
2254
    rc = copy_body_nofd(p, ci->srcfile, ci->srcoff, &(ci->srcinfo), 
2255
                        ci->destfile, ci->destoff, ci->len, ci->updtimeout);
2256
2257
    if(rc != APR_ETIMEDOUT && rc != APR_SUCCESS) {
2258
        apr_file_remove(ci->destfile, p);
2259
    }
2260
2261
    /* FIXME: Debug */
2262
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s,
2263
                 "disk_cache: bgcopy_thread: done %s -> %s",
2264
                 ci->srcfile, ci->destfile);
2265
2266
    apr_thread_exit(t, rc);
2267
    return NULL;
2268
}
2269
#endif /* APR_HAS_THREADS */
2270
2271
2272
#if APR_HAS_FORK
2273
static apr_status_t bgcopy_child_cleanup(void *data) {
2274
    copyinfo *ci = data;
2275
    int status;
2276
    apr_exit_why_e why;
2277
    apr_pool_t *p;
2278
2279
    apr_proc_wait(ci->proc, &status, &why, APR_WAIT);
2280
    if(why == APR_PROC_EXIT) {
2281
        if(status != APR_SUCCESS) {
2282
            ap_log_error(APLOG_MARK, APLOG_ERR, status, ci->s,
2283
                         "disk_cache: Background caching body %s -> %s failed",
2284
                         ci->srcfile, ci->destfile);
2285
            return APR_SUCCESS;
2286
        }
2287
    }
2288
    else if(status & (APR_PROC_SIGNAL | APR_PROC_SIGNAL_CORE) ) {
2289
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, ci->s,
2290
                     "disk_cache: Background caching body %s -> %s failed, "
2291
                     "caught signal %d", ci->srcfile, ci->destfile, status);
2292
        return APR_SUCCESS;
2293
    }
2294
2295
    /* Destroy our private pool */
2296
    p = ci->pool;
2297
    apr_pool_destroy(p);
2298
2299
    return APR_SUCCESS;
2300
}
2301
#endif /* APR_HAS_FORK */
2302
2303
2304
static apr_status_t do_bgcopy(apr_file_t *srcfd, apr_off_t srcoff, 
2305
                              apr_file_t *destfd, apr_off_t destoff, 
2306
                              apr_off_t len, apr_interval_time_t updtimeout,
2307
                              conn_rec *c)
2308
{
2309
    copyinfo *ci;
2310
    apr_status_t rv;
2311
    apr_pool_t *newpool;
2312
    const char *srcfile, *destfile;
2313
    int mpm_query_info;
2314
2315
    /* It seems pool gets destroyed (ie. fd's closed) before our cleanup 
2316
       function is called when an error occurs (a dropped connection, for
2317
       example), so we need a pool of our own.
2318
     */
2319
    rv = apr_pool_create(&newpool, NULL);
945
    if (rv != APR_SUCCESS) {
2320
    if (rv != APR_SUCCESS) {
2321
        return rv;
2322
    }
2323
2324
    ci = apr_palloc(newpool, sizeof(*ci));
2325
    if(ci == NULL) {
2326
        apr_pool_destroy(newpool);
2327
        return APR_ENOMEM;
2328
    }
2329
2330
    rv = apr_file_name_get(&srcfile, srcfd);
2331
    if(rv != APR_SUCCESS) {
2332
        return rv;
2333
    }
2334
    rv = apr_file_info_get(&(ci->srcinfo), APR_FINFO_INODE|APR_FINFO_MTIME,
2335
                           srcfd);
2336
    if(rv != APR_SUCCESS) {
2337
        return rv;
2338
    }
2339
2340
    rv = apr_file_name_get(&destfile, destfd);
2341
    if(rv != APR_SUCCESS) {
2342
        return rv;
2343
    }
2344
2345
    ci->pool = newpool;
2346
    ci->srcfile = apr_pstrdup(newpool, srcfile);
2347
    ci->srcoff = srcoff;
2348
    ci->destfile = apr_pstrdup(newpool, destfile);
2349
    ci->destoff = destoff;
2350
    ci->len = len;
2351
    ci->updtimeout = updtimeout;
2352
    ci->s = c->base_server;
2353
2354
#if APR_HAS_THREADS
2355
    if(ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_query_info) == APR_SUCCESS) {
2356
        apr_threadattr_t *ta;
2357
        apr_thread_t *t;
2358
        rv = apr_threadattr_create(&ta, newpool);
2359
        if(rv != APR_SUCCESS) {
2360
            apr_pool_destroy(newpool);
2361
            return rv;
2362
        }
2363
2364
        apr_threadattr_detach_set(ta, FALSE);
2365
2366
        /* FIXME: This makes module unloadable on AIX */
2367
#if 0
2368
#ifdef AP_MPM_WANT_SET_STACKSIZE
2369
        if (ap_thread_stacksize != 0) {
2370
            apr_threadattr_stacksize_set(ta, ap_thread_stacksize);
2371
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server,
2372
                    "disk_cache: BG thread stacksize set to %"
2373
                    APR_SIZE_T_FMT, ap_thread_stacksize);
2374
        }
2375
#endif /* AP_MPM_WANT_SET_STACKSIZE */
2376
#endif /* 0 */
2377
2378
        rv = apr_thread_create (&t, ta, bgcopy_thread, ci, newpool);
2379
        if (rv != APR_SUCCESS) {
2380
            apr_pool_destroy(newpool);
2381
            return rv;
2382
        }
2383
        ci->t = t;
2384
2385
        apr_pool_cleanup_register(c->pool, ci, 
2386
                                  bgcopy_thread_cleanup, apr_pool_cleanup_null);
2387
    }
2388
    else
2389
#endif /* APR_HAS_THREADS */
2390
#if APR_HAS_FORK
2391
    if(ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_query_info) == APR_SUCCESS) {
2392
        ci->proc = apr_palloc(newpool, sizeof(apr_proc_t));
2393
        if(ci->proc == NULL) {
2394
            apr_pool_destroy(newpool);
2395
            return APR_ENOMEM;
2396
        }
2397
        rv = apr_proc_fork(ci->proc, newpool);
2398
        if(rv == APR_INCHILD) {
2399
            /* Child */
2400
            rv = copy_body_nofd(ci->pool, ci->srcfile, ci->srcoff, 
2401
                                &(ci->srcinfo), ci->destfile, ci->destoff, 
2402
                                ci->len, ci->updtimeout);
2403
            if(rv != APR_ETIMEDOUT && rv != APR_SUCCESS) {
2404
                apr_file_remove(ci->destfile, ci->pool);
2405
            }
2406
            exit(rv);
2407
        }
2408
        else if(rv == APR_INPARENT) {
2409
            apr_pool_cleanup_register(c->pool, ci, 
2410
                                      bgcopy_child_cleanup, 
2411
                                      apr_pool_cleanup_null);
2412
        }
2413
        else {
2414
            return rv;
2415
        }
2416
    }
2417
    else 
2418
#endif /* APR_HAS_FORK */
2419
    if(1)
2420
    {
2421
        rv = copy_body(newpool, srcfd, ci->srcoff, destfd, ci->destoff,
2422
                       ci->len, ci->updtimeout);
2423
        apr_pool_destroy(newpool);
2424
    }
2425
2426
    return rv;
2427
}
2428
2429
2430
static apr_status_t replace_brigade_with_cache(cache_handle_t *h,
2431
                                               request_rec *r,
2432
                                               apr_bucket_brigade *bb)
2433
{
2434
    apr_status_t rv;
2435
    apr_bucket *e;
2436
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
2437
2438
    if(dobj->bfd) {
2439
        apr_file_close(dobj->bfd);
2440
        dobj->bfd = NULL;
2441
    }
2442
2443
    rv = open_body_timeout(r, h->cache_obj);
2444
    if(rv == CACHE_EDECLINED) {
2445
        return APR_ETIMEDOUT;
2446
    }
2447
    else if(rv != APR_SUCCESS) {
946
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2448
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
947
                     "disk_cache: rename tempfile to hdrsfile failed: %s -> %s",
2449
                     "disk_cache: Error opening bodyfile %s for URL %s",
948
                     dobj->tempfile, dobj->hdrsfile);
2450
                     dobj->bodyfile, dobj->name);
949
        return rv;
2451
        return rv;
950
    }
2452
    }
951
2453
952
    dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
2454
    /* First, empty the brigade */
2455
    e  = APR_BRIGADE_FIRST(bb);
2456
    while (e != APR_BRIGADE_SENTINEL(bb)) {
2457
        apr_bucket *d;
2458
        d = e;
2459
        e = APR_BUCKET_NEXT(e);
2460
        apr_bucket_delete(d);
2461
    }
953
2462
2463
    /* Then, populate it with our cached instance */
2464
    rv = recall_body(h, r->pool, bb);
2465
    if (rv != APR_SUCCESS) {
2466
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2467
                     "disk_cache: Error serving URL %s from cache", dobj->name);
2468
        return rv;
2469
    }
954
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2470
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
955
                 "disk_cache: Stored headers for URL %s",  dobj->name);
2471
                 "disk_cache: Serving cached body for URL %s", dobj->name);
2472
956
    return APR_SUCCESS;
2473
    return APR_SUCCESS;
957
}
2474
}
958
2475
2476
959
static apr_status_t store_body(cache_handle_t *h, request_rec *r,
2477
static apr_status_t store_body(cache_handle_t *h, request_rec *r,
960
                               apr_bucket_brigade *bb)
2478
                               apr_bucket_brigade *bb)
961
{
2479
{
962
    apr_bucket *e;
2480
    apr_bucket *e;
963
    apr_status_t rv;
2481
    apr_status_t rv;
2482
    int copy_file = FALSE, first_call = FALSE, did_bgcopy = FALSE;
964
    disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
2483
    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,
2484
    disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
966
                                                 &disk_cache_module);
2485
                                                 &disk_cache_module);
967
2486
968
    /* We write to a temp file and then atomically rename the file over
2487
    if(r->no_cache) {
969
     * in file_cache_el_final().
2488
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
970
     */
2489
                     "disk_cache: store_body called for URL %s even though"
971
    if (!dobj->tfd) {
2490
                     "no_cache is set", dobj->name);
972
        rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
2491
        file_cache_errorcleanup(dobj, r);
973
                             APR_CREATE | APR_WRITE | APR_BINARY |
2492
        return APR_EGENERAL;
974
                             APR_BUFFERED | APR_EXCL, r->pool);
2493
    }
975
        if (rv != APR_SUCCESS) {
2494
976
            return rv;
2495
    if(dobj->initial_size == 0) {
2496
        /* Don't waste a body cachefile on a 0 length body */
2497
        return APR_SUCCESS;
2498
    }
2499
2500
    /* Only perform these actions when called the first time */
2501
    if(dobj->bfd == NULL) {
2502
        first_call = TRUE;
2503
2504
        if(dobj->lastmod != APR_DATE_BAD) {
2505
            apr_finfo_t finfo;
2506
            rv = apr_stat(&finfo, dobj->bodyfile, 
2507
                          APR_FINFO_MTIME | APR_FINFO_SIZE | APR_FINFO_CSIZE, 
2508
                          r->pool);
2509
            if(rv == APR_SUCCESS || APR_STATUS_IS_INCOMPLETE(rv)) {
2510
                /* Dest-file will have same mtime as source if it's
2511
                   current */
2512
                /* FIXME: This code and the one used in open_body should
2513
                   probably be identical... */
2514
                if(dobj->lastmod <= finfo.mtime && 
2515
                        dobj->initial_size == finfo.size &&
2516
                        !(finfo.valid & APR_FINFO_CSIZE && finfo.csize < finfo.size))
2517
                {
2518
                    /* Assume it's a valid cached body there already */
2519
                    dobj->skipstore = TRUE;
2520
                }
2521
            }
2522
        }
2523
2524
        if(!dobj->skipstore) {
2525
            /* FIXME: We should pass the source file's size and mtime so
2526
               open_new_file() can more reliably determine if the target
2527
               file is current or stale. */
2528
            rv = open_new_file(r, dobj->bodyfile, &(dobj->bfd), conf);
2529
            if(rv == CACHE_EEXIST) {
2530
                /* Someone else beat us to storing this */
2531
                dobj->skipstore = TRUE;
2532
            }
2533
            else if(rv != APR_SUCCESS) {
2534
                file_cache_errorcleanup(dobj, r);
2535
                apr_file_remove(dobj->hdrsfile, r->pool);
2536
                return rv;
2537
            }
2538
            else {
2539
                dobj->file_size = 0;
2540
            }
2541
        }
2542
2543
        if(dobj->skipstore) {
2544
            /* Someone else beat us to storing this object */
2545
            if( dobj->initial_size > 0 &&
2546
                    APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
2547
            {
2548
                /* Yay, we can replace the body with the cached instance */
2549
                return replace_brigade_with_cache(h, r, bb);
2550
            }
2551
2552
            return APR_SUCCESS;
977
        }
2553
        }
978
        dobj->file_size = 0;
979
    }
2554
    }
980
2555
981
    for (e = APR_BRIGADE_FIRST(bb);
2556
    /* Check if this is a complete single sequential file, eligable for
982
         e != APR_BRIGADE_SENTINEL(bb);
2557
     * file copy.
983
         e = APR_BUCKET_NEXT(e))
2558
     */
2559
    /* FIXME: Make the min size to do file copy run-time config? */
2560
    if(dobj->file_size == 0 && 
2561
            dobj->initial_size > APR_BUCKET_BUFF_SIZE &&
2562
            APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)) )
984
    {
2563
    {
985
        const char *str;
2564
        apr_off_t begin = -1;
986
        apr_size_t length, written;
2565
        apr_off_t pos = -1;
987
        rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
2566
        apr_file_t *fd = NULL;
988
        if (rv != APR_SUCCESS) {
2567
        apr_bucket_file *a;
989
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2568
990
                         "cache_disk: Error when reading bucket for URL %s",
2569
        copy_file = TRUE;
991
                         h->cache_obj->key);
2570
992
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2571
        for (e = APR_BRIGADE_FIRST(bb);
993
            file_cache_errorcleanup(dobj, r);
2572
                e != APR_BRIGADE_SENTINEL(bb);
994
            return rv;
2573
                e = APR_BUCKET_NEXT(e))
2574
        {
2575
            if(APR_BUCKET_IS_EOS(e)) {
2576
                break;
2577
            }
2578
            if(!APR_BUCKET_IS_FILE(e)) {
2579
                copy_file = FALSE;
2580
                break;
2581
            }
2582
2583
            a = e->data;
2584
2585
            if(begin < 0) {
2586
                begin = pos = e->start;
2587
                fd = a->fd;
2588
            }
2589
2590
            if(fd != a->fd || pos != e->start) {
2591
                copy_file = FALSE;
2592
                break;
2593
            }
2594
2595
            pos += e->length;
995
        }
2596
        }
996
        rv = apr_file_write_full(dobj->tfd, str, length, &written);
2597
997
        if (rv != APR_SUCCESS) {
2598
        if(copy_file) {
998
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
2599
            dobj->file_size = pos;
999
                         "cache_disk: Error when writing cache file for URL %s",
2600
        }
1000
                         h->cache_obj->key);
2601
    }
1001
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2602
1002
            file_cache_errorcleanup(dobj, r);
2603
    if(copy_file) {
2604
        apr_bucket_file *a;
2605
2606
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
2607
                     "disk_cache: Copying body for URL %s, len %"
2608
                     APR_OFF_T_FMT, dobj->name, dobj->file_size);
2609
2610
        e = APR_BRIGADE_FIRST(bb);
2611
        a = e->data;
2612
2613
        if(dobj->file_size > conf->minbgsize) {
2614
            rv = do_bgcopy(a->fd, e->start, dobj->bfd, 0, dobj->file_size,
2615
                           dobj->updtimeout, r->connection);
2616
            did_bgcopy = TRUE;
2617
        }
2618
        else {
2619
            rv = copy_body(r->pool, a->fd, e->start, dobj->bfd, 0,
2620
                           dobj->file_size, dobj->updtimeout);
2621
        }
2622
        if(rv != APR_SUCCESS) {
2623
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2624
                         "disk_cache: Copying body failed, "
2625
                         "URL %s", dobj->name);
2626
            if(rv != APR_ETIMEDOUT) {
2627
                file_cache_errorcleanup(dobj, r);
2628
                apr_file_remove(dobj->bodyfile, r->pool);
2629
            }
1003
            return rv;
2630
            return rv;
1004
        }
2631
        }
1005
        dobj->file_size += written;
2632
1006
        if (dobj->file_size > conf->maxfs) {
2633
    }
1007
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2634
    else {
1008
                         "cache_disk: URL %s failed the size check "
2635
        if(first_call) {
1009
                         "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
2636
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1010
                         h->cache_obj->key, dobj->file_size, conf->maxfs);
2637
                         "disk_cache: Caching body for URL %s, len %"
1011
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2638
                         APR_OFF_T_FMT, dobj->name, dobj->initial_size);
1012
            file_cache_errorcleanup(dobj, r);
2639
        }
1013
            return APR_EGENERAL;
2640
2641
        for (e = APR_BRIGADE_FIRST(bb);
2642
                e != APR_BRIGADE_SENTINEL(bb);
2643
                e = APR_BUCKET_NEXT(e))
2644
        {   
2645
            const char *str;
2646
            apr_size_t length, written;
2647
2648
            /* Ignore the non-data-buckets */
2649
            if(APR_BUCKET_IS_METADATA(e)) {
2650
                continue;
2651
            }
2652
2653
            rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
2654
            if (rv != APR_SUCCESS) {
2655
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2656
                             "disk_cache: Error when reading bucket for URL %s",
2657
                             dobj->name);
2658
                file_cache_errorcleanup(dobj, r);
2659
                apr_file_remove(dobj->hdrsfile, r->pool);
2660
                apr_file_remove(dobj->bodyfile, r->pool);
2661
                return rv;
2662
            }
2663
            rv = apr_file_write_full(dobj->bfd, str, length, &written);
2664
            if (rv != APR_SUCCESS) {
2665
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
2666
                             "disk_cache: Error when writing cache file for "
2667
                             "URL %s", dobj->name);
2668
                file_cache_errorcleanup(dobj, r);
2669
                apr_file_remove(dobj->hdrsfile, r->pool);
2670
                apr_file_remove(dobj->bodyfile, r->pool);
2671
                return rv;
2672
            }
2673
            dobj->file_size += written;
2674
            if (dobj->file_size > conf->maxfs) {
2675
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2676
                             "disk_cache: URL %s failed the size check "
2677
                             "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
2678
                             dobj->name, dobj->file_size, conf->maxfs);
2679
                file_cache_errorcleanup(dobj, r);
2680
                apr_file_remove(dobj->hdrsfile, r->pool);
2681
                apr_file_remove(dobj->bodyfile, r->pool);
2682
                return APR_EGENERAL;
2683
            }
1014
        }
2684
        }
1015
    }
2685
    }
1016
2686
1017
    /* Was this the final bucket? If yes, close the temp file and perform
2687
1018
     * sanity checks.
2688
    /* Drop out here if this wasn't the end */
1019
     */
2689
    if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1020
    if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
2690
        return APR_SUCCESS;
1021
        if (r->connection->aborted || r->no_cache) {
2691
    }
2692
2693
    if(!copy_file) {
2694
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2695
                     "disk_cache: Done caching URL %s, len %" APR_OFF_T_FMT,
2696
                     dobj->name, dobj->file_size);
2697
2698
        /* FIXME: Do we really need to check r->no_cache here since we checked
2699
           it in the beginning? */
2700
        /* Assume that if we've got an initial size then bucket brigade
2701
           was complete and there's no danger in keeping it even if the
2702
           connection was aborted */
2703
        if (r->no_cache || (r->connection->aborted && dobj->initial_size < 0)) {
1022
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
2704
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
1023
                         "disk_cache: Discarding body for URL %s "
2705
                         "disk_cache: Discarding body for URL %s "
1024
                         "because connection has been aborted.",
2706
                         "because connection has been aborted.",
1025
                         h->cache_obj->key);
2707
                         dobj->name);
1026
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2708
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
1027
            file_cache_errorcleanup(dobj, r);
2709
            file_cache_errorcleanup(dobj, r);
2710
            apr_file_remove(dobj->hdrsfile, r->pool);
2711
            apr_file_remove(dobj->bodyfile, r->pool);
1028
            return APR_EGENERAL;
2712
            return APR_EGENERAL;
1029
        }
2713
        }
2714
1030
        if (dobj->file_size < conf->minfs) {
2715
        if (dobj->file_size < conf->minfs) {
1031
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2716
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1032
                         "cache_disk: URL %s failed the size check "
2717
                         "disk_cache: URL %s failed the size check "
1033
                         "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
2718
                         "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
1034
                         h->cache_obj->key, dobj->file_size, conf->minfs);
2719
                         dobj->name, dobj->file_size, conf->minfs);
1035
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
2720
            /* Remove the intermediate cache file and return non-APR_SUCCESS */
1036
            file_cache_errorcleanup(dobj, r);
2721
            file_cache_errorcleanup(dobj, r);
2722
            apr_file_remove(dobj->hdrsfile, r->pool);
2723
            apr_file_remove(dobj->bodyfile, r->pool);
1037
            return APR_EGENERAL;
2724
            return APR_EGENERAL;
1038
        }
2725
        }
1039
2726
1040
        /* All checks were fine. Move tempfile to final destination */
2727
        if(dobj->initial_size < 0) {
1041
        /* Link to the perm file, and close the descriptor */
2728
            /* Update header information now that we know the size */
1042
        file_cache_el_final(dobj, r);
2729
            dobj->initial_size = dobj->file_size;
1043
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2730
            rv = store_headers(h, r, &(h->cache_obj->info));
1044
                     "disk_cache: Body for URL %s cached.",  dobj->name);
2731
            if(rv != APR_SUCCESS) {
2732
                file_cache_errorcleanup(dobj, r);
2733
                apr_file_remove(dobj->hdrsfile, r->pool);
2734
                apr_file_remove(dobj->bodyfile, r->pool);
2735
                return rv;
2736
            }
2737
        }
2738
        else if(dobj->initial_size != dobj->file_size) {
2739
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
2740
                         "disk_cache: URL %s - body size mismatch: suggested %"
2741
                         APR_OFF_T_FMT "  file_size %" APR_OFF_T_FMT ")",
2742
                         dobj->name, dobj->initial_size, dobj->file_size);
2743
            file_cache_errorcleanup(dobj, r);
2744
            apr_file_remove(dobj->hdrsfile, r->pool);
2745
            apr_file_remove(dobj->bodyfile, r->pool);
2746
            return APR_EGENERAL;
2747
        }
2748
    }
2749
2750
    /* All checks were fine, close output file */
2751
    rv = apr_file_close(dobj->bfd);
2752
    dobj->bfd = NULL;
2753
    if(rv != APR_SUCCESS) {
2754
        apr_file_remove(dobj->bodyfile, r->pool);
2755
        file_cache_errorcleanup(dobj, r);
2756
        return rv;
2757
    }
2758
2759
    /* Set mtime on body file */
2760
    if(!did_bgcopy && dobj->lastmod != APR_DATE_BAD) {
2761
        apr_file_mtime_set(dobj->bodyfile, dobj->lastmod, r->pool);
2762
    }
2763
2764
2765
    /* Redirect to cachefile if we copied a plain file */
2766
    if(copy_file) {
2767
        rv = replace_brigade_with_cache(h, r, bb);
2768
        if(rv != APR_SUCCESS) {
2769
            return rv;
2770
        }
1045
    }
2771
    }
1046
2772
1047
    return APR_SUCCESS;
2773
    return APR_SUCCESS;
Lines 1056-1061 Link Here
1056
    conf->dirlength = DEFAULT_DIRLENGTH;
2782
    conf->dirlength = DEFAULT_DIRLENGTH;
1057
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
2783
    conf->maxfs = DEFAULT_MAX_FILE_SIZE;
1058
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
2784
    conf->minfs = DEFAULT_MIN_FILE_SIZE;
2785
    conf->updtimeout = DEFAULT_UPDATE_TIMEOUT;
2786
    conf->minbgsize = DEFAULT_MIN_BACKGROUND_SIZE;
1059
2787
1060
    conf->cache_root = NULL;
2788
    conf->cache_root = NULL;
1061
    conf->cache_root_len = 0;
2789
    conf->cache_root_len = 0;
Lines 1097-1102 Link Here
1097
    conf->dirlevels = val;
2825
    conf->dirlevels = val;
1098
    return NULL;
2826
    return NULL;
1099
}
2827
}
2828
1100
static const char
2829
static const char
1101
*set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2830
*set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1102
{
2831
{
Lines 1136-1144 Link Here
1136
    {
2865
    {
1137
        return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
2866
        return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
1138
    }
2867
    }
2868
2869
    return NULL;
2870
}
2871
2872
2873
static const char
2874
*set_cache_updtimeout(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2875
{
2876
    apr_int64_t val;
2877
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2878
                                                 &disk_cache_module);
2879
2880
    if (apr_strtoff(&val, arg, NULL, 0) != APR_SUCCESS || val < 0) 
2881
    {
2882
        return "CacheUpdateTimeout argument must be a non-negative integer representing the timeout in milliseconds for cache update operations";
2883
    }
2884
2885
    conf->updtimeout = val * 1000;
2886
2887
    return NULL;
2888
}
2889
2890
2891
static const char
2892
*set_cache_minbgsize(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2893
{
2894
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2895
                                                 &disk_cache_module);
2896
2897
    if (apr_strtoff(&conf->minbgsize, arg, NULL, 0) != APR_SUCCESS ||
2898
            conf->minbgsize < 0) 
2899
    {
2900
        return "CacheMinBGSize argument must be a non-negative integer representing the min size in bytes for a file to be eligable for background caching";
2901
    }
2902
2903
    return NULL;
2904
}
2905
2906
2907
static const char
2908
*set_cache_removedirs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
2909
{
2910
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
2911
                                                 &disk_cache_module);
2912
2913
    if (strcasecmp(arg, "on") == 0 || strcasecmp(arg, "true")) {
2914
        conf->removedirs = TRUE;
2915
    }
2916
    else if (strcasecmp(arg, "off") == 0 || strcasecmp(arg, "false")) {
2917
        conf->removedirs = FALSE;
2918
    }
2919
    else {
2920
        return "CacheRemoveDirectories argument must be either on, true, off or false";
2921
    }
2922
1139
    return NULL;
2923
    return NULL;
1140
}
2924
}
1141
2925
2926
1142
static const command_rec disk_cache_cmds[] =
2927
static const command_rec disk_cache_cmds[] =
1143
{
2928
{
1144
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
2929
    AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
Lines 1151-1156 Link Here
1151
                  "The minimum file size to cache a document"),
2936
                  "The minimum file size to cache a document"),
1152
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
2937
    AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
1153
                  "The maximum file size to cache a document"),
2938
                  "The maximum file size to cache a document"),
2939
    AP_INIT_TAKE1("CacheUpdateTimeout", set_cache_updtimeout, NULL, RSRC_CONF,
2940
                  "Timeout in ms for cache updates"),
2941
    AP_INIT_TAKE1("CacheMinBGSize", set_cache_minbgsize, NULL, RSRC_CONF,
2942
                  "The minimum file size for background caching"),
2943
    AP_INIT_TAKE1("CacheRemoveDirectories", set_cache_removedirs, NULL, RSRC_CONF,
2944
                  "Should we try to remove directories when we remove expired cache files."),
1154
    {NULL}
2945
    {NULL}
1155
};
2946
};
1156
2947
(-)dist/modules/cache/mod_disk_cache.h (-21 / +131 lines)
Lines 22-33 Link Here
22
 */
22
 */
23
23
24
#define VARY_FORMAT_VERSION 3
24
#define VARY_FORMAT_VERSION 3
25
#define DISK_FORMAT_VERSION 4
25
#define DISK_FORMAT_VERSION_OLD 4
26
#define DISK_FORMAT_VERSION_OLD2 5
27
#define DISK_FORMAT_VERSION_OLD3 7
28
#define DISK_FORMAT_VERSION 8
26
29
27
#define CACHE_HEADER_SUFFIX ".header"
30
#define CACHE_HEADER_SUFFIX ".header"
28
#define CACHE_DATA_SUFFIX   ".data"
31
#define CACHE_BODY_SUFFIX   ".body"
29
#define CACHE_VDIR_SUFFIX   ".vary"
32
#define CACHE_VDIR_SUFFIX   ".vary"
30
33
34
/* Size of buffer used when copying files */
35
#define CACHE_BUF_SIZE 262144
36
37
/* How much the file on disk must have grown beyond the current offset
38
   before diskcache_bucket_read breaks out of the stat/sleep-loop */
39
#define CACHE_BUCKET_MINCHUNK 524288
40
41
/* How long to sleep before retrying while looping (micro-seconds) */
42
#define CACHE_LOOP_MINSLEEP 10000
43
#define CACHE_LOOP_MAXSLEEP 1000000
44
31
#define AP_TEMPFILE_PREFIX "/"
45
#define AP_TEMPFILE_PREFIX "/"
32
#define AP_TEMPFILE_BASE   "aptmp"
46
#define AP_TEMPFILE_BASE   "aptmp"
33
#define AP_TEMPFILE_SUFFIX "XXXXXX"
47
#define AP_TEMPFILE_SUFFIX "XXXXXX"
Lines 35-56 Link Here
35
#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
49
#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
36
#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
50
#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
37
51
52
typedef apr_uint32_t disk_cache_format_t;
53
38
typedef struct {
54
typedef struct {
39
    /* Indicates the format of the header struct stored on-disk. */
40
    apr_uint32_t format;
41
    /* The HTTP status code returned for this response.  */
55
    /* The HTTP status code returned for this response.  */
42
    int status;
56
    apr_int32_t status;
43
    /* The size of the entity name that follows. */
44
    apr_size_t name_len;
45
    /* The number of times we've cached this entity. */
57
    /* The number of times we've cached this entity. */
46
    apr_size_t entity_version;
58
    apr_uint32_t entity_version;
47
    /* Miscellaneous time values. */
59
    /* Miscellaneous time values. */
48
    apr_time_t date;
60
    apr_time_t date;
49
    apr_time_t expire;
61
    apr_time_t expire;
50
    apr_time_t request_time;
62
    apr_time_t request_time;
51
    apr_time_t response_time;
63
    apr_time_t response_time;
64
    apr_time_t lastmod; /* Last-Modified (if present) */
65
66
    /* The body size forced to 64bit to not break when people go from non-LFS
67
     * to LFS builds */
68
    apr_int64_t file_size;
69
70
    /* The size of the entity name that follows. */
71
    apr_uint32_t name_len;
72
    /* The size of the body cache filename */
73
    apr_uint32_t bodyname_len;
74
    /* The size of the filename that follows, to fill in r->filename */
75
    apr_uint32_t filename_len;
76
77
    /* On disk:
78
       * name_len long string of entity name.
79
       * bodyname_len long string of body cache filename (without cacheroot).
80
       * filename_len long string of filename
81
     */
52
} disk_cache_info_t;
82
} disk_cache_info_t;
53
83
84
85
/* Don't expose module-related stuff unless needed */
86
#ifdef AP_FILTER_H
54
/*
87
/*
55
 * disk_cache_object_t
88
 * disk_cache_object_t
56
 * Pointed to by cache_object_t::vobj
89
 * Pointed to by cache_object_t::vobj
Lines 58-75 Link Here
58
typedef struct disk_cache_object {
91
typedef struct disk_cache_object {
59
    const char *root;        /* the location of the cache directory */
92
    const char *root;        /* the location of the cache directory */
60
    apr_size_t root_len;
93
    apr_size_t root_len;
61
    char *tempfile;    /* temp file tohold the content */
94
62
    const char *prefix;
95
    /* Temporary file */
63
    const char *datafile;    /* name of file where the data will go */
96
    apr_file_t *tfd;
64
    const char *hdrsfile;    /* name of file where the hdrs will go */
97
    char *tempfile;
65
    const char *hashfile;    /* Computed hash key for this URI */
98
66
    const char *name;   /* Requested URI without vary bits - suitable for mortals. */
99
    /* Header cache file */
67
    const char *key;    /* On-disk prefix; URI with Vary bits (if present) */
100
    apr_file_t *hfd;
68
    apr_file_t *fd;          /* data file */
101
    const char *hdrsfile;
69
    apr_file_t *hfd;         /* headers file */
102
70
    apr_file_t *tfd;         /* temporary file for data */
103
    /* Body cache file */
71
    apr_off_t file_size;     /*  File size of the cached data file  */
104
    apr_file_t *bfd;
72
    disk_cache_info_t disk_info; /* Header information. */
105
    const char *bodyfile;
106
107
    const char *name;           /* Requested URI without vary bits - 
108
                                   suitable for mortals. */
109
    const char *prefix;         /* Prefix to deal with Vary headers */
110
    char *filename;             /* Filename of requested URL (if present) */
111
    char **rfilename;           /* Pointer to r->filename */
112
113
    apr_off_t initial_size;      /* Size of body as reported upstreams */
114
    apr_off_t file_size;         /* File size of the cached body */
115
116
    apr_time_t lastmod;          /* Last-Modified (if present) */
117
118
    int skipstore;              /* Set if we should skip storing stuff */
119
120
    int removedirs;             /* Set it we should rmdir when doing rm */
121
122
    int header_only;            /* Copy of r->header_only */
123
124
    apr_interval_time_t updtimeout; /* Cache update timeout */
125
126
    disk_cache_info_t disk_info; /* Disk header information. */
73
} disk_cache_object_t;
127
} disk_cache_object_t;
74
128
75
129
Lines 82-95 Link Here
82
#define DEFAULT_DIRLENGTH 2
136
#define DEFAULT_DIRLENGTH 2
83
#define DEFAULT_MIN_FILE_SIZE 1
137
#define DEFAULT_MIN_FILE_SIZE 1
84
#define DEFAULT_MAX_FILE_SIZE 1000000
138
#define DEFAULT_MAX_FILE_SIZE 1000000
139
/* Background caching disabled by default */
140
#define DEFAULT_MIN_BACKGROUND_SIZE DEFAULT_MAX_FILE_SIZE
141
#define DEFAULT_UPDATE_TIMEOUT apr_time_from_sec(10)
85
142
86
typedef struct {
143
typedef struct {
87
    const char* cache_root;
144
    const char* cache_root;
88
    apr_size_t cache_root_len;
145
    apr_size_t cache_root_len;
89
    int dirlevels;               /* Number of levels of subdirectories */
146
    int dirlevels;               /* Number of levels of subdirectories */
90
    int dirlength;               /* Length of subdirectory names */
147
    int dirlength;               /* Length of subdirectory names */
91
    apr_off_t minfs;             /* minimum file size for cached files */
148
    apr_off_t minfs;             /* minumum file size for cached files */
92
    apr_off_t maxfs;             /* maximum file size for cached files */
149
    apr_off_t maxfs;             /* maximum file size for cached files */
150
    apr_off_t minbgsize;         /* minimum file size to do bg caching */
151
    apr_interval_time_t updtimeout;   /* Cache update timeout */
152
    int removedirs;              /* Should we try to remove directories? */
93
} disk_cache_conf;
153
} disk_cache_conf;
94
154
155
typedef struct diskcache_bucket_data diskcache_bucket_data;
156
struct diskcache_bucket_data {
157
    /** Number of buckets using this memory */
158
    apr_bucket_refcount  refcount;
159
    apr_file_t  *fd;
160
    /** The pool into which any needed structures should
161
     *  be created while reading from this file bucket */
162
    apr_pool_t *readpool;
163
    /* Cache update timeout */
164
    apr_interval_time_t updtimeout;
165
    /* Adaptive loop delay timeout */
166
    apr_interval_time_t polldelay;
167
};
168
169
/* Stuff needed by the background copy thread */
170
typedef struct copyinfo copyinfo;
171
struct copyinfo {
172
    apr_off_t len;
173
    /* Source info */
174
    const char *srcfile;
175
    apr_finfo_t srcinfo;
176
    apr_off_t srcoff;
177
    /* Destination info */
178
    const char *destfile;
179
    apr_off_t destoff;
180
181
    /* Cache update timeout */
182
    apr_interval_time_t updtimeout;
183
184
    /* Our private pool */
185
    apr_pool_t *pool;
186
187
#if APR_HAS_THREADS
188
    /* Background process info */
189
    apr_thread_t *t;
190
#endif /* APR_HAS_THREADS */
191
#if APR_HAS_FORK
192
    apr_proc_t *proc;
193
#endif /* APR_HAS_FORK */
194
195
    /* For logging */
196
    const server_rec *s;
197
};
198
199
#define CACHE_ENODATA (APR_OS_START_USERERR+1)
200
#define CACHE_EDECLINED (APR_OS_START_USERERR+2)
201
#define CACHE_EEXIST (APR_OS_START_USERERR+3)
202
203
#endif /* AP_FILTER_H */
204
95
#endif /*MOD_DISK_CACHE_H*/
205
#endif /*MOD_DISK_CACHE_H*/
(-)dist/srclib/apr/file_io/unix/filestat.c (-6 / +5 lines)
Lines 83-94 Link Here
83
    apr_time_ansi_put(&finfo->atime, info->st_atime);
83
    apr_time_ansi_put(&finfo->atime, info->st_atime);
84
    apr_time_ansi_put(&finfo->mtime, info->st_mtime);
84
    apr_time_ansi_put(&finfo->mtime, info->st_mtime);
85
    apr_time_ansi_put(&finfo->ctime, info->st_ctime);
85
    apr_time_ansi_put(&finfo->ctime, info->st_ctime);
86
    /* ### needs to be revisited  
86
    /* FIXME: ACC: Needs a configure-test to make sure st_blocks is present */
87
     * if (wanted & APR_FINFO_CSIZE) {
87
    if (wanted & APR_FINFO_CSIZE) {
88
     *   finfo->csize = info->st_blocks * 512;
88
        finfo->csize = info->st_blocks * 512;
89
     *   finfo->valid |= APR_FINFO_CSIZE;
89
        finfo->valid |= APR_FINFO_CSIZE;
90
     * }
90
    }
91
     */
92
}
91
}
93
92
94
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, 
93
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, 
(-)dist/support/htcacheclean.c (-121 / +47 lines)
Lines 70-79 Link Here
70
typedef struct _direntry {
70
typedef struct _direntry {
71
    APR_RING_ENTRY(_direntry) link;
71
    APR_RING_ENTRY(_direntry) link;
72
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
72
    int type;         /* type of file/fileset: TEMP, HEADER, DATA, HEADERDATA */
73
    apr_time_t htime; /* headers file modification time */
73
    apr_time_t htime; /* file modification time */
74
    apr_time_t dtime; /* body file modification time */
74
    apr_off_t hsize;  /* file size */
75
    apr_off_t hsize;  /* headers file size */
76
    apr_off_t dsize;  /* body or temporary file size */
77
    char *basename;   /* file/fileset base name */
75
    char *basename;   /* file/fileset base name */
78
} DIRENTRY;
76
} DIRENTRY;
79
77
Lines 81-91 Link Here
81
    APR_RING_ENTRY(_entry) link;
79
    APR_RING_ENTRY(_entry) link;
82
    apr_time_t expire;        /* cache entry exiration time */
80
    apr_time_t expire;        /* cache entry exiration time */
83
    apr_time_t response_time; /* cache entry time of last response to client */
81
    apr_time_t response_time; /* cache entry time of last response to client */
84
    apr_time_t htime;         /* headers file modification time */
82
    apr_time_t htime;         /* file modification time */
85
    apr_time_t dtime;         /* body file modification time */
83
    apr_off_t hsize;          /* file size */
86
    apr_off_t hsize;          /* headers file size */
87
    apr_off_t dsize;          /* body or temporary file size */
88
    char *basename;           /* fileset base name */
84
    char *basename;           /* fileset base name */
85
    char *name;               /* entity name */
89
} ENTRY;
86
} ENTRY;
90
87
91
88
Lines 255-264 Link Here
255
    /* temp pool, otherwise lots of memory could be allocated */
252
    /* temp pool, otherwise lots of memory could be allocated */
256
    apr_pool_create(&p, pool);
253
    apr_pool_create(&p, pool);
257
254
258
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_HEADER_SUFFIX, NULL);
255
    nextpath = apr_pstrcat(p, path, "/", basename, NULL);
259
    apr_file_remove(nextpath, p);
260
261
    nextpath = apr_pstrcat(p, path, "/", basename, CACHE_DATA_SUFFIX, NULL);
262
    apr_file_remove(nextpath, p);
256
    apr_file_remove(nextpath, p);
263
257
264
    apr_pool_destroy(p);
258
    apr_pool_destroy(p);
Lines 286-292 Link Here
286
    apr_finfo_t info;
280
    apr_finfo_t info;
287
    apr_size_t len;
281
    apr_size_t len;
288
    apr_time_t current, deviation;
282
    apr_time_t current, deviation;
289
    char *nextpath, *base, *ext, *orig_basename;
283
    char *nextpath, *base, *orig_basename;
290
    APR_RING_ENTRY(_direntry) anchor;
284
    APR_RING_ENTRY(_direntry) anchor;
291
    DIRENTRY *d, *t, *n;
285
    DIRENTRY *d, *t, *n;
292
    ENTRY *e;
286
    ENTRY *e;
Lines 329-340 Link Here
329
        if (!base++) {
323
        if (!base++) {
330
            base = d->basename;
324
            base = d->basename;
331
        }
325
        }
332
        ext = strchr(base, '.');
333
326
334
        /* there may be temporary files which may be gone before
327
        /* there may be temporary files which may be gone before
335
         * processing, always skip these if not in realclean mode
328
         * processing, always skip these if not in realclean mode
336
         */
329
         */
337
        if (!ext && !realclean) {
330
        if (!realclean) {
338
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
331
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
339
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
332
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
340
                continue;
333
                continue;
Lines 386-436 Link Here
386
            continue;
379
            continue;
387
        }
380
        }
388
381
389
        if (!ext) {
382
        if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
390
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
391
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
383
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
392
                d->basename += skip;
393
                d->type = TEMP;
394
                d->dsize = info.size;
395
                apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
396
            }
397
            continue;
398
        }
399
400
        if (!strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
401
            *ext = '\0';
402
            d->basename += skip;
384
            d->basename += skip;
403
            /* if a user manually creates a '.header' file */
385
            d->type = TEMP;
404
            if (d->basename[0] == '\0') {
405
                continue;
406
            }
407
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
408
            if (t) {
409
                d = t;
410
            }
411
            d->type |= HEADER;
412
            d->htime = info.mtime;
413
            d->hsize = info.size;
386
            d->hsize = info.size;
414
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
387
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
415
            continue;
388
            continue;
416
        }
389
        }
417
390
418
        if (!strcasecmp(ext, CACHE_DATA_SUFFIX)) {
391
        /* Assume that everything else are cachefiles */
419
            *ext = '\0';
392
        d->basename += skip;
420
            d->basename += skip;
393
        t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
421
            /* if a user manually creates a '.data' file */
394
        if (t) {
422
            if (d->basename[0] == '\0') {
395
            d = t;
423
                continue;
396
        }
424
            }
397
        d->type |= HEADERDATA;
425
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
398
        d->htime = info.mtime;
426
            if (t) {
399
        d->hsize = info.size;
427
                d = t;
400
        apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
428
            }
429
            d->type |= DATA;
430
            d->dtime = info.mtime;
431
            d->dsize = info.size;
432
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
433
        }
434
    }
401
    }
435
402
436
    if (interrupted) {
403
    if (interrupted) {
Lines 448-455 Link Here
448
415
449
        switch(d->type) {
416
        switch(d->type) {
450
        case HEADERDATA:
417
        case HEADERDATA:
451
            nextpath = apr_pstrcat(p, path, "/", d->basename,
418
            nextpath = apr_pstrcat(p, path, "/", d->basename, NULL);
452
                                   CACHE_HEADER_SUFFIX, NULL);
453
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
419
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
454
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
420
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
455
                len = sizeof(format);
421
                len = sizeof(format);
Lines 464-481 Link Here
464
430
465
                        if (apr_file_read_full(fd, &disk_info, len,
431
                        if (apr_file_read_full(fd, &disk_info, len,
466
                                               &len) == APR_SUCCESS) {
432
                                               &len) == APR_SUCCESS) {
467
                            apr_file_close(fd);
468
                            e = apr_palloc(pool, sizeof(ENTRY));
433
                            e = apr_palloc(pool, sizeof(ENTRY));
469
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
434
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
470
                            e->expire = disk_info.expire;
435
                            e->expire = disk_info.expire;
471
                            e->response_time = disk_info.response_time;
436
                            e->response_time = disk_info.response_time;
472
                            e->htime = d->htime;
437
                            e->htime = d->htime;
473
                            e->dtime = d->dtime;
474
                            e->hsize = d->hsize;
438
                            e->hsize = d->hsize;
475
                            e->dsize = d->dsize;
476
                            e->basename = apr_palloc(pool,
439
                            e->basename = apr_palloc(pool,
477
                                                     strlen(d->basename) + 1);
440
                                                     strlen(d->basename) + 1);
478
                            strcpy(e->basename, d->basename);
441
                            strcpy(e->basename, d->basename);
442
                            e->name = apr_palloc(pool, disk_info.name_len+1);
443
                            if(apr_file_read_full(fd, e->name,
444
                                                  disk_info.name_len, NULL)
445
                                    == APR_SUCCESS)
446
                            {
447
                                e->name[disk_info.name_len] = '\0';
448
                            }
449
                            else {
450
                                e->name = "UNKNOWN";
451
                            }
452
                            apr_file_close(fd);
479
                            break;
453
                            break;
480
                        }
454
                        }
481
                        else {
455
                        else {
Lines 484-494 Link Here
484
                    }
458
                    }
485
                    else if (format == VARY_FORMAT_VERSION) {
459
                    else if (format == VARY_FORMAT_VERSION) {
486
                        /* This must be a URL that added Vary headers later,
460
                        /* This must be a URL that added Vary headers later,
487
                         * so kill the orphaned .data file
461
                         * so kill the orphaned cachefile
488
                         */
462
                         */
489
                        apr_file_close(fd);
463
                        apr_file_close(fd);
490
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
464
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
491
                                                    CACHE_DATA_SUFFIX, NULL),
465
                                                    NULL),
492
                                        p);
466
                                        p);
493
                    }
467
                    }
494
                }
468
                }
Lines 509-564 Link Here
509
                || d->htime > current + deviation) {
483
                || d->htime > current + deviation) {
510
                delete_entry(path, d->basename, p);
484
                delete_entry(path, d->basename, p);
511
                unsolicited += d->hsize;
485
                unsolicited += d->hsize;
512
                unsolicited += d->dsize;
513
            }
514
            break;
515
516
        /* single data and header files may be deleted either in realclean
517
         * mode or if their modification timestamp is not within a
518
         * specified positive or negative offset to the current time.
519
         * this handling is necessary due to possible race conditions
520
         * between apache and this process
521
         */
522
        case HEADER:
523
            current = apr_time_now();
524
            nextpath = apr_pstrcat(p, path, "/", d->basename,
525
                                   CACHE_HEADER_SUFFIX, NULL);
526
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
527
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
528
                len = sizeof(format);
529
                if (apr_file_read_full(fd, &format, len,
530
                                       &len) == APR_SUCCESS) {
531
                    if (format == VARY_FORMAT_VERSION) {
532
                        apr_time_t expires;
533
534
                        len = sizeof(expires);
535
536
                        apr_file_read_full(fd, &expires, len, &len);
537
538
                        apr_file_close(fd);
539
540
                        if (expires < current) {
541
                            delete_entry(path, d->basename, p);
542
                        }
543
                        break;
544
                    }
545
                }
546
                apr_file_close(fd);
547
            }
548
549
            if (realclean || d->htime < current - deviation
550
                || d->htime > current + deviation) {
551
                delete_entry(path, d->basename, p);
552
                unsolicited += d->hsize;
553
            }
554
            break;
555
556
        case DATA:
557
            current = apr_time_now();
558
            if (realclean || d->dtime < current - deviation
559
                || d->dtime > current + deviation) {
560
                delete_entry(path, d->basename, p);
561
                unsolicited += d->dsize;
562
            }
486
            }
563
            break;
487
            break;
564
488
Lines 567-573 Link Here
567
         */
491
         */
568
        case TEMP:
492
        case TEMP:
569
            delete_file(path, d->basename, p);
493
            delete_file(path, d->basename, p);
570
            unsolicited += d->dsize;
494
            unsolicited += d->hsize;
571
            break;
495
            break;
572
        }
496
        }
573
    }
497
    }
Lines 604-610 Link Here
604
         e != APR_RING_SENTINEL(&root, _entry, link);
528
         e != APR_RING_SENTINEL(&root, _entry, link);
605
         e = APR_RING_NEXT(e, link)) {
529
         e = APR_RING_NEXT(e, link)) {
606
        sum += e->hsize;
530
        sum += e->hsize;
607
        sum += e->dsize;
608
        entries++;
531
        entries++;
609
    }
532
    }
610
533
Lines 623-632 Link Here
623
    for (e = APR_RING_FIRST(&root);
546
    for (e = APR_RING_FIRST(&root);
624
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
547
         e != APR_RING_SENTINEL(&root, _entry, link) && !interrupted;) {
625
        n = APR_RING_NEXT(e, link);
548
        n = APR_RING_NEXT(e, link);
626
        if (e->response_time > now || e->htime > now || e->dtime > now) {
549
        if (e->response_time > now || e->htime > now ) {
627
            delete_entry(path, e->basename, pool);
550
            delete_entry(path, e->basename, pool);
628
            sum -= e->hsize;
551
            sum -= e->hsize;
629
            sum -= e->dsize;
630
            entries--;
552
            entries--;
631
            APR_RING_REMOVE(e, link);
553
            APR_RING_REMOVE(e, link);
632
            if (sum <= max) {
554
            if (sum <= max) {
Lines 649-656 Link Here
649
        n = APR_RING_NEXT(e, link);
571
        n = APR_RING_NEXT(e, link);
650
        if (e->expire != APR_DATE_BAD && e->expire < now) {
572
        if (e->expire != APR_DATE_BAD && e->expire < now) {
651
            delete_entry(path, e->basename, pool);
573
            delete_entry(path, e->basename, pool);
574
            if(verbose > 1) {
575
                apr_file_printf(errfile, "Expired: %s\n", e->name);
576
            }
652
            sum -= e->hsize;
577
            sum -= e->hsize;
653
            sum -= e->dsize;
654
            entries--;
578
            entries--;
655
            APR_RING_REMOVE(e, link);
579
            APR_RING_REMOVE(e, link);
656
            if (sum <= max) {
580
            if (sum <= max) {
Lines 678-691 Link Here
678
        for (e = APR_RING_NEXT(oldest, link);
602
        for (e = APR_RING_NEXT(oldest, link);
679
             e != APR_RING_SENTINEL(&root, _entry, link);
603
             e != APR_RING_SENTINEL(&root, _entry, link);
680
             e = APR_RING_NEXT(e, link)) {
604
             e = APR_RING_NEXT(e, link)) {
681
            if (e->dtime < oldest->dtime) {
605
            if (e->htime < oldest->htime) {
682
                oldest = e;
606
                oldest = e;
683
            }
607
            }
684
        }
608
        }
685
609
686
        delete_entry(path, oldest->basename, pool);
610
        delete_entry(path, oldest->basename, pool);
611
        if(verbose > 1) {
612
            apr_file_printf(errfile, "Old: (%d s) "
613
                            "(%" APR_OFF_T_FMT " b) %s\n", 
614
                            (int) apr_time_sec(apr_time_now() - oldest->htime),
615
                            oldest->hsize, oldest->name);
616
        }
687
        sum -= oldest->hsize;
617
        sum -= oldest->hsize;
688
        sum -= oldest->dsize;
689
        entries--;
618
        entries--;
690
        APR_RING_REMOVE(oldest, link);
619
        APR_RING_REMOVE(oldest, link);
691
    }
620
    }
Lines 833-842 Link Here
833
                break;
762
                break;
834
763
835
            case 'v':
764
            case 'v':
836
                if (verbose) {
765
                verbose++;
837
                    usage();
838
                }
839
                verbose = 1;
840
                break;
766
                break;
841
767
842
            case 'r':
768
            case 'r':

Return to bug 39380