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