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#include "cache_storage.h"
20#include "cache_util.h"
21
22module AP_MODULE_DECLARE_DATA cache_module;
23APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
24
25/* -------------------------------------------------------------- */
26
27
28/* Handles for cache filters, resolved at startup to eliminate
29 * a name-to-function mapping on each request
30 */
31static ap_filter_rec_t *cache_filter_handle;
32static ap_filter_rec_t *cache_save_filter_handle;
33static ap_filter_rec_t *cache_save_subreq_filter_handle;
34static ap_filter_rec_t *cache_out_filter_handle;
35static ap_filter_rec_t *cache_out_subreq_filter_handle;
36static ap_filter_rec_t *cache_remove_url_filter_handle;
37static ap_filter_rec_t *cache_invalidate_filter_handle;
38
39/*
40 * CACHE handler
41 * -------------
42 *
43 * Can we deliver this request from the cache?
44 * If yes:
45 *   deliver the content by installing the CACHE_OUT filter.
46 * If no:
47 *   check whether we're allowed to try cache it
48 *   If yes:
49 *     add CACHE_SAVE filter
50 *   If No:
51 *     oh well.
52 *
53 * By default, the cache handler runs in the quick handler, bypassing
54 * virtually all server processing and offering the cache its optimal
55 * performance. In this mode, the cache bolts onto the front of the
56 * server, and behaves as a discrete RFC2616 caching proxy
57 * implementation.
58 *
59 * Under certain circumstances, an admin might want to run the cache as
60 * a normal handler instead of a quick handler, allowing the cache to
61 * run after the authorisation hooks, or by allowing fine control over
62 * the placement of the cache in the filter chain. This option comes at
63 * a performance penalty, and should only be used to achieve specific
64 * caching goals where the admin understands what they are doing.
65 */
66
67static int cache_quick_handler(request_rec *r, int lookup)
68{
69    apr_status_t rv;
70    const char *auth;
71    cache_provider_list *providers;
72    cache_request_rec *cache;
73    apr_bucket_brigade *out;
74    apr_bucket *e;
75    ap_filter_t *next;
76    ap_filter_rec_t *cache_out_handle;
77    cache_server_conf *conf;
78
79    conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
80                                                      &cache_module);
81
82    /* only run if the quick handler is enabled */
83    if (!conf->quick) {
84        return DECLINED;
85    }
86
87    /*
88     * Which cache module (if any) should handle this request?
89     */
90    if (!(providers = cache_get_providers(r, conf, r->parsed_uri))) {
91        return DECLINED;
92    }
93
94    /* make space for the per request config */
95    cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
96    cache->size = -1;
97    cache->out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
98
99    /* save away the possible providers */
100    cache->providers = providers;
101
102    /*
103     * Are we allowed to serve cached info at all?
104     */
105    if (!ap_cache_check_no_store(cache, r)) {
106        return DECLINED;
107    }
108
109    /* find certain cache controlling headers */
110    auth = apr_table_get(r->headers_in, "Authorization");
111
112    /* First things first - does the request allow us to return
113     * cached information at all? If not, just decline the request.
114     */
115    if (auth) {
116        return DECLINED;
117    }
118
119    /* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities.
120     */
121    switch (r->method_number) {
122    case M_PUT:
123    case M_POST:
124    case M_DELETE:
125    {
126
127        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02461)
128                "PUT/POST/DELETE: Adding CACHE_INVALIDATE filter for %s",
129                r->uri);
130
131        /* Add cache_invalidate filter to this request to force a
132         * cache entry to be invalidated if the response is
133         * ultimately successful (2xx).
134         */
135        ap_add_output_filter_handle(
136                cache_invalidate_filter_handle, cache, r,
137                r->connection);
138
139        return DECLINED;
140    }
141    case M_GET: {
142        break;
143    }
144    default : {
145
146        ap_log_rerror(
147                APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02462) "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri);
148
149        return DECLINED;
150    }
151    }
152
153    /*
154     * Try to serve this request from the cache.
155     *
156     * If no existing cache file (DECLINED)
157     *   add cache_save filter
158     * If cached file (OK)
159     *   clear filter stack
160     *   add cache_out filter
161     *   return OK
162     */
163    rv = cache_select(cache, r);
164    if (rv != OK) {
165        if (rv == DECLINED) {
166            if (!lookup) {
167
168                /* try to obtain a cache lock at this point. if we succeed,
169                 * we are the first to try and cache this url. if we fail,
170                 * it means someone else is already trying to cache this
171                 * url, and we should just let the request through to the
172                 * backend without any attempt to cache. this stops
173                 * duplicated simultaneous attempts to cache an entity.
174                 */
175                rv = cache_try_lock(conf, cache, r);
176                if (APR_SUCCESS == rv) {
177
178                    /*
179                     * Add cache_save filter to cache this request. Choose
180                     * the correct filter by checking if we are a subrequest
181                     * or not.
182                     */
183                    if (r->main) {
184                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
185                                r, APLOGNO(00749) "Adding CACHE_SAVE_SUBREQ filter for %s",
186                                r->uri);
187                        cache->save_filter = ap_add_output_filter_handle(
188                                cache_save_subreq_filter_handle, cache, r,
189                                r->connection);
190                    }
191                    else {
192                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
193                                r, APLOGNO(00750) "Adding CACHE_SAVE filter for %s",
194                                r->uri);
195                        cache->save_filter = ap_add_output_filter_handle(
196                                cache_save_filter_handle, cache, r,
197                                r->connection);
198                    }
199
200                    apr_pool_userdata_setn(cache, CACHE_CTX_KEY, NULL, r->pool);
201
202                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00751)
203                            "Adding CACHE_REMOVE_URL filter for %s",
204                            r->uri);
205
206                    /* Add cache_remove_url filter to this request to remove a
207                     * stale cache entry if needed. Also put the current cache
208                     * request rec in the filter context, as the request that
209                     * is available later during running the filter may be
210                     * different due to an internal redirect.
211                     */
212                    cache->remove_url_filter = ap_add_output_filter_handle(
213                            cache_remove_url_filter_handle, cache, r,
214                            r->connection);
215
216                }
217                else {
218                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv,
219                            r, APLOGNO(00752) "Cache locked for url, not caching "
220                            "response: %s", r->uri);
221                }
222            }
223            else {
224                if (cache->stale_headers) {
225                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
226                            r, APLOGNO(00753) "Restoring request headers for %s",
227                            r->uri);
228
229                    r->headers_in = cache->stale_headers;
230                }
231            }
232        }
233        else {
234            /* error */
235            return rv;
236        }
237        return DECLINED;
238    }
239
240    /* we've got a cache hit! tell everyone who cares */
241    cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT,
242            "cache hit");
243
244    /* if we are a lookup, we are exiting soon one way or another; Restore
245     * the headers. */
246    if (lookup) {
247        if (cache->stale_headers) {
248            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00754)
249                    "Restoring request headers.");
250            r->headers_in = cache->stale_headers;
251        }
252    }
253
254    rv = ap_meets_conditions(r);
255    if (rv != OK) {
256        /* If we are a lookup, we have to return DECLINED as we have no
257         * way of knowing if we will be able to serve the content.
258         */
259        if (lookup) {
260            return DECLINED;
261        }
262
263        /* Return cached status. */
264        return rv;
265    }
266
267    /* If we're a lookup, we can exit now instead of serving the content. */
268    if (lookup) {
269        return OK;
270    }
271
272    /* Serve up the content */
273
274    /* We are in the quick handler hook, which means that no output
275     * filters have been set. So lets run the insert_filter hook.
276     */
277    ap_run_insert_filter(r);
278
279    /*
280     * Add cache_out filter to serve this request. Choose
281     * the correct filter by checking if we are a subrequest
282     * or not.
283     */
284    if (r->main) {
285        cache_out_handle = cache_out_subreq_filter_handle;
286    }
287    else {
288        cache_out_handle = cache_out_filter_handle;
289    }
290    ap_add_output_filter_handle(cache_out_handle, cache, r, r->connection);
291
292    /*
293     * Remove all filters that are before the cache_out filter. This ensures
294     * that we kick off the filter stack with our cache_out filter being the
295     * first in the chain. This make sense because we want to restore things
296     * in the same manner as we saved them.
297     * There may be filters before our cache_out filter, because
298     *
299     * 1. We call ap_set_content_type during cache_select. This causes
300     *    Content-Type specific filters to be added.
301     * 2. We call the insert_filter hook. This causes filters e.g. like
302     *    the ones set with SetOutputFilter to be added.
303     */
304    next = r->output_filters;
305    while (next && (next->frec != cache_out_handle)) {
306        ap_remove_output_filter(next);
307        next = next->next;
308    }
309
310    /* kick off the filter stack */
311    out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
312    e = apr_bucket_eos_create(out->bucket_alloc);
313    APR_BRIGADE_INSERT_TAIL(out, e);
314
315    return ap_pass_brigade_fchk(r, out,
316                                "cache_quick_handler(%s): ap_pass_brigade returned",
317                                cache->provider_name);
318}
319
320/**
321 * If the two filter handles are present within the filter chain, replace
322 * the last instance of the first filter with the last instance of the
323 * second filter, and return true. If the second filter is not present at
324 * all, the first filter is removed, and false is returned. If neither
325 * filter is present, false is returned and this function does nothing.
326 * If a stop filter is specified, processing will stop once this filter is
327 * reached.
328 */
329static int cache_replace_filter(ap_filter_t *next, ap_filter_rec_t *from,
330        ap_filter_rec_t *to, ap_filter_rec_t *stop) {
331    ap_filter_t *ffrom = NULL, *fto = NULL;
332    while (next && next->frec != stop) {
333        if (next->frec == from) {
334            ffrom = next;
335        }
336        if (next->frec == to) {
337            fto = next;
338        }
339        next = next->next;
340    }
341    if (ffrom && fto) {
342        ffrom->frec = fto->frec;
343        ffrom->ctx = fto->ctx;
344        ap_remove_output_filter(fto);
345        return 1;
346    }
347    if (ffrom) {
348        ap_remove_output_filter(ffrom);
349    }
350    return 0;
351}
352
353/**
354 * Find the given filter, and return it if found, or NULL otherwise.
355 */
356static ap_filter_t *cache_get_filter(ap_filter_t *next, ap_filter_rec_t *rec) {
357    while (next) {
358        if (next->frec == rec && next->ctx) {
359            break;
360        }
361        next = next->next;
362    }
363    return next;
364}
365
366/**
367 * The cache handler is functionally similar to the cache_quick_hander,
368 * however a number of steps that are required by the quick handler are
369 * not required here, as the normal httpd processing has already handled
370 * these steps.
371 */
372static int cache_handler(request_rec *r)
373{
374    apr_status_t rv;
375    cache_provider_list *providers;
376    cache_request_rec *cache;
377    apr_bucket_brigade *out;
378    apr_bucket *e;
379    ap_filter_t *next;
380    ap_filter_rec_t *cache_out_handle;
381    ap_filter_rec_t *cache_save_handle;
382    cache_server_conf *conf;
383
384    conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
385                                                      &cache_module);
386
387    /* only run if the quick handler is disabled */
388    if (conf->quick) {
389        return DECLINED;
390    }
391
392    /*
393     * Which cache module (if any) should handle this request?
394     */
395    if (!(providers = cache_get_providers(r, conf, r->parsed_uri))) {
396        return DECLINED;
397    }
398
399    /* make space for the per request config */
400    cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
401    cache->size = -1;
402    cache->out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
403
404    /* save away the possible providers */
405    cache->providers = providers;
406
407    /*
408     * Are we allowed to serve cached info at all?
409     */
410    if (!ap_cache_check_no_store(cache, r)) {
411        return DECLINED;
412    }
413
414    /* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities.
415     */
416    switch (r->method_number) {
417    case M_PUT:
418    case M_POST:
419    case M_DELETE:
420    {
421
422        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02463)
423                "PUT/POST/DELETE: Adding CACHE_INVALIDATE filter for %s",
424                r->uri);
425
426        /* Add cache_invalidate filter to this request to force a
427         * cache entry to be invalidated if the response is
428         * ultimately successful (2xx).
429         */
430        ap_add_output_filter_handle(
431                cache_invalidate_filter_handle, cache, r,
432                r->connection);
433
434        return DECLINED;
435    }
436    case M_GET: {
437        break;
438    }
439    default : {
440
441        ap_log_rerror(
442                APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02464) "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri);
443
444        return DECLINED;
445    }
446    }
447
448    /*
449     * Try to serve this request from the cache.
450     *
451     * If no existing cache file (DECLINED)
452     *   add cache_save filter
453     * If cached file (OK)
454     *   clear filter stack
455     *   add cache_out filter
456     *   return OK
457     */
458    rv = cache_select(cache, r);
459    if (rv != OK) {
460        if (rv == DECLINED) {
461
462            /* try to obtain a cache lock at this point. if we succeed,
463             * we are the first to try and cache this url. if we fail,
464             * it means someone else is already trying to cache this
465             * url, and we should just let the request through to the
466             * backend without any attempt to cache. this stops
467             * duplicated simultaneous attempts to cache an entity.
468             */
469            rv = cache_try_lock(conf, cache, r);
470            if (APR_SUCCESS == rv) {
471
472                /*
473                 * Add cache_save filter to cache this request. Choose
474                 * the correct filter by checking if we are a subrequest
475                 * or not.
476                 */
477                if (r->main) {
478                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
479                            r, APLOGNO(00756) "Adding CACHE_SAVE_SUBREQ filter for %s",
480                            r->uri);
481                    cache_save_handle = cache_save_subreq_filter_handle;
482                }
483                else {
484                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
485                            r, APLOGNO(00757) "Adding CACHE_SAVE filter for %s",
486                            r->uri);
487                    cache_save_handle = cache_save_filter_handle;
488                }
489                ap_add_output_filter_handle(cache_save_handle, cache, r,
490                        r->connection);
491
492                /*
493                 * Did the user indicate the precise location of the
494                 * CACHE_SAVE filter by inserting the CACHE filter as a
495                 * marker?
496                 *
497                 * If so, we get cunning and replace CACHE with the
498                 * CACHE_SAVE filter. This has the effect of inserting
499                 * the CACHE_SAVE filter at the precise location where
500                 * the admin wants to cache the content. All filters that
501                 * lie before and after the original location of the CACHE
502                 * filter will remain in place.
503                 */
504                if (cache_replace_filter(r->output_filters,
505                        cache_filter_handle, cache_save_handle,
506                        ap_get_input_filter_handle("SUBREQ_CORE"))) {
507                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
508                            r, APLOGNO(00758) "Replacing CACHE with CACHE_SAVE "
509                            "filter for %s", r->uri);
510                }
511
512                /* save away the save filter stack */
513                cache->save_filter = cache_get_filter(r->output_filters,
514                        cache_save_filter_handle);
515
516                apr_pool_userdata_setn(cache, CACHE_CTX_KEY, NULL, r->pool);
517
518                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00759)
519                        "Adding CACHE_REMOVE_URL filter for %s",
520                        r->uri);
521
522                /* Add cache_remove_url filter to this request to remove a
523                 * stale cache entry if needed. Also put the current cache
524                 * request rec in the filter context, as the request that
525                 * is available later during running the filter may be
526                 * different due to an internal redirect.
527                 */
528                cache->remove_url_filter
529                        = ap_add_output_filter_handle(
530                                cache_remove_url_filter_handle, cache, r,
531                                r->connection);
532
533            }
534            else {
535                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv,
536                        r, APLOGNO(00760) "Cache locked for url, not caching "
537                        "response: %s", r->uri);
538            }
539        }
540        else {
541            /* error */
542            return rv;
543        }
544        return DECLINED;
545    }
546
547    /* we've got a cache hit! tell everyone who cares */
548    cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT,
549            "cache hit");
550
551    rv = ap_meets_conditions(r);
552    if (rv != OK) {
553        return rv;
554    }
555
556    /* Serve up the content */
557
558    /*
559     * Add cache_out filter to serve this request. Choose
560     * the correct filter by checking if we are a subrequest
561     * or not.
562     */
563    if (r->main) {
564        cache_out_handle = cache_out_subreq_filter_handle;
565    }
566    else {
567        cache_out_handle = cache_out_filter_handle;
568    }
569    ap_add_output_filter_handle(cache_out_handle, cache, r, r->connection);
570
571    /*
572     * Did the user indicate the precise location of the CACHE_OUT filter by
573     * inserting the CACHE filter as a marker?
574     *
575     * If so, we get cunning and replace CACHE with the CACHE_OUT filters.
576     * This has the effect of inserting the CACHE_OUT filter at the precise
577     * location where the admin wants to cache the content. All filters that
578     * lie *after* the original location of the CACHE filter will remain in
579     * place.
580     */
581    if (cache_replace_filter(r->output_filters, cache_filter_handle,
582            cache_out_handle, ap_get_input_filter_handle("SUBREQ_CORE"))) {
583        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
584                r, APLOGNO(00761) "Replacing CACHE with CACHE_OUT filter for %s",
585                r->uri);
586    }
587
588    /*
589     * Remove all filters that are before the cache_out filter. This ensures
590     * that we kick off the filter stack with our cache_out filter being the
591     * first in the chain. This make sense because we want to restore things
592     * in the same manner as we saved them.
593     * There may be filters before our cache_out filter, because
594     *
595     * 1. We call ap_set_content_type during cache_select. This causes
596     *    Content-Type specific filters to be added.
597     * 2. We call the insert_filter hook. This causes filters e.g. like
598     *    the ones set with SetOutputFilter to be added.
599     */
600    next = r->output_filters;
601    while (next && (next->frec != cache_out_handle)) {
602        ap_remove_output_filter(next);
603        next = next->next;
604    }
605
606    /* kick off the filter stack */
607    out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
608    e = apr_bucket_eos_create(out->bucket_alloc);
609    APR_BRIGADE_INSERT_TAIL(out, e);
610    return ap_pass_brigade_fchk(r, out, "cache(%s): ap_pass_brigade returned",
611                                cache->provider_name);
612}
613
614/*
615 * CACHE_OUT filter
616 * ----------------
617 *
618 * Deliver cached content (headers and body) up the stack.
619 */
620static apr_status_t cache_out_filter(ap_filter_t *f, apr_bucket_brigade *in)
621{
622    request_rec *r = f->r;
623    apr_bucket *e;
624    cache_request_rec *cache = (cache_request_rec *)f->ctx;
625
626    if (!cache) {
627        /* user likely configured CACHE_OUT manually; they should use mod_cache
628         * configuration to do that */
629        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00762)
630                "CACHE/CACHE_OUT filter enabled while caching is disabled, ignoring");
631        ap_remove_output_filter(f);
632        return ap_pass_brigade(f->next, in);
633    }
634
635    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00763)
636            "cache: running CACHE_OUT filter");
637
638    /* clean out any previous response up to EOS, if any */
639    for (e = APR_BRIGADE_FIRST(in);
640         e != APR_BRIGADE_SENTINEL(in);
641         e = APR_BUCKET_NEXT(e))
642    {
643        if (APR_BUCKET_IS_EOS(e)) {
644            apr_bucket_brigade *bb = apr_brigade_create(r->pool,
645                    r->connection->bucket_alloc);
646
647            /* restore content type of cached response if available */
648            /* Needed especially when stale content gets served. */
649            const char *ct = apr_table_get(cache->handle->resp_hdrs, "Content-Type");
650            if (ct) {
651                ap_set_content_type(r, ct);
652            }
653
654            /* restore status of cached response */
655            r->status = cache->handle->cache_obj->info.status;
656
657            /* recall_headers() was called in cache_select() */
658            cache->provider->recall_body(cache->handle, r->pool, bb);
659            APR_BRIGADE_PREPEND(in, bb);
660
661            /* This filter is done once it has served up its content */
662            ap_remove_output_filter(f);
663
664            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00764)
665                    "cache: serving %s", r->uri);
666            return ap_pass_brigade(f->next, in);
667
668        }
669        apr_bucket_delete(e);
670    }
671
672    return APR_SUCCESS;
673}
674
675/*
676 * Having jumped through all the hoops and decided to cache the
677 * response, call store_body() for each brigade, handling the
678 * case where the provider can't swallow the full brigade. In this
679 * case, we write the brigade we were passed out downstream, and
680 * loop around to try and cache some more until the in brigade is
681 * completely empty. As soon as the out brigade contains eos, call
682 * commit_entity() to finalise the cached element.
683 */
684static int cache_save_store(ap_filter_t *f, apr_bucket_brigade *in,
685        cache_server_conf *conf, cache_request_rec *cache)
686{
687    int rv = APR_SUCCESS;
688    apr_bucket *e;
689
690    /* pass the brigade in into the cache provider, which is then
691     * expected to move cached buckets to the out brigade, for us
692     * to pass up the filter stack. repeat until in is empty, or
693     * we fail.
694     */
695    while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
696
697        rv = cache->provider->store_body(cache->handle, f->r, in, cache->out);
698        if (rv != APR_SUCCESS) {
699            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, f->r, APLOGNO(00765)
700                    "cache: Cache provider's store_body failed!");
701            ap_remove_output_filter(f);
702
703            /* give someone else the chance to cache the file */
704            cache_remove_lock(conf, cache, f->r, NULL);
705
706            /* give up trying to cache, just step out the way */
707            APR_BRIGADE_PREPEND(in, cache->out);
708            return ap_pass_brigade(f->next, in);
709
710        }
711
712        /* does the out brigade contain eos? if so, we're done, commit! */
713        for (e = APR_BRIGADE_FIRST(cache->out);
714             e != APR_BRIGADE_SENTINEL(cache->out);
715             e = APR_BUCKET_NEXT(e))
716        {
717            if (APR_BUCKET_IS_EOS(e)) {
718                rv = cache->provider->commit_entity(cache->handle, f->r);
719                break;
720            }
721        }
722
723        /* conditionally remove the lock as soon as we see the eos bucket */
724        cache_remove_lock(conf, cache, f->r, cache->out);
725
726        if (APR_BRIGADE_EMPTY(cache->out)) {
727            if (APR_BRIGADE_EMPTY(in)) {
728                /* cache provider wants more data before passing the brigade
729                 * upstream, oblige the provider by leaving to fetch more.
730                 */
731                break;
732            }
733            else {
734                /* oops, no data out, but not all data read in either, be
735                 * safe and stand down to prevent a spin.
736                 */
737                ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, f->r, APLOGNO(00766)
738                        "cache: Cache provider's store_body returned an "
739                        "empty brigade, but didn't consume all of the "
740                        "input brigade, standing down to prevent a spin");
741                ap_remove_output_filter(f);
742
743                /* give someone else the chance to cache the file */
744                cache_remove_lock(conf, cache, f->r, NULL);
745
746                return ap_pass_brigade(f->next, in);
747            }
748        }
749
750        rv = ap_pass_brigade(f->next, cache->out);
751    }
752
753    return rv;
754}
755
756/**
757 * Sanity check for 304 Not Modified responses, as per RFC2616 Section 10.3.5.
758 */
759static const char *cache_header_cmp(apr_pool_t *pool, apr_table_t *left,
760        apr_table_t *right, const char *key)
761{
762    const char *h1, *h2;
763
764    if ((h1 = cache_table_getm(pool, left, key))
765            && (h2 = cache_table_getm(pool, right, key)) && (strcmp(h1, h2))) {
766        return apr_pstrcat(pool, "contradiction: 304 Not Modified, but ", key,
767                " modified", NULL);
768    }
769    return NULL;
770}
771
772/*
773 * CACHE_SAVE filter
774 * ---------------
775 *
776 * Decide whether or not this content should be cached.
777 * If we decide no it should not:
778 *   remove the filter from the chain
779 * If we decide yes it should:
780 *   Have we already started saving the response?
781 *      If we have started, pass the data to the storage manager via store_body
782 *      Otherwise:
783 *        Check to see if we *can* save this particular response.
784 *        If we can, call cache_create_entity() and save the headers and body
785 *   Finally, pass the data to the next filter (the network or whatever)
786 *
787 * After the various failure cases, the cache lock is proactively removed, so
788 * that another request is given the opportunity to attempt to cache without
789 * waiting for a potentially slow client to acknowledge the failure.
790 */
791
792static apr_status_t cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
793{
794    int rv = !OK;
795    request_rec *r = f->r;
796    cache_request_rec *cache = (cache_request_rec *)f->ctx;
797    cache_server_conf *conf;
798    cache_dir_conf *dconf;
799    cache_control_t control;
800    const char *cc_out, *cl, *pragma;
801    const char *exps, *lastmods, *dates, *etag;
802    apr_time_t exp, date, lastmod, now;
803    apr_off_t size = -1;
804    cache_info *info = NULL;
805    const char *reason;
806    apr_pool_t *p;
807    apr_bucket *e;
808    apr_table_t *headers;
809
810    conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
811                                                      &cache_module);
812
813    /* Setup cache_request_rec */
814    if (!cache) {
815        /* user likely configured CACHE_SAVE manually; they should really use
816         * mod_cache configuration to do that
817         */
818        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00767)
819                "CACHE/CACHE_SAVE filter enabled while caching is disabled, ignoring");
820        ap_remove_output_filter(f);
821        return ap_pass_brigade(f->next, in);
822    }
823
824    reason = NULL;
825    p = r->pool;
826    /*
827     * Pass Data to Cache
828     * ------------------
829     * This section passes the brigades into the cache modules, but only
830     * if the setup section (see below) is complete.
831     */
832    if (cache->block_response) {
833        /* We've already sent down the response and EOS.  So, ignore
834         * whatever comes now.
835         */
836        return APR_SUCCESS;
837    }
838
839    /* have we already run the cacheability check and set up the
840     * cached file handle?
841     */
842    if (cache->in_checked) {
843        return cache_save_store(f, in, conf, cache);
844    }
845
846    /*
847     * Setup Data in Cache
848     * -------------------
849     * This section opens the cache entity and sets various caching
850     * parameters, and decides whether this URL should be cached at
851     * all. This section is* run before the above section.
852     */
853
854    dconf = ap_get_module_config(r->per_dir_config, &cache_module);
855
856    /* RFC2616 13.8 Errors or Incomplete Response Cache Behavior:
857     * If a cache receives a 5xx response while attempting to revalidate an
858     * entry, it MAY either forward this response to the requesting client,
859     * or act as if the server failed to respond. In the latter case, it MAY
860     * return a previously received response unless the cached entry
861     * includes the "must-revalidate" cache-control directive (see section
862     * 14.9).
863     *
864     * This covers the case where an error was generated behind us, for example
865     * by a backend server via mod_proxy.
866     */
867    if (dconf->stale_on_error && r->status >= HTTP_INTERNAL_SERVER_ERROR) {
868
869        ap_remove_output_filter(cache->remove_url_filter);
870
871        if (cache->stale_handle
872                && !cache->stale_handle->cache_obj->info.control.must_revalidate
873                && !cache->stale_handle->cache_obj->info.control.proxy_revalidate) {
874            const char *warn_head;
875
876            /* morph the current save filter into the out filter, and serve from
877             * cache.
878             */
879            cache->handle = cache->stale_handle;
880            if (r->main) {
881                f->frec = cache_out_subreq_filter_handle;
882            }
883            else {
884                f->frec = cache_out_filter_handle;
885            }
886
887            r->headers_out = cache->stale_handle->resp_hdrs;
888
889            ap_set_content_type(r, apr_table_get(
890                    cache->stale_handle->resp_hdrs, "Content-Type"));
891
892            /* add a revalidation warning */
893            warn_head = apr_table_get(r->err_headers_out, "Warning");
894            if ((warn_head == NULL) || ((warn_head != NULL)
895                    && (ap_strstr_c(warn_head, "111") == NULL))) {
896                apr_table_mergen(r->err_headers_out, "Warning",
897                        "111 Revalidation failed");
898            }
899
900            cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT,
901                    apr_psprintf(r->pool,
902                            "cache hit: %d status; stale content returned",
903                            r->status));
904
905            /* give someone else the chance to cache the file */
906            cache_remove_lock(conf, cache, f->r, NULL);
907
908            /* pass brigade to our morphed out filter */
909            return ap_pass_brigade(f, in);
910        }
911    }
912
913    /* read expiry date; if a bad date, then leave it so the client can
914     * read it
915     */
916    exps = apr_table_get(r->err_headers_out, "Expires");
917    if (exps == NULL) {
918        exps = apr_table_get(r->headers_out, "Expires");
919    }
920    if (exps != NULL) {
921        exp = apr_date_parse_http(exps);
922    }
923    else {
924        exp = APR_DATE_BAD;
925    }
926
927    /* read the last-modified date; if the date is bad, then delete it */
928    lastmods = apr_table_get(r->err_headers_out, "Last-Modified");
929    if (lastmods == NULL) {
930        lastmods = apr_table_get(r->headers_out, "Last-Modified");
931    }
932    if (lastmods != NULL) {
933        lastmod = apr_date_parse_http(lastmods);
934        if (lastmod == APR_DATE_BAD) {
935            lastmods = NULL;
936        }
937    }
938    else {
939        lastmod = APR_DATE_BAD;
940    }
941
942    /* read the etag and cache-control from the entity */
943    etag = apr_table_get(r->err_headers_out, "Etag");
944    if (etag == NULL) {
945        etag = apr_table_get(r->headers_out, "Etag");
946    }
947    cc_out = cache_table_getm(r->pool, r->err_headers_out, "Cache-Control");
948    pragma = cache_table_getm(r->pool, r->err_headers_out, "Pragma");
949    headers = r->err_headers_out;
950    if (!cc_out && !pragma) {
951        cc_out = cache_table_getm(r->pool, r->headers_out, "Cache-Control");
952        pragma = cache_table_getm(r->pool, r->headers_out, "Pragma");
953        headers = r->headers_out;
954    }
955
956    /* Have we received a 304 response without any headers at all? Fall back to
957     * the original headers in the original cached request.
958     */
959    if (r->status == HTTP_NOT_MODIFIED && cache->stale_handle && !cc_out
960            && !pragma) {
961        cc_out = cache_table_getm(r->pool, cache->stale_handle->resp_hdrs,
962                "Cache-Control");
963        pragma = cache_table_getm(r->pool, cache->stale_handle->resp_hdrs,
964                "Pragma");
965    }
966
967    /* Parse the cache control header */
968    memset(&control, 0, sizeof(cache_control_t));
969    ap_cache_control(r, &control, cc_out, pragma, headers);
970
971    /*
972     * what responses should we not cache?
973     *
974     * At this point we decide based on the response headers whether it
975     * is appropriate _NOT_ to cache the data from the server. There are
976     * a whole lot of conditions that prevent us from caching this data.
977     * They are tested here one by one to be clear and unambiguous.
978     */
979    if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
980        && r->status != HTTP_PARTIAL_CONTENT
981        && r->status != HTTP_MULTIPLE_CHOICES
982        && r->status != HTTP_MOVED_PERMANENTLY
983        && r->status != HTTP_NOT_MODIFIED) {
984        /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
985         * We allow the caching of 206, but a cache implementation might choose
986         * to decline to cache a 206 if it doesn't know how to.
987         * We include 304 Not Modified here too as this is the origin server
988         * telling us to serve the cached copy.
989         */
990        if (exps != NULL || cc_out != NULL) {
991            /* We are also allowed to cache any response given that it has a
992             * valid Expires or Cache Control header. If we find a either of
993             * those here,  we pass request through the rest of the tests. From
994             * the RFC:
995             *
996             * A response received with any other status code (e.g. status
997             * codes 302 and 307) MUST NOT be returned in a reply to a
998             * subsequent request unless there are cache-control directives or
999             * another header(s) that explicitly allow it. For example, these
1000             * include the following: an Expires header (section 14.21); a
1001             * "max-age", "s-maxage",  "must-revalidate", "proxy-revalidate",
1002             * "public" or "private" cache-control directive (section 14.9).
1003             */
1004        }
1005        else {
1006            reason = apr_psprintf(p, "Response status %d", r->status);
1007        }
1008    }
1009
1010    if (reason) {
1011        /* noop */
1012    }
1013    else if (exps != NULL && exp == APR_DATE_BAD) {
1014        /* if a broken Expires header is present, don't cache it */
1015        reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL);
1016    }
1017    else if (!dconf->store_expired && exp != APR_DATE_BAD
1018            && exp < r->request_time) {
1019        /* if a Expires header is in the past, don't cache it */
1020        reason = "Expires header already expired; not cacheable";
1021    }
1022    else if (!dconf->store_expired && (control.must_revalidate
1023            || control.proxy_revalidate) && (!control.s_maxage_value
1024            || (!control.s_maxage && !control.max_age_value)) && lastmods
1025            == NULL && etag == NULL) {
1026        /* if we're already stale, but can never revalidate, don't cache it */
1027        reason
1028                = "s-maxage or max-age zero and no Last-Modified or Etag; not cacheable";
1029    }
1030    else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL
1031            && !control.max_age && !control.s_maxage) {
1032        /* if a query string is present but no explicit expiration time,
1033         * don't cache it (RFC 2616/13.9 & 13.2.1)
1034         */
1035        reason = "Query string present but no explicit expiration time";
1036    }
1037    else if (r->status == HTTP_NOT_MODIFIED &&
1038             !cache->handle && !cache->stale_handle) {
1039        /* if the server said 304 Not Modified but we have no cache
1040         * file - pass this untouched to the user agent, it's not for us.
1041         */
1042        reason = "HTTP Status 304 Not Modified";
1043    }
1044    else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL && (exps
1045            == NULL) && (dconf->no_last_mod_ignore == 0) && !control.max_age
1046            && !control.s_maxage) {
1047        /* 200 OK response from HTTP/1.0 and up without Last-Modified,
1048         * Etag, Expires, Cache-Control:max-age, or Cache-Control:s-maxage
1049         * headers.
1050         */
1051        /* Note: mod-include clears last_modified/expires/etags - this
1052         * is why we have an optional function for a key-gen ;-)
1053         */
1054        reason = "No Last-Modified; Etag; Expires; Cache-Control:max-age or Cache-Control:s-maxage headers";
1055    }
1056    else if (!dconf->store_nostore && control.no_store) {
1057        /* RFC2616 14.9.2 Cache-Control: no-store response
1058         * indicating do not cache, or stop now if you are
1059         * trying to cache it.
1060         */
1061        reason = "Cache-Control: no-store present";
1062    }
1063    else if (!dconf->store_private && control.private) {
1064        /* RFC2616 14.9.1 Cache-Control: private response
1065         * this object is marked for this user's eyes only. Behave
1066         * as a tunnel.
1067         */
1068        reason = "Cache-Control: private present";
1069    }
1070    else if (apr_table_get(r->headers_in, "Authorization")
1071            && !(control.s_maxage || control.must_revalidate
1072                    || control.proxy_revalidate || control.public)) {
1073        /* RFC2616 14.8 Authorisation:
1074         * if authorisation is included in the request, we don't cache,
1075         * but we can cache if the following exceptions are true:
1076         * 1) If Cache-Control: s-maxage is included
1077         * 2) If Cache-Control: must-revalidate is included
1078         * 3) If Cache-Control: public is included
1079         */
1080        reason = "Authorization required";
1081    }
1082    else if (ap_find_token(NULL, apr_table_get(r->headers_out, "Vary"), "*")) {
1083        reason = "Vary header contains '*'";
1084    }
1085    else if (apr_table_get(r->subprocess_env, "no-cache") != NULL) {
1086        reason = "environment variable 'no-cache' is set";
1087    }
1088    else if (r->no_cache) {
1089        /* or we've been asked not to cache it above */
1090        reason = "r->no_cache present";
1091    }
1092    else if (cache->stale_handle
1093            && APR_DATE_BAD
1094                    != (date = apr_date_parse_http(
1095                            apr_table_get(r->headers_out, "Date")))
1096            && date < cache->stale_handle->cache_obj->info.date) {
1097
1098        /**
1099         * 13.12 Cache Replacement:
1100         *
1101         * Note: a new response that has an older Date header value than
1102         * existing cached responses is not cacheable.
1103         */
1104        reason = "updated entity is older than cached entity";
1105
1106        /* while this response is not cacheable, the previous response still is */
1107        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02474)
1108                "cache: Removing CACHE_REMOVE_URL filter.");
1109        ap_remove_output_filter(cache->remove_url_filter);
1110    }
1111    else if (r->status == HTTP_NOT_MODIFIED && cache->stale_handle) {
1112        apr_table_t *left = cache->stale_handle->resp_hdrs;
1113        apr_table_t *right = r->headers_out;
1114
1115        /* and lastly, contradiction checks for revalidated responses
1116         * as per RFC2616 Section 10.3.5
1117         */
1118        if (((reason = cache_header_cmp(r->pool, left, right, "Allow")))
1119                || ((reason = cache_header_cmp(r->pool, left, right,
1120                        "Content-Encoding")))
1121                || ((reason = cache_header_cmp(r->pool, left, right,
1122                        "Content-Language")))
1123                || ((reason = cache_header_cmp(r->pool, left, right,
1124                        "Content-Length")))
1125                || ((reason = cache_header_cmp(r->pool, left, right,
1126                        "Content-Location")))
1127                || ((reason = cache_header_cmp(r->pool, left, right,
1128                        "Content-MD5")))
1129                || ((reason = cache_header_cmp(r->pool, left, right,
1130                        "Content-Range")))
1131                || ((reason = cache_header_cmp(r->pool, left, right,
1132                        "Content-Type")))
1133                || ((reason = cache_header_cmp(r->pool, left, right, "ETag")))
1134                || ((reason = cache_header_cmp(r->pool, left, right,
1135                        "Last-Modified")))) {
1136            /* contradiction: 304 Not Modified, but entity header modified */
1137        }
1138    }
1139
1140    /**
1141     * Enforce RFC2616 Section 10.3.5, just in case. We caught any
1142     * inconsistencies above.
1143     *
1144     * If the conditional GET used a strong cache validator (see section
1145     * 13.3.3), the response SHOULD NOT include other entity-headers.
1146     * Otherwise (i.e., the conditional GET used a weak validator), the
1147     * response MUST NOT include other entity-headers; this prevents
1148     * inconsistencies between cached entity-bodies and updated headers.
1149     */
1150    if (r->status == HTTP_NOT_MODIFIED) {
1151        apr_table_unset(r->headers_out, "Allow");
1152        apr_table_unset(r->headers_out, "Content-Encoding");
1153        apr_table_unset(r->headers_out, "Content-Language");
1154        apr_table_unset(r->headers_out, "Content-Length");
1155        apr_table_unset(r->headers_out, "Content-MD5");
1156        apr_table_unset(r->headers_out, "Content-Range");
1157        apr_table_unset(r->headers_out, "Content-Type");
1158        apr_table_unset(r->headers_out, "Last-Modified");
1159    }
1160
1161    /* Hold the phone. Some servers might allow us to cache a 2xx, but
1162     * then make their 304 responses non cacheable. RFC2616 says this:
1163     *
1164     * If a 304 response indicates an entity not currently cached, then
1165     * the cache MUST disregard the response and repeat the request
1166     * without the conditional.
1167     *
1168     * A 304 response with contradictory headers is technically a
1169     * different entity, to be safe, we remove the entity from the cache.
1170     */
1171    if (reason && r->status == HTTP_NOT_MODIFIED && cache->stale_handle) {
1172
1173        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02473)
1174                "cache: %s responded with an uncacheable 304, "
1175                "retrying the request. Reason: %s",
1176                r->unparsed_uri, reason);
1177
1178        /* we've got a cache conditional miss! tell anyone who cares */
1179        cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
1180                apr_psprintf(r->pool,
1181                        "conditional cache miss: 304 was uncacheable, entity removed: %s",
1182                        reason));
1183
1184        /* remove the cached entity immediately, we might cache it again */
1185        ap_remove_output_filter(cache->remove_url_filter);
1186        cache_remove_url(cache, r);
1187
1188        /* let someone else attempt to cache */
1189        cache_remove_lock(conf, cache, r, NULL);
1190
1191        /* remove this filter from the chain */
1192        ap_remove_output_filter(f);
1193
1194        /* retry without the conditionals */
1195        apr_table_unset(r->headers_in, "If-Match");
1196        apr_table_unset(r->headers_in, "If-Modified-Since");
1197        apr_table_unset(r->headers_in, "If-None-Match");
1198        apr_table_unset(r->headers_in, "If-Range");
1199        apr_table_unset(r->headers_in, "If-Unmodified-Since");
1200
1201        ap_internal_redirect(r->uri, r);
1202
1203        return APR_SUCCESS;
1204    }
1205
1206    if (reason) {
1207        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00768)
1208                "cache: %s not cached. Reason: %s", r->unparsed_uri,
1209                reason);
1210
1211        /* we've got a cache miss! tell anyone who cares */
1212        cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
1213                reason);
1214
1215        /* remove this filter from the chain */
1216        ap_remove_output_filter(f);
1217
1218        /* remove the lock file unconditionally */
1219        cache_remove_lock(conf, cache, r, NULL);
1220
1221        /* ship the data up the stack */
1222        return ap_pass_brigade(f->next, in);
1223    }
1224
1225    /* Make it so that we don't execute this path again. */
1226    cache->in_checked = 1;
1227
1228    /* Set the content length if known.
1229     */
1230    cl = apr_table_get(r->err_headers_out, "Content-Length");
1231    if (cl == NULL) {
1232        cl = apr_table_get(r->headers_out, "Content-Length");
1233    }
1234    if (cl) {
1235        char *errp;
1236        if (apr_strtoff(&size, cl, &errp, 10) || *errp || size < 0) {
1237            cl = NULL; /* parse error, see next 'if' block */
1238        }
1239    }
1240
1241    if (!cl) {
1242        /* if we don't get the content-length, see if we have all the
1243         * buckets and use their length to calculate the size
1244         */
1245        int all_buckets_here=0;
1246        size=0;
1247        for (e = APR_BRIGADE_FIRST(in);
1248             e != APR_BRIGADE_SENTINEL(in);
1249             e = APR_BUCKET_NEXT(e))
1250        {
1251            if (APR_BUCKET_IS_EOS(e)) {
1252                all_buckets_here=1;
1253                break;
1254            }
1255            if (APR_BUCKET_IS_FLUSH(e)) {
1256                continue;
1257            }
1258            if (e->length == (apr_size_t)-1) {
1259                break;
1260            }
1261            size += e->length;
1262        }
1263        if (!all_buckets_here) {
1264            size = -1;
1265        }
1266    }
1267
1268    /* remember content length to check response size against later */
1269    cache->size = size;
1270
1271    /* It's safe to cache the response.
1272     *
1273     * There are two possiblities at this point:
1274     * - cache->handle == NULL. In this case there is no previously
1275     * cached entity anywhere on the system. We must create a brand
1276     * new entity and store the response in it.
1277     * - cache->stale_handle != NULL. In this case there is a stale
1278     * entity in the system which needs to be replaced by new
1279     * content (unless the result was 304 Not Modified, which means
1280     * the cached entity is actually fresh, and we should update
1281     * the headers).
1282     */
1283
1284    /* Did we have a stale cache entry that really is stale?
1285     */
1286    if (cache->stale_handle) {
1287        if (r->status == HTTP_NOT_MODIFIED) {
1288            /* Oh, hey.  It isn't that stale!  Yay! */
1289            cache->handle = cache->stale_handle;
1290            info = &cache->handle->cache_obj->info;
1291            rv = OK;
1292        }
1293        else {
1294            /* Oh, well.  Toss it. */
1295            cache->provider->remove_entity(cache->stale_handle);
1296            /* Treat the request as if it wasn't conditional. */
1297            cache->stale_handle = NULL;
1298            /*
1299             * Restore the original request headers as they may be needed
1300             * by further output filters like the byterange filter to make
1301             * the correct decisions.
1302             */
1303            r->headers_in = cache->stale_headers;
1304        }
1305    }
1306
1307    /* no cache handle, create a new entity */
1308    if (!cache->handle) {
1309        rv = cache_create_entity(cache, r, size, in);
1310        info = apr_pcalloc(r->pool, sizeof(cache_info));
1311        /* We only set info->status upon the initial creation. */
1312        info->status = r->status;
1313    }
1314
1315    if (rv != OK) {
1316        /* we've got a cache miss! tell anyone who cares */
1317        cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
1318                "cache miss: cache unwilling to store response");
1319
1320        /* Caching layer declined the opportunity to cache the response */
1321        ap_remove_output_filter(f);
1322        cache_remove_lock(conf, cache, r, NULL);
1323        return ap_pass_brigade(f->next, in);
1324    }
1325
1326    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00769)
1327            "cache: Caching url: %s", r->unparsed_uri);
1328
1329    /* We are actually caching this response. So it does not
1330     * make sense to remove this entity any more.
1331     */
1332    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00770)
1333            "cache: Removing CACHE_REMOVE_URL filter.");
1334    ap_remove_output_filter(cache->remove_url_filter);
1335
1336    /*
1337     * We now want to update the cache file header information with
1338     * the new date, last modified, expire and content length and write
1339     * it away to our cache file. First, we determine these values from
1340     * the response, using heuristics if appropriate.
1341     *
1342     * In addition, we make HTTP/1.1 age calculations and write them away
1343     * too.
1344     */
1345
1346    /* store away the previously parsed cache control headers */
1347    memcpy(&info->control, &control, sizeof(cache_control_t));
1348
1349    /* Read the date. Generate one if one is not supplied */
1350    dates = apr_table_get(r->err_headers_out, "Date");
1351    if (dates == NULL) {
1352        dates = apr_table_get(r->headers_out, "Date");
1353    }
1354    if (dates != NULL) {
1355        info->date = apr_date_parse_http(dates);
1356    }
1357    else {
1358        info->date = APR_DATE_BAD;
1359    }
1360
1361    now = apr_time_now();
1362    if (info->date == APR_DATE_BAD) {  /* No, or bad date */
1363        /* no date header (or bad header)! */
1364        info->date = now;
1365    }
1366    date = info->date;
1367
1368    /* set response_time for HTTP/1.1 age calculations */
1369    info->response_time = now;
1370
1371    /* get the request time */
1372    info->request_time = r->request_time;
1373
1374    /* check last-modified date */
1375    if (lastmod != APR_DATE_BAD && lastmod > date) {
1376        /* if it's in the future, then replace by date */
1377        lastmod = date;
1378        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0,
1379                r, APLOGNO(00771) "cache: Last modified is in the future, "
1380                "replacing with now");
1381    }
1382
1383    /* if no expiry date then
1384     *   if Cache-Control: max-age
1385     *      expiry date = date + max-age
1386     *   else if lastmod
1387     *      expiry date = date + min((date - lastmod) * factor, maxexpire)
1388     *   else
1389     *      expire date = date + defaultexpire
1390     */
1391    if (exp == APR_DATE_BAD) {
1392
1393        if (control.max_age) {
1394            apr_int64_t x;
1395
1396            errno = 0;
1397            x = control.max_age_value;
1398            if (errno) {
1399                x = dconf->defex;
1400            }
1401            else {
1402                x = x * MSEC_ONE_SEC;
1403            }
1404            if (x < dconf->minex) {
1405                x = dconf->minex;
1406            }
1407            if (x > dconf->maxex) {
1408                x = dconf->maxex;
1409            }
1410            exp = date + x;
1411        }
1412        else if ((lastmod != APR_DATE_BAD) && (lastmod < date)) {
1413            /* if lastmod == date then you get 0*conf->factor which results in
1414             * an expiration time of now. This causes some problems with
1415             * freshness calculations, so we choose the else path...
1416             */
1417            apr_time_t x = (apr_time_t) ((date - lastmod) * dconf->factor);
1418
1419            if (x < dconf->minex) {
1420                x = dconf->minex;
1421            }
1422            if (x > dconf->maxex) {
1423                x = dconf->maxex;
1424            }
1425            exp = date + x;
1426        }
1427        else {
1428            exp = date + dconf->defex;
1429        }
1430    }
1431    info->expire = exp;
1432
1433    /* We found a stale entry which wasn't really stale. */
1434    if (cache->stale_handle) {
1435
1436        /* RFC 2616 10.3.5 states that entity headers are not supposed
1437         * to be in the 304 response.  Therefore, we need to combine the
1438         * response headers with the cached headers *before* we update
1439         * the cached headers.
1440         *
1441         * However, before doing that, we need to first merge in
1442         * err_headers_out and we also need to strip any hop-by-hop
1443         * headers that might have snuck in.
1444         */
1445        r->headers_out = ap_cache_cacheable_headers_out(r);
1446
1447        /* Merge in our cached headers.  However, keep any updated values. */
1448        /* take output, overlay on top of cached */
1449        cache_accept_headers(cache->handle, r, r->headers_out,
1450                cache->handle->resp_hdrs, 1);
1451    }
1452
1453    /* Write away header information to cache. It is possible that we are
1454     * trying to update headers for an entity which has already been cached.
1455     *
1456     * This may fail, due to an unwritable cache area. E.g. filesystem full,
1457     * permissions problems or a read-only (re)mount. This must be handled
1458     * later.
1459     */
1460    rv = cache->provider->store_headers(cache->handle, r, info);
1461
1462    /* Did we just update the cached headers on a revalidated response?
1463     *
1464     * If so, we can now decide what to serve to the client.  This is done in
1465     * the same way as with a regular response, but conditions are now checked
1466     * against the cached or merged response headers.
1467     */
1468    if (cache->stale_handle) {
1469        apr_bucket_brigade *bb;
1470        apr_bucket *bkt;
1471        int status;
1472
1473        /* Load in the saved status and clear the status line. */
1474        r->status = info->status;
1475        r->status_line = NULL;
1476
1477        /* We're just saving response headers, so we are done. Commit
1478         * the response at this point, unless there was a previous error.
1479         */
1480        if (rv == APR_SUCCESS) {
1481            rv = cache->provider->commit_entity(cache->handle, r);
1482        }
1483
1484        bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
1485
1486        /* Restore the original request headers and see if we need to
1487         * return anything else than the cached response (ie. the original
1488         * request was conditional).
1489         */
1490        r->headers_in = cache->stale_headers;
1491        status = ap_meets_conditions(r);
1492        if (status != OK) {
1493            r->status = status;
1494
1495            bkt = apr_bucket_flush_create(bb->bucket_alloc);
1496            APR_BRIGADE_INSERT_TAIL(bb, bkt);
1497        }
1498        else {
1499            cache->provider->recall_body(cache->handle, r->pool, bb);
1500
1501            bkt = apr_bucket_eos_create(bb->bucket_alloc);
1502            APR_BRIGADE_INSERT_TAIL(bb, bkt);
1503        }
1504
1505        cache->block_response = 1;
1506
1507        /* Before returning we need to handle the possible case of an
1508         * unwritable cache. Rather than leaving the entity in the cache
1509         * and having it constantly re-validated, now that we have recalled
1510         * the body it is safe to try and remove the url from the cache.
1511         */
1512        if (rv != APR_SUCCESS) {
1513            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00772)
1514                    "cache: updating headers with store_headers failed. "
1515                    "Removing cached url.");
1516
1517            rv = cache->provider->remove_url(cache->stale_handle, r);
1518            if (rv != OK) {
1519                /* Probably a mod_cache_disk cache area has been (re)mounted
1520                 * read-only, or that there is a permissions problem.
1521                 */
1522                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00773)
1523                        "cache: attempt to remove url from cache unsuccessful.");
1524            }
1525
1526            /* we've got a cache conditional hit! tell anyone who cares */
1527            cache_run_cache_status(cache->handle, r, r->headers_out,
1528                    AP_CACHE_REVALIDATE,
1529                    "conditional cache hit: entity refresh failed");
1530
1531        }
1532        else {
1533
1534            /* we've got a cache conditional hit! tell anyone who cares */
1535            cache_run_cache_status(cache->handle, r, r->headers_out,
1536                    AP_CACHE_REVALIDATE,
1537                    "conditional cache hit: entity refreshed");
1538
1539        }
1540
1541        /* let someone else attempt to cache */
1542        cache_remove_lock(conf, cache, r, NULL);
1543
1544        return ap_pass_brigade(f->next, bb);
1545    }
1546
1547    if (rv != APR_SUCCESS) {
1548        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00774)
1549                "cache: store_headers failed");
1550
1551        /* we've got a cache miss! tell anyone who cares */
1552        cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
1553                "cache miss: store_headers failed");
1554
1555        ap_remove_output_filter(f);
1556        cache_remove_lock(conf, cache, r, NULL);
1557        return ap_pass_brigade(f->next, in);
1558    }
1559
1560    /* we've got a cache miss! tell anyone who cares */
1561    cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
1562            "cache miss: attempting entity save");
1563
1564    return cache_save_store(f, in, conf, cache);
1565}
1566
1567/*
1568 * CACHE_REMOVE_URL filter
1569 * -----------------------
1570 *
1571 * This filter gets added in the quick handler every time the CACHE_SAVE filter
1572 * gets inserted. Its purpose is to remove a confirmed stale cache entry from
1573 * the cache.
1574 *
1575 * CACHE_REMOVE_URL has to be a protocol filter to ensure that is run even if
1576 * the response is a canned error message, which removes the content filters
1577 * and thus the CACHE_SAVE filter from the chain.
1578 *
1579 * CACHE_REMOVE_URL expects cache request rec within its context because the
1580 * request this filter runs on can be different from the one whose cache entry
1581 * should be removed, due to internal redirects.
1582 *
1583 * Note that CACHE_SAVE_URL (as a content-set filter, hence run before the
1584 * protocol filters) will remove this filter if it decides to cache the file.
1585 * Therefore, if this filter is left in, it must mean we need to toss any
1586 * existing files.
1587 */
1588static apr_status_t cache_remove_url_filter(ap_filter_t *f,
1589                                            apr_bucket_brigade *in)
1590{
1591    request_rec *r = f->r;
1592    cache_request_rec *cache;
1593
1594    /* Setup cache_request_rec */
1595    cache = (cache_request_rec *) f->ctx;
1596
1597    if (!cache) {
1598        /* user likely configured CACHE_REMOVE_URL manually; they should really
1599         * use mod_cache configuration to do that. So:
1600         * 1. Remove ourselves
1601         * 2. Do nothing and bail out
1602         */
1603        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00775)
1604                "cache: CACHE_REMOVE_URL enabled unexpectedly");
1605        ap_remove_output_filter(f);
1606        return ap_pass_brigade(f->next, in);
1607    }
1608
1609    /* Now remove this cache entry from the cache */
1610    cache_remove_url(cache, r);
1611
1612    /* remove ourselves */
1613    ap_remove_output_filter(f);
1614    return ap_pass_brigade(f->next, in);
1615}
1616
1617/*
1618 * CACHE_INVALIDATE filter
1619 * -----------------------
1620 *
1621 * This filter gets added in the quick handler should a PUT, POST or DELETE
1622 * method be detected. If the response is successful, we must invalidate any
1623 * cached entity as per RFC2616 section 13.10.
1624 *
1625 * CACHE_INVALIDATE has to be a protocol filter to ensure that is run even if
1626 * the response is a canned error message, which removes the content filters
1627 * from the chain.
1628 *
1629 * CACHE_INVALIDATE expects cache request rec within its context because the
1630 * request this filter runs on can be different from the one whose cache entry
1631 * should be removed, due to internal redirects.
1632 */
1633static apr_status_t cache_invalidate_filter(ap_filter_t *f,
1634                                            apr_bucket_brigade *in)
1635{
1636    request_rec *r = f->r;
1637    cache_request_rec *cache;
1638
1639    /* Setup cache_request_rec */
1640    cache = (cache_request_rec *) f->ctx;
1641
1642    if (!cache) {
1643        /* user likely configured CACHE_INVALIDATE manually; they should really
1644         * use mod_cache configuration to do that. So:
1645         * 1. Remove ourselves
1646         * 2. Do nothing and bail out
1647         */
1648        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02465)
1649                "cache: CACHE_INVALIDATE enabled unexpectedly: %s", r->uri);
1650    }
1651    else {
1652
1653        if (r->status > 299) {
1654
1655            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02466)
1656                    "cache: response status to '%s' method is %d (>299), not invalidating cached entity: %s", r->method, r->status, r->uri);
1657
1658        }
1659        else {
1660
1661            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02467)
1662                    "cache: Invalidating all cached entities in response to '%s' request for %s",
1663                    r->method, r->uri);
1664
1665            cache_invalidate(cache, r);
1666
1667            /* we've got a cache invalidate! tell everyone who cares */
1668            cache_run_cache_status(cache->handle, r, r->headers_out,
1669                    AP_CACHE_INVALIDATE, apr_psprintf(r->pool,
1670                            "cache invalidated by %s", r->method));
1671
1672        }
1673
1674    }
1675
1676    /* remove ourselves */
1677    ap_remove_output_filter(f);
1678    return ap_pass_brigade(f->next, in);
1679}
1680
1681/*
1682 * CACHE filter
1683 * ------------
1684 *
1685 * This filter can be optionally inserted into the filter chain by the admin as
1686 * a marker representing the precise location within the filter chain where
1687 * caching is to be performed.
1688 *
1689 * When the filter chain is set up in the non-quick version of the URL handler,
1690 * the CACHE filter is replaced by the CACHE_OUT or CACHE_SAVE filter,
1691 * effectively inserting the caching filters at the point indicated by the
1692 * admin. The CACHE filter is then removed.
1693 *
1694 * This allows caching to be performed before the content is passed to the
1695 * INCLUDES filter, or to a filter that might perform transformations unique
1696 * to the specific request and that would otherwise be non-cacheable.
1697 */
1698static apr_status_t cache_filter(ap_filter_t *f, apr_bucket_brigade *in)
1699{
1700
1701    cache_server_conf
1702            *conf =
1703                    (cache_server_conf *) ap_get_module_config(f->r->server->module_config,
1704                            &cache_module);
1705
1706    /* was the quick handler enabled */
1707    if (conf->quick) {
1708        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(00776)
1709                "cache: CACHE filter was added in quick handler mode and "
1710                "will be ignored: %s", f->r->unparsed_uri);
1711    }
1712    /* otherwise we may have been bypassed, nothing to see here */
1713    else {
1714        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(00777)
1715                "cache: CACHE filter was added twice, or was added where "
1716                "the cache has been bypassed and will be ignored: %s",
1717                f->r->unparsed_uri);
1718    }
1719
1720    /* we are just a marker, so let's just remove ourselves */
1721    ap_remove_output_filter(f);
1722    return ap_pass_brigade(f->next, in);
1723}
1724
1725/**
1726 * If configured, add the status of the caching attempt to the subprocess
1727 * environment, and if configured, to headers in the response.
1728 *
1729 * The status is saved below the broad category of the status (hit, miss,
1730 * revalidate), as well as a single cache-status key. This can be used for
1731 * conditional logging.
1732 *
1733 * The status is optionally saved to an X-Cache header, and the detail of
1734 * why a particular cache entry was cached (or not cached) is optionally
1735 * saved to an X-Cache-Detail header. This extra detail is useful for
1736 * service developers who may need to know whether their Cache-Control headers
1737 * are working correctly.
1738 */
1739static int cache_status(cache_handle_t *h, request_rec *r,
1740        apr_table_t *headers, ap_cache_status_e status, const char *reason)
1741{
1742    cache_server_conf
1743            *conf =
1744                    (cache_server_conf *) ap_get_module_config(r->server->module_config,
1745                            &cache_module);
1746
1747    cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_module);
1748    int x_cache = 0, x_cache_detail = 0;
1749
1750    switch (status) {
1751    case AP_CACHE_HIT: {
1752        apr_table_setn(r->subprocess_env, AP_CACHE_HIT_ENV, reason);
1753        break;
1754    }
1755    case AP_CACHE_REVALIDATE: {
1756        apr_table_setn(r->subprocess_env, AP_CACHE_REVALIDATE_ENV, reason);
1757        break;
1758    }
1759    case AP_CACHE_MISS: {
1760        apr_table_setn(r->subprocess_env, AP_CACHE_MISS_ENV, reason);
1761        break;
1762    }
1763    case AP_CACHE_INVALIDATE: {
1764        apr_table_setn(r->subprocess_env, AP_CACHE_INVALIDATE_ENV, reason);
1765        break;
1766    }
1767    }
1768
1769    apr_table_setn(r->subprocess_env, AP_CACHE_STATUS_ENV, reason);
1770
1771    if (dconf && dconf->x_cache_set) {
1772        x_cache = dconf->x_cache;
1773    }
1774    else {
1775        x_cache = conf->x_cache;
1776    }
1777    if (x_cache) {
1778        apr_table_setn(headers, "X-Cache", apr_psprintf(r->pool, "%s from %s",
1779                status == AP_CACHE_HIT ? "HIT"
1780                        : status == AP_CACHE_REVALIDATE ? "REVALIDATE" : status
1781                                == AP_CACHE_INVALIDATE ? "INVALIDATE" : "MISS",
1782                r->server->server_hostname));
1783    }
1784
1785    if (dconf && dconf->x_cache_detail_set) {
1786        x_cache_detail = dconf->x_cache_detail;
1787    }
1788    else {
1789        x_cache_detail = conf->x_cache_detail;
1790    }
1791    if (x_cache_detail) {
1792        apr_table_setn(headers, "X-Cache-Detail", apr_psprintf(r->pool,
1793                "\"%s\" from %s", reason, r->server->server_hostname));
1794    }
1795
1796    return OK;
1797}
1798
1799/**
1800 * If an error has occurred, but we have a stale cached entry, restore the
1801 * filter stack from the save filter onwards. The canned error message will
1802 * be discarded in the process, and replaced with the cached response.
1803 */
1804static void cache_insert_error_filter(request_rec *r)
1805{
1806    void *dummy;
1807    cache_dir_conf *dconf;
1808
1809    /* ignore everything except for 5xx errors */
1810    if (r->status < HTTP_INTERNAL_SERVER_ERROR) {
1811        return;
1812    }
1813
1814    dconf = ap_get_module_config(r->per_dir_config, &cache_module);
1815
1816    if (!dconf->stale_on_error) {
1817        return;
1818    }
1819
1820    /* RFC2616 13.8 Errors or Incomplete Response Cache Behavior:
1821     * If a cache receives a 5xx response while attempting to revalidate an
1822     * entry, it MAY either forward this response to the requesting client,
1823     * or act as if the server failed to respond. In the latter case, it MAY
1824     * return a previously received response unless the cached entry
1825     * includes the "must-revalidate" cache-control directive (see section
1826     * 14.9).
1827     *
1828     * This covers the case where the error was generated by our server via
1829     * ap_die().
1830     */
1831    apr_pool_userdata_get(&dummy, CACHE_CTX_KEY, r->pool);
1832    if (dummy) {
1833        cache_request_rec *cache = (cache_request_rec *) dummy;
1834
1835        ap_remove_output_filter(cache->remove_url_filter);
1836
1837        if (cache->stale_handle && cache->save_filter
1838                && !cache->stale_handle->cache_obj->info.control.must_revalidate
1839                && !cache->stale_handle->cache_obj->info.control.proxy_revalidate
1840                && !cache->stale_handle->cache_obj->info.control.s_maxage) {
1841            const char *warn_head;
1842            cache_server_conf
1843                    *conf =
1844                            (cache_server_conf *) ap_get_module_config(r->server->module_config,
1845                                    &cache_module);
1846
1847            /* morph the current save filter into the out filter, and serve from
1848             * cache.
1849             */
1850            cache->handle = cache->stale_handle;
1851            if (r->main) {
1852                cache->save_filter->frec = cache_out_subreq_filter_handle;
1853            }
1854            else {
1855                cache->save_filter->frec = cache_out_filter_handle;
1856            }
1857
1858            r->output_filters = cache->save_filter;
1859
1860            r->err_headers_out = cache->stale_handle->resp_hdrs;
1861
1862            /* add a revalidation warning */
1863            warn_head = apr_table_get(r->err_headers_out, "Warning");
1864            if ((warn_head == NULL) || ((warn_head != NULL)
1865                    && (ap_strstr_c(warn_head, "111") == NULL))) {
1866                apr_table_mergen(r->err_headers_out, "Warning",
1867                        "111 Revalidation failed");
1868            }
1869
1870            cache_run_cache_status(
1871                    cache->handle,
1872                    r,
1873                    r->err_headers_out,
1874                    AP_CACHE_HIT,
1875                    apr_psprintf(
1876                            r->pool,
1877                            "cache hit: %d status; stale content returned",
1878                            r->status));
1879
1880            /* give someone else the chance to cache the file */
1881            cache_remove_lock(conf, cache, r, NULL);
1882
1883        }
1884    }
1885
1886    return;
1887}
1888
1889/* -------------------------------------------------------------- */
1890/* Setup configurable data */
1891
1892static void *create_dir_config(apr_pool_t *p, char *dummy)
1893{
1894    cache_dir_conf *dconf = apr_pcalloc(p, sizeof(cache_dir_conf));
1895
1896    dconf->no_last_mod_ignore = 0;
1897    dconf->store_expired = 0;
1898    dconf->store_private = 0;
1899    dconf->store_nostore = 0;
1900
1901    /* maximum time to cache a document */
1902    dconf->maxex = DEFAULT_CACHE_MAXEXPIRE;
1903    dconf->minex = DEFAULT_CACHE_MINEXPIRE;
1904    /* default time to cache a document */
1905    dconf->defex = DEFAULT_CACHE_EXPIRE;
1906
1907    /* factor used to estimate Expires date from LastModified date */
1908    dconf->factor = DEFAULT_CACHE_LMFACTOR;
1909
1910    dconf->x_cache = DEFAULT_X_CACHE;
1911    dconf->x_cache_detail = DEFAULT_X_CACHE_DETAIL;
1912
1913    dconf->stale_on_error = DEFAULT_CACHE_STALE_ON_ERROR;
1914
1915    /* array of providers for this URL space */
1916    dconf->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
1917
1918    return dconf;
1919}
1920
1921static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv) {
1922    cache_dir_conf *new = (cache_dir_conf *) apr_pcalloc(p, sizeof(cache_dir_conf));
1923    cache_dir_conf *add = (cache_dir_conf *) addv;
1924    cache_dir_conf *base = (cache_dir_conf *) basev;
1925
1926    new->no_last_mod_ignore = (add->no_last_mod_ignore_set == 0) ? base->no_last_mod_ignore : add->no_last_mod_ignore;
1927    new->no_last_mod_ignore_set = add->no_last_mod_ignore_set || base->no_last_mod_ignore_set;
1928
1929    new->store_expired = (add->store_expired_set == 0) ? base->store_expired : add->store_expired;
1930    new->store_expired_set = add->store_expired_set || base->store_expired_set;
1931    new->store_private = (add->store_private_set == 0) ? base->store_private : add->store_private;
1932    new->store_private_set = add->store_private_set || base->store_private_set;
1933    new->store_nostore = (add->store_nostore_set == 0) ? base->store_nostore : add->store_nostore;
1934    new->store_nostore_set = add->store_nostore_set || base->store_nostore_set;
1935
1936    /* maximum time to cache a document */
1937    new->maxex = (add->maxex_set == 0) ? base->maxex : add->maxex;
1938    new->maxex_set = add->maxex_set || base->maxex_set;
1939    new->minex = (add->minex_set == 0) ? base->minex : add->minex;
1940    new->minex_set = add->minex_set || base->minex_set;
1941
1942    /* default time to cache a document */
1943    new->defex = (add->defex_set == 0) ? base->defex : add->defex;
1944    new->defex_set = add->defex_set || base->defex_set;
1945
1946    /* factor used to estimate Expires date from LastModified date */
1947    new->factor = (add->factor_set == 0) ? base->factor : add->factor;
1948    new->factor_set = add->factor_set || base->factor_set;
1949
1950    new->x_cache = (add->x_cache_set == 0) ? base->x_cache : add->x_cache;
1951    new->x_cache_set = add->x_cache_set || base->x_cache_set;
1952    new->x_cache_detail = (add->x_cache_detail_set == 0) ? base->x_cache_detail
1953            : add->x_cache_detail;
1954    new->x_cache_detail_set = add->x_cache_detail_set
1955            || base->x_cache_detail_set;
1956
1957    new->stale_on_error = (add->stale_on_error_set == 0) ? base->stale_on_error
1958            : add->stale_on_error;
1959    new->stale_on_error_set = add->stale_on_error_set
1960            || base->stale_on_error_set;
1961
1962    new->cacheenable = add->enable_set ? apr_array_append(p, base->cacheenable,
1963            add->cacheenable) : base->cacheenable;
1964    new->enable_set = add->enable_set || base->enable_set;
1965    new->disable = (add->disable_set == 0) ? base->disable : add->disable;
1966    new->disable_set = add->disable_set || base->disable_set;
1967
1968    return new;
1969}
1970
1971static void * create_cache_config(apr_pool_t *p, server_rec *s)
1972{
1973    const char *tmppath = NULL;
1974    cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
1975
1976    /* array of URL prefixes for which caching is enabled */
1977    ps->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
1978    /* array of URL prefixes for which caching is disabled */
1979    ps->cachedisable = apr_array_make(p, 10, sizeof(struct cache_disable));
1980    ps->ignorecachecontrol = 0;
1981    ps->ignorecachecontrol_set = 0;
1982    /* array of headers that should not be stored in cache */
1983    ps->ignore_headers = apr_array_make(p, 10, sizeof(char *));
1984    ps->ignore_headers_set = CACHE_IGNORE_HEADERS_UNSET;
1985    /* flag indicating that query-string should be ignored when caching */
1986    ps->ignorequerystring = 0;
1987    ps->ignorequerystring_set = 0;
1988    /* by default, run in the quick handler */
1989    ps->quick = 1;
1990    ps->quick_set = 0;
1991    /* array of identifiers that should not be used for key calculation */
1992    ps->ignore_session_id = apr_array_make(p, 10, sizeof(char *));
1993    ps->ignore_session_id_set = CACHE_IGNORE_SESSION_ID_UNSET;
1994    ps->lock = 0; /* thundering herd lock defaults to off */
1995    ps->lock_set = 0;
1996    apr_temp_dir_get(&tmppath, p);
1997    if (tmppath) {
1998        ps->lockpath = apr_pstrcat(p, tmppath, DEFAULT_CACHE_LOCKPATH, NULL);
1999    }
2000    ps->lockmaxage = apr_time_from_sec(DEFAULT_CACHE_MAXAGE);
2001    ps->x_cache = DEFAULT_X_CACHE;
2002    ps->x_cache_detail = DEFAULT_X_CACHE_DETAIL;
2003    return ps;
2004}
2005
2006static void * merge_cache_config(apr_pool_t *p, void *basev, void *overridesv)
2007{
2008    cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
2009    cache_server_conf *base = (cache_server_conf *) basev;
2010    cache_server_conf *overrides = (cache_server_conf *) overridesv;
2011
2012    /* array of URL prefixes for which caching is disabled */
2013    ps->cachedisable = apr_array_append(p,
2014                                        base->cachedisable,
2015                                        overrides->cachedisable);
2016    /* array of URL prefixes for which caching is enabled */
2017    ps->cacheenable = apr_array_append(p,
2018                                       base->cacheenable,
2019                                       overrides->cacheenable);
2020
2021    ps->ignorecachecontrol  =
2022        (overrides->ignorecachecontrol_set == 0)
2023        ? base->ignorecachecontrol
2024        : overrides->ignorecachecontrol;
2025    ps->ignore_headers =
2026        (overrides->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET)
2027        ? base->ignore_headers
2028        : overrides->ignore_headers;
2029    ps->ignorequerystring =
2030        (overrides->ignorequerystring_set == 0)
2031        ? base->ignorequerystring
2032        : overrides->ignorequerystring;
2033    ps->ignore_session_id =
2034        (overrides->ignore_session_id_set == CACHE_IGNORE_SESSION_ID_UNSET)
2035        ? base->ignore_session_id
2036        : overrides->ignore_session_id;
2037    ps->lock =
2038        (overrides->lock_set == 0)
2039        ? base->lock
2040        : overrides->lock;
2041    ps->lockpath =
2042        (overrides->lockpath_set == 0)
2043        ? base->lockpath
2044        : overrides->lockpath;
2045    ps->lockmaxage =
2046        (overrides->lockmaxage_set == 0)
2047        ? base->lockmaxage
2048        : overrides->lockmaxage;
2049    ps->quick =
2050        (overrides->quick_set == 0)
2051        ? base->quick
2052        : overrides->quick;
2053    ps->x_cache =
2054        (overrides->x_cache_set == 0)
2055        ? base->x_cache
2056        : overrides->x_cache;
2057    ps->x_cache_detail =
2058        (overrides->x_cache_detail_set == 0)
2059        ? base->x_cache_detail
2060        : overrides->x_cache_detail;
2061    ps->base_uri =
2062        (overrides->base_uri_set == 0)
2063        ? base->base_uri
2064        : overrides->base_uri;
2065    return ps;
2066}
2067
2068static const char *set_cache_quick_handler(cmd_parms *parms, void *dummy,
2069                                           int flag)
2070{
2071    cache_server_conf *conf;
2072
2073    conf =
2074        (cache_server_conf *)ap_get_module_config(parms->server->module_config
2075,
2076                                                  &cache_module);
2077    conf->quick = flag;
2078    conf->quick_set = 1;
2079    return NULL;
2080
2081}
2082
2083static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
2084                                                int flag)
2085{
2086    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2087
2088    dconf->no_last_mod_ignore = flag;
2089    dconf->no_last_mod_ignore_set = 1;
2090    return NULL;
2091
2092}
2093
2094static const char *set_cache_ignore_cachecontrol(cmd_parms *parms,
2095                                                 void *dummy, int flag)
2096{
2097    cache_server_conf *conf;
2098
2099    conf =
2100        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2101                                                  &cache_module);
2102    conf->ignorecachecontrol = flag;
2103    conf->ignorecachecontrol_set = 1;
2104    return NULL;
2105}
2106
2107static const char *set_cache_store_expired(cmd_parms *parms, void *dummy,
2108                                           int flag)
2109{
2110    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2111
2112    dconf->store_expired = flag;
2113    dconf->store_expired_set = 1;
2114    return NULL;
2115}
2116
2117static const char *set_cache_store_private(cmd_parms *parms, void *dummy,
2118                                           int flag)
2119{
2120    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2121
2122    dconf->store_private = flag;
2123    dconf->store_private_set = 1;
2124    return NULL;
2125}
2126
2127static const char *set_cache_store_nostore(cmd_parms *parms, void *dummy,
2128                                           int flag)
2129{
2130    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2131
2132    dconf->store_nostore = flag;
2133    dconf->store_nostore_set = 1;
2134    return NULL;
2135}
2136
2137static const char *add_ignore_header(cmd_parms *parms, void *dummy,
2138                                     const char *header)
2139{
2140    cache_server_conf *conf;
2141    char **new;
2142
2143    conf =
2144        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2145                                                  &cache_module);
2146    if (!strcasecmp(header, "None")) {
2147        /* if header None is listed clear array */
2148        conf->ignore_headers->nelts = 0;
2149    }
2150    else {
2151        if ((conf->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET) ||
2152            (conf->ignore_headers->nelts)) {
2153            /* Only add header if no "None" has been found in header list
2154             * so far.
2155             * (When 'None' is passed, IGNORE_HEADERS_SET && nelts == 0.)
2156             */
2157            new = (char **)apr_array_push(conf->ignore_headers);
2158            (*new) = (char *)header;
2159        }
2160    }
2161    conf->ignore_headers_set = CACHE_IGNORE_HEADERS_SET;
2162    return NULL;
2163}
2164
2165static const char *add_ignore_session_id(cmd_parms *parms, void *dummy,
2166                                         const char *identifier)
2167{
2168    cache_server_conf *conf;
2169    char **new;
2170
2171    conf =
2172        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2173                                                  &cache_module);
2174    if (!strcasecmp(identifier, "None")) {
2175        /* if identifier None is listed clear array */
2176        conf->ignore_session_id->nelts = 0;
2177    }
2178    else {
2179        if ((conf->ignore_session_id_set == CACHE_IGNORE_SESSION_ID_UNSET) ||
2180            (conf->ignore_session_id->nelts)) {
2181            /*
2182             * Only add identifier if no "None" has been found in identifier
2183             * list so far.
2184             */
2185            new = (char **)apr_array_push(conf->ignore_session_id);
2186            (*new) = (char *)identifier;
2187        }
2188    }
2189    conf->ignore_session_id_set = CACHE_IGNORE_SESSION_ID_SET;
2190    return NULL;
2191}
2192
2193static const char *add_cache_enable(cmd_parms *parms, void *dummy,
2194                                    const char *type,
2195                                    const char *url)
2196{
2197    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2198    cache_server_conf *conf;
2199    struct cache_enable *new;
2200
2201    const char *err = ap_check_cmd_context(parms,
2202                                           NOT_IN_DIRECTORY|NOT_IN_LIMIT|NOT_IN_FILES);
2203    if (err != NULL) {
2204        return err;
2205    }
2206
2207    if (*type == '/') {
2208        return apr_psprintf(parms->pool,
2209          "provider (%s) starts with a '/'.  Are url and provider switched?",
2210          type);
2211    }
2212
2213    if (!url) {
2214        url = parms->path;
2215    }
2216    if (!url) {
2217        return apr_psprintf(parms->pool,
2218          "CacheEnable provider (%s) is missing an URL.", type);
2219    }
2220    if (parms->path && strncmp(parms->path, url, strlen(parms->path))) {
2221        return "When in a Location, CacheEnable must specify a path or an URL below "
2222        "that location.";
2223    }
2224
2225    conf =
2226        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2227                                                  &cache_module);
2228
2229    if (parms->path) {
2230        new = apr_array_push(dconf->cacheenable);
2231        dconf->enable_set = 1;
2232    }
2233    else {
2234        new = apr_array_push(conf->cacheenable);
2235    }
2236
2237    new->type = type;
2238    if (apr_uri_parse(parms->pool, url, &(new->url))) {
2239        return NULL;
2240    }
2241    if (new->url.path) {
2242        new->pathlen = strlen(new->url.path);
2243    } else {
2244        new->pathlen = 1;
2245        new->url.path = "/";
2246    }
2247    return NULL;
2248}
2249
2250static const char *add_cache_disable(cmd_parms *parms, void *dummy,
2251                                     const char *url)
2252{
2253    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2254    cache_server_conf *conf;
2255    struct cache_disable *new;
2256
2257    const char *err = ap_check_cmd_context(parms,
2258                                           NOT_IN_DIRECTORY|NOT_IN_LIMIT|NOT_IN_FILES);
2259    if (err != NULL) {
2260        return err;
2261    }
2262
2263    conf =
2264        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2265                                                  &cache_module);
2266
2267    if (parms->path) {
2268        if (!strcasecmp(url, "on")) {
2269            dconf->disable = 1;
2270            dconf->disable_set = 1;
2271            return NULL;
2272        }
2273        else {
2274            return "CacheDisable must be followed by the word 'on' when in a Location.";
2275        }
2276    }
2277
2278    if (!url || (url[0] != '/' && !ap_strchr_c(url, ':'))) {
2279        return "CacheDisable must specify a path or an URL.";
2280    }
2281
2282    new = apr_array_push(conf->cachedisable);
2283    if (apr_uri_parse(parms->pool, url, &(new->url))) {
2284        return NULL;
2285    }
2286    if (new->url.path) {
2287        new->pathlen = strlen(new->url.path);
2288    } else {
2289        new->pathlen = 1;
2290        new->url.path = "/";
2291    }
2292    return NULL;
2293}
2294
2295static const char *set_cache_maxex(cmd_parms *parms, void *dummy,
2296                                   const char *arg)
2297{
2298    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2299
2300    dconf->maxex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
2301    dconf->maxex_set = 1;
2302    return NULL;
2303}
2304
2305static const char *set_cache_minex(cmd_parms *parms, void *dummy,
2306                                   const char *arg)
2307{
2308    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2309
2310    dconf->minex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
2311    dconf->minex_set = 1;
2312    return NULL;
2313}
2314
2315static const char *set_cache_defex(cmd_parms *parms, void *dummy,
2316                                   const char *arg)
2317{
2318    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2319
2320    dconf->defex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
2321    dconf->defex_set = 1;
2322    return NULL;
2323}
2324
2325static const char *set_cache_factor(cmd_parms *parms, void *dummy,
2326                                    const char *arg)
2327{
2328    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2329    double val;
2330
2331    if (sscanf(arg, "%lg", &val) != 1) {
2332        return "CacheLastModifiedFactor value must be a float";
2333    }
2334    dconf->factor = val;
2335    dconf->factor_set = 1;
2336    return NULL;
2337}
2338
2339static const char *set_cache_ignore_querystring(cmd_parms *parms, void *dummy,
2340                                                int flag)
2341{
2342    cache_server_conf *conf;
2343
2344    conf =
2345        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2346                                                  &cache_module);
2347    conf->ignorequerystring = flag;
2348    conf->ignorequerystring_set = 1;
2349    return NULL;
2350}
2351
2352static const char *set_cache_lock(cmd_parms *parms, void *dummy,
2353                                                int flag)
2354{
2355    cache_server_conf *conf;
2356
2357    conf =
2358        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2359                                                  &cache_module);
2360    conf->lock = flag;
2361    conf->lock_set = 1;
2362    return NULL;
2363}
2364
2365static const char *set_cache_lock_path(cmd_parms *parms, void *dummy,
2366                                    const char *arg)
2367{
2368    cache_server_conf *conf;
2369
2370    conf =
2371        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2372                                                  &cache_module);
2373
2374    conf->lockpath = ap_server_root_relative(parms->pool, arg);
2375    if (!conf->lockpath) {
2376        return apr_pstrcat(parms->pool, "Invalid CacheLockPath path ",
2377                           arg, NULL);
2378    }
2379    conf->lockpath_set = 1;
2380    return NULL;
2381}
2382
2383static const char *set_cache_lock_maxage(cmd_parms *parms, void *dummy,
2384                                    const char *arg)
2385{
2386    cache_server_conf *conf;
2387    apr_int64_t seconds;
2388
2389    conf =
2390        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2391                                                  &cache_module);
2392    seconds = apr_atoi64(arg);
2393    if (seconds <= 0) {
2394        return "CacheLockMaxAge value must be a non-zero positive integer";
2395    }
2396    conf->lockmaxage = apr_time_from_sec(seconds);
2397    conf->lockmaxage_set = 1;
2398    return NULL;
2399}
2400
2401static const char *set_cache_x_cache(cmd_parms *parms, void *dummy, int flag)
2402{
2403
2404    if (parms->path) {
2405        cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2406
2407        dconf->x_cache = flag;
2408        dconf->x_cache_set = 1;
2409
2410    }
2411    else {
2412        cache_server_conf *conf =
2413            (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2414                                                      &cache_module);
2415
2416        conf->x_cache = flag;
2417        conf->x_cache_set = 1;
2418
2419    }
2420
2421    return NULL;
2422}
2423
2424static const char *set_cache_x_cache_detail(cmd_parms *parms, void *dummy, int flag)
2425{
2426
2427    if (parms->path) {
2428        cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2429
2430        dconf->x_cache_detail = flag;
2431        dconf->x_cache_detail_set = 1;
2432
2433    }
2434    else {
2435        cache_server_conf *conf =
2436            (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2437                                                      &cache_module);
2438
2439        conf->x_cache_detail = flag;
2440        conf->x_cache_detail_set = 1;
2441
2442    }
2443
2444    return NULL;
2445}
2446
2447static const char *set_cache_key_base_url(cmd_parms *parms, void *dummy,
2448        const char *arg)
2449{
2450    cache_server_conf *conf;
2451    apr_status_t rv;
2452
2453    conf =
2454        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
2455                                                  &cache_module);
2456    conf->base_uri = apr_pcalloc(parms->pool, sizeof(apr_uri_t));
2457    rv = apr_uri_parse(parms->pool, arg, conf->base_uri);
2458    if (rv != APR_SUCCESS) {
2459        return apr_psprintf(parms->pool, "Could not parse '%s' as an URL.", arg);
2460    }
2461    else if (!conf->base_uri->scheme && !conf->base_uri->hostname &&
2462            !conf->base_uri->port_str) {
2463        return apr_psprintf(parms->pool, "URL '%s' must contain at least one of a scheme, a hostname or a port.", arg);
2464    }
2465    conf->base_uri_set = 1;
2466    return NULL;
2467}
2468
2469static const char *set_cache_stale_on_error(cmd_parms *parms, void *dummy,
2470        int flag)
2471{
2472    cache_dir_conf *dconf = (cache_dir_conf *)dummy;
2473
2474    dconf->stale_on_error = flag;
2475    dconf->stale_on_error_set = 1;
2476    return NULL;
2477}
2478
2479static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
2480                             apr_pool_t *ptemp, server_rec *s)
2481{
2482    /* This is the means by which unusual (non-unix) os's may find alternate
2483     * means to run a given command (e.g. shebang/registry parsing on Win32)
2484     */
2485    cache_generate_key = APR_RETRIEVE_OPTIONAL_FN(ap_cache_generate_key);
2486    if (!cache_generate_key) {
2487        cache_generate_key = cache_generate_key_default;
2488    }
2489    return OK;
2490}
2491
2492
2493static const command_rec cache_cmds[] =
2494{
2495    /* XXX
2496     * Consider a new config directive that enables loading specific cache
2497     * implememtations (like mod_cache_mem, mod_cache_file, etc.).
2498     * Rather than using a LoadModule directive, admin would use something
2499     * like CacheModule  mem_cache_module | file_cache_module, etc,
2500     * which would cause the approprpriate cache module to be loaded.
2501     * This is more intuitive that requiring a LoadModule directive.
2502     */
2503
2504    AP_INIT_TAKE12("CacheEnable", add_cache_enable, NULL, RSRC_CONF|ACCESS_CONF,
2505                   "A cache type and partial URL prefix below which "
2506                   "caching is enabled"),
2507    AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF|ACCESS_CONF,
2508                  "A partial URL prefix below which caching is disabled"),
2509    AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF|ACCESS_CONF,
2510                  "The maximum time in seconds to cache a document"),
2511    AP_INIT_TAKE1("CacheMinExpire", set_cache_minex, NULL, RSRC_CONF|ACCESS_CONF,
2512                  "The minimum time in seconds to cache a document"),
2513    AP_INIT_TAKE1("CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF|ACCESS_CONF,
2514                  "The default time in seconds to cache a document"),
2515    AP_INIT_FLAG("CacheQuickHandler", set_cache_quick_handler, NULL,
2516                 RSRC_CONF,
2517                 "Run the cache in the quick handler, default on"),
2518    AP_INIT_FLAG("CacheIgnoreNoLastMod", set_cache_ignore_no_last_mod, NULL,
2519                 RSRC_CONF|ACCESS_CONF,
2520                 "Ignore Responses where there is no Last Modified Header"),
2521    AP_INIT_FLAG("CacheIgnoreCacheControl", set_cache_ignore_cachecontrol,
2522                 NULL, RSRC_CONF,
2523                 "Ignore requests from the client for uncached content"),
2524    AP_INIT_FLAG("CacheStoreExpired", set_cache_store_expired,
2525                 NULL, RSRC_CONF|ACCESS_CONF,
2526                 "Ignore expiration dates when populating cache, resulting in "
2527                 "an If-Modified-Since request to the backend on retrieval"),
2528    AP_INIT_FLAG("CacheStorePrivate", set_cache_store_private,
2529                 NULL, RSRC_CONF|ACCESS_CONF,
2530                 "Ignore 'Cache-Control: private' and store private content"),
2531    AP_INIT_FLAG("CacheStoreNoStore", set_cache_store_nostore,
2532                 NULL, RSRC_CONF|ACCESS_CONF,
2533                 "Ignore 'Cache-Control: no-store' and store sensitive content"),
2534    AP_INIT_ITERATE("CacheIgnoreHeaders", add_ignore_header, NULL, RSRC_CONF,
2535                    "A space separated list of headers that should not be "
2536                    "stored by the cache"),
2537    AP_INIT_FLAG("CacheIgnoreQueryString", set_cache_ignore_querystring,
2538                 NULL, RSRC_CONF,
2539                 "Ignore query-string when caching"),
2540    AP_INIT_ITERATE("CacheIgnoreURLSessionIdentifiers", add_ignore_session_id,
2541                    NULL, RSRC_CONF, "A space separated list of session "
2542                    "identifiers that should be ignored for creating the key "
2543                    "of the cached entity."),
2544    AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF|ACCESS_CONF,
2545                  "The factor used to estimate Expires date from "
2546                  "LastModified date"),
2547    AP_INIT_FLAG("CacheLock", set_cache_lock,
2548                 NULL, RSRC_CONF,
2549                 "Enable or disable the thundering herd lock."),
2550    AP_INIT_TAKE1("CacheLockPath", set_cache_lock_path, NULL, RSRC_CONF,
2551                  "The thundering herd lock path. Defaults to the '"
2552                  DEFAULT_CACHE_LOCKPATH "' directory in the system "
2553                  "temp directory."),
2554    AP_INIT_TAKE1("CacheLockMaxAge", set_cache_lock_maxage, NULL, RSRC_CONF,
2555                  "Maximum age of any thundering herd lock."),
2556    AP_INIT_FLAG("CacheHeader", set_cache_x_cache, NULL, RSRC_CONF | ACCESS_CONF,
2557                 "Add a X-Cache header to responses. Default is off."),
2558    AP_INIT_FLAG("CacheDetailHeader", set_cache_x_cache_detail, NULL,
2559                 RSRC_CONF | ACCESS_CONF,
2560                 "Add a X-Cache-Detail header to responses. Default is off."),
2561    AP_INIT_TAKE1("CacheKeyBaseURL", set_cache_key_base_url, NULL, RSRC_CONF,
2562                  "Override the base URL of reverse proxied cache keys."),
2563    AP_INIT_FLAG("CacheStaleOnError", set_cache_stale_on_error,
2564                 NULL, RSRC_CONF|ACCESS_CONF,
2565                 "Serve stale content on 5xx errors if present. Defaults to on."),
2566    {NULL}
2567};
2568
2569static void register_hooks(apr_pool_t *p)
2570{
2571    /* cache initializer */
2572    /* cache quick handler */
2573    ap_hook_quick_handler(cache_quick_handler, NULL, NULL, APR_HOOK_FIRST);
2574    /* cache handler */
2575    ap_hook_handler(cache_handler, NULL, NULL, APR_HOOK_REALLY_FIRST);
2576    /* cache status */
2577    cache_hook_cache_status(cache_status, NULL, NULL, APR_HOOK_MIDDLE);
2578    /* cache error handler */
2579    ap_hook_insert_error_filter(cache_insert_error_filter, NULL, NULL, APR_HOOK_MIDDLE);
2580    /* cache filters
2581     * XXX The cache filters need to run right after the handlers and before
2582     * any other filters. Consider creating AP_FTYPE_CACHE for this purpose.
2583     *
2584     * Depending on the type of request (subrequest / main request) they
2585     * need to be run before AP_FTYPE_CONTENT_SET / after AP_FTYPE_CONTENT_SET
2586     * filters. Thus create two filter handles for each type:
2587     * cache_save_filter_handle / cache_out_filter_handle to be used by
2588     * main requests and
2589     * cache_save_subreq_filter_handle / cache_out_subreq_filter_handle
2590     * to be run by subrequest
2591     */
2592    /*
2593     * CACHE is placed into the filter chain at an admin specified location,
2594     * and when the cache_handler is run, the CACHE filter is swapped with
2595     * the CACHE_OUT filter, or CACHE_SAVE filter as appropriate. This has
2596     * the effect of offering optional fine control of where the cache is
2597     * inserted into the filter chain.
2598     */
2599    cache_filter_handle =
2600        ap_register_output_filter("CACHE",
2601                                  cache_filter,
2602                                  NULL,
2603                                  AP_FTYPE_RESOURCE);
2604    /*
2605     * CACHE_SAVE must go into the filter chain after a possible DEFLATE
2606     * filter to ensure that the compressed content is stored.
2607     * Incrementing filter type by 1 ensures this happens.
2608     */
2609    cache_save_filter_handle =
2610        ap_register_output_filter("CACHE_SAVE",
2611                                  cache_save_filter,
2612                                  NULL,
2613                                  AP_FTYPE_CONTENT_SET+1);
2614    /*
2615     * CACHE_SAVE_SUBREQ must go into the filter chain before SUBREQ_CORE to
2616     * handle subrequsts. Decrementing filter type by 1 ensures this
2617     * happens.
2618     */
2619    cache_save_subreq_filter_handle =
2620        ap_register_output_filter("CACHE_SAVE_SUBREQ",
2621                                  cache_save_filter,
2622                                  NULL,
2623                                  AP_FTYPE_CONTENT_SET-1);
2624    /*
2625     * CACHE_OUT must go into the filter chain after a possible DEFLATE
2626     * filter to ensure that already compressed cache objects do not
2627     * get compressed again. Incrementing filter type by 1 ensures
2628     * this happens.
2629     */
2630    cache_out_filter_handle =
2631        ap_register_output_filter("CACHE_OUT",
2632                                  cache_out_filter,
2633                                  NULL,
2634                                  AP_FTYPE_CONTENT_SET+1);
2635    /*
2636     * CACHE_OUT_SUBREQ must go into the filter chain before SUBREQ_CORE to
2637     * handle subrequsts. Decrementing filter type by 1 ensures this
2638     * happens.
2639     */
2640    cache_out_subreq_filter_handle =
2641        ap_register_output_filter("CACHE_OUT_SUBREQ",
2642                                  cache_out_filter,
2643                                  NULL,
2644                                  AP_FTYPE_CONTENT_SET-1);
2645    /* CACHE_REMOVE_URL has to be a protocol filter to ensure that is
2646     * run even if the response is a canned error message, which
2647     * removes the content filters.
2648     */
2649    cache_remove_url_filter_handle =
2650        ap_register_output_filter("CACHE_REMOVE_URL",
2651                                  cache_remove_url_filter,
2652                                  NULL,
2653                                  AP_FTYPE_PROTOCOL);
2654    cache_invalidate_filter_handle =
2655        ap_register_output_filter("CACHE_INVALIDATE",
2656                                  cache_invalidate_filter,
2657                                  NULL,
2658                                  AP_FTYPE_PROTOCOL);
2659    ap_hook_post_config(cache_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
2660}
2661
2662AP_DECLARE_MODULE(cache) =
2663{
2664    STANDARD20_MODULE_STUFF,
2665    create_dir_config,      /* create per-directory config structure */
2666    merge_dir_config,       /* merge per-directory config structures */
2667    create_cache_config,    /* create per-server config structure */
2668    merge_cache_config,     /* merge per-server config structures */
2669    cache_cmds,             /* command apr_table_t */
2670    register_hooks
2671};
2672
2673APR_HOOK_STRUCT(
2674    APR_HOOK_LINK(cache_status)
2675)
2676
2677APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, cache_status,
2678        (cache_handle_t *h, request_rec *r,
2679                apr_table_t *headers, ap_cache_status_e status,
2680                const char *reason), (h, r, headers, status, reason),
2681        OK, DECLINED)
2682