From: Christos Tsantilas Date: Fri, 26 Jul 2013 12:21:20 +0000 (-0600) Subject: Bug 2112: Reload into If-None-Match X-Git-Tag: SQUID_3_3_10~52 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b9b6c2d2639c6875e938502fc588fe9bc508963c;p=thirdparty%2Fsquid.git 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 --- diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc index 8f067e9457..841dde68ce 100644 --- a/src/HttpRequest.cc +++ b/src/HttpRequest.cc @@ -172,6 +172,8 @@ HttpRequest::clean() extacl_message.clean(); + etag.clean(); + #if USE_ADAPTATION adaptHistory_ = NULL; #endif @@ -217,6 +219,7 @@ HttpRequest::clone() const // 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? diff --git a/src/HttpRequest.h b/src/HttpRequest.h index 2f8ef03ba3..8b891100ff 100644 --- a/src/HttpRequest.h +++ b/src/HttpRequest.h @@ -211,6 +211,9 @@ public: 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; diff --git a/src/Store.h b/src/Store.h index 32b216aa55..16b2c3e3c9 100644 --- a/src/Store.h +++ b/src/Store.h @@ -140,6 +140,8 @@ public: 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; diff --git a/src/cf.data.pre b/src/cf.data.pre index 53087043f7..dfce3568da 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -4466,10 +4466,12 @@ DOC_START 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 diff --git a/src/client_side_reply.cc b/src/client_side_reply.cc index 833c8f8686..51628cc8d5 100644 --- a/src/client_side_reply.cc +++ b/src/client_side_reply.cc @@ -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 @@ clientReplyContext::processExpired() #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); diff --git a/src/http.cc b/src/http.cc index d4b2f2a1c4..fde0656586 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1715,10 +1715,17 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request, 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)); diff --git a/src/store.cc b/src/store.cc index ab1199beb2..5161fa53b9 100644 --- a/src/store.cc +++ b/src/store.cc @@ -1951,6 +1951,17 @@ StoreEntry::modifiedSince(HttpRequest * request) const } } +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 {