upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / experimental / mod_cache.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define CORE_PRIVATE
18
19 #include "mod_cache.h"
20
21 module AP_MODULE_DECLARE_DATA cache_module;
22 APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
23
24 /* -------------------------------------------------------------- */
25
26
27 /* Handles for cache filters, resolved at startup to eliminate
28  * a name-to-function mapping on each request
29  */
30 static ap_filter_rec_t *cache_save_filter_handle;
31 static ap_filter_rec_t *cache_out_filter_handle;
32
33 /*
34  * CACHE handler
35  * -------------
36  *
37  * Can we deliver this request from the cache?
38  * If yes:
39  *   deliver the content by installing the CACHE_OUT filter.
40  * If no:
41  *   check whether we're allowed to try cache it
42  *   If yes:
43  *     add CACHE_SAVE filter
44  *   If No:
45  *     oh well.
46  */
47
48 static int cache_url_handler(request_rec *r, int lookup)
49 {
50     apr_status_t rv;
51     const char *pragma, *auth;
52     apr_uri_t uri;
53     char *url;
54     char *path;
55     cache_provider_list *providers;
56     cache_info *info;
57     cache_request_rec *cache;
58     cache_server_conf *conf;
59     apr_bucket_brigade *out;
60
61     /* Delay initialization until we know we are handling a GET */
62     if (r->method_number != M_GET) {
63         return DECLINED;
64     }
65
66     uri = r->parsed_uri;
67     url = r->unparsed_uri;
68     path = uri.path;
69     info = NULL;
70
71     conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
72                                                       &cache_module);
73
74     /*
75      * Which cache module (if any) should handle this request?
76      */
77     if (!(providers = ap_cache_get_providers(r, conf, path))) {
78         return DECLINED;
79     }
80
81     /* make space for the per request config */
82     cache = (cache_request_rec *) ap_get_module_config(r->request_config,
83                                                        &cache_module);
84     if (!cache) {
85         cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
86         ap_set_module_config(r->request_config, &cache_module, cache);
87     }
88
89     /* save away the possible providers */
90     cache->providers = providers;
91
92     /*
93      * Are we allowed to serve cached info at all?
94      */
95
96     /* find certain cache controlling headers */
97     pragma = apr_table_get(r->headers_in, "Pragma");
98     auth = apr_table_get(r->headers_in, "Authorization");
99
100     /* first things first - does the request allow us to return
101      * cached information at all? If not, just decline the request.
102      *
103      * Note that there is a big difference between not being allowed
104      * to cache a request (no-store) and not being allowed to return
105      * a cached request without revalidation (max-age=0).
106      *
107      * Caching is forbidden under the following circumstances:
108      *
109      * - RFC2616 14.9.2 Cache-Control: no-store
110      * - Pragma: no-cache
111      * - Any requests requiring authorization.
112      */
113     if (conf->ignorecachecontrol == 1 && auth == NULL) {
114         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
115                      "incoming request is asking for a uncached version of "
116                      "%s, but we know better and are ignoring it", url);
117     }
118     else {
119         if (ap_cache_liststr(NULL, pragma, "no-cache", NULL) ||
120             auth != NULL) {
121             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
122                          "cache: no-cache or authorization forbids caching "
123                          "of %s", url);
124             return DECLINED;
125         }
126     }
127
128     /*
129      * Try to serve this request from the cache.
130      *
131      * If no existing cache file (DECLINED)
132      *   add cache_save filter
133      * If cached file (OK)
134      *   clear filter stack
135      *   add cache_out filter
136      *   return OK
137      */
138     rv = cache_select_url(r, url);
139     if (rv != OK) {
140         if (rv == DECLINED) {
141             if (!lookup) {
142                 /* add cache_save filter to cache this request */
143                 ap_add_output_filter_handle(cache_save_filter_handle, NULL, r,
144                                             r->connection);
145             }
146         }
147         else {
148             /* error */
149             ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
150                          "cache: error returned while checking for cached "
151                          "file by %s cache", cache->provider_name);
152         }
153         return DECLINED;
154     }
155
156     /* We have located a suitable cache file now. */
157     info = &(cache->handle->cache_obj->info);
158
159     if (info && info->lastmod) {
160         ap_update_mtime(r, info->lastmod);
161     }
162
163     rv = ap_meets_conditions(r);
164     if (rv != OK) {
165         /* Return cached status. */
166         return rv;
167     }
168
169     /* If we're a lookup, we can exit now instead of serving the content. */
170     if (lookup) {
171         return OK;
172     }
173
174     /* Serve up the content */
175
176     /* We are in the quick handler hook, which means that no output
177      * filters have been set. So lets run the insert_filter hook.
178      */
179     ap_run_insert_filter(r);
180     ap_add_output_filter_handle(cache_out_filter_handle, NULL,
181                                 r, r->connection);
182
183     /* kick off the filter stack */
184     out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
185     rv = ap_pass_brigade(r->output_filters, out);
186     if (rv != APR_SUCCESS) {
187         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
188                      "cache: error returned while trying to return %s "
189                      "cached data", 
190                      cache->provider_name);
191         return rv;
192     }
193
194     return OK;
195 }
196
197 /*
198  * CACHE_OUT filter
199  * ----------------
200  *
201  * Deliver cached content (headers and body) up the stack.
202  */
203 static int cache_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
204 {
205     request_rec *r = f->r;
206     cache_request_rec *cache;
207
208     cache = (cache_request_rec *) ap_get_module_config(r->request_config, 
209                                                        &cache_module);
210
211     if (!cache) {
212         /* user likely configured CACHE_OUT manually; they should use mod_cache
213          * configuration to do that */
214         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
215                      "CACHE_OUT enabled unexpectedly");
216         ap_remove_output_filter(f);
217         return ap_pass_brigade(f->next, bb);
218     }
219
220     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
221                  "cache: running CACHE_OUT filter");
222
223     /* restore status of cached response */
224     r->status = cache->handle->status;
225
226     /* recall_headers() was called in cache_select_url() */
227     cache->provider->recall_body(cache->handle, r->pool, bb);
228
229     /* This filter is done once it has served up its content */
230     ap_remove_output_filter(f);
231
232     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
233                  "cache: serving %s", r->uri);
234     return ap_pass_brigade(f->next, bb);
235 }
236
237
238 /*
239  * CACHE_SAVE filter
240  * ---------------
241  *
242  * Decide whether or not this content should be cached.
243  * If we decide no it should not:
244  *   remove the filter from the chain
245  * If we decide yes it should:
246  *   Have we already started saving the response?
247  *      If we have started, pass the data to the storage manager via store_body
248  *      Otherwise:
249  *        Check to see if we *can* save this particular response.
250  *        If we can, call cache_create_entity() and save the headers and body
251  *   Finally, pass the data to the next filter (the network or whatever)
252  */
253
254 static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
255 {
256     int rv;
257     int date_in_errhdr = 0;
258     request_rec *r = f->r;
259     cache_request_rec *cache;
260     cache_server_conf *conf;
261     char *url = r->unparsed_uri;
262     const char *cc_in, *cc_out, *cl, *vary_out;
263     const char *exps, *lastmods, *dates, *etag;
264     apr_time_t exp, date, lastmod, now;
265     apr_off_t size;
266     cache_info *info;
267     char *reason;
268     apr_pool_t *p;
269
270     /* check first whether running this filter has any point or not */
271     /* If the user has Cache-Control: no-store from RFC 2616, don't store! */
272     cc_in = apr_table_get(r->headers_in, "Cache-Control");
273     vary_out = apr_table_get(r->headers_out, "Vary");
274     if (r->no_cache || ap_cache_liststr(NULL, cc_in, "no-store", NULL) || 
275         ap_cache_liststr(NULL, vary_out, "*", NULL)) {
276         ap_remove_output_filter(f);
277         return ap_pass_brigade(f->next, in);
278     }
279
280     /* Setup cache_request_rec */
281     cache = (cache_request_rec *) ap_get_module_config(r->request_config,
282                                                        &cache_module);
283     if (!cache) {
284         /* user likely configured CACHE_SAVE manually; they should really use
285          * mod_cache configuration to do that
286          */
287         cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
288         ap_set_module_config(r->request_config, &cache_module, cache);
289     }
290
291     reason = NULL;
292     p = r->pool;
293     /*
294      * Pass Data to Cache
295      * ------------------
296      * This section passes the brigades into the cache modules, but only
297      * if the setup section (see below) is complete.
298      */
299     if (cache->block_response) {
300         /* We've already sent down the response and EOS.  So, ignore
301          * whatever comes now.
302          */
303         return APR_SUCCESS;
304     }
305
306     /* have we already run the cachability check and set up the
307      * cached file handle? 
308      */
309     if (cache->in_checked) {
310         /* pass the brigades into the cache, then pass them
311          * up the filter stack
312          */
313         rv = cache->provider->store_body(cache->handle, r, in);
314         if (rv != APR_SUCCESS) {
315             ap_remove_output_filter(f);
316         }
317         return ap_pass_brigade(f->next, in);
318     }
319
320     /*
321      * Setup Data in Cache
322      * -------------------
323      * This section opens the cache entity and sets various caching
324      * parameters, and decides whether this URL should be cached at
325      * all. This section is* run before the above section.
326      */
327
328     /* read expiry date; if a bad date, then leave it so the client can
329      * read it 
330      */
331     exps = apr_table_get(r->err_headers_out, "Expires");
332     if (exps == NULL) {
333         exps = apr_table_get(r->headers_out, "Expires");
334     }
335     if (exps != NULL) {
336         if (APR_DATE_BAD == (exp = apr_date_parse_http(exps))) {
337             exps = NULL;
338         }
339     }
340     else {
341         exp = APR_DATE_BAD;
342     }
343
344     /* read the last-modified date; if the date is bad, then delete it */
345     lastmods = apr_table_get(r->err_headers_out, "Last-Modified");
346     if (lastmods == NULL) {
347         lastmods = apr_table_get(r->headers_out, "Last-Modified");
348     }
349     if (lastmods != NULL) {
350         if (APR_DATE_BAD == (lastmod = apr_date_parse_http(lastmods))) {
351             lastmods = NULL;
352         }
353     }
354     else {
355         lastmod = APR_DATE_BAD;
356     }
357
358     conf = (cache_server_conf *) ap_get_module_config(r->server->module_config, &cache_module);
359     /* read the etag and cache-control from the entity */
360     etag = apr_table_get(r->err_headers_out, "Etag");
361     if (etag == NULL) {
362         etag = apr_table_get(r->headers_out, "Etag");
363     }
364     cc_out = apr_table_get(r->err_headers_out, "Cache-Control");
365     if (cc_out == NULL) {
366         cc_out = apr_table_get(r->headers_out, "Cache-Control");
367     }
368
369     /*
370      * what responses should we not cache?
371      *
372      * At this point we decide based on the response headers whether it
373      * is appropriate _NOT_ to cache the data from the server. There are
374      * a whole lot of conditions that prevent us from caching this data.
375      * They are tested here one by one to be clear and unambiguous. 
376      */
377     if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
378         && r->status != HTTP_MULTIPLE_CHOICES
379         && r->status != HTTP_MOVED_PERMANENTLY
380         && r->status != HTTP_NOT_MODIFIED) {
381         /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
382          * We don't cache 206, because we don't (yet) cache partial responses.
383          * We include 304 Not Modified here too as this is the origin server
384          * telling us to serve the cached copy.
385          */
386         reason = apr_psprintf(p, "Response status %d", r->status);
387     } 
388     else if (exps != NULL && exp == APR_DATE_BAD) {
389         /* if a broken Expires header is present, don't cache it */
390         reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL);
391     }
392     else if (r->args && exps == NULL) {
393         /* if query string present but no expiration time, don't cache it
394          * (RFC 2616/13.9)
395          */
396         reason = "Query string present but no expires header";
397     }
398     else if (r->status == HTTP_NOT_MODIFIED &&
399              !cache->handle && !cache->stale_handle) {
400         /* if the server said 304 Not Modified but we have no cache
401          * file - pass this untouched to the user agent, it's not for us.
402          */
403         reason = "HTTP Status 304 Not Modified";
404     }
405     else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL 
406              && (exps == NULL) && (conf->no_last_mod_ignore ==0)) {
407         /* 200 OK response from HTTP/1.0 and up without Last-Modified,
408          * Etag, or Expires headers.
409          */
410         /* Note: mod-include clears last_modified/expires/etags - this
411          * is why we have an optional function for a key-gen ;-) 
412          */
413         reason = "No Last-Modified, Etag, or Expires headers";
414     }
415     else if (r->header_only) {
416         /* HEAD requests */
417         reason = "HTTP HEAD request";
418     }
419     else if (ap_cache_liststr(NULL, cc_out, "no-store", NULL)) {
420         /* RFC2616 14.9.2 Cache-Control: no-store response
421          * indicating do not cache, or stop now if you are
422          * trying to cache it */
423         reason = "Cache-Control: no-store present";
424     }
425     else if (ap_cache_liststr(NULL, cc_out, "private", NULL)) {
426         /* RFC2616 14.9.1 Cache-Control: private
427          * this object is marked for this user's eyes only. Behave
428          * as a tunnel.
429          */
430         reason = "Cache-Control: private present";
431     }
432     else if (apr_table_get(r->headers_in, "Authorization") != NULL
433              && !(ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)
434                   || ap_cache_liststr(NULL, cc_out, "must-revalidate", NULL)
435                   || ap_cache_liststr(NULL, cc_out, "public", NULL))) {
436         /* RFC2616 14.8 Authorisation:
437          * if authorisation is included in the request, we don't cache,
438          * but we can cache if the following exceptions are true:
439          * 1) If Cache-Control: s-maxage is included
440          * 2) If Cache-Control: must-revalidate is included
441          * 3) If Cache-Control: public is included
442          */
443         reason = "Authorization required";
444     }
445     else if (r->no_cache) {
446         /* or we've been asked not to cache it above */
447         reason = "no_cache present";
448     }
449
450     if (reason) {
451         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
452                      "cache: %s not cached. Reason: %s", url, reason);
453         /* remove this object from the cache 
454          * BillS Asks.. Why do we need to make this call to remove_url?
455          * leave it in for now..
456          */
457         cache_remove_url(r, url);
458
459         /* remove this filter from the chain */
460         ap_remove_output_filter(f);
461
462         /* ship the data up the stack */
463         return ap_pass_brigade(f->next, in);
464     }
465
466     /* Make it so that we don't execute this path again. */
467     cache->in_checked = 1;
468
469     /* Set the content length if known. 
470      */
471     cl = apr_table_get(r->err_headers_out, "Content-Length");
472     if (cl == NULL) {
473         cl = apr_table_get(r->headers_out, "Content-Length");
474     }
475     if (cl) {
476 #if 0
477         char *errp;
478         if (apr_strtoff(&size, cl, &errp, 10) || *errp || size < 0) {
479             cl = NULL; /* parse error, see next 'if' block */
480         }
481 #else
482         size = apr_atoi64(cl);
483         if (size < 0) {
484             cl = NULL;
485         }
486 #endif
487     }
488
489     if (!cl) {
490         /* if we don't get the content-length, see if we have all the 
491          * buckets and use their length to calculate the size 
492          */
493         apr_bucket *e;
494         int all_buckets_here=0;
495         int unresolved_length = 0;
496         size=0;
497         for (e = APR_BRIGADE_FIRST(in);
498              e != APR_BRIGADE_SENTINEL(in);
499              e = APR_BUCKET_NEXT(e))
500         {
501             if (APR_BUCKET_IS_EOS(e)) {
502                 all_buckets_here=1;
503                 break;
504             }
505             if (APR_BUCKET_IS_FLUSH(e)) {
506                 unresolved_length = 1;
507                 continue;
508             }
509             if (e->length == (apr_size_t)-1) {
510                 break;
511             }
512             size += e->length;
513         }
514         if (!all_buckets_here) {
515             size = -1;
516         }
517     }
518
519     /* It's safe to cache the response.
520      *
521      * There are two possiblities at this point:
522      * - cache->handle == NULL. In this case there is no previously
523      * cached entity anywhere on the system. We must create a brand
524      * new entity and store the response in it.
525      * - cache->stale_handle != NULL. In this case there is a stale
526      * entity in the system which needs to be replaced by new
527      * content (unless the result was 304 Not Modified, which means
528      * the cached entity is actually fresh, and we should update
529      * the headers).
530      */
531
532     /* Did we have a stale cache entry that really is stale? */
533     if (cache->stale_handle) {
534         if (r->status == HTTP_NOT_MODIFIED) {
535             /* Oh, hey.  It isn't that stale!  Yay! */
536             cache->handle = cache->stale_handle;
537             info = &cache->handle->cache_obj->info;
538         }
539         else {
540             /* Oh, well.  Toss it. */
541             cache->provider->remove_entity(cache->stale_handle);
542             /* Treat the request as if it wasn't conditional. */
543             cache->stale_handle = NULL;
544         }
545     }
546
547     /* no cache handle, create a new entity */
548     if (!cache->handle) {
549         rv = cache_create_entity(r, url, size);
550         info = apr_pcalloc(r->pool, sizeof(cache_info));
551         /* We only set info->status upon the initial creation. */
552         info->status = r->status;
553     }
554
555     if (rv != OK) {
556         /* Caching layer declined the opportunity to cache the response */
557         ap_remove_output_filter(f);
558         return ap_pass_brigade(f->next, in);
559     }
560
561     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
562                  "cache: Caching url: %s", url);
563
564     /*
565      * We now want to update the cache file header information with
566      * the new date, last modified, expire and content length and write
567      * it away to our cache file. First, we determine these values from
568      * the response, using heuristics if appropriate.
569      *
570      * In addition, we make HTTP/1.1 age calculations and write them away
571      * too.
572      */
573
574     /* Read the date. Generate one if one is not supplied */
575     dates = apr_table_get(r->err_headers_out, "Date");
576     if (dates != NULL) {
577         date_in_errhdr = 1;
578     }
579     else {
580         dates = apr_table_get(r->headers_out, "Date");
581     }
582     if (dates != NULL) {
583         info->date = apr_date_parse_http(dates);
584     }
585     else {
586         info->date = APR_DATE_BAD;
587     }
588
589     now = apr_time_now();
590     if (info->date == APR_DATE_BAD) {  /* No, or bad date */
591         char *dates;
592         /* no date header (or bad header)! */
593         /* add one; N.B. use the time _now_ rather than when we were checking
594          * the cache 
595          */
596         if (date_in_errhdr == 1) {
597             apr_table_unset(r->err_headers_out, "Date");
598         }
599         date = now;
600         dates = apr_pcalloc(r->pool, MAX_STRING_LEN);
601         apr_rfc822_date(dates, now);
602         apr_table_set(r->headers_out, "Date", dates);
603         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
604                      "cache: Added date header");
605         info->date = date;
606     }
607     else {
608         date = info->date;
609     }
610
611     /* set response_time for HTTP/1.1 age calculations */
612     info->response_time = now;
613
614     /* get the request time */
615     info->request_time = r->request_time;
616
617     /* check last-modified date */
618     if (lastmod != APR_DATE_BAD && lastmod > date) {
619         /* if it's in the future, then replace by date */
620         lastmod = date;
621         lastmods = dates;
622         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 
623                      r->server,
624                      "cache: Last modified is in the future, "
625                      "replacing with now");
626     }
627     info->lastmod = lastmod;
628
629     /* if no expiry date then
630      *   if lastmod
631      *      expiry date = date + min((date - lastmod) * factor, maxexpire)
632      *   else
633      *      expire date = date + defaultexpire
634      */
635     if (exp == APR_DATE_BAD) {
636         /* if lastmod == date then you get 0*conf->factor which results in
637          *   an expiration time of now. This causes some problems with
638          *   freshness calculations, so we choose the else path...
639          */
640         if ((lastmod != APR_DATE_BAD) && (lastmod < date)) {
641             apr_time_t x = (apr_time_t) ((date - lastmod) * conf->factor);
642
643             if (x > conf->maxex) {
644                 x = conf->maxex;
645             }
646             exp = date + x;
647         }
648         else {
649             exp = date + conf->defex;
650         }
651     }
652     info->expire = exp;
653
654     info->content_type = apr_pstrdup(r->pool, r->content_type);
655     info->etag = apr_pstrdup(r->pool, etag);
656     info->lastmods = apr_pstrdup(r->pool, lastmods);
657     info->filename = apr_pstrdup(r->pool, r->filename);
658
659     /*
660      * Write away header information to cache.
661      */
662     rv = cache->provider->store_headers(cache->handle, r, info);
663
664     /* Did we actually find an entity before, but it wasn't really stale? */
665     if (rv == APR_SUCCESS && cache->stale_handle) {
666         apr_bucket_brigade *bb;
667         apr_bucket *bkt;
668
669         bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
670
671         /* Were we initially a conditional request? */
672         if (ap_cache_request_is_conditional(cache->stale_headers)) {
673             /* FIXME: Should we now go and make sure it's really not
674              * modified since what the user thought?
675              */
676             bkt = apr_bucket_eos_create(bb->bucket_alloc);
677             APR_BRIGADE_INSERT_TAIL(bb, bkt);
678         }
679         else {
680             r->status = info->status;
681             cache->provider->recall_body(cache->handle, r->pool, bb);
682         }
683
684         cache->block_response = 1;
685         return ap_pass_brigade(f->next, bb);
686     }
687
688     if (rv == APR_SUCCESS) {
689         rv = cache->provider->store_body(cache->handle, r, in);
690     }
691     if (rv != APR_SUCCESS) {
692         ap_remove_output_filter(f);
693     }
694
695     return ap_pass_brigade(f->next, in);
696 }
697
698 /* -------------------------------------------------------------- */
699 /* Setup configurable data */
700
701 static void * create_cache_config(apr_pool_t *p, server_rec *s)
702 {
703     cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
704
705     /* array of URL prefixes for which caching is enabled */
706     ps->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
707     /* array of URL prefixes for which caching is disabled */
708     ps->cachedisable = apr_array_make(p, 10, sizeof(struct cache_disable));
709     /* maximum time to cache a document */
710     ps->maxex = DEFAULT_CACHE_MAXEXPIRE;
711     ps->maxex_set = 0;
712     /* default time to cache a document */
713     ps->defex = DEFAULT_CACHE_EXPIRE;
714     ps->defex_set = 0;
715     /* factor used to estimate Expires date from LastModified date */
716     ps->factor = DEFAULT_CACHE_LMFACTOR;
717     ps->factor_set = 0;
718     /* default percentage to force cache completion */
719     ps->complete = DEFAULT_CACHE_COMPLETION;
720     ps->complete_set = 0;
721     ps->no_last_mod_ignore_set = 0;
722     ps->no_last_mod_ignore = 0;
723     ps->ignorecachecontrol = 0;
724     ps->ignorecachecontrol_set = 0 ;
725     /* array of headers that should not be stored in cache */
726     ps->ignore_headers = apr_array_make(p, 10, sizeof(char *));
727     ps->ignore_headers_set = CACHE_IGNORE_HEADERS_UNSET;
728     return ps;
729 }
730
731 static void * merge_cache_config(apr_pool_t *p, void *basev, void *overridesv)
732 {
733     cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
734     cache_server_conf *base = (cache_server_conf *) basev;
735     cache_server_conf *overrides = (cache_server_conf *) overridesv;
736
737     /* array of URL prefixes for which caching is disabled */
738     ps->cachedisable = apr_array_append(p, 
739                                         base->cachedisable, 
740                                         overrides->cachedisable);
741     /* array of URL prefixes for which caching is enabled */
742     ps->cacheenable = apr_array_append(p, 
743                                        base->cacheenable, 
744                                        overrides->cacheenable);
745     /* maximum time to cache a document */
746     ps->maxex = (overrides->maxex_set == 0) ? base->maxex : overrides->maxex;
747     /* default time to cache a document */
748     ps->defex = (overrides->defex_set == 0) ? base->defex : overrides->defex;
749     /* factor used to estimate Expires date from LastModified date */
750     ps->factor =
751         (overrides->factor_set == 0) ? base->factor : overrides->factor;
752     /* default percentage to force cache completion */
753     ps->complete =
754         (overrides->complete_set == 0) ? base->complete : overrides->complete;
755
756     ps->no_last_mod_ignore =
757         (overrides->no_last_mod_ignore_set == 0)
758         ? base->no_last_mod_ignore
759         : overrides->no_last_mod_ignore;
760     ps->ignorecachecontrol  =
761         (overrides->ignorecachecontrol_set == 0)
762         ? base->ignorecachecontrol
763         : overrides->ignorecachecontrol;
764     ps->ignore_headers =
765         (overrides->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET)
766         ? base->ignore_headers
767         : overrides->ignore_headers;
768     return ps;
769 }
770 static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
771                                                 int flag)
772 {
773     cache_server_conf *conf;
774
775     conf =
776         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
777                                                   &cache_module);
778     conf->no_last_mod_ignore = flag;
779     conf->no_last_mod_ignore_set = 1;
780     return NULL;
781
782 }
783
784 static const char *set_cache_ignore_cachecontrol(cmd_parms *parms,
785                                                  void *dummy, int flag)
786 {
787     cache_server_conf *conf;
788
789     conf =
790         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
791                                                   &cache_module);
792     conf->ignorecachecontrol = flag;
793     conf->ignorecachecontrol_set = 1;
794     return NULL;
795 }
796
797 static const char *add_ignore_header(cmd_parms *parms, void *dummy,
798                                      const char *header)
799 {
800     cache_server_conf *conf;
801     char **new;
802
803     conf =
804         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
805                                                   &cache_module);
806     if (!strncasecmp(header, "None", 4)) {
807         /* if header None is listed clear array */
808         conf->ignore_headers->nelts = 0;
809     }
810     else {
811         if ((conf->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET) ||
812             (conf->ignore_headers->nelts)) {
813             /* Only add header if no "None" has been found in header list
814              * so far.
815              * (When 'None' is passed, IGNORE_HEADERS_SET && nelts == 0.)
816              */
817             new = (char **)apr_array_push(conf->ignore_headers);
818             (*new) = (char*)header;
819         }
820     }
821     conf->ignore_headers_set = CACHE_IGNORE_HEADERS_SET;
822     return NULL;
823 }
824
825 static const char *add_cache_enable(cmd_parms *parms, void *dummy, 
826                                     const char *type, 
827                                     const char *url)
828 {
829     cache_server_conf *conf;
830     struct cache_enable *new;
831
832     conf =
833         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
834                                                   &cache_module);
835     new = apr_array_push(conf->cacheenable);
836     new->type = type;
837     new->url = url;
838     new->urllen = strlen(url);
839     return NULL;
840 }
841
842 static const char *add_cache_disable(cmd_parms *parms, void *dummy,
843                                      const char *url)
844 {
845     cache_server_conf *conf;
846     struct cache_disable *new;
847
848     conf =
849         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
850                                                   &cache_module);
851     new = apr_array_push(conf->cachedisable);
852     new->url = url;
853     new->urllen = strlen(url);
854     return NULL;
855 }
856
857 static const char *set_cache_maxex(cmd_parms *parms, void *dummy,
858                                    const char *arg)
859 {
860     cache_server_conf *conf;
861
862     conf =
863         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
864                                                   &cache_module);
865     conf->maxex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
866     conf->maxex_set = 1;
867     return NULL;
868 }
869
870 static const char *set_cache_defex(cmd_parms *parms, void *dummy,
871                                    const char *arg)
872 {
873     cache_server_conf *conf;
874
875     conf =
876         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
877                                                   &cache_module);
878     conf->defex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
879     conf->defex_set = 1;
880     return NULL;
881 }
882
883 static const char *set_cache_factor(cmd_parms *parms, void *dummy,
884                                     const char *arg)
885 {
886     cache_server_conf *conf;
887     double val;
888
889     conf =
890         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
891                                                   &cache_module);
892     if (sscanf(arg, "%lg", &val) != 1) {
893         return "CacheLastModifiedFactor value must be a float";
894     }
895     conf->factor = val;
896     conf->factor_set = 1;
897     return NULL;
898 }
899
900 static const char *set_cache_complete(cmd_parms *parms, void *dummy,
901                                       const char *arg)
902 {
903     cache_server_conf *conf;
904     int val;
905
906     conf =
907         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
908                                                   &cache_module);
909     if (sscanf(arg, "%u", &val) != 1) {
910         return "CacheForceCompletion value must be a percentage";
911     }
912     conf->complete = val;
913     conf->complete_set = 1;
914     return NULL;
915 }
916
917 static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
918                              apr_pool_t *ptemp, server_rec *s)
919 {
920     /* This is the means by which unusual (non-unix) os's may find alternate
921      * means to run a given command (e.g. shebang/registry parsing on Win32)
922      */
923     cache_generate_key = APR_RETRIEVE_OPTIONAL_FN(ap_cache_generate_key);
924     if (!cache_generate_key) {
925         cache_generate_key = cache_generate_key_default;
926     }
927     return OK;
928 }
929
930 static const command_rec cache_cmds[] =
931 {
932     /* XXX
933      * Consider a new config directive that enables loading specific cache
934      * implememtations (like mod_cache_mem, mod_cache_file, etc.).
935      * Rather than using a LoadModule directive, admin would use something
936      * like CacheModule  mem_cache_module | file_cache_module, etc,
937      * which would cause the approprpriate cache module to be loaded.
938      * This is more intuitive that requiring a LoadModule directive.
939      */
940
941     AP_INIT_TAKE2("CacheEnable", add_cache_enable, NULL, RSRC_CONF,
942                   "A cache type and partial URL prefix below which "
943                   "caching is enabled"),
944     AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF,
945                   "A partial URL prefix below which caching is disabled"),
946     AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF,
947                   "The maximum time in seconds to cache a document"),
948      AP_INIT_TAKE1("CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF,
949                    "The default time in seconds to cache a document"),
950      AP_INIT_FLAG("CacheIgnoreNoLastMod", set_cache_ignore_no_last_mod, NULL, 
951                   RSRC_CONF, 
952                   "Ignore Responses where there is no Last Modified Header"),
953      AP_INIT_FLAG("CacheIgnoreCacheControl", set_cache_ignore_cachecontrol,
954                   NULL, 
955                   RSRC_CONF, 
956                   "Ignore requests from the client for uncached content"),
957     AP_INIT_ITERATE("CacheIgnoreHeaders", add_ignore_header, NULL, RSRC_CONF,
958                     "A space separated list of headers that should not be "
959                     "stored by the cache"),
960     AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF,
961                   "The factor used to estimate Expires date from "
962                   "LastModified date"),
963     AP_INIT_TAKE1("CacheForceCompletion", set_cache_complete, NULL, RSRC_CONF,
964                   "Percentage of download to arrive for the cache to force "
965                   "complete transfer"),
966     {NULL}
967 };
968
969 static void register_hooks(apr_pool_t *p)
970 {
971     /* cache initializer */
972     /* cache handler */
973     ap_hook_quick_handler(cache_url_handler, NULL, NULL, APR_HOOK_FIRST);
974     /* cache filters 
975      * XXX The cache filters need to run right after the handlers and before
976      * any other filters. Consider creating AP_FTYPE_CACHE for this purpose.
977      * Make them AP_FTYPE_CONTENT for now.
978      * XXX ianhH:they should run AFTER all the other content filters.
979      */
980     cache_save_filter_handle = 
981         ap_register_output_filter("CACHE_SAVE", 
982                                   cache_save_filter, 
983                                   NULL,
984                                   AP_FTYPE_CONTENT_SET-1);
985     /* CACHE_OUT must go into the filter chain before SUBREQ_CORE to
986      * handle subrequsts. Decrementing filter type by 1 ensures this 
987      * happens.
988      */
989     cache_out_filter_handle = 
990         ap_register_output_filter("CACHE_OUT", 
991                                   cache_out_filter, 
992                                   NULL,
993                                   AP_FTYPE_CONTENT_SET-1);
994     ap_hook_post_config(cache_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
995 }
996
997 module AP_MODULE_DECLARE_DATA cache_module =
998 {
999     STANDARD20_MODULE_STUFF,
1000     NULL,                   /* create per-directory config structure */
1001     NULL,                   /* merge per-directory config structures */
1002     create_cache_config,    /* create per-server config structure */
1003     merge_cache_config,     /* merge per-server config structures */
1004     cache_cmds,             /* command apr_table_t */
1005     register_hooks
1006 };