]> git.ipfire.org Git - thirdparty/squid.git/blob - src/servers/Http1Server.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / servers / Http1Server.cc
1 /*
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: section 33 Client-side Routines */
10
11 #include "squid.h"
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"
23 #include "Store.h"
24
25 CBDATA_NAMESPACED_CLASS_INIT(Http1, Server);
26
27 Http::One::Server::Server(const MasterXaction::Pointer &xact, bool beHttpsServer):
28 AsyncJob("Http1::Server"),
29 ConnStateData(xact),
30 isHttpsServer(beHttpsServer)
31 {
32 }
33
34 time_t
35 Http::One::Server::idleTimeout() const
36 {
37 return Config.Timeout.clientIdlePconn;
38 }
39
40 void
41 Http::One::Server::start()
42 {
43 ConnStateData::start();
44
45 // XXX: Until we create an HttpsServer class, use this hack to allow old
46 // client_side.cc code to manipulate ConnStateData object directly
47 if (isHttpsServer) {
48 postHttpsAccept();
49 return;
50 }
51
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);
56 readSomeData();
57 }
58
59 void
60 Http::One::Server::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
61 {
62 if (!handleRequestBodyData())
63 return;
64
65 // too late to read more body
66 if (!isOpen() || stoppedReceiving())
67 return;
68
69 readSomeData();
70 }
71
72 Http::Stream *
73 Http::One::Server::parseOneRequest()
74 {
75 PROF_start(HttpServer_parseOneRequest);
76
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();
80
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_);
86
87 /* Process request */
88 Http::Stream *context = parseHttpRequest(parser_);
89
90 PROF_stop(HttpServer_parseOneRequest);
91 return context;
92 }
93
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);
96
97 bool
98 Http::One::Server::buildHttpRequest(Http::StreamPointer &context)
99 {
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;
111 break;
112 case Http::scMethodNotAllowed:
113 errPage = ERR_UNSUP_REQ;
114 break;
115 case Http::scHttpVersionNotSupported:
116 errPage = ERR_UNSUP_HTTPVERSION;
117 break;
118 default:
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.
123 break;
124 }
125 // setReplyToError() requires log_uri
126 // must be already initialized via ConnStateData::abortRequestParsing()
127 assert(http->log_uri);
128
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
134 }
135
136 return false;
137 }
138
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());
143 if (!request) {
144 debugs(33, 5, "Invalid URL: " << http->uri);
145 // setReplyToError() requires log_uri
146 http->setLogUriToRawUri(http->uri, parser_->method());
147
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
153 }
154 return false;
155 }
156
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) ) {
162
163 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << parser_->messageProtocol());
164 // setReplyToError() requires log_uri
165 http->setLogUriToRawUri(http->uri, parser_->method());
166
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);
171 }
172 return false;
173 }
174
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);
184 }
185 return false;
186 }
187
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());
196 }
197
198 // TODO: We fill request notes here until we find a way to verify whether
199 // no ACL checking is performed before ClientHttpRequest::doCallouts().
200 if (hasNotes()) {
201 assert(!request->hasNotes());
202 request->notes()->append(notes().getRaw());
203 }
204
205 http->initRequest(request.getRaw());
206
207 return true;
208 }
209
210 void
211 Http::One::Server::setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
212 {
213 quitAfterError(request.getRaw());
214 if (!context->connRegistered()) {
215 debugs(33, 2, "Client stream deregister it self, nothing to do");
216 clientConnection->close();
217 return;
218 }
219 clientStreamNode *node = context->getClientReplyContext();
220 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
221 assert (repContext);
222
223 repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, this, nullptr, requestErrorBytes, nullptr);
224
225 assert(context->http->out.offset == 0);
226 context->pullData();
227 }
228
229 void
230 Http::One::Server::proceedAfterBodyContinuation(Http::StreamPointer context)
231 {
232 debugs(33, 5, "Body Continuation written");
233 clientProcessRequest(this, parser_, context.getRaw());
234 }
235
236 int
237 Http::One::Server::pipelinePrefetchMax() const
238 {
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))
242 return 0;
243
244 return ConnStateData::pipelinePrefetchMax();
245 }
246
247 void
248 Http::One::Server::processParsedRequest(Http::StreamPointer &context)
249 {
250 if (!buildHttpRequest(context))
251 return;
252
253 ClientHttpRequest *http = context->http;
254 HttpRequest::Pointer request = http->request;
255
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());
265 assert (repContext);
266 repContext->setReplyToError(ERR_INVALID_REQ, Http::scExpectationFailed, request->method, http->uri,
267 this, request.getRaw(), nullptr, nullptr);
268 assert(context->http->out.offset == 0);
269 context->pullData();
270 clientProcessRequestFinished(this, request);
271 return;
272 }
273
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;
281 //sendControlMsg
282 HttpReply::Pointer rep = new HttpReply;
283 rep->sline.set(Http::ProtocolVersion(), Http::scContinue);
284
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));
288 return;
289 }
290 }
291 }
292 clientProcessRequest(this, parser_, context.getRaw());
293 }
294
295 void
296 Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr)
297 {
298 ConnStateData::noteBodyConsumerAborted(ptr);
299 stopReceiving("virgin request body consumer aborted"); // closes ASAP
300 }
301
302 void
303 Http::One::Server::handleReply(HttpReply *rep, StoreIOBuffer receivedData)
304 {
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;
309 Must(http != NULL);
310
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);
322 return;
323 }
324
325 if (!context->startOfOutput()) {
326 context->sendBody(receivedData);
327 return;
328 }
329
330 assert(rep);
331 context->sendStartOfMessage(rep, receivedData);
332 }
333
334 bool
335 Http::One::Server::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
336 {
337 Http::StreamPointer context = pipeline.front();
338 Must(context != nullptr);
339
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);
344 return false;
345 }
346
347 const ClientHttpRequest *http = context->http;
348
349 // remember Upgrade header; removeHopByHopEntries() will remove it
350 String upgradeHeader;
351 const auto switching = (rep->sline.status() == Http::scSwitchingProtocols);
352 if (switching)
353 upgradeHeader = rep->header.getList(Http::HdrType::UPGRADE);
354
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();
360
361 if (switching && /* paranoid: */ upgradeHeader.size()) {
362 rep->header.putStr(Http::HdrType::UPGRADE, upgradeHeader.termedBuf());
363 rep->header.putStr(Http::HdrType::CONNECTION, "upgrade");
364 // keep-alive is redundant, breaks some 101 (Switching Protocols) recipients
365 } else {
366 rep->header.putStr(Http::HdrType::CONNECTION, "keep-alive");
367 }
368
369 httpHdrMangleList(&rep->header, http->request, http->al, ROR_REPLY);
370
371 MemBuf *mb = rep->pack();
372
373 debugs(11, 2, "HTTP Client " << clientConnection);
374 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
375
376 Comm::Write(clientConnection, mb, call);
377
378 delete mb;
379 return true;
380 }
381
382 void switchToTunnel(HttpRequest *request, const Comm::ConnectionPointer &clientConn, const Comm::ConnectionPointer &srvConn, const SBuf &preReadServerData);
383
384 void
385 Http::One::Server::noteTakeServerConnectionControl(ServerConnectionContext server)
386 {
387 const auto context = pipeline.front();
388 assert(context);
389 const auto http = context->http;
390 assert(http);
391 assert(http->request);
392
393 stopReading();
394 Must(!writer);
395
396 switchToTunnel(http->request, clientConnection,
397 server.connection(), server.preReadServerBytes);
398 }
399
400 ConnStateData *
401 Http::NewServer(MasterXactionPointer &xact)
402 {
403 return new Http1::Server(xact, false);
404 }
405
406 ConnStateData *
407 Https::NewServer(MasterXactionPointer &xact)
408 {
409 return new Http1::Server(xact, true);
410 }
411