Http1Server.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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) {
49  return;
50  }
51 
52  typedef CommCbMemFunT<Server, CommTimeoutCbParams> TimeoutDialer;
53  AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
54  TimeoutDialer, this, Http1::Server::requestTimeout);
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
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())
86 
87  /* Process request */
89 
90  PROF_stop(HttpServer_parseOneRequest);
91  return context;
92 }
93 
95 bool clientTunnelOnError(ConnStateData *conn, Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError);
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
142  if ((request = HttpRequest::FromUrl(http->uri, mx, parser_->method())) == NULL) {
143  debugs(33, 5, "Invalid URL: " << http->uri);
144  // setReplyToError() requires log_uri
145  http->setLogUriToRawUri(http->uri, parser_->method());
146 
147  const char * requestErrorBytes = inBuf.c_str();
148  if (!clientTunnelOnError(this, context, request, parser_->method(), ERR_INVALID_URL)) {
149  setReplyError(context, request, parser_->method(), ERR_INVALID_URL, Http::scBadRequest, requestErrorBytes);
150  // HttpRequest object not build yet, there is no reason to call
151  // clientProcessRequestFinished method
152  }
153  return false;
154  }
155 
156  /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */
157  /* We currently only support 0.9, 1.0, 1.1 properly */
158  /* TODO: move HTTP-specific processing into servers/HttpServer and such */
159  if ( (parser_->messageProtocol().major == 0 && parser_->messageProtocol().minor != 9) ||
160  (parser_->messageProtocol().major > 1) ) {
161 
162  debugs(33, 5, "Unsupported HTTP version discovered. :\n" << parser_->messageProtocol());
163  // setReplyToError() requires log_uri
164  http->setLogUriToRawUri(http->uri, parser_->method());
165 
166  const char * requestErrorBytes = NULL; //HttpParserHdrBuf(parser_);
167  if (!clientTunnelOnError(this, context, request, parser_->method(), ERR_UNSUP_HTTPVERSION)) {
168  setReplyError(context, request, parser_->method(), ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, requestErrorBytes);
169  clientProcessRequestFinished(this, request);
170  }
171  return false;
172  }
173 
174  /* compile headers */
175  if (parser_->messageProtocol().major >= 1 && !request->parseHeader(*parser_.getRaw())) {
176  debugs(33, 5, "Failed to parse request headers:\n" << parser_->mimeHeader());
177  // setReplyToError() requires log_uri
178  http->setLogUriToRawUri(http->uri, parser_->method());
179  const char * requestErrorBytes = NULL; //HttpParserHdrBuf(parser_);
180  if (!clientTunnelOnError(this, context, request, parser_->method(), ERR_INVALID_REQ)) {
181  setReplyError(context, request, parser_->method(), ERR_INVALID_REQ, Http::scBadRequest, requestErrorBytes);
182  clientProcessRequestFinished(this, request);
183  }
184  return false;
185  }
186 
187  // when absolute-URI is provided Host header should be ignored. However
188  // some code still uses Host directly so normalize it using the previously
189  // sanitized URL authority value.
190  // For now preserve the case where Host is completely absent. That matters.
191  if (const auto x = request->header.delById(Http::HOST)) {
192  debugs(33, 5, "normalize " << x << " Host header using " << request->url.authority());
193  SBuf tmp(request->url.authority());
194  request->header.putStr(Http::HOST, tmp.c_str());
195  }
196 
197  // TODO: We fill request notes here until we find a way to verify whether
198  // no ACL checking is performed before ClientHttpRequest::doCallouts().
199  if (hasNotes()) {
200  assert(!request->hasNotes());
201  request->notes()->append(notes().getRaw());
202  }
203 
204  http->initRequest(request.getRaw());
205 
206  return true;
207 }
208 
209 void
210 Http::One::Server::setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
211 {
212  quitAfterError(request.getRaw());
213  if (!context->connRegistered()) {
214  debugs(33, 2, "Client stream deregister it self, nothing to do");
216  return;
217  }
218  clientStreamNode *node = context->getClientReplyContext();
219  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
220  assert (repContext);
221 
222  repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, clientConnection->remote, nullptr, requestErrorBytes, nullptr);
223 
224  assert(context->http->out.offset == 0);
225  context->pullData();
226 }
227 
228 void
230 {
231  debugs(33, 5, "Body Continuation written");
232  clientProcessRequest(this, parser_, context.getRaw());
233 }
234 
235 void
237 {
238  if (!buildHttpRequest(context))
239  return;
240 
241  ClientHttpRequest *http = context->http;
242  HttpRequest::Pointer request = http->request;
243 
244  if (request->header.has(Http::HdrType::EXPECT)) {
245  const String expect = request->header.getList(Http::HdrType::EXPECT);
246  const bool supportedExpect = (expect.caseCmp("100-continue") == 0);
247  if (!supportedExpect) {
248  clientStreamNode *node = context->getClientReplyContext();
249  quitAfterError(request.getRaw());
250  // setReplyToError() requires log_uri
251  assert(http->log_uri);
252  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
253  assert (repContext);
255  clientConnection->remote, request.getRaw(), NULL, NULL);
256  assert(context->http->out.offset == 0);
257  context->pullData();
258  clientProcessRequestFinished(this, request);
259  return;
260  }
261 
263  ACLFilledChecklist bodyContinuationCheck(Config.accessList.forceRequestBodyContinuation, request.getRaw(), NULL);
264  bodyContinuationCheck.al = http->al;
265  bodyContinuationCheck.syncAle(request.getRaw(), http->log_uri);
266  if (bodyContinuationCheck.fastCheck().allowed()) {
267  debugs(33, 5, "Body Continuation forced");
268  request->forcedBodyContinuation = true;
269  //sendControlMsg
270  HttpReply::Pointer rep = new HttpReply;
272 
274  const AsyncCall::Pointer cb = asyncCall(11, 3, "Http1::Server::proceedAfterBodyContinuation", CbDialer(this, &Http1::Server::proceedAfterBodyContinuation, Http::StreamPointer(context)));
275  sendControlMsg(HttpControlMsg(rep, cb));
276  return;
277  }
278  }
279  }
280  clientProcessRequest(this, parser_, context.getRaw());
281 }
282 
283 void
285 {
287  stopReceiving("virgin request body consumer aborted"); // closes ASAP
288 }
289 
290 void
292 {
293  // the caller guarantees that we are dealing with the current context only
294  Http::StreamPointer context = pipeline.front();
295  Must(context != nullptr);
296  const ClientHttpRequest *http = context->http;
297  Must(http != NULL);
298 
299  // After sending Transfer-Encoding: chunked (at least), always send
300  // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
301  const bool mustSendLastChunk = http->request->flags.chunkedReply &&
302  !http->request->flags.streamError &&
304  !context->startOfOutput();
305  const bool responseFinishedOrFailed = !rep &&
306  !receivedData.data &&
307  !receivedData.length;
308  if (responseFinishedOrFailed && !mustSendLastChunk) {
309  context->writeComplete(0);
310  return;
311  }
312 
313  if (!context->startOfOutput()) {
314  context->sendBody(receivedData);
315  return;
316  }
317 
318  assert(rep);
319  HTTPMSGUNLOCK(http->al->reply);
320  http->al->reply = rep;
321  HTTPMSGLOCK(http->al->reply);
322  context->sendStartOfMessage(rep, receivedData);
323 }
324 
325 bool
327 {
328  Http::StreamPointer context = pipeline.front();
329  Must(context != nullptr);
330 
331  // Ignore this late control message if we have started sending a
332  // reply to the user already (e.g., after an error).
333  if (context->reply) {
334  debugs(11, 2, "drop 1xx made late by " << context->reply);
335  return false;
336  }
337 
338  const ClientHttpRequest *http = context->http;
339 
340  // apply selected clientReplyContext::buildReplyHeader() mods
341  // it is not clear what headers are required for control messages
343  // paranoid: ContentLengthInterpreter has cleaned non-generated replies
345  rep->header.putStr(Http::HdrType::CONNECTION, "keep-alive");
346  httpHdrMangleList(&rep->header, http->request, http->al, ROR_REPLY);
347 
348  MemBuf *mb = rep->pack();
349 
350  debugs(11, 2, "HTTP Client " << clientConnection);
351  debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
352 
353  Comm::Write(clientConnection, mb, call);
354 
355  delete mb;
356  return true;
357 }
358 
361 {
362  return new Http1::Server(xact, false);
363 }
364 
367 {
368  return new Http1::Server(xact, true);
369 }
370 
int delById(Http::HdrType id)
Definition: HttpHeader.cc:681
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
bool buildHttpRequest(Http::StreamPointer &context)
Definition: Http1Server.cc:98
#define assert(EX)
Definition: assert.h:17
SBuf & authority(bool requirePort=false) const
Definition: Uri.cc:503
const char * stoppedReceiving() const
true if we stopped receiving the request
Definition: client_side.h:150
MemBuf * pack() const
Definition: HttpReply.cc:111
ConnStateData * NewServer(MasterXactionPointer &xact)
create a new HTTP connection handler; never returns NULL
Definition: Http1Server.cc:360
Definition: Server.h:28
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
void set(const AnyP::ProtocolVersion &newVersion, Http::StatusCode newStatus, const char *newReason=NULL)
Definition: StatusLine.cc:30
virtual void processParsedRequest(Http::StreamPointer &context)
start processing a freshly parsed request
Definition: Http1Server.cc:236
Definition: SBuf.h:86
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:828
HttpRequestMethod method
Definition: HttpRequest.h:114
acl_access * forceRequestBodyContinuation
Definition: SquidConfig.h:403
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void removeHopByHopEntries()
Definition: HttpHeader.cc:1691
void proceedAfterBodyContinuation(Http::StreamPointer context)
Definition: Http1Server.cc:229
bool forcedBodyContinuation
whether we have responded with HTTP 100 or FTP 150 already
Definition: HttpRequest.h:195
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:160
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
StoreEntry * storeEntry() const
void clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
CBDATA_NAMESPACED_CLASS_INIT(Http1, Server)
bool isOpen() const
Definition: client_side.cc:606
void requestTimeout(const CommTimeoutCbParams &params)
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
virtual void handleReply(HttpReply *rep, StoreIOBuffer receivedData)
Definition: Http1Server.cc:291
NotePairs::Pointer notes()
Definition: HttpRequest.cc:715
Http::StreamPointer front() const
get the first request context in the pipeline
Definition: Pipeline.cc:28
Definition: parse.c:104
StatusCode
Definition: StatusCode.h:20
virtual void sendControlMsg(HttpControlMsg)
called to send the 1xx message and notify the Source
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
HttpReply * reply
void readSomeData()
maybe grow the inBuf and schedule Comm::Read()
Definition: Server.cc:85
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
Manages a connection from an HTTP/1 or HTTP/0.9 client.
Definition: Http1Server.h:20
ConnStateData * NewServer(MasterXactionPointer &xact)
create a new HTTPS connection handler; never returns NULL
Definition: Http1Server.cc:366
bool streamError
Definition: RequestFlags.h:92
Server(const MasterXaction::Pointer &xact, const bool beHttpsServer)
Definition: Http1Server.cc:27
void setLogUriToRawUri(const char *rawUri, const HttpRequestMethod &)
NotePairs::Pointer notes()
void quitAfterError(HttpRequest *request)
bool chunkedReply
Definition: RequestFlags.h:90
bundles HTTP 1xx reply and the "successfully forwarded" callback
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
void stopReceiving(const char *error)
note request receiving error and close as soon as we write the response
bool handleRequestBodyData()
struct SquidConfig::@113 accessList
bool parseHeader(Http1::Parser &hp)
Definition: HttpRequest.cc:674
const char * c_str()
Definition: SBuf.cc:526
uint16_t flags
Definition: Store.h:210
ClientStreamData data
Definition: clientStream.h:94
AccessLogEntry::Pointer al
access.log entry
Http::StatusLine sline
Definition: HttpReply.h:60
Http1::RequestParserPointer parser_
Definition: Http1Server.h:59
virtual void noteBodyConsumerAborted(BodyPipe::Pointer)
Definition: Http1Server.cc:284
time_t request_start_timeout
Definition: SquidConfig.h:119
virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer)
Definition: Http1Server.cc:60
static HttpRequest * FromUrl(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:543
bool clientTunnelOnError(ConnStateData *conn, Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod &method, err_type requestError)
ConnStateData::tunnelOnError() wrapper. Reduces code changes. TODO: Remove.
HttpHeader header
Definition: Message.h:75
void postHttpsAccept()
the second part of old httpsAccept, waiting for future HttpsServer home
bool hasNotes() const
Definition: HttpRequest.h:245
char * buf
Definition: MemBuf.h:134
RequestFlags flags
Definition: HttpRequest.h:141
int has(Http::HdrType id) const
Definition: HttpHeader.cc:977
Ip::Address remote
Definition: Connection.h:138
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1038
#define PROF_start(probename)
Definition: Profiler.h:62
virtual Http::Stream * parseOneRequest()
Definition: Http1Server.cc:73
bool shouldPreserveClientData() const
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:149
bool hasNotes() const
Definition: client_side.h:326
C * getRaw() const
Definition: RefCount.h:74
SBuf inBuf
read I/O buffer for the client connection
Definition: Server.h:110
time_t clientIdlePconn
Definition: SquidConfig.h:113
Definition: MemBuf.h:23
Comm::ConnectionPointer clientConnection
Definition: Server.h:97
struct SquidConfig::@98 Timeout
virtual time_t idleTimeout() const
timeout to use when waiting for the next request
Definition: Http1Server.cc:35
void setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod &method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
Definition: Http1Server.cc:210
HttpRequest *const request
#define PROF_stop(probename)
Definition: Profiler.h:63
void initRequest(HttpRequest *)
bool preservingClientData_
whether preservedClientData is valid and should be kept up to date
Definition: client_side.h:376
Http::Stream * parseHttpRequest(const Http1::RequestParserPointer &)
virtual void start()
called by AsyncStart; do not call directly
Definition: Http1Server.cc:41
virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
handle a control message received by context from a peer and call back
Definition: Http1Server.cc:326
void removeIrrelevantContentLength()
Some response status codes prohibit sending Content-Length (RFC 7230 section 3.3.2).
Definition: HttpReply.cc:668
const bool isHttpsServer
temporary hack to avoid creating a true HttpsServer class
Definition: Http1Server.h:63
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:552
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
Comm::ConnectionPointer tcpClient
the client TCP connection which originated this transaction
Definition: MasterXaction.h:53
class SquidConfig Config
Definition: SquidConfig.cc:12
void append(const NotePairs *src)
Append the entries of the src NotePairs list to our list.
Definition: Notes.cc:342
#define NULL
Definition: types.h:166
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
int caseCmp(char const *) const
Definition: String.cc:299
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:35
virtual void start()
called by AsyncStart; do not call directly
virtual void noteBodyConsumerAborted(BodyPipe::Pointer)=0
err_type
Definition: err_type.h:12
Pipeline pipeline
set of requests waiting to be serviced
Definition: Server.h:115

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors