]> granicus.if.org Git - apache/blob - modules/cache/mod_cache.c
8212d63fdacfcdd9e9842b513d867a7d7a6abce4
[apache] / modules / cache / 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 #include "mod_cache.h"
18
19 module AP_MODULE_DECLARE_DATA cache_module;
20 APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
21
22 /* -------------------------------------------------------------- */
23
24
25 /* Handles for cache filters, resolved at startup to eliminate
26  * a name-to-function mapping on each request
27  */
28 static ap_filter_rec_t *cache_save_filter_handle;
29 static ap_filter_rec_t *cache_save_subreq_filter_handle;
30 static ap_filter_rec_t *cache_out_filter_handle;
31 static ap_filter_rec_t *cache_out_subreq_filter_handle;
32 static ap_filter_rec_t *cache_remove_url_filter_handle;
33
34 /*
35  * CACHE handler
36  * -------------
37  *
38  * Can we deliver this request from the cache?
39  * If yes:
40  *   deliver the content by installing the CACHE_OUT filter.
41  * If no:
42  *   check whether we're allowed to try cache it
43  *   If yes:
44  *     add CACHE_SAVE filter
45  *   If No:
46  *     oh well.
47  */
48
49 static int cache_url_handler(request_rec *r, int lookup)
50 {
51     apr_status_t rv;
52     const char *auth;
53     cache_provider_list *providers;
54     cache_request_rec *cache;
55     cache_server_conf *conf;
56     apr_bucket_brigade *out;
57     ap_filter_t *next;
58     ap_filter_rec_t *cache_out_handle;
59
60     /* Delay initialization until we know we are handling a GET */
61     if (r->method_number != M_GET) {
62         return DECLINED;
63     }
64
65     conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
66                                                       &cache_module);
67
68     /*
69      * Which cache module (if any) should handle this request?
70      */
71     if (!(providers = ap_cache_get_providers(r, conf, r->parsed_uri))) {
72         return DECLINED;
73     }
74
75     /* make space for the per request config */
76     cache = (cache_request_rec *) ap_get_module_config(r->request_config,
77                                                        &cache_module);
78     if (!cache) {
79         cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
80         ap_set_module_config(r->request_config, &cache_module, cache);
81     }
82
83     /* save away the possible providers */
84     cache->providers = providers;
85
86     /*
87      * Are we allowed to serve cached info at all?
88      */
89
90     /* find certain cache controlling headers */
91     auth = apr_table_get(r->headers_in, "Authorization");
92
93     /* First things first - does the request allow us to return
94      * cached information at all? If not, just decline the request.
95      */
96     if (auth) {
97         return DECLINED;
98     }
99
100     /*
101      * Try to serve this request from the cache.
102      *
103      * If no existing cache file (DECLINED)
104      *   add cache_save filter
105      * If cached file (OK)
106      *   clear filter stack
107      *   add cache_out filter
108      *   return OK
109      */
110     rv = cache_select(r);
111     if (rv != OK) {
112         if (rv == DECLINED) {
113             if (!lookup) {
114
115                 /*
116                  * Add cache_save filter to cache this request. Choose
117                  * the correct filter by checking if we are a subrequest
118                  * or not.
119                  */
120                 if (r->main) {
121                     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
122                                  r->server,
123                                  "Adding CACHE_SAVE_SUBREQ filter for %s",
124                                  r->uri);
125                     ap_add_output_filter_handle(cache_save_subreq_filter_handle,
126                                                 NULL, r, r->connection);
127                 }
128                 else {
129                     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
130                                  r->server, "Adding CACHE_SAVE filter for %s",
131                                  r->uri);
132                     ap_add_output_filter_handle(cache_save_filter_handle,
133                                                 NULL, r, r->connection);
134                 }
135
136                 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
137                              "Adding CACHE_REMOVE_URL filter for %s",
138                              r->uri);
139
140                 /* Add cache_remove_url filter to this request to remove a
141                  * stale cache entry if needed. Also put the current cache
142                  * request rec in the filter context, as the request that
143                  * is available later during running the filter maybe
144                  * different due to an internal redirect.
145                  */
146                 cache->remove_url_filter =
147                     ap_add_output_filter_handle(cache_remove_url_filter_handle,
148                                                 cache, r, r->connection);
149             }
150             else {
151                 if (cache->stale_headers) {
152                     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
153                                  r->server, "Restoring request headers for %s",
154                                  r->uri);
155
156                     r->headers_in = cache->stale_headers;
157                 }
158
159                 /* Delete our per-request configuration. */
160                 ap_set_module_config(r->request_config, &cache_module, NULL);
161             }
162         }
163         else {
164             /* error */
165             ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
166                          "cache: error returned while checking for cached "
167                          "file by %s cache", cache->provider_name);
168         }
169         return DECLINED;
170     }
171
172     /* if we are a lookup, we are exiting soon one way or another; Restore
173      * the headers. */
174     if (lookup) {
175         if (cache->stale_headers) {
176             ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
177                          "Restoring request headers.");
178             r->headers_in = cache->stale_headers;
179         }
180
181         /* Delete our per-request configuration. */
182         ap_set_module_config(r->request_config, &cache_module, NULL);
183     }
184
185     rv = ap_meets_conditions(r);
186     if (rv != OK) {
187         /* If we are a lookup, we have to return DECLINED as we have no
188          * way of knowing if we will be able to serve the content.
189          */
190         if (lookup) {
191             return DECLINED;
192         }
193
194         /* Return cached status. */
195         return rv;
196     }
197
198     /* If we're a lookup, we can exit now instead of serving the content. */
199     if (lookup) {
200         return OK;
201     }
202
203     /* Serve up the content */
204
205     /* We are in the quick handler hook, which means that no output
206      * filters have been set. So lets run the insert_filter hook.
207      */
208     ap_run_insert_filter(r);
209
210     /*
211      * Add cache_out filter to serve this request. Choose
212      * the correct filter by checking if we are a subrequest
213      * or not.
214      */
215     if (r->main) {
216         cache_out_handle = cache_out_subreq_filter_handle;
217     }
218     else {
219         cache_out_handle = cache_out_filter_handle;
220     }
221     ap_add_output_filter_handle(cache_out_handle, NULL, r, r->connection);
222
223     /*
224      * Remove all filters that are before the cache_out filter. This ensures
225      * that we kick off the filter stack with our cache_out filter being the
226      * first in the chain. This make sense because we want to restore things
227      * in the same manner as we saved them.
228      * There may be filters before our cache_out filter, because
229      *
230      * 1. We call ap_set_content_type during cache_select. This causes
231      *    Content-Type specific filters to be added.
232      * 2. We call the insert_filter hook. This causes filters e.g. like
233      *    the ones set with SetOutputFilter to be added.
234      */
235     next = r->output_filters;
236     while (next && (next->frec != cache_out_handle)) {
237         ap_remove_output_filter(next);
238         next = next->next;
239     }
240
241     /* kick off the filter stack */
242     out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
243     rv = ap_pass_brigade(r->output_filters, out);
244     if (rv != APR_SUCCESS) {
245         if (rv != AP_FILTER_ERROR) {
246             ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
247                          "cache: error returned while trying to return %s "
248                          "cached data",
249                          cache->provider_name);
250         }
251         return rv;
252     }
253
254     return OK;
255 }
256
257 /*
258  * CACHE_OUT filter
259  * ----------------
260  *
261  * Deliver cached content (headers and body) up the stack.
262  */
263 static int cache_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
264 {
265     request_rec *r = f->r;
266     cache_request_rec *cache;
267
268     cache = (cache_request_rec *) ap_get_module_config(r->request_config,
269                                                        &cache_module);
270
271     if (!cache) {
272         /* user likely configured CACHE_OUT manually; they should use mod_cache
273          * configuration to do that */
274         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
275                      "CACHE_OUT enabled unexpectedly");
276         ap_remove_output_filter(f);
277         return ap_pass_brigade(f->next, bb);
278     }
279
280     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
281                  "cache: running CACHE_OUT filter");
282
283     /* restore status of cached response */
284     /* XXX: This exposes a bug in mem_cache, since it does not
285      * restore the status into it's handle. */
286     r->status = cache->handle->cache_obj->info.status;
287
288     /* recall_headers() was called in cache_select() */
289     cache->provider->recall_body(cache->handle, r->pool, bb);
290
291     /* This filter is done once it has served up its content */
292     ap_remove_output_filter(f);
293
294     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
295                  "cache: serving %s", r->uri);
296     return ap_pass_brigade(f->next, bb);
297 }
298
299
300 /*
301  * CACHE_SAVE filter
302  * ---------------
303  *
304  * Decide whether or not this content should be cached.
305  * If we decide no it should not:
306  *   remove the filter from the chain
307  * If we decide yes it should:
308  *   Have we already started saving the response?
309  *      If we have started, pass the data to the storage manager via store_body
310  *      Otherwise:
311  *        Check to see if we *can* save this particular response.
312  *        If we can, call cache_create_entity() and save the headers and body
313  *   Finally, pass the data to the next filter (the network or whatever)
314  */
315
316 static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
317 {
318     int rv = !OK;
319     request_rec *r = f->r;
320     cache_request_rec *cache;
321     cache_server_conf *conf;
322     const char *cc_out, *cl;
323     const char *exps, *lastmods, *dates, *etag;
324     apr_time_t exp, date, lastmod, now;
325     apr_off_t size;
326     cache_info *info = NULL;
327     char *reason;
328     apr_pool_t *p;
329
330     conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
331                                                       &cache_module);
332
333     /* Setup cache_request_rec */
334     cache = (cache_request_rec *) ap_get_module_config(r->request_config,
335                                                        &cache_module);
336     if (!cache) {
337         /* user likely configured CACHE_SAVE manually; they should really use
338          * mod_cache configuration to do that
339          */
340         cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
341         ap_set_module_config(r->request_config, &cache_module, cache);
342     }
343
344     reason = NULL;
345     p = r->pool;
346     /*
347      * Pass Data to Cache
348      * ------------------
349      * This section passes the brigades into the cache modules, but only
350      * if the setup section (see below) is complete.
351      */
352     if (cache->block_response) {
353         /* We've already sent down the response and EOS.  So, ignore
354          * whatever comes now.
355          */
356         return APR_SUCCESS;
357     }
358
359     /* have we already run the cachability check and set up the
360      * cached file handle?
361      */
362     if (cache->in_checked) {
363         /* pass the brigades into the cache, then pass them
364          * up the filter stack
365          */
366         rv = cache->provider->store_body(cache->handle, r, in);
367         if (rv != APR_SUCCESS) {
368             ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
369                          "cache: Cache provider's store_body failed!");
370             ap_remove_output_filter(f);
371         }
372         return ap_pass_brigade(f->next, in);
373     }
374
375     /*
376      * Setup Data in Cache
377      * -------------------
378      * This section opens the cache entity and sets various caching
379      * parameters, and decides whether this URL should be cached at
380      * all. This section is* run before the above section.
381      */
382
383     /* read expiry date; if a bad date, then leave it so the client can
384      * read it
385      */
386     exps = apr_table_get(r->err_headers_out, "Expires");
387     if (exps == NULL) {
388         exps = apr_table_get(r->headers_out, "Expires");
389     }
390     if (exps != NULL) {
391         if (APR_DATE_BAD == (exp = apr_date_parse_http(exps))) {
392             exps = NULL;
393         }
394     }
395     else {
396         exp = APR_DATE_BAD;
397     }
398
399     /* read the last-modified date; if the date is bad, then delete it */
400     lastmods = apr_table_get(r->err_headers_out, "Last-Modified");
401     if (lastmods == NULL) {
402         lastmods = apr_table_get(r->headers_out, "Last-Modified");
403     }
404     if (lastmods != NULL) {
405         lastmod = apr_date_parse_http(lastmods);
406         if (lastmod == APR_DATE_BAD) {
407             lastmods = NULL;
408         }
409     }
410     else {
411         lastmod = APR_DATE_BAD;
412     }
413
414     /* read the etag and cache-control from the entity */
415     etag = apr_table_get(r->err_headers_out, "Etag");
416     if (etag == NULL) {
417         etag = apr_table_get(r->headers_out, "Etag");
418     }
419     cc_out = apr_table_get(r->err_headers_out, "Cache-Control");
420     if (cc_out == NULL) {
421         cc_out = apr_table_get(r->headers_out, "Cache-Control");
422     }
423
424     /*
425      * what responses should we not cache?
426      *
427      * At this point we decide based on the response headers whether it
428      * is appropriate _NOT_ to cache the data from the server. There are
429      * a whole lot of conditions that prevent us from caching this data.
430      * They are tested here one by one to be clear and unambiguous.
431      */
432     if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
433         && r->status != HTTP_MULTIPLE_CHOICES
434         && r->status != HTTP_MOVED_PERMANENTLY
435         && r->status != HTTP_NOT_MODIFIED) {
436         /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
437          * We don't cache 206, because we don't (yet) cache partial responses.
438          * We include 304 Not Modified here too as this is the origin server
439          * telling us to serve the cached copy.
440          */
441         reason = apr_psprintf(p, "Response status %d", r->status);
442     }
443     else if (exps != NULL && exp == APR_DATE_BAD) {
444         /* if a broken Expires header is present, don't cache it */
445         reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL);
446     }
447     else if (exp != APR_DATE_BAD && exp < r->request_time)
448     {
449         /* if a Expires header is in the past, don't cache it */
450         reason = "Expires header already expired, not cacheable";
451     }
452     else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL &&
453              !ap_cache_liststr(NULL, cc_out, "max-age", NULL)) {
454         /* if a query string is present but no explicit expiration time,
455          * don't cache it (RFC 2616/13.9 & 13.2.1)
456          */
457         reason = "Query string present but no explicit expiration time";
458     }
459     else if (r->status == HTTP_NOT_MODIFIED &&
460              !cache->handle && !cache->stale_handle) {
461         /* if the server said 304 Not Modified but we have no cache
462          * file - pass this untouched to the user agent, it's not for us.
463          */
464         reason = "HTTP Status 304 Not Modified";
465     }
466     else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL
467              && (exps == NULL) && (conf->no_last_mod_ignore ==0)) {
468         /* 200 OK response from HTTP/1.0 and up without Last-Modified,
469          * Etag, or Expires headers.
470          */
471         /* Note: mod-include clears last_modified/expires/etags - this
472          * is why we have an optional function for a key-gen ;-)
473          */
474         reason = "No Last-Modified, Etag, or Expires headers";
475     }
476     else if (r->header_only && !cache->stale_handle) {
477         /* Forbid HEAD requests unless we have it cached already */
478         reason = "HTTP HEAD request";
479     }
480     else if (!conf->store_nostore &&
481              ap_cache_liststr(NULL, cc_out, "no-store", NULL)) {
482         /* RFC2616 14.9.2 Cache-Control: no-store response
483          * indicating do not cache, or stop now if you are
484          * trying to cache it.
485          */
486         /* FIXME: The Cache-Control: no-store could have come in on a 304,
487          * FIXME: while the original request wasn't conditional.  IOW, we
488          * FIXME:  made the the request conditional earlier to revalidate
489          * FIXME: our cached response.
490          */
491         reason = "Cache-Control: no-store present";
492     }
493     else if (!conf->store_private &&
494              ap_cache_liststr(NULL, cc_out, "private", NULL)) {
495         /* RFC2616 14.9.1 Cache-Control: private response
496          * this object is marked for this user's eyes only. Behave
497          * as a tunnel.
498          */
499         /* FIXME: See above (no-store) */
500         reason = "Cache-Control: private present";
501     }
502     else if (apr_table_get(r->headers_in, "Authorization") != NULL
503              && !(ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)
504                   || ap_cache_liststr(NULL, cc_out, "must-revalidate", NULL)
505                   || ap_cache_liststr(NULL, cc_out, "public", NULL))) {
506         /* RFC2616 14.8 Authorisation:
507          * if authorisation is included in the request, we don't cache,
508          * but we can cache if the following exceptions are true:
509          * 1) If Cache-Control: s-maxage is included
510          * 2) If Cache-Control: must-revalidate is included
511          * 3) If Cache-Control: public is included
512          */
513         reason = "Authorization required";
514     }
515     else if (ap_cache_liststr(NULL,
516                               apr_table_get(r->headers_out, "Vary"),
517                               "*", NULL)) {
518         reason = "Vary header contains '*'";
519     }
520     else if (r->no_cache) {
521         /* or we've been asked not to cache it above */
522         reason = "r->no_cache present";
523     }
524
525     if (reason) {
526         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
527                      "cache: %s not cached. Reason: %s", r->unparsed_uri,
528                      reason);
529
530         /* remove this filter from the chain */
531         ap_remove_output_filter(f);
532
533         /* ship the data up the stack */
534         return ap_pass_brigade(f->next, in);
535     }
536
537     /* Make it so that we don't execute this path again. */
538     cache->in_checked = 1;
539
540     /* Set the content length if known.
541      */
542     cl = apr_table_get(r->err_headers_out, "Content-Length");
543     if (cl == NULL) {
544         cl = apr_table_get(r->headers_out, "Content-Length");
545     }
546     if (cl) {
547         char *errp;
548         if (apr_strtoff(&size, cl, &errp, 10) || *errp || size < 0) {
549             cl = NULL; /* parse error, see next 'if' block */
550         }
551     }
552
553     if (!cl) {
554         /* if we don't get the content-length, see if we have all the
555          * buckets and use their length to calculate the size
556          */
557         apr_bucket *e;
558         int all_buckets_here=0;
559         int unresolved_length = 0;
560         size=0;
561         for (e = APR_BRIGADE_FIRST(in);
562              e != APR_BRIGADE_SENTINEL(in);
563              e = APR_BUCKET_NEXT(e))
564         {
565             if (APR_BUCKET_IS_EOS(e)) {
566                 all_buckets_here=1;
567                 break;
568             }
569             if (APR_BUCKET_IS_FLUSH(e)) {
570                 unresolved_length = 1;
571                 continue;
572             }
573             if (e->length == (apr_size_t)-1) {
574                 break;
575             }
576             size += e->length;
577         }
578         if (!all_buckets_here) {
579             size = -1;
580         }
581     }
582
583     /* It's safe to cache the response.
584      *
585      * There are two possiblities at this point:
586      * - cache->handle == NULL. In this case there is no previously
587      * cached entity anywhere on the system. We must create a brand
588      * new entity and store the response in it.
589      * - cache->stale_handle != NULL. In this case there is a stale
590      * entity in the system which needs to be replaced by new
591      * content (unless the result was 304 Not Modified, which means
592      * the cached entity is actually fresh, and we should update
593      * the headers).
594      */
595
596     /* Did we have a stale cache entry that really is stale?
597      *
598      * Note that for HEAD requests, we won't get the body, so for a stale
599      * HEAD request, we don't remove the entity - instead we let the
600      * CACHE_REMOVE_URL filter remove the stale item from the cache.
601      */
602     if (cache->stale_handle) {
603         if (r->status == HTTP_NOT_MODIFIED) {
604             /* Oh, hey.  It isn't that stale!  Yay! */
605             cache->handle = cache->stale_handle;
606             info = &cache->handle->cache_obj->info;
607             rv = OK;
608         }
609         else if (!r->header_only) {
610             /* Oh, well.  Toss it. */
611             cache->provider->remove_entity(cache->stale_handle);
612             /* Treat the request as if it wasn't conditional. */
613             cache->stale_handle = NULL;
614             /*
615              * Restore the original request headers as they may be needed
616              * by further output filters like the byterange filter to make
617              * the correct decisions.
618              */
619             r->headers_in = cache->stale_headers;
620         }
621     }
622
623     /* no cache handle, create a new entity only for non-HEAD requests */
624     if (!cache->handle && !r->header_only) {
625         rv = cache_create_entity(r, size);
626         info = apr_pcalloc(r->pool, sizeof(cache_info));
627         /* We only set info->status upon the initial creation. */
628         info->status = r->status;
629     }
630
631     if (rv != OK) {
632         /* Caching layer declined the opportunity to cache the response */
633         ap_remove_output_filter(f);
634         return ap_pass_brigade(f->next, in);
635     }
636
637     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
638                  "cache: Caching url: %s", r->unparsed_uri);
639
640     /* We are actually caching this response. So it does not
641      * make sense to remove this entity any more.
642      */
643     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
644                  "cache: Removing CACHE_REMOVE_URL filter.");
645     ap_remove_output_filter(cache->remove_url_filter);
646
647     /*
648      * We now want to update the cache file header information with
649      * the new date, last modified, expire and content length and write
650      * it away to our cache file. First, we determine these values from
651      * the response, using heuristics if appropriate.
652      *
653      * In addition, we make HTTP/1.1 age calculations and write them away
654      * too.
655      */
656
657     /* Read the date. Generate one if one is not supplied */
658     dates = apr_table_get(r->err_headers_out, "Date");
659     if (dates == NULL) {
660         dates = apr_table_get(r->headers_out, "Date");
661     }
662     if (dates != NULL) {
663         info->date = apr_date_parse_http(dates);
664     }
665     else {
666         info->date = APR_DATE_BAD;
667     }
668
669     now = apr_time_now();
670     if (info->date == APR_DATE_BAD) {  /* No, or bad date */
671         /* no date header (or bad header)! */
672         info->date = now;
673     }
674     date = info->date;
675
676     /* set response_time for HTTP/1.1 age calculations */
677     info->response_time = now;
678
679     /* get the request time */
680     info->request_time = r->request_time;
681
682     /* check last-modified date */
683     if (lastmod != APR_DATE_BAD && lastmod > date) {
684         /* if it's in the future, then replace by date */
685         lastmod = date;
686         lastmods = dates;
687         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
688                      r->server,
689                      "cache: Last modified is in the future, "
690                      "replacing with now");
691     }
692
693     /* if no expiry date then
694      *   if Cache-Control: max-age
695      *      expiry date = date + max-age
696      *   else if lastmod
697      *      expiry date = date + min((date - lastmod) * factor, maxexpire)
698      *   else
699      *      expire date = date + defaultexpire
700      */
701     if (exp == APR_DATE_BAD) {
702         char *max_age_val;
703
704         if (ap_cache_liststr(r->pool, cc_out, "max-age", &max_age_val) &&
705             max_age_val != NULL) {
706             apr_int64_t x;
707
708             errno = 0;
709             x = apr_atoi64(max_age_val);
710             if (errno) {
711                 x = conf->defex;
712             }
713             else {
714                 x = x * MSEC_ONE_SEC;
715             }
716             if (x < conf->minex) {
717                 x = conf->minex;
718             }
719             if (x > conf->maxex) {
720                 x = conf->maxex;
721             }
722             exp = date + x;
723         }
724         else if ((lastmod != APR_DATE_BAD) && (lastmod < date)) {
725             /* if lastmod == date then you get 0*conf->factor which results in
726              * an expiration time of now. This causes some problems with
727              * freshness calculations, so we choose the else path...
728              */
729             apr_time_t x = (apr_time_t) ((date - lastmod) * conf->factor);
730
731             if (x < conf->minex) {
732                 x = conf->minex;
733             }
734             if (x > conf->maxex) {
735                 x = conf->maxex;
736             }
737             exp = date + x;
738         }
739         else {
740             exp = date + conf->defex;
741         }
742     }
743     info->expire = exp;
744
745     /* We found a stale entry which wasn't really stale. */
746     if (cache->stale_handle) {
747         /* Load in the saved status and clear the status line. */
748         r->status = info->status;
749         r->status_line = NULL;
750
751         /* RFC 2616 10.3.5 states that entity headers are not supposed
752          * to be in the 304 response.  Therefore, we need to combine the
753          * response headers with the cached headers *before* we update
754          * the cached headers.
755          *
756          * However, before doing that, we need to first merge in
757          * err_headers_out and we also need to strip any hop-by-hop
758          * headers that might have snuck in.
759          */
760         r->headers_out = apr_table_overlay(r->pool, r->headers_out,
761                                            r->err_headers_out);
762
763         /* XXX check -- we're not patching up content-type - i.e. this
764          *     propably should be ap_cache_cacheable_headers_out().
765          */
766         r->headers_out = ap_cache_cacheable_headers(r->pool, r->headers_out,
767                                                      r->server);
768         apr_table_clear(r->err_headers_out);
769
770         /* Merge in our cached headers.  However, keep any updated values. */
771         ap_cache_accept_headers(cache->handle, r, 1);
772     }
773
774     /* Write away header information to cache. It is possible that we are
775      * trying to update headers for an entity which has already been cached.
776      *
777      * This may fail, due to an unwritable cache area. E.g. filesystem full,
778      * permissions problems or a read-only (re)mount. This must be handled
779      * later.
780      */
781     rv = cache->provider->store_headers(cache->handle, r, info);
782
783     /* Did we just update the cached headers on a revalidated response?
784      *
785      * If so, we can now decide what to serve to the client.  This is done in
786      * the same way as with a regular response, but conditions are now checked
787      * against the cached or merged response headers.
788      */
789     if (cache->stale_handle) {
790         apr_bucket_brigade *bb;
791         apr_bucket *bkt;
792         int status;
793
794         bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
795
796         /* Restore the original request headers and see if we need to
797          * return anything else than the cached response (ie. the original
798          * request was conditional).
799          */
800         r->headers_in = cache->stale_headers;
801         status = ap_meets_conditions(r);
802         if (status != OK) {
803             r->status = status;
804
805             bkt = apr_bucket_flush_create(bb->bucket_alloc);
806             APR_BRIGADE_INSERT_TAIL(bb, bkt);
807         }
808         else {
809             cache->provider->recall_body(cache->handle, r->pool, bb);
810         }
811
812         cache->block_response = 1;
813
814         /* Before returning we need to handle the possible case of an
815          * unwritable cache. Rather than leaving the entity in the cache
816          * and having it constantly re-validated, now that we have recalled
817          * the body it is safe to try and remove the url from the cache.
818          */
819         if (rv != APR_SUCCESS) {
820             ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
821                          "cache: updating headers with store_headers failed. "
822                          "Removing cached url.");
823
824             rv = cache->provider->remove_url(cache->stale_handle, r->pool);
825             if (rv != OK) {
826                 /* Probably a mod_disk_cache cache area has been (re)mounted
827                  * read-only, or that there is a permissions problem.
828                  */
829                 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
830                      "cache: attempt to remove url from cache unsuccessful.");
831             }
832         }
833
834         return ap_pass_brigade(f->next, bb);
835     }
836
837     if(rv != APR_SUCCESS) {
838         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
839                      "cache: store_headers failed");
840         ap_remove_output_filter(f);
841
842         return ap_pass_brigade(f->next, in);
843     }
844
845     rv = cache->provider->store_body(cache->handle, r, in);
846     if (rv != APR_SUCCESS) {
847         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
848                      "cache: store_body failed");
849         ap_remove_output_filter(f);
850     }
851
852     return ap_pass_brigade(f->next, in);
853 }
854
855 /*
856  * CACHE_REMOVE_URL filter
857  * ---------------
858  *
859  * This filter gets added in the quick handler every time the CACHE_SAVE filter
860  * gets inserted. Its purpose is to remove a confirmed stale cache entry from
861  * the cache.
862  *
863  * CACHE_REMOVE_URL has to be a protocol filter to ensure that is run even if
864  * the response is a canned error message, which removes the content filters
865  * and thus the CACHE_SAVE filter from the chain.
866  *
867  * CACHE_REMOVE_URL expects cache request rec within its context because the
868  * request this filter runs on can be different from the one whose cache entry
869  * should be removed, due to internal redirects.
870  *
871  * Note that CACHE_SAVE_URL (as a content-set filter, hence run before the
872  * protocol filters) will remove this filter if it decides to cache the file.
873  * Therefore, if this filter is left in, it must mean we need to toss any
874  * existing files.
875  */
876 static int cache_remove_url_filter(ap_filter_t *f, apr_bucket_brigade *in)
877 {
878     request_rec *r = f->r;
879     cache_request_rec *cache;
880
881     /* Setup cache_request_rec */
882     cache = (cache_request_rec *) f->ctx;
883
884     if (!cache) {
885         /* user likely configured CACHE_REMOVE_URL manually; they should really
886          * use mod_cache configuration to do that. So:
887          * 1. Remove ourselves
888          * 2. Do nothing and bail out
889          */
890         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
891                      "cache: CACHE_REMOVE_URL enabled unexpectedly");
892         ap_remove_output_filter(f);
893         return ap_pass_brigade(f->next, in);
894     }
895     /* Now remove this cache entry from the cache */
896     cache_remove_url(cache, r->pool);
897
898     /* remove ourselves */
899     ap_remove_output_filter(f);
900     return ap_pass_brigade(f->next, in);
901 }
902
903 /* -------------------------------------------------------------- */
904 /* Setup configurable data */
905
906 static void * create_cache_config(apr_pool_t *p, server_rec *s)
907 {
908     cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
909
910     /* array of URL prefixes for which caching is enabled */
911     ps->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
912     /* array of URL prefixes for which caching is disabled */
913     ps->cachedisable = apr_array_make(p, 10, sizeof(struct cache_disable));
914     /* maximum time to cache a document */
915     ps->maxex = DEFAULT_CACHE_MAXEXPIRE;
916     ps->maxex_set = 0;
917     ps->minex = DEFAULT_CACHE_MINEXPIRE;
918     ps->minex_set = 0;
919     /* default time to cache a document */
920     ps->defex = DEFAULT_CACHE_EXPIRE;
921     ps->defex_set = 0;
922     /* factor used to estimate Expires date from LastModified date */
923     ps->factor = DEFAULT_CACHE_LMFACTOR;
924     ps->factor_set = 0;
925     ps->no_last_mod_ignore_set = 0;
926     ps->no_last_mod_ignore = 0;
927     ps->ignorecachecontrol = 0;
928     ps->ignorecachecontrol_set = 0;
929     ps->store_private = 0;
930     ps->store_private_set = 0;
931     ps->store_nostore = 0;
932     ps->store_nostore_set = 0;
933     /* array of headers that should not be stored in cache */
934     ps->ignore_headers = apr_array_make(p, 10, sizeof(char *));
935     ps->ignore_headers_set = CACHE_IGNORE_HEADERS_UNSET;
936     /* flag indicating that query-string should be ignored when caching */
937     ps->ignorequerystring = 0;
938     ps->ignorequerystring_set = 0;
939     return ps;
940 }
941
942 static void * merge_cache_config(apr_pool_t *p, void *basev, void *overridesv)
943 {
944     cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
945     cache_server_conf *base = (cache_server_conf *) basev;
946     cache_server_conf *overrides = (cache_server_conf *) overridesv;
947
948     /* array of URL prefixes for which caching is disabled */
949     ps->cachedisable = apr_array_append(p,
950                                         base->cachedisable,
951                                         overrides->cachedisable);
952     /* array of URL prefixes for which caching is enabled */
953     ps->cacheenable = apr_array_append(p,
954                                        base->cacheenable,
955                                        overrides->cacheenable);
956     /* maximum time to cache a document */
957     ps->maxex = (overrides->maxex_set == 0) ? base->maxex : overrides->maxex;
958     ps->minex = (overrides->minex_set == 0) ? base->minex : overrides->minex;
959     /* default time to cache a document */
960     ps->defex = (overrides->defex_set == 0) ? base->defex : overrides->defex;
961     /* factor used to estimate Expires date from LastModified date */
962     ps->factor =
963         (overrides->factor_set == 0) ? base->factor : overrides->factor;
964
965     ps->no_last_mod_ignore =
966         (overrides->no_last_mod_ignore_set == 0)
967         ? base->no_last_mod_ignore
968         : overrides->no_last_mod_ignore;
969     ps->ignorecachecontrol  =
970         (overrides->ignorecachecontrol_set == 0)
971         ? base->ignorecachecontrol
972         : overrides->ignorecachecontrol;
973     ps->store_private  =
974         (overrides->store_private_set == 0)
975         ? base->store_private
976         : overrides->store_private;
977     ps->store_nostore  =
978         (overrides->store_nostore_set == 0)
979         ? base->store_nostore
980         : overrides->store_nostore;
981     ps->ignore_headers =
982         (overrides->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET)
983         ? base->ignore_headers
984         : overrides->ignore_headers;
985     ps->ignorequerystring =
986         (overrides->ignorequerystring_set == 0)
987         ? base->ignorequerystring
988         : overrides->ignorequerystring;
989     return ps;
990 }
991 static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
992                                                 int flag)
993 {
994     cache_server_conf *conf;
995
996     conf =
997         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
998                                                   &cache_module);
999     conf->no_last_mod_ignore = flag;
1000     conf->no_last_mod_ignore_set = 1;
1001     return NULL;
1002
1003 }
1004
1005 static const char *set_cache_ignore_cachecontrol(cmd_parms *parms,
1006                                                  void *dummy, int flag)
1007 {
1008     cache_server_conf *conf;
1009
1010     conf =
1011         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1012                                                   &cache_module);
1013     conf->ignorecachecontrol = flag;
1014     conf->ignorecachecontrol_set = 1;
1015     return NULL;
1016 }
1017
1018 static const char *set_cache_store_private(cmd_parms *parms, void *dummy,
1019                                            int flag)
1020 {
1021     cache_server_conf *conf;
1022
1023     conf =
1024         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1025                                                   &cache_module);
1026     conf->store_private = flag;
1027     conf->store_private_set = 1;
1028     return NULL;
1029 }
1030
1031 static const char *set_cache_store_nostore(cmd_parms *parms, void *dummy,
1032                                            int flag)
1033 {
1034     cache_server_conf *conf;
1035
1036     conf =
1037         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1038                                                   &cache_module);
1039     conf->store_nostore = flag;
1040     conf->store_nostore_set = 1;
1041     return NULL;
1042 }
1043
1044 static const char *add_ignore_header(cmd_parms *parms, void *dummy,
1045                                      const char *header)
1046 {
1047     cache_server_conf *conf;
1048     char **new;
1049
1050     conf =
1051         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1052                                                   &cache_module);
1053     if (!strncasecmp(header, "None", 4)) {
1054         /* if header None is listed clear array */
1055         conf->ignore_headers->nelts = 0;
1056     }
1057     else {
1058         if ((conf->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET) ||
1059             (conf->ignore_headers->nelts)) {
1060             /* Only add header if no "None" has been found in header list
1061              * so far.
1062              * (When 'None' is passed, IGNORE_HEADERS_SET && nelts == 0.)
1063              */
1064             new = (char **)apr_array_push(conf->ignore_headers);
1065             (*new) = (char *)header;
1066         }
1067     }
1068     conf->ignore_headers_set = CACHE_IGNORE_HEADERS_SET;
1069     return NULL;
1070 }
1071
1072 static const char *add_cache_enable(cmd_parms *parms, void *dummy,
1073                                     const char *type,
1074                                     const char *url)
1075 {
1076     cache_server_conf *conf;
1077     struct cache_enable *new;
1078
1079     if (*type == '/') {
1080         return apr_psprintf(parms->pool,
1081           "provider (%s) starts with a '/'.  Are url and provider switched?",
1082           type);
1083     }
1084
1085     conf =
1086         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1087                                                   &cache_module);
1088     new = apr_array_push(conf->cacheenable);
1089     new->type = type;
1090     if (apr_uri_parse(parms->pool, url, &(new->url))) {
1091         return NULL;
1092     }
1093     if (new->url.path) {
1094         new->pathlen = strlen(new->url.path);
1095     } else {
1096         new->pathlen = 1;
1097         new->url.path = "/";
1098     }
1099     return NULL;
1100 }
1101
1102 static const char *add_cache_disable(cmd_parms *parms, void *dummy,
1103                                      const char *url)
1104 {
1105     cache_server_conf *conf;
1106     struct cache_disable *new;
1107
1108     conf =
1109         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1110                                                   &cache_module);
1111     new = apr_array_push(conf->cachedisable);
1112     if (apr_uri_parse(parms->pool, url, &(new->url))) {
1113         return NULL;
1114     }
1115     if (new->url.path) {
1116         new->pathlen = strlen(new->url.path);
1117     } else {
1118         new->pathlen = 1;
1119         new->url.path = "/";
1120     }
1121     return NULL;
1122 }
1123
1124 static const char *set_cache_maxex(cmd_parms *parms, void *dummy,
1125                                    const char *arg)
1126 {
1127     cache_server_conf *conf;
1128
1129     conf =
1130         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1131                                                   &cache_module);
1132     conf->maxex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
1133     conf->maxex_set = 1;
1134     return NULL;
1135 }
1136
1137 static const char *set_cache_minex(cmd_parms *parms, void *dummy,
1138                                    const char *arg)
1139 {
1140     cache_server_conf *conf;
1141
1142     conf =
1143         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1144                                                   &cache_module);
1145     conf->minex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
1146     conf->minex_set = 1;
1147     return NULL;
1148 }
1149
1150 static const char *set_cache_defex(cmd_parms *parms, void *dummy,
1151                                    const char *arg)
1152 {
1153     cache_server_conf *conf;
1154
1155     conf =
1156         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1157                                                   &cache_module);
1158     conf->defex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
1159     conf->defex_set = 1;
1160     return NULL;
1161 }
1162
1163 static const char *set_cache_factor(cmd_parms *parms, void *dummy,
1164                                     const char *arg)
1165 {
1166     cache_server_conf *conf;
1167     double val;
1168
1169     conf =
1170         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1171                                                   &cache_module);
1172     if (sscanf(arg, "%lg", &val) != 1) {
1173         return "CacheLastModifiedFactor value must be a float";
1174     }
1175     conf->factor = val;
1176     conf->factor_set = 1;
1177     return NULL;
1178 }
1179
1180 static const char *set_cache_ignore_querystring(cmd_parms *parms, void *dummy,
1181                                                 int flag)
1182 {
1183     cache_server_conf *conf;
1184
1185     conf =
1186         (cache_server_conf *)ap_get_module_config(parms->server->module_config,
1187                                                   &cache_module);
1188     conf->ignorequerystring = flag;
1189     conf->ignorequerystring_set = 1;
1190     return NULL;
1191 }
1192
1193 static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
1194                              apr_pool_t *ptemp, server_rec *s)
1195 {
1196     /* This is the means by which unusual (non-unix) os's may find alternate
1197      * means to run a given command (e.g. shebang/registry parsing on Win32)
1198      */
1199     cache_generate_key = APR_RETRIEVE_OPTIONAL_FN(ap_cache_generate_key);
1200     if (!cache_generate_key) {
1201         cache_generate_key = cache_generate_key_default;
1202     }
1203     return OK;
1204 }
1205
1206
1207 static const command_rec cache_cmds[] =
1208 {
1209     /* XXX
1210      * Consider a new config directive that enables loading specific cache
1211      * implememtations (like mod_cache_mem, mod_cache_file, etc.).
1212      * Rather than using a LoadModule directive, admin would use something
1213      * like CacheModule  mem_cache_module | file_cache_module, etc,
1214      * which would cause the approprpriate cache module to be loaded.
1215      * This is more intuitive that requiring a LoadModule directive.
1216      */
1217
1218     AP_INIT_TAKE2("CacheEnable", add_cache_enable, NULL, RSRC_CONF,
1219                   "A cache type and partial URL prefix below which "
1220                   "caching is enabled"),
1221     AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF,
1222                   "A partial URL prefix below which caching is disabled"),
1223     AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF,
1224                   "The maximum time in seconds to cache a document"),
1225     AP_INIT_TAKE1("CacheMinExpire", set_cache_minex, NULL, RSRC_CONF,
1226                   "The minimum time in seconds to cache a document"),
1227     AP_INIT_TAKE1("CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF,
1228                   "The default time in seconds to cache a document"),
1229     AP_INIT_FLAG("CacheIgnoreNoLastMod", set_cache_ignore_no_last_mod, NULL,
1230                  RSRC_CONF,
1231                  "Ignore Responses where there is no Last Modified Header"),
1232     AP_INIT_FLAG("CacheIgnoreCacheControl", set_cache_ignore_cachecontrol,
1233                  NULL, RSRC_CONF,
1234                  "Ignore requests from the client for uncached content"),
1235     AP_INIT_FLAG("CacheStorePrivate", set_cache_store_private,
1236                  NULL, RSRC_CONF,
1237                  "Ignore 'Cache-Control: private' and store private content"),
1238     AP_INIT_FLAG("CacheStoreNoStore", set_cache_store_nostore,
1239                  NULL, RSRC_CONF,
1240                  "Ignore 'Cache-Control: no-store' and store sensitive content"),
1241     AP_INIT_ITERATE("CacheIgnoreHeaders", add_ignore_header, NULL, RSRC_CONF,
1242                     "A space separated list of headers that should not be "
1243                     "stored by the cache"),
1244     AP_INIT_FLAG("CacheIgnoreQueryString", set_cache_ignore_querystring,
1245                  NULL, RSRC_CONF,
1246                  "Ignore query-string when caching"),
1247     AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF,
1248                   "The factor used to estimate Expires date from "
1249                   "LastModified date"),
1250     {NULL}
1251 };
1252
1253 static void register_hooks(apr_pool_t *p)
1254 {
1255     /* cache initializer */
1256     /* cache handler */
1257     ap_hook_quick_handler(cache_url_handler, NULL, NULL, APR_HOOK_FIRST);
1258     /* cache filters
1259      * XXX The cache filters need to run right after the handlers and before
1260      * any other filters. Consider creating AP_FTYPE_CACHE for this purpose.
1261      *
1262      * Depending on the type of request (subrequest / main request) they
1263      * need to be run before AP_FTYPE_CONTENT_SET / after AP_FTYPE_CONTENT_SET
1264      * filters. Thus create two filter handles for each type:
1265      * cache_save_filter_handle / cache_out_filter_handle to be used by
1266      * main requests and
1267      * cache_save_subreq_filter_handle / cache_out_subreq_filter_handle
1268      * to be run by subrequest
1269      */
1270     /*
1271      * CACHE_SAVE must go into the filter chain after a possible DEFLATE
1272      * filter to ensure that the compressed content is stored.
1273      * Incrementing filter type by 1 ensures his happens.
1274      */
1275     cache_save_filter_handle =
1276         ap_register_output_filter("CACHE_SAVE",
1277                                   cache_save_filter,
1278                                   NULL,
1279                                   AP_FTYPE_CONTENT_SET+1);
1280     /*
1281      * CACHE_SAVE_SUBREQ must go into the filter chain before SUBREQ_CORE to
1282      * handle subrequsts. Decrementing filter type by 1 ensures this
1283      * happens.
1284      */
1285     cache_save_subreq_filter_handle =
1286         ap_register_output_filter("CACHE_SAVE_SUBREQ",
1287                                   cache_save_filter,
1288                                   NULL,
1289                                   AP_FTYPE_CONTENT_SET-1);
1290     /*
1291      * CACHE_OUT must go into the filter chain after a possible DEFLATE
1292      * filter to ensure that already compressed cache objects do not
1293      * get compressed again. Incrementing filter type by 1 ensures
1294      * his happens.
1295      */
1296     cache_out_filter_handle =
1297         ap_register_output_filter("CACHE_OUT",
1298                                   cache_out_filter,
1299                                   NULL,
1300                                   AP_FTYPE_CONTENT_SET+1);
1301     /*
1302      * CACHE_OUT_SUBREQ must go into the filter chain before SUBREQ_CORE to
1303      * handle subrequsts. Decrementing filter type by 1 ensures this
1304      * happens.
1305      */
1306     cache_out_subreq_filter_handle =
1307         ap_register_output_filter("CACHE_OUT_SUBREQ",
1308                                   cache_out_filter,
1309                                   NULL,
1310                                   AP_FTYPE_CONTENT_SET-1);
1311     /* CACHE_REMOVE_URL has to be a protocol filter to ensure that is
1312      * run even if the response is a canned error message, which
1313      * removes the content filters.
1314      */
1315     cache_remove_url_filter_handle =
1316         ap_register_output_filter("CACHE_REMOVE_URL",
1317                                   cache_remove_url_filter,
1318                                   NULL,
1319                                   AP_FTYPE_PROTOCOL);
1320     ap_hook_post_config(cache_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
1321 }
1322
1323 module AP_MODULE_DECLARE_DATA cache_module =
1324 {
1325     STANDARD20_MODULE_STUFF,
1326     NULL,                   /* create per-directory config structure */
1327     NULL,                   /* merge per-directory config structures */
1328     create_cache_config,    /* create per-server config structure */
1329     merge_cache_config,     /* merge per-server config structures */
1330     cache_cmds,             /* command apr_table_t */
1331     register_hooks
1332 };