1 /* ====================================================================
2 * The Apache Software License, Version 1.1
3 *
4 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
5 * reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
25 *
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
30 *
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
34 *
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
53 *
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
57 */
58
59 /* Cache and garbage collection routines for Apache proxy */
60
61 #include "mod_proxy.h"
62 #include "http_conf_globals.h"
63 #include "http_log.h"
64 #include "http_main.h"
65 #include "http_core.h"
66 #include "util_date.h"
67 #include <utime.h>
68 #include "multithread.h"
69 #include "ap_md5.h"
70
71 struct gc_ent {
72 unsigned long int len;
73 time_t expire;
74 char file[HASH_LEN + 1];
75 };
76
77 /* Poor man's 61 bit arithmetic */
78 typedef struct {
79 long lower; /* lower 30 bits of result */
80 long upper; /* upper 31 bits of result */
81 } long61_t;
82
83 /* FIXME: The block size can be different on a `per file system' base.
84 * This would make automatic detection highly OS specific.
85 * In the GNU fileutils code for du(1), you can see how complicated it can
86 * become to detect the block size. And, with BSD-4.x fragments, it
87 * it even more difficult to get precise results.
88 * As a compromise (and to improve on the incorrect counting of cache
89 * size on byte level, omitting directory sizes entirely, which was
90 * used up to apache-1.3b7) we're rounding to multiples of 512 here.
91 * Your file system may be using larger blocks (I certainly hope so!)
92 * but it will hardly use smaller blocks.
93 * (So this approximation is still closer to reality than the old behavior).
94 * The best solution would be automatic detection, the next best solution
95 * IMHO is a sensible default and the possibility to override it.
96 */
97
98 #define ROUNDUP2BLOCKS(_bytes) (((_bytes)+block_size-1) & ~(block_size-1))
99 static long block_size = 512; /* this must be a power of 2 */
100 static long61_t curbytes, cachesize;
101 static time_t garbage_now, garbage_expire;
102 static mutex *garbage_mutex = NULL;
103
104
ap_proxy_garbage_init(server_rec * r,pool * p)105 int ap_proxy_garbage_init(server_rec *r, pool *p)
106 {
107 if (!garbage_mutex)
108 garbage_mutex = ap_create_mutex(NULL);
109
110 return (0);
111 }
112
113
114 static int sub_garbage_coll(request_rec *r, array_header *files,
115 const char *cachedir, const char *cachesubdir);
116 static void help_proxy_garbage_coll(request_rec *r);
117 static int should_proxy_garbage_coll(request_rec *r);
118 static void detached_proxy_garbage_coll(request_rec *r);
119
120
ap_proxy_garbage_coll(request_rec * r)121 void ap_proxy_garbage_coll(request_rec *r)
122 {
123 static int inside = 0;
124
125 (void)ap_acquire_mutex(garbage_mutex);
126 if (inside == 1) {
127 (void)ap_release_mutex(garbage_mutex);
128 return;
129 }
130 else
131 inside = 1;
132 (void)ap_release_mutex(garbage_mutex);
133
134 ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */
135 if (should_proxy_garbage_coll(r))
136 detached_proxy_garbage_coll(r);
137 ap_unblock_alarms();
138
139 (void)ap_acquire_mutex(garbage_mutex);
140 inside = 0;
141 (void)ap_release_mutex(garbage_mutex);
142 }
143
144
add_long61(long61_t * accu,long val)145 static void add_long61(long61_t *accu, long val)
146 {
147 /* Add in lower 30 bits */
148 accu->lower += (val & 0x3FFFFFFFL);
149 /* add in upper bits, and carry */
150 accu->upper += (val >> 30) + ((accu->lower & ~0x3FFFFFFFL) != 0L);
151 /* Clear carry */
152 accu->lower &= 0x3FFFFFFFL;
153 }
154
sub_long61(long61_t * accu,long val)155 static void sub_long61(long61_t *accu, long val)
156 {
157 int carry = (val & 0x3FFFFFFFL) > accu->lower;
158 /* Subtract lower 30 bits */
159 accu->lower = accu->lower - (val & 0x3FFFFFFFL) + ((carry) ? 0x40000000 : 0);
160 /* add in upper bits, and carry */
161 accu->upper -= (val >> 30) + carry;
162 }
163
164 /* Compare two long61's:
165 * return <0 when left < right
166 * return 0 when left == right
167 * return >0 when left > right
168 */
cmp_long61(long61_t * left,long61_t * right)169 static long cmp_long61(long61_t *left, long61_t *right)
170 {
171 return (left->upper == right->upper) ? (left->lower - right->lower)
172 : (left->upper - right->upper);
173 }
174
175 /* Compare two gc_ent's, sort them by expiration date */
gcdiff(const void * ap,const void * bp)176 static int gcdiff(const void *ap, const void *bp)
177 {
178 const struct gc_ent *a = (const struct gc_ent *) ap;
179 const struct gc_ent *b = (const struct gc_ent *) bp;
180
181 if (a->expire > b->expire)
182 return 1;
183 else if (a->expire < b->expire)
184 return -1;
185 else
186 return 0;
187 }
188
detached_proxy_garbage_coll(request_rec * r)189 static void detached_proxy_garbage_coll(request_rec *r)
190 {
191 pid_t pid;
192 int status;
193 pid_t pgrp;
194
195 switch (pid = fork()) {
196 case -1:
197 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
198 "proxy: fork() for cache cleanup failed");
199 return;
200
201 case 0: /* Child */
202
203 /* close all sorts of things, including the socket fd */
204 ap_cleanup_for_exec();
205
206 /* Fork twice to disassociate from the child */
207 switch (pid = fork()) {
208 case -1:
209 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
210 "proxy: fork(2nd) for cache cleanup failed");
211 exit(1);
212
213 case 0: /* Child */
214 /* The setpgrp() stuff was snarfed from http_main.c */
215 if ((pgrp = setsid()) == -1) {
216 perror("setsid");
217 fprintf(stderr, "%s: setsid failed\n",
218 ap_server_argv0);
219 exit(1);
220 }
221 help_proxy_garbage_coll(r);
222 exit(0);
223
224 default: /* Father */
225 /* After grandson has been forked off, */
226 /* there's nothing else to do. */
227 exit(0);
228 }
229 default:
230 /* Wait until grandson has been forked off */
231 /* (without wait we'd leave a zombie) */
232 waitpid(pid, &status, 0);
233 return;
234 }
235 }
236
237 #define DOT_TIME "/.time" /* marker */
238
should_proxy_garbage_coll(request_rec * r)239 static int should_proxy_garbage_coll(request_rec *r)
240 {
241 void *sconf = r->server->module_config;
242 proxy_server_conf *pconf =
243 (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
244 const struct cache_conf *conf = &pconf->cache;
245
246 const char *cachedir = conf->root;
247 char *filename;
248 size_t fnlen;
249 struct stat buf;
250 int timefd;
251 time_t every = conf->gcinterval;
252 static time_t lastcheck = BAD_DATE; /* static (per-process) data!!! */
253
254 if (cachedir == NULL || every == -1)
255 return 0;
256
257 fnlen = strlen(cachedir) + strlen(DOT_TIME) + 1;
258 filename = ap_palloc(r->pool, fnlen);
259
260 garbage_now = time(NULL);
261 /*
262 * Usually, the modification time of <cachedir>/.time can only increase.
263 * Thus, even with several child processes having their own copy of
264 * lastcheck, if time(NULL) still < lastcheck then it's not time for GC
265 * yet.
266 */
267 if (garbage_now != -1 && lastcheck != BAD_DATE && garbage_now < lastcheck + every)
268 return 0;
269
270 strlcpy(filename, cachedir, fnlen);
271 strlcat(filename, DOT_TIME, fnlen);
272
273 /*
274 * At this point we have a bit of an engineering compromise. We could
275 * either create and/or mark the .time file (prior to the fork which
276 * might fail on a resource issue) or wait until we are safely forked.
277 * The advantage of doing it now in this process is that we get some
278 * usefull live out of the global last check variable. (XXX which should
279 * go scoreboard IMHO.) Note that the actual counting is at a later
280 * moment.
281 */
282 if (stat(filename, &buf) == -1) { /* does not exist */
283 if (errno != ENOENT) {
284 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
285 "proxy: stat(%s)", filename);
286 return 0;
287 }
288 if ((timefd = creat(filename, 0666)) == -1) {
289 if (errno != EEXIST)
290 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
291 "proxy: creat(%s)", filename);
292 else
293 lastcheck = garbage_now; /* someone else got in there */
294 return 0;
295 }
296 close(timefd);
297 }
298 else {
299 lastcheck = buf.st_mtime; /* save the time */
300 if (garbage_now < lastcheck + every) {
301 return 0;
302 }
303 if (utime(filename, NULL) == -1)
304 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
305 "proxy: utimes(%s)", filename);
306 }
307
308 return 1;
309 }
310
help_proxy_garbage_coll(request_rec * r)311 static void help_proxy_garbage_coll(request_rec *r)
312 {
313 const char *cachedir;
314 void *sconf = r->server->module_config;
315 proxy_server_conf *pconf =
316 (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
317 const struct cache_conf *conf = &pconf->cache;
318 array_header *files;
319 struct gc_ent *fent;
320 char *filename;
321 int i;
322
323 cachedir = conf->root;
324 filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2);
325 /* configured size is given in kB. Make it bytes, convert to long61_t: */
326 cachesize.lower = cachesize.upper = 0;
327 add_long61(&cachesize, conf->space << 10);
328
329 ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */
330
331 files = ap_make_array(r->pool, 100, sizeof(struct gc_ent));
332 curbytes.upper = curbytes.lower = 0L;
333
334 sub_garbage_coll(r, files, cachedir, "/");
335
336 if (cmp_long61(&curbytes, &cachesize) < 0L) {
337 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server,
338 "proxy GC: Cache is %ld%% full (nothing deleted)",
339 (long)(((curbytes.upper << 20) | (curbytes.lower >> 10)) * 100 / conf->space));
340 ap_unblock_alarms();
341 return;
342 }
343
344 /* sort the files we found by expiration date */
345 qsort(files->elts, files->nelts, sizeof(struct gc_ent), gcdiff);
346
347 for (i = 0; i < files->nelts; i++) {
348 fent = &((struct gc_ent *) files->elts)[i];
349 snprintf(filename, sizeof(fent->file), "%s%s", cachedir, fent->file);
350 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now);
351 #if TESTING
352 fprintf(stderr, "Would unlink %s\n", filename);
353 #else
354 if (unlink(filename) == -1) {
355 if (errno != ENOENT)
356 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
357 "proxy gc: unlink(%s)", filename);
358 }
359 else
360 #endif
361 {
362 sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len));
363 if (cmp_long61(&curbytes, &cachesize) < 0)
364 break;
365 }
366 }
367
368 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server,
369 "proxy GC: Cache is %ld%% full (%d deleted)",
370 (long)(((curbytes.upper << 20) | (curbytes.lower >> 10)) * 100 / conf->space), i);
371 ap_unblock_alarms();
372 }
373
sub_garbage_coll(request_rec * r,array_header * files,const char * cachebasedir,const char * cachesubdir)374 static int sub_garbage_coll(request_rec *r, array_header *files,
375 const char *cachebasedir, const char *cachesubdir)
376 {
377 char line[17 * (3)];
378 char cachedir[HUGE_STRING_LEN];
379 struct stat buf;
380 int fd, i;
381 DIR *dir;
382 struct dirent *ent;
383 struct gc_ent *fent;
384 int nfiles = 0;
385 char *filename;
386 size_t fnlen;
387
388 snprintf(cachedir, sizeof(cachedir), "%s%s", cachebasedir, cachesubdir);
389 fnlen = strlen(cachedir) + HASH_LEN + 2;
390 filename = ap_palloc(r->pool, fnlen);
391 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "GC Examining directory %s", cachedir);
392 dir = opendir(cachedir);
393 if (dir == NULL) {
394 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
395 "proxy gc: opendir(%s)", cachedir);
396 return 0;
397 }
398
399 while ((ent = readdir(dir)) != NULL) {
400 if (ent->d_name[0] == '.')
401 continue;
402 snprintf(filename, fnlen, "%s%s", cachedir, ent->d_name);
403 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "GC Examining file %s", filename);
404 /* is it a temporary file? */
405 if (strncmp(ent->d_name, "tmp", 3) == 0) {
406 /* then stat it to see how old it is; delete temporary files > 1 day old */
407 if (stat(filename, &buf) == -1) {
408 if (errno != ENOENT)
409 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
410 "proxy gc: stat(%s)", filename);
411 }
412 else if (garbage_now != -1 && buf.st_atime < garbage_now - SEC_ONE_DAY &&
413 buf.st_mtime < garbage_now - SEC_ONE_DAY) {
414 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "GC unlink %s", filename);
415 ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, r->server,
416 "proxy gc: deleting orphaned cache file %s", filename);
417 #if TESTING
418 fprintf(stderr, "Would unlink %s\n", filename);
419 #else
420 unlink(filename);
421 #endif
422 }
423 continue;
424 }
425 ++nfiles;
426 /* is it another file? */
427 /* FIXME: Shouldn't any unexpected files be deleted? */
428 /* if (strlen(ent->d_name) != HASH_LEN) continue; */
429
430 /* read the file */
431 fd = open(filename, O_RDONLY | O_BINARY);
432 if (fd == -1) {
433 if (errno != ENOENT)
434 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
435 "proxy gc: open(%s)", filename);
436 continue;
437 }
438 if (fstat(fd, &buf) == -1) {
439 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
440 "proxy gc: fstat(%s)", filename);
441 close(fd);
442 continue;
443 }
444
445 if (S_ISDIR(buf.st_mode)) {
446 char newcachedir[HUGE_STRING_LEN];
447 close(fd);
448 snprintf(newcachedir, sizeof(newcachedir),
449 "%s%s/", cachesubdir, ent->d_name);
450 if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
451 snprintf(newcachedir, sizeof(newcachedir),
452 "%s%s", cachedir, ent->d_name);
453 #if TESTING
454 fprintf(stderr, "Would remove directory %s\n", newcachedir);
455 #else
456 rmdir(newcachedir);
457 #endif
458 --nfiles;
459 }
460 else {
461 /* Directory is not empty. Account for its size: */
462 add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
463 }
464 continue;
465 }
466
467
468 i = read(fd, line, 17 * (3) - 1);
469 close(fd);
470 if (i == -1) {
471 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
472 "proxy gc: read(%s)", filename);
473 continue;
474 }
475 line[i] = '\0';
476 garbage_expire = ap_proxy_hex2sec(line + 17 * (2));
477 if (!ap_checkmask(line, "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&") ||
478 garbage_expire == BAD_DATE) {
479 /* bad file */
480 if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY &&
481 buf.st_mtime > garbage_now + SEC_ONE_DAY) {
482 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, r->server,
483 "proxy: deleting bad cache file with future date: %s", filename);
484 #if TESTING
485 fprintf(stderr, "Would unlink bad file %s\n", filename);
486 #else
487 unlink(filename);
488 #endif
489 }
490 continue;
491 }
492
493 /*
494 * we need to calculate an 'old' factor, and remove the 'oldest' files
495 * so that the space requirement is met; sort by the expires date of the
496 * file.
497 *
498 */
499 fent = (struct gc_ent *) ap_push_array(files);
500 fent->len = buf.st_size;
501 fent->expire = garbage_expire;
502 strlcpy(fent->file, cachesubdir, sizeof(fent->file));
503 strlcat(fent->file, ent->d_name, sizeof(fent->file));
504
505 /* accumulate in blocks, to cope with directories > 4Gb */
506 add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
507 }
508
509 closedir(dir);
510
511 return nfiles;
512
513 }
514
515
516 /*
517 * Read a cache file;
518 * returns 1 on success,
519 * 0 on failure (bad file or wrong URL)
520 * -1 on UNIX error
521 *
522 * We read the cache hex header, then the message response line and
523 * response headers, and finally we return with the filepointer
524 * pointing at the start of the message body itself, ready to be
525 * shipped to the client later on, if appropriate.
526 */
rdcache(request_rec * r,BUFF * cachefp,cache_req * c)527 static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c)
528 {
529 char urlbuff[HUGE_STRING_LEN], *strp;
530 int len;
531
532 /* read the data from the cache file */
533
534 /*
535 * Format:
536 *
537 * The cache needs to keep track of the following information: - Date,
538 * LastMod, Version, ReqTime, RespTime, ContentLength - The original
539 * request headers (for Vary) - The original response headers (for
540 * returning with a cached response) - The body of the message
541 *
542 * date SP lastmod SP expire SP count SP request-time SP response-time SP
543 * content-lengthCRLF (dates are stored as hex seconds since 1970)
544 * Original URLCRLF Original Request Headers CRLF Original Response
545 * Headers CRLF Body
546 *
547 */
548
549 /* retrieve cachefile information values */
550 len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
551 if (len == -1) {
552 /* Delete broken cache file */
553 unlink(c->filename);
554 return -1;
555 }
556 if (len == 0 || urlbuff[len - 1] != '\n')
557 return 0;
558 urlbuff[len - 1] = '\0';
559
560 if (!ap_checkmask(urlbuff,
561 "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&"))
562 return 0;
563
564 c->date = ap_proxy_hex2sec(urlbuff + 17 * (0));
565 c->lmod = ap_proxy_hex2sec(urlbuff + 17 * (1));
566 c->expire = ap_proxy_hex2sec(urlbuff + 17 * (2));
567 c->version = ap_proxy_hex2sec(urlbuff + 17 * (3));
568 c->req_time = ap_proxy_hex2sec(urlbuff + 17 * (4));
569 c->resp_time = ap_proxy_hex2sec(urlbuff + 17 * (5));
570 c->len = ap_proxy_hex2sec(urlbuff + 17 * (6));
571
572 /* check that we have the same URL */
573 len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
574 if (len == -1) {
575 /* Delete broken cache file */
576 unlink(c->filename);
577 return -1;
578 }
579 if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 ||
580 urlbuff[len - 1] != '\n')
581 return 0;
582 urlbuff[len - 1] = '\0';
583 if (strcmp(urlbuff + 7, c->url) != 0)
584 return 0;
585
586 /* then the original request headers */
587 c->req_hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
588 if (c->req_hdrs == NULL) {
589 /* Delete broken cache file */
590 unlink(c->filename);
591 return -1;
592 }
593
594 /* then the original response headers */
595 len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
596 if (len == -1) {
597 /* Delete broken cache file */
598 unlink(c->filename);
599 return -1;
600 }
601 if (len == 0 || urlbuff[len - 1] != '\n')
602 return 0;
603 urlbuff[--len] = '\0';
604
605 c->resp_line = ap_pstrdup(r->pool, urlbuff);
606 strp = strchr(urlbuff, ' ');
607 if (strp == NULL)
608 return 0;
609
610 c->status = atoi(strp);
611 c->hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
612 if (c->hdrs == NULL) {
613 /* Delete broken cache file */
614 unlink(c->filename);
615 return -1;
616 }
617 if (c->len != -1) /* add a content-length header */
618 if (ap_table_get(c->hdrs, "Content-Length") == NULL) {
619 ap_table_set(c->hdrs, "Content-Length",
620 ap_psprintf(r->pool, "%lu", (unsigned long)c->len));
621 }
622
623
624 return 1;
625 }
626
627 /*
628 * Call this to check the possible conditional status of
629 * the client request, and return the response from the cache
630 *
631 * Conditionals include If-Modified-Since, If-Match, If-Unmodified-Since
632 * and If-None-Match.
633 *
634 * We don't yet understand If-Range, but we will...
635 */
ap_proxy_cache_conditional(request_rec * r,cache_req * c,BUFF * cachefp)636 int ap_proxy_cache_conditional(request_rec *r, cache_req *c, BUFF *cachefp)
637 {
638 const char *etag, *wetag = NULL;
639
640 /* get etag */
641 if ((etag = ap_table_get(c->hdrs, "Etag"))) {
642 wetag = ap_pstrcat(r->pool, "W/", etag, NULL);
643 }
644
645 /* check for If-Match, If-Unmodified-Since */
646 while (1) {
647
648 /*
649 * check If-Match and If-Unmodified-Since exist
650 *
651 * If neither of these exist, the request is not conditional, and we
652 * serve it normally
653 */
654 if (!c->im && BAD_DATE == c->ius) {
655 break;
656 }
657
658 /*
659 * check If-Match
660 *
661 * we check if the Etag on the cached file is in the list of Etags in
662 * the If-Match field. The comparison must be a strong comparison, so
663 * the Etag cannot be marked as weak. If the comparison fails we
664 * return 412 Precondition Failed.
665 *
666 * if If-Match is specified AND If-Match is not a "*" AND Etag is
667 * missing or weak or not in the list THEN return 412 Precondition
668 * Failed
669 */
670
671 if (c->im) {
672 if (strcmp(c->im, "*") &&
673 (!etag || (strlen(etag) > 1 && 'W' == etag[0] && '/' == etag[1]) || !ap_proxy_liststr(c->im, etag, NULL))) {
674 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-Match specified, and it didn't - return 412");
675 }
676 else {
677 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-Match specified, and it matched");
678 break;
679 }
680 }
681
682 /*
683 * check If-Unmodified-Since
684 *
685 * if If-Unmodified-Since is specified AND Last-Modified is specified
686 * somewhere AND If-Unmodified-Since is in the past compared to
687 * Last-Modified THEN return 412 Precondition Failed
688 */
689 if (BAD_DATE != c->ius && BAD_DATE != c->lmod) {
690 if (c->ius < c->lmod) {
691 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-Unmodified-Since specified, but it wasn't - return 412");
692 }
693 else {
694 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-Unmodified-Since specified, and it was unmodified");
695 break;
696 }
697 }
698
699 /* if cache file is being updated */
700 if (c->origfp) {
701 ap_proxy_write_headers(c, c->resp_line, c->hdrs);
702 ap_proxy_send_fb(c->origfp, r, c, c->len, 1, 0, IOBUFSIZE);
703 ap_proxy_cache_tidy(c);
704 }
705 else
706 ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
707
708 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Use your cached copy, conditional precondition failed.");
709 return HTTP_PRECONDITION_FAILED;
710 }
711
712
713 /* check for If-None-Match, If-Modified-Since */
714 while (1) {
715
716 /*
717 * check for existance of If-None-Match and If-Modified-Since
718 *
719 * if neither of these headers have been set, then the request is not
720 * conditional, and we just send the cached response and be done with
721 * it.
722 */
723 if (!c->inm && BAD_DATE == c->ims) {
724 break;
725 }
726
727 /*
728 * check If-None-Match
729 *
730 * we check if the Etag on the cached file is in the list of Etags in
731 * the If-None-Match field. The comparison must be a strong
732 * comparison, so the Etag cannot be marked as weak. If the
733 * comparison fails we return 412 Precondition Failed.
734 *
735 * if If-None-Match is specified: if If-None-Match is a "*" THEN 304
736 * else if Etag is specified AND we get a match THEN 304 else if Weak
737 * Etag is specified AND we get a match THEN 304 else sent the
738 * original object
739 */
740 if (c->inm) {
741 if (!strcmp(c->inm, "*")) {
742 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-None-Match: * specified, return 304");
743 }
744 else if (etag && ap_proxy_liststr(c->inm, etag, NULL)) {
745 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-None-Match: specified and we got a strong match - return 304");
746 }
747 else if (wetag && ap_proxy_liststr(c->inm, wetag, NULL)) {
748 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-None-Match specified, and we got a weak match - return 304");
749 }
750 else
751 break;
752 }
753
754 /*
755 * check If-Modified-Since
756 *
757 * if If-Modified-Since is specified AND Last-Modified is specified
758 * somewhere: if last modification date is earlier than
759 * If-Modified-Since THEN 304 else send the original object
760 */
761 if (BAD_DATE != c->ims && BAD_DATE != c->lmod) {
762 if (c->ims >= c->lmod) {
763 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "If-Modified-Since specified and not modified, try return 304");
764 }
765 else
766 break;
767 }
768
769
770 /* are we updating the cache file? */
771 if (c->origfp) {
772 ap_proxy_write_headers(c, c->resp_line, c->hdrs);
773 ap_proxy_send_fb(c->origfp, r, c, c->len, 1, 0, IOBUFSIZE);
774 ap_proxy_cache_tidy(c);
775 }
776 else
777 ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
778
779 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Use local copy, cached file hasn't changed");
780 return HTTP_NOT_MODIFIED;
781 }
782
783
784 /* No conditional - just send it cousin! */
785 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Local copy modified, send it");
786 r->status_line = strchr(c->resp_line, ' ') + 1;
787 r->status = c->status;
788
789 /* Prepare and send headers to client */
790 ap_proxy_table_replace(r->headers_out, c->hdrs);
791 /* make sure our X-Cache header does not stomp on a previous header */
792 ap_table_mergen(r->headers_out, "X-Cache", c->xcache);
793
794 /* content type is already set in the headers */
795 r->content_type = ap_table_get(r->headers_out, "Content-Type");
796
797 ap_send_http_header(r);
798
799 /* are we rewriting the cache file? */
800 if (c->origfp) {
801 ap_proxy_write_headers(c, c->resp_line, c->hdrs);
802 ap_proxy_send_fb(c->origfp, r, c, c->len, r->header_only, 0, IOBUFSIZE);
803 ap_proxy_cache_tidy(c);
804 return OK;
805 }
806
807 /* no, we not */
808 if (!r->header_only) {
809 ap_proxy_send_fb(cachefp, r, NULL, c->len, 0, 0, IOBUFSIZE);
810 }
811 else {
812 ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
813 }
814
815 return OK;
816 }
817
818
819 /*
820 * Call this to test for a resource in the cache
821 * Returns DECLINED if we need to check the remote host
822 * or an HTTP status code if successful
823 *
824 * Functions:
825 * if URL is cached then
826 * if cached file is not expired then
827 * if last modified after if-modified-since then send body
828 * else send 304 Not modified
829 * else if cached file is expired then
830 * if last modified after if-modified-since then add
831 * last modified date to request
832 */
ap_proxy_cache_check(request_rec * r,char * url,struct cache_conf * conf,cache_req ** cr)833 int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf * conf,
834 cache_req **cr)
835 {
836 const char *datestr, *pragma_req = NULL, *pragma_cresp = NULL, *cc_req = NULL,
837 *cc_cresp = NULL;
838 cache_req *c;
839 BUFF *cachefp;
840 int i;
841 void *sconf = r->server->module_config;
842 proxy_server_conf *pconf =
843 (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
844 const char *agestr = NULL;
845 char *val;
846 time_t age_c = 0;
847 time_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale, minfresh;
848
849 c = ap_pcalloc(r->pool, sizeof(cache_req));
850 *cr = c;
851 c->req = r;
852 c->url = ap_pstrdup(r->pool, url);
853 c->filename = NULL;
854 c->tempfile = NULL;
855 c->fp = NULL;
856 c->origfp = NULL;
857 c->version = 0;
858 c->len = -1;
859 c->req_hdrs = NULL;
860 c->hdrs = NULL;
861 c->xcache = NULL;
862
863 /* get the If-Modified-Since date of the request, if it exists */
864 c->ims = BAD_DATE;
865 datestr = ap_table_get(r->headers_in, "If-Modified-Since");
866 if (datestr != NULL) {
867 /* this may modify the value in the original table */
868 datestr = ap_proxy_date_canon(r->pool, datestr);
869 c->ims = ap_parseHTTPdate(datestr);
870 if (c->ims == BAD_DATE) /* bad or out of range date; remove it */
871 ap_table_unset(r->headers_in, "If-Modified-Since");
872 }
873
874 /* get the If-Unmodified-Since date of the request, if it exists */
875 c->ius = BAD_DATE;
876 datestr = ap_table_get(r->headers_in, "If-Unmodified-Since");
877 if (datestr != NULL) {
878 /* this may modify the value in the original table */
879 datestr = ap_proxy_date_canon(r->pool, datestr);
880 c->ius = ap_parseHTTPdate(datestr);
881 if (c->ius == BAD_DATE) /* bad or out of range date; remove it */
882 ap_table_unset(r->headers_in, "If-Unmodified-Since");
883 }
884
885 /* get the If-Match of the request, if it exists */
886 c->im = ap_table_get(r->headers_in, "If-Match");
887
888 /* get the If-None-Match of the request, if it exists */
889 c->inm = ap_table_get(r->headers_in, "If-None-Match");
890
891 /* find the filename for this cache entry */
892 if (conf->root != NULL) {
893 char hashfile[66];
894 ap_proxy_hash(url, hashfile, pconf->cache.dirlevels, pconf->cache.dirlength);
895 c->filename = ap_pstrcat(r->pool, conf->root, "/", hashfile, NULL);
896 }
897 else {
898 c->filename = NULL;
899 c->fp = NULL;
900 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "No CacheRoot, so no caching. Declining.");
901 return DECLINED;
902 }
903
904 /* find certain cache controlling headers */
905 pragma_req = ap_table_get(r->headers_in, "Pragma");
906 cc_req = ap_table_get(r->headers_in, "Cache-Control");
907
908 /* first things first - does the request allow us to return
909 * cached information at all? If not, just decline the request.
910 *
911 * Note that there is a big difference between not being allowed
912 * to cache a request (no-store) and not being allowed to return
913 * a cached request without revalidation (max-age=0).
914 *
915 * Caching is forbidden under the following circumstances:
916 *
917 * - RFC2616 14.9.2 Cache-Control: no-store
918 * we are not supposed to store this request at all. Behave as a tunnel.
919 *
920 */
921 if (ap_proxy_liststr(cc_req, "no-store", NULL)) {
922
923 /* delete the previously cached file */
924 if (c->filename)
925 unlink(c->filename);
926 c->fp = NULL;
927 c->filename = NULL;
928 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "no-store forbids caching. Declining.");
929 return DECLINED;
930 }
931
932 /* if the cache file exists, open it */
933 cachefp = NULL;
934 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Request for %s, pragma_req=%s, ims=%ld", url,
935 (pragma_req == NULL) ? "(unset)" : pragma_req, (long)c->ims);
936 /* find out about whether the request can access the cache */
937 if (c->filename != NULL && r->method_number == M_GET &&
938 strlen(url) < 1024) {
939 cachefp = ap_proxy_open_cachefile(r, c->filename);
940 }
941
942
943 /*
944 * if a cache file exists, try reading body and headers from cache file
945 */
946 if (cachefp != NULL) {
947 i = rdcache(r, cachefp, c);
948 if (i == -1)
949 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
950 "proxy: error reading cache file %s",
951 c->filename);
952 else if (i == 0)
953 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
954 "proxy: bad (short?) cache file: %s", c->filename);
955 if (i != 1) {
956 ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
957 cachefp = NULL;
958 }
959 if (c->hdrs) {
960 cc_cresp = ap_table_get(c->hdrs, "Cache-Control");
961 pragma_cresp = ap_table_get(c->hdrs, "Pragma");
962 if ((agestr = ap_table_get(c->hdrs, "Age"))) {
963 age_c = atoi(agestr);
964 }
965 }
966 }
967
968 /* if a cache file does not exist, create empty header array */
969 /* fixed? in this case, we want to get the headers from the remote server
970 it will be handled later if we don't do this (I hope ;-)
971
972 if (cachefp == NULL)
973 c->hdrs = ap_make_table(r->pool, 20);
974 */
975 /* FIXME: Shouldn't we check the URL somewhere? */
976
977 /*
978 * Check Content-Negotiation - Vary
979 *
980 * At this point we need to make sure that the object we found in the cache
981 * is the same object that would be delivered to the client, when the
982 * effects of content negotiation are taken into effect.
983 *
984 * In plain english, we want to make sure that a language-negotiated
985 * document in one language is not given to a client asking for a
986 * language negotiated document in a different language by mistake.
987 *
988 * RFC2616 13.6 and 14.44 describe the Vary mechanism.
989 */
990 if (c->hdrs && c->req_hdrs) {
991 char *vary = ap_pstrdup(r->pool, ap_table_get(c->hdrs, "Vary"));
992
993 while (vary && *vary) {
994 char *name = vary;
995 const char *h1, *h2;
996
997 /* isolate header name */
998 while (*vary && !ap_isspace(*vary) && (*vary != ','))
999 ++vary;
1000 while (ap_isspace(*vary) || (*vary == ',')) {
1001 *vary = '\0';
1002 ++vary;
1003 }
1004
1005 /*
1006 * is this header in the request and the header in the cached
1007 * request identical? If not, we give up and do a straight get
1008 */
1009 h1 = ap_table_get(r->headers_in, name);
1010 h2 = ap_table_get(c->req_hdrs, name);
1011 if (h1 == h2) {
1012 /* both headers NULL, so a match - do nothing */
1013 }
1014 else if (h1 && h2 && !strcmp(h1, h2)) {
1015 /* both headers exist and are equal - do nothing */
1016 }
1017 else {
1018
1019 /* headers do not match, so Vary failed */
1020 c->fp = cachefp;
1021 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Vary header mismatch - object must be fetched from scratch. Declining.");
1022 return DECLINED;
1023 }
1024 }
1025 }
1026
1027
1028 /*
1029 * We now want to check if our cached data is still fresh. This depends
1030 * on a few things, in this order:
1031 *
1032 * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache no-cache in
1033 * either the request or the cached response means that we must
1034 * revalidate the request unconditionally, overriding any expiration
1035 * mechanism. It's equivalent to max-age=0,must-revalidate.
1036 *
1037 * - RFC2616 14.32 Pragma: no-cache This is treated the same as
1038 * Cache-Control: no-cache.
1039 *
1040 * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate,
1041 * proxy-revalidate if the max-stale request header exists, modify the
1042 * stale calculations below so that an object can be at most <max-stale>
1043 * seconds stale before we request a revalidation, _UNLESS_ a
1044 * must-revalidate or proxy-revalidate cached response header exists to
1045 * stop us doing this.
1046 *
1047 * - RFC2616 14.9.3 Cache-Control: s-maxage the origin server specifies the
1048 * maximum age an object can be before it is considered stale. This
1049 * directive has the effect of proxy|must revalidate, which in turn means
1050 * simple ignore any max-stale setting.
1051 *
1052 * - RFC2616 14.9.4 Cache-Control: max-age this header can appear in both
1053 * requests and responses. If both are specified, the smaller of the two
1054 * takes priority.
1055 *
1056 * - RFC2616 14.21 Expires: if this request header exists in the cached
1057 * entity, and it's value is in the past, it has expired.
1058 *
1059 */
1060
1061 /* calculate age of object */
1062 age = ap_proxy_current_age(c, age_c);
1063
1064 /* extract s-maxage */
1065 if (cc_cresp && ap_proxy_liststr(cc_cresp, "s-maxage", &val))
1066 smaxage = atoi(val);
1067 else
1068 smaxage = -1;
1069
1070 /* extract max-age from request */
1071 if (cc_req && ap_proxy_liststr(cc_req, "max-age", &val))
1072 maxage_req = atoi(val);
1073 else
1074 maxage_req = -1;
1075
1076 /* extract max-age from response */
1077 if (cc_cresp && ap_proxy_liststr(cc_cresp, "max-age", &val))
1078 maxage_cresp = atoi(val);
1079 else
1080 maxage_cresp = -1;
1081
1082 /*
1083 * if both maxage request and response, the smaller one takes priority
1084 */
1085 if (-1 == maxage_req)
1086 maxage = maxage_cresp;
1087 else if (-1 == maxage_cresp)
1088 maxage = maxage_req;
1089 else
1090 maxage = MIN(maxage_req, maxage_cresp);
1091
1092 /* extract max-stale */
1093 if (cc_req && ap_proxy_liststr(cc_req, "max-stale", &val))
1094 maxstale = atoi(val);
1095 else
1096 maxstale = 0;
1097
1098 /* extract min-fresh */
1099 if (cc_req && ap_proxy_liststr(cc_req, "min-fresh", &val))
1100 minfresh = atoi(val);
1101 else
1102 minfresh = 0;
1103
1104 /* override maxstale if must-revalidate or proxy-revalidate */
1105 if (maxstale && ((cc_cresp && ap_proxy_liststr(cc_cresp, "must-revalidate", NULL)) || (cc_cresp && ap_proxy_liststr(cc_cresp, "proxy-revalidate", NULL))))
1106 maxstale = 0;
1107
1108 if (cachefp != NULL &&
1109
1110 /* handle no-cache */
1111 !((cc_req && ap_proxy_liststr(cc_req, "no-cache", NULL)) ||
1112 (pragma_req && ap_proxy_liststr(pragma_req, "no-cache", NULL)) ||
1113 (cc_cresp && ap_proxy_liststr(cc_cresp, "no-cache", NULL)) ||
1114 (pragma_cresp && ap_proxy_liststr(pragma_cresp, "no-cache", NULL))) &&
1115
1116 /* handle expiration */
1117 ((-1 < smaxage && age < (smaxage - minfresh)) ||
1118 (-1 < maxage && age < (maxage + maxstale - minfresh)) ||
1119 (c->expire != BAD_DATE && age < (c->expire - c->date + maxstale - minfresh)))
1120 ) {
1121
1122 /* it's fresh darlings... */
1123
1124 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Unexpired data available");
1125
1126 /* set age header on response */
1127 ap_table_set(c->hdrs, "Age",
1128 ap_psprintf(r->pool, "%lu", (unsigned long)age));
1129
1130 /* add warning if maxstale overrode freshness calculation */
1131 if (!((-1 < smaxage && age < smaxage) ||
1132 (-1 < maxage && age < maxage) ||
1133 (c->expire != BAD_DATE && (c->expire - c->date) > age))) {
1134 /* make sure we don't stomp on a previous warning */
1135 ap_table_merge(c->hdrs, "Warning", "110 Response is stale");
1136 }
1137
1138 /* check conditionals (If-Modified-Since, etc) */
1139 c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), NULL);
1140 return ap_proxy_cache_conditional(r, c, cachefp);
1141
1142
1143 }
1144
1145 /*
1146 * at this point we have determined our cached data needs revalidation
1147 * but first - we check 1 thing:
1148 *
1149 * RFC2616 14.9.4 - if "only-if-cached" specified, send a 504 Gateway
1150 * Timeout - we're not allowed to revalidate the object
1151 */
1152 if (ap_proxy_liststr(cc_req, "only-if-cached", NULL)) {
1153 if (cachefp)
1154 ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
1155 return HTTP_GATEWAY_TIME_OUT;
1156 }
1157
1158
1159 /*
1160 * If we already have cached data and a last-modified date, and it is not
1161 * a head request, then add an If-Modified-Since.
1162 *
1163 * If we also have an Etag, then the object must have come from an HTTP/1.1
1164 * server. Add an If-None-Match as well.
1165 *
1166 * See RFC2616 13.3.4
1167 */
1168
1169 if (cachefp != NULL && !r->header_only) {
1170
1171 const char *etag = ap_table_get(c->hdrs, "Etag");
1172
1173 /* If-Modified-Since */
1174 if (c->lmod != BAD_DATE) {
1175 /*
1176 * use the later of the one from the request and the
1177 * last-modified date from the cache
1178 */
1179 if (c->ims == BAD_DATE || c->ims < c->lmod) {
1180 const char *q;
1181
1182 if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL)
1183 ap_table_set(r->headers_in, "If-Modified-Since", (char *)q);
1184 }
1185 }
1186
1187 /* If-None-Match */
1188 if (etag) {
1189 ap_table_set(r->headers_in, "If-None-Match", etag);
1190 }
1191
1192 }
1193
1194
1195 c->fp = cachefp;
1196
1197 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Local copy not present or expired. Declining.");
1198
1199 return DECLINED;
1200 }
1201
1202 /*
1203 * Having read the response from the client, decide what to do
1204 * If the response is not cachable, then delete any previously cached
1205 * response, and copy data from remote server to client.
1206 * Functions:
1207 * parse dates
1208 * check for an uncachable response
1209 * calculate an expiry date, if one is not provided
1210 * if the remote file has not been modified, then return the document
1211 * from the cache, maybe updating the header line
1212 * otherwise, delete the old cached file and open a new temporary file
1213 */
ap_proxy_cache_update(cache_req * c,table * resp_hdrs,const int is_HTTP1,int nocache)1214 int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
1215 const int is_HTTP1, int nocache)
1216 {
1217 request_rec *r = c->req;
1218 char *p;
1219 const char *expire, *lmods, *dates, *clen;
1220 time_t expc, date, lmod, now;
1221 char buff[17 * 7 + 1];
1222 void *sconf = r->server->module_config;
1223 proxy_server_conf *conf =
1224 (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
1225 const char *cc_resp;
1226 table *req_hdrs;
1227 size_t tflen;
1228
1229 cc_resp = ap_table_get(resp_hdrs, "Cache-Control");
1230
1231 c->tempfile = NULL;
1232
1233 /* we've received the response from the origin server */
1234
1235 /*
1236 * read expiry date; if a bad date, then leave it so the client can read
1237 * it
1238 */
1239 expire = ap_table_get(resp_hdrs, "Expires");
1240 if (expire != NULL)
1241 expc = ap_parseHTTPdate(expire);
1242 else
1243 expc = BAD_DATE;
1244
1245 /* read the last-modified date; if the date is bad, then delete it */
1246 lmods = ap_table_get(resp_hdrs, "Last-Modified");
1247 if (lmods != NULL) {
1248 lmod = ap_parseHTTPdate(lmods);
1249 if (lmod == BAD_DATE) {
1250 /* kill last modified date */
1251 lmods = NULL;
1252 }
1253 }
1254 else
1255 lmod = BAD_DATE;
1256
1257
1258 /*
1259 * what responses should we not cache?
1260 *
1261 * At this point we decide based on the response headers whether it is
1262 * appropriate _NOT_ to cache the data from the server. There are a whole
1263 * lot of conditions that prevent us from caching this data. They are
1264 * tested here one by one to be clear and unambiguous.
1265 */
1266
1267 /*
1268 * RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410 We
1269 * don't cache 206, because we don't (yet) cache partial responses. We
1270 * include 304 Not Modified here too as this is the origin server telling
1271 * us to serve the cached copy.
1272 */
1273 if ((r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE && r->status != HTTP_MULTIPLE_CHOICES && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) ||
1274
1275 /* if a broken Expires header is present, don't cache it */
1276 (expire != NULL && expc == BAD_DATE) ||
1277
1278 /*
1279 * if the server said 304 Not Modified but we have no cache file - pass
1280 * this untouched to the user agent, it's not for us.
1281 */
1282 (r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) ||
1283
1284 /*
1285 * 200 OK response from HTTP/1.0 and up without a Last-Modified header
1286 */
1287 (r->status == HTTP_OK && lmods == NULL && is_HTTP1) ||
1288
1289 /* HEAD requests */
1290 r->header_only ||
1291
1292 /*
1293 * RFC2616 14.9.2 Cache-Control: no-store response indicating do not
1294 * cache, or stop now if you are trying to cache it
1295 */
1296 ap_proxy_liststr(cc_resp, "no-store", NULL) ||
1297
1298 /*
1299 * RFC2616 14.9.1 Cache-Control: private this object is marked for this
1300 * user's eyes only. Behave as a tunnel.
1301 */
1302 ap_proxy_liststr(cc_resp, "private", NULL) ||
1303
1304 /*
1305 * RFC2616 14.8 Authorisation: if authorisation is included in the
1306 * request, we don't cache, but we can cache if the following exceptions
1307 * are true: 1) If Cache-Control: s-maxage is included 2) If
1308 * Cache-Control: must-revalidate is included 3) If Cache-Control: public
1309 * is included
1310 */
1311 (ap_table_get(r->headers_in, "Authorization") != NULL
1312
1313 && !(ap_proxy_liststr(cc_resp, "s-maxage", NULL) || ap_proxy_liststr(cc_resp, "must-revalidate", NULL) || ap_proxy_liststr(cc_resp, "public", NULL))
1314 ) ||
1315
1316 /* or we've been asked not to cache it above */
1317 nocache) {
1318
1319 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Response is not cacheable, unlinking %s", c->filename);
1320
1321 /* close the file */
1322 if (c->fp != NULL) {
1323 ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
1324 c->fp = NULL;
1325 }
1326
1327 /* delete the previously cached file */
1328 if (c->filename)
1329 unlink(c->filename);
1330 return DECLINED; /* send data to client but not cache */
1331 }
1332
1333
1334 /*
1335 * It's safe to cache the response.
1336 *
1337 * We now want to update the cache file header information with the new
1338 * date, last modified, expire and content length and write it away to
1339 * our cache file. First, we determine these values from the response,
1340 * using heuristics if appropriate.
1341 *
1342 * In addition, we make HTTP/1.1 age calculations and write them away too.
1343 */
1344
1345 /* Read the date. Generate one if one is not supplied */
1346 dates = ap_table_get(resp_hdrs, "Date");
1347 if (dates != NULL)
1348 date = ap_parseHTTPdate(dates);
1349 else
1350 date = BAD_DATE;
1351
1352 now = time(NULL);
1353
1354 if (date == BAD_DATE) { /* No, or bad date */
1355 /* no date header! */
1356 /* add one; N.B. use the time _now_ rather than when we were checking the cache
1357 */
1358 date = now;
1359 dates = ap_gm_timestr_822(r->pool, now);
1360 ap_table_set(resp_hdrs, "Date", dates);
1361 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Added date header");
1362 }
1363
1364 /* set response_time for HTTP/1.1 age calculations */
1365 c->resp_time = now;
1366
1367 /* check last-modified date */
1368 if (lmod != BAD_DATE && lmod > date)
1369 /* if its in the future, then replace by date */
1370 {
1371 lmod = date;
1372 lmods = dates;
1373 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Last modified is in the future, replacing with now");
1374 }
1375 /* if the response did not contain the header, then use the cached version */
1376 if (lmod == BAD_DATE && c->fp != NULL) {
1377 lmod = c->lmod;
1378 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Reusing cached last modified");
1379 }
1380
1381 /* we now need to calculate the expire data for the object. */
1382 if (expire == NULL && c->fp != NULL) { /* no expiry data sent in
1383 * response */
1384 expire = ap_table_get(c->hdrs, "Expires");
1385 if (expire != NULL)
1386 expc = ap_parseHTTPdate(expire);
1387 }
1388 /* so we now have the expiry date */
1389 /* if no expiry date then
1390 * if lastmod
1391 * expiry date = now + min((date - lastmod) * factor, maxexpire)
1392 * else
1393 * expire date = now + defaultexpire
1394 */
1395 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Expiry date is %ld", (long)expc);
1396 if (expc == BAD_DATE) {
1397 if (lmod != BAD_DATE) {
1398 double x = (double)(date - lmod) * conf->cache.lmfactor;
1399 double maxex = conf->cache.maxexpire;
1400 if (x > maxex)
1401 x = maxex;
1402 expc = now + (int)x;
1403 }
1404 else
1405 expc = now + conf->cache.defaultexpire;
1406 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Expiry date calculated %ld", (long)expc);
1407 }
1408
1409 /* get the content-length header */
1410 clen = ap_table_get(resp_hdrs, "Content-Length");
1411 if (clen == NULL)
1412 c->len = -1;
1413 else
1414 c->len = ap_strtol(clen, NULL, 10);
1415
1416 /* we have all the header information we need - write it to the cache file */
1417 c->version++;
1418 ap_proxy_sec2hex(date, buff + 17 * (0), sizeof(buff) - 17 * 0);
1419 buff[17 * (1) - 1] = ' ';
1420 ap_proxy_sec2hex(lmod, buff + 17 * (1), sizeof(buff) - 17 * 1);
1421 buff[17 * (2) - 1] = ' ';
1422 ap_proxy_sec2hex(expc, buff + 17 * (2), sizeof(buff) - 17 * 2);
1423 buff[17 * (3) - 1] = ' ';
1424 ap_proxy_sec2hex(c->version, buff + 17 * (3), sizeof(buff) - 17 * 3);
1425 buff[17 * (4) - 1] = ' ';
1426 ap_proxy_sec2hex(c->req_time, buff + 17 * (4), sizeof(buff) - 17 * 4);
1427 buff[17 * (5) - 1] = ' ';
1428 ap_proxy_sec2hex(c->resp_time, buff + 17 * (5), sizeof(buff) - 17 * 5);
1429 buff[17 * (6) - 1] = ' ';
1430 ap_proxy_sec2hex(c->len, buff + 17 * (6), sizeof(buff) - 17 * 6);
1431 buff[17 * (7) - 1] = '\n';
1432 buff[17 * (7)] = '\0';
1433
1434 /* Was the server response a 304 Not Modified?
1435 *
1436 * If it was, it means that we requested a revalidation, and that
1437 * the result of that revalidation was that the object was fresh.
1438 *
1439 */
1440
1441 /* if response from server 304 not modified */
1442 if (r->status == HTTP_NOT_MODIFIED) {
1443
1444 /* Have the headers changed?
1445 *
1446 * if not - we fulfil the request and return now.
1447 */
1448
1449 if (c->hdrs) {
1450 /* recall at this point that c->len is already set from resp_hdrs.
1451 If Content-Length was NULL, then c->len is -1, otherwise it's
1452 set to whatever the value was. */
1453 if (c->len == 0 || c->len == -1) {
1454 const char *c_clen_str;
1455 off_t c_clen;
1456 if ( (c_clen_str = ap_table_get(c->hdrs, "Content-Length")) &&
1457 ( (c_clen = ap_strtol(c_clen_str, NULL, 10)) > 0) ) {
1458 ap_table_set(resp_hdrs, "Content-Length", c_clen_str);
1459 c->len = c_clen;
1460 ap_proxy_sec2hex(c->len, buff + 17 * (6),
1461 sizeof(buff) - 17 * 6);
1462 buff[17 * (7) - 1] = '\n';
1463 buff[17 * (7)] = '\0';
1464 }
1465 }
1466 if (!ap_proxy_table_replace(c->hdrs, resp_hdrs)) {
1467 c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
1468 return ap_proxy_cache_conditional(r, c, c->fp);
1469 }
1470 }
1471 else
1472 c->hdrs = resp_hdrs;
1473 /* if we get here - the headers have changed. Go through the motions
1474 * of creating a new temporary cache file below, we'll then serve
1475 * the request like we would have in ap_proxy_cache_conditional()
1476 * above, and at the same time we will also rewrite the contents
1477 * to the new temporary file.
1478 */
1479 }
1480
1481 /*
1482 * Ok - lets prepare and open the cached file
1483 *
1484 * If a cached file (in c->fp) is already open, then we want to
1485 * update that cached file. Copy the c->fp to c->origfp and open
1486 * up a new one.
1487 *
1488 * If the cached file (in c->fp) is NULL, we must open a new cached
1489 * file from scratch.
1490 *
1491 * The new cache file will be moved to it's final location in the
1492 * directory tree later, overwriting the old cache file should it exist.
1493 */
1494
1495 /* if a cache file was already open */
1496 if (c->fp != NULL) {
1497 c->origfp = c->fp;
1498 }
1499
1500 while (1) {
1501 /* create temporary filename */
1502 #define TMPFILESTR "/tmpXXXXXXXXXX"
1503 if (conf->cache.root == NULL) {
1504 c = ap_proxy_cache_error(c);
1505 break;
1506 }
1507 tflen = strlen(conf->cache.root) + sizeof(TMPFILESTR);
1508 c->tempfile = ap_palloc(r->pool, tflen);
1509 strlcpy(c->tempfile, conf->cache.root, tflen);
1510 strlcat(c->tempfile, TMPFILESTR, tflen);
1511 #undef TMPFILESTR
1512 p = mktemp(c->tempfile);
1513 if (p == NULL) {
1514 c = ap_proxy_cache_error(c);
1515 break;
1516 }
1517
1518 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Create temporary file %s", c->tempfile);
1519
1520 /* create the new file */
1521 c->fp = ap_proxy_create_cachefile(r, c->tempfile);
1522 if (NULL == c->fp) {
1523 c = ap_proxy_cache_error(c);
1524 break;
1525 }
1526
1527 /* write away the cache header and the URL */
1528 if (ap_bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) {
1529 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1530 "proxy: error writing cache file(%s)", c->tempfile);
1531 c = ap_proxy_cache_error(c);
1532 break;
1533 }
1534
1535 /* get original request headers */
1536 if (c->req_hdrs)
1537 req_hdrs = ap_copy_table(r->pool, c->req_hdrs);
1538 else
1539 req_hdrs = ap_copy_table(r->pool, r->headers_in);
1540
1541 /* remove hop-by-hop headers */
1542 ap_proxy_clear_connection(r->pool, req_hdrs);
1543
1544 /* save original request headers */
1545 if (c->req_hdrs)
1546 ap_table_do(ap_proxy_send_hdr_line, c, c->req_hdrs, NULL);
1547 else
1548 ap_table_do(ap_proxy_send_hdr_line, c, r->headers_in, NULL);
1549 if (ap_bputs(CRLF, c->fp) == -1) {
1550 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
1551 "proxy: error writing request headers terminating CRLF to %s", c->tempfile);
1552 c = ap_proxy_cache_error(c);
1553 break;
1554 }
1555 break;
1556 }
1557
1558 /* Was the server response a 304 Not Modified?
1559 *
1560 * If so, we have some work to do that we didn't do when we first
1561 * checked above. We need to fulfil the request, and we need to
1562 * copy the body from the old object to the new one.
1563 */
1564
1565 /* if response from server 304 not modified */
1566 if (r->status == HTTP_NOT_MODIFIED) {
1567
1568 /* fulfil the request */
1569 c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
1570 return ap_proxy_cache_conditional(r, c, c->fp);
1571
1572 }
1573 return DECLINED;
1574 }
1575
ap_proxy_cache_tidy(cache_req * c)1576 void ap_proxy_cache_tidy(cache_req *c)
1577 {
1578 server_rec *s;
1579 long int bc;
1580
1581 if (!c || !c->fp)
1582 return;
1583
1584 s = c->req->server;
1585
1586 /* don't care how much was sent, but rather how much was written to cache
1587 ap_bgetopt(c->req->connection->client, BO_BYTECT, &bc);
1588 */
1589 bc = c->written;
1590
1591 if (c->len != -1) {
1592 /* file lengths don't match; don't cache it */
1593 if (bc != c->len) {
1594 ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); /* no need to flush */
1595 unlink(c->tempfile);
1596 return;
1597 }
1598 }
1599 /* don't care if aborted, cache it if fully retrieved from host!
1600 else if (c->req->connection->aborted) {
1601 ap_pclosef(c->req->pool, c->fp->fd); / no need to flush /
1602 unlink(c->tempfile);
1603 return;
1604 }
1605 */
1606 else {
1607 /* update content-length of file */
1608 char buff[17];
1609 off_t curpos;
1610
1611 c->len = bc;
1612 ap_bflush(c->fp);
1613 ap_proxy_sec2hex(c->len, buff, sizeof(buff));
1614 curpos = lseek(ap_bfileno(c->fp, B_WR), 17 * 6, SEEK_SET);
1615 if (curpos == -1)
1616 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1617 "proxy: error seeking on cache file %s", c->tempfile);
1618 else if (write(ap_bfileno(c->fp, B_WR), buff, sizeof(buff) - 1) == -1)
1619 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1620 "proxy: error updating cache file %s", c->tempfile);
1621 }
1622
1623 if (ap_bflush(c->fp) == -1) {
1624 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1625 "proxy: error writing to cache file %s",
1626 c->tempfile);
1627 ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
1628 unlink(c->tempfile);
1629 return;
1630 }
1631
1632 if (ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR))== -1) {
1633 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1634 "proxy: error closing cache file %s", c->tempfile);
1635 unlink(c->tempfile);
1636 return;
1637 }
1638
1639 if (unlink(c->filename) == -1 && errno != ENOENT) {
1640 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1641 "proxy: error deleting old cache file %s",
1642 c->filename);
1643 (void)unlink(c->tempfile);
1644 }
1645 else {
1646 char *p;
1647 proxy_server_conf *conf =
1648 (proxy_server_conf *)ap_get_module_config(s->module_config, &proxy_module);
1649
1650 for (p = c->filename + strlen(conf->cache.root) + 1;;) {
1651 p = strchr(p, '/');
1652 if (!p)
1653 break;
1654 *p = '\0';
1655 if (mkdir(c->filename, S_IREAD | S_IWRITE | S_IEXEC) < 0 && errno != EEXIST)
1656 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1657 "proxy: error creating cache directory %s",
1658 c->filename);
1659 *p = '/';
1660 ++p;
1661 }
1662 if (link(c->tempfile, c->filename) == -1)
1663 ap_log_error(APLOG_MARK, APLOG_INFO, s,
1664 "proxy: error linking cache file %s to %s",
1665 c->tempfile, c->filename);
1666 if (unlink(c->tempfile) == -1)
1667 ap_log_error(APLOG_MARK, APLOG_ERR, s,
1668 "proxy: error deleting temp file %s", c->tempfile);
1669 }
1670 }
1671