]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/client_side_reply.cc
merge from trunk-r14768
[thirdparty/squid.git] / src / client_side_reply.cc
index c777cfecd95b7696f20f08b0010148d6d0ae5157..6b25ea1a76cb213d5570b87a12ca88f980ad8de5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -20,7 +20,7 @@
 #include "format/Token.h"
 #include "FwdState.h"
 #include "globals.h"
-#include "globals.h"
+#include "http/Stream.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
@@ -89,7 +89,8 @@ clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) :
     reply(NULL),
     old_entry(NULL),
     old_sc(NULL),
-    deleting(false)
+    deleting(false),
+    collapsedRevalidation(crNone)
 {
     *tempbuf = 0;
 }
@@ -264,7 +265,6 @@ void
 clientReplyContext::processExpired()
 {
     const char *url = storeId();
-    StoreEntry *entry = NULL;
     debugs(88, 3, "clientReplyContext::processExpired: '" << http->uri << "'");
     assert(http->storeEntry()->lastmod >= 0);
     /*
@@ -278,6 +278,7 @@ clientReplyContext::processExpired()
         return;
     }
 
+    http->logType = LOG_TCP_REFRESH;
     http->request->flags.refresh = true;
 #if STORE_CLIENT_LIST_DEBUG
     /* Prevent a race with the store client memory free routines
@@ -286,9 +287,36 @@ clientReplyContext::processExpired()
 #endif
     /* Prepare to make a new temporary request */
     saveState();
-    entry = storeCreateEntry(url,
-                             http->log_uri, http->request->flags, http->request->method);
-    /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
+
+    // TODO: support collapsed revalidation for Vary-controlled entries
+    const bool collapsingAllowed = Config.onoff.collapsed_forwarding &&
+                                   !Store::Root().smpAware() &&
+                                   http->request->vary_headers.isEmpty();
+
+    StoreEntry *entry = nullptr;
+    if (collapsingAllowed) {
+        if ((entry = storeGetPublicByRequest(http->request, ksRevalidation)))
+            entry->lock("clientReplyContext::processExpired#alreadyRevalidating");
+    }
+
+    if (entry) {
+        debugs(88, 5, "collapsed on existing revalidation entry: " << *entry);
+        collapsedRevalidation = crSlave;
+    } else {
+        entry = storeCreateEntry(url,
+                                 http->log_uri, http->request->flags, http->request->method);
+        /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
+
+        if (collapsingAllowed) {
+            debugs(88, 5, "allow other revalidation requests to collapse on " << *entry);
+            Store::Root().allowCollapsing(entry, http->request->flags,
+                                          http->request->method);
+            collapsedRevalidation = crInitiator;
+        } else {
+            collapsedRevalidation = crNone;
+        }
+    }
+
     sc = storeClientListAdd(entry, this);
 #if USE_DELAY_POOLS
     /* delay_id is already set on original store client */
@@ -308,13 +336,14 @@ clientReplyContext::processExpired()
     assert(http->out.offset == 0);
     assert(http->request->clientConnectionManager == http->getConn());
 
-    /*
-     * A refcounted pointer so that FwdState stays around as long as
-     * this clientReplyContext does
-     */
-    Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
-    FwdState::Start(conn, http->storeEntry(), http->request, http->al);
-
+    if (collapsedRevalidation != crSlave) {
+        /*
+         * A refcounted pointer so that FwdState stays around as long as
+         * this clientReplyContext does
+         */
+        Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
+        FwdState::Start(conn, http->storeEntry(), http->request, http->al);
+    }
     /* Register with storage manager to receive updates when data comes in. */
 
     if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
@@ -397,7 +426,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
         sendClientOldEntry();
     }
 
-    HttpReply *old_rep = (HttpReply *) old_entry->getReply();
+    const HttpReply *old_rep = old_entry->getReply();
 
     // origin replied 304
     if (status == Http::scNotModified) {
@@ -405,8 +434,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
         http->request->flags.staleIfHit = false; // old_entry is no longer stale
 
         // update headers on existing entry
-        old_rep->updateOnNotModified(http->storeEntry()->getReply());
-        old_entry->timestampsSet();
+        Store::Root().updateOnNotModified(old_entry, *http->storeEntry());
 
         // if client sent IMS
 
@@ -427,6 +455,10 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
         // forward response from origin
         http->logType = LOG_TCP_REFRESH_MODIFIED;
         debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
+
+        if (collapsedRevalidation)
+            http->storeEntry()->clearPublicKeyScope();
+
         sendClientUpstreamResponse();
     }
 
@@ -1007,9 +1039,8 @@ clientReplyContext::purgeDoPurgeHead(StoreEntry *newEntry)
     }
 
     /* And for Vary, release the base URI if none of the headers was included in the request */
