2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 33 Client-side Routines */
12 #include "acl/FilledChecklist.h"
13 #include "client_side.h"
14 #include "client_side_reply.h"
15 #include "client_side_request.h"
16 #include "comm/Write.h"
17 #include "http/one/RequestParser.h"
18 #include "http/Stream.h"
19 #include "HttpHeaderTools.h"
20 #include "profiler/Profiler.h"
21 #include "servers/Http1Server.h"
22 #include "SquidConfig.h"
25 CBDATA_NAMESPACED_CLASS_INIT(Http1
, Server
);
27 Http::One::Server::Server(const MasterXaction::Pointer
&xact
, bool beHttpsServer
):
28 AsyncJob("Http1::Server"),
30 isHttpsServer(beHttpsServer
)
35 Http::One::Server::idleTimeout() const
37 return Config
.Timeout
.clientIdlePconn
;
41 Http::One::Server::start()
43 ConnStateData::start();
45 // XXX: Until we create an HttpsServer class, use this hack to allow old
46 // client_side.cc code to manipulate ConnStateData object directly
52 typedef CommCbMemFunT
<Server
, CommTimeoutCbParams
> TimeoutDialer
;
53 AsyncCall::Pointer timeoutCall
= JobCallback(33, 5,
54 TimeoutDialer
, this, Http1::Server::requestTimeout
);
55 commSetConnTimeout(clientConnection
, Config
.Timeout
.request_start_timeout
, timeoutCall
);
60 Http::One::Server::noteMoreBodySpaceAvailable(BodyPipe::Pointer
)
62 if (!handleRequestBodyData())
65 // too late to read more body
66 if (!isOpen() || stoppedReceiving())
73 Http::One::Server::parseOneRequest()
75 PROF_start(HttpServer_parseOneRequest
);
77 // reset because the protocol may have changed if this is the first request
78 // and because we never bypass parsing failures of N+1st same-proto request
79 preservingClientData_
= shouldPreserveClientData();
81 // parser is incremental. Generate new parser state if we,
82 // a) do not have one already
83 // b) have completed the previous request parsing already
84 if (!parser_
|| !parser_
->needsMoreData())
85 parser_
= new Http1::RequestParser(preservingClientData_
);
88 Http::Stream
*context
= parseHttpRequest(parser_
);
90 PROF_stop(HttpServer_parseOneRequest
);
94 void clientProcessRequestFinished(ConnStateData
*conn
, const HttpRequest::Pointer
&request
);
95 bool clientTunnelOnError(ConnStateData
*conn
, Http::StreamPointer
&context
, HttpRequest::Pointer
&request
, const HttpRequestMethod
& method
, err_type requestError
);
98 Http::One::Server::buildHttpRequest(Http::StreamPointer
&context
)
100 HttpRequest::Pointer request
;
101 ClientHttpRequest
*http
= context
->http
;
102 if (context
->flags
.parsed_ok
== 0) {
103 debugs(33, 2, "Invalid Request");
104 // determine which error page templates to use for specific parsing errors
105 err_type errPage
= ERR_INVALID_REQ
;
106 switch (parser_
->parseStatusCode
) {
107 case Http::scRequestHeaderFieldsTooLarge
:
108 // fall through to next case
109 case Http::scUriTooLong
:
110 errPage
= ERR_TOO_BIG
;
112 case Http::scMethodNotAllowed
:
113 errPage
= ERR_UNSUP_REQ
;
115 case Http::scHttpVersionNotSupported
:
116 errPage
= ERR_UNSUP_HTTPVERSION
;
119 if (parser_
->method() == METHOD_NONE
|| parser_
->requestUri().length() == 0)
120 // no method or url parsed, probably is wrong protocol
121 errPage
= ERR_PROTOCOL_UNKNOWN
;
122 // else use default ERR_INVALID_REQ set above.
125 // setReplyToError() requires log_uri
126 // must be already initialized via ConnStateData::abortRequestParsing()
127 assert(http
->log_uri
);
129 const char * requestErrorBytes
= inBuf
.c_str();
130 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), errPage
)) {
131 setReplyError(context
, request
, parser_
->method(), errPage
, parser_
->parseStatusCode
, requestErrorBytes
);
132 // HttpRequest object not build yet, there is no reason to call
133 // clientProcessRequestFinished method
139 // TODO: move URL parse into Http Parser and INVALID_URL into the above parse error handling
140 MasterXaction::Pointer mx
= new MasterXaction(XactionInitiator::initClient
);
141 mx
->tcpClient
= clientConnection
;
142 request
= HttpRequest::FromUrlXXX(http
->uri
, mx
, parser_
->method());
144 debugs(33, 5, "Invalid URL: " << http
->uri
);
145 // setReplyToError() requires log_uri
146 http
->setLogUriToRawUri(http
->uri
, parser_
->method());
148 const char * requestErrorBytes
= inBuf
.c_str();
149 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), ERR_INVALID_URL
)) {
150 setReplyError(context
, request
, parser_
->method(), ERR_INVALID_URL
, Http::scBadRequest
, requestErrorBytes
);
151 // HttpRequest object not build yet, there is no reason to call
152 // clientProcessRequestFinished method
157 /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */
158 /* We currently only support 0.9, 1.0, 1.1 properly */
159 /* TODO: move HTTP-specific processing into servers/HttpServer and such */
160 if ( (parser_
->messageProtocol().major
== 0 && parser_
->messageProtocol().minor
!= 9) ||
161 (parser_
->messageProtocol().major
> 1) ) {
163 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << parser_
->messageProtocol());
164 // setReplyToError() requires log_uri
165 http
->setLogUriToRawUri(http
->uri
, parser_
->method());
167 const char * requestErrorBytes
= NULL
; //HttpParserHdrBuf(parser_);
168 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), ERR_UNSUP_HTTPVERSION
)) {
169 setReplyError(context
, request
, parser_
->method(), ERR_UNSUP_HTTPVERSION
, Http::scHttpVersionNotSupported
, requestErrorBytes
);
170 clientProcessRequestFinished(this, request
);
175 /* compile headers */
176 if (parser_
->messageProtocol().major
>= 1 && !request
->parseHeader(*parser_
.getRaw())) {
177 debugs(33, 5, "Failed to parse request headers:\n" << parser_
->mimeHeader());
178 // setReplyToError() requires log_uri
179 http
->setLogUriToRawUri(http
->uri
, parser_
->method());
180 const char * requestErrorBytes
= NULL
; //HttpParserHdrBuf(parser_);
181 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), ERR_INVALID_REQ
)) {
182 setReplyError(context
, request
, parser_
->method(), ERR_INVALID_REQ
, Http::scBadRequest
, requestErrorBytes
);
183 clientProcessRequestFinished(this, request
);
188 // when absolute-URI is provided Host header should be ignored. However
189 // some code still uses Host directly so normalize it using the previously
190 // sanitized URL authority value.
191 // For now preserve the case where Host is completely absent. That matters.
192 if (const auto x
= request
->header
.delById(Http::HOST
)) {
193 debugs(33, 5, "normalize " << x
<< " Host header using " << request
->url
.authority());
194 SBuf
tmp(request
->url
.authority());
195 request
->header
.putStr(Http::HOST
, tmp
.c_str());
198 // TODO: We fill request notes here until we find a way to verify whether
199 // no ACL checking is performed before ClientHttpRequest::doCallouts().
201 assert(!request
->hasNotes());
202 request
->notes()->append(notes().getRaw());
205 http
->initRequest(request
.getRaw());
211 Http::One::Server::setReplyError(Http::StreamPointer
&context
, HttpRequest::Pointer
&request
, const HttpRequestMethod
& method
, err_type requestError
, Http::StatusCode errStatusCode
, const char *requestErrorBytes
)
213 quitAfterError(request
.getRaw());
214 if (!context
->connRegistered()) {
215 debugs(33, 2, "Client stream deregister it self, nothing to do");
216 clientConnection
->close();
219 clientStreamNode
*node
= context
->getClientReplyContext();
220 clientReplyContext
*repContext
= dynamic_cast<clientReplyContext
*>(node
->data
.getRaw());
223 repContext
->setReplyToError(requestError
, errStatusCode
, method
, context
->http
->uri
, clientConnection
->remote
, nullptr, requestErrorBytes
, nullptr);
225 assert(context
->http
->out
.offset
== 0);
230 Http::One::Server::proceedAfterBodyContinuation(Http::StreamPointer context
)
232 debugs(33, 5, "Body Continuation written");
233 clientProcessRequest(this, parser_
, context
.getRaw());
237 Http::One::Server::pipelinePrefetchMax() const
239 const auto context
= pipeline
.back();
240 const auto request
= (context
&& context
->http
) ? context
->http
->request
: nullptr;
241 if (request
&& request
->header
.has(Http::HdrType::UPGRADE
))
244 return ConnStateData::pipelinePrefetchMax();
248 Http::One::Server::processParsedRequest(Http::StreamPointer
&context
)
250 if (!buildHttpRequest(context
))
253 ClientHttpRequest
*http
= context
->http
;
254 HttpRequest::Pointer request
= http
->request
;
256 if (request
->header
.has(Http::HdrType::EXPECT
)) {
257 const String expect
= request
->header
.getList(Http::HdrType::EXPECT
);
258 const bool supportedExpect
= (expect
.caseCmp("100-continue") == 0);
259 if (!supportedExpect
) {
260 clientStreamNode
*node
= context
->getClientReplyContext();
261 quitAfterError(request
.getRaw());
262 // setReplyToError() requires log_uri
263 assert(http
->log_uri
);
264 clientReplyContext
*repContext
= dynamic_cast<clientReplyContext
*>(node
->data
.getRaw());
266 repContext
->setReplyToError(ERR_INVALID_REQ
, Http::scExpectationFailed
, request
->method
, http
->uri
,
267 clientConnection
->remote
, request
.getRaw(), NULL
, NULL
);
268 assert(context
->http
->out
.offset
== 0);
270 clientProcessRequestFinished(this, request
);
274 if (Config
.accessList
.forceRequestBodyContinuation
) {
275 ACLFilledChecklist
bodyContinuationCheck(Config
.accessList
.forceRequestBodyContinuation
, request
.getRaw(), NULL
);
276 bodyContinuationCheck
.al
= http
->al
;
277 bodyContinuationCheck
.syncAle(request
.getRaw(), http
->log_uri
);
278 if (bodyContinuationCheck
.fastCheck().allowed()) {
279 debugs(33, 5, "Body Continuation forced");
280 request
->forcedBodyContinuation
= true;
282 HttpReply::Pointer rep
= new HttpReply
;
283 rep
->sline
.set(Http::ProtocolVersion(), Http::scContinue
);
285 typedef UnaryMemFunT
<Http1::Server
, Http::StreamPointer
> CbDialer
;
286 const AsyncCall::Pointer cb
= asyncCall(11, 3, "Http1::Server::proceedAfterBodyContinuation", CbDialer(this, &Http1::Server::proceedAfterBodyContinuation
, Http::StreamPointer(context
)));
287 sendControlMsg(HttpControlMsg(rep
, cb
));
292 clientProcessRequest(this, parser_
, context
.getRaw());
296 Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr
)
298 ConnStateData::noteBodyConsumerAborted(ptr
);
299 stopReceiving("virgin request body consumer aborted"); // closes ASAP
303 Http::One::Server::handleReply(HttpReply
*rep
, StoreIOBuffer receivedData
)
305 // the caller guarantees that we are dealing with the current context only
306 Http::StreamPointer context
= pipeline
.front();
307 Must(context
!= nullptr);
308 const ClientHttpRequest
*http
= context
->http
;
311 // After sending Transfer-Encoding: chunked (at least), always send
312 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
313 const bool mustSendLastChunk
= http
->request
->flags
.chunkedReply
&&
314 !http
->request
->flags
.streamError
&&
315 !EBIT_TEST(http
->storeEntry()->flags
, ENTRY_BAD_LENGTH
) &&
316 !context
->startOfOutput();
317 const bool responseFinishedOrFailed
= !rep
&&
318 !receivedData
.data
&&
319 !receivedData
.length
;
320 if (responseFinishedOrFailed
&& !mustSendLastChunk
) {
321 context
->writeComplete(0);
325 if (!context
->startOfOutput()) {
326 context
->sendBody(receivedData
);
331 context
->sendStartOfMessage(rep
, receivedData
);
335 Http::One::Server::writeControlMsgAndCall(HttpReply
*rep
, AsyncCall::Pointer
&call
)
337 Http::StreamPointer context
= pipeline
.front();
338 Must(context
!= nullptr);
340 // Ignore this late control message if we have started sending a
341 // reply to the user already (e.g., after an error).
342 if (context
->reply
) {
343 debugs(11, 2, "drop 1xx made late by " << context
->reply
);
347 const ClientHttpRequest
*http
= context
->http
;
349 // remember Upgrade header; removeHopByHopEntries() will remove it
350 String upgradeHeader
;
351 const auto switching
= (rep
->sline
.status() == Http::scSwitchingProtocols
);
353 upgradeHeader
= rep
->header
.getList(Http::HdrType::UPGRADE
);
355 // apply selected clientReplyContext::buildReplyHeader() mods
356 // it is not clear what headers are required for control messages
357 rep
->header
.removeHopByHopEntries();
358 // paranoid: ContentLengthInterpreter has cleaned non-generated replies
359 rep
->removeIrrelevantContentLength();
361 if (switching
&& /* paranoid: */ upgradeHeader
.size()) {
362 rep
->header
.putStr(Http::HdrType::UPGRADE
, upgradeHeader
.termedBuf());
363 rep
->header
.putStr(Http::HdrType::CONNECTION
, "upgrade, keep-alive");
365 rep
->header
.putStr(Http::HdrType::CONNECTION
, "keep-alive");
368 httpHdrMangleList(&rep
->header
, http
->request
, http
->al
, ROR_REPLY
);
370 MemBuf
*mb
= rep
->pack();
372 debugs(11, 2, "HTTP Client " << clientConnection
);
373 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb
->buf
<< "\n----------");
375 Comm::Write(clientConnection
, mb
, call
);
381 void switchToTunnel(HttpRequest
*request
, const Comm::ConnectionPointer
&clientConn
, const Comm::ConnectionPointer
&srvConn
, const SBuf
&preReadServerData
);
384 Http::One::Server::noteTakeServerConnectionControl(ServerConnectionContext server
)
386 const auto context
= pipeline
.front();
388 const auto http
= context
->http
;
390 assert(http
->request
);
395 switchToTunnel(http
->request
, clientConnection
,
396 server
.connection(), server
.preReadServerBytes
);
400 Http::NewServer(MasterXactionPointer
&xact
)
402 return new Http1::Server(xact
, false);
406 Https::NewServer(MasterXactionPointer
&xact
)
408 return new Http1::Server(xact
, true);