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::processParsedRequest(Http::StreamPointer
&context
)
239 if (!buildHttpRequest(context
))
242 ClientHttpRequest
*http
= context
->http
;
243 HttpRequest::Pointer request
= http
->request
;
245 if (request
->header
.has(Http::HdrType::EXPECT
)) {
246 const String expect
= request
->header
.getList(Http::HdrType::EXPECT
);
247 const bool supportedExpect
= (expect
.caseCmp("100-continue") == 0);
248 if (!supportedExpect
) {
249 clientStreamNode
*node
= context
->getClientReplyContext();
250 quitAfterError(request
.getRaw());
251 // setReplyToError() requires log_uri
252 assert(http
->log_uri
);
253 clientReplyContext
*repContext
= dynamic_cast<clientReplyContext
*>(node
->data
.getRaw());
255 repContext
->setReplyToError(ERR_INVALID_REQ
, Http::scExpectationFailed
, request
->method
, http
->uri
,
256 clientConnection
->remote
, request
.getRaw(), NULL
, NULL
);
257 assert(context
->http
->out
.offset
== 0);
259 clientProcessRequestFinished(this, request
);
263 if (Config
.accessList
.forceRequestBodyContinuation
) {
264 ACLFilledChecklist
bodyContinuationCheck(Config
.accessList
.forceRequestBodyContinuation
, request
.getRaw(), NULL
);
265 bodyContinuationCheck
.al
= http
->al
;
266 bodyContinuationCheck
.syncAle(request
.getRaw(), http
->log_uri
);
267 if (bodyContinuationCheck
.fastCheck().allowed()) {
268 debugs(33, 5, "Body Continuation forced");
269 request
->forcedBodyContinuation
= true;
271 HttpReply::Pointer rep
= new HttpReply
;
272 rep
->sline
.set(Http::ProtocolVersion(), Http::scContinue
);
274 typedef UnaryMemFunT
<Http1::Server
, Http::StreamPointer
> CbDialer
;
275 const AsyncCall::Pointer cb
= asyncCall(11, 3, "Http1::Server::proceedAfterBodyContinuation", CbDialer(this, &Http1::Server::proceedAfterBodyContinuation
, Http::StreamPointer(context
)));
276 sendControlMsg(HttpControlMsg(rep
, cb
));
281 clientProcessRequest(this, parser_
, context
.getRaw());
285 Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr
)
287 ConnStateData::noteBodyConsumerAborted(ptr
);
288 stopReceiving("virgin request body consumer aborted"); // closes ASAP
292 Http::One::Server::handleReply(HttpReply
*rep
, StoreIOBuffer receivedData
)
294 // the caller guarantees that we are dealing with the current context only
295 Http::StreamPointer context
= pipeline
.front();
296 Must(context
!= nullptr);
297 const ClientHttpRequest
*http
= context
->http
;
300 // After sending Transfer-Encoding: chunked (at least), always send
301 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
302 const bool mustSendLastChunk
= http
->request
->flags
.chunkedReply
&&
303 !http
->request
->flags
.streamError
&&
304 !EBIT_TEST(http
->storeEntry()->flags
, ENTRY_BAD_LENGTH
) &&
305 !context
->startOfOutput();
306 const bool responseFinishedOrFailed
= !rep
&&
307 !receivedData
.data
&&
308 !receivedData
.length
;
309 if (responseFinishedOrFailed
&& !mustSendLastChunk
) {
310 context
->writeComplete(0);
314 if (!context
->startOfOutput()) {
315 context
->sendBody(receivedData
);
320 context
->sendStartOfMessage(rep
, receivedData
);
324 Http::One::Server::writeControlMsgAndCall(HttpReply
*rep
, AsyncCall::Pointer
&call
)
326 Http::StreamPointer context
= pipeline
.front();
327 Must(context
!= nullptr);
329 // Ignore this late control message if we have started sending a
330 // reply to the user already (e.g., after an error).
331 if (context
->reply
) {
332 debugs(11, 2, "drop 1xx made late by " << context
->reply
);
336 const ClientHttpRequest
*http
= context
->http
;
338 // apply selected clientReplyContext::buildReplyHeader() mods
339 // it is not clear what headers are required for control messages
340 rep
->header
.removeHopByHopEntries();
341 // paranoid: ContentLengthInterpreter has cleaned non-generated replies
342 rep
->removeIrrelevantContentLength();
343 rep
->header
.putStr(Http::HdrType::CONNECTION
, "keep-alive");
344 httpHdrMangleList(&rep
->header
, http
->request
, http
->al
, ROR_REPLY
);
346 MemBuf
*mb
= rep
->pack();
348 debugs(11, 2, "HTTP Client " << clientConnection
);
349 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb
->buf
<< "\n----------");
351 Comm::Write(clientConnection
, mb
, call
);
358 Http::NewServer(MasterXactionPointer
&xact
)
360 return new Http1::Server(xact
, false);
364 Https::NewServer(MasterXactionPointer
&xact
)
366 return new Http1::Server(xact
, true);