/*
- * 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
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));
{
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
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()");
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;
}
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);
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 {
}
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)
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();
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();
} 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()));
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
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);
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;
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
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());
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
}
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);
+ }
+}
+