2 * Copyright (C) 1996-2017 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();
46 // XXX: Until we create an HttpsServer class, use this hack to allow old
47 // client_side.cc code to manipulate ConnStateData object directly
54 typedef CommCbMemFunT
<Server
, CommTimeoutCbParams
> TimeoutDialer
;
55 AsyncCall::Pointer timeoutCall
= JobCallback(33, 5,
56 TimeoutDialer
, this, Http1::Server::requestTimeout
);
57 commSetConnTimeout(clientConnection
, Config
.Timeout
.request_start_timeout
, timeoutCall
);
62 Http::One::Server::noteMoreBodySpaceAvailable(BodyPipe::Pointer
)
64 if (!handleRequestBodyData())
67 // too late to read more body
68 if (!isOpen() || stoppedReceiving())
75 Http::One::Server::parseOneRequest()
77 PROF_start(HttpServer_parseOneRequest
);
79 // parser is incremental. Generate new parser state if we,
80 // a) dont have one already
81 // b) have completed the previous request parsing already
82 if (!parser_
|| !parser_
->needsMoreData())
83 parser_
= new Http1::RequestParser(mayTunnelUnsupportedProto());
86 Http::Stream
*context
= parseHttpRequest(this, parser_
);
88 PROF_stop(HttpServer_parseOneRequest
);
92 void clientProcessRequestFinished(ConnStateData
*conn
, const HttpRequest::Pointer
&request
);
93 bool clientTunnelOnError(ConnStateData
*conn
, Http::StreamPointer
&context
, HttpRequest::Pointer
&request
, const HttpRequestMethod
& method
, err_type requestError
);
96 Http::One::Server::buildHttpRequest(Http::StreamPointer
&context
)
98 HttpRequest::Pointer request
;
99 ClientHttpRequest
*http
= context
->http
;
100 if (context
->flags
.parsed_ok
== 0) {
101 debugs(33, 2, "Invalid Request");
102 // determine which error page templates to use for specific parsing errors
103 err_type errPage
= ERR_INVALID_REQ
;
104 switch (parser_
->parseStatusCode
) {
105 case Http::scRequestHeaderFieldsTooLarge
:
106 // fall through to next case
107 case Http::scUriTooLong
:
108 errPage
= ERR_TOO_BIG
;
110 case Http::scMethodNotAllowed
:
111 errPage
= ERR_UNSUP_REQ
;
113 case Http::scHttpVersionNotSupported
:
114 errPage
= ERR_UNSUP_HTTPVERSION
;
117 if (parser_
->method() == METHOD_NONE
|| parser_
->requestUri().length() == 0)
118 // no method or url parsed, probably is wrong protocol
119 errPage
= ERR_PROTOCOL_UNKNOWN
;
120 // else use default ERR_INVALID_REQ set above.
123 // setLogUri should called before repContext->setReplyToError
124 setLogUri(http
, http
->uri
, true);
125 const char * requestErrorBytes
= inBuf
.c_str();
126 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), errPage
)) {
127 setReplyError(context
, request
, parser_
->method(), errPage
, parser_
->parseStatusCode
, requestErrorBytes
);
128 // HttpRequest object not build yet, there is no reason to call
129 // clientProcessRequestFinished method
135 // TODO: move URL parse into Http Parser and INVALID_URL into the above parse error handling
136 MasterXaction::Pointer mx
= new MasterXaction(XactionInitiator::initClient
);
137 mx
->tcpClient
= clientConnection
;
138 if ((request
= HttpRequest::FromUrl(http
->uri
, mx
, parser_
->method())) == NULL
) {
139 debugs(33, 5, "Invalid URL: " << http
->uri
);
140 // setLogUri should called before repContext->setReplyToError
141 setLogUri(http
, http
->uri
, true);
143 const char * requestErrorBytes
= inBuf
.c_str();
144 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), ERR_INVALID_URL
)) {
145 setReplyError(context
, request
, parser_
->method(), ERR_INVALID_URL
, Http::scBadRequest
, requestErrorBytes
);
146 // HttpRequest object not build yet, there is no reason to call
147 // clientProcessRequestFinished method
152 /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */
153 /* We currently only support 0.9, 1.0, 1.1 properly */
154 /* TODO: move HTTP-specific processing into servers/HttpServer and such */
155 if ( (parser_
->messageProtocol().major
== 0 && parser_
->messageProtocol().minor
!= 9) ||
156 (parser_
->messageProtocol().major
> 1) ) {
158 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << parser_
->messageProtocol());
159 // setLogUri should called before repContext->setReplyToError
160 setLogUri(http
, http
->uri
, true);
162 const char * requestErrorBytes
= NULL
; //HttpParserHdrBuf(parser_);
163 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), ERR_UNSUP_HTTPVERSION
)) {
164 setReplyError(context
, request
, parser_
->method(), ERR_UNSUP_HTTPVERSION
, Http::scHttpVersionNotSupported
, requestErrorBytes
);
165 clientProcessRequestFinished(this, request
);
170 /* compile headers */
171 if (parser_
->messageProtocol().major
>= 1 && !request
->parseHeader(*parser_
.getRaw())) {
172 debugs(33, 5, "Failed to parse request headers:\n" << parser_
->mimeHeader());
173 // setLogUri should called before repContext->setReplyToError
174 setLogUri(http
, http
->uri
, true);
175 const char * requestErrorBytes
= NULL
; //HttpParserHdrBuf(parser_);
176 if (!clientTunnelOnError(this, context
, request
, parser_
->method(), ERR_INVALID_REQ
)) {
177 setReplyError(context
, request
, parser_
->method(), ERR_INVALID_REQ
, Http::scBadRequest
, requestErrorBytes
);
178 clientProcessRequestFinished(this, request
);
183 // when absolute-URI is provided Host header should be ignored. However
184 // some code still uses Host directly so normalize it using the previously
185 // sanitized URL authority value.
186 // For now preserve the case where Host is completely absent. That matters.
187 if (const auto x
= request
->header
.delById(Http::HOST
)) {
188 debugs(33, 5, "normalize " << x
<< " Host header using " << request
->url
.authority());
189 SBuf
tmp(request
->url
.authority());
190 request
->header
.putStr(Http::HOST
, tmp
.c_str());
193 // TODO: We fill request notes here until we find a way to verify whether
194 // no ACL checking is performed before ClientHttpRequest::doCallouts().
196 assert(!request
->hasNotes());
197 request
->notes()->append(notes().getRaw());
200 http
->request
= request
.getRaw();
201 HTTPMSGLOCK(http
->request
);
207 Http::One::Server::setReplyError(Http::StreamPointer
&context
, HttpRequest::Pointer
&request
, const HttpRequestMethod
& method
, err_type requestError
, Http::StatusCode errStatusCode
, const char *requestErrorBytes
)
209 quitAfterError(request
.getRaw());
210 if (!context
->connRegistered()) {
211 debugs(33, 2, "Client stream deregister it self, nothing to do");
212 clientConnection
->close();
215 clientStreamNode
*node
= context
->getClientReplyContext();
216 clientReplyContext
*repContext
= dynamic_cast<clientReplyContext
*>(node
->data
.getRaw());
219 repContext
->setReplyToError(requestError
, errStatusCode
, method
, context
->http
->uri
, clientConnection
->remote
, nullptr, requestErrorBytes
, nullptr);
221 assert(context
->http
->out
.offset
== 0);
226 Http::One::Server::proceedAfterBodyContinuation(Http::StreamPointer context
)
228 debugs(33, 5, "Body Continuation written");
229 clientProcessRequest(this, parser_
, context
.getRaw());
233 Http::One::Server::processParsedRequest(Http::StreamPointer
&context
)
235 if (!buildHttpRequest(context
))
238 ClientHttpRequest
*http
= context
->http
;
239 HttpRequest::Pointer request
= http
->request
;
241 if (request
->header
.has(Http::HdrType::EXPECT
)) {
242 const String expect
= request
->header
.getList(Http::HdrType::EXPECT
);
243 const bool supportedExpect
= (expect
.caseCmp("100-continue") == 0);
244 if (!supportedExpect
) {
245 clientStreamNode
*node
= context
->getClientReplyContext();
246 quitAfterError(request
.getRaw());
247 // setLogUri should called before repContext->setReplyToError
248 setLogUri(http
, urlCanonicalClean(request
.getRaw()));
249 clientReplyContext
*repContext
= dynamic_cast<clientReplyContext
*>(node
->data
.getRaw());
251 repContext
->setReplyToError(ERR_INVALID_REQ
, Http::scExpectationFailed
, request
->method
, http
->uri
,
252 clientConnection
->remote
, request
.getRaw(), NULL
, NULL
);
253 assert(context
->http
->out
.offset
== 0);
255 clientProcessRequestFinished(this, request
);
259 if (Config
.accessList
.forceRequestBodyContinuation
) {
260 ACLFilledChecklist
bodyContinuationCheck(Config
.accessList
.forceRequestBodyContinuation
, request
.getRaw(), NULL
);
261 if (bodyContinuationCheck
.fastCheck().allowed()) {
262 debugs(33, 5, "Body Continuation forced");
263 request
->forcedBodyContinuation
= true;
265 HttpReply::Pointer rep
= new HttpReply
;
266 rep
->sline
.set(Http::ProtocolVersion(), Http::scContinue
);
268 typedef UnaryMemFunT
<Http1::Server
, Http::StreamPointer
> CbDialer
;
269 const AsyncCall::Pointer cb
= asyncCall(11, 3, "Http1::Server::proceedAfterBodyContinuation", CbDialer(this, &Http1::Server::proceedAfterBodyContinuation
, Http::StreamPointer(context
)));
270 sendControlMsg(HttpControlMsg(rep
, cb
));
275 clientProcessRequest(this, parser_
, context
.getRaw());
279 Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr
)
281 ConnStateData::noteBodyConsumerAborted(ptr
);
282 stopReceiving("virgin request body consumer aborted"); // closes ASAP
286 Http::One::Server::handleReply(HttpReply
*rep
, StoreIOBuffer receivedData
)
288 // the caller guarantees that we are dealing with the current context only
289 Http::StreamPointer context
= pipeline
.front();
290 Must(context
!= nullptr);
291 const ClientHttpRequest
*http
= context
->http
;
294 // After sending Transfer-Encoding: chunked (at least), always send
295 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
296 const bool mustSendLastChunk
= http
->request
->flags
.chunkedReply
&&
297 !http
->request
->flags
.streamError
&&
298 !EBIT_TEST(http
->storeEntry()->flags
, ENTRY_BAD_LENGTH
) &&
299 !context
->startOfOutput();
300 const bool responseFinishedOrFailed
= !rep
&&
301 !receivedData
.data
&&
302 !receivedData
.length
;
303 if (responseFinishedOrFailed
&& !mustSendLastChunk
) {
304 context
->writeComplete(0);
308 if (!context
->startOfOutput()) {
309 context
->sendBody(receivedData
);
314 http
->al
->reply
= rep
;
315 HTTPMSGLOCK(http
->al
->reply
);
316 context
->sendStartOfMessage(rep
, receivedData
);
320 Http::One::Server::writeControlMsgAndCall(HttpReply
*rep
, AsyncCall::Pointer
&call
)
322 Http::StreamPointer context
= pipeline
.front();
323 Must(context
!= nullptr);
325 // Ignore this late control message if we have started sending a
326 // reply to the user already (e.g., after an error).
327 if (context
->reply
) {
328 debugs(11, 2, "drop 1xx made late by " << context
->reply
);
332 const ClientHttpRequest
*http
= context
->http
;
334 // apply selected clientReplyContext::buildReplyHeader() mods
335 // it is not clear what headers are required for control messages
336 rep
->header
.removeHopByHopEntries();
337 rep
->header
.putStr(Http::HdrType::CONNECTION
, "keep-alive");
338 httpHdrMangleList(&rep
->header
, http
->request
, http
->al
, ROR_REPLY
);
340 MemBuf
*mb
= rep
->pack();
342 debugs(11, 2, "HTTP Client " << clientConnection
);
343 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb
->buf
<< "\n----------");
345 Comm::Write(clientConnection
, mb
, call
);
352 Http::NewServer(MasterXactionPointer
&xact
)
354 return new Http1::Server(xact
, false);
358 Https::NewServer(MasterXactionPointer
&xact
)
360 return new Http1::Server(xact
, true);