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