Http1Server.cc
Go to the documentation of this file.
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 
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
36 {
38 }
39 
40 void
42 {
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
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 
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 
96 
97 bool
99 {
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) {
108  // fall through to next case
109  case Http::scUriTooLong:
110  errPage = ERR_TOO_BIG;
111  break;
113  errPage = ERR_UNSUP_REQ;
114  break;
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
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);
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);
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
231 {
232  debugs(33, 5, "Body Continuation written");
233  clientProcessRequest(this, parser_, context.getRaw());
234 }
235 
236 int
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 
245 }
246 
247 void
249 {
250  if (!buildHttpRequest(context))
251  return;
252 
253  ClientHttpRequest *http = context->http;
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);
267  clientConnection->remote, request.getRaw(), NULL, NULL);
268  assert(context->http->out.offset == 0);
269  context->pullData();
271  return;
272  }
273 
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;
284 
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
297 {
299  stopReceiving("virgin request body consumer aborted"); // closes ASAP
300 }
301 
302 void
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 &&
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
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
358  // paranoid: ContentLengthInterpreter has cleaned non-generated replies
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
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 
402 {
403  return new Http1::Server(xact, false);
404 }
405 
408 {
409  return new Http1::Server(xact, true);
410 }
411 
@ initClient
HTTP or FTP client.
int caseCmp(char const *) const
Definition: String.cc:299
Definition: parse.c:104
virtual void noteBodyConsumerAborted(BodyPipe::Pointer)=0
char method[16]
Definition: tcp-banger2.c:115
char * buf
Definition: MemBuf.h:134
@ scMethodNotAllowed
Definition: StatusCode.h:49
@ scBadRequest
Definition: StatusCode.h:44
virtual Http::Stream * parseOneRequest()
Definition: Http1Server.cc:73
void removeHopByHopEntries()
Definition: HttpHeader.cc:1757
HttpHeader header
Definition: Message.h:75
time_t clientIdlePconn
Definition: SquidConfig.h:116
virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
handle a control message received by context from a peer and call back
Definition: Http1Server.cc:335
void initRequest(HttpRequest *)
RequestFlags flags
Definition: HttpRequest.h:141
Server(const MasterXaction::Pointer &xact, const bool beHttpsServer)
Definition: Http1Server.cc:27
struct SquidConfig::@113 accessList
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
Definition: SBuf.h:86
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:567
void setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod &method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
Definition: Http1Server.cc:211
Definition: Server.h:28
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:858
time_t request_start_timeout
Definition: SquidConfig.h:122
C * getRaw() const
Definition: RefCount.h:80
uint16_t flags
Definition: Store.h:224
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
bool clientTunnelOnError(ConnStateData *conn, Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod &method, err_type requestError)
ConnStateData::tunnelOnError() wrapper. Reduces code changes. TODO: Remove.
virtual int pipelinePrefetchMax() const
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
Definition: Http1Server.cc:237
virtual void start()
called by AsyncStart; do not call directly
Definition: Http1Server.cc:41
Http::StatusLine sline
Definition: HttpReply.h:60
StatusCode
Definition: StatusCode.h:20
#define PROF_stop(probename)
Definition: Profiler.h:63
virtual void noteBodyConsumerAborted(BodyPipe::Pointer)
Definition: Http1Server.cc:296
@ ENTRY_BAD_LENGTH
Definition: enums.h:114
@ ERR_INVALID_REQ
Definition: err_type.h:41
void removeIrrelevantContentLength()
Some response status codes prohibit sending Content-Length (RFC 7230 section 3.3.2).
Definition: HttpReply.cc:672
bool streamError
Definition: RequestFlags.h:92
void setLogUriToRawUri(const char *rawUri, const HttpRequestMethod &)
Comm::ConnectionPointer tcpClient
the client TCP connection which originated this transaction
Definition: MasterXaction.h:53
#define NULL
Definition: types.h:166
virtual time_t idleTimeout() const
timeout to use when waiting for the next request
Definition: Http1Server.cc:35
@ scSwitchingProtocols
Definition: StatusCode.h:23
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
bundles HTTP 1xx reply and the "successfully forwarded" callback
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
virtual void noteTakeServerConnectionControl(ServerConnectionContext)
Definition: Http1Server.cc:385
const Acl::Answer & fastCheck()
Definition: Checklist.cc:336
MemBuf * pack() const
Definition: HttpReply.cc:111
Definition: MemBuf.h:23
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
@ ERR_UNSUP_HTTPVERSION
Definition: err_type.h:40
void setReplyToError(err_type, Http::StatusCode, const HttpRequestMethod &, char const *, Ip::Address &, HttpRequest *, const char *, Auth::UserRequest::Pointer)
builds error using clientBuildError() and calls setReplyToError() below
AccessLogEntry::Pointer al
access.log entry
err_type
Definition: err_type.h:12
@ ERR_UNSUP_REQ
Definition: err_type.h:42
virtual int pipelinePrefetchMax() const
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
@ scRequestHeaderFieldsTooLarge
Definition: StatusCode.h:69
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
@ scUriTooLong
Definition: StatusCode.h:58
@ scContinue
Definition: StatusCode.h:22
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const
assigns uninitialized adapted_request and url ALE components
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
const char * c_str()
Definition: SBuf.cc:526
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:35
void clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
CBDATA_NAMESPACED_CLASS_INIT(Http1, Server)
bool chunkedReply
Definition: RequestFlags.h:90
virtual void start()
called by AsyncStart; do not call directly
@ scHttpVersionNotSupported
Definition: StatusCode.h:76
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
acl_access * forceRequestBodyContinuation
Definition: SquidConfig.h:408
noteTakeServerConnectionControl() callback parameter
Definition: client_side.h:201
const char * termedBuf() const
Definition: SquidString.h:91
bool allowed() const
Definition: Acl.h:143
virtual void handleReply(HttpReply *rep, StoreIOBuffer receivedData)
Definition: Http1Server.cc:303
virtual void processParsedRequest(Http::StreamPointer &context)
start processing a freshly parsed request
Definition: Http1Server.cc:248
bool buildHttpRequest(Http::StreamPointer &context)
Definition: Http1Server.cc:98
static char server[MAXLINE]
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1068
void set(const AnyP::ProtocolVersion &newVersion, Http::StatusCode newStatus, const char *newReason=NULL)
Definition: StatusLine.cc:30
size_type size() const
Definition: SquidString.h:72
char * url
Definition: tcp-banger2.c:114
@ METHOD_NONE
Definition: MethodType.h:22
@ ERR_TOO_BIG
Definition: err_type.h:38
size_t HttpReply *STUB StoreEntry const KeyScope scope const HttpRequestMethod & method
Definition: stub_store.cc:108
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:546
ConnStateData * NewServer(MasterXactionPointer &xact)
create a new HTTP connection handler; never returns NULL
Definition: Http1Server.cc:401
void switchToTunnel(HttpRequest *request, const Comm::ConnectionPointer &clientConn, const Comm::ConnectionPointer &srvConn, const SBuf &preReadServerData)
Definition: tunnel.cc:1441
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
@ ERR_PROTOCOL_UNKNOWN
Definition: err_type.h:71
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
ConnStateData * NewServer(MasterXactionPointer &xact)
create a new HTTPS connection handler; never returns NULL
Definition: Http1Server.cc:407
@ ERR_INVALID_URL
Definition: err_type.h:43
struct SquidConfig::@98 Timeout
StoreEntry * storeEntry() const
Manages a connection from an HTTP/1 or HTTP/0.9 client.
Definition: Http1Server.h:20
bool isOpen(const int fd)
Definition: comm.cc:87
@ scExpectationFailed
Definition: StatusCode.h:61
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void proceedAfterBodyContinuation(Http::StreamPointer context)
Definition: Http1Server.cc:230
#define PROF_start(probename)
Definition: Profiler.h:62
void requestTimeout(const CommTimeoutCbParams &params)
@ ROR_REPLY
class SquidConfig Config
Definition: SquidConfig.cc:12
HttpRequest *const request
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer)
Definition: Http1Server.cc:60

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors