]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/client_side_reply.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / client_side_reply.cc
index c7b06a10f8a9e79b9f75966c2f004ac340000705..d087b58b43556a70e4722eb848af93f7cd47f374 100644 (file)
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
-#include "config.h"
-
-/* for ClientActiveRequests global */
-#include "dlink.h"
-
-/* old includes without reasons given. */
 #include "squid.h"
 #include "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
-#include "auth/UserRequest.h"
-#include "client_side.h"
+#include "anyp/PortCfg.h"
 #include "client_side_reply.h"
+#include "client_side.h"
 #include "clientStream.h"
-#if USE_DELAY_POOLS
-#include "DelayPools.h"
-#endif
+#include "dlink.h"
 #include "errorpage.h"
-#if USE_SQUID_ESI
-#include "esi/Esi.h"
-#endif
 #include "fde.h"
+#include "format/Token.h"
 #include "forward.h"
+#include "globals.h"
+#include "globals.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "ip/QosConfig.h"
-#include "log/Tokens.h"
+#include "ipcache.h"
 #include "MemObject.h"
+#include "protos.h"
 #include "SquidTime.h"
-#include "StoreClient.h"
 #include "Store.h"
+#include "StoreClient.h"
+#if USE_AUTH
+#include "auth/UserRequest.h"
+#endif
+#if USE_DELAY_POOLS
+#include "DelayPools.h"
+#endif
+#if USE_SQUID_ESI
+#include "esi/Esi.h"
+#endif
 
 CBDATA_CLASS_INIT(clientReplyContext);
 
@@ -97,28 +99,51 @@ void
 clientReplyContext::setReplyToError(
     err_type err, http_status status, const HttpRequestMethod& method, char const *uri,
     Ip::Address &addr, HttpRequest * failedrequest, const char *unparsedrequest,
-    AuthUserRequest::Pointer auth_user_request)
+#if USE_AUTH
+    Auth::UserRequest::Pointer auth_user_request
+#else
+    void*
+#endif
+)
 {
     ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
 
     if (unparsedrequest)
         errstate->request_hdrs = xstrdup(unparsedrequest);
 
-    if (status == HTTP_NOT_IMPLEMENTED && http->request)
+#if USE_AUTH
+    errstate->auth_user_request = auth_user_request;
+#endif
+    setReplyToError(method, errstate);
+}
+
+void clientReplyContext::setReplyToError(const HttpRequestMethod& method, ErrorState *errstate)
+{
+    if (errstate->httpStatus == HTTP_NOT_IMPLEMENTED && http->request)
         /* prevent confusion over whether we default to persistent or not */
         http->request->flags.proxy_keepalive = 0;
 
-    http->al.http.code = errstate->httpStatus;
+    http->al->http.code = errstate->httpStatus;
 
     createStoreEntry(method, request_flags());
-
-    errstate->auth_user_request = auth_user_request;
-
     assert(errstate->callback_data == NULL);
     errorAppendEntry(http->storeEntry(), errstate);
     /* Now the caller reads to get this */
 }
 
+void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry)
+{
+    entry->lock(); // removeClientStoreReference() unlocks
+    sc = storeClientListAdd(entry, this);
+#if USE_DELAY_POOLS
+    sc->setDelayId(DelayId::DelayClient(http));
+#endif
+    reqofs = 0;
+    reqsize = 0;
+    flags.storelogiccomplete = 1;
+    http->storeEntry(entry);
+}
+
 void
 clientReplyContext::removeStoreReference(store_client ** scp,
         StoreEntry ** ep)
@@ -261,20 +286,19 @@ clientReplyContext::processExpired()
     debugs(88, 5, "clientReplyContext::processExpired : lastmod " << entry->lastmod );
     http->storeEntry(entry);
     assert(http->out.offset == 0);
-
-    http->request->clientConnection = http->getConn();
+    assert(http->request->clientConnectionManager == http->getConn());
 
     /*
      * A refcounted pointer so that FwdState stays around as long as
      * this clientReplyContext does
      */
