]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/http.cc
Remove remainder of EXTERNNEW hackup.
[thirdparty/squid.git] / src / http.cc
index 2b307f7ed88e7d515529350ff4398e14d82f2506..36c9f2c511d240430f0f22058197853b80c2206a 100644 (file)
  */
 
 #include "squid.h"
+
+#include "acl/FilledChecklist.h"
+#include "auth/UserRequest.h"
+#if DELAY_POOLS
+#include "DelayPools.h"
+#endif
 #include "errorpage.h"
-#include "MemBuf.h"
+#include "fde.h"
 #include "http.h"
-#include "AuthUserRequest.h"
-#include "Store.h"
-#include "HttpReply.h"
-#include "HttpRequest.h"
-#include "MemObject.h"
 #include "HttpHdrContRange.h"
 #include "HttpHdrSc.h"
 #include "HttpHdrScTarget.h"
-#include "ACLChecklist.h"
-#include "fde.h"
-#if DELAY_POOLS
-#include "DelayPools.h"
-#endif
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "MemBuf.h"
+#include "MemObject.h"
+#include "protos.h"
+#include "rfc1738.h"
 #include "SquidTime.h"
+#include "Store.h"
 #include "TextException.h"
 
+
 #define SQUID_ENTER_THROWING_CODE() try {
 #define SQUID_EXIT_THROWING_CODE(status) \
        status = true; \
@@ -76,16 +80,21 @@ static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeader
         HttpHeader * hdr_out, const int we_do_ranges, const http_state_flags);
 
 HttpStateData::HttpStateData(FwdState *theFwdState) : AsyncJob("HttpStateData"), ServerStateData(theFwdState),
-        lastChunk(0), header_bytes_read(0), reply_bytes_read(0), httpChunkDecoder(NULL)
+        lastChunk(0), header_bytes_read(0), reply_bytes_read(0),
+        body_bytes_truncated(0), httpChunkDecoder(NULL)
 {
     debugs(11,5,HERE << "HttpStateData " << this << " created");
     ignoreCacheControl = false;
     surrogateNoStore = false;
     fd = fwd->server_fd;
     readBuf = new MemBuf;
-    readBuf->init(4096, SQUID_TCP_SO_RCVBUF);
+    readBuf->init();
     orig_request = HTTPMSGLOCK(fwd->request);
 
+    // reset peer response time stats for %<pt
+    orig_request->hier.peer_http_request_sent.tv_sec = 0;
+    orig_request->hier.peer_http_request_sent.tv_usec = 0;
+
     if (fwd->servers)
         _peer = fwd->servers->_peer;         /* might be NULL */
 
@@ -301,12 +310,8 @@ httpMaybeRemovePublic(StoreEntry * e, http_status status)
 void
 HttpStateData::processSurrogateControl(HttpReply *reply)
 {
-#if USE_SQUID_ESI
-
     if (request->flags.accelerated && reply->surrogate_control) {
-        HttpHdrScTarget *sctusable =
-            httpHdrScGetMergedTarget(reply->surrogate_control,
-                                     Config.Accel.surrogate_id);
+        HttpHdrScTarget *sctusable = httpHdrScGetMergedTarget(reply->surrogate_control, Config.Accel.surrogate_id);
 
         if (sctusable) {
             if (EBIT_TEST(sctusable->mask, SC_NO_STORE) ||
@@ -318,7 +323,7 @@ HttpStateData::processSurrogateControl(HttpReply *reply)
 
             /* The HttpHeader logic cannot tell if the header it's parsing is a reply to an
              * accelerated request or not...
-             * Still, this is an abtraction breach. - RC
+             * Still, this is an abstraction breach. - RC
              */
             if (sctusable->max_age != -1) {
                 if (sctusable->max_age < sctusable->max_stale)
@@ -336,8 +341,6 @@ HttpStateData::processSurrogateControl(HttpReply *reply)
             httpHdrScTargetDestroy(sctusable);
         }
     }
-
-#endif
 }
 
 int
@@ -533,8 +536,9 @@ HttpStateData::cacheableReply()
 
         return 0;
 
-    default:                   /* Unknown status code */
-        debugs (11, 0, HERE << "HttpStateData::cacheableReply: unexpected http status code " << rep->sline.status);
+    default:
+        /* RFC 2616 section 6.1.1: an unrecognized response MUST NOT be cached. */
+        debugs (11, 3, HERE << "Unknown HTTP status code " << rep->sline.status << ". Not cacheable.");
 
         return 0;
 
@@ -633,8 +637,8 @@ HttpStateData::keepaliveAccounting(HttpReply *reply)
         if (_peer)
             _peer->stats.n_keepalives_recv++;
 
-       if (Config.onoff.detect_broken_server_pconns
-       && reply->bodySize(request->method) == -1 && !flags.chunked) {
+        if (Config.onoff.detect_broken_server_pconns
+                && reply->bodySize(request->method) == -1 && !flags.chunked) {
             debugs(11, 1, "keepaliveAccounting: Impossible keep-alive header from '" << entry->url() << "'" );
             // debugs(11, 2, "GOT HTTP REPLY HDR:\n---------\n" << readBuf->content() << "\n----------" );
             flags.keepalive_broken = 1;
@@ -673,12 +677,10 @@ HttpStateData::processReplyHeader()
     HttpReply *newrep = new HttpReply;
     const bool parsed = newrep->parse(readBuf, eof, &error);
 
-    if (!parsed && readBuf->contentSize() > 5 && strncmp(readBuf->content(), "HTTP/", 5) != 0) {
+    if (!parsed && readBuf->contentSize() > 5 && strncmp(readBuf->content(), "HTTP/", 5) != 0 && strncmp(readBuf->content(), "ICY", 3) != 0) {
         MemBuf *mb;
         HttpReply *tmprep = new HttpReply;
-        tmprep->sline.version = HttpVersion(1, 0);
-        tmprep->sline.status = HTTP_OK;
-        tmprep->header.putTime(HDR_DATE, squid_curtime);
+        tmprep->setHeaders(HTTP_OK, "Gatewaying", NULL, -1, -1, -1);
         tmprep->header.putExt("X-Transformed-From", "HTTP/0.9");
         mb = tmprep->pack();
         newrep->parse(mb, eof, &error);
@@ -687,7 +689,7 @@ HttpStateData::processReplyHeader()
         if (!parsed && error > 0) { // unrecoverable parsing error
             debugs(11, 3, "processReplyHeader: Non-HTTP-compliant header: '" <<  readBuf->content() << "'");
             flags.headers_parsed = 1;
-            newrep->sline.version = HttpVersion(1, 0);
+            newrep->sline.version = HttpVersion(1,0);
             newrep->sline.status = error;
             HttpReply *vrep = setVirginReply(newrep);
             entry->replaceHttpReply(vrep);
@@ -709,8 +711,28 @@ HttpStateData::processReplyHeader()
         readBuf->consume(header_bytes_read);
     }
 
+    /* Skip 1xx messages for now. Advertised in Via as an internal 1.0 hop */
+    if (newrep->sline.protocol == PROTO_HTTP && newrep->sline.status >= 100 && newrep->sline.status < 200) {
+
+#if WHEN_HTTP11
+        /* When HTTP/1.1 check if the client is expecting a 1xx reply and maybe pass it on */
+        if (orig_request->header.has(HDR_EXPECT)) {
+            // TODO: pass to the client anyway?
+        }
+#endif
+        delete newrep;
+        debugs(11, 2, HERE << "1xx headers consume " << header_bytes_read << " bytes header.");
+        header_bytes_read = 0;
+        if (reply_bytes_read > 0)
+            debugs(11, 2, HERE << "1xx headers consume " << reply_bytes_read << " bytes reply.");
+        reply_bytes_read = 0;
+        ctx_exit(ctx);
+        processReplyHeader();
+        return;
+    }
+
     flags.chunked = 0;
-    if (newrep->header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',')) {
+    if (newrep->sline.protocol == PROTO_HTTP && newrep->header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',')) {
         flags.chunked = 1;
         httpChunkDecoder = new ChunkedCodingParser;
     }
@@ -731,6 +753,8 @@ HttpStateData::processReplyHeader()
      * Parse the header and remove all referenced headers
      */
 
+    orig_request->hier.peer_reply_status = newrep->sline.status;
+
     ctx_exit(ctx);
 
 }
@@ -827,8 +851,9 @@ HttpStateData::haveParsedReplyHeaders()
 
         if (!vary) {
             entry->makePrivate();
+            if (!fwd->reforwardableStatus(rep->sline.status))
+                EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
             goto no_cache;
-
         }
 
         entry->mem_obj->vary_headers = xstrdup(vary);
@@ -976,6 +1001,9 @@ HttpStateData::persistentConnStatus() const
 
         if (body_bytes_read < vrep->content_length)
             return INCOMPLETE_MSG;
+
+        if (body_bytes_truncated > 0) // already read more than needed
+            return COMPLETE_NONPERSISTENT_MSG; // disable pconns
     }
 
     /** \par
@@ -994,14 +1022,14 @@ HttpStateData::ReadReplyWrapper(int fd, char *buf, size_t len, comm_err_t flag,
     assert (fd == httpState->fd);
     // assert(buf == readBuf->content());
     PROF_start(HttpStateData_readReply);
-    httpState->readReply (len, flag, xerrno);
+    httpState->readReply(len, flag, xerrno);
     PROF_stop(HttpStateData_readReply);
 }
 */
 
 /* XXX this function is too long! */
 void
-HttpStateData::readReply (const CommIoCbParams &io)
+HttpStateData::readReply(const CommIoCbParams &io)
 {
     int bin;
     int clen;
@@ -1060,6 +1088,11 @@ HttpStateData::readReply (const CommIoCbParams &io)
             clen >>= 1;
 
         IOStats.Http.read_hist[bin]++;
+
+        // update peer response time stats (%<pt)
+        const timeval &sent = orig_request->hier.peer_http_request_sent;
+        orig_request->hier.peer_response_time =
+            sent.tv_sec ? tvSubMsec(sent, current_time) : -1;
     }
 
     /** \par
@@ -1134,22 +1167,28 @@ HttpStateData::continueAfterParsingHeader()
             const http_status s = vrep->sline.status;
             const HttpVersion &v = vrep->sline.version;
             if (s == HTTP_INVALID_HEADER && v != HttpVersion(0,9)) {
+                debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Bad header encountered from " << entry->url() << " AKA " << orig_request->GetHost() << orig_request->urlpath.termedBuf() );
                 error = ERR_INVALID_RESP;
-            } else
-                if (s == HTTP_HEADER_TOO_LARGE) {
-                    fwd->dontRetry(true);
-                    error = ERR_TOO_BIG;
-                } else {
-                    return true; // done parsing, got reply, and no error
-                }
+            } else if (s == HTTP_HEADER_TOO_LARGE) {
+                fwd->dontRetry(true);
+                error = ERR_TOO_BIG;
+            } else {
+                return true; // done parsing, got reply, and no error
+            }
         } else {
             // parsed headers but got no reply
+            debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: No reply at all for " << entry->url() << " AKA " << orig_request->GetHost() << orig_request->urlpath.termedBuf() );
             error = ERR_INVALID_RESP;
         }
     } else {
         assert(eof);
-        error = readBuf->hasContent() ?
-                ERR_INVALID_RESP : ERR_ZERO_SIZE_OBJECT;
+        if (readBuf->hasContent()) {
+            error = ERR_INVALID_RESP;
+            debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Headers did not parse at all for " << entry->url() << " AKA " << orig_request->GetHost() << orig_request->urlpath.termedBuf() );
+        } else {
+            error = ERR_ZERO_SIZE_OBJECT;
+            debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: No object data received for " << entry->url() << " AKA " << orig_request->GetHost() << orig_request->urlpath.termedBuf() );
+        }
     }
 
     assert(error != ERR_NONE);
@@ -1160,6 +1199,33 @@ HttpStateData::continueAfterParsingHeader()
     return false; // quit on error
 }
 
+/** truncate what we read if we read too much so that writeReplyBody()
+    writes no more than what we should have read */
+void
+HttpStateData::truncateVirginBody()
+{
+    assert(flags.headers_parsed);
+
+    HttpReply *vrep = virginReply();
+    int64_t clen = -1;
+    if (!vrep->expectingBody(request->method, clen) || clen < 0)
+        return; // no body or a body of unknown size, including chunked
+
+    const int64_t body_bytes_read = reply_bytes_read - header_bytes_read;
+    if (body_bytes_read - body_bytes_truncated <= clen)
+        return; // we did not read too much or already took care of the extras
+
+    if (const int64_t extras = body_bytes_read - body_bytes_truncated - clen) {
+        // server sent more that the advertised content length
+        debugs(11,5, HERE << "body_bytes_read=" << body_bytes_read <<
+               " clen=" << clen << '/' << vrep->content_length <<
+               " body_bytes_truncated=" << body_bytes_truncated << '+' << extras);
+
+        readBuf->truncate(extras);
+        body_bytes_truncated += extras;
+    }
+}
+
 /**
  * Call this when there is data from the origin server
  * which should be sent to either StoreEntry, or to ICAP...
@@ -1167,6 +1233,7 @@ HttpStateData::continueAfterParsingHeader()
 void
 HttpStateData::writeReplyBody()
 {
+    truncateVirginBody(); // if needed
     const char *data = readBuf->content();
     int len = readBuf->contentSize();
     addVirginReplyBody(data, len);
@@ -1178,22 +1245,22 @@ HttpStateData::decodeAndWriteReplyBody()
 {
     const char *data = NULL;
     int len;
-    bool status = false;
+    bool wasThereAnException = false;
     assert(flags.chunked);
     assert(httpChunkDecoder);
     SQUID_ENTER_THROWING_CODE();
     MemBuf decodedData;
     decodedData.init();
-    const bool done = httpChunkDecoder->parse(readBuf,&decodedData);
+    const bool doneParsing = httpChunkDecoder->parse(readBuf,&decodedData);
     len = decodedData.contentSize();
     data=decodedData.content();
     addVirginReplyBody(data, len);
-    if (done) {
+    if (doneParsing) {
         lastChunk = 1;
         flags.do_next_read = 0;
     }
-    SQUID_EXIT_THROWING_CODE(status);
-    return status;
+    SQUID_EXIT_THROWING_CODE(wasThereAnException);
+    return wasThereAnException;
 }
 
 /**
@@ -1217,6 +1284,7 @@ HttpStateData::processReplyBody()
     }
 
 #if USE_ADAPTATION
+    debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
     if (adaptationAccessCheckPending)
         return;
 
@@ -1304,10 +1372,12 @@ HttpStateData::processReplyBody()
 void
 HttpStateData::maybeReadVirginBody()
 {
-    int read_sz = replyBodySpace(readBuf->spaceSize());
+    // we may need to grow the buffer if headers do not fit
+    const int minRead = flags.headers_parsed ? 0 :1024;
+    const int read_size = replyBodySpace(*readBuf, minRead);
 
     debugs(11,9, HERE << (flags.do_next_read ? "may" : "wont") <<
-           " read up to " << read_sz << " bytes from FD " << fd);
+           " read up to " << read_size << " bytes from FD " << fd);
 
     /*
      * why <2? Because delayAwareRead() won't actually read if
@@ -1317,17 +1387,13 @@ HttpStateData::maybeReadVirginBody()
      * handler until we get a notification from someone that
      * its okay to read again.
      */
-    if (read_sz < 2) {
-        if (flags.headers_parsed)
-            return;
-        else
-            read_sz = 1024;
-    }
+    if (read_size < 2)
+        return;
 
     if (flags.do_next_read) {
         flags.do_next_read = 0;
         typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
-        entry->delayAwareRead(fd, readBuf->space(read_sz), read_sz,
+        entry->delayAwareRead(fd, readBuf->space(read_size), read_size,
                               asyncCall(11, 5, "HttpStateData::readReply",
                                         Dialer(this, &HttpStateData::readReply)));
     }
@@ -1378,6 +1444,8 @@ HttpStateData::sendComplete(const CommIoCbParams &io)
     commSetTimeout(fd, Config.Timeout.read, timeoutCall);
 
     flags.request_sent = 1;
+
+    orig_request->hier.peer_http_request_sent = current_time;
 }
 
 // Close the HTTP server connection. Used by serverComplete().
@@ -1401,6 +1469,92 @@ HttpStateData::doneWithServer() const
     return fd < 0;
 }
 
+
+/*
+ * Fixup authentication request headers for special cases
+ */
+static void
+httpFixupAuthentication(HttpRequest * request, HttpRequest * orig_request, const HttpHeader * hdr_in, HttpHeader * hdr_out, http_state_flags flags)
+{
+    http_hdr_type header = flags.originpeer ? HDR_AUTHORIZATION : HDR_PROXY_AUTHORIZATION;
+
+    /* Nothing to do unless we are forwarding to a peer */
+    if (!request->flags.proxying)
+        return;
+
+    /* Needs to be explicitly enabled */
+    if (!orig_request->peer_login)
+        return;
+
+    /* Maybe already dealt with? */
+    if (hdr_out->has(header))
+        return;
+
+    /* Nothing to do here for PASSTHRU */
+    if (strcmp(orig_request->peer_login, "PASSTHRU") == 0)
+        return;
+
+    /* PROXYPASS is a special case, single-signon to servers with the proxy password (basic only) */
+    if (flags.originpeer && strcmp(orig_request->peer_login, "PROXYPASS") == 0 && hdr_in->has(HDR_PROXY_AUTHORIZATION)) {
+        const char *auth = hdr_in->getStr(HDR_PROXY_AUTHORIZATION);
+
+        if (auth && strncasecmp(auth, "basic ", 6) == 0) {
+            hdr_out->putStr(header, auth);
+            return;
+        }
+    }
+
+    /* Special mode to pass the username to the upstream cache */
+    if (*orig_request->peer_login == '*') {
+        char loginbuf[256];
+        const char *username = "-";
+
+        if (orig_request->extacl_user.size())
+            username = orig_request->extacl_user.termedBuf();
+        else if (orig_request->auth_user_request)
+            username = orig_request->auth_user_request->username();
+
+        snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
+
+        httpHeaderPutStrf(hdr_out, header, "Basic %s",
+                          base64_encode(loginbuf));
+        return;
+    }
+
+    /* external_acl provided credentials */
+    if (orig_request->extacl_user.size() && orig_request->extacl_passwd.size() &&
+            (strcmp(orig_request->peer_login, "PASS") == 0 ||
+             strcmp(orig_request->peer_login, "PROXYPASS") == 0)) {
+        char loginbuf[256];
+        snprintf(loginbuf, sizeof(loginbuf), SQUIDSTRINGPH ":" SQUIDSTRINGPH,
+                 SQUIDSTRINGPRINT(orig_request->extacl_user),
+                 SQUIDSTRINGPRINT(orig_request->extacl_passwd));
+        httpHeaderPutStrf(hdr_out, header, "Basic %s",
+                          base64_encode(loginbuf));
+        return;
+    }
+
+    /* Kerberos login to peer */
+#if HAVE_KRB5 && HAVE_GSSAPI
+    if (strncmp(orig_request->peer_login, "NEGOTIATE",strlen("NEGOTIATE")) == 0) {
+        char *Token=NULL;
+        char *PrincipalName=NULL,*p;
+        if ((p=strchr(orig_request->peer_login,':')) != NULL ) {
+            PrincipalName=++p;
+        }
+        Token = peer_proxy_negotiate_auth(PrincipalName,request->peer_host);
+        if (Token) {
+            httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Negotiate %s",Token);
+        }
+        return;
+    }
+#endif /* HAVE_KRB5 && HAVE_GSSAPI */
+
+    httpHeaderPutStrf(hdr_out, header, "Basic %s",
+                      base64_encode(orig_request->peer_login));
+    return;
+}
+
 /*
  * build request headers and append them to a given MemBuf
  * used by buildRequestPrefix()
@@ -1419,7 +1573,6 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
     LOCAL_ARRAY(char, ntoabuf, MAX_IPSTRLEN);
     const HttpHeader *hdr_in = &orig_request->header;
     const HttpHeaderEntry *e = NULL;
-    String strFwd;
     HttpHeaderPos pos = HttpHeaderInitPos;
     assert (hdr_out->owner == hoRequest);
 
@@ -1458,35 +1611,47 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
         strVia.clean();
     }
 
-#if USE_SQUID_ESI
-    {
+    if (orig_request->flags.accelerated) {
         /* Append Surrogate-Capabilities */
-        String strSurrogate (hdr_in->getList(HDR_SURROGATE_CAPABILITY));
-        snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"",
-                 Config.Accel.surrogate_id);
+        String strSurrogate(hdr_in->getList(HDR_SURROGATE_CAPABILITY));
+#if USE_SQUID_ESI
+        snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id);
+#else
+        snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0\"", Config.Accel.surrogate_id);
+#endif
         strListAdd(&strSurrogate, bbuf, ',');
         hdr_out->putStr(HDR_SURROGATE_CAPABILITY, strSurrogate.termedBuf());
     }
