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 #ifndef SQUID_HAPPYCONNOPENER_H
10 #define SQUID_HAPPYCONNOPENER_H
11 #include "base/RefCount.h"
13 #include "comm/Connection.h"
14 #include "comm/ConnOpener.h"
15 #include "http/forward.h"
16 #include "log/forward.h"
17 #include "ResolvedPeers.h"
21 class HappyConnOpener
;
22 class HappyOrderEnforcer
;
24 typedef RefCount
<ResolvedPeers
> ResolvedPeersPointer
;
26 /// A FIFO queue of HappyConnOpener jobs waiting to open a spare connection.
27 typedef std::list
< CbcPointer
<HappyConnOpener
> > HappySpareWaitList
;
29 /// absolute time in fractional seconds; compatible with current_timed
30 typedef double HappyAbsoluteTime
;
32 /// keeps track of HappyConnOpener spare track waiting state
33 class HappySpareWait
{
35 explicit operator bool() const { return toGivePrimeItsChance
|| forSpareAllowance
|| forPrimesToFail
|| forNewPeer
; }
37 /// restores default-constructed state
38 /// nullifies but does not cancel the callback
39 void clear() { *this = HappySpareWait(); }
41 CodeContext::Pointer codeContext
; ///< requestor's context
43 /// a pending noteGavePrimeItsChance() or noteSpareAllowance() call
44 AsyncCall::Pointer callback
;
46 /// location on the toGivePrimeItsChance or forSpareAllowance wait list
47 /// invalidated when the callback is set
48 HappySpareWaitList::iterator position
;
50 /* The following four fields represent mutually exclusive wait reasons. */
52 /// Honoring happy_eyeballs_connect_timeout (once per currentPeer).
53 /// A prime connection failure ends this wait.
54 bool toGivePrimeItsChance
= false;
56 /// Honors happy_eyeballs_connect_gap and positive happy_eyeballs_connect_limit
57 /// (one allowance per spare path).
58 /// Does not start until there is a new spare path to try.
59 /// Prime exhaustion ends this wait (see ignoreSpareRestrictions).
60 bool forSpareAllowance
= false;
62 /// Honors zero happy_eyeballs_connect_limit.
63 /// Prime exhaustion ends this wait (see ignoreSpareRestrictions).
64 bool forPrimesToFail
= false;
66 /// The current peer has no spares left to try.
67 /// Prime exhaustion ends this wait (by changing currentPeer).
68 bool forNewPeer
= false;
71 /// Final result (an open connection or an error) sent to the job initiator.
72 class HappyConnOpenerAnswer
75 ~HappyConnOpenerAnswer();
77 /// whether HappyConnOpener succeeded, returning a usable connection
78 bool success() const { return !error
; }
80 /// on success: an open, ready-to-use Squid-to-peer connection
81 /// on failure: either a closed failed Squid-to-peer connection or nil
82 PeerConnectionPointer conn
;
84 // answer recipients must clear the error member in order to keep its info
85 // XXX: We should refcount ErrorState instead of cbdata-protecting it.
86 CbcPointer
<ErrorState
> error
; ///< problem details (nil on success)
88 /// The total number of attempts to establish a connection. Includes any
89 /// failed attempts and [always successful] persistent connection reuse.
92 /// whether conn was open earlier, by/for somebody else
96 /// reports Answer details (for AsyncCall parameter debugging)
97 std::ostream
&operator <<(std::ostream
&, const HappyConnOpenerAnswer
&);
99 /// A TCP connection opening algorithm based on Happy Eyeballs (RFC 8305).
100 /// Maintains two concurrent connection opening tracks: prime and spare.
101 /// Shares ResolvedPeers list with the job initiator.
102 class HappyConnOpener
: public AsyncJob
104 CBDATA_CHILD(HappyConnOpener
);
106 typedef HappyConnOpenerAnswer Answer
;
108 /// AsyncCall dialer for our callback. Gives us access to callback Answer.
109 template <class Initiator
>
110 class CbDialer
: public CallDialer
, public Answer
{
112 // initiator method to receive our answer
113 typedef void (Initiator::*Method
)(Answer
&);
115 CbDialer(Method method
, Initiator
*initiator
): initiator_(initiator
), method_(method
) {}
116 virtual ~CbDialer() = default;
119 bool canDial(AsyncCall
&) { return initiator_
.valid(); }
120 void dial(AsyncCall
&) {((*initiator_
).*method_
)(*this); }
121 virtual void print(std::ostream
&os
) const override
{
122 os
<< '(' << static_cast<const Answer
&>(*this) << ')';
126 CbcPointer
<Initiator
> initiator_
; ///< object to deliver the answer to
127 Method method_
; ///< initiator_ method to call with the answer
131 HappyConnOpener(const ResolvedPeersPointer
&, const AsyncCall::Pointer
&, HttpRequestPointer
&, const time_t aFwdStart
, int tries
, const AccessLogEntryPointer
&al
);
132 virtual ~HappyConnOpener() override
;
134 /// configures reuse of old connections
135 void allowPersistent(bool permitted
) { allowPconn_
= permitted
; }
137 /// configures whether the request may be retried later if things go wrong
138 void setRetriable(bool retriable
) { retriable_
= retriable
; }
140 /// configures the origin server domain name
141 void setHost(const char *);
143 /// reacts to changes in the destinations list
144 void noteCandidatesChange();
146 /// reacts to expired happy_eyeballs_connect_timeout
147 void noteGavePrimeItsChance();
149 /// reacts to satisfying happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
150 void noteSpareAllowance();
152 /// the start of the first connection attempt for the currentPeer
153 HappyAbsoluteTime primeStart
= 0;
156 /// a connection opening attempt in progress (or falsy)
159 explicit operator bool() const { return static_cast<bool>(path
); }
161 /// reacts to a natural attempt completion (successful or otherwise)
162 void finish() { clear(); }
164 /// aborts an in-progress attempt
165 void cancel(const char *reason
);
167 PeerConnectionPointer path
; ///< the destination we are connecting to
168 AsyncCall::Pointer connector
; ///< our opener callback
169 Comm::ConnOpener::Pointer opener
; ///< connects to path and calls us
172 /// cleans up after the attempt ends (successfully or otherwise)
173 void clear() { path
= nullptr; connector
= nullptr; opener
= nullptr; }
177 virtual void start() override
;
178 virtual bool doneAll() const override
;
179 virtual void swanSong() override
;
180 virtual const char *status() const override
;
182 void maybeOpenAnotherPrimeConnection();
184 void maybeGivePrimeItsChance();
185 void stopGivingPrimeItsChance();
186 void stopWaitingForSpareAllowance();
187 void maybeOpenSpareConnection();
189 void startConnecting(Attempt
&, PeerConnectionPointer
&);
190 void openFreshConnection(Attempt
&, PeerConnectionPointer
&);
191 bool reuseOldConnection(PeerConnectionPointer
&);
193 void connectDone(const CommConnectCbParams
&);
195 void checkForNewConnection();
197 void updateSpareWaitAfterPrimeFailure();
199 void cancelSpareWait(const char *reason
);
201 bool ranOutOfTimeOrAttempts() const;
203 ErrorState
*makeError(const err_type type
) const;
204 Answer
*futureAnswer(const PeerConnectionPointer
&);
205 void sendSuccess(const PeerConnectionPointer
&conn
, bool reused
, const char *connKind
);
207 void cancelAttempt(Attempt
&, const char *reason
);
209 const time_t fwdStart
; ///< requestor start time
211 AsyncCall::Pointer callback_
; ///< handler to be called on connection completion.
213 /// Candidate paths. Shared with the initiator. May not be finalized yet.
214 ResolvedPeersPointer destinations
;
216 /// current connection opening attempt on the prime track (if any)
219 /// current connection opening attempt on the spare track (if any)
222 /// CachePeer and IP address family of the peer we are trying to connect
223 /// to now (or, if we are just waiting for paths to a new peer, nil)
224 Comm::ConnectionPointer currentPeer
;
226 /// preconditions for an attempt to open a spare connection
227 HappySpareWait spareWaiting
;
228 friend class HappyOrderEnforcer
;
230 AccessLogEntryPointer ale
; ///< transaction details
232 ErrorState
*lastError
= nullptr; ///< last problem details (or nil)
233 PeerConnectionPointer lastFailedConnection
; ///< nil if none has failed
235 /// whether spare connection attempts disregard happy_eyeballs_* settings
236 bool ignoreSpareRestrictions
= false;
238 /// whether we have received a permission to open a spare while spares are limited
239 bool gotSpareAllowance
= false;
241 /// whether persistent connections are allowed
242 bool allowPconn_
= true;
244 /// whether we are opening connections for a request that may be resent
245 bool retriable_
= true;
247 /// origin server domain name (or equivalent)
248 const char *host_
= nullptr;
250 /// the request that needs a to-server connection
251 HttpRequestPointer cause
;
253 /// number of connection opening attempts, including those in the requestor
256 /// Reason to ran out of time or attempts
257 mutable const char *ranOutOfTimeOrAttemptsEarlier_
= nullptr;