]> git.ipfire.org Git - thirdparty/squid.git/blob - src/servers/Http1Server.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / servers / Http1Server.cc
1 /*
2 * Copyright (C) 1996-2020 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, clientConnection->remote, 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 void
237 Http::One::Server::processParsedRequest(Http::StreamPointer &context)
238 {
239 if (!buildHttpRequest(context))
240 return;
241
242 ClientHttpRequest *http = context->http;
243 HttpRequest::Pointer request = http->request;
244
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());
254 assert (repContext);
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);
258 context->pullData();
259 clientProcessRequestFinished(this, request);
260 return;
261 }
262
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;
270 //sendControlMsg
271 HttpReply::Pointer rep = new HttpReply;
272 rep->sline.set(Http::ProtocolVersion(), Http::scContinue);
273
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));
277 return;
278 }
279 }
280 }
281 clientProcessRequest(this, parser_, context.getRaw());
282 }
283
284 void
285 Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr)
286 {
287 ConnStateData::noteBodyConsumerAborted(ptr);
288 stopReceiving("virgin request body consumer aborted"); // closes ASAP
289 }
290
291 void
292 Http::One::Server::handleReply(HttpReply *rep, StoreIOBuffer receivedData)
293 {
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;
298 Must(http != NULL);
299
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);
311 return;
312 }
313
314 if (!context->startOfOutput()) {
315 context->sendBody(receivedData);
316 return;
317 }
318
319 assert(rep);
320 context->sendStartOfMessage(rep, receivedData);
321 }
322
323 bool
324 Http::One::Server::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
325 {
326 Http::StreamPointer context = pipeline.front();
327 Must(context != nullptr);
328
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);
333 return false;
334 }
335
336 const ClientHttpRequest *http = context->http;
337
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);
345
346 MemBuf *mb = rep->pack();
347
348 debugs(11, 2, "HTTP Client " << clientConnection);
349 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
350
351 Comm::Write(clientConnection, mb, call);
352
353 delete mb;
354 return true;
355 }
356
357 ConnStateData *
358 Http::NewServer(MasterXactionPointer &xact)
359 {
360 return new Http1::Server(xact, false);
361 }
362
363 ConnStateData *
364 Https::NewServer(MasterXactionPointer &xact)
365 {
366 return new Http1::Server(xact, true);
367 }
368