-#endif
-
-    strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR);
 
     /** \pre Handle X-Forwarded-For */
     if (strcmp(opt_forwarded_for, "delete") != 0) {
+
+        String strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR);
+
+        if (strFwd.size() > 65536/2) {
+            // There is probably a forwarding loop with Via detection disabled.
+            // If we do nothing, String will assert on overflow soon.
+            // TODO: Terminate all transactions with huge XFF?
+            strFwd = "error";
+
+            static int warnedCount = 0;
+            if (warnedCount++ < 100) {
+                const char *url = entry ? entry->url() : urlCanonical(orig_request);
+                debugs(11, 1, "Warning: likely forwarding loop with " << url);
+            }
+        }
+
         if (strcmp(opt_forwarded_for, "on") == 0) {
             /** If set to ON - append client IP or 'unknown'. */
-            strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR);
             if ( orig_request->client_addr.IsNoAddr() )
                 strListAdd(&strFwd, "unknown", ',');
             else
                 strListAdd(&strFwd, orig_request->client_addr.NtoA(ntoabuf, MAX_IPSTRLEN), ',');
         } else if (strcmp(opt_forwarded_for, "off") == 0) {
             /** If set to OFF - append 'unknown'. */
-            strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR);
             strListAdd(&strFwd, "unknown", ',');
         } else if (strcmp(opt_forwarded_for, "transparent") == 0) {
             /** If set to TRANSPARENT - pass through unchanged. */
-            strFwd = hdr_in->getList(HDR_X_FORWARDED_FOR);
         } else if (strcmp(opt_forwarded_for, "truncate") == 0) {
             /** If set to TRUNCATE - drop existing list and replace with client IP or 'unknown'. */
             if ( orig_request->client_addr.IsNoAddr() )
@@ -1498,7 +1663,6 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
             hdr_out->putStr(HDR_X_FORWARDED_FOR, strFwd.termedBuf());
     }
     /** If set to DELETE - do not copy through. */
-    strFwd.clean();
 
     /* append Host if not there already */
     if (!hdr_out->has(HDR_HOST)) {
@@ -1522,83 +1686,11 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
         }
     }
 
-    /* append Proxy-Authorization if configured for peer, and proxying */
-    if (request->flags.proxying && orig_request->peer_login &&
-            !hdr_out->has(HDR_PROXY_AUTHORIZATION)) {
-        if (*orig_request->peer_login == '*') {
-            /* Special mode, to pass the username to the upstream cache */
-            char loginbuf[256];
-            const char *username = "-";
-
-            if (orig_request->extacl_user.size())
-                username = orig_request->extacl_user.termedBuf();
-            else if (orig_request->auth_user_request)
-                username = orig_request->auth_user_request->username();
-
-            snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
-
-            httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
-                              base64_encode(loginbuf));
-        } else if (strcmp(orig_request->peer_login, "PASS") == 0) {
-            if (orig_request->extacl_user.size() && orig_request->extacl_passwd.size()) {
-                char loginbuf[256];
-                snprintf(loginbuf, sizeof(loginbuf), SQUIDSTRINGPH ":" SQUIDSTRINGPH,
-                    SQUIDSTRINGPRINT(orig_request->extacl_user),
-                    SQUIDSTRINGPRINT(orig_request->extacl_passwd));
-                httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
-                                  base64_encode(loginbuf));
-            }
-        } else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) {
-            /* Nothing to do */
-        } else {
-            httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
-                              base64_encode(orig_request->peer_login));
-        }
-    }
-
-    /* append WWW-Authorization if configured for peer */
-    if (flags.originpeer && orig_request->peer_login &&
-            !hdr_out->has(HDR_AUTHORIZATION)) {
-        if (strcmp(orig_request->peer_login, "PASS") == 0) {
-            /* No credentials to forward.. (should have been done above if available) */
-        } else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) {
-            /* Special mode, convert proxy authentication to WWW authentication
-            * (also applies to authentication provided by external acl)
-             */
-            const char *auth = hdr_in->getStr(HDR_PROXY_AUTHORIZATION);
-
-            if (auth && strncasecmp(auth, "basic ", 6) == 0) {
-                hdr_out->putStr(HDR_AUTHORIZATION, auth);
-            } else if (orig_request->extacl_user.size() && orig_request->extacl_passwd.size()) {
-                char loginbuf[256];
-                snprintf(loginbuf, sizeof(loginbuf), SQUIDSTRINGPH ":" SQUIDSTRINGPH,
-                    SQUIDSTRINGPRINT(orig_request->extacl_user),
-                    SQUIDSTRINGPRINT(orig_request->extacl_passwd));
-                httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
-                                  base64_encode(loginbuf));
-            }
-        } else if (*orig_request->peer_login == '*') {
-            /* Special mode, to pass the username to the upstream cache */
-            char loginbuf[256];
-            const char *username = "-";
-
-            if (orig_request->auth_user_request)
-                username = orig_request->auth_user_request->username();
-            else if (orig_request->extacl_user.size())
-                username = orig_request->extacl_user.termedBuf();
-
-            snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);
+    /* Fixup (Proxy-)Authorization special cases. Plain relaying dealt with above */
+    httpFixupAuthentication(request, orig_request, hdr_in, hdr_out, flags);
 
-            httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
-                              base64_encode(loginbuf));
-        } else {
-            /* Fixed login string */
-            httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
-                              base64_encode(orig_request->peer_login));
-        }
-    }
-
-    /* append Cache-Control, add max-age if not there already */ {
+    /* append Cache-Control, add max-age if not there already */
+    {
         HttpHdrCc *cc = hdr_in->getCc();
 
         if (!cc)
@@ -1610,8 +1702,8 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
             EBIT_SET(cc->mask, CC_NO_CACHE);
 #endif
 
-       /* Add max-age only without no-cache */
-       if (!EBIT_TEST(cc->mask, CC_MAX_AGE) && !EBIT_TEST(cc->mask, CC_NO_CACHE)) {
+        /* Add max-age only without no-cache */
+        if (!EBIT_TEST(cc->mask, CC_MAX_AGE) && !EBIT_TEST(cc->mask, CC_NO_CACHE)) {
             const char *url =
                 entry ? entry->url() : urlCanonical(orig_request);
             httpHdrCcSetMaxAge(cc, getMaxAge(url));
@@ -1662,23 +1754,22 @@ copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, co
 
     switch (e->id) {
 
-/** \title RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid should not pass on. */
+        /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid should not pass on. */
 
     case HDR_PROXY_AUTHORIZATION:
         /** \par Proxy-Authorization:
          * Only pass on proxy authentication to peers for which
          * authentication forwarding is explicitly enabled
          */
-
-        if (flags.proxying && orig_request->peer_login &&
+        if (!flags.originpeer && flags.proxying && orig_request->peer_login &&
                 (strcmp(orig_request->peer_login, "PASS") == 0 ||
-                 strcmp(orig_request->peer_login, "PROXYPASS") == 0)) {
+                 strcmp(orig_request->peer_login, "PROXYPASS") == 0 ||
+                 strcmp(orig_request->peer_login, "PASSTHRU") == 0)) {
             hdr_out->addEntry(e->clone());
         }
-
         break;
 
-/** \title RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid does not pass on. */
+        /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid does not pass on. */
 
     case HDR_CONNECTION:          /** \par Connection: */
     case HDR_TE:                  /** \par TE: */
@@ -1690,7 +1781,7 @@ copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, co
         break;
 
 
-/** \title OTHER headers I haven't bothered to track down yet. */
+        /** \par OTHER headers I haven't bothered to track down yet. */
 
     case HDR_AUTHORIZATION:
         /** \par WWW-Authorization:
@@ -1700,11 +1791,11 @@ copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, co
             hdr_out->addEntry(e->clone());
         } else {
             /** \note In accelerators, only forward authentication if enabled
-             * by login=PASS or login=PROXYPASS
-             * (see also below for proxy->server authentication)
+             * (see also httpFixupAuthentication for special cases)
              */
             if (orig_request->peer_login &&
                     (strcmp(orig_request->peer_login, "PASS") == 0 ||
+                     strcmp(orig_request->peer_login, "PASSTHRU") == 0 ||
                      strcmp(orig_request->peer_login, "PROXYPASS") == 0)) {
                 hdr_out->addEntry(e->clone());
             }
@@ -1739,7 +1830,7 @@ copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, co
 
     case HDR_IF_MODIFIED_SINCE:
         /** \par If-Modified-Since:
-        * append unless we added our own;
+        * append unless we added our own;
          * \note at most one client's ims header can pass through */
 
         if (!hdr_out->has(HDR_IF_MODIFIED_SINCE))
@@ -1749,12 +1840,12 @@ copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, co
 
     case HDR_MAX_FORWARDS:
         /** \par Max-Forwards:
-         * pass only on TRACE requests */
-        if (orig_request->method == METHOD_TRACE) {
-            const int hops = e->getInt();
+         * pass only on TRACE or OPTIONS requests */
+        if (orig_request->method == METHOD_TRACE || orig_request->method == METHOD_OPTIONS) {
+            const int64_t hops = e->getInt64();
 
             if (hops > 0)
-                hdr_out->putInt(HDR_MAX_FORWARDS, hops - 1);
+                hdr_out->putInt64(HDR_MAX_FORWARDS, hops - 1);
         }
 
         break;
@@ -1842,28 +1933,28 @@ HttpStateData::decideIfWeDoRanges (HttpRequest * orig_request)
 /* build request prefix and append it to a given MemBuf;
  * return the length of the prefix */
 mb_size_t
-HttpStateData::buildRequestPrefix(HttpRequest * request,
-                                  HttpRequest * orig_request,
-                                  StoreEntry * entry,
+HttpStateData::buildRequestPrefix(HttpRequest * aRequest,
+                                  HttpRequest * original_request,
+                                  StoreEntry * sentry,
                                   MemBuf * mb,
-                                  http_state_flags flags)
+                                  http_state_flags stateFlags)
 {
     const int offset = mb->size;
-    HttpVersion httpver(1, 0);
+    HttpVersion httpver(1,1);
     mb->Printf("%s %s HTTP/%d.%d\r\n",
-               RequestMethodStr(request->method),
-               request->urlpath.size() ? request->urlpath.termedBuf() : "/",
+               RequestMethodStr(aRequest->method),
+               aRequest->urlpath.size() ? aRequest->urlpath.termedBuf() : "/",
                httpver.major,httpver.minor);
     /* build and pack headers */
     {
         HttpHeader hdr(hoRequest);
         Packer p;
-        httpBuildRequestHeader(request, orig_request, entry, &hdr, flags);
+        httpBuildRequestHeader(aRequest, original_request, sentry, &hdr, stateFlags);
 
-        if (request->flags.pinned && request->flags.connection_auth)
-            request->flags.auth_sent = 1;
+        if (aRequest->flags.pinned && aRequest->flags.connection_auth)
+            aRequest->flags.auth_sent = 1;
         else if (hdr.has(HDR_AUTHORIZATION))
-            request->flags.auth_sent = 1;
+            aRequest->flags.auth_sent = 1;
 
         packerToMemInit(&p, mb);
         hdr.packInto(&p);
@@ -1939,6 +2030,7 @@ HttpStateData::sendRequest()
     }
 
     mb.init();
+    request->peer_host=_peer?_peer->host:NULL;
     buildRequestPrefix(request, orig_request, entry, &mb, flags);
     debugs(11, 6, "httpSendRequest: FD " << fd << ":\n" << mb.buf);
     comm_write_mbuf(fd, &mb, requestSender);
@@ -1974,13 +2066,8 @@ HttpStateData::doneSendingRequestBody()
     debugs(11,5, HERE << "doneSendingRequestBody: FD " << fd);
 
 #if HTTP_VIOLATIONS
-    ACLChecklist ch;
-    ch.request = HTTPMSGLOCK(request);
-
     if (Config.accessList.brokenPosts) {
-        ch.accessList = cbdataReference(Config.accessList.brokenPosts);
-        /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
-
+        ACLFilledChecklist ch(Config.accessList.brokenPosts, request, NULL);
         if (!ch.fastCheck()) {
             debugs(11, 5, "doneSendingRequestBody: didn't match brokenPosts");
             CommIoCbParams io(NULL);
@@ -2076,15 +2163,6 @@ HttpStateData::abortTransaction(const char *reason)
     deleteThis("HttpStateData::abortTransaction");
 }
 
-#if DEAD_CODE
-void
-httpBuildVersion(HttpVersion * version, unsigned int major, unsigned int minor)
-{
-    version->major = major;
-    version->minor = minor;
-}
-#endif
-
 HttpRequest *
 HttpStateData::originalRequest()
 {