]>
Commit | Line | Data |
---|---|---|
062e2281 | 1 | /* |
f6e9a3ee | 2 | * Copyright (C) 1996-2019 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
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. | |
062e2281 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 44 Peer Selection Algorithm */ |
10 | ||
582c2af2 FC |
11 | #include "squid.h" |
12 | #include "acl/FilledChecklist.h" | |
6043e368 | 13 | #include "base/InstanceId.h" |
a011edee | 14 | #include "CachePeer.h" |
21c22f04 | 15 | #include "carp.h" |
582c2af2 | 16 | #include "client_side.h" |
4a3b98d7 | 17 | #include "dns/LookupDetails.h" |
a37fdd8a | 18 | #include "errorpage.h" |
a553a5a3 | 19 | #include "event.h" |
eb13c21e | 20 | #include "FwdState.h" |
af69c635 | 21 | #include "globals.h" |
bbaf2685 | 22 | #include "hier_code.h" |
924f73bc | 23 | #include "htcp.h" |
d3dddfb5 | 24 | #include "http/Stream.h" |
582c2af2 | 25 | #include "HttpRequest.h" |
9b5c4a9a | 26 | #include "icmp/net_db.h" |
582c2af2 | 27 | #include "ICP.h" |
c7a52e6d | 28 | #include "ip/tools.h" |
602d9612 | 29 | #include "ipcache.h" |
f0ba2534 | 30 | #include "neighbors.h" |
f795b373 | 31 | #include "peer_sourcehash.h" |
37236ba1 | 32 | #include "peer_userhash.h" |
582c2af2 | 33 | #include "PeerSelectState.h" |
4d5904f7 | 34 | #include "SquidConfig.h" |
582c2af2 FC |
35 | #include "SquidTime.h" |
36 | #include "Store.h" | |
062e2281 | 37 | |
6043e368 AR |
38 | /** |
39 | * A CachePeer which has been selected as a possible destination. | |
40 | * Listed as pointers here so as to prevent duplicates being added but will | |
41 | * be converted to a set of IP address path options before handing back out | |
42 | * to the caller. | |
43 | * | |
44 | * Certain connection flags and outgoing settings will also be looked up and | |
45 | * set based on the received request and CachePeer settings before handing back. | |
46 | */ | |
47 | class FwdServer | |
48 | { | |
49 | MEMPROXY_CLASS(FwdServer); | |
50 | ||
51 | public: | |
52 | FwdServer(CachePeer *p, hier_code c) : | |
53 | _peer(p), | |
54 | code(c), | |
55 | next(nullptr) | |
56 | {} | |
57 | ||
58 | CbcPointer<CachePeer> _peer; /* NULL --> origin server */ | |
59 | hier_code code; | |
60 | FwdServer *next; | |
61 | }; | |
62 | ||
26ac0430 | 63 | static struct { |
75e88d56 | 64 | int timeouts; |
2fadd50d | 65 | } PeerStats; |
062e2281 | 66 | |
26ac0430 AJ |
67 | static const char *DirectStr[] = { |
68 | "DIRECT_UNKNOWN", | |
69 | "DIRECT_NO", | |
70 | "DIRECT_MAYBE", | |
71 | "DIRECT_YES" | |
72 | }; | |
75e88d56 | 73 | |
e7b08eec AR |
74 | /// a helper class to report a selected destination (for debugging) |
75 | class PeerSelectionDumper | |
76 | { | |
77 | public: | |
cb835136 AR |
78 | PeerSelectionDumper(const PeerSelector * const aSelector, const CachePeer * const aPeer, const hier_code aCode): |
79 | selector(aSelector), peer(aPeer), code(aCode) {} | |
e7b08eec | 80 | |
cb835136 | 81 | const PeerSelector * const selector; ///< selection parameters |
e7b08eec AR |
82 | const CachePeer * const peer; ///< successful selection info |
83 | const hier_code code; ///< selection algorithm | |
84 | }; | |
85 | ||
cb835136 | 86 | CBDATA_CLASS_INIT(PeerSelector); |
aa839030 | 87 | |
e7b08eec AR |
88 | /// prints PeerSelectionDumper (for debugging) |
89 | static std::ostream & | |
90 | operator <<(std::ostream &os, const PeerSelectionDumper &fsd) | |
91 | { | |
92 | os << hier_code_str[fsd.code]; | |
93 | ||
94 | if (fsd.peer) | |
95 | os << '/' << fsd.peer->host; | |
cb835136 AR |
96 | else if (fsd.selector) // useful for DIRECT and gone PINNED destinations |
97 | os << '#' << fsd.selector->request->url.host(); | |
e7b08eec AR |
98 | |
99 | return os; | |
100 | } | |
101 | ||
cb835136 | 102 | PeerSelector::~PeerSelector() |
348b2031 | 103 | { |
36339742 AJ |
104 | while (servers) { |
105 | FwdServer *next = servers->next; | |
3c670b50 | 106 | delete servers; |
36339742 AJ |
107 | servers = next; |
108 | } | |
109 | ||
029c8349 AJ |
110 | if (entry) { |
111 | debugs(44, 3, entry->url()); | |
5ae21d99 | 112 | |
029c8349 | 113 | if (entry->ping_status == PING_WAITING) |
cb835136 | 114 | eventDelete(HandlePingTimeout, this); |
5ae21d99 | 115 | |
029c8349 | 116 | entry->ping_status = PING_DONE; |
5ae21d99 AJ |
117 | } |
118 | ||
029c8349 | 119 | if (acl_checklist) { |
cb835136 | 120 | debugs(44, DBG_IMPORTANT, "BUG: peer selector gone while waiting for a slow ACL"); |
029c8349 | 121 | delete acl_checklist; |
348b2031 | 122 | } |
62e76326 | 123 | |
029c8349 | 124 | HTTPMSGUNLOCK(request); |
62e76326 | 125 | |
029c8349 AJ |
126 | if (entry) { |
127 | assert(entry->ping_status != PING_WAITING); | |
1f90fd4a | 128 | entry->unlock("peerSelect"); |
029c8349 | 129 | entry = NULL; |
73a201f8 | 130 | } |
62e76326 | 131 | |
029c8349 | 132 | delete lastError; |
348b2031 | 133 | } |
062e2281 | 134 | |
2d72d4fd | 135 | static int |
cb365059 | 136 | peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry * entry) |
062e2281 | 137 | { |
cb365059 EB |
138 | assert(ps); |
139 | HttpRequest *request = ps->request; | |
140 | ||
7b665aeb | 141 | int n; |
db1cd23c | 142 | assert(entry); |
143 | assert(entry->ping_status == PING_NONE); | |
9bd6a36d | 144 | assert(direct != DIRECT_YES); |
cb835136 | 145 | debugs(44, 3, entry->url()); |
62e76326 | 146 | |
45e5102d | 147 | if (!request->flags.hierarchical && direct != DIRECT_NO) |
62e76326 | 148 | return 0; |
149 | ||
d46a87a8 | 150 | if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys) |
62e76326 | 151 | if (direct != DIRECT_NO) |
152 | return 0; | |
153 | ||
cb365059 | 154 | n = neighborsCount(ps); |
62e76326 | 155 | |
cb835136 | 156 | debugs(44, 3, "counted " << n << " neighbors"); |
62e76326 | 157 | |
7b665aeb | 158 | return n; |
062e2281 | 159 | } |
160 | ||
6043e368 AR |
161 | static void |
162 | peerSelect(PeerSelectionInitiator *initiator, | |
cfd66529 | 163 | HttpRequest * request, |
d4806c91 | 164 | AccessLogEntry::Pointer const &al, |
6043e368 | 165 | StoreEntry * entry) |
75e88d56 | 166 | { |
86b389fc | 167 | if (entry) |
7f06a3d8 | 168 | debugs(44, 3, *entry << ' ' << entry->url()); |
86b389fc | 169 | else |
7f06a3d8 | 170 | debugs(44, 3, request->method); |
62e76326 | 171 | |
cb835136 | 172 | const auto selector = new PeerSelector(initiator); |
62e76326 | 173 | |
cb835136 AR |
174 | selector->request = request; |
175 | HTTPMSGLOCK(selector->request); | |
176 | selector->al = al; | |
62e76326 | 177 | |
cb835136 | 178 | selector->entry = entry; |
62e76326 | 179 | |
6cfa8966 | 180 | #if USE_CACHE_DIGESTS |
62e76326 | 181 | |
39edba21 | 182 | request->hier.peer_select_start = current_time; |
62e76326 | 183 | |
39edba21 | 184 | #endif |
62e76326 | 185 | |
cb835136 AR |
186 | if (selector->entry) |
187 | selector->entry->lock("peerSelect"); | |
62e76326 | 188 | |
cb835136 | 189 | selector->selectMore(); |
75e88d56 | 190 | } |
191 | ||
6043e368 AR |
192 | void |
193 | PeerSelectionInitiator::startSelectingDestinations(HttpRequest *request, const AccessLogEntry::Pointer &ale, StoreEntry *entry) | |
194 | { | |
195 | subscribed = true; | |
196 | peerSelect(this, request, ale, entry); | |
197 | // and wait for noteDestination() and/or noteDestinationsEnd() calls | |
198 | } | |
199 | ||
cb835136 | 200 | void |
329c128c | 201 | PeerSelector::checkNeverDirectDone(const Acl::Answer answer) |
75e88d56 | 202 | { |
cb835136 AR |
203 | acl_checklist = nullptr; |
204 | debugs(44, 3, answer); | |
205 | never_direct = answer; | |
3985b3f8 | 206 | switch (answer) { |
ae026ec6 | 207 | case ACCESS_ALLOWED: |
f365d297 | 208 | /** if never_direct says YES, do that. */ |
cb835136 AR |
209 | direct = DIRECT_NO; |
210 | debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct allow)"); | |
ae026ec6 AJ |
211 | break; |
212 | case ACCESS_DENIED: // not relevant. | |
1f585949 | 213 | case ACCESS_DUNNO: // not relevant. |
ae026ec6 | 214 | break; |
1f585949 | 215 | case ACCESS_AUTH_REQUIRED: |
ae026ec6 | 216 | debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here."); |
1f585949 | 217 | break; |
ae026ec6 | 218 | } |
cb835136 | 219 | selectMore(); |
75e88d56 | 220 | } |
221 | ||
cb835136 | 222 | void |
329c128c | 223 | PeerSelector::CheckNeverDirectDone(Acl::Answer answer, void *data) |
75e88d56 | 224 | { |
cb835136 AR |
225 | static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer); |
226 | } | |
227 | ||
228 | void | |
329c128c | 229 | PeerSelector::checkAlwaysDirectDone(const Acl::Answer answer) |
cb835136 AR |
230 | { |
231 | acl_checklist = nullptr; | |
232 | debugs(44, 3, answer); | |
233 | always_direct = answer; | |
3985b3f8 | 234 | switch (answer) { |
ae026ec6 AJ |
235 | case ACCESS_ALLOWED: |
236 | /** if always_direct says YES, do that. */ | |
cb835136 AR |
237 | direct = DIRECT_YES; |
238 | debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct allow)"); | |
ae026ec6 AJ |
239 | break; |
240 | case ACCESS_DENIED: // not relevant. | |
1f585949 | 241 | case ACCESS_DUNNO: // not relevant. |
ae026ec6 | 242 | break; |
1f585949 | 243 | case ACCESS_AUTH_REQUIRED: |
ae026ec6 | 244 | debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here."); |
1f585949 | 245 | break; |
ae026ec6 | 246 | } |
cb835136 AR |
247 | selectMore(); |
248 | } | |
249 | ||
250 | void | |
329c128c | 251 | PeerSelector::CheckAlwaysDirectDone(Acl::Answer answer, void *data) |
cb835136 AR |
252 | { |
253 | static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer); | |
75e88d56 | 254 | } |
255 | ||
cb835136 | 256 | /// \returns true (after destroying "this") if the peer initiator is gone |
6043e368 | 257 | /// \returns false (without side effects) otherwise |
cb835136 AR |
258 | bool |
259 | PeerSelector::selectionAborted() | |
6043e368 | 260 | { |
cb835136 | 261 | if (interestedInitiator()) |
6043e368 AR |
262 | return false; |
263 | ||
264 | debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest."); | |
cb835136 | 265 | delete this; |
6043e368 AR |
266 | return true; |
267 | } | |
268 | ||
cb835136 | 269 | /// A single DNS resolution loop iteration: Converts selected FwdServer to IPs. |
cfd66529 | 270 | void |
cb835136 | 271 | PeerSelector::resolveSelected() |
cfd66529 | 272 | { |
cb835136 | 273 | if (selectionAborted()) |
36339742 | 274 | return; |
6043e368 | 275 | |
cb835136 | 276 | FwdServer *fs = servers; |
36339742 | 277 | |
7177edfb AJ |
278 | // Bug 3243: CVE 2009-0801 |
279 | // Bypass of browser same-origin access control in intercepted communication | |
280 | // To resolve this we must use only the original client destination when going DIRECT | |
281 | // on intercepted traffic which failed Host verification | |
cb835136 | 282 | const HttpRequest *req = request; |
45e5102d | 283 | const bool isIntercepted = !req->flags.redirected && |
0d901ef4 | 284 | (req->flags.intercepted || req->flags.interceptTproxy); |
45e5102d | 285 | const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified; |
7177edfb AJ |
286 | const bool choseDirect = fs && fs->code == HIER_DIRECT; |
287 | if (isIntercepted && useOriginalDst && choseDirect) { | |
d9371d7b AJ |
288 | // check the client is still around before using any of its details |
289 | if (req->clientConnectionManager.valid()) { | |
290 | // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT | |
291 | Comm::ConnectionPointer p = new Comm::Connection(); | |
292 | p->remote = req->clientConnectionManager->clientConnection->local; | |
6043e368 | 293 | fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display. |
cb835136 | 294 | handlePath(p, *fs); |
d9371d7b | 295 | } |
7177edfb AJ |
296 | |
297 | // clear the used fs and continue | |
cb835136 | 298 | servers = fs->next; |
3c670b50 | 299 | delete fs; |
cb835136 | 300 | resolveSelected(); |
7177edfb AJ |
301 | return; |
302 | } | |
303 | ||
3dde9e52 CT |
304 | if (fs && fs->code == PINNED) { |
305 | // Nil path signals a PINNED destination selection. Our initiator should | |
306 | // borrow and use clientConnectionManager's pinned connection object | |
307 | // (regardless of that connection destination). | |
308 | handlePath(nullptr, *fs); | |
309 | servers = fs->next; | |
310 | delete fs; | |
311 | resolveSelected(); | |
312 | return; | |
313 | } | |
314 | ||
cfd66529 | 315 | // convert the list of FwdServer destinations into destinations IP addresses |
cb835136 | 316 | if (fs && wantsMoreDestinations()) { |
cfd66529 | 317 | // send the next one off for DNS lookup. |
cb835136 AR |
318 | const char *host = fs->_peer.valid() ? fs->_peer->host : request->url.host(); |
319 | debugs(44, 2, "Find IP destination for: " << url() << "' via " << host); | |
320 | Dns::nbgethostbyname(host, this); | |
cfd66529 AJ |
321 | return; |
322 | } | |
323 | ||
c2f4e9cc AJ |
324 | // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries. |
325 | // due to the allocation method of fs, we must deallocate each manually. | |
326 | // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries | |
6043e368 | 327 | if (fs) { |
cb835136 | 328 | assert(fs == servers); |
9bc68187 | 329 | while (fs) { |
cb835136 | 330 | servers = fs->next; |
3c670b50 | 331 | delete fs; |
cb835136 | 332 | fs = servers; |
c2f4e9cc AJ |
333 | } |
334 | } | |
335 | ||
cfd66529 | 336 | // done with DNS lookups. pass back to caller |
62e76326 | 337 | |
cb835136 AR |
338 | debugs(44, 2, id << " found all " << foundPaths << " destinations for " << url()); |
339 | debugs(44, 2, " always_direct = " << always_direct); | |
340 | debugs(44, 2, " never_direct = " << never_direct); | |
341 | debugs(44, 2, " timedout = " << ping.timedout); | |
5ae21d99 | 342 | |
cb835136 AR |
343 | ping.stop = current_time; |
344 | request->hier.ping = ping; // final result | |
3dfe8371 | 345 | |
cb835136 | 346 | if (lastError && foundPaths) { |
6043e368 AR |
347 | // nobody cares about errors if we found destinations despite them |
348 | debugs(44, 3, "forgetting the last error"); | |
cb835136 AR |
349 | delete lastError; |
350 | lastError = nullptr; | |
db1cd23c | 351 | } |
62e76326 | 352 | |
cb835136 AR |
353 | if (const auto initiator = interestedInitiator()) |
354 | initiator->noteDestinationsEnd(lastError); | |
355 | lastError = nullptr; // initiator owns the ErrorState object now | |
356 | delete this; | |
93775f90 | 357 | } |
358 | ||
fd9c47d1 | 359 | void |
cb835136 | 360 | PeerSelector::noteLookup(const Dns::LookupDetails &details) |
cfd66529 | 361 | { |
fd9c47d1 | 362 | /* ignore lookup delays that occurred after the initiator moved on */ |
cfd66529 | 363 | |
cb835136 | 364 | if (selectionAborted()) |
fd9c47d1 | 365 | return; |
cfd66529 | 366 | |
fd9c47d1 AR |
367 | if (!wantsMoreDestinations()) |
368 | return; | |
cfd66529 | 369 | |
fd9c47d1 AR |
370 | request->recordLookup(details); |
371 | } | |
5229395c | 372 | |
fd9c47d1 | 373 | void |
cb835136 | 374 | PeerSelector::noteIp(const Ip::Address &ip) |
fd9c47d1 | 375 | { |
cb835136 | 376 | if (selectionAborted()) |
fd9c47d1 | 377 | return; |
cfd66529 | 378 | |
fd9c47d1 AR |
379 | if (!wantsMoreDestinations()) |
380 | return; | |
27d1f0a0 | 381 | |
fd9c47d1 | 382 | const auto peer = servers->_peer.valid(); |
cfd66529 | 383 | |
fd9c47d1 AR |
384 | // for TPROXY spoofing, we must skip unusable addresses |
385 | if (request->flags.spoofClientIp && !(peer && peer->options.no_tproxy) ) { | |
386 | if (ip.isIPv4() != request->client_addr.isIPv4()) | |
387 | return; // cannot spoof the client address on this link | |
388 | } | |
c7a52e6d | 389 | |
fd9c47d1 AR |
390 | Comm::ConnectionPointer p = new Comm::Connection(); |
391 | p->remote = ip; | |
392 | p->remote.port(peer ? peer->http_port : request->url.port()); | |
393 | handlePath(p, *servers); | |
394 | } | |
c7a52e6d | 395 | |
fd9c47d1 | 396 | void |
cb835136 | 397 | PeerSelector::noteIps(const Dns::CachedIps *ia, const Dns::LookupDetails &details) |
fd9c47d1 | 398 | { |
cb835136 | 399 | if (selectionAborted()) |
fd9c47d1 | 400 | return; |
cfd66529 | 401 | |
fd9c47d1 AR |
402 | FwdServer *fs = servers; |
403 | if (!ia) { | |
404 | debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host())); | |
a37fdd8a | 405 | // discard any previous error. |
fd9c47d1 AR |
406 | delete lastError; |
407 | lastError = NULL; | |
a37fdd8a | 408 | if (fs->code == HIER_DIRECT) { |
7e6eabbc | 409 | lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, request, al); |
fd9c47d1 | 410 | lastError->dnsError = details.error; |
a37fdd8a | 411 | } |
cfd66529 | 412 | } |
fd9c47d1 | 413 | // else noteIp() calls have already processed all IPs in *ia |
cfd66529 | 414 | |
fd9c47d1 | 415 | servers = fs->next; |
3c670b50 | 416 | delete fs; |
cfd66529 | 417 | |
cb835136 AR |
418 | // continue resolving selected peers |
419 | resolveSelected(); | |
cfd66529 AJ |
420 | } |
421 | ||
cb835136 AR |
422 | int |
423 | PeerSelector::checkNetdbDirect() | |
b3264694 | 424 | { |
9b5c4a9a | 425 | #if USE_ICMP |
a3c6762c | 426 | CachePeer *p; |
b3264694 | 427 | int myrtt; |
428 | int myhops; | |
62e76326 | 429 | |
cb835136 | 430 | if (direct == DIRECT_NO) |
62e76326 | 431 | return 0; |
432 | ||
9b5c4a9a AJ |
433 | /* base lookup on RTT and Hops if ICMP NetDB is enabled. */ |
434 | ||
cb835136 | 435 | myrtt = netdbHostRtt(request->url.host()); |
5c51bffb AJ |
436 | debugs(44, 3, "MY RTT = " << myrtt << " msec"); |
437 | debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec"); | |
62e76326 | 438 | |
5f84d830 | 439 | if (myrtt && myrtt <= Config.minDirectRtt) |
62e76326 | 440 | return 1; |
441 | ||
cb835136 | 442 | myhops = netdbHostHops(request->url.host()); |
62e76326 | 443 | |
cb835136 AR |
444 | debugs(44, 3, "MY hops = " << myhops); |
445 | debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops); | |
62e76326 | 446 | |
b3264694 | 447 | if (myhops && myhops <= Config.minDirectHops) |
62e76326 | 448 | return 1; |
449 | ||
cb835136 | 450 | p = whichPeer(closest_parent_miss); |
62e76326 | 451 | |
5f84d830 | 452 | if (p == NULL) |
62e76326 | 453 | return 0; |
454 | ||
cb835136 | 455 | debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec"); |
62e76326 | 456 | |
cb835136 | 457 | if (myrtt && myrtt <= ping.p_rtt) |
62e76326 | 458 | return 1; |
459 | ||
9b5c4a9a AJ |
460 | #endif /* USE_ICMP */ |
461 | ||
b3264694 | 462 | return 0; |
463 | } | |
464 | ||
cb835136 AR |
465 | void |
466 | PeerSelector::selectMore() | |
062e2281 | 467 | { |
cb835136 | 468 | if (selectionAborted()) |
36339742 | 469 | return; |
36339742 | 470 | |
5c51bffb | 471 | debugs(44, 3, request->method << ' ' << request->url.host()); |
62e76326 | 472 | |
45e5102d | 473 | /** If we don't know whether DIRECT is permitted ... */ |
cb835136 AR |
474 | if (direct == DIRECT_UNKNOWN) { |
475 | if (always_direct == ACCESS_DUNNO) { | |
476 | debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)"); | |
45e5102d | 477 | /** check always_direct; */ |
d4806c91 | 478 | ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL); |
cb835136 AR |
479 | ch->al = al; |
480 | acl_checklist = ch; | |
cb365059 | 481 | acl_checklist->syncAle(request, nullptr); |
cb835136 | 482 | acl_checklist->nonBlockingCheck(CheckAlwaysDirectDone, this); |
62e76326 | 483 | return; |
cb835136 AR |
484 | } else if (never_direct == ACCESS_DUNNO) { |
485 | debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)"); | |
45e5102d | 486 | /** check never_direct; */ |
d4806c91 | 487 | ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL); |
cb835136 AR |
488 | ch->al = al; |
489 | acl_checklist = ch; | |
cb365059 | 490 | acl_checklist->syncAle(request, nullptr); |
cb835136 | 491 | acl_checklist->nonBlockingCheck(CheckNeverDirectDone, this); |
62e76326 | 492 | return; |
450fe1cb | 493 | } else if (request->flags.noDirect) { |
45e5102d | 494 | /** if we are accelerating, direct is not an option. */ |
cb835136 AR |
495 | direct = DIRECT_NO; |
496 | debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)"); | |
450fe1cb | 497 | } else if (request->flags.loopDetected) { |
45e5102d | 498 | /** if we are in a forwarding-loop, direct is not an option. */ |
cb835136 AR |
499 | direct = DIRECT_YES; |
500 | debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)"); | |
501 | } else if (checkNetdbDirect()) { | |
502 | direct = DIRECT_YES; | |
503 | debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)"); | |
62e76326 | 504 | } else { |
cb835136 AR |
505 | direct = DIRECT_MAYBE; |
506 | debugs(44, 3, "direct = " << DirectStr[direct] << " (default)"); | |
62e76326 | 507 | } |
508 | ||
cb835136 | 509 | debugs(44, 3, "direct = " << DirectStr[direct]); |
db1cd23c | 510 | } |
62e76326 | 511 | |
d67acb4e | 512 | if (!entry || entry->ping_status == PING_NONE) |
cb835136 | 513 | selectPinned(); |
df503d6c | 514 | if (entry == NULL) { |
62e76326 | 515 | (void) 0; |
df503d6c | 516 | } else if (entry->ping_status == PING_NONE) { |
cb835136 | 517 | selectSomeNeighbor(); |
62e76326 | 518 | |
519 | if (entry->ping_status == PING_WAITING) | |
520 | return; | |
db1cd23c | 521 | } else if (entry->ping_status == PING_WAITING) { |
cb835136 | 522 | selectSomeNeighborReplies(); |
62e76326 | 523 | entry->ping_status = PING_DONE; |
db1cd23c | 524 | } |
62e76326 | 525 | |
cb835136 | 526 | switch (direct) { |
62e76326 | 527 | |
168dfda9 | 528 | case DIRECT_YES: |
cb835136 | 529 | selectSomeDirect(); |
62e76326 | 530 | break; |
531 | ||
168dfda9 | 532 | case DIRECT_NO: |
cb835136 AR |
533 | selectSomeParent(); |
534 | selectAllParents(); | |
62e76326 | 535 | break; |
536 | ||
168dfda9 | 537 | default: |
62e76326 | 538 | |
539 | if (Config.onoff.prefer_direct) | |
cb835136 | 540 | selectSomeDirect(); |
62e76326 | 541 | |
45e5102d | 542 | if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) { |
cb835136 AR |
543 | selectSomeParent(); |
544 | selectAllParents(); | |
8b9a89c9 | 545 | } |
62e76326 | 546 | |
547 | if (!Config.onoff.prefer_direct) | |
cb835136 | 548 | selectSomeDirect(); |
62e76326 | 549 | |
550 | break; | |
168dfda9 | 551 | } |
62e76326 | 552 | |
cb835136 AR |
553 | // end peer selection; start resolving selected peers |
554 | resolveSelected(); | |
db1cd23c | 555 | } |
556 | ||
cb365059 | 557 | bool peerAllowedToUse(const CachePeer *, PeerSelector*); |
cfd66529 | 558 | |
cb835136 AR |
559 | /// Selects a pinned connection if it exists, is valid, and is allowed. |
560 | void | |
561 | PeerSelector::selectPinned() | |
d67acb4e | 562 | { |
cb835136 | 563 | // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough. |
d67acb4e AJ |
564 | if (!request->pinnedConnection()) |
565 | return; | |
3dde9e52 CT |
566 | |
567 | if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request))) { | |
568 | const auto pear = request->pinnedConnection()->pinnedPeer(); | |
cb365059 | 569 | const bool usePinned = pear ? peerAllowedToUse(pear, this) : (direct != DIRECT_NO); |
cb835136 AR |
570 | if (usePinned) { |
571 | addSelection(pear, PINNED); | |
572 | if (entry) | |
573 | entry->ping_status = PING_DONE; // skip ICP | |
d67acb4e AJ |
574 | } |
575 | } | |
cb835136 AR |
576 | // If the pinned connection is prohibited (for this request) or gone, then |
577 | // the initiator must decide whether it is OK to open a new one instead. | |
d67acb4e AJ |
578 | } |
579 | ||
27774cee | 580 | /** |
db1cd23c | 581 | * Selects a neighbor (parent or sibling) based on one of the |
582 | * following methods: | |
583 | * Cache Digests | |
584 | * CARP | |
9b5c4a9a | 585 | * ICMP Netdb RTT estimates |
db1cd23c | 586 | * ICP/HTCP queries |
587 | */ | |
cb835136 AR |
588 | void |
589 | PeerSelector::selectSomeNeighbor() | |
db1cd23c | 590 | { |
a3c6762c | 591 | CachePeer *p; |
db1cd23c | 592 | hier_code code = HIER_NONE; |
593 | assert(entry->ping_status == PING_NONE); | |
62e76326 | 594 | |
cb835136 | 595 | if (direct == DIRECT_YES) { |
62e76326 | 596 | entry->ping_status = PING_DONE; |
597 | return; | |
124511e5 | 598 | } |
62e76326 | 599 | |
6cfa8966 | 600 | #if USE_CACHE_DIGESTS |
cb365059 | 601 | if ((p = neighborsDigestSelect(this))) { |
5c51bffb | 602 | if (neighborType(p, request->url) == PEER_PARENT) |
62e76326 | 603 | code = CD_PARENT_HIT; |
604 | else | |
605 | code = CD_SIBLING_HIT; | |
db1cd23c | 606 | } else |
c127134a | 607 | #endif |
cb365059 | 608 | if ((p = netdbClosestParent(this))) { |
62e76326 | 609 | code = CLOSEST_PARENT; |
cb365059 | 610 | } else if (peerSelectIcpPing(this, direct, entry)) { |
cb835136 AR |
611 | debugs(44, 3, "Doing ICP pings"); |
612 | ping.start = current_time; | |
613 | ping.n_sent = neighborsUdpPing(request, | |
614 | entry, | |
615 | HandlePingReply, | |
616 | this, | |
617 | &ping.n_replies_expected, | |
618 | &ping.timeout); | |
619 | ||
620 | if (ping.n_sent == 0) | |
fa84c01d | 621 | debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0"); |
cb835136 AR |
622 | debugs(44, 3, ping.n_replies_expected << |
623 | " ICP replies expected, RTT " << ping.timeout << | |
26ac0430 | 624 | " msec"); |
62e76326 | 625 | |
cb835136 | 626 | if (ping.n_replies_expected > 0) { |
62e76326 | 627 | entry->ping_status = PING_WAITING; |
cb835136 AR |
628 | eventAdd("PeerSelector::HandlePingTimeout", |
629 | HandlePingTimeout, | |
630 | this, | |
631 | 0.001 * ping.timeout, | |
62e76326 | 632 | 0); |
633 | return; | |
634 | } | |
635 | } | |
636 | ||
db1cd23c | 637 | if (code != HIER_NONE) { |
62e76326 | 638 | assert(p); |
cb835136 | 639 | addSelection(p, code); |
db1cd23c | 640 | } |
62e76326 | 641 | |
db1cd23c | 642 | entry->ping_status = PING_DONE; |
643 | } | |
644 | ||
cb835136 AR |
645 | /// Selects a neighbor (parent or sibling) based on ICP/HTCP replies. |
646 | void | |
647 | PeerSelector::selectSomeNeighborReplies() | |
db1cd23c | 648 | { |
a3c6762c | 649 | CachePeer *p = NULL; |
db1cd23c | 650 | hier_code code = HIER_NONE; |
cb835136 AR |
651 | assert(entry->ping_status == PING_WAITING); |
652 | assert(direct != DIRECT_YES); | |
62e76326 | 653 | |
cb835136 | 654 | if (checkNetdbDirect()) { |
62e76326 | 655 | code = CLOSEST_DIRECT; |
cb835136 | 656 | addSelection(nullptr, code); |
62e76326 | 657 | return; |
db1cd23c | 658 | } |
62e76326 | 659 | |
cb835136 AR |
660 | if ((p = hit)) { |
661 | code = hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT; | |
26ac0430 | 662 | } else { |
cb835136 AR |
663 | if (!closest_parent_miss.isAnyAddr()) { |
664 | p = whichPeer(closest_parent_miss); | |
cc192b50 | 665 | code = CLOSEST_PARENT_MISS; |
cb835136 AR |
666 | } else if (!first_parent_miss.isAnyAddr()) { |
667 | p = whichPeer(first_parent_miss); | |
cc192b50 | 668 | code = FIRST_PARENT_MISS; |
669 | } | |
670 | } | |
db1cd23c | 671 | if (p && code != HIER_NONE) { |
cb835136 | 672 | addSelection(p, code); |
db1cd23c | 673 | } |
674 | } | |
675 | ||
cb835136 AR |
676 | /// Adds a "direct" entry if the request can be forwarded to the origin server. |
677 | void | |
678 | PeerSelector::selectSomeDirect() | |
db1cd23c | 679 | { |
cb835136 | 680 | if (direct == DIRECT_NO) |
62e76326 | 681 | return; |
682 | ||
db80e881 | 683 | /* WAIS is not implemented natively */ |
cb835136 | 684 | if (request->url.getScheme() == AnyP::PROTO_WAIS) |
26ac0430 | 685 | return; |
db80e881 | 686 | |
cb835136 | 687 | addSelection(nullptr, HIER_DIRECT); |
db1cd23c | 688 | } |
689 | ||
cb835136 AR |
690 | void |
691 | PeerSelector::selectSomeParent() | |
db1cd23c | 692 | { |
a3c6762c | 693 | CachePeer *p; |
db1cd23c | 694 | hier_code code = HIER_NONE; |
5c51bffb | 695 | debugs(44, 3, request->method << ' ' << request->url.host()); |
62e76326 | 696 | |
cb835136 | 697 | if (direct == DIRECT_YES) |
62e76326 | 698 | return; |
699 | ||
cb365059 | 700 | if ((p = peerSourceHashSelectParent(this))) { |
b4ab8666 | 701 | code = SOURCEHASH_PARENT; |
2f1431ea | 702 | #if USE_AUTH |
cb365059 | 703 | } else if ((p = peerUserHashSelectParent(this))) { |
f7e1d9ce | 704 | code = USERHASH_PARENT; |
2f1431ea | 705 | #endif |
cb365059 | 706 | } else if ((p = carpSelectParent(this))) { |
62e76326 | 707 | code = CARP; |
cb365059 | 708 | } else if ((p = getRoundRobinParent(this))) { |
62e76326 | 709 | code = ROUNDROBIN_PARENT; |
cb365059 | 710 | } else if ((p = getWeightedRoundRobinParent(this))) { |
62e76326 | 711 | code = ROUNDROBIN_PARENT; |
cb365059 | 712 | } else if ((p = getFirstUpParent(this))) { |
62e76326 | 713 | code = FIRSTUP_PARENT; |
cb365059 | 714 | } else if ((p = getDefaultParent(this))) { |
b4ab8666 | 715 | code = DEFAULT_PARENT; |
db1cd23c | 716 | } |
62e76326 | 717 | |
db1cd23c | 718 | if (code != HIER_NONE) { |
cb835136 | 719 | addSelection(p, code); |
062e2281 | 720 | } |
721 | } | |
722 | ||
cb835136 AR |
723 | /// Adds alive parents. Used as a last resort for never_direct. |
724 | void | |
725 | PeerSelector::selectAllParents() | |
168dfda9 | 726 | { |
a3c6762c | 727 | CachePeer *p; |
168dfda9 | 728 | /* Add all alive parents */ |
62e76326 | 729 | |
168dfda9 | 730 | for (p = Config.peers; p; p = p->next) { |
62e76326 | 731 | /* XXX: neighbors.c lacks a public interface for enumerating |
732 | * parents to a request so we have to dig some here.. | |
733 | */ | |
734 | ||
5c51bffb | 735 | if (neighborType(p, request->url) != PEER_PARENT) |
62e76326 | 736 | continue; |
737 | ||
cb365059 | 738 | if (!peerHTTPOkay(p, this)) |
62e76326 | 739 | continue; |
740 | ||
cb835136 | 741 | addSelection(p, ANY_OLD_PARENT); |
168dfda9 | 742 | } |
62e76326 | 743 | |
168dfda9 | 744 | /* XXX: should add dead parents here, but it is currently |
745 | * not possible to find out which parents are dead or which | |
746 | * simply are not configured to handle the request. | |
747 | */ | |
748 | /* Add default parent as a last resort */ | |
cb365059 | 749 | if ((p = getDefaultParent(this))) { |
cb835136 | 750 | addSelection(p, DEFAULT_PARENT); |
168dfda9 | 751 | } |
752 | } | |
753 | ||
cb835136 AR |
754 | void |
755 | PeerSelector::handlePingTimeout() | |
062e2281 | 756 | { |
cb835136 | 757 | debugs(44, 3, url()); |
62e76326 | 758 | |
cb835136 | 759 | if (entry) |
6043e368 AR |
760 | entry->ping_status = PING_DONE; |
761 | ||
cb835136 | 762 | if (selectionAborted()) |
62e76326 | 763 | return; |
62e76326 | 764 | |
5db6bf73 | 765 | ++PeerStats.timeouts; |
cb835136 AR |
766 | ping.timedout = 1; |
767 | selectMore(); | |
768 | } | |
769 | ||
770 | void | |
771 | PeerSelector::HandlePingTimeout(void *data) | |
772 | { | |
773 | static_cast<PeerSelector*>(data)->handlePingTimeout(); | |
85034133 | 774 | } |
775 | ||
776 | void | |
777 | peerSelectInit(void) | |
778 | { | |
75e88d56 | 779 | memset(&PeerStats, '\0', sizeof(PeerStats)); |
062e2281 | 780 | } |
93775f90 | 781 | |
cb835136 AR |
782 | void |
783 | PeerSelector::handleIcpParentMiss(CachePeer *p, icp_common_t *header) | |
b3264694 | 784 | { |
785 | int rtt; | |
62e76326 | 786 | |
9b5c4a9a | 787 | #if USE_ICMP |
b3264694 | 788 | if (Config.onoff.query_icmp) { |
62e76326 | 789 | if (header->flags & ICP_FLAG_SRC_RTT) { |
790 | rtt = header->pad & 0xFFFF; | |
9b5c4a9a | 791 | int hops = (header->pad >> 16) & 0xFFFF; |
62e76326 | 792 | |
793 | if (rtt > 0 && rtt < 0xFFFF) | |
cb835136 | 794 | netdbUpdatePeer(request->url, p, rtt, hops); |
62e76326 | 795 | |
cb835136 AR |
796 | if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) { |
797 | closest_parent_miss = p->in_addr; | |
798 | ping.p_rtt = rtt; | |
62e76326 | 799 | } |
800 | } | |
b3264694 | 801 | } |
9b5c4a9a | 802 | #endif /* USE_ICMP */ |
62e76326 | 803 | |
18ec72b2 | 804 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 805 | if (p->options.closest_only) |
62e76326 | 806 | return; |
807 | ||
85223cd7 | 808 | /* set FIRST_MISS if there is no CLOSEST parent */ |
cb835136 | 809 | if (!closest_parent_miss.isAnyAddr()) |
62e76326 | 810 | return; |
811 | ||
cb835136 | 812 | rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 813 | |
d1b63fc8 | 814 | if (rtt < 1) |
62e76326 | 815 | rtt = 1; |
816 | ||
cb835136 AR |
817 | if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) { |
818 | first_parent_miss = p->in_addr; | |
819 | ping.w_rtt = rtt; | |
b3264694 | 820 | } |
821 | } | |
93775f90 | 822 | |
cb835136 AR |
823 | void |
824 | PeerSelector::handleIcpReply(CachePeer *p, const peer_t type, icp_common_t *header) | |
93775f90 | 825 | { |
e6ccf245 | 826 | icp_opcode op = header->getOpCode(); |
cb835136 | 827 | debugs(44, 3, icp_opcode_str[op] << ' ' << url()); |
69c95dd3 | 828 | #if USE_CACHE_DIGESTS && 0 |
26b164ac | 829 | /* do cd lookup to count false misses */ |
62e76326 | 830 | |
3ab66981 | 831 | if (p && request) |
62e76326 | 832 | peerNoteDigestLookup(request, p, |
cb365059 | 833 | peerDigestLookup(p, this)); |
62e76326 | 834 | |
26b164ac | 835 | #endif |
62e76326 | 836 | |
cb835136 | 837 | ++ping.n_recv; |
62e76326 | 838 | |
27cd7235 | 839 | if (op == ICP_MISS || op == ICP_DECHO) { |
62e76326 | 840 | if (type == PEER_PARENT) |
cb835136 | 841 | handleIcpParentMiss(p, header); |
a7c05555 | 842 | } else if (op == ICP_HIT) { |
cb835136 AR |
843 | hit = p; |
844 | hit_type = type; | |
845 | selectMore(); | |
62e76326 | 846 | return; |
db1cd23c | 847 | } |
62e76326 | 848 | |
cb835136 | 849 | if (ping.n_recv < ping.n_replies_expected) |
62e76326 | 850 | return; |
851 | ||
cb835136 | 852 | selectMore(); |
93775f90 | 853 | } |
86aebcda | 854 | |
855 | #if USE_HTCP | |
cb835136 AR |
856 | void |
857 | PeerSelector::handleHtcpReply(CachePeer *p, const peer_t type, HtcpReplyData *htcp) | |
86aebcda | 858 | { |
cb835136 AR |
859 | debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url()); |
860 | ++ping.n_recv; | |
62e76326 | 861 | |
44e237d0 | 862 | if (htcp->hit) { |
cb835136 AR |
863 | hit = p; |
864 | hit_type = type; | |
865 | selectMore(); | |
62e76326 | 866 | return; |
44e237d0 | 867 | } |
62e76326 | 868 | |
44e237d0 | 869 | if (type == PEER_PARENT) |
cb835136 | 870 | handleHtcpParentMiss(p, htcp); |
62e76326 | 871 | |
cb835136 | 872 | if (ping.n_recv < ping.n_replies_expected) |
62e76326 | 873 | return; |
874 | ||
cb835136 | 875 | selectMore(); |
44e237d0 | 876 | } |
877 | ||
cb835136 AR |
878 | void |
879 | PeerSelector::handleHtcpParentMiss(CachePeer *p, HtcpReplyData *htcp) | |
44e237d0 | 880 | { |
881 | int rtt; | |
62e76326 | 882 | |
9b5c4a9a | 883 | #if USE_ICMP |
44e237d0 | 884 | if (Config.onoff.query_icmp) { |
62e76326 | 885 | if (htcp->cto.rtt > 0) { |
886 | rtt = (int) htcp->cto.rtt * 1000; | |
9b5c4a9a | 887 | int hops = (int) htcp->cto.hops * 1000; |
cb835136 | 888 | netdbUpdatePeer(request->url, p, rtt, hops); |
62e76326 | 889 | |
cb835136 AR |
890 | if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) { |
891 | closest_parent_miss = p->in_addr; | |
892 | ping.p_rtt = rtt; | |
62e76326 | 893 | } |
894 | } | |
44e237d0 | 895 | } |
9b5c4a9a | 896 | #endif /* USE_ICMP */ |
62e76326 | 897 | |
44e237d0 | 898 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 899 | if (p->options.closest_only) |
62e76326 | 900 | return; |
901 | ||
44e237d0 | 902 | /* set FIRST_MISS if there is no CLOSEST parent */ |
cb835136 | 903 | if (!closest_parent_miss.isAnyAddr()) |
62e76326 | 904 | return; |
905 | ||
cb835136 | 906 | rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 907 | |
d1b63fc8 | 908 | if (rtt < 1) |
62e76326 | 909 | rtt = 1; |
910 | ||
cb835136 AR |
911 | if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) { |
912 | first_parent_miss = p->in_addr; | |
913 | ping.w_rtt = rtt; | |
44e237d0 | 914 | } |
86aebcda | 915 | } |
62e76326 | 916 | |
86aebcda | 917 | #endif |
918 | ||
cb835136 AR |
919 | void |
920 | PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data) | |
86aebcda | 921 | { |
0c3d3f65 | 922 | if (proto == AnyP::PROTO_ICP) |
cb835136 | 923 | static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata)); |
62e76326 | 924 | |
86aebcda | 925 | #if USE_HTCP |
62e76326 | 926 | |
0c3d3f65 | 927 | else if (proto == AnyP::PROTO_HTCP) |
cb835136 | 928 | static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata)); |
62e76326 | 929 | |
86aebcda | 930 | #endif |
62e76326 | 931 | |
86aebcda | 932 | else |
cb835136 | 933 | debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto); |
86aebcda | 934 | } |
db1cd23c | 935 | |
cb835136 AR |
936 | void |
937 | PeerSelector::addSelection(CachePeer *peer, const hier_code code) | |
db1cd23c | 938 | { |
e7b08eec | 939 | // Find the end of the servers list. Bail on a duplicate destination. |
cb835136 AR |
940 | auto **serversTail = &servers; |
941 | while (const auto server = *serversTail) { | |
e7b08eec AR |
942 | // There can be at most one PINNED destination. |
943 | // Non-PINNED destinations are uniquely identified by their CachePeer | |
944 | // (even though a DIRECT destination might match a cache_peer address). | |
945 | const bool duplicate = (server->code == PINNED) ? | |
cb835136 | 946 | (code == PINNED) : (server->_peer == peer); |
e7b08eec | 947 | if (duplicate) { |
cb835136 AR |
948 | debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) << |
949 | "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code)); | |
e7b08eec AR |
950 | return; |
951 | } | |
cb835136 | 952 | serversTail = &server->next; |
e7b08eec | 953 | } |
62e76326 | 954 | |
cb835136 AR |
955 | debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code)); |
956 | *serversTail = new FwdServer(peer, code); | |
db1cd23c | 957 | } |
b24880fe | 958 | |
cb835136 | 959 | PeerSelector::PeerSelector(PeerSelectionInitiator *initiator): |
6043e368 | 960 | request(nullptr), |
f53969cc SM |
961 | entry (NULL), |
962 | always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED), | |
963 | never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED), | |
964 | direct(DIRECT_UNKNOWN), | |
f53969cc SM |
965 | lastError(NULL), |
966 | servers (NULL), | |
967 | first_parent_miss(), | |
968 | closest_parent_miss(), | |
969 | hit(NULL), | |
970 | hit_type(PEER_NONE), | |
6043e368 AR |
971 | acl_checklist (NULL), |
972 | initiator_(initiator) | |
b24880fe | 973 | { |
cc192b50 | 974 | ; // no local defaults. |
b24880fe | 975 | } |
976 | ||
851feda6 | 977 | const SBuf |
cb835136 | 978 | PeerSelector::url() const |
769c64cc AJ |
979 | { |
980 | if (entry) | |
851feda6 | 981 | return SBuf(entry->url()); |
769c64cc AJ |
982 | |
983 | if (request) | |
851feda6 | 984 | return request->effectiveRequestUri(); |
769c64cc | 985 | |
851feda6 AJ |
986 | static const SBuf noUrl("[no URL]"); |
987 | return noUrl; | |
769c64cc AJ |
988 | } |
989 | ||
6043e368 AR |
990 | /// \returns valid/interested peer initiator or nil |
991 | PeerSelectionInitiator * | |
cb835136 | 992 | PeerSelector::interestedInitiator() |
6043e368 AR |
993 | { |
994 | const auto initiator = initiator_.valid(); | |
995 | ||
996 | if (!initiator) { | |
997 | debugs(44, 3, id << " initiator gone"); | |
998 | return nullptr; | |
999 | } | |
1000 | ||
1001 | if (!initiator->subscribed) { | |
1002 | debugs(44, 3, id << " initiator lost interest"); | |
1003 | return nullptr; | |
1004 | } | |
1005 | ||
fd9c47d1 | 1006 | debugs(44, 7, id); |
6043e368 AR |
1007 | return initiator; |
1008 | } | |
1009 | ||
1010 | bool | |
cb835136 | 1011 | PeerSelector::wantsMoreDestinations() const { |
6043e368 AR |
1012 | const auto maxCount = Config.forward_max_tries; |
1013 | return maxCount >= 0 && foundPaths < | |
d22511f9 | 1014 | static_cast<std::make_unsigned<decltype(maxCount)>::type>(maxCount); |
6043e368 AR |
1015 | } |
1016 | ||
1017 | void | |
3dde9e52 | 1018 | PeerSelector::handlePath(const Comm::ConnectionPointer &path, FwdServer &fs) |
6043e368 AR |
1019 | { |
1020 | ++foundPaths; | |
1021 | ||
3dde9e52 CT |
1022 | if (path) { |
1023 | path->peerType = fs.code; | |
1024 | path->setPeer(fs._peer.get()); | |
6043e368 | 1025 | |
3dde9e52 CT |
1026 | // check for a configured outgoing address for this destination... |
1027 | getOutgoingAddress(request, path); | |
1028 | debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url()); | |
1029 | } else | |
1030 | debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url()); | |
6043e368 AR |
1031 | |
1032 | request->hier.ping = ping; // may be updated later | |
1033 | ||
6043e368 AR |
1034 | debugs(44, 2, " always_direct = " << always_direct); |
1035 | debugs(44, 2, " never_direct = " << never_direct); | |
1036 | debugs(44, 2, " timedout = " << ping.timedout); | |
1037 | ||
1038 | if (const auto initiator = interestedInitiator()) | |
1039 | initiator->noteDestination(path); | |
1040 | } | |
1041 | ||
cb835136 | 1042 | InstanceIdDefinitions(PeerSelector, "PeerSelector"); |
6043e368 | 1043 | |
b24880fe | 1044 | ping_data::ping_data() : |
f53969cc SM |
1045 | n_sent(0), |
1046 | n_recv(0), | |
1047 | n_replies_expected(0), | |
1048 | timeout(0), | |
1049 | timedout(0), | |
1050 | w_rtt(0), | |
1051 | p_rtt(0) | |
b24880fe | 1052 | { |
1053 | start.tv_sec = 0; | |
1054 | start.tv_usec = 0; | |
1055 | stop.tv_sec = 0; | |
1056 | stop.tv_usec = 0; | |
1057 | } | |
f53969cc | 1058 |