]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ResolvedPeers.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / ResolvedPeers.cc
1 /*
2 * Copyright (C) 1996-2023 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 #include "squid.h"
10 #include "CachePeer.h"
11 #include "comm/Connection.h"
12 #include "comm/ConnOpener.h"
13 #include "ResolvedPeers.h"
14 #include "SquidConfig.h"
15
16 ResolvedPeers::ResolvedPeers()
17 {
18 if (Config.forward_max_tries > 0)
19 paths_.reserve(Config.forward_max_tries);
20 }
21
22 void
23 ResolvedPeers::reinstatePath(const PeerConnectionPointer &path)
24 {
25 debugs(17, 4, path);
26 assert(path);
27
28 const auto pos = path.position_;
29 assert(pos < paths_.size());
30
31 assert(!paths_[pos].available);
32 paths_[pos].available = true;
33 increaseAvailability();
34
35 // if we restored availability of a path that we used to skip, update
36 const auto pathsToTheLeft = pos;
37 if (pathsToTheLeft < pathsToSkip) {
38 pathsToSkip = pathsToTheLeft;
39 } else {
40 // *found was unavailable so pathsToSkip could not end at it
41 Must(pathsToTheLeft != pathsToSkip);
42 }
43 }
44
45 void
46 ResolvedPeers::addPath(const Comm::ConnectionPointer &path)
47 {
48 paths_.emplace_back(path);
49 Must(paths_.back().available); // no pathsToSkip updates are needed
50 increaseAvailability();
51 }
52
53 /// \returns the beginning iterator for any available-path search
54 ResolvedPeers::Paths::iterator
55 ResolvedPeers::start()
56 {
57 Must(pathsToSkip <= paths_.size());
58 return paths_.begin() + pathsToSkip; // may return end()
59 }
60
61 /// finalizes the iterator part of the given preliminary find*() result
62 ResolvedPeers::Finding
63 ResolvedPeers::makeFinding(const Paths::iterator &path, bool foundOther)
64 {
65 return std::make_pair((foundOther ? paths_.end() : path), foundOther);
66 }
67
68 /// \returns the first available same-peer same-family Finding or <end,...>
69 ResolvedPeers::Finding
70 ResolvedPeers::findPrime(const Comm::Connection &currentPeer)
71 {
72 const auto path = start();
73 const auto foundNextOrSpare = path != paths_.end() &&
74 (currentPeer.getPeer() != path->connection->getPeer() || // next peer
75 ConnectionFamily(currentPeer) != ConnectionFamily(*path->connection));
76 return makeFinding(path, foundNextOrSpare);
77 }
78
79 /// \returns the first available same-peer different-family Finding or <end,...>
80 ResolvedPeers::Finding
81 ResolvedPeers::findSpare(const Comm::Connection &currentPeer)
82 {
83 const auto primeFamily = ConnectionFamily(currentPeer);
84 const auto primePeer = currentPeer.getPeer();
85 const auto path = std::find_if(start(), paths_.end(),
86 [primeFamily, primePeer](const ResolvedPeerPath &candidate) {
87 if (!candidate.available)
88 return false;
89 if (primePeer != candidate.connection->getPeer())
90 return true; // found next peer
91 if (primeFamily != ConnectionFamily(*candidate.connection))
92 return true; // found spare
93 return false;
94 });
95 const auto foundNext = path != paths_.end() &&
96 primePeer != path->connection->getPeer();
97 return makeFinding(path, foundNext);
98 }
99
100 /// \returns the first available same-peer Finding or <end,...>
101 ResolvedPeers::Finding
102 ResolvedPeers::findPeer(const Comm::Connection &currentPeer)
103 {
104 const auto path = start();
105 const auto foundNext = path != paths_.end() &&
106 currentPeer.getPeer() != path->connection->getPeer();
107 return makeFinding(path, foundNext);
108 }
109
110 PeerConnectionPointer
111 ResolvedPeers::extractFront()
112 {
113 Must(!empty());
114 return extractFound("first: ", start());
115 }
116
117 PeerConnectionPointer
118 ResolvedPeers::extractPrime(const Comm::Connection &currentPeer)
119 {
120 const auto found = findPrime(currentPeer).first;
121 if (found != paths_.end())
122 return extractFound("same-peer same-family match: ", found);
123
124 debugs(17, 7, "no same-peer same-family paths");
125 return nullptr;
126 }
127
128 PeerConnectionPointer
129 ResolvedPeers::extractSpare(const Comm::Connection &currentPeer)
130 {
131 const auto found = findSpare(currentPeer).first;
132 if (found != paths_.end())
133 return extractFound("same-peer different-family match: ", found);
134
135 debugs(17, 7, "no same-peer different-family paths");
136 return nullptr;
137 }
138
139 /// convenience method to finish a successful extract*() call
140 PeerConnectionPointer
141 ResolvedPeers::extractFound(const char *description, const Paths::iterator &found)
142 {
143 auto &path = *found;
144 debugs(17, 7, description << path.connection);
145 assert(path.available);
146 path.available = false;
147 decreaseAvailability();
148
149 // if we extracted the left-most available path, find the next one
150 if (static_cast<size_type>(found - paths_.begin()) == pathsToSkip) {
151 while (++pathsToSkip < paths_.size() && !paths_[pathsToSkip].available) {}
152 }
153
154 const auto cleanPath = path.connection->cloneProfile();
155 return PeerConnectionPointer(cleanPath, found - paths_.begin());
156 }
157
158 bool
159 ResolvedPeers::haveSpare(const Comm::Connection &currentPeer)
160 {
161 return findSpare(currentPeer).first != paths_.end();
162 }
163
164 /// whether paths_ have no (and will have no) Xs for the current peer based on
165 /// the given findX(current peer) result
166 bool
167 ResolvedPeers::doneWith(const Finding &findings) const
168 {
169 if (findings.first != paths_.end())
170 return false; // not done because the caller found a viable path X
171
172 // The caller did not find any path X. If the caller found any "other"
173 // paths, then we are done with paths X. If there are no "other" paths,
174 // then destinationsFinalized is the answer.
175 return findings.second ? true : destinationsFinalized;
176 }
177
178 bool
179 ResolvedPeers::doneWithSpares(const Comm::Connection &currentPeer)
180 {
181 return doneWith(findSpare(currentPeer));
182 }
183
184 bool
185 ResolvedPeers::doneWithPrimes(const Comm::Connection &currentPeer)
186 {
187 return doneWith(findPrime(currentPeer));
188 }
189
190 bool
191 ResolvedPeers::doneWithPeer(const Comm::Connection &currentPeer)
192 {
193 return doneWith(findPeer(currentPeer));
194 }
195
196 int
197 ResolvedPeers::ConnectionFamily(const Comm::Connection &conn)
198 {
199 return conn.remote.isIPv4() ? AF_INET : AF_INET6;
200 }
201
202 /// increments the number of available paths
203 void
204 ResolvedPeers::increaseAvailability()
205 {
206 ++availablePaths;
207 assert(availablePaths <= paths_.size());
208 }
209
210 /// decrements the number of available paths
211 void
212 ResolvedPeers::decreaseAvailability()
213 {
214 assert(availablePaths > 0);
215 --availablePaths;
216 }
217
218 std::ostream &
219 operator <<(std::ostream &os, const ResolvedPeers &peers)
220 {
221 if (peers.empty())
222 return os << "[no paths]";
223 return os << peers.size() << (peers.destinationsFinalized ? "" : "+") << " paths";
224 }
225
226 /* PeerConnectionPointer */
227
228 void
229 PeerConnectionPointer::print(std::ostream &os) const
230 {
231 // We should see no combinations of a nil connection and a set position
232 // because assigning nullptr (to our smart pointer) naturally erases both
233 // fields. We report such unexpected combinations for debugging sake, but do
234 // not complicate this code to report them beautifully.
235
236 if (connection_)
237 os << connection_;
238
239 if (position_ != npos)
240 os << " @" << position_;
241 }
242