-    FwdState::fwdStart(http->getConn() != NULL ? http->getConn()->fd : -1,
-                       http->storeEntry(),
-                       http->request);
+    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))
-        debugs(88, 0, "clientReplyContext::processExpired: Found ENTRY_ABORTED object");
+        debugs(88, DBG_CRITICAL, "clientReplyContext::processExpired: Found ENTRY_ABORTED object");
 
     {
         /* start counting the length from 0 */
@@ -283,7 +307,6 @@ clientReplyContext::processExpired()
     }
 }
 
-
 void
 clientReplyContext::sendClientUpstreamResponse()
 {
@@ -359,6 +382,7 @@ clientReplyContext::handleIMSReply(StoreIOBuffer result)
     // origin replied 304
     if (status == HTTP_NOT_MODIFIED) {
         http->logType = LOG_TCP_REFRESH_UNMODIFIED;
+        http->request->flags.stale_if_hit = 0; // old_entry is no longer stale
 
         // update headers on existing entry
         old_rep->updateOnNotModified(http->storeEntry()->getReply());
@@ -466,7 +490,7 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
     assert(http->logType == LOG_TCP_HIT);
 
     if (strcmp(e->mem_obj->url, urlCanonical(r)) != 0) {
-        debugs(33, 1, "clientProcessHit: URL mismatch, '" << e->mem_obj->url << "' != '" << urlCanonical(r) << "'");
+        debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->url << "' != '" << urlCanonical(r) << "'");
         processMiss();
         return;
     }
@@ -497,7 +521,7 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
 
     case VARY_CANCEL:
         /* varyEvaluateMatch found a object loop. Process as miss */
-        debugs(88, 1, "clientProcessHit: Vary object loop!");
+        debugs(88, DBG_IMPORTANT, "clientProcessHit: Vary object loop!");
         processMiss();
         return;
     }
@@ -544,7 +568,7 @@ clientReplyContext::cacheHit(StoreIOBuffer result)
              */
             http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
             processMiss();
-        } else if (r->protocol == PROTO_HTTP) {
+        } else if (r->protocol == AnyP::PROTO_HTTP) {
             /*
              * Object needs to be revalidated
              * XXX This could apply to FTP as well, if Last-Modified is known.
@@ -596,8 +620,8 @@ clientReplyContext::processMiss()
      */
     if (http->storeEntry()) {
         if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
-            debugs(88, 0, "clientProcessMiss: miss on a special object (" << url << ").");
-            debugs(88, 0, "\tlog_type = " << log_tags[http->logType]);
+            debugs(88, DBG_CRITICAL, "clientProcessMiss: miss on a special object (" << url << ").");
+            debugs(88, DBG_CRITICAL, "\tlog_type = " << Format::log_tags[http->logType]);
             http->storeEntry()->dump(1);
         }
 
@@ -624,8 +648,8 @@ clientReplyContext::processMiss()
     /// Deny loops for accelerator and interceptor. TODO: deny in all modes?
     if (r->flags.loopdetect &&
             (http->flags.accel || http->flags.intercepted)) {
-        http->al.http.code = HTTP_FORBIDDEN;
-        err = clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL, http->getConn()->peer, http->request);
+        http->al->http.code = HTTP_FORBIDDEN;
+        err = clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL, http->getConn()->clientConnection->remote, http->request);
         createStoreEntry(r->method, request_flags());
         errorAppendEntry(http->storeEntry(), err);
         triggerInitialStoreRead();