-
-    if (http->request->vary_headers
-            && !strstr(http->request->vary_headers, "=")) {
+    if (!http->request->vary_headers.isEmpty()
+            && http->request->vary_headers.find('=') != SBuf::npos) {
         // XXX: performance regression, c_str() reallocates
         SBuf tmp(http->request->effectiveRequestUri());
         StoreEntry *entry = storeGetPublic(tmp.c_str(), Http::METHOD_GET);
@@ -1147,7 +1178,7 @@ clientReplyContext::storeNotOKTransferDone() const
     if (curReply->content_length < 0)
         return 0;
 
-    int64_t expectedLength = curReply->content_length + http->out.headers_sz;
+    uint64_t expectedLength = curReply->content_length + http->out.headers_sz;
 
     if (http->out.size < expectedLength)
         return 0;
@@ -1238,6 +1269,11 @@ clientReplyContext::replyStatus()
             return STREAM_FAILED;
         }
 
+        if (EBIT_TEST(http->storeEntry()->flags, ENTRY_BAD_LENGTH)) {
+            debugs(88, 5, "clientReplyStatus: truncated response body");
+            return STREAM_UNPLANNED_COMPLETE;
+        }
+
         if (!done) {
             debugs(88, 5, "clientReplyStatus: closing, !done, but read 0 bytes");
             return STREAM_FAILED;
@@ -1348,19 +1384,19 @@ clientReplyContext::buildReplyHeader()
          */
         if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
             hdr->delById(Http::HdrType::DATE);
-            hdr->insertTime(Http::HdrType::DATE, squid_curtime);
+            hdr->putTime(Http::HdrType::DATE, squid_curtime);
         } else if (http->getConn() && http->getConn()->port->actAsOrigin) {
             // Swap the Date: header to current time if we are simulating an origin
             HttpHeaderEntry *h = hdr->findEntry(Http::HdrType::DATE);
             if (h)
                 hdr->putExt("X-Origin-Date", h->value.termedBuf());
             hdr->delById(Http::HdrType::DATE);
-            hdr->insertTime(Http::HdrType::DATE, squid_curtime);
+            hdr->putTime(Http::HdrType::DATE, squid_curtime);
             h = hdr->findEntry(Http::HdrType::EXPIRES);
             if (h && http->storeEntry()->expires >= 0) {
                 hdr->putExt("X-Origin-Expires", h->value.termedBuf());
                 hdr->delById(Http::HdrType::EXPIRES);
-                hdr->insertTime(Http::HdrType::EXPIRES, squid_curtime + http->storeEntry()->expires - http->storeEntry()->timestamp);
+                hdr->putTime(Http::HdrType::EXPIRES, squid_curtime + http->storeEntry()->expires - http->storeEntry()->timestamp);
             }
             if (http->storeEntry()->timestamp <= squid_curtime) {
                 // put X-Cache-Age: instead of Age:
@@ -1399,9 +1435,9 @@ clientReplyContext::buildReplyHeader()
      */
     if ( !hdr->has(Http::HdrType::DATE) ) {
         if (!http->storeEntry())
-            hdr->insertTime(Http::HdrType::DATE, squid_curtime);
+            hdr->putTime(Http::HdrType::DATE, squid_curtime);
         else if (http->storeEntry()->timestamp > 0)
-            hdr->insertTime(Http::HdrType::DATE, http->storeEntry()->timestamp);
+            hdr->putTime(Http::HdrType::DATE, http->storeEntry()->timestamp);
         else {
             debugs(88,DBG_IMPORTANT,"BUG 3279: HTTP reply without Date:");
             /* dump something useful about the problem */
@@ -1529,9 +1565,7 @@ clientReplyContext::buildReplyHeader()
     }
 
     // Decide if we send chunked reply
-    if (maySendChunkedReply &&
-            request->flags.proxyKeepalive &&
-            reply->bodySize(request->method) < 0) {
+    if (maySendChunkedReply && reply->bodySize(request->method) < 0) {
         debugs(88, 3, "clientBuildReplyHeader: chunked reply");
         request->flags.chunkedReply = true;
         hdr->putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
@@ -1573,7 +1607,7 @@ clientReplyContext::buildReplyHeader()
         /* TODO: else case: drop any controls intended specifically for our surrogate ID */
     }
 
-    httpHdrMangleList(hdr, request, ROR_REPLY);
+    httpHdrMangleList(hdr, request, http->al, ROR_REPLY);
 }
 
 void
@@ -2119,21 +2153,28 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
 
     StoreEntry *entry = http->storeEntry();
 
-    ConnStateData * conn = http->getConn();
+    if (ConnStateData * conn = http->getConn()) {
+        if (!conn->isOpen()) {
+            debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
+            return;
+        }
+        if (conn->pinning.zeroReply) {
+            debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
+            return;
+        }
 
-    // too late, our conn is closing
-    // TODO: should we also quit?
-    if (conn == NULL) {
-        debugs(33,3, "not sending more data to a closed connection" );
-        return;
-    }
-    if (!conn->isOpen()) {
-        debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
-        return;
-    }
-    if (conn->pinning.zeroReply) {
-        debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
-        return;
+        if (reqofs==0 && !http->logType.isTcpHit()) {
+            if (Ip::Qos::TheConfig.isHitTosActive()) {
+                Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
+            }
+            if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
+                Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
+            }
+        }
+
+        debugs(88, 5, conn->clientConnection <<
+               " '" << entry->url() << "'" <<
+               " out.offset=" << http->out.offset);
     }
 
     char *buf = next()->readBuffer.data;
@@ -2144,15 +2185,6 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
         memcpy(buf, result.data, result.length);
     }
 
-    if (reqofs==0 && !http->logType.isTcpHit() && Comm::IsConnOpen(conn->clientConnection)) {
-        if (Ip::Qos::TheConfig.isHitTosActive()) {
-            Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
-        }
-        if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
-            Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
-        }
-    }
-
     /* We've got the final data to start pushing... */
     flags.storelogiccomplete = 1;
 
@@ -2171,10 +2203,6 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
     debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
            reqofs << " bytes (" << result.length <<
            " new bytes)");
-    debugs(88, 5, "clientReplyContext::sendMoreData:"
-           << conn->clientConnection <<
-           " '" << entry->url() << "'" <<
-           " out.offset=" << http->out.offset);
 
     /* update size of the request */
     reqsize = reqofs;
@@ -2191,6 +2219,11 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
 
     cloneReply();
 
+#if USE_DELAY_POOLS
+    if (sc)
+        sc->setDelayId(DelayId::DelayClient(http,reply));
+#endif
+
     /* handle headers */
 
     if (Config.onoff.log_mime_hdrs) {