2 * Copyright (C) 1996-2021 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 26 Secure Sockets Layer Proxy */
12 #include "acl/FilledChecklist.h"
13 #include "base/CbcPointer.h"
14 #include "CachePeer.h"
16 #include "client_side.h"
17 #include "client_side_request.h"
18 #include "clients/HttpTunneler.h"
20 #include "comm/Connection.h"
21 #include "comm/ConnOpener.h"
22 #include "comm/Read.h"
23 #include "comm/Write.h"
24 #include "errorpage.h"
29 #include "HappyConnOpener.h"
31 #include "http/Stream.h"
32 #include "HttpRequest.h"
33 #include "icmp/net_db.h"
34 #include "ip/QosConfig.h"
37 #include "neighbors.h"
38 #include "PeerSelectState.h"
39 #include "ResolvedPeers.h"
40 #include "sbuf/SBuf.h"
41 #include "security/BlindPeerConnector.h"
42 #include "SquidConfig.h"
43 #include "SquidTime.h"
44 #include "StatCounters.h"
47 #include "ssl/ServerBump.h"
58 * TunnelStateData is the state engine performing the tasks for
59 * setup of a TCP tunnel from an existing open client FD to a server
60 * then shuffling binary data between the resulting FD pair.
63 * TODO 1: implement a read/write API on ConnStateData to send/receive blocks
64 * of pre-formatted data. Then we can use that as the client side of the tunnel
65 * instead of re-implementing it here and occasionally getting the ConnStateData
66 * read/write state wrong.
68 * TODO 2: then convert this into a AsyncJob, possibly a child of 'Server'
70 class TunnelStateData
: public PeerSelectionInitiator
72 CBDATA_CHILD(TunnelStateData
);
75 TunnelStateData(ClientHttpRequest
*);
76 virtual ~TunnelStateData();
77 TunnelStateData(const TunnelStateData
&); // do not implement
78 TunnelStateData
&operator =(const TunnelStateData
&); // do not implement
81 static void ReadClient(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
);
82 static void ReadServer(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
);
83 static void WriteClientDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
);
84 static void WriteServerDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
);
86 bool noConnections() const;
87 /// closes both client and server connections
88 void closeConnections();
91 CbcPointer
<ClientHttpRequest
> http
;
92 HttpRequest::Pointer request
;
93 AccessLogEntryPointer al
;
95 const char * getHost() const {
96 return (server
.conn
!= NULL
&& server
.conn
->getPeer() ? server
.conn
->getPeer()->host
: request
->url
.host());
99 /// Whether the client sent a CONNECT request to us.
100 bool clientExpectsConnectResponse() const {
101 // If we are forcing a tunnel after receiving a client CONNECT, then we
102 // have already responded to that CONNECT before tunnel.cc started.
103 if (request
&& request
->flags
.forceTunnel
)
106 // We are bumping and we had already send "OK CONNECTED"
107 if (http
.valid() && http
->getConn() && http
->getConn()->serverBump() && http
->getConn()->serverBump()->at(XactionStep::tlsBump2
, XactionStep::tlsBump3
))
110 return !(request
!= NULL
&&
111 (request
->flags
.interceptTproxy
|| request
->flags
.intercepted
));
114 /// starts connecting to the next hop, either for the first time or while
115 /// recovering from the previous connect failure
116 void startConnecting();
117 void closePendingConnection(const Comm::ConnectionPointer
&conn
, const char *reason
);
119 /// called when negotiations with the peer have been successfully completed
120 void notePeerReadyToShovel(const Comm::ConnectionPointer
&);
126 Connection() : len (0), buf ((char *)xmalloc(SQUID_TCP_SO_RCVBUF
)), size_ptr(NULL
), delayedLoops(0),
128 readPending(NULL
), readPendingFunc(NULL
) {}
132 /// initiates Comm::Connection ownership, including closure monitoring
133 template <typename Method
>
134 void initConnection(const Comm::ConnectionPointer
&aConn
, Method method
, const char *name
, TunnelStateData
*tunnelState
);
136 /// reacts to the external closure of our connection
139 int bytesWanted(int lower
=0, int upper
= INT_MAX
) const;
140 void bytesIn(int const &);
143 void setDelayId(DelayId
const &);
146 void error(int const xerrno
);
147 int debugLevelForError(int const xerrno
) const;
149 void dataSent (size_t amount
);
150 /// writes 'b' buffer, setting the 'writer' member to 'callback'.
151 void write(const char *b
, int size
, AsyncCall::Pointer
&callback
, FREE
* free_func
);
154 AsyncCall::Pointer writer
; ///< pending Comm::Write callback
155 uint64_t *size_ptr
; /* pointer to size in an ConnStateData for logging */
157 Comm::ConnectionPointer conn
; ///< The currently connected connection.
158 uint8_t delayedLoops
; ///< how many times a read on this connection has been postponed.
160 bool dirty
; ///< whether write() has been called (at least once)
162 // XXX: make these an AsyncCall when event API can handle them
163 TunnelStateData
*readPending
;
164 EVH
*readPendingFunc
;
172 /// the registered close handler for the connection
173 AsyncCall::Pointer closer
;
176 Connection client
, server
;
177 int *status_ptr
; ///< pointer for logging HTTP status
178 LogTags
*logTag_ptr
; ///< pointer for logging Squid processing code
180 SBuf preReadClientData
;
181 SBuf preReadServerData
;
182 time_t startTime
; ///< object creation time, before any peer selection/connection attempts
183 /// Whether we are waiting for the CONNECT request/response exchange with the peer.
184 bool waitingForConnectExchange
;
185 HappyConnOpenerPointer connOpener
; ///< current connection opening job
186 ResolvedPeersPointer destinations
; ///< paths for forwarding the request
187 bool destinationsFound
; ///< At least one candidate path found
188 /// whether another destination may be still attempted if the TCP connection
189 /// was unexpectedly closed
191 // TODO: remove after fixing deferred reads in TunnelStateData::copyRead()
192 CodeContext::Pointer codeContext
; ///< our creator context
194 // AsyncCalls which we set and may need cancelling.
196 AsyncCall::Pointer connector
; ///< a call linking us to the ConnOpener producing serverConn.
199 void copyRead(Connection
&from
, IOCB
*completion
);
201 /// continue to set up connection to a peer, going async for SSL peers
202 void connectToPeer(const Comm::ConnectionPointer
&);
203 void secureConnectionToPeer(const Comm::ConnectionPointer
&);
205 /* PeerSelectionInitiator API */
206 virtual void noteDestination(Comm::ConnectionPointer conn
) override
;
207 virtual void noteDestinationsEnd(ErrorState
*selectionError
) override
;
209 void syncHierNote(const Comm::ConnectionPointer
&server
, const char *origin
);
211 /// called when a connection has been successfully established or
212 /// when all candidate destinations have been tried and all have failed
213 void noteConnection(HappyConnOpenerAnswer
&);
215 /// whether we are waiting for HappyConnOpener
216 /// same as calls.connector but may differ from connOpener.valid()
217 bool opening() const { return connOpener
.set(); }
219 void cancelOpening(const char *reason
);
221 /// Start using an established connection
222 void connectDone(const Comm::ConnectionPointer
&conn
, const char *origin
, const bool reused
);
224 void notifyConnOpener();
226 void saveError(ErrorState
*finalError
);
227 void sendError(ErrorState
*finalError
, const char *reason
);
230 /// Gives Security::PeerConnector access to Answer in the TunnelStateData callback dialer.
231 class MyAnswerDialer
: public CallDialer
, public Security::PeerConnector::CbDialer
234 typedef void (TunnelStateData::*Method
)(Security::EncryptorAnswer
&);
236 MyAnswerDialer(Method method
, TunnelStateData
*tunnel
):
237 method_(method
), tunnel_(tunnel
), answer_() {}
240 virtual bool canDial(AsyncCall
&call
) { return tunnel_
.valid(); }
241 void dial(AsyncCall
&call
) { ((&(*tunnel_
))->*method_
)(answer_
); }
242 virtual void print(std::ostream
&os
) const {
243 os
<< '(' << tunnel_
.get() << ", " << answer_
<< ')';
246 /* Security::PeerConnector::CbDialer API */
247 virtual Security::EncryptorAnswer
&answer() { return answer_
; }
251 CbcPointer
<TunnelStateData
> tunnel_
;
252 Security::EncryptorAnswer answer_
;
257 /// callback handler for the Security::PeerConnector encryptor
258 void noteSecurityPeerConnectorAnswer(Security::EncryptorAnswer
&);
260 /// called after connection setup (including any encryption)
261 void connectedToPeer(const Comm::ConnectionPointer
&);
262 void establishTunnelThruProxy(const Comm::ConnectionPointer
&);
264 template <typename StepStart
>
265 void advanceDestination(const char *stepDescription
, const Comm::ConnectionPointer
&conn
, const StepStart
&startStep
);
267 /// \returns whether the request should be retried (nil) or the description why it should not
268 const char *checkRetry();
270 /// details of the "last tunneling attempt" failure (if it failed)
271 ErrorState
*savedError
= nullptr;
273 /// resumes operations after the (possibly failed) HTTP CONNECT exchange
274 void tunnelEstablishmentDone(Http::TunnelerAnswer
&answer
);
279 bool keepGoingAfterRead(size_t len
, Comm::Flag errcode
, int xerrno
, Connection
&from
, Connection
&to
);
280 void copy(size_t len
, Connection
&from
, Connection
&to
, IOCB
*);
281 void readServer(char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
);
282 void readClient(char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
);
283 void writeClientDone(char *buf
, size_t len
, Comm::Flag flag
, int xerrno
);
284 void writeServerDone(char *buf
, size_t len
, Comm::Flag flag
, int xerrno
);
286 void copyClientBytes();
287 void copyServerBytes();
289 /// handles client-to-Squid connection closure; may destroy us
292 /// handles Squid-to-server connection closure; may destroy us
295 /// tries connecting to another destination, if available,
296 /// otherwise, initiates the transaction termination
297 void retryOrBail(const char *context
);
300 static ERCB tunnelErrorComplete
;
301 static CLCB tunnelServerClosed
;
302 static CLCB tunnelClientClosed
;
303 static CTCB tunnelTimeout
;
304 static EVH tunnelDelayedClientRead
;
305 static EVH tunnelDelayedServerRead
;
307 /// TunnelStateData::serverClosed() wrapper
309 tunnelServerClosed(const CommCloseCbParams
¶ms
)
311 const auto tunnelState
= reinterpret_cast<TunnelStateData
*>(params
.data
);
312 tunnelState
->serverClosed();
316 TunnelStateData::serverClosed()
318 server
.noteClosure();
321 /// TunnelStateData::clientClosed() wrapper
323 tunnelClientClosed(const CommCloseCbParams
¶ms
)
325 const auto tunnelState
= reinterpret_cast<TunnelStateData
*>(params
.data
);
326 tunnelState
->clientClosed();
330 TunnelStateData::clientClosed()
332 client
.noteClosure();
338 server
.conn
->close();
341 /// destroys the tunnel (after performing potentially-throwing cleanup)
343 TunnelStateData::deleteThis()
345 assert(noConnections());
346 // ConnStateData pipeline should contain the CONNECT we are performing
347 // but it may be invalid already (bug 4392)
348 if (const auto h
= http
.valid()) {
349 if (const auto c
= h
->getConn())
350 if (const auto ctx
= c
->pipeline
.front())
356 TunnelStateData::TunnelStateData(ClientHttpRequest
*clientRequest
) :
357 startTime(squid_curtime
),
358 waitingForConnectExchange(false),
359 destinations(new ResolvedPeers()),
360 destinationsFound(false),
362 codeContext(CodeContext::Current())
364 debugs(26, 3, "TunnelStateData constructed this=" << this);
365 client
.readPendingFunc
= &tunnelDelayedClientRead
;
366 server
.readPendingFunc
= &tunnelDelayedServerRead
;
368 assert(clientRequest
);
369 url
= xstrdup(clientRequest
->uri
);
370 request
= clientRequest
->request
;
372 server
.size_ptr
= &clientRequest
->out
.size
;
373 client
.size_ptr
= &clientRequest
->al
->http
.clientRequestSz
.payloadData
;
374 status_ptr
= &clientRequest
->al
->http
.code
;
375 logTag_ptr
= &clientRequest
->logType
;
376 al
= clientRequest
->al
;
377 http
= clientRequest
;
379 client
.initConnection(clientRequest
->getConn()->clientConnection
, tunnelClientClosed
, "tunnelClientClosed", this);
381 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
382 CommTimeoutCbPtrFun(tunnelTimeout
, this));
383 commSetConnTimeout(client
.conn
, Config
.Timeout
.lifetime
, timeoutCall
);
386 TunnelStateData::~TunnelStateData()
388 debugs(26, 3, "TunnelStateData destructed this=" << this);
389 assert(noConnections());
392 cancelOpening("~TunnelStateData");
396 TunnelStateData::Connection::~Connection()
399 eventDelete(readPendingFunc
, readPending
);
405 TunnelStateData::checkRetry()
408 return "shutting down";
409 if (!FwdState::EnoughTimeToReForward(startTime
))
410 return "forwarding timeout";
412 return "not retriable";
414 return "no connections";
419 TunnelStateData::retryOrBail(const char *context
)
421 // Since no TCP payload has been passed to client or server, we may
422 // TCP-connect to other destinations (including alternate IPs).
424 assert(!server
.conn
);
426 const auto *bailDescription
= checkRetry();
427 if (!bailDescription
) {
428 if (!destinations
->empty())
429 return startConnecting(); // try connecting to another destination
432 debugs(26, 4, "wait for more destinations to try");
433 return; // expect a noteDestination*() call
436 // fall through to bail
442 request
->hier
.stopPeerClock(false);
444 // TODO: Add sendSavedErrorOr(err_type type, Http::StatusCode, context).
445 // Then, the remaining method code (below) should become the common part of
446 // sendNewError() and sendSavedErrorOr(), used in "error detected" cases.
448 saveError(new ErrorState(ERR_CANNOT_FORWARD
, Http::scInternalServerError
, request
.getRaw(), al
));
449 const auto canSendError
= Comm::IsConnOpen(client
.conn
) && !client
.dirty
&&
450 clientExpectsConnectResponse();
452 return sendError(savedError
, bailDescription
? bailDescription
: context
);
453 *status_ptr
= savedError
->httpStatus
;
458 // This is a "Comm::IsConnOpen(client.conn) but !canSendError" case.
459 // Closing the connection (after finishing writing) is the best we can do.
461 client
.conn
->close();
462 // else writeClientDone() must notice a closed server and close the client
466 TunnelStateData::Connection::bytesWanted(int lowerbound
, int upperbound
) const
469 return delayId
.bytesWanted(lowerbound
, upperbound
);
477 TunnelStateData::Connection::bytesIn(int const &count
)
479 debugs(26, 3, HERE
<< "len=" << len
<< " + count=" << count
);
481 delayId
.bytesIn(count
);
487 /// update "hierarchy" annotations with a new (possibly failed) destination
488 /// \param origin the name of the origin server we were trying to reach
490 TunnelStateData::syncHierNote(const Comm::ConnectionPointer
&conn
, const char *origin
)
492 request
->hier
.resetPeerNotes(conn
, origin
);
494 al
->hier
.resetPeerNotes(conn
, origin
);
498 TunnelStateData::Connection::debugLevelForError(int const xerrno
) const
502 if (xerrno
== ECONNRESET
)
507 if (ignoreErrno(xerrno
))
513 /* Read from server side and queue it for writing to the client */
515 TunnelStateData::ReadServer(const Comm::ConnectionPointer
&c
, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
)
517 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
518 assert(cbdataReferenceValid(tunnelState
));
519 debugs(26, 3, HERE
<< c
);
521 tunnelState
->readServer(buf
, len
, errcode
, xerrno
);
525 TunnelStateData::readServer(char *, size_t len
, Comm::Flag errcode
, int xerrno
)
527 debugs(26, 3, HERE
<< server
.conn
<< ", read " << len
<< " bytes, err=" << errcode
);
528 server
.delayedLoops
=0;
531 * Bail out early on Comm::ERR_CLOSING
532 * - close handlers will tidy up for us
535 if (errcode
== Comm::ERR_CLOSING
)
540 statCounter
.server
.all
.kbytes_in
+= len
;
541 statCounter
.server
.other
.kbytes_in
+= len
;
542 request
->hier
.notePeerRead();
545 if (keepGoingAfterRead(len
, errcode
, xerrno
, server
, client
))
546 copy(len
, server
, client
, WriteClientDone
);
550 TunnelStateData::Connection::error(int const xerrno
)
552 debugs(50, debugLevelForError(xerrno
), HERE
<< conn
<< ": read/write failure: " << xstrerr(xerrno
));
554 if (!ignoreErrno(xerrno
))
558 /* Read from client side and queue it for writing to the server */
560 TunnelStateData::ReadClient(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag errcode
, int xerrno
, void *data
)
562 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
563 assert (cbdataReferenceValid (tunnelState
));
565 tunnelState
->readClient(buf
, len
, errcode
, xerrno
);
569 TunnelStateData::readClient(char *, size_t len
, Comm::Flag errcode
, int xerrno
)
571 debugs(26, 3, HERE
<< client
.conn
<< ", read " << len
<< " bytes, err=" << errcode
);
572 client
.delayedLoops
=0;
575 * Bail out early on Comm::ERR_CLOSING
576 * - close handlers will tidy up for us
579 if (errcode
== Comm::ERR_CLOSING
)
584 statCounter
.client_http
.kbytes_in
+= len
;
587 if (keepGoingAfterRead(len
, errcode
, xerrno
, client
, server
))
588 copy(len
, client
, server
, WriteServerDone
);
591 /// Updates state after reading from client or server.
592 /// Returns whether the caller should use the data just read.
594 TunnelStateData::keepGoingAfterRead(size_t len
, Comm::Flag errcode
, int xerrno
, Connection
&from
, Connection
&to
)
596 debugs(26, 3, HERE
<< "from={" << from
.conn
<< "}, to={" << to
.conn
<< "}");
598 /* I think this is to prevent free-while-in-a-callback behaviour
600 * from.conn->close() / to.conn->close() done here trigger close callbacks which may free TunnelStateData
602 const CbcPointer
<TunnelStateData
> safetyLock(this);
604 /* Bump the source connection read timeout on any activity */
605 if (Comm::IsConnOpen(from
.conn
)) {
606 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
607 CommTimeoutCbPtrFun(tunnelTimeout
, this));
608 commSetConnTimeout(from
.conn
, Config
.Timeout
.read
, timeoutCall
);
611 /* Bump the dest connection read timeout on any activity */
612 /* see Bug 3659: tunnels can be weird, with very long one-way transfers */
613 if (Comm::IsConnOpen(to
.conn
)) {
614 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
615 CommTimeoutCbPtrFun(tunnelTimeout
, this));
616 commSetConnTimeout(to
.conn
, Config
.Timeout
.read
, timeoutCall
);
621 else if (len
== 0 || !Comm::IsConnOpen(to
.conn
)) {
622 debugs(26, 3, HERE
<< "Nothing to write or client gone. Terminate the tunnel.");
625 /* Only close the remote end if we've finished queueing data to it */
626 if (from
.len
== 0 && Comm::IsConnOpen(to
.conn
) ) {
629 } else if (cbdataReferenceValid(this)) {
637 TunnelStateData::copy(size_t len
, Connection
&from
, Connection
&to
, IOCB
*completion
)
639 debugs(26, 3, HERE
<< "Schedule Write");
640 AsyncCall::Pointer call
= commCbCall(5,5, "TunnelBlindCopyWriteHandler",
641 CommIoCbPtrFun(completion
, this));
642 to
.write(from
.buf
, len
, call
, NULL
);
645 /* Writes data from the client buffer to the server side */
647 TunnelStateData::WriteServerDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
)
649 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
650 assert (cbdataReferenceValid (tunnelState
));
651 tunnelState
->server
.writer
= NULL
;
653 tunnelState
->writeServerDone(buf
, len
, flag
, xerrno
);
657 TunnelStateData::writeServerDone(char *, size_t len
, Comm::Flag flag
, int xerrno
)
659 debugs(26, 3, HERE
<< server
.conn
<< ", " << len
<< " bytes written, flag=" << flag
);
661 if (flag
== Comm::ERR_CLOSING
)
664 request
->hier
.notePeerWrite();
667 if (flag
!= Comm::OK
) {
668 debugs(26, 4, "to-server write failed: " << xerrno
);
669 server
.error(xerrno
); // may call comm_close
675 debugs(26, 4, HERE
<< "No read input. Closing server connection.");
676 server
.conn
->close();
681 statCounter
.server
.all
.kbytes_out
+= len
;
682 statCounter
.server
.other
.kbytes_out
+= len
;
683 client
.dataSent(len
);
685 /* If the other end has closed, so should we */
686 if (!Comm::IsConnOpen(client
.conn
)) {
687 debugs(26, 4, HERE
<< "Client gone away. Shutting down server connection.");
688 server
.conn
->close();
692 const CbcPointer
<TunnelStateData
> safetyLock(this); /* ??? should be locked by the caller... */
694 if (cbdataReferenceValid(this))
698 /* Writes data from the server buffer to the client side */
700 TunnelStateData::WriteClientDone(const Comm::ConnectionPointer
&, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
)
702 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
703 assert (cbdataReferenceValid (tunnelState
));
704 tunnelState
->client
.writer
= NULL
;
706 tunnelState
->writeClientDone(buf
, len
, flag
, xerrno
);
710 TunnelStateData::Connection::dataSent(size_t amount
)
712 debugs(26, 3, HERE
<< "len=" << len
<< " - amount=" << amount
);
713 assert(amount
== (size_t)len
);
715 /* increment total object size */
723 TunnelStateData::Connection::write(const char *b
, int size
, AsyncCall::Pointer
&callback
, FREE
* free_func
)
727 Comm::Write(conn
, b
, size
, callback
, free_func
);
730 template <typename Method
>
732 TunnelStateData::Connection::initConnection(const Comm::ConnectionPointer
&aConn
, Method method
, const char *name
, TunnelStateData
*tunnelState
)
734 Must(!Comm::IsConnOpen(conn
));
736 Must(Comm::IsConnOpen(aConn
));
738 closer
= commCbCall(5, 4, name
, CommCloseCbPtrFun(method
, tunnelState
));
739 comm_add_close_handler(conn
->fd
, closer
);
743 TunnelStateData::Connection::noteClosure()
748 writer
= nullptr; // may already be nil
752 TunnelStateData::writeClientDone(char *, size_t len
, Comm::Flag flag
, int xerrno
)
754 debugs(26, 3, HERE
<< client
.conn
<< ", " << len
<< " bytes written, flag=" << flag
);
756 if (flag
== Comm::ERR_CLOSING
)
760 if (flag
!= Comm::OK
) {
761 debugs(26, 4, "from-client read failed: " << xerrno
);
762 client
.error(xerrno
); // may call comm_close
768 debugs(26, 4, HERE
<< "Closing client connection due to 0 byte read.");
769 client
.conn
->close();
774 statCounter
.client_http
.kbytes_out
+= len
;
775 server
.dataSent(len
);
777 /* If the other end has closed, so should we */
778 if (!Comm::IsConnOpen(server
.conn
)) {
779 debugs(26, 4, HERE
<< "Server has gone away. Terminating client connection.");
780 client
.conn
->close();
784 CbcPointer
<TunnelStateData
> safetyLock(this); /* ??? should be locked by the caller... */
786 if (cbdataReferenceValid(this))
791 tunnelTimeout(const CommTimeoutCbParams
&io
)
793 TunnelStateData
*tunnelState
= static_cast<TunnelStateData
*>(io
.data
);
794 debugs(26, 3, HERE
<< io
.conn
);
795 /* Temporary lock to protect our own feets (comm_close -> tunnelClientClosed -> Free) */
796 CbcPointer
<TunnelStateData
> safetyLock(tunnelState
);
798 tunnelState
->closeConnections();
802 TunnelStateData::closePendingConnection(const Comm::ConnectionPointer
&conn
, const char *reason
)
804 debugs(26, 3, "because " << reason
<< "; " << conn
);
805 assert(!server
.conn
);
806 if (IsConnOpen(conn
))
811 TunnelStateData::closeConnections()
813 if (Comm::IsConnOpen(server
.conn
))
814 server
.conn
->close();
815 if (Comm::IsConnOpen(client
.conn
))
816 client
.conn
->close();
820 tunnelDelayedClientRead(void *data
)
825 TunnelStateData
*tunnel
= static_cast<TunnelStateData
*>(data
);
826 const auto savedContext
= CodeContext::Current();
827 CodeContext::Reset(tunnel
->codeContext
);
828 tunnel
->client
.readPending
= NULL
;
829 static uint64_t counter
=0;
830 debugs(26, 7, "Client read(2) delayed " << ++counter
<< " times");
831 tunnel
->copyRead(tunnel
->client
, TunnelStateData::ReadClient
);
832 CodeContext::Reset(savedContext
);
836 tunnelDelayedServerRead(void *data
)
841 TunnelStateData
*tunnel
= static_cast<TunnelStateData
*>(data
);
842 const auto savedContext
= CodeContext::Current();
843 CodeContext::Reset(tunnel
->codeContext
);
844 tunnel
->server
.readPending
= NULL
;
845 static uint64_t counter
=0;
846 debugs(26, 7, "Server read(2) delayed " << ++counter
<< " times");
847 tunnel
->copyRead(tunnel
->server
, TunnelStateData::ReadServer
);
848 CodeContext::Reset(savedContext
);
852 TunnelStateData::copyRead(Connection
&from
, IOCB
*completion
)
854 assert(from
.len
== 0);
855 // If only the minimum permitted read size is going to be attempted
856 // then we schedule an event to try again in a few I/O cycles.
857 // Allow at least 1 byte to be read every (0.3*10) seconds.
858 int bw
= from
.bytesWanted(1, SQUID_TCP_SO_RCVBUF
);
859 // XXX: Delay pools must not delay client-to-Squid traffic (i.e. when
860 // from.readPendingFunc is tunnelDelayedClientRead()).
861 // XXX: Bug #4913: Use DeferredRead instead.
862 if (bw
== 1 && ++from
.delayedLoops
< 10) {
863 from
.readPending
= this;
864 eventAdd("tunnelDelayedServerRead", from
.readPendingFunc
, from
.readPending
, 0.3, true);
868 AsyncCall::Pointer call
= commCbCall(5,4, "TunnelBlindCopyReadHandler",
869 CommIoCbPtrFun(completion
, this));
870 comm_read(from
.conn
, from
.buf
, bw
, call
);
874 TunnelStateData::copyClientBytes()
876 if (preReadClientData
.length()) {
877 size_t copyBytes
= preReadClientData
.length() > SQUID_TCP_SO_RCVBUF
? SQUID_TCP_SO_RCVBUF
: preReadClientData
.length();
878 memcpy(client
.buf
, preReadClientData
.rawContent(), copyBytes
);
879 preReadClientData
.consume(copyBytes
);
880 client
.bytesIn(copyBytes
);
881 if (keepGoingAfterRead(copyBytes
, Comm::OK
, 0, client
, server
))
882 copy(copyBytes
, client
, server
, TunnelStateData::WriteServerDone
);
884 copyRead(client
, ReadClient
);
888 TunnelStateData::copyServerBytes()
890 if (preReadServerData
.length()) {
891 size_t copyBytes
= preReadServerData
.length() > SQUID_TCP_SO_RCVBUF
? SQUID_TCP_SO_RCVBUF
: preReadServerData
.length();
892 memcpy(server
.buf
, preReadServerData
.rawContent(), copyBytes
);
893 preReadServerData
.consume(copyBytes
);
894 server
.bytesIn(copyBytes
);
895 if (keepGoingAfterRead(copyBytes
, Comm::OK
, 0, server
, client
))
896 copy(copyBytes
, server
, client
, TunnelStateData::WriteClientDone
);
898 copyRead(server
, ReadServer
);
902 * Set the HTTP status for this request and sets the read handlers for client
903 * and server side connections.
906 tunnelStartShoveling(TunnelStateData
*tunnelState
)
908 assert(!tunnelState
->waitingForConnectExchange
);
909 assert(tunnelState
->server
.conn
);
910 AsyncCall::Pointer timeoutCall
= commCbCall(5, 4, "tunnelTimeout",
911 CommTimeoutCbPtrFun(tunnelTimeout
, tunnelState
));
912 commSetConnTimeout(tunnelState
->server
.conn
, Config
.Timeout
.read
, timeoutCall
);
914 *tunnelState
->status_ptr
= Http::scOkay
;
915 if (tunnelState
->logTag_ptr
)
916 tunnelState
->logTag_ptr
->update(LOG_TCP_TUNNEL
);
917 if (cbdataReferenceValid(tunnelState
)) {
919 // Shovel any payload already pushed into reply buffer by the server response
920 if (!tunnelState
->server
.len
)
921 tunnelState
->copyServerBytes();
923 debugs(26, DBG_DATA
, "Tunnel server PUSH Payload: \n" << Raw("", tunnelState
->server
.buf
, tunnelState
->server
.len
) << "\n----------");
924 tunnelState
->copy(tunnelState
->server
.len
, tunnelState
->server
, tunnelState
->client
, TunnelStateData::WriteClientDone
);
927 if (tunnelState
->http
.valid() && tunnelState
->http
->getConn() && !tunnelState
->http
->getConn()->inBuf
.isEmpty()) {
928 SBuf
* const in
= &tunnelState
->http
->getConn()->inBuf
;
929 debugs(26, DBG_DATA
, "Tunnel client PUSH Payload: \n" << *in
<< "\n----------");
930 tunnelState
->preReadClientData
.append(*in
);
931 in
->consume(); // ConnStateData buffer accounting after the shuffle.
933 tunnelState
->copyClientBytes();
938 * All the pieces we need to write to client and/or server connection
940 * Call the tunnelStartShoveling to start the blind pump.
943 tunnelConnectedWriteDone(const Comm::ConnectionPointer
&conn
, char *, size_t len
, Comm::Flag flag
, int, void *data
)
945 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
946 debugs(26, 3, HERE
<< conn
<< ", flag=" << flag
);
947 tunnelState
->client
.writer
= NULL
;
949 if (flag
!= Comm::OK
) {
950 *tunnelState
->status_ptr
= Http::scInternalServerError
;
951 tunnelErrorComplete(conn
->fd
, data
, 0);
955 if (auto http
= tunnelState
->http
.get()) {
956 http
->out
.headers_sz
+= len
;
957 http
->out
.size
+= len
;
960 tunnelStartShoveling(tunnelState
);
964 TunnelStateData::tunnelEstablishmentDone(Http::TunnelerAnswer
&answer
)
969 logTag_ptr
->update(LOG_TCP_TUNNEL
);
971 if (answer
.peerResponseStatus
!= Http::scNone
)
972 *status_ptr
= answer
.peerResponseStatus
;
974 waitingForConnectExchange
= false;
976 auto sawProblem
= false;
978 if (!answer
.positive()) {
980 Must(!Comm::IsConnOpen(answer
.conn
));
981 } else if (!Comm::IsConnOpen(answer
.conn
) || fd_table
[answer
.conn
->fd
].closing()) {
983 closePendingConnection(answer
.conn
, "conn was closed while waiting for tunnelEstablishmentDone");
987 assert(answer
.positive()); // paranoid
988 // copy any post-200 OK bytes to our buffer
989 preReadServerData
= answer
.leftovers
;
990 notePeerReadyToShovel(answer
.conn
);
994 ErrorState
*error
= nullptr;
995 if (answer
.positive()) {
996 error
= new ErrorState(ERR_CANNOT_FORWARD
, Http::scServiceUnavailable
, request
.getRaw(), al
);
998 error
= answer
.squidError
.get();
1000 answer
.squidError
.clear(); // preserve error for errorSendComplete()
1004 retryOrBail("tunneler error");
1008 TunnelStateData::notePeerReadyToShovel(const Comm::ConnectionPointer
&conn
)
1010 assert(!client
.dirty
);
1012 server
.initConnection(conn
, tunnelServerClosed
, "tunnelServerClosed", this);
1014 if (!clientExpectsConnectResponse())
1015 tunnelStartShoveling(this); // ssl-bumped connection, be quiet
1017 *status_ptr
= Http::scOkay
;
1018 AsyncCall::Pointer call
= commCbCall(5,5, "tunnelConnectedWriteDone",
1019 CommIoCbPtrFun(tunnelConnectedWriteDone
, this));
1020 al
->reply
= HttpReply::MakeConnectionEstablished();
1021 const auto mb
= al
->reply
->pack();
1022 client
.write(mb
->content(), mb
->contentSize(), call
, mb
->freeFunc());
1028 tunnelErrorComplete(int fd
/*const Comm::ConnectionPointer &*/, void *data
, size_t)
1030 TunnelStateData
*tunnelState
= (TunnelStateData
*)data
;
1031 debugs(26, 3, HERE
<< "FD " << fd
);
1032 assert(tunnelState
!= NULL
);
1033 /* temporary lock to save our own feets (comm_close -> tunnelClientClosed -> Free) */
1034 CbcPointer
<TunnelStateData
> safetyLock(tunnelState
);
1036 if (Comm::IsConnOpen(tunnelState
->client
.conn
))
1037 tunnelState
->client
.conn
->close();
1039 if (Comm::IsConnOpen(tunnelState
->server
.conn
))
1040 tunnelState
->server
.conn
->close();
1044 TunnelStateData::noteConnection(HappyConnOpener::Answer
&answer
)
1046 calls
.connector
= nullptr;
1049 ErrorState
*error
= nullptr;
1050 if ((error
= answer
.error
.get())) {
1051 Must(!Comm::IsConnOpen(answer
.conn
));
1052 syncHierNote(answer
.conn
, request
->url
.host());
1053 answer
.error
.clear();
1054 } else if (!Comm::IsConnOpen(answer
.conn
) || fd_table
[answer
.conn
->fd
].closing()) {
1055 error
= new ErrorState(ERR_CANNOT_FORWARD
, Http::scServiceUnavailable
, request
.getRaw(), al
);
1056 closePendingConnection(answer
.conn
, "conn was closed while waiting for noteConnection");
1061 retryOrBail("tried all destinations");
1065 connectDone(answer
.conn
, request
->url
.host(), answer
.reused
);
1069 TunnelStateData::connectDone(const Comm::ConnectionPointer
&conn
, const char *origin
, const bool reused
)
1071 Must(Comm::IsConnOpen(conn
));
1074 ResetMarkingsToServer(request
.getRaw(), *conn
);
1075 // else Comm::ConnOpener already applied proper/current markings
1077 syncHierNote(conn
, origin
);
1080 /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
1081 if (conn
->getPeer() && conn
->getPeer()->options
.no_delay
)
1082 server
.setDelayId(DelayId());
1085 netdbPingSite(request
->url
.host());
1087 request
->peer_host
= conn
->getPeer() ? conn
->getPeer()->host
: nullptr;
1089 bool toOrigin
= false; // same semantics as StateFlags::toOrigin
1090 if (const auto * const peer
= conn
->getPeer()) {
1091 request
->prepForPeering(*peer
);
1092 toOrigin
= peer
->options
.originserver
;
1094 request
->prepForDirect();
1099 connectToPeer(conn
);
1101 notePeerReadyToShovel(conn
);
1106 tunnelStart(ClientHttpRequest
* http
)
1108 debugs(26, 3, HERE
);
1109 /* Create state structure. */
1110 TunnelStateData
*tunnelState
= NULL
;
1111 ErrorState
*err
= NULL
;
1112 HttpRequest
*request
= http
->request
;
1113 char *url
= http
->uri
;
1116 * client_addr.isNoAddr() indicates this is an "internal" request
1117 * from peer_digest.c, asn.c, netdb.c, etc and should always
1118 * be allowed. yuck, I know.
1121 if (Config
.accessList
.miss
&& !request
->client_addr
.isNoAddr()) {
1123 * Check if this host is allowed to fetch MISSES from us (miss_access)
1124 * default is to allow.
1126 ACLFilledChecklist
ch(Config
.accessList
.miss
, request
, NULL
);
1128 ch
.src_addr
= request
->client_addr
;
1129 ch
.my_addr
= request
->my_addr
;
1130 ch
.syncAle(request
, http
->log_uri
);
1131 if (ch
.fastCheck().denied()) {
1132 debugs(26, 4, HERE
<< "MISS access forbidden.");
1133 err
= new ErrorState(ERR_FORWARDING_DENIED
, Http::scForbidden
, request
, http
->al
);
1134 http
->al
->http
.code
= Http::scForbidden
;
1135 errorSend(http
->getConn()->clientConnection
, err
);
1140 debugs(26, 3, request
->method
<< ' ' << url
<< ' ' << request
->http_ver
);
1141 ++statCounter
.server
.all
.requests
;
1142 ++statCounter
.server
.other
.requests
;
1144 tunnelState
= new TunnelStateData(http
);
1146 tunnelState
->server
.setDelayId(DelayId::DelayClient(http
));
1148 tunnelState
->startSelectingDestinations(request
, http
->al
, nullptr);
1152 TunnelStateData::connectToPeer(const Comm::ConnectionPointer
&conn
)
1154 if (const auto p
= conn
->getPeer()) {
1155 if (p
->secure
.encryptTransport
)
1156 return advanceDestination("secure connection to peer", conn
, [this,&conn
] {
1157 secureConnectionToPeer(conn
);
1161 connectedToPeer(conn
);
1164 /// encrypts an established TCP connection to peer
1166 TunnelStateData::secureConnectionToPeer(const Comm::ConnectionPointer
&conn
)
1168 AsyncCall::Pointer callback
= asyncCall(5,4, "TunnelStateData::noteSecurityPeerConnectorAnswer",
1169 MyAnswerDialer(&TunnelStateData::noteSecurityPeerConnectorAnswer
, this));
1170 const auto connector
= new Security::BlindPeerConnector(request
, conn
, callback
, al
);
1171 AsyncJob::Start(connector
); // will call our callback
1174 /// starts a preparation step for an established connection; retries on failures
1175 template <typename StepStart
>
1177 TunnelStateData::advanceDestination(const char *stepDescription
, const Comm::ConnectionPointer
&conn
, const StepStart
&startStep
)
1179 // TODO: Extract destination-specific handling from TunnelStateData so that
1180 // all the awkward, limited-scope advanceDestination() calls can be replaced
1181 // with a single simple try/catch,retry block.
1184 // now wait for the step callback
1186 debugs (26, 2, "exception while trying to " << stepDescription
<< ": " << CurrentException
);
1187 closePendingConnection(conn
, "connection preparation exception");
1189 saveError(new ErrorState(ERR_CANNOT_FORWARD
, Http::scInternalServerError
, request
.getRaw(), al
));
1190 retryOrBail(stepDescription
);
1194 /// callback handler for the connection encryptor
1196 TunnelStateData::noteSecurityPeerConnectorAnswer(Security::EncryptorAnswer
&answer
)
1198 ErrorState
*error
= nullptr;
1199 if ((error
= answer
.error
.get())) {
1200 Must(!Comm::IsConnOpen(answer
.conn
));
1201 answer
.error
.clear();
1202 } else if (!Comm::IsConnOpen(answer
.conn
) || fd_table
[answer
.conn
->fd
].closing()) {
1203 error
= new ErrorState(ERR_CANNOT_FORWARD
, Http::scServiceUnavailable
, request
.getRaw(), al
);
1204 closePendingConnection(answer
.conn
, "conn was closed while waiting for noteSecurityPeerConnectorAnswer");
1209 retryOrBail("TLS peer connection error");
1213 connectedToPeer(answer
.conn
);
1217 TunnelStateData::connectedToPeer(const Comm::ConnectionPointer
&conn
)
1219 advanceDestination("establish tunnel through proxy", conn
, [this,&conn
] {
1220 establishTunnelThruProxy(conn
);
1225 TunnelStateData::establishTunnelThruProxy(const Comm::ConnectionPointer
&conn
)
1227 assert(!waitingForConnectExchange
);
1229 AsyncCall::Pointer callback
= asyncCall(5,4,
1230 "TunnelStateData::tunnelEstablishmentDone",
1231 Http::Tunneler::CbDialer
<TunnelStateData
>(&TunnelStateData::tunnelEstablishmentDone
, this));
1232 const auto tunneler
= new Http::Tunneler(conn
, request
, callback
, Config
.Timeout
.lifetime
, al
);
1234 tunneler
->setDelayId(server
.delayId
);
1236 AsyncJob::Start(tunneler
);
1237 waitingForConnectExchange
= true;
1238 // and wait for the tunnelEstablishmentDone() call
1242 TunnelStateData::noteDestination(Comm::ConnectionPointer path
)
1244 destinationsFound
= true;
1246 if (!path
) { // decided to use a pinned connection
1247 // We can call usePinned() without fear of clashing with an earlier
1248 // forwarding attempt because PINNED must be the first destination.
1249 assert(destinations
->empty());
1254 destinations
->addPath(path
);
1256 if (Comm::IsConnOpen(server
.conn
)) {
1257 // We are already using a previously opened connection but also
1258 // receiving destinations in case we need to re-forward.
1265 return; // and continue to wait for tunnelConnectDone() callback
1272 TunnelStateData::noteDestinationsEnd(ErrorState
*selectionError
)
1274 PeerSelectionInitiator::subscribed
= false;
1275 destinations
->destinationsFinalized
= true;
1276 if (!destinationsFound
) {
1278 // XXX: Honor clientExpectsConnectResponse() before replying.
1281 return sendError(selectionError
, "path selection has failed");
1284 return sendError(savedError
, "all found paths have failed");
1286 return sendError(new ErrorState(ERR_CANNOT_FORWARD
, Http::scInternalServerError
, request
.getRaw(), al
),
1287 "path selection found no paths");
1289 // else continue to use one of the previously noted destinations;
1290 // if all of them fail, tunneling as whole will fail
1291 Must(!selectionError
); // finding at least one path means selection succeeded
1293 if (Comm::IsConnOpen(server
.conn
)) {
1294 // We are already using a previously opened connection but also
1295 // receiving destinations in case we need to re-forward.
1300 Must(opening()); // or we would be stuck with nothing to do or wait for
1304 /// remembers an error to be used if there will be no more connection attempts
1306 TunnelStateData::saveError(ErrorState
*error
)
1308 debugs(26, 4, savedError
<< " ? " << error
);
1310 delete savedError
; // may be nil
1314 /// Starts sending the given error message to the client, leading to the
1315 /// eventual transaction termination. Call with savedError to send savedError.
1317 TunnelStateData::sendError(ErrorState
*finalError
, const char *reason
)
1319 debugs(26, 3, "aborting transaction for " << reason
);
1322 request
->hier
.stopPeerClock(false);
1325 cancelOpening(reason
);
1329 // get rid of any cached error unless that is what the caller is sending
1330 if (savedError
!= finalError
)
1331 delete savedError
; // may be nil
1332 savedError
= nullptr;
1334 // we cannot try other destinations after responding with an error
1335 PeerSelectionInitiator::subscribed
= false; // may already be false
1337 *status_ptr
= finalError
->httpStatus
;
1338 finalError
->callback
= tunnelErrorComplete
;
1339 finalError
->callback_data
= this;
1340 errorSend(client
.conn
, finalError
);
1343 /// Notify connOpener that we no longer need connections. We do not have to do
1344 /// this -- connOpener would eventually notice on its own, but notifying reduces
1345 /// waste and speeds up spare connection opening for other transactions (that
1346 /// could otherwise wait for this transaction to use its spare allowance).
1348 TunnelStateData::cancelOpening(const char *reason
)
1350 assert(calls
.connector
);
1351 calls
.connector
->cancel(reason
);
1352 calls
.connector
= nullptr;
1358 TunnelStateData::startConnecting()
1361 request
->hier
.startPeerClock();
1363 assert(!destinations
->empty());
1365 calls
.connector
= asyncCall(17, 5, "TunnelStateData::noteConnection", HappyConnOpener::CbDialer
<TunnelStateData
>(&TunnelStateData::noteConnection
, this));
1366 const auto cs
= new HappyConnOpener(destinations
, calls
.connector
, request
, startTime
, 0, al
);
1367 cs
->setHost(request
->url
.host());
1368 cs
->setRetriable(false);
1369 cs
->allowPersistent(false);
1370 destinations
->notificationPending
= true; // start() is async
1372 AsyncJob::Start(cs
);
1375 /// send request on an existing connection dedicated to the requesting client
1377 TunnelStateData::usePinned()
1380 const auto connManager
= request
->pinnedConnection();
1382 const auto serverConn
= ConnStateData::BorrowPinnedConnection(request
.getRaw(), al
);
1383 debugs(26, 7, "pinned peer connection: " << serverConn
);
1385 // Set HttpRequest pinned related flags for consistency even if
1386 // they are not really used by tunnel.cc code.
1387 request
->flags
.pinned
= true;
1388 if (connManager
->pinnedAuth())
1389 request
->flags
.auth
= true;
1391 // the server may close the pinned connection before this request
1392 const auto reused
= true;
1393 connectDone(serverConn
, connManager
->pinning
.host
, reused
);
1394 } catch (ErrorState
* const error
) {
1395 syncHierNote(nullptr, connManager
? connManager
->pinning
.host
: request
->url
.host());
1396 // XXX: Honor clientExpectsConnectResponse() before replying.
1397 // a PINNED path failure is fatal; do not wait for more paths
1398 sendError(error
, "pinned path failure");
1404 CBDATA_CLASS_INIT(TunnelStateData
);
1407 TunnelStateData::noConnections() const
1409 return !Comm::IsConnOpen(server
.conn
) && !Comm::IsConnOpen(client
.conn
);
1414 TunnelStateData::Connection::setDelayId(DelayId
const &newDelay
)
1421 /// makes sure connOpener knows that destinations have changed
1423 TunnelStateData::notifyConnOpener()
1425 if (destinations
->notificationPending
) {
1426 debugs(17, 7, "reusing pending notification");
1428 destinations
->notificationPending
= true;
1429 CallJobHere(17, 5, connOpener
, HappyConnOpener
, noteCandidatesChange
);
1434 * Sets up a TCP tunnel through Squid and starts shoveling traffic.
1435 * \param request the request that initiated/caused this tunnel
1436 * \param clientConn the already accepted client-to-Squid TCP connection
1437 * \param srvConn the already established Squid-to-server TCP connection
1438 * \param preReadServerData server-sent bytes to be forwarded to the client
1441 switchToTunnel(HttpRequest
*request
, const Comm::ConnectionPointer
&clientConn
, const Comm::ConnectionPointer
&srvConn
, const SBuf
&preReadServerData
)
1443 Must(Comm::IsConnOpen(clientConn
));
1444 Must(Comm::IsConnOpen(srvConn
));
1446 debugs(26,5, "Revert to tunnel FD " << clientConn
->fd
<< " with FD " << srvConn
->fd
);
1448 /* Create state structure. */
1449 ++statCounter
.server
.all
.requests
;
1450 ++statCounter
.server
.other
.requests
;
1452 auto conn
= request
->clientConnectionManager
.get();
1454 Http::StreamPointer context
= conn
->pipeline
.front();
1455 Must(context
&& context
->http
);
1457 debugs(26, 3, request
->method
<< " " << context
->http
->uri
<< " " << request
->http_ver
);
1459 TunnelStateData
*tunnelState
= new TunnelStateData(context
->http
);
1460 tunnelState
->retriable
= false;
1462 request
->hier
.resetPeerNotes(srvConn
, tunnelState
->getHost());
1464 tunnelState
->server
.initConnection(srvConn
, tunnelServerClosed
, "tunnelServerClosed", tunnelState
);
1467 /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
1468 if (!srvConn
->getPeer() || !srvConn
->getPeer()->options
.no_delay
)
1469 tunnelState
->server
.setDelayId(DelayId::DelayClient(context
->http
));
1472 request
->peer_host
= srvConn
->getPeer() ? srvConn
->getPeer()->host
: nullptr;
1474 debugs(26, 4, "determine post-connect handling pathway.");
1475 if (const auto peer
= srvConn
->getPeer())
1476 request
->prepForPeering(*peer
);
1478 request
->prepForDirect();
1480 tunnelState
->preReadServerData
= preReadServerData
;
1482 tunnelStartShoveling(tunnelState
);