]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HappyConnOpener.h
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / HappyConnOpener.h
1 /*
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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"
17 #include "ResolvedPeers.h"
18
19 #include <iosfwd>
20
21 class HappyConnOpener;
22 class HappyOrderEnforcer;
23 class JobGapEnforcer;
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
41 CodeContext::Pointer codeContext; ///< requestor's context
42
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
82 PeerConnectionPointer conn;
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:
159 explicit operator bool() const { return static_cast<bool>(path); }
160
161 /// reacts to a natural attempt completion (successful or otherwise)
162 void finish() { clear(); }
163
164 /// aborts an in-progress attempt
165 void cancel(const char *reason);
166
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
170
171 private:
172 /// cleans up after the attempt ends (successfully or otherwise)
173 void clear() { path = nullptr; connector = nullptr; opener = nullptr; }
174 };
175
176 /* AsyncJob API */
177 virtual void start() override;
178 virtual bool doneAll() const override;
179 virtual void swanSong() override;
180 virtual const char *status() const override;
181
182 void maybeOpenAnotherPrimeConnection();
183
184 void maybeGivePrimeItsChance();
185 void stopGivingPrimeItsChance();
186 void stopWaitingForSpareAllowance();
187 void maybeOpenSpareConnection();
188
189 void startConnecting(Attempt &, PeerConnectionPointer &);
190 void openFreshConnection(Attempt &, PeerConnectionPointer &);
191 bool reuseOldConnection(PeerConnectionPointer &);
192
193 void connectDone(const CommConnectCbParams &);
194
195 void checkForNewConnection();
196
197 void updateSpareWaitAfterPrimeFailure();
198
199 void cancelSpareWait(const char *reason);
200
201 bool ranOutOfTimeOrAttempts() const;
202
203 ErrorState *makeError(const err_type type) const;
204 Answer *futureAnswer(const PeerConnectionPointer &);
205 void sendSuccess(const PeerConnectionPointer &conn, bool reused, const char *connKind);
206 void sendFailure();
207 void cancelAttempt(Attempt &, const char *reason);
208
209 const time_t fwdStart; ///< requestor start time
210
211 AsyncCall::Pointer callback_; ///< handler to be called on connection completion.
212
213 /// Candidate paths. Shared with the initiator. May not be finalized yet.
214 ResolvedPeersPointer destinations;
215
216 /// current connection opening attempt on the prime track (if any)
217 Attempt prime;
218
219 /// current connection opening attempt on the spare track (if any)
220 Attempt spare;
221
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;
225
226 /// preconditions for an attempt to open a spare connection
227 HappySpareWait spareWaiting;
228 friend class HappyOrderEnforcer;
229
230 AccessLogEntryPointer ale; ///< transaction details
231
232 ErrorState *lastError = nullptr; ///< last problem details (or nil)
233 PeerConnectionPointer lastFailedConnection; ///< nil if none has failed
234
235 /// whether spare connection attempts disregard happy_eyeballs_* settings
236 bool ignoreSpareRestrictions = false;
237
238 /// whether we have received a permission to open a spare while spares are limited
239 bool gotSpareAllowance = false;
240
241 /// whether persistent connections are allowed
242 bool allowPconn_ = true;
243
244 /// whether we are opening connections for a request that may be resent
245 bool retriable_ = true;
246
247 /// origin server domain name (or equivalent)
248 const char *host_ = nullptr;
249
250 /// the request that needs a to-server connection
251 HttpRequestPointer cause;
252
253 /// number of connection opening attempts, including those in the requestor
254 int n_tries;
255
256 /// Reason to ran out of time or attempts
257 mutable const char *ranOutOfTimeOrAttemptsEarlier_ = nullptr;
258 };
259
260 #endif
261