@@ -637,11 +661,7 @@ clientReplyContext::processMiss()
 
         if (http->redirect.status) {
             HttpReply *rep = new HttpReply;
-#if LOG_TCP_REDIRECTS
-
             http->logType = LOG_TCP_REDIRECT;
-#endif
-
             http->storeEntry()->releaseRequest();
             rep->redirect(http->redirect.status, http->redirect.location);
             http->storeEntry()->replaceHttpReply(rep);
@@ -651,14 +671,13 @@ clientReplyContext::processMiss()
 
         /** Check for internal requests. Update Protocol info if so. */
         if (http->flags.internal)
-            r->protocol = PROTO_INTERNAL;
+            r->protocol = AnyP::PROTO_INTERNAL;
 
-        r->clientConnection = http->getConn();
+        assert(r->clientConnectionManager == http->getConn());
 
         /** Start forwarding to get the new object from network */
-        FwdState::fwdStart(http->getConn() != NULL ? http->getConn()->fd : -1,
-                           http->storeEntry(),
-                           r);
+        Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
+        FwdState::Start(conn, http->storeEntry(), r, http->al);
     }
 }
 
@@ -671,11 +690,11 @@ clientReplyContext::processMiss()
 void
 clientReplyContext::processOnlyIfCachedMiss()
 {
-    ErrorState *err = NULL;
     debugs(88, 4, "clientProcessOnlyIfCachedMiss: '" <<
            RequestMethodStr(http->request->method) << " " << http->uri << "'");
-    http->al.http.code = HTTP_GATEWAY_TIMEOUT;
-    err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT, NULL, http->getConn()->peer, http->request);
+    http->al->http.code = HTTP_GATEWAY_TIMEOUT;
+    ErrorState *err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT, NULL,
+                                       http->getConn()->clientConnection->remote, http->request);
     removeClientStoreReference(&sc, http);
     startError(err);
 }
@@ -841,7 +860,8 @@ clientReplyContext::purgeFoundObject(StoreEntry *entry)
 
     if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
         http->logType = LOG_TCP_DENIED;
-        ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL, http->getConn()->peer, http->request);
+        ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
+                                           http->getConn()->clientConnection->remote, http->request);
         startError(err);
         return;
     }
@@ -879,7 +899,7 @@ clientReplyContext::purgeRequest()
 
     if (!Config2.onoff.enable_purge) {
         http->logType = LOG_TCP_DENIED;
-        ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL, http->getConn()->peer, http->request);
+        ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL, http->getConn()->clientConnection->remote, http->request);
         startError(err);
         return;
     }
