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 |
|