]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HappyConnOpener.h
Bug 5055: FATAL FwdState::noteDestinationsEnd exception: opening (#877)
[thirdparty/squid.git] / src / HappyConnOpener.h
CommitLineData
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
21class HappyConnOpener;
22class HappyOrderEnforcer;
23class JobGapEnforcer;
55622953
CT
24typedef RefCount<ResolvedPeers> ResolvedPeersPointer;
25
26/// A FIFO queue of HappyConnOpener jobs waiting to open a spare connection.
27typedef std::list< CbcPointer<HappyConnOpener> > HappySpareWaitList;
28
29/// absolute time in fractional seconds; compatible with current_timed
30typedef double HappyAbsoluteTime;
31
32/// keeps track of HappyConnOpener spare track waiting state
33class HappySpareWait {
34public:
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.
72class HappyConnOpenerAnswer
73{
74public:
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)
97std::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.
102class HappyConnOpener: public AsyncJob
103{
104 CBDATA_CHILD(HappyConnOpener);
105public:
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
130public:
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
155private:
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