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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors