------------------------------------------------------------ revno: 12597 revision-id: squid3@treenet.co.nz-20130726122120-tq8lgc5au5q5tq41 parent: squid3@treenet.co.nz-20130726114748-i7l2deukqpkeyp9d fixes bug(s): http://bugs.squid-cache.org/show_bug.cgi?id=2112 author: Christos Tsantilas committer: Amos Jeffries branch nick: 3.3 timestamp: Fri 2013-07-26 06:21:20 -0600 message: Bug 2112: Reload into If-None-Match This patch sends an If-None-Match request, when we need to re-validate if a cached object which has a strong ETag is still valid. This is also done in the cases an HTTP client request contains HTTP headers prohibiting a from-cache response (i.e., a "reload" request). The use of If-None-Match request in this context violates RFC 2616 and requires using reload-into-ims option within refresh_pattern squid.conf directive. The exact definition of a "reload request" and the adjustment/removal of "reload" headers is the same as currently used for reload-into-ims option support. This patch is not modifying that code/logic, just adding an If-None-Match header in addition to the IMS header that Squid already adds. This is a Measurement Factory Project ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: squid3@treenet.co.nz-20130726122120-tq8lgc5au5q5tq41 # target_branch: http://bzr.squid-cache.org/bzr/squid3/3.3 # testament_sha1: f52d451d51b7cd045fd6a8d37983d9d2024d5d7c # timestamp: 2013-07-26 12:35:39 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/3.3 # base_revision_id: squid3@treenet.co.nz-20130726114748-\ # i7l2deukqpkeyp9d # # Begin patch === modified file 'src/HttpRequest.cc' --- src/HttpRequest.cc 2013-07-26 11:47:48 +0000 +++ src/HttpRequest.cc 2013-07-26 12:21:20 +0000 @@ -172,6 +172,8 @@ extacl_message.clean(); + etag.clean(); + #if USE_ADAPTATION adaptHistory_ = NULL; #endif @@ -217,6 +219,7 @@ // XXX: what to do with copy->peer_login? copy->lastmod = lastmod; + copy->etag = etag; copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL; // XXX: what to do with copy->peer_domain? === modified file 'src/HttpRequest.h' --- src/HttpRequest.h 2012-10-04 09:14:06 +0000 +++ src/HttpRequest.h 2013-07-26 12:21:20 +0000 @@ -211,6 +211,9 @@ String x_forwarded_for_iterator; /* XXX a list of IP addresses */ #endif /* FOLLOW_X_FORWARDED_FOR */ + /// A strong etag of the cached entry. Used for refreshing that entry. + String etag; + public: bool multipartRangeRequest() const; === modified file 'src/Store.h' --- src/Store.h 2012-10-16 23:43:54 +0000 +++ src/Store.h 2013-07-26 12:21:20 +0000 @@ -140,6 +140,8 @@ bool hasIfMatchEtag(const HttpRequest &request) const; /// has ETag matching at least one of the If-None-Match etags bool hasIfNoneMatchEtag(const HttpRequest &request) const; + /// whether this entry has an ETag; if yes, puts ETag value into parameter + bool hasEtag(ETag &etag) const; /** What store does this entry belong too ? */ virtual RefCount store() const; === modified file 'src/cf.data.pre' --- src/cf.data.pre 2013-06-11 13:47:06 +0000 +++ src/cf.data.pre 2013-07-26 12:21:20 +0000 @@ -4466,10 +4466,12 @@ override-lastmod enforces min age even on objects that were modified recently. - reload-into-ims changes client no-cache or ``reload'' - to If-Modified-Since requests. Doing this VIOLATES the - HTTP standard. Enabling this feature could make you - liable for problems which it causes. + reload-into-ims changes a client no-cache or ``reload'' + request for a cached entry into a conditional request using + If-Modified-Since and/or If-None-Match headers, provided the + cached entry has a Last-Modified and/or a strong ETag header. + Doing this VIOLATES the HTTP standard. Enabling this feature + could make you liable for problems which it causes. ignore-reload ignores a client no-cache or ``reload'' header. Doing this VIOLATES the HTTP standard. Enabling === modified file 'src/client_side_reply.cc' --- src/client_side_reply.cc 2013-01-28 04:11:25 +0000 +++ src/client_side_reply.cc 2013-07-26 12:21:20 +0000 @@ -38,6 +38,7 @@ #include "clientStream.h" #include "dlink.h" #include "errorpage.h" +#include "ETag.h" #include "fd.h" #include "fde.h" #include "format/Token.h" @@ -291,6 +292,13 @@ #endif http->request->lastmod = old_entry->lastmod; + + if (!http->request->header.has(HDR_IF_NONE_MATCH)) { + ETag etag = {NULL, -1}; // TODO: make that a default ETag constructor + if (old_entry->hasEtag(etag) && !etag.weak) + http->request->etag = etag.str; + } + debugs(88, 5, "clientReplyContext::processExpired : lastmod " << entry->lastmod ); http->storeEntry(entry); assert(http->out.offset == 0); === modified file 'src/http.cc' --- src/http.cc 2013-03-29 05:55:45 +0000 +++ src/http.cc 2013-07-26 12:21:20 +0000 @@ -1715,10 +1715,17 @@ HttpHeaderPos pos = HttpHeaderInitPos; assert (hdr_out->owner == hoRequest); - /* append our IMS header */ + /* use our IMS header if the cached entry has Last-Modified time */ if (request->lastmod > -1) hdr_out->putTime(HDR_IF_MODIFIED_SINCE, request->lastmod); + // Add our own If-None-Match field if the cached entry has a strong ETag. + // copyOneHeaderFromClientsideRequestToUpstreamRequest() adds client ones. + if (request->etag.defined()) { + hdr_out->addEntry(new HttpHeaderEntry(HDR_IF_NONE_MATCH, NULL, + request->etag.termedBuf())); + } + bool we_do_ranges = decideIfWeDoRanges (request); String strConnection (hdr_in->getList(HDR_CONNECTION)); === modified file 'src/store.cc' --- src/store.cc 2013-02-14 07:34:42 +0000 +++ src/store.cc 2013-07-26 12:21:20 +0000 @@ -1952,6 +1952,17 @@ } bool +StoreEntry::hasEtag(ETag &etag) const +{ + if (const HttpReply *reply = getReply()) { + etag = reply->header.getETag(HDR_ETAG); + if (etag.str) + return true; + } + return false; +} + +bool StoreEntry::hasIfMatchEtag(const HttpRequest &request) const { const String reqETags = request.header.getList(HDR_IF_MATCH);