@@ -1036,6 +1056,8 @@ clientReplyContext::checkTransferDone()
 int
 clientReplyContext::storeOKTransferDone() const
 {
+    assert(http->storeEntry()->objectLen() >= 0);
+    assert(http->storeEntry()->objectLen() >= headers_sz);
     if (http->out.offset >= http->storeEntry()->objectLen() - headers_sz) {
         debugs(88,3,HERE << "storeOKTransferDone " <<
                " out.offset=" << http->out.offset <<
@@ -1063,14 +1085,6 @@ clientReplyContext::storeNotOKTransferDone() const
         /* haven't found end of headers yet */
         return 0;
 
-    int sending = SENDING_BODY;
-
-    if (curReply->sline.status == HTTP_NO_CONTENT ||
-            curReply->sline.status == HTTP_NOT_MODIFIED ||
-            curReply->sline.status < HTTP_OK ||
-            http->request->method == METHOD_HEAD)
-        sending = SENDING_HDRSONLY;
-
     /*
      * Figure out how much data we are supposed to send.
      * If we are sending a body and we don't have a content-length,
@@ -1091,7 +1105,6 @@ clientReplyContext::storeNotOKTransferDone() const
     }
 }
 
-
 /* A write has completed, what is the next status based on the
  * canonical request data?
  * 1 something is wrong
@@ -1103,18 +1116,18 @@ clientHttpRequestStatus(int fd, ClientHttpRequest const *http)
 {
 #if SIZEOF_INT64_T == 4
     if (http->out.size > 0x7FFF0000) {
-        debugs(88, 1, "WARNING: closing FD " << fd << " to prevent out.size counter overflow");
-        debugs(88, 1, "\tclient " << http->getConn()->peer);
-        debugs(88, 1, "\treceived " << http->out.size << " bytes");
-        debugs(88, 1, "\tURI " << http->log_uri);
+        debugs(88, DBG_IMPORTANT, "WARNING: closing FD " << fd << " to prevent out.size counter overflow");
+        debugs(88, DBG_IMPORTANT, "\tclient " << http->getConn()->peer);
+        debugs(88, DBG_IMPORTANT, "\treceived " << http->out.size << " bytes");
+        debugs(88, DBG_IMPORTANT, "\tURI " << http->log_uri);
         return 1;
     }
 
     if (http->out.offset > 0x7FFF0000) {
-        debugs(88, 1, "WARNING: closing FD " << fd < " to prevent out.offset counter overflow");
-        debugs(88, 1, "\tclient " << http->getConn()->peer);
-        debugs(88, 1, "\treceived " << http->out.size << " bytes, offset " << http->out.offset);
-        debugs(88, 1, "\tURI " << http->log_uri);
+        debugs(88, DBG_IMPORTANT, "WARNING: closing FD " << fd < " to prevent out.offset counter overflow");
+        debugs(88, DBG_IMPORTANT, "\tclient " << http->getConn()->peer);
+        debugs(88, DBG_IMPORTANT, "\treceived " << http->out.size << " bytes, offset " << http->out.offset);
+        debugs(88, DBG_IMPORTANT, "\tURI " << http->log_uri);
         return 1;
     }
 
@@ -1258,9 +1271,9 @@ clientReplyContext::buildReplyHeader()
         hdr->delById(HDR_SET_COOKIE);
     // TODO: RFC 2965 : Must honour Cache-Control: no-cache="set-cookie2" and remove header.
 
-    // if there is not configured a peer proxy with login=PASS option enabled
+    // if there is not configured a peer proxy with login=PASS or login=PASSTHRU option enabled
     // remove the Proxy-Authenticate header
-    if ( !(request->peer_login && strcmp(request->peer_login,"PASS") ==0))
+    if ( !request->peer_login || (strcmp(request->peer_login,"PASS") != 0 && strcmp(request->peer_login,"PASSTHRU") != 0))
         reply->header.delById(HDR_PROXY_AUTHENTICATE);
 
     reply->header.removeHopByHopEntries();
@@ -1295,6 +1308,25 @@ clientReplyContext::buildReplyHeader()
         if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
             hdr->delById(HDR_DATE);
             hdr->insertTime(HDR_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(HDR_DATE);
+            if (h)
+                hdr->putExt("X-Origin-Date", h->value.termedBuf());
+            hdr->delById(HDR_DATE);
+            hdr->insertTime(HDR_DATE, squid_curtime);
+            h = hdr->findEntry(HDR_EXPIRES);
+            if (h && http->storeEntry()->expires >= 0) {
+                hdr->putExt("X-Origin-Expires", h->value.termedBuf());
+                hdr->delById(HDR_EXPIRES);
+                hdr->insertTime(HDR_EXPIRES, squid_curtime + http->storeEntry()->expires - http->storeEntry()->timestamp);
+            }
+            if (http->storeEntry()->timestamp <= squid_curtime) {
+                // put X-Cache-Age: instead of Age:
+                char age[64];
+                snprintf(age, sizeof(age), "%ld", (long int) squid_curtime - http->storeEntry()->timestamp);
+                hdr->putExt("X-Cache-Age", age);
+            }
         } else if (http->storeEntry()->timestamp <= squid_curtime) {
             hdr->putInt(HDR_AGE,
                         squid_curtime - http->storeEntry()->timestamp);
@@ -1330,8 +1362,9 @@ clientReplyContext::buildReplyHeader()
         else if (http->storeEntry()->timestamp > 0)
             hdr->insertTime(HDR_DATE, http->storeEntry()->timestamp);
         else {
-            debugs(88,1,"WARNING: An error inside Squid has caused an HTTP reply without Date:. Please report this");
-            /* TODO: dump something useful about the problem */
+            debugs(88,DBG_IMPORTANT,"WARNING: An error inside Squid has caused an HTTP reply without Date:. Please report this:");
+            /* dump something useful about the problem */
+            http->storeEntry()->dump(DBG_IMPORTANT);
         }
     }
 
@@ -1343,7 +1376,6 @@ clientReplyContext::buildReplyHeader()
     }
 
     /* Filter unproxyable authentication types */
