2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 33 Client-side Routines */
11 #ifndef SQUID_SRC_CLIENT_SIDE_H
12 #define SQUID_SRC_CLIENT_SIDE_H
14 #include "acl/ChecklistFiller.h"
15 #include "base/RunnersRegistry.h"
16 #include "clientStreamForward.h"
18 #include "error/Error.h"
19 #include "helper/forward.h"
20 #include "http/forward.h"
21 #include "HttpControlMsg.h"
22 #include "ipc/FdNotes.h"
23 #include "log/forward.h"
24 #include "proxyp/forward.h"
25 #include "sbuf/SBuf.h"
26 #include "servers/Server.h"
28 #include "auth/UserRequest.h"
30 #include "security/KeyLogger.h"
32 #include "security/forward.h"
33 #include "security/Handshake.h"
34 #include "ssl/support.h"
37 #include "MessageBucket.h"
42 class ClientHttpRequest
;
43 class HttpHdrRangeSpec
;
46 typedef RefCount
<MasterXaction
> MasterXactionPointer
;
56 * Legacy Server code managing a connection to a client.
58 * NP: presents AsyncJob API but does not operate autonomously as a Job.
59 * So Must() is not safe to use.
61 * Multiple requests (up to pipeline_prefetch) can be pipelined.
62 * This object is responsible for managing which one is currently being
63 * fulfilled and what happens to the queue if the current one causes the client
64 * connection to be closed early.
66 * Act as a manager for the client connection and passes data in buffer to a
67 * Parser relevant to the state (message headers vs body) that is being
70 * Performs HTTP message processing to kick off the actual HTTP request
71 * handling objects (Http::Stream, ClientHttpRequest, HttpRequest).
73 * Performs SSL-Bump processing for switching between HTTP and HTTPS protocols.
75 * To terminate a ConnStateData close() the client Comm::Connection it is
76 * managing, or for graceful half-close use the stopReceiving() or
77 * stopSending() methods.
81 public HttpControlMsgSink
,
82 public Acl::ChecklistFiller
,
83 private IndependentRunner
87 explicit ConnStateData(const MasterXactionPointer
&xact
);
88 ~ConnStateData() override
;
91 void receivedFirstByte() override
;
92 bool handleReadData() override
;
93 void afterClientRead() override
;
94 void afterClientWrite(size_t) override
;
96 /* HttpControlMsgSink API */
97 void sendControlMsg(HttpControlMsg
) override
;
98 void doneWithControlMsg() override
;
101 void readNextRequest();
103 /// try to make progress on a transaction or read more I/O
108 Http1::TeChunkedParser
*bodyParser
= nullptr; ///< parses HTTP/1.1 chunked request body
110 /** number of body bytes we need to comm_read for the "current" request
112 * \retval 0 We do not need to read any [more] body bytes
113 * \retval negative May need more but do not know how many; could be zero!
114 * \retval positive Need to read exactly that many more body bytes
116 int64_t mayNeedToReadMoreBody() const;
120 * Fetch the user details for connection based authentication
121 * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
123 const Auth::UserRequest::Pointer
&getAuth() const { return auth_
; }
126 * Set the user details for connection-based authentication to use from now until connection closure.
128 * Any change to existing credentials shows that something invalid has happened. Such as:
129 * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
130 * - NTLM/Negotiate auth was violated by the per-request headers being for another user
131 * - SSL-Bump CONNECT tunnel with persistent credentials has ended
133 void setAuth(const Auth::UserRequest::Pointer
&aur
, const char *cause
);
136 Ip::Address log_addr
;
139 bool readMore
= true; ///< needs comm_read (for this request or new requests)
140 bool swanSang
= false; // XXX: temporary flag to check proper cleanup
143 Comm::ConnectionPointer serverConnection
; /* pinned server side connection */
144 char *host
= nullptr; ///< host name of pinned connection
145 AnyP::Port port
; ///< destination port of the request that caused serverConnection
146 bool pinned
= false; ///< this connection was pinned
147 bool auth
= false; ///< pinned for www authentication
148 bool reading
= false; ///< we are monitoring for peer connection closure
149 bool zeroReply
= false; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
150 bool peerAccessDenied
= false; ///< cache_peer_access denied pinned connection reuse
151 CachePeer
*peer() const { return serverConnection
? serverConnection
->getPeer() : nullptr; }
152 AsyncCall::Pointer readHandler
; ///< detects serverConnection closure
153 AsyncCall::Pointer closeHandler
; ///< The close handler for pinned server side connection
156 bool transparent() const;
158 /// true if we stopped receiving the request
159 const char *stoppedReceiving() const { return stoppedReceiving_
; }
160 /// true if we stopped sending the response
161 const char *stoppedSending() const { return stoppedSending_
; }
162 /// note request receiving error and close as soon as we write the response
163 void stopReceiving(const char *error
);
164 /// note response sending error and close as soon as we read the request
165 void stopSending(const char *error
);
167 /// (re)sets timeout for receiving more bytes from the client
168 void resetReadTimeout(time_t timeout
);
169 /// (re)sets client_lifetime timeout
170 void extendLifetime();
172 void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
175 BodyPipe::Pointer
expectRequestBody(int64_t size
);
176 void noteMoreBodySpaceAvailable(BodyPipe::Pointer
) override
= 0;
177 void noteBodyConsumerAborted(BodyPipe::Pointer
) override
= 0;
179 bool handleRequestBodyData();
181 /// parameters for the async notePinnedConnectionBecameIdle() call
182 class PinnedIdleContext
185 PinnedIdleContext(const Comm::ConnectionPointer
&conn
, const HttpRequest::Pointer
&req
): connection(conn
), request(req
) {}
187 Comm::ConnectionPointer connection
; ///< to-server connection to be pinned
188 HttpRequest::Pointer request
; ///< to-server request that initiated serverConnection
191 /// Called when a pinned connection becomes available for forwarding the next request.
192 void notePinnedConnectionBecameIdle(PinnedIdleContext pic
);
193 /// Forward future client requests using the given to-server connection.
194 /// The connection is still being used by the current client request.
195 void pinBusyConnection(const Comm::ConnectionPointer
&pinServerConn
, const HttpRequest::Pointer
&request
);
196 /// Undo pinConnection() and, optionally, close the pinned connection.
197 void unpinConnection(const bool andClose
);
199 /// \returns validated pinned to-server connection, stopping its monitoring
200 /// \throws a newly allocated ErrorState if validation fails
201 static Comm::ConnectionPointer
BorrowPinnedConnection(HttpRequest
*, const AccessLogEntryPointer
&);
202 /// \returns the pinned CachePeer if one exists, nil otherwise
203 CachePeer
*pinnedPeer() const { return pinning
.peer(); }
204 bool pinnedAuth() const {return pinning
.auth
;}
206 /// called just before a FwdState-dispatched job starts using connection
207 virtual void notePeerConnection(Comm::ConnectionPointer
) {}
209 // pining related comm callbacks
210 virtual void clientPinnedConnectionClosed(const CommCloseCbParams
&io
);
212 /// noteTakeServerConnectionControl() callback parameter
213 class ServerConnectionContext
{
215 ServerConnectionContext(const Comm::ConnectionPointer
&conn
, const SBuf
&post101Bytes
) : preReadServerBytes(post101Bytes
), conn_(conn
) { conn_
->enterOrphanage(); }
217 /// gives to-server connection to the new owner
218 Comm::ConnectionPointer
connection() { conn_
->leaveOrphanage(); return conn_
; }
220 SBuf preReadServerBytes
; ///< post-101 bytes received from the server
223 friend std::ostream
&operator <<(std::ostream
&, const ServerConnectionContext
&);
224 Comm::ConnectionPointer conn_
; ///< to-server connection
227 /// Gives us the control of the Squid-to-server connection.
228 /// Used, for example, to initiate a TCP tunnel after protocol switching.
229 virtual void noteTakeServerConnectionControl(ServerConnectionContext
) {}
232 void clientReadFtpData(const CommIoCbParams
&io
);
233 void connStateClosed(const CommCloseCbParams
&io
);
234 void requestTimeout(const CommTimeoutCbParams
¶ms
);
235 void lifetimeTimeout(const CommTimeoutCbParams
¶ms
);
238 void start() override
;
239 bool doneAll() const override
{ return BodyProducer::doneAll() && false;}
240 void swanSong() override
;
241 void callException(const std::exception
&) override
;
243 /// Changes state so that we close the connection and quit after serving
244 /// the client-side-detected error response instead of getting stuck.
245 void quitAfterError(HttpRequest
*request
); // meant to be private
247 /// The caller assumes responsibility for connection closure detection.
248 void stopPinnedConnectionMonitoring();
250 /// Starts or resumes accepting a TLS connection. TODO: Make this helper
251 /// method protected after converting clientNegotiateSSL() into a method.
252 Security::IoResult
acceptTls();
254 /// the second part of old httpsAccept, waiting for future HttpsServer home
255 void postHttpsAccept();
258 /// Initializes and starts a peek-and-splice negotiation with the SSL client
259 void startPeekAndSplice();
261 /// Called when a peek-and-splice step finished. For example after
262 /// server SSL certificates received and fake server SSL certificates
264 void doPeekAndSpliceStep();
265 /// called by FwdState when it is done bumping the server
266 void httpsPeeked(PinnedIdleContext pic
);
268 /// Splice a bumped client connection on peek-and-splice mode
271 /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
272 void getSslContextStart();
274 /// finish configuring the newly created SSL context"
275 void getSslContextDone(Security::ContextPointer
&);
277 /// Callback function. It is called when squid receive message from ssl_crtd.
278 static void sslCrtdHandleReplyWrapper(void *data
, const Helper::Reply
&reply
);
279 /// Process response from ssl_crtd.
280 void sslCrtdHandleReply(const Helper::Reply
&reply
);
282 void switchToHttps(ClientHttpRequest
*, Ssl::BumpMode bumpServerMode
);
283 void parseTlsHandshake();
284 bool switchedToHttps() const { return switchedToHttps_
; }
285 Ssl::ServerBump
*serverBump() {return sslServerBump
;}
286 inline void setServerBump(Ssl::ServerBump
*srvBump
) {
288 sslServerBump
= srvBump
;
290 assert(sslServerBump
== srvBump
);
292 const SBuf
&sslCommonName() const {return sslCommonName_
;}
293 void resetSslCommonName(const char *name
) {sslCommonName_
= name
;}
294 const SBuf
&tlsClientSni() const { return tlsClientSni_
; }
295 /// Fill the certAdaptParams with the required data for certificate adaptation
296 /// and create the key for storing/retrieve the certificate to/from the cache
297 void buildSslCertGenerationParams(Ssl::CertificateProperties
&certProperties
);
298 /// Called when the client sends the first request on a bumped connection.
299 /// Returns false if no [delayed] error should be written to the client.
300 /// Otherwise, writes the error to the client and returns true. Also checks
301 /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
302 bool serveDelayedError(Http::Stream
*);
304 Ssl::BumpMode sslBumpMode
= Ssl::bumpEnd
; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
306 /// Tls parser to use for client HELLO messages parsing on bumped
308 Security::HandshakeParser tlsParser
;
310 bool switchedToHttps() const { return false; }
312 char *prepareTlsSwitchingURL(const Http1::RequestParserPointer
&hp
);
314 /// registers a newly created stream
315 void add(const Http::StreamPointer
&context
);
317 /// handle a control message received by context from a peer and call back
318 virtual bool writeControlMsgAndCall(HttpReply
*rep
, AsyncCall::Pointer
&call
) = 0;
320 /// ClientStream calls this to supply response header (once) and data
321 /// for the current Http::Stream.
322 virtual void handleReply(HttpReply
*header
, StoreIOBuffer receivedData
) = 0;
324 /// remove no longer needed leading bytes from the input buffer
325 void consumeInput(const size_t byteCount
);
327 /* TODO: Make the methods below (at least) non-public when possible. */
329 /// stop parsing the request and create context for relaying error info
330 Http::Stream
*abortRequestParsing(const char *const errUri
);
332 /// generate a fake CONNECT request with the given payload
333 /// at the beginning of the client I/O buffer
334 bool fakeAConnectRequest(const char *reason
, const SBuf
&payload
);
336 /// generates and sends to tunnel.cc a fake request with a given payload
337 bool initiateTunneledRequest(HttpRequest::Pointer
const &cause
, const char *reason
, const SBuf
&payload
);
339 /// whether we should start saving inBuf client bytes in anticipation of
340 /// tunneling them to the server later (on_unsupported_protocol)
341 bool shouldPreserveClientData() const;
343 /// build a fake http request
344 ClientHttpRequest
*buildFakeRequest(SBuf
&useHost
, AnyP::KnownPort usePort
, const SBuf
&payload
);
346 /// From-client handshake bytes (including bytes at the beginning of a
347 /// CONNECT tunnel) which we may need to forward as-is if their syntax does
348 /// not match the expected TLS or HTTP protocol (on_unsupported_protocol).
349 SBuf preservedClientData
;
351 /* Registered Runner API */
352 void startShutdown() override
;
353 void endingShutdown() override
;
355 /// \returns existing non-empty connection annotations,
356 /// creates and returns empty annotations otherwise
357 NotePairs::Pointer
notes();
358 bool hasNotes() const { return bool(theNotes
) && !theNotes
->empty(); }
360 const ProxyProtocol::HeaderPointer
&proxyProtocolHeader() const { return proxyProtocolHeader_
; }
362 /// if necessary, stores new error information (if any)
363 void updateError(const Error
&);
365 /// emplacement/convenience wrapper for updateError(const Error &)
366 void updateError(const err_type c
, const ErrorDetailPointer
&d
) { updateError(Error(c
, d
)); }
368 /* Acl::ChecklistFiller API */
369 void fillChecklist(ACLFilledChecklist
&) const override
;
371 /// fillChecklist() obligations not fulfilled by the front request
372 /// TODO: This is a temporary ACLFilledChecklist::setConn() callback to
373 /// allow filling checklist using our non-public information sources. It
374 /// should be removed as unnecessary by making ACLs extract the information
375 /// they need from the ACLFilledChecklist::conn() without filling/copying.
376 void fillConnectionLevelDetails(ACLFilledChecklist
&) const;
378 // Exposed to be accessible inside the ClientHttpRequest constructor.
379 // TODO: Remove. Make sure there is always a suitable ALE instead.
380 /// a problem that occurred without a request (e.g., while parsing headers)
383 /// managers logging of the being-accepted TLS connection secrets
384 Security::KeyLogger keyLogger
;
387 void startDechunkingRequest();
388 void finishDechunkingRequest(bool withSuccess
);
389 void abortChunkedRequestBody(const err_type error
);
390 err_type
handleChunkedRequestBody();
392 /// ConnStateData-specific part of BorrowPinnedConnection()
393 Comm::ConnectionPointer
borrowPinnedConnection(HttpRequest
*, const AccessLogEntryPointer
&);
395 void startPinnedConnectionMonitoring();
396 void clientPinnedConnectionRead(const CommIoCbParams
&io
);
398 /// Handles a ready-for-reading TLS squid-to-server connection that
399 /// we thought was idle.
400 /// \return false if and only if the connection should be closed.
401 bool handleIdleClientPinnedTlsRead();
404 /// Parse an HTTP request
405 /// \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
406 /// to 1 if the request was correctly parsed
407 /// \param[in] hp an Http1::RequestParser
408 /// \return NULL on incomplete requests,
409 /// a Http::Stream on success or failure.
410 /// TODO: Move to HttpServer. Warning: Move requires large code nonchanges!
411 Http::Stream
*parseHttpRequest(const Http1::RequestParserPointer
&);
413 /// parse input buffer prefix into a single transfer protocol request
414 /// return NULL to request more header bytes (after checking any limits)
415 /// use abortRequestParsing() to handle parsing errors w/o creating request
416 virtual Http::Stream
*parseOneRequest() = 0;
418 /// start processing a freshly parsed request
419 virtual void processParsedRequest(Http::StreamPointer
&) = 0;
421 /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
422 virtual int pipelinePrefetchMax() const;
424 /// timeout to use when waiting for the next request
425 virtual time_t idleTimeout() const = 0;
427 /// Perform client data lookups that depend on client src-IP.
428 /// The PROXY protocol may require some data input first.
429 void whenClientIpKnown();
431 BodyPipe::Pointer bodyPipe
; ///< set when we are reading request body
433 /// whether preservedClientData is valid and should be kept up to date
434 bool preservingClientData_
= false;
436 bool tunnelOnError(const err_type
);
440 void terminateAll(const Error
&, const LogTagsErrors
&) override
;
441 bool shouldCloseOnEof() const override
;
445 void parseRequests();
446 void clientAfterReadingRequests();
447 bool concurrentRequestQueueFilled() const;
449 void pinConnection(const Comm::ConnectionPointer
&pinServerConn
, const HttpRequest
&request
);
451 /* PROXY protocol functionality */
452 bool proxyProtocolValidateClient();
453 bool parseProxyProtocolHeader();
454 bool proxyProtocolError(const char *reason
);
457 /// \returns a pointer to the matching cached TLS context or nil
458 Security::ContextPointer
getTlsContextFromCache(const SBuf
&cacheKey
, const Ssl::CertificateProperties
&certProperties
);
460 /// Attempts to add a given TLS context to the cache, replacing the old
461 /// same-key context, if any
462 void storeTlsContextToCache(const SBuf
&cacheKey
, Security::ContextPointer
&ctx
);
463 void handleSslBumpHandshakeError(const Security::IoResult
&);
466 /// whether PROXY protocol header is still expected
467 bool needProxyProtocolHeader_
= false;
469 /// the parsed PROXY protocol header
470 ProxyProtocol::HeaderPointer proxyProtocolHeader_
;
473 /// some user details that can be used to perform authentication on this connection
474 Auth::UserRequest::Pointer auth_
;
478 bool switchedToHttps_
= false;
479 bool parsingTlsHandshake
= false; ///< whether we are getting/parsing TLS Hello bytes
480 /// The number of parsed HTTP requests headers on a bumped client connection
481 uint64_t parsedBumpedRequestCount
= 0;
483 // TODO: Replace tlsConnectHostOrIp and tlsConnectPort with CONNECT request AnyP::Uri
484 /// The TLS server host name appears in CONNECT request or the server ip address for the intercepted requests
485 SBuf tlsConnectHostOrIp
; ///< The TLS server host name as passed in the CONNECT request
486 AnyP::Port tlsConnectPort
; ///< The TLS server port number as passed in the CONNECT request
487 SBuf sslCommonName_
; ///< CN name for SSL certificate generation
489 /// TLS client delivered SNI value. Empty string if none has been received.
491 SBuf sslBumpCertKey
; ///< Key to use to store/retrieve generated certificate
493 /// HTTPS server cert. fetching state for bump-ssl-server-first
494 Ssl::ServerBump
*sslServerBump
= nullptr;
495 Ssl::CertSignAlgorithm signAlgorithm
= Ssl::algSignTrusted
; ///< The signing algorithm to use
498 /// the reason why we no longer write the response or nil
499 const char *stoppedSending_
= nullptr;
500 /// the reason why we no longer read the request or nil
501 const char *stoppedReceiving_
= nullptr;
502 /// Connection annotations, clt_conn_tag and other tags are stored here.
503 /// If set, are propagated to the current and all future master transactions
504 /// on the connection.
505 NotePairs::Pointer theNotes
;
508 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion
, const char *end
= nullptr);
510 int varyEvaluateMatch(StoreEntry
* entry
, HttpRequest
* req
);
512 /// accept requests to a given port and inform subCall about them
513 void clientStartListeningOn(AnyP::PortCfgPointer
&port
, const RefCount
< CommCbFunPtrCallT
<CommAcceptCbPtrFun
> > &subCall
, const Ipc::FdNoteId noteId
);
515 void clientOpenListenSockets(void);
516 void clientConnectionsClose(void);
517 void httpRequestFree(void *);
519 /// decide whether to expect multiple requests on the corresponding connection
520 void clientSetKeepaliveFlag(ClientHttpRequest
*http
);
522 /// append a "part" HTTP header (as in a multi-part/range reply) to the buffer
523 void clientPackRangeHdr(const HttpReplyPointer
&, const HttpHdrRangeSpec
*, String boundary
, MemBuf
*);
525 /// put terminating boundary for multiparts to the buffer
526 void clientPackTermBound(String boundary
, MemBuf
*);
528 /* misplaced declaratrions of Stream callbacks provided/used by client side */
529 CSR clientGetMoreData
;
530 CSS clientReplyStatus
;
531 CSD clientReplyDetach
;
532 CSCB clientSocketRecipient
;
533 CSD clientSocketDetach
;
535 void clientProcessRequest(ConnStateData
*, const Http1::RequestParserPointer
&, Http::Stream
*);
536 void clientProcessRequestFinished(ConnStateData
*, const HttpRequest::Pointer
&);
537 void clientPostHttpsAccept(ConnStateData
*);
539 std::ostream
&operator <<(std::ostream
&os
, const ConnStateData::PinnedIdleContext
&pic
);
540 std::ostream
&operator <<(std::ostream
&, const ConnStateData::ServerConnectionContext
&);
542 #endif /* SQUID_SRC_CLIENT_SIDE_H */