/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
/* make space for the per request config */
cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
cache->size = -1;
cache->out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
/* save away the possible providers */
cache->providers = providers;
/* *Areweallowedtoservecachedinfoatall?
*/ if (!ap_cache_check_no_store(cache, r)) { return DECLINED;
}
/* Add cache_invalidate filter to this request to force a *cacheentrytobeinvalidatediftheresponseis *ultimatelysuccessful(2xx).
*/
ap_add_output_filter_handle(
cache_invalidate_filter_handle, cache, r,
r->connection);
ap_log_rerror(
APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02462) "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri);
return DECLINED;
}
}
/* *Trytoservethisrequestfromthecache. * *Ifnoexistingcachefile(DECLINED) *addcache_savefilter *Ifcachedfile(OK) *clearfilterstack *addcache_outfilter *returnOK
*/
rv = cache_select(cache, r); if (rv != OK) { if (rv == DECLINED) { if (!lookup) {
/* try to obtain a cache lock at this point. if we succeed, *wearethefirsttotryandcachethisurl.ifwefail, *itmeanssomeoneelseisalreadytryingtocachethis *url,andweshouldjustlettherequestthroughtothe *backendwithoutanyattempttocache.thisstops *duplicatedsimultaneousattemptstocacheanentity.
*/
rv = cache_try_lock(conf, cache, r); if (APR_SUCCESS == rv) {
/* we've got a cache hit! tell everyone who cares */
cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT, "cache hit");
/* if we are a lookup, we are exiting soon one way or another; Restore
* the headers. */ if (lookup) { if (cache->stale_headers) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00754) "Restoring request headers.");
r->headers_in = cache->stale_headers;
}
}
rv = ap_meets_conditions(r); if (rv != OK) { /* If we are a lookup, we have to return DECLINED as we have no *wayofknowingifwewillbeabletoservethecontent.
*/ if (lookup) { return DECLINED;
}
/* Return cached status. */ return rv;
}
/* If we're a lookup, we can exit now instead of serving the content. */ if (lookup) { return OK;
}
/* Serve up the content */
/* We are in the quick handler hook, which means that no output *filtershavebeenset.Soletsruntheinsert_filterhook.
*/
ap_run_insert_filter(r);
/* *Removeallfiltersthatarebeforethecache_outfilter.Thisensures *thatwekickoffthefilterstackwithourcache_outfilterbeingthe *firstinthechain.Thismakesensebecausewewanttorestorethings *inthesamemanneraswesavedthem. *Theremaybefiltersbeforeourcache_outfilter,because * *1.Wecallap_set_content_typeduringcache_select.Thiscauses *Content-Typespecificfilterstobeadded. *2.Wecalltheinsert_filterhook.Thiscausesfilterse.g.like *theonessetwithSetOutputFiltertobeadded.
*/
next = r->output_filters; while (next && (next->frec != cache_out_handle)) {
ap_remove_output_filter(next);
next = next->next;
}
/* kick off the filter stack */
out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
e = apr_bucket_eos_create(out->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(out, e);
/* make space for the per request config */
cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
cache->size = -1;
cache->out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
/* save away the possible providers */
cache->providers = providers;
/* *Areweallowedtoservecachedinfoatall?
*/ if (!ap_cache_check_no_store(cache, r)) { return DECLINED;
}
/* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities.
*/ switch (r->method_number) { case M_PUT: case M_POST: case M_DELETE:
{
/* Add cache_invalidate filter to this request to force a *cacheentrytobeinvalidatediftheresponseis *ultimatelysuccessful(2xx).
*/
ap_add_output_filter_handle(
cache_invalidate_filter_handle, cache, r,
r->connection);
/* try to obtain a cache lock at this point. if we succeed, *wearethefirsttotryandcachethisurl.ifwefail, *itmeanssomeoneelseisalreadytryingtocachethis *url,andweshouldjustlettherequestthroughtothe *backendwithoutanyattempttocache.thisstops *duplicatedsimultaneousattemptstocacheanentity.
*/
rv = cache_try_lock(conf, cache, r); if (APR_SUCCESS == rv) {
if (!cache) { /* user likely configured CACHE_OUT manually; they should use mod_cache
* configuration to do that */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00762) "CACHE/CACHE_OUT filter enabled while caching is disabled, ignoring");
ap_remove_output_filter(f); return ap_pass_brigade(f->next, in);
}
/* clean out any previous response up to EOS, if any */ while (!APR_BRIGADE_EMPTY(in)) {
apr_bucket *e = APR_BRIGADE_FIRST(in); if (APR_BUCKET_IS_EOS(e)) {
apr_bucket_brigade *bb = apr_brigade_create(r->pool,
r->connection->bucket_alloc);
/* restore content type of cached response if available */ /* Needed especially when stale content gets served. */ constchar *ct = apr_table_get(cache->handle->resp_hdrs, "Content-Type"); if (ct) {
ap_set_content_type(r, ct);
}
/* restore status of cached response */
r->status = cache->handle->cache_obj->info.status;
/* recall_headers() was called in cache_select() */
cache->provider->recall_body(cache->handle, r->pool, bb);
APR_BRIGADE_PREPEND(in, bb);
/* This filter is done once it has served up its content */
ap_remove_output_filter(f);
/* pass the brigade in into the cache provider, which is then *expectedtomovecachedbucketstotheoutbrigade,forus *topassupthefilterstack.repeatuntilinisempty,or *wefail.
*/ while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
rv = cache->provider->store_body(cache->handle, f->r, in, cache->out); if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, f->r, APLOGNO(00765) "cache: Cache provider's store_body failed for URI %s", f->r->uri);
ap_remove_output_filter(f);
/* give someone else the chance to cache the file */
cache_remove_lock(conf, cache, f->r, NULL);
/* give up trying to cache, just step out the way */
APR_BRIGADE_PREPEND(in, cache->out); return ap_pass_brigade(f->next, in);
}
/* does the out brigade contain eos? if so, we're done, commit! */ for (e = APR_BRIGADE_FIRST(cache->out);
e != APR_BRIGADE_SENTINEL(cache->out);
e = APR_BUCKET_NEXT(e))
{ if (APR_BUCKET_IS_EOS(e)) {
rv = cache->provider->commit_entity(cache->handle, f->r); break;
}
}
/* conditionally remove the lock as soon as we see the eos bucket */
cache_remove_lock(conf, cache, f->r, cache->out);
if (APR_BRIGADE_EMPTY(cache->out)) { if (APR_BRIGADE_EMPTY(in)) { /* cache provider wants more data before passing the brigade *upstream,obligetheproviderbyleavingtofetchmore.
*/ break;
} else { /* oops, no data out, but not all data read in either, be *safeandstanddowntopreventaspin.
*/
ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, f->r, APLOGNO(00766) "cache: Cache provider's store_body returned an " "empty brigade, but didn't consume all of the " "input brigade, standing down to prevent a spin");
ap_remove_output_filter(f);
/* give someone else the chance to cache the file */
cache_remove_lock(conf, cache, f->r, NULL);
/* Setup cache_request_rec */ if (!cache) { /* user likely configured CACHE_SAVE manually; they should really use *mod_cacheconfigurationtodothat
*/
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00767) "CACHE/CACHE_SAVE filter enabled while caching is disabled, ignoring");
ap_remove_output_filter(f); return ap_pass_brigade(f->next, in);
}
reason = NULL;
p = r->pool; /* *PassDatatoCache *------------------ *Thissectionpassesthebrigadesintothecachemodules,butonly *ifthesetupsection(seebelow)iscomplete.
*/ if (cache->block_response) { /* We've already sent down the response and EOS. So, ignore *whatevercomesnow.
*/ return APR_SUCCESS;
}
/* have we already run the cacheability check and set up the *cachedfilehandle?
*/ if (cache->in_checked) { return cache_save_store(f, in, conf, cache);
}
if (cache->stale_handle
&& !cache->stale_handle->cache_obj->info.control.must_revalidate
&& !cache->stale_handle->cache_obj->info.control.proxy_revalidate) { constchar *warn_head;
/* morph the current save filter into the out filter, and serve from *cache.
*/
cache->handle = cache->stale_handle; if (r->main) {
f->frec = cache_out_subreq_filter_handle;
} else {
f->frec = cache_out_filter_handle;
}
/* read expiry date; if a bad date, then leave it so the client can *readit
*/
exps = apr_table_get(r->err_headers_out, "Expires"); if (exps == NULL) {
exps = apr_table_get(r->headers_out, "Expires");
} if (exps != NULL) {
exp = apr_date_parse_http(exps);
} else {
exp = APR_DATE_BAD;
}
/* read the last-modified date; if the date is bad, then delete it */
lastmods = apr_table_get(r->err_headers_out, "Last-Modified"); if (lastmods == NULL) {
lastmods = apr_table_get(r->headers_out, "Last-Modified");
} if (lastmods != NULL) {
lastmod = apr_date_parse_http(lastmods); if (lastmod == APR_DATE_BAD) {
lastmods = NULL;
}
} else {
lastmod = APR_DATE_BAD;
}
/* read the etag and cache-control from the entity */
etag = apr_table_get(r->err_headers_out, "Etag"); if (etag == NULL) {
etag = apr_table_get(r->headers_out, "Etag");
}
cc_out = cache_table_getm(r->pool, r->err_headers_out, "Cache-Control");
pragma = cache_table_getm(r->pool, r->err_headers_out, "Pragma");
headers = r->err_headers_out; if (!cc_out && !pragma) {
cc_out = cache_table_getm(r->pool, r->headers_out, "Cache-Control");
pragma = cache_table_getm(r->pool, r->headers_out, "Pragma");
headers = r->headers_out;
}
/* Have we received a 304 response without any headers at all? Fall back to *theoriginalheadersintheoriginalcachedrequest.
*/ if (r->status == HTTP_NOT_MODIFIED && cache->stale_handle) { if (!cc_out && !pragma) {
cc_out = cache_table_getm(r->pool, cache->stale_handle->resp_hdrs, "Cache-Control");
pragma = cache_table_getm(r->pool, cache->stale_handle->resp_hdrs, "Pragma");
}
/* 304 does not contain Content-Type and mod_mime regenerates the *Content-Typebasedonther->filename.Thiswouldleadtooriginal *Content-Typetobelost(overwrittenbywhatevermod_mimegenerates).
* We preserves the original Content-Type here. */
ap_set_content_type(r, apr_table_get(
cache->stale_handle->resp_hdrs, "Content-Type"));
}
/* Parse the cache control header */
memset(&control, 0, sizeof(cache_control_t));
ap_cache_control(r, &control, cc_out, pragma, headers);
/* *whatresponsesshouldwenotcache? * *Atthispointwedecidebasedontheresponseheaderswhetherit *isappropriate_NOT_tocachethedatafromtheserver.Thereare *awholelotofconditionsthatpreventusfromcachingthisdata. *Theyaretestedhereonebyonetobeclearandunambiguous.
*/ if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
&& r->status != HTTP_PARTIAL_CONTENT
&& r->status != HTTP_MULTIPLE_CHOICES
&& r->status != HTTP_MOVED_PERMANENTLY
&& r->status != HTTP_NOT_MODIFIED) { /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410 *Weallowthecachingof206,butacacheimplementationmightchoose *todeclinetocachea206ifitdoesn'tknowhowto. *Weinclude304NotModifiedheretooasthisistheoriginserver *tellingustoservethecachedcopy.
*/ if (exps != NULL || cc_out != NULL) { /* We are also allowed to cache any response given that it has a *validExpiresorCacheControlheader.Ifwefindaeitherof *thosehere,wepassrequestthroughtherestofthetests.From *theRFC: * *Aresponsereceivedwithanyotherstatuscode(e.g.status *codes302and307)MUSTNOTbereturnedinareplytoa *subsequentrequestunlesstherearecache-controldirectivesor *anotherheader(s)thatexplicitlyallowit.Forexample,these *includethefollowing:anExpiresheader(section14.21);a *"max-age","s-maxage","must-revalidate","proxy-revalidate", *"public"or"private"cache-controldirective(section14.9). * *FIXME:Wrongifcc_outhasjustanextensionwedon'tknowabout
*/
} else {
reason = apr_psprintf(p, "Response status %d", r->status);
}
}
if (reason) { /* noop */
} elseif (!control.s_maxage && !control.max_age && !dconf->store_expired
&& exps != NULL && exp == APR_DATE_BAD) { /* if a broken Expires header is present, don't cache it *UnlessCC:s-maxageormax-ageispresent
*/
reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL);
} elseif (!control.s_maxage && !control.max_age
&& !dconf->store_expired && exp != APR_DATE_BAD
&& exp < r->request_time) { /* if a Expires header is in the past, don't cache it *UnlessCC:s-maxageormax-ageispresent
*/
reason = "Expires header already expired; not cacheable";
} elseif (!dconf->store_expired && (control.must_revalidate
|| control.proxy_revalidate) && (!control.s_maxage_value
|| (!control.s_maxage && !control.max_age_value)) && lastmods
== NULL && etag == NULL) { /* if we're already stale, but can never revalidate, don't cache it */
reason
= "s-maxage or max-age zero and no Last-Modified or Etag; not cacheable";
} elseif (!conf->ignorequerystring && query && exps == NULL
&& !control.max_age && !control.s_maxage) { /* if a query string is present but no explicit expiration time, *don'tcacheit(RFC2616/13.9&13.2.1)
*/
reason = "Query string present but no explicit expiration time";
} elseif (r->status == HTTP_NOT_MODIFIED &&
!cache->handle && !cache->stale_handle) { /* if the server said 304 Not Modified but we have no cache *file-passthisuntouchedtotheuseragent,it'snotforus.
*/
reason = "HTTP Status 304 Not Modified";
} elseif (r->status == HTTP_OK && lastmods == NULL && etag == NULL && (exps
== NULL) && (dconf->no_last_mod_ignore == 0) && !control.max_age
&& !control.s_maxage) { /* 200 OK response from HTTP/1.0 and up without Last-Modified, *Etag,Expires,Cache-Control:max-age,orCache-Control:s-maxage *headers.
*/ /* Note: mod-include clears last_modified/expires/etags - this *iswhywehaveanoptionalfunctionforakey-gen;-)
*/
reason = "No Last-Modified; Etag; Expires; Cache-Control:max-age or Cache-Control:s-maxage headers";
} elseif (!dconf->store_nostore && control.no_store) { /* RFC2616 14.9.2 Cache-Control: no-store response *indicatingdonotcache,orstopnowifyouare *tryingtocacheit.
*/
reason = "Cache-Control: no-store present";
} elseif (!dconf->store_private && control.private) { /* RFC2616 14.9.1 Cache-Control: private response *thisobjectismarkedforthisuser'seyesonly.Behave *asatunnel.
*/
reason = "Cache-Control: private present";
} elseif (apr_table_get(r->headers_in, "Authorization")
&& !(control.s_maxage || control.must_revalidate
|| control.proxy_revalidate || control.public)) { /* RFC2616 14.8 Authorisation: *ifauthorisationisincludedintherequest,wedon'tcache, *butwecancacheifthefollowingexceptionsaretrue: *1)IfCache-Control:s-maxageisincluded *2)IfCache-Control:must-revalidateisincluded *3)IfCache-Control:publicisincluded
*/
reason = "Authorization required";
} elseif (ap_find_token(NULL, apr_table_get(r->headers_out, "Vary"), "*")) {
reason = "Vary header contains '*'";
} elseif (apr_table_get(r->subprocess_env, "no-cache") != NULL) {
reason = "environment variable 'no-cache' is set";
} elseif (r->no_cache) { /* or we've been asked not to cache it above */
reason = "r->no_cache present";
} elseif (cache->stale_handle
&& APR_DATE_BAD
!= (date = apr_date_parse_http(
apr_table_get(r->headers_out, "Date")))
&& date < cache->stale_handle->cache_obj->info.date) {
/** *13.12CacheReplacement: * *Note:anewresponsethathasanolderDateheadervaluethan *existingcachedresponsesisnotcacheable.
*/
reason = "updated entity is older than cached entity";
/* while this response is not cacheable, the previous response still is */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02474) "cache: Removing CACHE_REMOVE_URL filter.");
ap_remove_output_filter(cache->remove_url_filter);
} elseif (r->status == HTTP_NOT_MODIFIED && cache->stale_handle) {
apr_table_t *left = cache->stale_handle->resp_hdrs;
apr_table_t *right = r->headers_out; constchar *ehs = NULL;
/* and lastly, contradiction checks for revalidated responses *asperRFC2616Section10.3.5
*/ if (cache_header_cmp(r->pool, left, right, "ETag")) {
ehs = "ETag";
} for (eh = MOD_CACHE_ENTITY_HEADERS; *eh; ++eh) { if (cache_header_cmp(r->pool, left, right, *eh)) {
ehs = (ehs) ? apr_pstrcat(r->pool, ehs, ", ", *eh, NULL) : *eh;
}
} if (ehs) {
reason = apr_pstrcat(r->pool, "contradiction: 304 Not Modified; " "but ", ehs, " modified", NULL);
}
}
/* Hold the phone. Some servers might allow us to cache a 2xx, but *thenmaketheir304responsesnoncacheable.RFC2616saysthis: * *Ifa304responseindicatesanentitynotcurrentlycached,then *thecacheMUSTdisregardtheresponseandrepeattherequest *withouttheconditional. * *A304responsewithcontradictoryheadersistechnicallya *differententity,tobesafe,weremovetheentityfromthecache.
*/ if (reason && r->status == HTTP_NOT_MODIFIED && cache->stale_handle) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02473) "cache: %s responded with an uncacheable 304, " "retrying the request %s. Reason: %s",
cache->key, r->unparsed_uri, reason);
/* we've got a cache conditional miss! tell anyone who cares */
cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
apr_psprintf(r->pool, "conditional cache miss: 304 was uncacheable, entity removed: %s",
reason));
/* remove the cached entity immediately, we might cache it again */
ap_remove_output_filter(cache->remove_url_filter);
cache_remove_url(cache, r);
/* let someone else attempt to cache */
cache_remove_lock(conf, cache, r, NULL);
/* remove this filter from the chain */
ap_remove_output_filter(f);
/* retry without the conditionals */
apr_table_unset(r->headers_in, "If-Match");
apr_table_unset(r->headers_in, "If-Modified-Since");
apr_table_unset(r->headers_in, "If-None-Match");
apr_table_unset(r->headers_in, "If-Range");
apr_table_unset(r->headers_in, "If-Unmodified-Since");
/* Currently HTTP_NOT_MODIFIED, and after the redirect, handlers won't think to set status to HTTP_OK */
r->status = HTTP_OK;
ap_internal_redirect(r->unparsed_uri, r);
return APR_SUCCESS;
}
/* Set the content length if known.
*/
cl = apr_table_get(r->err_headers_out, "Content-Length"); if (cl == NULL) {
cl = apr_table_get(r->headers_out, "Content-Length");
} if (cl && !ap_parse_strict_length(&size, cl)) {
reason = "invalid content length";
}
if (reason) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00768) "cache: %s not cached for request %s. Reason: %s",
cache->key, r->unparsed_uri, reason);
/* we've got a cache miss! tell anyone who cares */
cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
reason);
/* remove this filter from the chain */
ap_remove_output_filter(f);
/* ship the data up the stack */ return ap_pass_brigade(f->next, in);
}
/* Make it so that we don't execute this path again. */
cache->in_checked = 1;
if (!cl) { /* if we don't get the content-length, see if we have all the *bucketsandusetheirlengthtocalculatethesize
*/ int all_buckets_here=0;
size=0; for (e = APR_BRIGADE_FIRST(in);
e != APR_BRIGADE_SENTINEL(in);
e = APR_BUCKET_NEXT(e))
{ if (APR_BUCKET_IS_EOS(e)) {
all_buckets_here=1; break;
} if (APR_BUCKET_IS_FLUSH(e)) { continue;
} if (e->length == (apr_size_t)-1) { break;
}
size += e->length;
} if (!all_buckets_here) {
size = -1;
}
}
/* remember content length to check response size against later */
cache->size = size;
/* It's safe to cache the response. * *Therearetwopossibilitiesatthispoint: *-cache->handle==NULL.Inthiscasethereisnopreviously *cachedentityanywhereonthesystem.Wemustcreateabrand *newentityandstoretheresponseinit. *-cache->stale_handle!=NULL.Inthiscasethereisastale *entityinthesystemwhichneedstobereplacedbynew *content(unlesstheresultwas304NotModified,whichmeans *thecachedentityisactuallyfresh,andweshouldupdate *theheaders).
*/
/* Did we have a stale cache entry that really is stale?
*/ if (cache->stale_handle) { if (r->status == HTTP_NOT_MODIFIED) { /* Oh, hey. It isn't that stale! Yay! */
cache->handle = cache->stale_handle;
info = &cache->handle->cache_obj->info;
rv = OK;
} else { /* Oh, well. Toss it. */
cache->provider->remove_entity(cache->stale_handle); /* Treat the request as if it wasn't conditional. */
cache->stale_handle = NULL; /* *Restoretheoriginalrequestheadersastheymaybeneeded *byfurtheroutputfilterslikethebyterangefiltertomake *thecorrectdecisions.
*/
r->headers_in = cache->stale_headers;
}
}
/* no cache handle, create a new entity */ if (!cache->handle) {
rv = cache_create_entity(cache, r, size, in);
info = apr_pcalloc(r->pool, sizeof(cache_info)); /* We only set info->status upon the initial creation. */
info->status = r->status;
}
if (rv != OK) { /* we've got a cache miss! tell anyone who cares */
cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS, "cache miss: cache unwilling to store response");
/* Caching layer declined the opportunity to cache the response */
ap_remove_output_filter(f);
cache_remove_lock(conf, cache, r, NULL); return ap_pass_brigade(f->next, in);
}
/* We are actually caching this response. So it does not *makesensetoremovethisentityanymore.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00770) "cache: Removing CACHE_REMOVE_URL filter.");
ap_remove_output_filter(cache->remove_url_filter);
/* store away the previously parsed cache control headers */
memcpy(&info->control, &control, sizeof(cache_control_t));
/* Read the date. Generate one if one is not supplied */
dates = apr_table_get(r->err_headers_out, "Date"); if (dates == NULL) {
dates = apr_table_get(r->headers_out, "Date");
} if (dates != NULL) {
info->date = apr_date_parse_http(dates);
} else {
info->date = APR_DATE_BAD;
}
now = apr_time_now(); if (info->date == APR_DATE_BAD) { /* No, or bad date */ /* no date header (or bad header)! */
info->date = now;
}
date = info->date;
/* set response_time for HTTP/1.1 age calculations */
info->response_time = now;
/* get the request time */
info->request_time = r->request_time;
/* check last-modified date */ if (lastmod != APR_DATE_BAD && lastmod > date) { /* if it's in the future, then replace by date */
lastmod = date;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0,
r, APLOGNO(00771) "cache: Last modified is in the future, " "replacing with now");
}
/* CC has priority over Expires. */ if (control.s_maxage || control.max_age) {
apr_int64_t x;
x = control.s_maxage ? control.s_maxage_value : control.max_age_value;
x = x * MSEC_ONE_SEC;
if (x < dconf->minex) {
x = dconf->minex;
} if (x > dconf->maxex) {
x = dconf->maxex;
}
exp = date + x;
}
/* if no expiry date then *ifCache-Control:s-maxage *expirydate=date+smaxage *ifCache-Control:max-age *expirydate=date+max-age *elseiflastmod *expirydate=date+min((date-lastmod)*factor,maxexpire) *else *expiredate=date+defaultexpire
*/
if (exp == APR_DATE_BAD) { if ((lastmod != APR_DATE_BAD) && (lastmod < date)) { /* if lastmod == date then you get 0*conf->factor which results in *anexpirationtimeofnow.Thiscausessomeproblemswith *freshnesscalculations,sowechoosetheelsepath...
*/
apr_time_t x = (apr_time_t) ((date - lastmod) * dconf->factor);
if (x < dconf->minex) {
x = dconf->minex;
} if (x > dconf->maxex) {
x = dconf->maxex;
}
exp = date + x;
} else {
exp = date + dconf->defex;
}
}
info->expire = exp;
/* We found a stale entry which wasn't really stale. */ if (cache->stale_handle) {
/* RFC 2616 10.3.5 states that entity headers are not supposed *tobeinthe304response.Therefore,weneedtocombinethe *responseheaderswiththecachedheaders*before*weupdate *thecachedheaders. * *However,beforedoingthat,weneedtofirstmergein *err_headers_out(notethatstore_headers()belowalreadyselects *thecacheableonlyheadersusingap_cache_cacheable_headers_out(), *herewewanttokeeptheoriginalheadersinr->headers_outand *forwardallofthemtotheclient,includingnon-cacheableones).
*/
r->headers_out = cache_merge_headers_out(r);
apr_table_clear(r->err_headers_out);
/* Merge in our cached headers. However, keep any updated values. */ /* take output, overlay on top of cached */
cache_accept_headers(cache->handle, r, r->headers_out,
cache->handle->resp_hdrs, 1);
}
/* Write away header information to cache. It is possible that we are *tryingtoupdateheadersforanentitywhichhasalreadybeencached. * *Thismayfail,duetoanunwritablecachearea.E.g.filesystemfull, *permissionsproblemsoraread-only(re)mount.Thismustbehandled *later.
*/
rv = cache->provider->store_headers(cache->handle, r, info);
/* Did we just update the cached headers on a revalidated response? * *Ifso,wecannowdecidewhattoservetotheclient.Thisisdonein *thesamewayaswitharegularresponse,butconditionsarenowchecked *againstthecachedormergedresponseheaders.
*/ if (cache->stale_handle) {
apr_bucket_brigade *bb;
apr_bucket *bkt; int status;
/* Load in the saved status and clear the status line. */
r->status = info->status;
r->status_line = NULL;
/* We're just saving response headers, so we are done. Commit *theresponseatthispoint,unlesstherewasapreviouserror.
*/ if (rv == APR_SUCCESS) {
rv = cache->provider->commit_entity(cache->handle, r);
}
/* Restore the original request headers and see if we need to *returnanythingelsethanthecachedresponse(ie.theoriginal *requestwasconditional).
*/
r->headers_in = cache->stale_headers;
status = ap_meets_conditions(r); if (status != OK) {
r->status = status;
/* Strip the entity headers merged from the cached headers before *updatingtheentry(seecache_accept_headers()above).
*/ for (eh = MOD_CACHE_ENTITY_HEADERS; *eh; ++eh) {
apr_table_unset(r->headers_out, *eh);
}
/* Before returning we need to handle the possible case of an *unwritablecache.Ratherthanleavingtheentityinthecache *andhavingitconstantlyre-validated,nowthatwehaverecalled *thebodyitissafetotryandremovetheurlfromthecache.
*/ if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00772) "cache: updating headers with store_headers failed. " "Removing cached url.");
rv = cache->provider->remove_url(cache->stale_handle, r); if (rv != OK) { /* Probably a mod_cache_disk cache area has been (re)mounted *read-only,orthatthereisapermissionsproblem.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00773) "cache: attempt to remove url from cache unsuccessful.");
}
if (!cache) { /* user likely configured CACHE_INVALIDATE manually; they should really *usemod_cacheconfigurationtodothat.So: *1.Removeourselves *2.Donothingandbailout
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02465) "cache: CACHE_INVALIDATE enabled unexpectedly: %s", r->uri);
} else {
if (r->status > 299) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02466) "cache: response status to '%s' method is %d (>299), not invalidating cached entity: %s", r->method, r->status, r->uri);
} else {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02467) "cache: Invalidating all cached entities in response to '%s' request for %s",
r->method, r->uri);
cache_invalidate(cache, r);
/* we've got a cache invalidate! tell everyone who cares */
cache_run_cache_status(cache->handle, r, r->headers_out,
AP_CACHE_INVALIDATE, apr_psprintf(r->pool, "cache invalidated by %s", r->method));
/* was the quick handler enabled */ if (conf->quick) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(00776) "cache: CACHE filter was added in quick handler mode and " "will be ignored: %s", f->r->unparsed_uri);
} /* otherwise we may have been bypassed, nothing to see here */ else {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(00777) "cache: CACHE filter was added twice, or was added where " "the cache has been bypassed and will be ignored: %s",
f->r->unparsed_uri);
}
/* we are just a marker, so let's just remove ourselves */
ap_remove_output_filter(f); return ap_pass_brigade(f->next, in);
}
/* morph the current save filter into the out filter, and serve from *cache.
*/
cache->handle = cache->stale_handle; if (r->main) {
cache->save_filter->frec = cache_out_subreq_filter_handle;
} else {
cache->save_filter->frec = cache_out_filter_handle;
}
/* maximum time to cache a document */
dconf->maxex = DEFAULT_CACHE_MAXEXPIRE;
dconf->minex = DEFAULT_CACHE_MINEXPIRE; /* default time to cache a document */
dconf->defex = DEFAULT_CACHE_EXPIRE;
/* factor used to estimate Expires date from LastModified date */
dconf->factor = DEFAULT_CACHE_LMFACTOR;
/* maximum time to cache a document */ new->maxex = (add->maxex_set == 0) ? base->maxex : add->maxex; new->maxex_set = add->maxex_set || base->maxex_set; new->minex = (add->minex_set == 0) ? base->minex : add->minex; new->minex_set = add->minex_set || base->minex_set;
/* default time to cache a document */ new->defex = (add->defex_set == 0) ? base->defex : add->defex; new->defex_set = add->defex_set || base->defex_set;
/* factor used to estimate Expires date from LastModified date */ new->factor = (add->factor_set == 0) ? base->factor : add->factor; new->factor_set = add->factor_set || base->factor_set;
/* array of URL prefixes for which caching is enabled */
ps->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable)); /* array of URL prefixes for which caching is disabled */
ps->cachedisable = apr_array_make(p, 10, sizeof(struct cache_disable));
ps->ignorecachecontrol = 0;
ps->ignorecachecontrol_set = 0; /* array of headers that should not be stored in cache */
ps->ignore_headers = apr_array_make(p, 10, sizeof(char *));
ps->ignore_headers_set = CACHE_IGNORE_HEADERS_UNSET; /* flag indicating that query-string should be ignored when caching */
ps->ignorequerystring = 0;
ps->ignorequerystring_set = 0; /* by default, run in the quick handler */
ps->quick = 1;
ps->quick_set = 0; /* array of identifiers that should not be used for key calculation */
ps->ignore_session_id = apr_array_make(p, 10, sizeof(char *));
ps->ignore_session_id_set = CACHE_IGNORE_SESSION_ID_UNSET;
ps->lock = 0; /* thundering herd lock defaults to off */
ps->lock_set = 0;
apr_temp_dir_get(&tmppath, p); if (tmppath) {
ps->lockpath = apr_pstrcat(p, tmppath, DEFAULT_CACHE_LOCKPATH, NULL);
}
ps->lockmaxage = apr_time_from_sec(DEFAULT_CACHE_MAXAGE);
ps->x_cache = DEFAULT_X_CACHE;
ps->x_cache_detail = DEFAULT_X_CACHE_DETAIL; return ps;
}
/* array of URL prefixes for which caching is disabled */
ps->cachedisable = apr_array_append(p,
base->cachedisable,
overrides->cachedisable); /* array of URL prefixes for which caching is enabled */
ps->cacheenable = apr_array_append(p,
base->cacheenable,
overrides->cacheenable);
conf =
(cache_server_conf *)ap_get_module_config(parms->server->module_config,
&cache_module); if (!strcasecmp(header, "None")) { /* if header None is listed clear array */
conf->ignore_headers->nelts = 0;
} else { if ((conf->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET) ||
(conf->ignore_headers->nelts)) { /* Only add header if no "None" has been found in header list *sofar. *(When'None'ispassed,IGNORE_HEADERS_SET&&nelts==0.)
*/ new = (char **)apr_array_push(conf->ignore_headers);
(*new) = (char *)header;
}
}
conf->ignore_headers_set = CACHE_IGNORE_HEADERS_SET; return NULL;
}
if (*type == '/') { return apr_psprintf(parms->pool, "provider (%s) starts with a '/'. Are url and provider switched?",
type);
}
if (!url) {
url = parms->path;
} if (!url) { return apr_psprintf(parms->pool, "CacheEnable provider (%s) is missing an URL.", type);
} if (parms->path && strncmp(parms->path, url, strlen(parms->path))) { return"When in a Location, CacheEnable must specify a path or an URL below " "that location.";
}
if (parms->path) { if (!strcasecmp(url, "on")) {
dconf->disable = 1;
dconf->disable_set = 1; return NULL;
} else { return"CacheDisable must be followed by the word 'on' when in a Location.";
}
}
if (!url || (url[0] != '/' && !ap_strchr_c(url, ':'))) { return"CacheDisable must specify a path or an URL.";
}
new = apr_array_push(conf->cachedisable); if (apr_uri_parse(parms->pool, url, &(new->url))) { return NULL;
} if (new->url.path) { new->pathlen = strlen(new->url.path);
} else { new->pathlen = 1; new->url.path = "/";
} return NULL;
}
if (sscanf(arg, "%lg", &val) != 1) { return"CacheLastModifiedFactor value must be a float";
}
dconf->factor = val;
dconf->factor_set = 1; return NULL;
}
staticconstchar *set_cache_ignore_querystring(cmd_parms *parms, void *dummy, int flag)
{
cache_server_conf *conf;
conf =
(cache_server_conf *)ap_get_module_config(parms->server->module_config,
&cache_module);
conf->base_uri = apr_pcalloc(parms->pool, sizeof(apr_uri_t));
rv = apr_uri_parse(parms->pool, arg, conf->base_uri); if (rv != APR_SUCCESS) { return apr_psprintf(parms->pool, "Could not parse '%s' as an URL.", arg);
} elseif (!conf->base_uri->scheme && !conf->base_uri->hostname &&
!conf->base_uri->port_str) { return apr_psprintf(parms->pool, "URL '%s' must contain at least one of a scheme, a hostname or a port.", arg);
}
conf->base_uri_set = 1; return NULL;
}
staticint cache_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{ /* This is the means by which unusual (non-unix) os's may find alternate *meanstorunagivencommand(e.g.shebang/registryparsingonWin32)
*/
cache_generate_key = APR_RETRIEVE_OPTIONAL_FN(ap_cache_generate_key); if (!cache_generate_key) {
cache_generate_key = cache_generate_key_default;
} return OK;
}
AP_INIT_TAKE12("CacheEnable", add_cache_enable, NULL, RSRC_CONF|ACCESS_CONF, "A cache type and partial URL prefix below which " "caching is enabled"),
AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF|ACCESS_CONF, "A partial URL prefix below which caching is disabled"),
AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF|ACCESS_CONF, "The maximum time in seconds to cache a document"),
AP_INIT_TAKE1("CacheMinExpire", set_cache_minex, NULL, RSRC_CONF|ACCESS_CONF, "The minimum time in seconds to cache a document"),
AP_INIT_TAKE1("CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF|ACCESS_CONF, "The default time in seconds to cache a document"),
AP_INIT_FLAG("CacheQuickHandler", set_cache_quick_handler, NULL,
RSRC_CONF, "Run the cache in the quick handler, default on"),
AP_INIT_FLAG("CacheIgnoreNoLastMod", set_cache_ignore_no_last_mod, NULL,
RSRC_CONF|ACCESS_CONF, "Ignore Responses where there is no Last Modified Header"),
AP_INIT_FLAG("CacheIgnoreCacheControl", set_cache_ignore_cachecontrol,
NULL, RSRC_CONF, "Ignore requests from the client for uncached content"),
AP_INIT_FLAG("CacheStoreExpired", set_cache_store_expired,
NULL, RSRC_CONF|ACCESS_CONF, "Ignore expiration dates when populating cache, resulting in " "an If-Modified-Since request to the backend on retrieval"),
AP_INIT_FLAG("CacheStorePrivate", set_cache_store_private,
NULL, RSRC_CONF|ACCESS_CONF, "Ignore 'Cache-Control: private' and store private content"),
AP_INIT_FLAG("CacheStoreNoStore", set_cache_store_nostore,
NULL, RSRC_CONF|ACCESS_CONF, "Ignore 'Cache-Control: no-store' and store sensitive content"),
AP_INIT_ITERATE("CacheIgnoreHeaders", add_ignore_header, NULL, RSRC_CONF, "A space separated list of headers that should not be " "stored by the cache"),
AP_INIT_FLAG("CacheIgnoreQueryString", set_cache_ignore_querystring,
NULL, RSRC_CONF, "Ignore query-string when caching"),
AP_INIT_ITERATE("CacheIgnoreURLSessionIdentifiers", add_ignore_session_id,
NULL, RSRC_CONF, "A space separated list of session " "identifiers that should be ignored for creating the key " "of the cached entity."),
AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF|ACCESS_CONF, "The factor used to estimate Expires date from " "LastModified date"),
AP_INIT_FLAG("CacheLock", set_cache_lock,
NULL, RSRC_CONF, "Enable or disable the thundering herd lock."),
AP_INIT_TAKE1("CacheLockPath", set_cache_lock_path, NULL, RSRC_CONF, "The thundering herd lock path. Defaults to the '"
DEFAULT_CACHE_LOCKPATH "' directory in the system " "temp directory."),
AP_INIT_TAKE1("CacheLockMaxAge", set_cache_lock_maxage, NULL, RSRC_CONF, "Maximum age of any thundering herd lock."),
AP_INIT_FLAG("CacheHeader", set_cache_x_cache, NULL, RSRC_CONF | ACCESS_CONF, "Add a X-Cache header to responses. Default is off."),
AP_INIT_FLAG("CacheDetailHeader", set_cache_x_cache_detail, NULL,
RSRC_CONF | ACCESS_CONF, "Add a X-Cache-Detail header to responses. Default is off."),
AP_INIT_TAKE1("CacheKeyBaseURL", set_cache_key_base_url, NULL, RSRC_CONF, "Override the base URL of reverse proxied cache keys."),
AP_INIT_FLAG("CacheStaleOnError", set_cache_stale_on_error,
NULL, RSRC_CONF|ACCESS_CONF, "Serve stale content on 5xx errors if present. Defaults to on."),
{NULL}
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.