2 * Copyright (C) 1996-2022 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
;
109 HappyConnOpener(const ResolvedPeersPointer
&, const AsyncCallback
<Answer
> &, const HttpRequestPointer
&, time_t aFwdStart
, int tries
, const AccessLogEntryPointer
&);
110 ~HappyConnOpener() override
;
112 /// configures reuse of old connections
113 void allowPersistent(bool permitted
) { allowPconn_
= permitted
; }
115 /// configures whether the request may be retried later if things go wrong
116 void setRetriable(bool retriable
) { retriable_
= retriable
; }
118 /// configures the origin server domain name
119 void setHost(const char *);
121 /// reacts to changes in the destinations list
122 void noteCandidatesChange();
124 /// reacts to expired happy_eyeballs_connect_timeout
125 void noteGavePrimeItsChance();
127 /// reacts to satisfying happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
128 void noteSpareAllowance();
130 /// the start of the first connection attempt for the currentPeer
131 HappyAbsoluteTime primeStart
= 0;
134 /// a connection opening attempt in progress (or falsy)
137 /// HappyConnOpener method implementing a ConnOpener callback
138 using CallbackMethod
= void (HappyConnOpener::*)(const CommConnectCbParams
&);
140 Attempt(const CallbackMethod method
, const char *methodName
);
142 explicit operator bool() const { return static_cast<bool>(path
); }
144 /// reacts to a natural attempt completion (successful or otherwise)
147 /// aborts an in-progress attempt
148 void cancel(const char *reason
);
150 PeerConnectionPointer path
; ///< the destination we are connecting to
152 /// waits for a connection to the peer to be established/opened
153 JobWait
<Comm::ConnOpener
> connWait
;
155 const CallbackMethod callbackMethod
; ///< ConnOpener calls this method
156 const char * const callbackMethodName
; ///< for callbackMethod debugging
158 friend std::ostream
&operator <<(std::ostream
&, const Attempt
&);
161 void start() override
;
162 bool doneAll() const override
;
163 void swanSong() override
;
164 const char *status() const override
;
166 void maybeOpenPrimeConnection();
167 void maybeOpenSpareConnection();
169 void maybeGivePrimeItsChance();
170 void stopGivingPrimeItsChance();
171 void stopWaitingForSpareAllowance();
173 void startConnecting(Attempt
&, PeerConnectionPointer
&);
174 void openFreshConnection(Attempt
&, PeerConnectionPointer
&);
175 bool reuseOldConnection(PeerConnectionPointer
&);
177 void notePrimeConnectDone(const CommConnectCbParams
&);
178 void noteSpareConnectDone(const CommConnectCbParams
&);
179 void handleConnOpenerAnswer(Attempt
&, const CommConnectCbParams
&, const char *connDescription
);
181 void checkForNewConnection();
183 void updateSpareWaitAfterPrimeFailure();
185 void cancelSpareWait(const char *reason
);
187 bool ranOutOfTimeOrAttempts() const;
189 ErrorState
*makeError(const err_type type
) const;
190 Answer
*futureAnswer(const PeerConnectionPointer
&);
191 void sendSuccess(const PeerConnectionPointer
&conn
, bool reused
, const char *connKind
);
193 void cancelAttempt(Attempt
&, const char *reason
);
195 const time_t fwdStart
; ///< requestor start time
197 /// answer destination
198 AsyncCallback
<Answer
> callback_
;
200 /// Candidate paths. Shared with the initiator. May not be finalized yet.
201 ResolvedPeersPointer destinations
;
203 /// current connection opening attempt on the prime track (if any)
206 /// current connection opening attempt on the spare track (if any)
209 /// CachePeer and IP address family of the peer we are trying to connect
210 /// to now (or, if we are just waiting for paths to a new peer, nil)
211 Comm::ConnectionPointer currentPeer
;
213 /// preconditions for an attempt to open a spare connection
214 HappySpareWait spareWaiting
;
215 friend class HappyOrderEnforcer
;
217 AccessLogEntryPointer ale
; ///< transaction details
219 ErrorState
*lastError
= nullptr; ///< last problem details (or nil)
220 PeerConnectionPointer lastFailedConnection
; ///< nil if none has failed
222 /// whether spare connection attempts disregard happy_eyeballs_* settings
223 bool ignoreSpareRestrictions
= false;
225 /// whether we have received a permission to open a spare while spares are limited
226 bool gotSpareAllowance
= false;
228 /// whether persistent connections are allowed
229 bool allowPconn_
= true;
231 /// whether we are opening connections for a request that may be resent
232 bool retriable_
= true;
234 /// origin server domain name (or equivalent)
235 const char *host_
= nullptr;
237 /// the request that needs a to-server connection
238 HttpRequestPointer cause
;
240 /// number of our finished connection opening attempts (including pconn
241 /// reuses) plus previously finished attempts supplied by the requestor
244 /// Reason to ran out of time or attempts
245 mutable const char *ranOutOfTimeOrAttemptsEarlier_
= nullptr;