]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HappyConnOpener.h
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / HappyConnOpener.h
CommitLineData
55622953 1/*
77b1029d 2 * Copyright (C) 1996-2020 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"
17
18#include <iosfwd>
19
20class HappyConnOpener;
21class HappyOrderEnforcer;
22class JobGapEnforcer;
23class ResolvedPeers;
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
82 Comm::ConnectionPointer 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)
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:
159 explicit operator bool() const { return static_cast<bool>(path); }
160 void clear() { path = nullptr; connector = nullptr; }
161
162 Comm::ConnectionPointer path; ///< the destination we are connecting to
163 AsyncCall::Pointer connector; ///< our Comm::ConnOpener callback
164 };
165
166 /* AsyncJob API */
167 virtual void start() override;
168 virtual bool doneAll() const override;
169 virtual void swanSong() override;
170 virtual const char *status() const override;
171
172 void maybeOpenAnotherPrimeConnection();
173
174 void maybeGivePrimeItsChance();
175 void stopGivingPrimeItsChance();
176 void stopWaitingForSpareAllowance();
177 void maybeOpenSpareConnection();
178
179 void startConnecting(Attempt &, Comm::ConnectionPointer &);
180 void openFreshConnection(Attempt &, Comm::ConnectionPointer &);
181 bool reuseOldConnection(const Comm::ConnectionPointer &);
182
183 void connectDone(const CommConnectCbParams &);
184
185 void checkForNewConnection();
186
187 void updateSpareWaitAfterPrimeFailure();
188
189 void cancelSpareWait(const char *reason);
190
191 bool ranOutOfTimeOrAttempts() const;
192
193 ErrorState *makeError(const err_type type) const;
194 Answer *futureAnswer(const Comm::ConnectionPointer &);
195 void sendSuccess(const Comm::ConnectionPointer &conn, bool reused, const char *connKind);
196 void sendFailure();
197
198 const time_t fwdStart; ///< requestor start time
199
200 AsyncCall::Pointer callback_; ///< handler to be called on connection completion.
201
202 /// Candidate paths. Shared with the initiator. May not be finalized yet.
203 ResolvedPeersPointer destinations;
204
205 /// current connection opening attempt on the prime track (if any)
206 Attempt prime;
207
208 /// current connection opening attempt on the spare track (if any)
209 Attempt spare;
210
211 /// CachePeer and IP address family of the peer we are trying to connect
212 /// to now (or, if we are just waiting for paths to a new peer, nil)
213 Comm::ConnectionPointer currentPeer;
214
215 /// preconditions for an attempt to open a spare connection
216 HappySpareWait spareWaiting;
217 friend class HappyOrderEnforcer;
218
219 AccessLogEntryPointer ale; ///< transaction details
220
221 ErrorState *lastError = nullptr; ///< last problem details (or nil)
222 Comm::ConnectionPointer lastFailedConnection; ///< nil if none has failed
223
224 /// whether spare connection attempts disregard happy_eyeballs_* settings
225 bool ignoreSpareRestrictions = false;
226
227 /// whether we have received a permission to open a spare while spares are limited
228 bool gotSpareAllowance = false;
229
230 /// whether persistent connections are allowed
231 bool allowPconn_ = true;
232
233 /// whether we are opening connections for a request that may be resent
234 bool retriable_ = true;
235
236 /// origin server domain name (or equivalent)
237 const char *host_ = nullptr;
238
239 /// the request that needs a to-server connection
240 HttpRequestPointer cause;
241
242 /// number of connection opening attempts, including those in the requestor
243 int n_tries;
244
245 /// Reason to ran out of time or attempts
246 mutable const char *ranOutOfTimeOrAttemptsEarlier_ = nullptr;
247};
248
249#endif
250