-
     if (http->logType != LOG_TCP_DENIED &&
             hdr->has(HDR_WWW_AUTHENTICATE)) {
         HttpHeaderPos pos = HttpHeaderInitPos;
@@ -1386,6 +1418,7 @@ clientReplyContext::buildReplyHeader()
             hdr->refreshMask();
     }
 
+#if USE_AUTH
     /* Handle authentication headers */
     if (http->logType == LOG_TCP_DENIED &&
             ( reply->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED ||
@@ -1400,6 +1433,7 @@ clientReplyContext::buildReplyHeader()
         authenticateFixHeader(reply, request->auth_user_request, request, 0, 1);
     } else if (request->auth_user_request != NULL)
         authenticateFixHeader(reply, request->auth_user_request, request, http->flags.accel, 0);
+#endif
 
     /* Append X-Cache */
     httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
@@ -1414,6 +1448,7 @@ clientReplyContext::buildReplyHeader()
 #endif
 
     const bool maySendChunkedReply = !request->multipartRangeRequest() &&
+                                     reply->sline.protocol == AnyP::PROTO_HTTP && // response is HTTP
                                      (request->http_ver >= HttpVersion(1, 1));
 
     /* Check whether we should send keep-alive */
@@ -1435,6 +1470,10 @@ clientReplyContext::buildReplyHeader()
     } else if (fdUsageHigh()&& !request->flags.must_keepalive) {
         debugs(88, 3, "clientBuildReplyHeader: Not many unused FDs, can't keep-alive");
         request->flags.proxy_keepalive = 0;
+    } else if (request->flags.sslBumped && !reply->persistent()) {
+        // We do not really have to close, but we pretend we are a tunnel.
+        debugs(88, 3, "clientBuildReplyHeader: bumped reply forces close");
+        request->flags.proxy_keepalive = 0;
     }
 
     // Decide if we send chunked reply
@@ -1485,7 +1524,6 @@ clientReplyContext::buildReplyHeader()
     httpHdrMangleList(hdr, request, ROR_REPLY);
 }
 
-
 void
 clientReplyContext::cloneReply()
 {
@@ -1495,7 +1533,7 @@ clientReplyContext::cloneReply()
 
     reply = HTTPMSGLOCK(rep);
 
-    if (reply->sline.protocol == PROTO_HTTP) {
+    if (reply->sline.protocol == AnyP::PROTO_HTTP) {
         /* RFC 2616 requires us to advertise our 1.1 version (but only on real HTTP traffic) */
         reply->sline.version = HttpVersion(1,1);
     }
@@ -1542,15 +1580,11 @@ clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
       */
     if (r->flags.nocache) {
 
-#if USE_DNSSERVERS
-
+#if USE_DNSHELPER
         ipcacheInvalidate(r->GetHost());
-
 #else
-
         ipcacheInvalidateNegative(r->GetHost());
-
-#endif /* USE_DNSSERVERS */
+#endif /* USE_DNSHELPER */
 
     }
 
@@ -1558,15 +1592,11 @@ clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
 
     else if (r->flags.nocache_hack) {
 
-#if USE_DNSSERVERS
-
+#if USE_DNSHELPER
         ipcacheInvalidate(r->GetHost());
-
 #else
-
         ipcacheInvalidateNegative(r->GetHost());
-
-#endif /* USE_DNSSERVERS */
+#endif /* USE_DNSHELPER */
 
     }
 
@@ -1595,9 +1625,9 @@ clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
 
     if (http->redirect.status) {
         /** \li If redirection status is True force this to be a MISS */
-        debugs(85, 3, "clientProcessRequest2: redirectStatus forced StoreEntry to NULL -  MISS");
+        debugs(85, 3, HERE << "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses)");
         http->storeEntry(NULL);
-        http->logType = LOG_TCP_MISS;
+        http->logType = LOG_TCP_REDIRECT;
         doGetMoreData();
         return;
     }
@@ -1652,7 +1682,6 @@ clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
     assert (context);
     assert(context->http == http);
 
-
     clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
 
     if (!context->ourNode)
@@ -1725,11 +1754,11 @@ clientReplyContext::doGetMoreData()
         assert(http->out.offset == 0);
 
         if (Ip::Qos::TheConfig.isHitTosActive()) {
-            Ip::Qos::doTosLocalHit(http->getConn()->fd);
+            Ip::Qos::doTosLocalHit(http->getConn()->clientConnection);
         }
 
         if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
-            Ip::Qos::doNfmarkLocalHit(http->getConn()->fd);
+            Ip::Qos::doNfmarkLocalHit(http->getConn()->clientConnection);
         }
 
         localTempBuffer.offset = reqofs;
@@ -1830,7 +1859,7 @@ clientReplyContext::sendBodyTooLargeError()
     tmp_noaddr.SetNoAddr(); // TODO: make a global const
     http->logType = LOG_TCP_DENIED_REPLY;
     ErrorState *err = clientBuildError(ERR_TOO_BIG, HTTP_FORBIDDEN, NULL,
-                                       http->getConn() != NULL ? http->getConn()->peer : tmp_noaddr,
+                                       http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
                                        http->request);
     removeClientStoreReference(&(sc), http);
     HTTPMSGUNLOCK(reply);
@@ -1845,7 +1874,7 @@ clientReplyContext::sendPreconditionFailedError()
     http->logType = LOG_TCP_HIT;
     ErrorState *const err =
         clientBuildError(ERR_PRECONDITION_FAILED, HTTP_PRECONDITION_FAILED,
-                         NULL, http->getConn()->peer, http->request);
+                         NULL, http->getConn()->clientConnection->remote, http->request);
     removeClientStoreReference(&sc, http);
     HTTPMSGUNLOCK(reply);
     startError(err);
