]> git.ipfire.org Git - thirdparty/squid.git/blob - src/servers/Http1Server.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / servers / Http1Server.cc
1 /*
2 * Copyright (C) 1996-2018 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 #if USE_OPENSSL
46 // XXX: Until we create an HttpsServer class, use this hack to allow old
47 // client_side.cc code to manipulate ConnStateData object directly
48 if (isHttpsServer) {
49 postHttpsAccept();
50 return;
51 }
52 #endif
53
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);
58 readSomeData();
59 }
60
61 void
62 Http::One::Server::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
63 {
64 if (!handleRequestBodyData())
65 return;
66
67 // too late to read more body
68 if (!isOpen() || stoppedReceiving())
69 return;
70
71 readSomeData();
72 }
73
74 Http::Stream *
75 Http::One::Server::parseOneRequest()
76 {
77 PROF_start(HttpServer_parseOneRequest);
78
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());
84
85 /* Process request */
86 Http::Stream *context = parseHttpRequest(this, parser_);
87
88 PROF_stop(HttpServer_parseOneRequest);
89 return context;
90 }
91
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);
94
95 bool
96 Http::One::Server::buildHttpRequest(Http::StreamPointer &context)
97 {
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;
109 break;
110 case Http::scMethodNotAllowed:
111 errPage = ERR_UNSUP_REQ;
112 break;
113 case Http::scHttpVersionNotSupported:
114 errPage = ERR_UNSUP_HTTPVERSION;
115 break;
116 default:
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.
121 break;
122 }
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
130 }
131
132 return false;
133 }
134
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);
142
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
148 }
149 return false;
150 }
151
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) ) {
157
158 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << parser_->messageProtocol());
159 // setLogUri should called before repContext->setReplyToError
160 setLogUri(http, http->uri, true);
161
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);
166 }
167 return false;
168 }
169
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);
179 }
180 return false;
181 }
182
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());
191 }
192
193 // TODO: We fill request notes here until we find a way to verify whether
194 // no ACL checking is performed before ClientHttpRequest::doCallouts().
195 if (hasNotes()) {
196 assert(!request->hasNotes());
197 request->notes()->append(notes().getRaw());
198 }
199
200 http->request = request.getRaw();
201 HTTPMSGLOCK(http->request);
202
203 return true;
204 }
205
206 void
207 Http::One::Server::setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
208 {
209 quitAfterError(request.getRaw());
210 if (!context->connRegistered()) {
211 debugs(33, 2, "Client stream deregister it self, nothing to do");
212 clientConnection->close();
213 return;
214 }
215 clientStreamNode *node = context->getClientReplyContext();
216 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
217 assert (repContext);
218
219 repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, clientConnection->remote, nullptr, requestErrorBytes, nullptr);
220
221 assert(context->http->out.offset == 0);
222 context->pullData();
223 }
224
225 void
226 Http::One::Server::proceedAfterBodyContinuation(Http::StreamPointer context)
227 {
228 debugs(33, 5, "Body Continuation written");
229 clientProcessRequest(this, parser_, context.getRaw());
230 }
231
232 void
233 Http::One::Server::processParsedRequest(Http::StreamPointer &context)
234 {
235 if (!buildHttpRequest(context))
236 return;
237
238 ClientHttpRequest *http = context->http;
239 HttpRequest::Pointer request = http->request;
240
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());
250 assert (repContext);
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);
254 context->pullData();
255 clientProcessRequestFinished(this, request);
256 return;
257 }
258
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;
264 //sendControlMsg
265 HttpReply::Pointer rep = new HttpReply;
266 rep->sline.set(Http::ProtocolVersion(), Http::scContinue);
267
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));
271 return;
272 }
273 }
274 }
275 clientProcessRequest(this, parser_, context.getRaw());
276 }
277
278 void
279 Http::One::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr)
280 {
281 ConnStateData::noteBodyConsumerAborted(ptr);
282 stopReceiving("virgin request body consumer aborted"); // closes ASAP
283 }
284
285 void
286 Http::One::Server::handleReply(HttpReply *rep, StoreIOBuffer receivedData)
287 {
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;
292 Must(http != NULL);
293
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);
305 return;
306 }
307
308 if (!context->startOfOutput()) {
309 context->sendBody(receivedData);
310 return;
311 }
312
313 assert(rep);
314 http->al->reply = rep;
315 HTTPMSGLOCK(http->al->reply);
316 context->sendStartOfMessage(rep, receivedData);
317 }
318
319 bool
320 Http::One::Server::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
321 {
322 Http::StreamPointer context = pipeline.front();
323 Must(context != nullptr);
324
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);
329 return false;
330 }
331
332 const ClientHttpRequest *http = context->http;
333
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);
339
340 MemBuf *mb = rep->pack();
341
342 debugs(11, 2, "HTTP Client " << clientConnection);
343 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
344
345 Comm::Write(clientConnection, mb, call);
346
347 delete mb;
348 return true;
349 }
350
351 ConnStateData *
352 Http::NewServer(MasterXactionPointer &xact)
353 {
354 return new Http1::Server(xact, false);
355 }
356
357 ConnStateData *
358 Https::NewServer(MasterXactionPointer &xact)
359 {
360 return new Http1::Server(xact, true);
361 }
362