From: Alex Rousskov Date: Wed, 18 Dec 2013 17:19:00 +0000 (-0700) Subject: Added send_hit and store_miss squid.conf directives X-Git-Tag: SQUID_3_5_0_1~455 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=707061493470b4d368b02cdab5e143845d53efd9;p=thirdparty%2Fsquid.git Added send_hit and store_miss squid.conf directives to control caching of responses using response info. The existing "cache" directive is checked before Squid has access to the response and, hence, could not use response-based ACLs such as http_status. Response-based ACLs may be essential when fine-tuning caching. Squid Bug 3937 (StoreID can lead to 302 infinite loop) is a good use case. Updated old "cache" directive documentation to provide more information, to help folks distinguish the three related directives, and to polish for clarity. TODO: Support lookup_hit and possibly deprecate/remove "cache". --- diff --git a/src/Server.cc b/src/Server.cc index 9c7fe1e6a0..3a9b2b4c5e 100644 --- a/src/Server.cc +++ b/src/Server.cc @@ -31,6 +31,7 @@ */ #include "squid.h" +#include "acl/FilledChecklist.h" #include "acl/Gadgets.h" #include "base/TextException.h" #include "comm/Connection.h" @@ -174,6 +175,8 @@ ServerStateData::setFinalReply(HttpReply *rep) // give entry the reply because haveParsedReplyHeaders() expects it there entry->replaceHttpReply(theFinalReply, false); // but do not write yet haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps) + if (EBIT_TEST(entry->flags, ENTRY_CACHABLE) && blockCaching()) + entry->release(); entry->startWriting(); // write the updated entry to store return theFinalReply; @@ -533,6 +536,24 @@ ServerStateData::haveParsedReplyHeaders() currentOffset = partial ? theFinalReply->content_range->spec.offset : 0; } +/// whether to prevent caching of an otherwise cachable response +bool +ServerStateData::blockCaching() +{ + if (const Acl::Tree *acl = Config.accessList.storeMiss) { + // This relatively expensive check is not in StoreEntry::checkCachable: + // That method lacks HttpRequest and may be called too many times. + ACLFilledChecklist ch(acl, originalRequest(), NULL); + ch.reply = const_cast(entry->getReply()); // ACLFilledChecklist API bug + HTTPMSGLOCK(ch.reply); + if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block + debugs(20, 3, "store_miss prohibits caching"); + return true; + } + } + return false; +} + HttpRequest * ServerStateData::originalRequest() { diff --git a/src/Server.h b/src/Server.h index 2633e8f8f3..25f9e69d39 100644 --- a/src/Server.h +++ b/src/Server.h @@ -131,6 +131,8 @@ protected: /// Entry-dependent callbacks use this check to quit if the entry went bad bool abortOnBadEntry(const char *abortReason); + bool blockCaching(); + #if USE_ADAPTATION void startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause); void adaptVirginReplyBody(const char *buf, ssize_t len); diff --git a/src/SquidConfig.h b/src/SquidConfig.h index 35855a908f..8efc16c6c2 100644 --- a/src/SquidConfig.h +++ b/src/SquidConfig.h @@ -364,6 +364,8 @@ public: acl_access *AlwaysDirect; acl_access *ASlists; acl_access *noCache; + acl_access *sendHit; + acl_access *storeMiss; acl_access *stats_collection; #if SQUID_SNMP diff --git a/src/cf.data.pre b/src/cf.data.pre index fa84a9cba1..8293d8c199 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -4851,18 +4851,97 @@ COMMENT_END NAME: cache no_cache TYPE: acl_access DEFAULT: none -DEFAULT_DOC: Allow caching, unless rules exist in squid.conf. +DEFAULT_DOC: By default, this directive is unused and has no effect. LOC: Config.accessList.noCache DOC_START - A list of ACL elements which, if matched and denied, cause the request to - not be satisfied from the cache and the reply to not be cached. - In other words, use this to force certain objects to never be cached. - - You must use the words 'allow' or 'deny' to indicate whether items - matching the ACL should be allowed or denied into the cache. + Requests denied by this directive will not be served from the cache + and their responses will not be stored in the cache. This directive + has no effect on other transactions and on already cached responses. This clause supports both fast and slow acl types. See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details. + + This and the two other similar caching directives listed below are + checked at different transaction processing stages, have different + access to response information, affect different cache operations, + and differ in slow ACLs support: + + * cache: Checked before Squid makes a hit/miss determination. + No access to reply information! + Denies both serving a hit and storing a miss. + Supports both fast and slow ACLs. + * send_hit: Checked after a hit was detected. + Has access to reply (hit) information. + Denies serving a hit only. + Supports fast ACLs only. + * store_miss: Checked before storing a cachable miss. + Has access to reply (miss) information. + Denies storing a miss only. + Supports fast ACLs only. + + If you are not sure which of the three directives to use, apply the + following decision logic: + + * If your ACL(s) are of slow type _and_ need response info, redesign. + Squid does not support that particular combination at this time. + Otherwise: + * If your directive ACL(s) are of slow type, use "cache"; and/or + * if your directive ACL(s) need no response info, use "cache". + Otherwise: + * If you do not want the response cached, use store_miss; and/or + * if you do not want a hit on a cached response, use send_hit. +DOC_END + +NAME: send_hit +TYPE: acl_access +DEFAULT: none +DEFAULT_DOC: By default, this directive is unused and has no effect. +LOC: Config.accessList.sendHit +DOC_START + Responses denied by this directive will not be served from the cache + (but may still be cached, see store_miss). This directive has no + effect on the responses it allows and on the cached objects. + + Please see the "cache" directive for a summary of differences among + store_miss, send_hit, and cache directives. + + Unlike the "cache" directive, send_hit only supports fast acl + types. See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details. + + For example: + + # apply custom Store ID mapping to some URLs + acl MapMe dstdomain .c.example.com + store_id_program ... + store_id_access allow MapMe + + # but prevent caching of special responses + # such as 302 redirects that cause StoreID loops + acl Ordinary http_status 200-299 + store_miss deny MapMe !Ordinary + + # and do not serve any previously stored special responses + # from the cache (in case they were already cached before + # the above store_miss rule was in effect). + send_hit deny MapMe !Ordinary +DOC_END + +NAME: store_miss +TYPE: acl_access +DEFAULT: none +DEFAULT_DOC: By default, this directive is unused and has no effect. +LOC: Config.accessList.storeMiss +DOC_START + Responses denied by this directive will not be cached (but may still + be served from the cache, see send_hit). This directive has no + effect on the responses it allows and on the already cached responses. + + Please see the "cache" directive for a summary of differences among + store_miss, send_hit, and cache directives. See the + send_hit directive for a usage example. + + Unlike the "cache" directive, store_miss only supports fast acl + types. See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details. DOC_END NAME: max_stale diff --git a/src/client_side_reply.cc b/src/client_side_reply.cc index d8617d3b1b..2d55c05479 100644 --- a/src/client_side_reply.cc +++ b/src/client_side_reply.cc @@ -536,6 +536,11 @@ clientReplyContext::cacheHit(StoreIOBuffer result) ) { http->logType = LOG_TCP_NEGATIVE_HIT; sendMoreData(result); + } else if (blockedHit()) { + debugs(88, 5, "send_hit forces a MISS"); + http->logType = LOG_TCP_MISS; + processMiss(); + return; } else if (!http->flags.internal && refreshCheckHTTP(e, r)) { debugs(88, 5, "clientCacheHit: in refreshCheck() block"); /* @@ -764,6 +769,30 @@ clientReplyContext::processConditional(StoreIOBuffer &result) } } +/// whether squid.conf send_hit prevents us from serving this hit +bool +clientReplyContext::blockedHit() const +{ + if (!Config.accessList.sendHit) + return false; // hits are not blocked by default + + if (http->flags.internal) + return false; // internal content "hits" cannot be blocked + + if (const HttpReply *rep = http->storeEntry()->getReply()) { + std::auto_ptr chl(clientAclChecklistCreate(Config.accessList.sendHit, http)); + chl->reply = const_cast(rep); // ACLChecklist API bug + HTTPMSGLOCK(chl->reply); + return chl->fastCheck() != ACCESS_ALLOWED; // when in doubt, block + } + + // This does not happen, I hope, because we are called from CacheHit, which + // is called via a storeClientCopy() callback, and store should initialize + // the reply before calling that callback. + debugs(88, 3, "Missing reply!"); + return false; +} + void clientReplyContext::purgeRequestFindObjectToPurge() { diff --git a/src/client_side_reply.h b/src/client_side_reply.h index 15a34fe0de..7cebe1cc7b 100644 --- a/src/client_side_reply.h +++ b/src/client_side_reply.h @@ -140,6 +140,7 @@ private: void triggerInitialStoreRead(); void sendClientOldEntry(); void purgeAllCached(); + bool blockedHit() const; void sendBodyTooLargeError(); void sendPreconditionFailedError();