@@ -1864,6 +1893,7 @@ clientReplyContext::sendNotModified()
     e = http->storeEntry();
     // Copy timestamp from the original entry so the 304
     // reply has a meaningful Age: header.
+    e->timestampsSet();
     e->timestamp = timestamp;
     e->replaceHttpReply(temprep);
     e->complete();
@@ -1898,7 +1928,7 @@ clientReplyContext::processReplyAccess ()
             http->logType == LOG_TCP_DENIED_REPLY ||
             alwaysAllowResponse(reply->sline.status)) {
         headers_sz = reply->hdr_sz;
-        processReplyAccessResult(1);
+        processReplyAccessResult(ACCESS_ALLOWED);
         return;
     }
 
@@ -1912,7 +1942,7 @@ clientReplyContext::processReplyAccess ()
 
     /** check for absent access controls (permit by default) */
     if (!Config.accessList.reply) {
-        processReplyAccessResult(1);
+        processReplyAccessResult(ACCESS_ALLOWED);
         return;
     }
 
@@ -1924,22 +1954,20 @@ clientReplyContext::processReplyAccess ()
 }
 
 void
-clientReplyContext::ProcessReplyAccessResult (int rv, void *voidMe)
+clientReplyContext::ProcessReplyAccessResult(allow_t rv, void *voidMe)
 {
     clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
     me->processReplyAccessResult(rv);
 }
 
 void
