]>
Commit | Line | Data |
---|---|---|
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 | ||
16 | ResolvedPeers::ResolvedPeers() | |
17 | { | |
18 | if (Config.forward_max_tries > 0) | |
19 | paths_.reserve(Config.forward_max_tries); | |
20 | } | |
21 | ||
22 | void | |
9b7992d9 | 23 | ResolvedPeers::reinstatePath(const PeerConnectionPointer &path) |
55622953 CT |
24 | { |
25 | debugs(17, 4, path); | |
26 | assert(path); | |
9b7992d9 EB |
27 | |
28 | const auto pos = path.position_; | |
29 | assert(pos < paths_.size()); | |
30 | ||
31 | assert(!paths_[pos].available); | |
32 | paths_[pos].available = true; | |
a4d576de EB |
33 | increaseAvailability(); |
34 | ||
35 | // if we restored availability of a path that we used to skip, update | |
9b7992d9 | 36 | const auto pathsToTheLeft = pos; |
a4d576de EB |
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 | } | |
55622953 CT |
43 | } |
44 | ||
45 | void | |
46 | ResolvedPeers::addPath(const Comm::ConnectionPointer &path) | |
47 | { | |
48 | paths_.emplace_back(path); | |
a4d576de EB |
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 ¤tPeer) | |
71 | { | |
72 | const auto path = start(); | |
73 | const auto foundNextOrSpare = path != paths_.end() && | |
70ac5b29 | 74 | (currentPeer.getPeer() != path->connection->getPeer() || // next peer |
75 | ConnectionFamily(currentPeer) != ConnectionFamily(*path->connection)); | |
a4d576de EB |
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 ¤tPeer) | |
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() && | |
70ac5b29 | 96 | primePeer != path->connection->getPeer(); |
a4d576de EB |
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 ¤tPeer) | |
103 | { | |
104 | const auto path = start(); | |
105 | const auto foundNext = path != paths_.end() && | |
70ac5b29 | 106 | currentPeer.getPeer() != path->connection->getPeer(); |
a4d576de | 107 | return makeFinding(path, foundNext); |
55622953 CT |
108 | } |
109 | ||
9b7992d9 | 110 | PeerConnectionPointer |
55622953 CT |
111 | ResolvedPeers::extractFront() |
112 | { | |
113 | Must(!empty()); | |
a4d576de | 114 | return extractFound("first: ", start()); |
55622953 CT |
115 | } |
116 | ||
9b7992d9 | 117 | PeerConnectionPointer |
55622953 CT |
118 | ResolvedPeers::extractPrime(const Comm::Connection ¤tPeer) |
119 | { | |
a4d576de EB |
120 | const auto found = findPrime(currentPeer).first; |
121 | if (found != paths_.end()) | |
122 | return extractFound("same-peer same-family match: ", found); | |
55622953 CT |
123 | |
124 | debugs(17, 7, "no same-peer same-family paths"); | |
125 | return nullptr; | |
126 | } | |
127 | ||
9b7992d9 | 128 | PeerConnectionPointer |
55622953 CT |
129 | ResolvedPeers::extractSpare(const Comm::Connection ¤tPeer) |
130 | { | |
a4d576de EB |
131 | const auto found = findSpare(currentPeer).first; |
132 | if (found != paths_.end()) | |
55622953 CT |
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 | |
9b7992d9 | 140 | PeerConnectionPointer |
a4d576de | 141 | ResolvedPeers::extractFound(const char *description, const Paths::iterator &found) |
55622953 | 142 | { |
a4d576de EB |
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 | ||
9b7992d9 EB |
154 | const auto cleanPath = path.connection->cloneDestinationDetails(); |
155 | return PeerConnectionPointer(cleanPath, found - paths_.begin()); | |
55622953 CT |
156 | } |
157 | ||
158 | bool | |
159 | ResolvedPeers::haveSpare(const Comm::Connection ¤tPeer) | |
160 | { | |
a4d576de EB |
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; | |
55622953 CT |
176 | } |
177 | ||
178 | bool | |
179 | ResolvedPeers::doneWithSpares(const Comm::Connection ¤tPeer) | |
180 | { | |
a4d576de | 181 | return doneWith(findSpare(currentPeer)); |
55622953 CT |
182 | } |
183 | ||
184 | bool | |
a4d576de | 185 | ResolvedPeers::doneWithPrimes(const Comm::Connection ¤tPeer) |
55622953 | 186 | { |
a4d576de | 187 | return doneWith(findPrime(currentPeer)); |
55622953 CT |
188 | } |
189 | ||
190 | bool | |
a4d576de | 191 | ResolvedPeers::doneWithPeer(const Comm::Connection ¤tPeer) |
55622953 | 192 | { |
a4d576de | 193 | return doneWith(findPeer(currentPeer)); |
55622953 CT |
194 | } |
195 | ||
196 | int | |
197 | ResolvedPeers::ConnectionFamily(const Comm::Connection &conn) | |
198 | { | |
199 | return conn.remote.isIPv4() ? AF_INET : AF_INET6; | |
200 | } | |
201 | ||
a4d576de EB |
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 | ||
55622953 CT |
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 | ||
9b7992d9 EB |
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 | } | |
70ac5b29 | 242 |