]>
Commit | Line | Data |
---|---|---|
55622953 | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
55622953 CT |
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 | #ifndef SQUID_HAPPYCONNOPENER_H | |
10 | #define SQUID_HAPPYCONNOPENER_H | |
11 | #include "base/RefCount.h" | |
12 | #include "comm.h" | |
13 | #include "comm/Connection.h" | |
14 | #include "comm/ConnOpener.h" | |
15 | #include "http/forward.h" | |
16 | #include "log/forward.h" | |
9b7992d9 | 17 | #include "ResolvedPeers.h" |
55622953 CT |
18 | |
19 | #include <iosfwd> | |
20 | ||
21 | class HappyConnOpener; | |
22 | class HappyOrderEnforcer; | |
23 | class JobGapEnforcer; | |
55622953 CT |
24 | typedef RefCount<ResolvedPeers> ResolvedPeersPointer; |
25 | ||
26 | /// A FIFO queue of HappyConnOpener jobs waiting to open a spare connection. | |
27 | typedef std::list< CbcPointer<HappyConnOpener> > HappySpareWaitList; | |
28 | ||
29 | /// absolute time in fractional seconds; compatible with current_timed | |
30 | typedef double HappyAbsoluteTime; | |
31 | ||
32 | /// keeps track of HappyConnOpener spare track waiting state | |
33 | class HappySpareWait { | |
34 | public: | |
35 | explicit operator bool() const { return toGivePrimeItsChance || forSpareAllowance || forPrimesToFail || forNewPeer; } | |
36 | ||
37 | /// restores default-constructed state | |
38 | /// nullifies but does not cancel the callback | |
39 | void clear() { *this = HappySpareWait(); } | |
40 | ||
99ecc6a4 EB |
41 | CodeContext::Pointer codeContext; ///< requestor's context |
42 | ||
55622953 CT |
43 | /// a pending noteGavePrimeItsChance() or noteSpareAllowance() call |
44 | AsyncCall::Pointer callback; | |
45 | ||
46 | /// location on the toGivePrimeItsChance or forSpareAllowance wait list | |
47 | /// invalidated when the callback is set | |
48 | HappySpareWaitList::iterator position; | |
49 | ||
50 | /* The following four fields represent mutually exclusive wait reasons. */ | |
51 | ||
52 | /// Honoring happy_eyeballs_connect_timeout (once per currentPeer). | |
53 | /// A prime connection failure ends this wait. | |
54 | bool toGivePrimeItsChance = false; | |
55 | ||
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; | |
61 | ||
62 | /// Honors zero happy_eyeballs_connect_limit. | |
63 | /// Prime exhaustion ends this wait (see ignoreSpareRestrictions). | |
64 | bool forPrimesToFail = false; | |
65 | ||
66 | /// The current peer has no spares left to try. | |
67 | /// Prime exhaustion ends this wait (by changing currentPeer). | |
68 | bool forNewPeer = false; | |
69 | }; | |
70 | ||
71 | /// Final result (an open connection or an error) sent to the job initiator. | |
72 | class HappyConnOpenerAnswer | |
73 | { | |
74 | public: | |
75 | ~HappyConnOpenerAnswer(); | |
76 | ||
77 | /// whether HappyConnOpener succeeded, returning a usable connection | |
78 | bool success() const { return !error; } | |
79 | ||
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 | |
9b7992d9 | 82 | PeerConnectionPointer conn; |
55622953 CT |
83 | |
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) | |
87 | ||
88 | /// The total number of attempts to establish a connection. Includes any | |
89 | /// failed attempts and [always successful] persistent connection reuse. | |
90 | int n_tries = 0; | |
91 | ||
92 | /// whether conn was open earlier, by/for somebody else | |
93 | bool reused = false; | |
94 | }; | |
95 | ||
96 | /// reports Answer details (for AsyncCall parameter debugging) | |
97 | std::ostream &operator <<(std::ostream &, const HappyConnOpenerAnswer &); | |
98 | ||
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 | |
103 | { | |
104 | CBDATA_CHILD(HappyConnOpener); | |
105 | public: | |
106 | typedef HappyConnOpenerAnswer Answer; | |
107 | ||
108 | /// AsyncCall dialer for our callback. Gives us access to callback Answer. | |
109 | template <class Initiator> | |
110 | class CbDialer: public CallDialer, public Answer { | |
111 | public: | |
112 | // initiator method to receive our answer | |
113 | typedef void (Initiator::*Method)(Answer &); | |
114 | ||
115 | CbDialer(Method method, Initiator *initiator): initiator_(initiator), method_(method) {} | |
116 | virtual ~CbDialer() = default; | |
117 | ||
118 | /* CallDialer API */ | |
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) << ')'; | |
123 | } | |
124 | ||
125 | private: | |
126 | CbcPointer<Initiator> initiator_; ///< object to deliver the answer to | |
127 | Method method_; ///< initiator_ method to call with the answer | |
128 | }; | |
129 | ||
130 | public: | |
131 | HappyConnOpener(const ResolvedPeersPointer &, const AsyncCall::Pointer &, HttpRequestPointer &, const time_t aFwdStart, int tries, const AccessLogEntryPointer &al); | |
132 | virtual ~HappyConnOpener() override; | |
133 | ||
134 | /// configures reuse of old connections | |
135 | void allowPersistent(bool permitted) { allowPconn_ = permitted; } | |
136 | ||
137 | /// configures whether the request may be retried later if things go wrong | |
138 | void setRetriable(bool retriable) { retriable_ = retriable; } | |
139 | ||
140 | /// configures the origin server domain name | |
141 | void setHost(const char *); | |
142 | ||
143 | /// reacts to changes in the destinations list | |
144 | void noteCandidatesChange(); | |
145 | ||
146 | /// reacts to expired happy_eyeballs_connect_timeout | |
147 | void noteGavePrimeItsChance(); | |
148 | ||
149 | /// reacts to satisfying happy_eyeballs_connect_gap and happy_eyeballs_connect_limit | |
150 | void noteSpareAllowance(); | |
151 | ||
152 | /// the start of the first connection attempt for the currentPeer | |
153 | HappyAbsoluteTime primeStart = 0; | |
154 | ||
155 | private: | |
156 | /// a connection opening attempt in progress (or falsy) | |
157 | class Attempt { | |
158 | public: | |
2b6b1bcb AR |
159 | /// HappyConnOpener method implementing a ConnOpener callback |
160 | using CallbackMethod = void (HappyConnOpener::*)(const CommConnectCbParams &); | |
161 | ||
162 | Attempt(const CallbackMethod method, const char *methodName); | |
163 | ||
55622953 | 164 | explicit operator bool() const { return static_cast<bool>(path); } |
a4d576de EB |
165 | |
166 | /// reacts to a natural attempt completion (successful or otherwise) | |
2b6b1bcb | 167 | void finish(); |
a4d576de EB |
168 | |
169 | /// aborts an in-progress attempt | |
170 | void cancel(const char *reason); | |
55622953 | 171 | |
9b7992d9 | 172 | PeerConnectionPointer path; ///< the destination we are connecting to |
a4d576de | 173 | |
2b6b1bcb AR |
174 | /// waits for a connection to the peer to be established/opened |
175 | JobWait<Comm::ConnOpener> connWait; | |
176 | ||
177 | const CallbackMethod callbackMethod; ///< ConnOpener calls this method | |
178 | const char * const callbackMethodName; ///< for callbackMethod debugging | |
55622953 | 179 | }; |
2b6b1bcb | 180 | friend std::ostream &operator <<(std::ostream &, const Attempt &); |
55622953 CT |
181 | |
182 | /* AsyncJob API */ | |
183 | virtual void start() override; | |
184 | virtual bool doneAll() const override; | |
185 | virtual void swanSong() override; | |
186 | virtual const char *status() const override; | |
187 | ||
188 | void maybeOpenAnotherPrimeConnection(); | |
189 | ||
190 | void maybeGivePrimeItsChance(); | |
191 | void stopGivingPrimeItsChance(); | |
192 | void stopWaitingForSpareAllowance(); | |
193 | void maybeOpenSpareConnection(); | |
194 | ||
9b7992d9 EB |
195 | void startConnecting(Attempt &, PeerConnectionPointer &); |
196 | void openFreshConnection(Attempt &, PeerConnectionPointer &); | |
197 | bool reuseOldConnection(PeerConnectionPointer &); | |
55622953 | 198 | |
2b6b1bcb AR |
199 | void notePrimeConnectDone(const CommConnectCbParams &); |
200 | void noteSpareConnectDone(const CommConnectCbParams &); | |
201 | void handleConnOpenerAnswer(Attempt &, const CommConnectCbParams &, const char *connDescription); | |
55622953 CT |
202 | |
203 | void checkForNewConnection(); | |
204 | ||
205 | void updateSpareWaitAfterPrimeFailure(); | |
206 | ||
207 | void cancelSpareWait(const char *reason); | |
208 | ||
209 | bool ranOutOfTimeOrAttempts() const; | |
210 | ||
211 | ErrorState *makeError(const err_type type) const; | |
9b7992d9 EB |
212 | Answer *futureAnswer(const PeerConnectionPointer &); |
213 | void sendSuccess(const PeerConnectionPointer &conn, bool reused, const char *connKind); | |
55622953 | 214 | void sendFailure(); |
a4d576de | 215 | void cancelAttempt(Attempt &, const char *reason); |
55622953 CT |
216 | |
217 | const time_t fwdStart; ///< requestor start time | |
218 | ||
219 | AsyncCall::Pointer callback_; ///< handler to be called on connection completion. | |
220 | ||
221 | /// Candidate paths. Shared with the initiator. May not be finalized yet. | |
222 | ResolvedPeersPointer destinations; | |
223 | ||
224 | /// current connection opening attempt on the prime track (if any) | |
225 | Attempt prime; | |
226 | ||
227 | /// current connection opening attempt on the spare track (if any) | |
228 | Attempt spare; | |
229 | ||
230 | /// CachePeer and IP address family of the peer we are trying to connect | |
231 | /// to now (or, if we are just waiting for paths to a new peer, nil) | |
232 | Comm::ConnectionPointer currentPeer; | |
233 | ||
234 | /// preconditions for an attempt to open a spare connection | |
235 | HappySpareWait spareWaiting; | |
236 | friend class HappyOrderEnforcer; | |
237 | ||
238 | AccessLogEntryPointer ale; ///< transaction details | |
239 | ||
240 | ErrorState *lastError = nullptr; ///< last problem details (or nil) | |
9b7992d9 | 241 | PeerConnectionPointer lastFailedConnection; ///< nil if none has failed |
55622953 CT |
242 | |
243 | /// whether spare connection attempts disregard happy_eyeballs_* settings | |
244 | bool ignoreSpareRestrictions = false; | |
245 | ||
246 | /// whether we have received a permission to open a spare while spares are limited | |
247 | bool gotSpareAllowance = false; | |
248 | ||
249 | /// whether persistent connections are allowed | |
250 | bool allowPconn_ = true; | |
251 | ||
252 | /// whether we are opening connections for a request that may be resent | |
253 | bool retriable_ = true; | |
254 | ||
255 | /// origin server domain name (or equivalent) | |
256 | const char *host_ = nullptr; | |
257 | ||
258 | /// the request that needs a to-server connection | |
259 | HttpRequestPointer cause; | |
260 | ||
261 | /// number of connection opening attempts, including those in the requestor | |
262 | int n_tries; | |
263 | ||
264 | /// Reason to ran out of time or attempts | |
265 | mutable const char *ranOutOfTimeOrAttemptsEarlier_ = nullptr; | |
266 | }; | |
267 | ||
268 | #endif | |
269 |