]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Bug 2112: Reload into If-None-Match
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Fri, 26 Jul 2013 12:21:20 +0000 (06:21 -0600)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 26 Jul 2013 12:21:20 +0000 (06:21 -0600)
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

src/HttpRequest.cc
src/HttpRequest.h
src/Store.h
src/cf.data.pre
src/client_side_reply.cc
src/http.cc
src/store.cc

index 8f067e9457f5b066796f83f4a454155910208eb2..841dde68ce02b8f4a3b26a52ed776f2fb474a976 100644 (file)
@@ -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?
 
index 2f8ef03ba35bc136d48c0a64f497b5b4ae100f22..8b891100ff0466c0859cdc4d588e2b06910049a7 100644 (file)
@@ -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;
 
index 32b216aa557af52e64689d299b288dfc3bab3096..16b2c3e3c96c8d33401135d0bb1a11ca7efcba65 100644 (file)
@@ -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<SwapDir> store() const;
index 53087043f7555a5b7a2c7d4eed6ae06c4c6d12a1..dfce3568da4fa193a6a002d83efa2cedf9ce22ce 100644 (file)
@@ -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
index 833c8f8686305d0030c1f595f707b1b0b18538e1..51628cc8d5d7aee4bad33c3c6d036488d8bd7f80 100644 (file)
@@ -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);
index d4b2f2a1c4c47e6866a403aeb654bf146b34eff1..fde0656586b7415f0e554b522ad57e68460131ce 100644 (file)
@@ -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));
index ab1199beb203d4d08ffd7002fcb36e0063ec8371..5161fa53b948ba83fae12c22911461c8bf25c757 100644 (file)
@@ -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
 {