-clientReplyContext::processReplyAccessResult(bool accessAllowed)
+clientReplyContext::processReplyAccessResult(const allow_t &accessAllowed)
 {
     debugs(88, 2, "The reply for " << RequestMethodStr(http->request->method)
-           << " " << http->uri << " is "
-           << ( accessAllowed ? "ALLOWED" : "DENIED")
-           << ", because it matched '"
+           << " " << http->uri << " is " << accessAllowed << ", because it matched '"
            << (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
 
-    if (!accessAllowed) {
+    if (accessAllowed != ACCESS_ALLOWED) {
         ErrorState *err;
         err_type page_id;
         page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
@@ -1952,7 +1980,7 @@ clientReplyContext::processReplyAccessResult(bool accessAllowed)
         Ip::Address tmp_noaddr;
         tmp_noaddr.SetNoAddr();
         err = clientBuildError(page_id, HTTP_FORBIDDEN, NULL,
-                               http->getConn() != NULL ? http->getConn()->peer : tmp_noaddr,
+                               http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
                                http->request);
 
         removeClientStoreReference(&sc, http);
@@ -1961,7 +1989,6 @@ clientReplyContext::processReplyAccessResult(bool accessAllowed)
 
         startError(err);
 
-
         return;
     }
 
@@ -2042,31 +2069,27 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
 
     ConnStateData * conn = http->getConn();
 
-    int fd = conn != NULL ? conn->fd : -1;
-    if (fd >= 0 && fd_table[fd].closing()) { // too late, our conn is closing
-        // TODO: should we also quit when fd is negative?
-        debugs(33,3, HERE << "not sending more data to a closing FD " << fd);
+    if (conn == NULL || !conn->isOpen()) {
+        // too late, our conn is closing
+        // TODO: should we also quit?
+        debugs(33,3, HERE << "not sending more data to a closing " << conn->clientConnection);
         return;
     }
 
     char *buf = next()->readBuffer.data;
 
-    char *body_buf = buf;
-
     if (buf != result.data) {
         /* we've got to copy some data */
         assert(result.length <= next()->readBuffer.length);
-        xmemcpy(buf, result.data, result.length);
-        body_buf = buf;
+        memcpy(buf, result.data, result.length);
     }
 
-    if (reqofs==0 && !logTypeIsATcpHit(http->logType)) {
-        assert(fd >= 0); // the beginning of this method implies fd may be -1
+    if (reqofs==0 && !logTypeIsATcpHit(http->logType) && Comm::IsConnOpen(conn->clientConnection)) {
         if (Ip::Qos::TheConfig.isHitTosActive()) {
-            Ip::Qos::doTosLocalMiss(fd, http->request->hier.code);
+            Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
         }
         if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
-            Ip::Qos::doNfmarkLocalMiss(fd, http->request->hier.code);
+            Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
         }
     }
 
@@ -2089,7 +2112,7 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
            reqofs << " bytes (" << result.length <<
            " new bytes)");
     debugs(88, 5, "clientReplyContext::sendMoreData:"
-           " FD " << fd <<
+           << conn->clientConnection <<
            " '" << entry->url() << "'" <<
            " out.offset=" << http->out.offset);
 
@@ -2114,9 +2137,9 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
         size_t k;
 
         if ((k = headersEnd(buf, reqofs))) {
-            safe_free(http->al.headers.reply);
-            http->al.headers.reply = (char *)xcalloc(k + 1, 1);
-            xstrncpy(http->al.headers.reply, buf, k);
+            safe_free(http->al->headers.reply);
+            http->al->headers.reply = (char *)xcalloc(k + 1, 1);
+            xstrncpy(http->al->headers.reply, buf, k);
         }
     }
 
@@ -2125,8 +2148,6 @@ clientReplyContext::sendMoreData (StoreIOBuffer result)
     return;
 }
 
-
-
 /* Using this breaks the client layering just a little!
  */
 void
@@ -2139,7 +2160,7 @@ clientReplyContext::createStoreEntry(const HttpRequestMethod& m, request_flags r
      */
 
     if (http->request == NULL)
-        http->request = HTTPMSGLOCK(new HttpRequest(m, PROTO_NONE, null_string));
+        http->request = HTTPMSGLOCK(new HttpRequest(m, AnyP::PROTO_NONE, null_string));
 
     StoreEntry *e = storeCreateEntry(http->uri, http->log_uri, reqFlags, m);
 
@@ -2175,7 +2196,7 @@ ErrorState *
 clientBuildError(err_type page_id, http_status status, char const *url,
                  Ip::Address &src_addr, HttpRequest * request)
 {
-    ErrorState *err = errorCon(page_id, status, request);
+    ErrorState *err = new ErrorState(page_id, status, request);
     err->src_addr = src_addr;
 
     if (url)