Squid Web Cache master
Loading...
Searching...
No Matches
Http1Server.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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 "HeaderMangling.h"
20#include "http/Stream.h"
21#include "servers/Http1Server.h"
22#include "SquidConfig.h"
23#include "Store.h"
24#include "tunnel.h"
25
27
28Http::One::Server::Server(const MasterXaction::Pointer &xact, bool beHttpsServer):
29 AsyncJob("Http1::Server"),
30 ConnStateData(xact),
31 isHttpsServer(beHttpsServer)
32{
33}
34
35time_t
40
41void
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
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
60void
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
94bool
96{
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) {
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))
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
203void
204Http::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
222void
224{
225 debugs(33, 5, "Body Continuation written");
226 clientProcessRequest(this, parser_, context.getRaw());
227}
228
229int
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
240void
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
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
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
288void
290{
292 stopReceiving("virgin request body consumer aborted"); // closes ASAP
293}
294
295void
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
327bool
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
375void
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
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.
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
@ ROR_REPLY
class SquidConfig Config
#define Must(condition)
#define assert(EX)
Definition assert.h:17
static char server[MAXLINE]
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
Definition cbdata.h:333
Acl::Answer const & fastCheck()
Definition Checklist.cc:298
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:82
SBuf & authority(bool requirePort=false) const
Definition Uri.cc:721
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
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 noteBodyConsumerAborted(BodyPipe::Pointer) override=0
bundles HTTP 1xx reply and the "successfully forwarded" callback
void removeHopByHopEntries()
void putStr(Http::HdrType id, const char *str)
String getList(Http::HdrType id) const
int has(Http::HdrType id) const
void updateOrAddStr(Http::HdrType, const SBuf &)
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:646
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
bool parseHeader(Http1::Parser &hp)
RequestFlags flags
bool forcedBodyContinuation
whether we have responded with HTTP 100 or FTP 150 already
NotePairs::Pointer notes()
bool hasNotes() const
AnyP::Uri url
the request URI
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
Server(const MasterXaction::Pointer &xact, const bool beHttpsServer)
void proceedAfterBodyContinuation(Http::StreamPointer context)
bool buildHttpRequest(Http::StreamPointer &context)
void setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
void noteTakeServerConnectionControl(ServerConnectionContext) override
void processParsedRequest(Http::StreamPointer &context) override
start processing a freshly parsed request
time_t idleTimeout() const override
timeout to use when waiting for the next request
bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) override
handle a control message received by context from a peer and call back
void start() override
called by AsyncStart; do not call directly
Http::Stream * parseOneRequest() override
void noteBodyConsumerAborted(BodyPipe::Pointer) override
void noteMoreBodySpaceAvailable(BodyPipe::Pointer) override
int pipelinePrefetchMax() const override
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
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)
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:384
C * getRaw() const
Definition RefCount.h:89
time_t request_start_timeout
struct SquidConfig::@77 Timeout
time_t clientIdlePconn
struct SquidConfig::@91 accessList
acl_access * forceRequestBodyContinuation
uint16_t flags
Definition Store.h:231
char const * termedBuf() const
Definition SquidString.h:93
int caseCmp(char const *) const
Definition String.cc:273
size_type size() const
Definition SquidString.h:74
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 clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition comm.cc:594
bool isOpen(const int fd)
Definition comm.cc:91
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define EBIT_TEST(flag, bit)
Definition defines.h:67
static int port
@ ENTRY_BAD_LENGTH
Definition enums.h:109
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
common part of ParseBws() and ParseStrctBws()
Definition forward.h:17
StatusCode
Definition StatusCode.h:20
@ scUriTooLong
Definition StatusCode.h:59
@ scHttpVersionNotSupported
Definition StatusCode.h:78
@ scBadRequest
Definition StatusCode.h:45
@ scExpectationFailed
Definition StatusCode.h:62
@ scMethodNotAllowed
Definition StatusCode.h:50
@ scContinue
Definition StatusCode.h:22
@ scSwitchingProtocols
Definition StatusCode.h:23
@ scRequestHeaderFieldsTooLarge
Definition StatusCode.h:71
ConnStateData * NewServer(const MasterXactionPointer &xact)
create a new HTTP connection handler; never returns NULL
@ METHOD_NONE
Definition MethodType.h:22
AnyP::ProtocolVersion ProtocolVersion()
ConnStateData * NewServer(const MasterXactionPointer &xact)
create a new HTTPS connection handler; never returns NULL
Definition parse.c:104
void switchToTunnel(HttpRequest *request, const Comm::ConnectionPointer &clientConn, const Comm::ConnectionPointer &srvConn, const SBuf &preReadServerData)
Definition tunnel.cc:1615