/*
- * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "comm/Read.h"
#include "comm/Write.h"
#include "CommRead.h"
-#include "err_detail_type.h"
+#include "error/Detail.h"
#include "errorpage.h"
#include "fd.h"
#include "fde.h"
#include "DelayPools.h"
#endif
-#define SQUID_ENTER_THROWING_CODE() try {
-#define SQUID_EXIT_THROWING_CODE(status) \
- status = true; \
- } \
- catch (const std::exception &e) { \
- debugs (11, 1, "Exception error:" << e.what()); \
- status = false; \
- }
-
CBDATA_CLASS_INIT(HttpStateData);
static const char *const crlf = "\r\n";
hp = new Http1::ResponseParser;
bool parsedOk = hp->parse(inBuf);
+ // remember the actual received status-code before returning on errors,
+ // overwriting any previously stored value from earlier forwarding attempts
+ request->hier.peer_reply_status = hp->messageStatus(); // may still be scNone
// sync the buffers after parsing.
inBuf = hp->remaining();
// XXX: RFC 7230 indicates we MAY ignore the reason phrase,
// and use an empty string on unknown status.
// We do that now to avoid performance regression from using SBuf::c_str()
- newrep->sline.set(Http::ProtocolVersion(1,1), hp->messageStatus() /* , hp->reasonPhrase() */);
- newrep->sline.protocol = newrep->sline.version.protocol = hp->messageProtocol().protocol;
- newrep->sline.version.major = hp->messageProtocol().major;
- newrep->sline.version.minor = hp->messageProtocol().minor;
+ newrep->sline.set(hp->messageProtocol(), hp->messageStatus() /* , hp->reasonPhrase() */);
// parse headers
if (!newrep->parseHeader(*hp)) {
- // XXX: when Http::ProtocolVersion is a function, remove this hack. just set with messageProtocol()
- newrep->sline.set(Http::ProtocolVersion(), Http::scInvalidHeader);
- newrep->sline.version.protocol = hp->messageProtocol().protocol;
- newrep->sline.version.major = hp->messageProtocol().major;
- newrep->sline.version.minor = hp->messageProtocol().minor;
+ newrep->sline.set(hp->messageProtocol(), Http::scInvalidHeader);
debugs(11, 2, "error parsing response headers mime block");
}
newrep->removeStaleWarnings();
- if (newrep->sline.protocol == AnyP::PROTO_HTTP && Http::Is1xx(newrep->sline.status())) {
+ if (newrep->sline.version.protocol == AnyP::PROTO_HTTP && Http::Is1xx(newrep->sline.status())) {
handle1xx(newrep);
ctx_exit(ctx);
return;
}
flags.chunked = false;
- if (newrep->sline.protocol == AnyP::PROTO_HTTP && newrep->header.chunked()) {
+ if (newrep->sline.version.protocol == AnyP::PROTO_HTTP && newrep->header.chunked()) {
flags.chunked = true;
httpChunkDecoder = new Http1::TeChunkedParser;
}
processSurrogateControl (vrep);
- request->hier.peer_reply_status = newrep->sline.status();
-
ctx_exit(ctx);
}
return COMPLETE_NONPERSISTENT_MSG;
/** \par
- * If we didn't send a keep-alive request header, then this
+ * If we sent a Connection:close request header, then this
* can not be a persistent connection.
*/
if (!flags.keepalive)
return COMPLETE_NONPERSISTENT_MSG;
+ /** \par
+ * If we banned reuse, then this cannot be a persistent connection.
+ */
+ if (flags.forceClose)
+ return COMPLETE_NONPERSISTENT_MSG;
+
/** \par
* If we haven't sent the whole request then this can not be a persistent
* connection.
Must(!flags.headers_parsed);
}
+ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+ abortTransaction("store entry aborted while we were waiting for processReply()");
+ return;
+ }
+
if (!flags.headers_parsed) { // have not parsed headers yet?
PROF_start(HttpStateData_processReplyHeader);
processReplyHeader();
} else if (vrep->header.conflictingContentLength()) {
fwd->dontRetry(true);
error = ERR_INVALID_RESP;
+ } else if (vrep->header.unsupportedTe()) {
+ fwd->dontRetry(true);
+ error = ERR_INVALID_RESP;
} else {
return true; // done parsing, got reply, and no error
}
bool
HttpStateData::decodeAndWriteReplyBody()
{
- const char *data = NULL;
- int len;
- bool wasThereAnException = false;
assert(flags.chunked);
assert(httpChunkDecoder);
- SQUID_ENTER_THROWING_CODE();
- MemBuf decodedData;
- decodedData.init();
- httpChunkDecoder->setPayloadBuffer(&decodedData);
- const bool doneParsing = httpChunkDecoder->parse(inBuf);
- inBuf = httpChunkDecoder->remaining(); // sync buffers after parse
- len = decodedData.contentSize();
- data=decodedData.content();
- addVirginReplyBody(data, len);
- if (doneParsing) {
- lastChunk = 1;
- flags.do_next_read = false;
+ try {
+ MemBuf decodedData;
+ decodedData.init();
+ httpChunkDecoder->setPayloadBuffer(&decodedData);
+ const bool doneParsing = httpChunkDecoder->parse(inBuf);
+ inBuf = httpChunkDecoder->remaining(); // sync buffers after parse
+ addVirginReplyBody(decodedData.content(), decodedData.contentSize());
+ if (doneParsing) {
+ lastChunk = 1;
+ flags.do_next_read = false;
+ }
+ return true;
+ }
+ catch (...) {
+ debugs (11, 2, "de-chunking failure: " << CurrentException);
}
- SQUID_EXIT_THROWING_CODE(wasThereAnException);
- return wasThereAnException;
+ return false;
}
/**
delete cc;
}
- // Always send Connection because HTTP/1.0 servers need explicit "keep-alive"
- // while HTTP/1.1 servers need explicit "close", and we do not always know
- // the server expectations.
- hdr_out->putStr(Http::HdrType::CONNECTION, flags.keepalive ? "keep-alive" : "close");
+ // Always send Connection because HTTP/1.0 servers need explicit
+ // "keep-alive", HTTP/1.1 servers need explicit "close", Upgrade recipients
+ // need bare "upgrade", and we do not always know the server expectations.
+ if (!hdr_out->has(Http::HdrType::CONNECTION)) // forwardUpgrade() may add it
+ hdr_out->putStr(Http::HdrType::CONNECTION, flags.keepalive ? "keep-alive" : "close");
/* append Front-End-Https */
if (flags.front_end_https) {
* regardless of the security implications.
*/
hdrOut.putStr(Http::HdrType::CONNECTION, "upgrade");
+
+ // Connection:close and Connection:keepalive confuse some Upgrade
+ // recipients, so we do not send those headers. Our Upgrade request
+ // implicitly offers connection persistency per HTTP/1.1 defaults.
+ // Update the keepalive flag to reflect that offer.
+ // * If the server upgrades, then we would not be talking HTTP past the
+ // HTTP 101 control message, and HTTP persistence would be irrelevant.
+ // * Otherwise, our request will contradict onoff.server_pconns=off or
+ // other no-keepalive conditions (if any). We compensate by copying
+ // the original no-keepalive decision now and honoring it later.
+ flags.forceClose = !flags.keepalive;
+ flags.keepalive = true; // should already be true in most cases
}
}
/* build and pack headers */
{
HttpHeader hdr(hoRequest);
- forwardUpgrade(hdr);
+ forwardUpgrade(hdr); // before httpBuildRequestHeader() for CONNECTION
httpBuildRequestHeader(request.getRaw(), entry, fwd->al, &hdr, flags);
if (request->flags.pinned && request->flags.connectionAuth)
// should not matter because either client-side will provide its own or
// there will be no response at all (e.g., if the the client has left).
const auto err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, fwd->request, fwd->al);
- err->detailError(ERR_DETAIL_SRV_REQMOD_REQ_BODY);
+ static const auto d = MakeNamedErrorDetail("SRV_REQMOD_REQ_BODY");
+ err->detailError(d);
fwd->fail(err);
}