Http1Server.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 "clientStream.h"
17 #include "comm/Write.h"
18 #include "http/one/RequestParser.h"
19 #include "http/Stream.h"
20 #include "HttpHeaderTools.h"
21 #include "servers/Http1Server.h"
22 #include "SquidConfig.h"
23 #include "Store.h"
24 #include "tunnel.h"
25 
27 
28 Http::One::Server::Server(const MasterXaction::Pointer &xact, bool beHttpsServer):
29  AsyncJob("Http1::Server"),
30  ConnStateData(xact),
31  isHttpsServer(beHttpsServer)
32 {
33 }
34 
35 time_t
37 {
39 }
40 
41 void
43 {
45 
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 
53  typedef CommCbMemFunT<Server, CommTimeoutCbParams> TimeoutDialer;
54  AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
55  TimeoutDialer, this, Http1::Server::requestTimeout);
56  commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
57  readSomeData();
58 }
59 
60 void
62 {
63  if (!handleRequestBodyData())
64  return;
65 
66  // too late to read more body
67  if (!isOpen() || stoppedReceiving())
68  return;
69 
70  readSomeData();
71 }
72 
75 {
76  // reset because the protocol may have changed if this is the first request
77  // and because we never bypass parsing failures of N+1st same-proto request
78  preservingClientData_ = shouldPreserveClientData();
79 
80  // parser is incremental. Generate new parser state if we,
81  // a) do not have one already
82  // b) have completed the previous request parsing already
83  if (!parser_ || !parser_->needsMoreData())
84  parser_ = new Http1::RequestParser(preservingClientData_);
85 
86  /* Process request */
87  Http::Stream *context = parseHttpRequest(parser_);
88 
89  return context;
90 }
91 
93 
94 bool
96 {
97  HttpRequest::Pointer request;
98  ClientHttpRequest *http = context->http;
99  if (context->flags.parsed_ok == 0) {
100  debugs(33, 2, "Invalid Request");
101  // determine which error page templates to use for specific parsing errors
102  err_type errPage = ERR_INVALID_REQ;
103  switch (parser_->parseStatusCode) {
105  case Http::scUriTooLong:
106  errPage = ERR_TOO_BIG;
107  break;
109  errPage = ERR_UNSUP_REQ;
110  break;
112  errPage = ERR_UNSUP_HTTPVERSION;
113  break;
114  default:
115  if (parser_->method() == METHOD_NONE || parser_->requestUri().length() == 0)
116  // no method or url parsed, probably is wrong protocol
117  errPage = ERR_PROTOCOL_UNKNOWN;
118  // else use default ERR_INVALID_REQ set above.
119  break;
120  }
121  // setReplyToError() requires log_uri
122  // must be already initialized via ConnStateData::abortRequestParsing()
123  assert(http->log_uri);
124 
125  const char * requestErrorBytes = inBuf.c_str();
126  if (!tunnelOnError(errPage)) {
127  setReplyError(context, request, 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  const auto mx = MasterXaction::MakePortful(port);
137  mx->tcpClient = clientConnection;
138  request = HttpRequest::FromUrlXXX(http->uri, mx, parser_->method());
139  if (!request) {
140  debugs(33, 5, "Invalid URL: " << http->uri);
141  // setReplyToError() requires log_uri
142  http->setLogUriToRawUri(http->uri, parser_->method());
143 
144  const char * requestErrorBytes = inBuf.c_str();
145  if (!tunnelOnError(ERR_INVALID_URL)) {
146  setReplyError(context, request, ERR_INVALID_URL, Http::scBadRequest, requestErrorBytes);
147  // HttpRequest object not build yet, there is no reason to call
148  // clientProcessRequestFinished method
149  }
150  return false;
151  }
152 
153  /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */
154  /* We currently only support 0.9, 1.0, 1.1 properly */
155  /* TODO: move HTTP-specific processing into servers/HttpServer and such */
156  if ( (parser_->messageProtocol().major == 0 && parser_->messageProtocol().minor != 9) ||
157  (parser_->messageProtocol().major > 1) ) {
158 
159  debugs(33, 5, "Unsupported HTTP version discovered. :\n" << parser_->messageProtocol());
160  // setReplyToError() requires log_uri
161  http->setLogUriToRawUri(http->uri, parser_->method());
162 
163  const char * requestErrorBytes = nullptr; //HttpParserHdrBuf(parser_);
164  if (!tunnelOnError(ERR_UNSUP_HTTPVERSION)) {
165  setReplyError(context, request, ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, requestErrorBytes);
166  clientProcessRequestFinished(this, request);
167  }
168  return false;
169  }
170 
171  /* compile headers */
172  if (parser_->messageProtocol().major >= 1 && !request->parseHeader(*parser_.getRaw())) {
173  debugs(33, 5, "Failed to parse request headers:\n" << parser_->mimeHeader());
174  // setReplyToError() requires log_uri
175  http->setLogUriToRawUri(http->uri, parser_->method());
176  const char * requestErrorBytes = nullptr; //HttpParserHdrBuf(parser_);
177  if (!tunnelOnError(ERR_INVALID_REQ)) {
178  setReplyError(context, request, ERR_INVALID_REQ, Http::scBadRequest, requestErrorBytes);
179  clientProcessRequestFinished(this, request);
180  }
181  return false;
182  }
183 
184  // when absolute-URI is provided Host header should be ignored. However
185  // some code still uses Host directly so normalize it using the previously
186  // sanitized URL authority value.
187  // For now preserve the case where Host is completely absent. That matters.
188  if (request->header.has(Http::HdrType::HOST))
189  request->header.updateOrAddStr(Http::HdrType::HOST, request->url.authority());
190 
191  // TODO: We fill request notes here until we find a way to verify whether
192  // no ACL checking is performed before ClientHttpRequest::doCallouts().
193  if (hasNotes()) {
194  assert(!request->hasNotes());
195  request->notes()->append(notes().getRaw());
196  }
197 
198  http->initRequest(request.getRaw());
199 
200  return true;
201 }
202 
203 void
204 Http::One::Server::setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
205 {
206  quitAfterError(request.getRaw());
207  if (!context->connRegistered()) {
208  debugs(33, 2, "Client stream deregister it self, nothing to do");
209  clientConnection->close();
210  return;
211  }
212  clientStreamNode *node = context->getClientReplyContext();
213  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
214  assert (repContext);
215 
216  repContext->setReplyToError(requestError, errStatusCode, context->http->uri, this, nullptr, requestErrorBytes, nullptr);
217 
218  assert(context->http->out.offset == 0);
219  context->pullData();
220 }
221 
222 void
224 {
225  debugs(33, 5, "Body Continuation written");
226  clientProcessRequest(this, parser_, context.getRaw());
227 }
228 
229 int
231 {
232  const auto context = pipeline.back();
233  const auto request = (context && context->http) ? context->http->request : nullptr;
234  if (request && request->header.has(Http::HdrType::UPGRADE))
235  return 0;
236 
238 }
239 
240 void
242 {
243  if (!buildHttpRequest(context))
244  return;
245 
246  ClientHttpRequest *http = context->http;
247  HttpRequest::Pointer request = http->request;
248 
249  if (request->header.has(Http::HdrType::EXPECT)) {
250  const String expect = request->header.getList(Http::HdrType::EXPECT);
251  const bool supportedExpect = (expect.caseCmp("100-continue") == 0);
252  if (!supportedExpect) {
253  clientStreamNode *node = context->getClientReplyContext();
254  quitAfterError(request.getRaw());
255  // setReplyToError() requires log_uri
256  assert(http->log_uri);
257  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
258  assert (repContext);
260  this, request.getRaw(), nullptr, nullptr);
261  assert(context->http->out.offset == 0);
262  context->pullData();
263  clientProcessRequestFinished(this, request);
264  return;
265  }
266 
268  ACLFilledChecklist bodyContinuationCheck(Config.accessList.forceRequestBodyContinuation, request.getRaw());
269  bodyContinuationCheck.al = http->al;
270  bodyContinuationCheck.syncAle(request.getRaw(), http->log_uri);
271  if (bodyContinuationCheck.fastCheck().allowed()) {
272  debugs(33, 5, "Body Continuation forced");
273  request->forcedBodyContinuation = true;
274  //sendControlMsg
275  HttpReply::Pointer rep = new HttpReply;
277 
279  const AsyncCall::Pointer cb = asyncCall(11, 3, "Http1::Server::proceedAfterBodyContinuation", CbDialer(this, &Http1::Server::proceedAfterBodyContinuation, Http::StreamPointer(context)));
280  sendControlMsg(HttpControlMsg(rep, cb));
281  return;
282  }
283  }
284  }
285  clientProcessRequest(this, parser_, context.getRaw());
286 }
287 
288 void
290 {
292  stopReceiving("virgin request body consumer aborted"); // closes ASAP
293 }
294 
295 void
297 {
298  // the caller guarantees that we are dealing with the current context only
299  Http::StreamPointer context = pipeline.front();
300  Must(context != nullptr);
301  const ClientHttpRequest *http = context->http;
302  Must(http != nullptr);
303 
304  // After sending Transfer-Encoding: chunked (at least), always send
305  // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
306  const bool mustSendLastChunk = http->request->flags.chunkedReply &&
307  !http->request->flags.streamError &&
309  !context->startOfOutput();
310  const bool responseFinishedOrFailed = !rep &&
311  !receivedData.data &&
312  !receivedData.length;
313  if (responseFinishedOrFailed && !mustSendLastChunk) {
314  context->writeComplete(0);
315  return;
316  }
317 
318  if (!context->startOfOutput()) {
319  context->sendBody(receivedData);
320  return;
321  }
322 
323  assert(rep);
324  context->sendStartOfMessage(rep, receivedData);
325 }
326 
327 bool
329 {
330  Http::StreamPointer context = pipeline.front();
331  Must(context != nullptr);
332 
333  // Ignore this late control message if we have started sending a
334  // reply to the user already (e.g., after an error).
335  if (context->reply) {
336  debugs(11, 2, "drop 1xx made late by " << context->reply);
337  return false;
338  }
339 
340  const ClientHttpRequest *http = context->http;
341 
342  // remember Upgrade header; removeHopByHopEntries() will remove it
343  String upgradeHeader;
344  const auto switching = (rep->sline.status() == Http::scSwitchingProtocols);
345  if (switching)
346  upgradeHeader = rep->header.getList(Http::HdrType::UPGRADE);
347 
348  // apply selected clientReplyContext::buildReplyHeader() mods
349  // it is not clear what headers are required for control messages
351  // paranoid: ContentLengthInterpreter has cleaned non-generated replies
353 
354  if (switching && /* paranoid: */ upgradeHeader.size()) {
355  rep->header.putStr(Http::HdrType::UPGRADE, upgradeHeader.termedBuf());
356  rep->header.putStr(Http::HdrType::CONNECTION, "upgrade");
357  // keep-alive is redundant, breaks some 101 (Switching Protocols) recipients
358  } else {
359  rep->header.putStr(Http::HdrType::CONNECTION, "keep-alive");
360  }
361 
362  httpHdrMangleList(&rep->header, http->request, http->al, ROR_REPLY);
363 
364  MemBuf *mb = rep->pack();
365 
366  debugs(11, 2, "HTTP Client " << clientConnection);
367  debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
368 
369  Comm::Write(clientConnection, mb, call);
370 
371  delete mb;
372  return true;
373 }
374 
375 void
377 {
378  const auto context = pipeline.front();
379  assert(context);
380  const auto http = context->http;
381  assert(http);
382  assert(http->request);
383 
384  stopReading();
385  Must(!writer);
386 
387  switchToTunnel(http->request, clientConnection,
388  server.connection(), server.preReadServerBytes);
389 }
390 
393 {
394  return new Http1::Server(xact, false);
395 }
396 
399 {
400  return new Http1::Server(xact, true);
401 }
402 
int caseCmp(char const *) const
Definition: String.cc:266
void setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
Definition: Http1Server.cc:204
Definition: parse.c:104
void setLogUriToRawUri(const char *, const HttpRequestMethod &)
char * buf
Definition: MemBuf.h:134
@ ERR_INVALID_URL
Definition: forward.h:45
Http::Stream * parseOneRequest() override
Definition: Http1Server.cc:74
@ scMethodNotAllowed
Definition: StatusCode.h:50
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
bool parseHeader(Http1::Parser &hp)
Definition: HttpRequest.cc:711
@ scBadRequest
Definition: StatusCode.h:45
struct SquidConfig::@98 accessList
bool hasNotes() const
Definition: HttpRequest.h:247
void removeHopByHopEntries()
Definition: HttpHeader.cc:1710
HttpHeader header
Definition: Message.h:74
time_t clientIdlePconn
Definition: SquidConfig.h:119
void initRequest(HttpRequest *)
RequestFlags flags
Definition: HttpRequest.h:141
Server(const MasterXaction::Pointer &xact, const bool beHttpsServer)
Definition: Http1Server.cc:28
@ ENTRY_BAD_LENGTH
Definition: enums.h:109
void processParsedRequest(Http::StreamPointer &context) override
start processing a freshly parsed request
Definition: Http1Server.cc:241
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
@ ERR_UNSUP_REQ
Definition: forward.h:44
Definition: Server.h:31
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:788
void noteMoreBodySpaceAvailable(BodyPipe::Pointer) override
Definition: Http1Server.cc:61
void noteBodyConsumerAborted(BodyPipe::Pointer) override=0
time_t request_start_timeout
Definition: SquidConfig.h:125
C * getRaw() const
Definition: RefCount.h:89
uint16_t flags
Definition: Store.h:231
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
void updateOrAddStr(Http::HdrType, const SBuf &)
Definition: HttpHeader.cc:1083
Http::StatusLine sline
Definition: HttpReply.h:56
StatusCode
Definition: StatusCode.h:20
err_type
Definition: forward.h:14
@ ERR_UNSUP_HTTPVERSION
Definition: forward.h:42
static int port
Definition: ldap_backend.cc:70
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
void syncAle(HttpRequest *adaptedRequest, const char *logUri) const override
assigns uninitialized adapted_request and url ALE components
ConnStateData * NewServer(const MasterXactionPointer &xact)
create a new HTTP connection handler; never returns NULL
Definition: Http1Server.cc:392
void removeIrrelevantContentLength()
Some response status codes prohibit sending Content-Length (RFC 7230 section 3.3.2).
Definition: HttpReply.cc:646
@ ERR_PROTOCOL_UNKNOWN
Definition: forward.h:73
@ scSwitchingProtocols
Definition: StatusCode.h:23
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
void noteTakeServerConnectionControl(ServerConnectionContext) override
Definition: Http1Server.cc:376
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
MemBuf * pack() const
Definition: HttpReply.cc:112
ConnStateData * NewServer(const MasterXactionPointer &xact)
create a new HTTPS connection handler; never returns NULL
Definition: Http1Server.cc:398
Definition: MemBuf.h:23
time_t idleTimeout() const override
timeout to use when waiting for the next request
Definition: Http1Server.cc:36
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
void handleReply(HttpReply *rep, StoreIOBuffer receivedData) override
Definition: Http1Server.cc:296
void start() override
called by AsyncStart; do not call directly
void switchToTunnel(HttpRequest *request, const Comm::ConnectionPointer &clientConn, const Comm::ConnectionPointer &srvConn, const SBuf &preReadServerData)
Definition: tunnel.cc:1540
void set(const AnyP::ProtocolVersion &newVersion, Http::StatusCode newStatus, const char *newReason=nullptr)
Definition: StatusLine.cc:35
virtual int pipelinePrefetchMax() const
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
@ scRequestHeaderFieldsTooLarge
Definition: StatusCode.h:71
#define assert(EX)
Definition: assert.h:17
@ scUriTooLong
Definition: StatusCode.h:59
@ scContinue
Definition: StatusCode.h:22
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:70
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
void clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
void noteBodyConsumerAborted(BodyPipe::Pointer) override
Definition: Http1Server.cc:289
SBuf & authority(bool requirePort=false) const
Definition: Uri.cc:689
CBDATA_NAMESPACED_CLASS_INIT(Http1, Server)
bool chunkedReply
Definition: RequestFlags.h:99
int pipelinePrefetchMax() const override
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
Definition: Http1Server.cc:230
@ scHttpVersionNotSupported
Definition: StatusCode.h:78
void start() override
called by AsyncStart; do not call directly
Definition: Http1Server.cc:42
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
acl_access * forceRequestBodyContinuation
Definition: SquidConfig.h:404
noteTakeServerConnectionControl() callback parameter
Definition: client_side.h:213
void append(const NotePairs *src)
Append the entries of the src NotePairs list to our list.
Definition: Notes.cc:379
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:592
const char * termedBuf() const
Definition: SquidString.h:92
bool allowed() const
Definition: Acl.h:82
bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) override
handle a control message received by context from a peer and call back
Definition: Http1Server.cc:328
bool buildHttpRequest(Http::StreamPointer &context)
Definition: Http1Server.cc:95
@ ERR_TOO_BIG
Definition: forward.h:40
struct SquidConfig::@84 Timeout
bool forcedBodyContinuation
whether we have responded with HTTP 100 or FTP 150 already
Definition: HttpRequest.h:194
int has(Http::HdrType id) const
Definition: HttpHeader.cc:937
static char server[MAXLINE]
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:995
size_type size() const
Definition: SquidString.h:73
@ METHOD_NONE
Definition: MethodType.h:22
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:528
void setReplyToError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const char *, Auth::UserRequest::Pointer)
builds error using clientBuildError() and calls setReplyToError() below
@ ERR_INVALID_REQ
Definition: forward.h:43
#define Must(condition)
Definition: TextException.h:75
const AccessLogEntry::Pointer al
access.log entry
StoreEntry * storeEntry() const
Manages a connection from an HTTP/1 or HTTP/0.9 client.
Definition: Http1Server.h:23
bool isOpen(const int fd)
Definition: comm.cc:89
NotePairs::Pointer notes()
Definition: HttpRequest.cc:752
@ scExpectationFailed
Definition: StatusCode.h:62
void proceedAfterBodyContinuation(Http::StreamPointer context)
Definition: Http1Server.cc:223
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
common part of ParseBws() and ParseStrctBws()
Definition: forward.h:17
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.
static Pointer MakePortful(const AnyP::PortCfgPointer &aPort)
Definition: MasterXaction.h:54

 

Introduction

Documentation

Support

Miscellaneous