]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/adaptation/icap/ModXact.cc
Allow upgrading from HTTP/1.1 to other protocols (#481)
[thirdparty/squid.git] / src / adaptation / icap / ModXact.cc
index 9ed17fb7989f6041c8256f4ecc2f2db14ec97f1c..a73f32c547e39599c667992b168fad22f706aa23 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
 #include "comm.h"
 #include "comm/Connection.h"
 #include "err_detail_type.h"
-#include "http/one/TeChunkedParser.h"
+#include "http/ContentLengthInterpreter.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
+#include "MasterXaction.h"
+#include "parser/Tokenizer.h"
+#include "sbuf/Stream.h"
 #include "SquidTime.h"
-#include "URL.h"
 
 // flow and terminology:
 //     HTTP| --> receive --> encode --> write --> |network
@@ -42,6 +44,8 @@ CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, ModXactLauncher);
 
 static const size_t TheBackupLimit = BodyPipe::MaxCapacity;
 
+const SBuf Adaptation::Icap::ChunkExtensionValueParser::UseOriginalBodyName("use-original-body");
+
 Adaptation::Icap::ModXact::State::State()
 {
     memset(this, 0, sizeof(*this));
@@ -395,7 +399,7 @@ bool Adaptation::Icap::ModXact::virginBodyEndReached(const Adaptation::Icap::Vir
 {
     return
         !act.active() || // did all (assuming it was originally planned)
-        !virgin.body_pipe->expectMoreAfter(act.offset()); // wont have more
+        !virgin.body_pipe->expectMoreAfter(act.offset()); // will not have more
 }
 
 // the size of buffered virgin body data available for the specified activity
@@ -736,7 +740,7 @@ void Adaptation::Icap::ModXact::maybeAllocateHttpMsg()
         setOutcome(service().cfg().method == ICAP::methodReqmod ?
                    xoSatisfied : xoModified);
     } else if (gotEncapsulated("req-hdr")) {
-        adapted.setHeader(new HttpRequest);
+        adapted.setHeader(new HttpRequest(virginRequest().masterXaction));
         setOutcome(xoModified);
     } else
         throw TexcHere("Neither res-hdr nor req-hdr in maybeAllocateHttpMsg()");
@@ -797,7 +801,8 @@ void Adaptation::Icap::ModXact::parseIcapHead()
         trailerParser = new TrailerParser;
     }
 
-    if (httpHeaderHasConnDir(&icapReply->header, "close")) {
+    static SBuf close("close", 5);
+    if (httpHeaderHasConnDir(&icapReply->header, close)) {
         debugs(93, 5, HERE << "found connection close");
         reuseConnection = false;
     }
@@ -969,12 +974,12 @@ void Adaptation::Icap::ModXact::prepEchoing()
     Must(!adapted.header);
     {
         Http::MessagePointer newHead;
-        if (dynamic_cast<const HttpRequest*>(oldHead)) {
-            newHead = new HttpRequest;
+        if (const HttpRequest *r = dynamic_cast<const HttpRequest*>(oldHead)) {
+            newHead = new HttpRequest(r->masterXaction);
         } else if (dynamic_cast<const HttpReply*>(oldHead)) {
             newHead = new HttpReply;
         }
-        Must(newHead != NULL);
+        Must(newHead);
 
         newHead->inheritProperties(oldHead);
 
@@ -1143,6 +1148,7 @@ void Adaptation::Icap::ModXact::decideOnParsingBody()
         state.parsing = State::psBody;
         replyHttpBodySize = 0;
         bodyParser = new Http1::TeChunkedParser;
+        bodyParser->parseExtensionValuesWith(&extensionParser);
         makeAdaptedBodyPipe("adapted response from the ICAP server");
         Must(state.sending == State::sendingAdapted);
     } else {
@@ -1180,8 +1186,8 @@ void Adaptation::Icap::ModXact::parseBody()
     }
 
     if (parsed) {
-        if (state.readyForUob && bodyParser->useOriginBody >= 0)
-            prepPartialBodyEchoing(static_cast<uint64_t>(bodyParser->useOriginBody));
+        if (state.readyForUob && extensionParser.sawUseOriginalBody())
+            prepPartialBodyEchoing(extensionParser.useOriginalBody());
         else
             stopSending(true); // the parser succeeds only if all parsed data fits
         if (trailerParser)
@@ -1332,11 +1338,8 @@ void Adaptation::Icap::ModXact::finalizeLogInfo()
     al.adapted_request = adapted_request_;
     HTTPMSGLOCK(al.adapted_request);
 
-    if (adapted_reply_) {
-        al.reply = adapted_reply_;
-        HTTPMSGLOCK(al.reply);
-    } else
-        al.reply = NULL;
+    // XXX: This reply (and other ALE members!) may have been needed earlier.
+    al.reply = adapted_reply_;
 
     if (h->rfc931.size())
         al.cache.rfc931 = h->rfc931.termedBuf();
@@ -1371,12 +1374,6 @@ void Adaptation::Icap::ModXact::finalizeLogInfo()
         if (replyHttpBodySize >= 0)
             al.cache.highOffset = replyHttpBodySize;
         //don't set al.cache.objectSize because it hasn't exist yet
-
-        MemBuf mb;
-        mb.init();
-        adapted_reply_->header.packInto(&mb);
-        al.headers.reply = xstrdup(mb.buf);
-        mb.clean();
     }
     prepareLogWithRequestDetails(adapted_request_, alep);
     Xaction::finalizeLogInfo();
@@ -1411,7 +1408,7 @@ void Adaptation::Icap::ModXact::makeRequestHeaders(MemBuf &buf)
     } else if (request->extacl_user.size() > 0 && request->extacl_passwd.size() > 0) {
         struct base64_encode_ctx ctx;
         base64_encode_init(&ctx);
-        uint8_t base64buf[base64_encode_len(MAX_LOGIN_SZ)];
+        char base64buf[base64_encode_len(MAX_LOGIN_SZ)];
         size_t resultLen = base64_encode_update(&ctx, base64buf, request->extacl_user.size(), reinterpret_cast<const uint8_t*>(request->extacl_user.rawBuf()));
         resultLen += base64_encode_update(&ctx, base64buf+resultLen, 1, reinterpret_cast<const uint8_t*>(":"));
         resultLen += base64_encode_update(&ctx, base64buf+resultLen, request->extacl_passwd.size(), reinterpret_cast<const uint8_t*>(request->extacl_passwd.rawBuf()));
@@ -1536,11 +1533,8 @@ void Adaptation::Icap::ModXact::makeAllowHeader(MemBuf &buf)
     const bool allow204 = allow204in || allow204out;
     const bool allow206 = allow206in || allow206out;
 
-    if (!allow204 && !allow206 && !allowTrailers)
-        return; // nothing to do
-
-    if (virginBody.expected()) // if there is a virgin body, plan to send it
-        virginBodySending.plan();
+    if ((allow204 || allow206) && virginBody.expected())
+        virginBodySending.plan(); // if there is a virgin body, plan to send it
 
     // writing Preview:...   means we will honor 204 inside preview
     // writing Allow/204     means we will honor 204 outside preview
@@ -1573,7 +1567,7 @@ void Adaptation::Icap::ModXact::makeUsernameHeader(const HttpRequest *request, M
 
     if (value) {
         if (TheConfig.client_username_encode) {
-            uint8_t base64buf[base64_encode_len(MAX_LOGIN_SZ)];
+            char base64buf[base64_encode_len(MAX_LOGIN_SZ)];
             size_t resultLen = base64_encode_update(&ctx, base64buf, strlen(value), reinterpret_cast<const uint8_t*>(value));
             resultLen += base64_encode_final(&ctx, base64buf+resultLen);
             buf.appendf("%s: %.*s\r\n", TheConfig.client_username_header, (int)resultLen, base64buf);
@@ -1593,7 +1587,7 @@ Adaptation::Icap::ModXact::encapsulateHead(MemBuf &icapBuf, const char *section,
     Http::MessagePointer headClone;
 
     if (const HttpRequest* old_request = dynamic_cast<const HttpRequest*>(head)) {
-        HttpRequest::Pointer new_request(new HttpRequest);
+        HttpRequest::Pointer new_request(new HttpRequest(old_request->masterXaction));
         // copy the request-line details
         new_request->method = old_request->method;
         new_request->url = old_request->url;
@@ -1604,12 +1598,11 @@ Adaptation::Icap::ModXact::encapsulateHead(MemBuf &icapBuf, const char *section,
         new_reply->sline = old_reply->sline;
         headClone = new_reply.getRaw();
     }
-    Must(headClone != NULL);
+    Must(headClone);
     headClone->inheritProperties(head);
 
     HttpHeaderPos pos = HttpHeaderInitPos;
-    HttpHeaderEntry* p_head_entry = NULL;
-    while (NULL != (p_head_entry = head->header.getEntry(&pos)) )
+    while (HttpHeaderEntry* p_head_entry = head->header.getEntry(&pos))
         headClone->header.addEntry(p_head_entry->clone());
 
     // end cloning
@@ -1618,6 +1611,13 @@ Adaptation::Icap::ModXact::encapsulateHead(MemBuf &icapBuf, const char *section,
     headClone->header.delById(Http::HdrType::PROXY_AUTHENTICATE);
     headClone->header.removeHopByHopEntries();
 
+    // TODO: modify HttpHeader::removeHopByHopEntries to accept a list of
+    // excluded hop-by-hop headers
+    if (head->header.has(Http::HdrType::UPGRADE)) {
+        const auto upgrade = head->header.getList(Http::HdrType::UPGRADE);
+        headClone->header.putStr(Http::HdrType::UPGRADE, upgrade.termedBuf());
+    }
+
     // pack polished HTTP header
     packHead(httpBuf, headClone.getRaw());
 
@@ -1809,8 +1809,8 @@ void Adaptation::Icap::ModXact::fillDoneStatus(MemBuf &buf) const
 
 bool Adaptation::Icap::ModXact::gotEncapsulated(const char *section) const
 {
-    return icapReply->header.getByNameListMember("Encapsulated",
-            section, ',').size() > 0;
+    return !icapReply->header.getByNameListMember("Encapsulated",
+            section, ',').isEmpty();
 }
 
 // calculate whether there is a virgin HTTP body and
@@ -2063,9 +2063,24 @@ void Adaptation::Icap::ModXactLauncher::updateHistory(bool doStart)
 }
 
 bool Adaptation::Icap::TrailerParser::parse(const char *buf, int len, int atEnd, Http::StatusCode *error) {
-    const int parsed = trailer.parse(buf, len, atEnd, hdr_sz);
+    Http::ContentLengthInterpreter clen;
+    // RFC 7230 section 4.1.2: MUST NOT generate a trailer that contains
+    // a field necessary for message framing (e.g., Transfer-Encoding and Content-Length)
+    clen.applyTrailerRules();
+    const int parsed = trailer.parse(buf, len, atEnd, hdr_sz, clen);
     if (parsed < 0)
         *error = Http::scInvalidHeader; // TODO: should we add a new Http::scInvalidTrailer?
     return parsed > 0;
 }
 
+void
+Adaptation::Icap::ChunkExtensionValueParser::parse(Tokenizer &tok, const SBuf &extName)
+{
+    if (extName == UseOriginalBodyName) {
+        useOriginalBody_ = tok.udec64("use-original-body");
+        assert(useOriginalBody_ >= 0);
+    } else {
+        Ignore(tok, extName);
+    }
+}
+