]>
Commit | Line | Data |
---|---|---|
062e2281 | 1 | /* |
262a0e14 | 2 | * $Id$ |
062e2281 | 3 | * |
4 | * DEBUG: section 44 Peer Selection Algorithm | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
2b6662ba | 7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 8 | * ---------------------------------------------------------- |
062e2281 | 9 | * |
2b6662ba | 10 | * Squid is the result of efforts by numerous individuals from |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
062e2281 | 18 | * |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
26ac0430 | 23 | * |
062e2281 | 24 | * This program is distributed in the hope that it will be useful, |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
26ac0430 | 28 | * |
062e2281 | 29 | * You should have received a copy of the GNU General Public License |
30 | * along with this program; if not, write to the Free Software | |
cbdec147 | 31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 32 | * |
062e2281 | 33 | */ |
34 | ||
35 | #include "squid.h" | |
a553a5a3 | 36 | #include "event.h" |
b24880fe | 37 | #include "PeerSelectState.h" |
e6ccf245 | 38 | #include "Store.h" |
39 | #include "ICP.h" | |
528b2c61 | 40 | #include "HttpRequest.h" |
8000a965 | 41 | #include "ACLChecklist.h" |
924f73bc | 42 | #include "htcp.h" |
b6b6f466 | 43 | #include "forward.h" |
cc192b50 | 44 | #include "SquidTime.h" |
9b5c4a9a | 45 | #include "icmp/net_db.h" |
062e2281 | 46 | |
26ac0430 AJ |
47 | const char *hier_strings[] = { |
48 | "NONE", | |
49 | "DIRECT", | |
50 | "SIBLING_HIT", | |
51 | "PARENT_HIT", | |
52 | "DEFAULT_PARENT", | |
53 | "SINGLE_PARENT", | |
54 | "FIRST_UP_PARENT", | |
55 | "FIRST_PARENT_MISS", | |
56 | "CLOSEST_PARENT_MISS", | |
57 | "CLOSEST_PARENT", | |
58 | "CLOSEST_DIRECT", | |
59 | "NO_DIRECT_FAIL", | |
60 | "SOURCE_FASTEST", | |
61 | "ROUNDROBIN_PARENT", | |
6cfa8966 | 62 | #if USE_CACHE_DIGESTS |
26ac0430 AJ |
63 | "CD_PARENT_HIT", |
64 | "CD_SIBLING_HIT", | |
afd88fbe | 65 | #endif |
26ac0430 AJ |
66 | "CARP", |
67 | "ANY_PARENT", | |
68 | "USERHASH", | |
69 | "SOURCEHASH", | |
70 | "INVALID CODE" | |
71 | }; | |
72 | ||
73 | static struct { | |
75e88d56 | 74 | int timeouts; |
2fadd50d | 75 | } PeerStats; |
062e2281 | 76 | |
26ac0430 AJ |
77 | static const char *DirectStr[] = { |
78 | "DIRECT_UNKNOWN", | |
79 | "DIRECT_NO", | |
80 | "DIRECT_MAYBE", | |
81 | "DIRECT_YES" | |
82 | }; | |
75e88d56 | 83 | |
f5b8bbc4 | 84 | static void peerSelectFoo(ps_state *); |
85 | static void peerPingTimeout(void *data); | |
db1cd23c | 86 | static void peerSelectCallback(ps_state * psstate); |
86aebcda | 87 | static IRCB peerHandlePingReply; |
f5b8bbc4 | 88 | static void peerSelectStateFree(ps_state * psstate); |
89 | static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *); | |
44e237d0 | 90 | #if USE_HTCP |
91 | static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *); | |
92 | static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *); | |
93 | #endif | |
f5b8bbc4 | 94 | static int peerCheckNetdbDirect(ps_state * psstate); |
db1cd23c | 95 | static void peerGetSomeNeighbor(ps_state *); |
96 | static void peerGetSomeNeighborReplies(ps_state *); | |
97 | static void peerGetSomeDirect(ps_state *); | |
98 | static void peerGetSomeParent(ps_state *); | |
168dfda9 | 99 | static void peerGetAllParents(ps_state *); |
db1cd23c | 100 | static void peerAddFwdServer(FwdServer **, peer *, hier_code); |
d67acb4e | 101 | static void peerSelectPinned(ps_state * ps); |
348b2031 | 102 | |
aa839030 | 103 | CBDATA_CLASS_INIT(ps_state); |
104 | ||
348b2031 | 105 | static void |
106 | peerSelectStateFree(ps_state * psstate) | |
107 | { | |
108 | if (psstate->acl_checklist) { | |
bf8fe701 | 109 | debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree"); |
62e76326 | 110 | delete (psstate->acl_checklist); |
348b2031 | 111 | } |
62e76326 | 112 | |
6dd9f4bd | 113 | HTTPMSGUNLOCK(psstate->request); |
62e76326 | 114 | |
73a201f8 | 115 | if (psstate->entry) { |
62e76326 | 116 | assert(psstate->entry->ping_status != PING_WAITING); |
97b5e68f | 117 | psstate->entry->unlock(); |
62e76326 | 118 | psstate->entry = NULL; |
73a201f8 | 119 | } |
62e76326 | 120 | |
8407afee | 121 | cbdataFree(psstate); |
348b2031 | 122 | } |
062e2281 | 123 | |
2d72d4fd | 124 | static int |
190154cf | 125 | peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry) |
062e2281 | 126 | { |
7b665aeb | 127 | int n; |
db1cd23c | 128 | assert(entry); |
129 | assert(entry->ping_status == PING_NONE); | |
9bd6a36d | 130 | assert(direct != DIRECT_YES); |
bf8fe701 | 131 | debugs(44, 3, "peerSelectIcpPing: " << entry->url() ); |
62e76326 | 132 | |
92695e5e | 133 | if (!request->flags.hierarchical && direct != DIRECT_NO) |
62e76326 | 134 | return 0; |
135 | ||
d46a87a8 | 136 | if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys) |
62e76326 | 137 | if (direct != DIRECT_NO) |
138 | return 0; | |
139 | ||
7b665aeb | 140 | n = neighborsCount(request); |
62e76326 | 141 | |
bf8fe701 | 142 | debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors"); |
62e76326 | 143 | |
7b665aeb | 144 | return n; |
062e2281 | 145 | } |
146 | ||
147 | ||
062e2281 | 148 | void |
190154cf | 149 | peerSelect(HttpRequest * request, |
62e76326 | 150 | StoreEntry * entry, |
151 | PSC * callback, | |
152 | void *callback_data) | |
75e88d56 | 153 | { |
28c60158 | 154 | ps_state *psstate; |
62e76326 | 155 | |
86b389fc | 156 | if (entry) |
bf8fe701 | 157 | debugs(44, 3, "peerSelect: " << entry->url() ); |
86b389fc | 158 | else |
60745f24 | 159 | debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method)); |
62e76326 | 160 | |
b24880fe | 161 | psstate = new ps_state; |
62e76326 | 162 | |
6dd9f4bd | 163 | psstate->request = HTTPMSGLOCK(request); |
62e76326 | 164 | |
b6c0e933 | 165 | psstate->entry = entry; |
62e76326 | 166 | |
b6c0e933 | 167 | psstate->callback = callback; |
62e76326 | 168 | |
fa80a8ef | 169 | psstate->callback_data = cbdataReference(callback_data); |
62e76326 | 170 | |
db1cd23c | 171 | psstate->direct = DIRECT_UNKNOWN; |
62e76326 | 172 | |
6cfa8966 | 173 | #if USE_CACHE_DIGESTS |
62e76326 | 174 | |
39edba21 | 175 | request->hier.peer_select_start = current_time; |
62e76326 | 176 | |
39edba21 | 177 | #endif |
62e76326 | 178 | |
2395cb21 | 179 | if (psstate->entry) |
cc192b50 | 180 | psstate->entry->lock(); |
62e76326 | 181 | |
b6c0e933 | 182 | peerSelectFoo(psstate); |
75e88d56 | 183 | } |
184 | ||
185 | static void | |
186 | peerCheckNeverDirectDone(int answer, void *data) | |
187 | { | |
e6ccf245 | 188 | ps_state *psstate = (ps_state *) data; |
348b2031 | 189 | psstate->acl_checklist = NULL; |
bf8fe701 | 190 | debugs(44, 3, "peerCheckNeverDirectDone: " << answer); |
b6c0e933 | 191 | psstate->never_direct = answer ? 1 : -1; |
192 | peerSelectFoo(psstate); | |
75e88d56 | 193 | } |
194 | ||
195 | static void | |
196 | peerCheckAlwaysDirectDone(int answer, void *data) | |
197 | { | |
e6ccf245 | 198 | ps_state *psstate = (ps_state *)data; |
348b2031 | 199 | psstate->acl_checklist = NULL; |
bf8fe701 | 200 | debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer); |
7b665aeb | 201 | psstate->always_direct = answer ? 1 : -1; |
b6c0e933 | 202 | peerSelectFoo(psstate); |
75e88d56 | 203 | } |
204 | ||
93775f90 | 205 | static void |
db1cd23c | 206 | peerSelectCallback(ps_state * psstate) |
93775f90 | 207 | { |
b6c0e933 | 208 | StoreEntry *entry = psstate->entry; |
db1cd23c | 209 | FwdServer *fs = psstate->servers; |
fa80a8ef | 210 | PSC *callback; |
211 | void *cbdata; | |
62e76326 | 212 | |
b6c0e933 | 213 | if (entry) { |
bf8fe701 | 214 | debugs(44, 3, "peerSelectCallback: " << entry->url() ); |
62e76326 | 215 | |
216 | if (entry->ping_status == PING_WAITING) | |
217 | eventDelete(peerPingTimeout, psstate); | |
218 | ||
219 | entry->ping_status = PING_DONE; | |
b6c0e933 | 220 | } |
62e76326 | 221 | |
db1cd23c | 222 | if (fs == NULL) { |
bf8fe701 | 223 | debugs(44, 1, "Failed to select source for '" << entry->url() << "'" ); |
224 | debugs(44, 1, " always_direct = " << psstate->always_direct ); | |
225 | debugs(44, 1, " never_direct = " << psstate->never_direct ); | |
226 | debugs(44, 1, " timedout = " << psstate->ping.timedout ); | |
db1cd23c | 227 | } |
62e76326 | 228 | |
44e237d0 | 229 | psstate->ping.stop = current_time; |
0bdf2621 | 230 | psstate->request->hier.ping = psstate->ping; |
fa80a8ef | 231 | callback = psstate->callback; |
232 | psstate->callback = NULL; | |
62e76326 | 233 | |
fa80a8ef | 234 | if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) { |
62e76326 | 235 | psstate->servers = NULL; |
236 | callback(fs, cbdata); | |
db1cd23c | 237 | } |
62e76326 | 238 | |
348b2031 | 239 | peerSelectStateFree(psstate); |
93775f90 | 240 | } |
241 | ||
b3264694 | 242 | static int |
243 | peerCheckNetdbDirect(ps_state * psstate) | |
244 | { | |
9b5c4a9a | 245 | #if USE_ICMP |
5f84d830 | 246 | peer *p; |
b3264694 | 247 | int myrtt; |
248 | int myhops; | |
62e76326 | 249 | |
0886a797 | 250 | if (psstate->direct == DIRECT_NO) |
62e76326 | 251 | return 0; |
252 | ||
9b5c4a9a AJ |
253 | /* base lookup on RTT and Hops if ICMP NetDB is enabled. */ |
254 | ||
cc192b50 | 255 | myrtt = netdbHostRtt(psstate->request->GetHost()); |
62e76326 | 256 | |
bf8fe701 | 257 | debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec"); |
258 | debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec"); | |
62e76326 | 259 | |
5f84d830 | 260 | if (myrtt && myrtt <= Config.minDirectRtt) |
62e76326 | 261 | return 1; |
262 | ||
cc192b50 | 263 | myhops = netdbHostHops(psstate->request->GetHost()); |
62e76326 | 264 | |
bf8fe701 | 265 | debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops); |
266 | debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops); | |
62e76326 | 267 | |
b3264694 | 268 | if (myhops && myhops <= Config.minDirectHops) |
62e76326 | 269 | return 1; |
270 | ||
cc192b50 | 271 | p = whichPeer(psstate->closest_parent_miss); |
62e76326 | 272 | |
5f84d830 | 273 | if (p == NULL) |
62e76326 | 274 | return 0; |
275 | ||
bf8fe701 | 276 | debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec"); |
62e76326 | 277 | |
5f84d830 | 278 | if (myrtt && myrtt <= psstate->ping.p_rtt) |
62e76326 | 279 | return 1; |
280 | ||
9b5c4a9a AJ |
281 | #endif /* USE_ICMP */ |
282 | ||
b3264694 | 283 | return 0; |
284 | } | |
285 | ||
93775f90 | 286 | static void |
511f47bb | 287 | peerSelectFoo(ps_state * ps) |
062e2281 | 288 | { |
511f47bb | 289 | StoreEntry *entry = ps->entry; |
190154cf | 290 | HttpRequest *request = ps->request; |
60745f24 | 291 | debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'"); |
62e76326 | 292 | |
511f47bb | 293 | if (ps->direct == DIRECT_UNKNOWN) { |
62e76326 | 294 | if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) { |
295 | ps->acl_checklist = aclChecklistCreate( | |
296 | Config.accessList.AlwaysDirect, | |
297 | request, | |
298 | NULL); /* ident */ | |
299 | ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, | |
300 | ps); | |
301 | return; | |
302 | } else if (ps->always_direct > 0) { | |
303 | ps->direct = DIRECT_YES; | |
304 | } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) { | |
305 | ps->acl_checklist = aclChecklistCreate( | |
306 | Config.accessList.NeverDirect, | |
307 | request, | |
308 | NULL); /* ident */ | |
309 | ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, | |
310 | ps); | |
311 | return; | |
312 | } else if (ps->never_direct > 0) { | |
313 | ps->direct = DIRECT_NO; | |
314 | } else if (request->flags.accelerated) { | |
315 | ps->direct = DIRECT_NO; | |
316 | } else if (request->flags.loopdetect) { | |
317 | ps->direct = DIRECT_YES; | |
318 | } else if (peerCheckNetdbDirect(ps)) { | |
319 | ps->direct = DIRECT_YES; | |
320 | } else { | |
321 | ps->direct = DIRECT_MAYBE; | |
322 | } | |
323 | ||
bf8fe701 | 324 | debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]); |
db1cd23c | 325 | } |
62e76326 | 326 | |
d67acb4e AJ |
327 | if (!entry || entry->ping_status == PING_NONE) |
328 | peerSelectPinned(ps); | |
df503d6c | 329 | if (entry == NULL) { |
62e76326 | 330 | (void) 0; |
df503d6c | 331 | } else if (entry->ping_status == PING_NONE) { |
62e76326 | 332 | peerGetSomeNeighbor(ps); |
333 | ||
334 | if (entry->ping_status == PING_WAITING) | |
335 | return; | |
db1cd23c | 336 | } else if (entry->ping_status == PING_WAITING) { |
62e76326 | 337 | peerGetSomeNeighborReplies(ps); |
338 | entry->ping_status = PING_DONE; | |
db1cd23c | 339 | } |
62e76326 | 340 | |
168dfda9 | 341 | switch (ps->direct) { |
62e76326 | 342 | |
168dfda9 | 343 | case DIRECT_YES: |
62e76326 | 344 | peerGetSomeDirect(ps); |
345 | break; | |
346 | ||
168dfda9 | 347 | case DIRECT_NO: |
62e76326 | 348 | peerGetSomeParent(ps); |
349 | peerGetAllParents(ps); | |
350 | break; | |
351 | ||
168dfda9 | 352 | default: |
62e76326 | 353 | |
354 | if (Config.onoff.prefer_direct) | |
355 | peerGetSomeDirect(ps); | |
356 | ||
357 | if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) | |
358 | peerGetSomeParent(ps); | |
359 | ||
360 | if (!Config.onoff.prefer_direct) | |
361 | peerGetSomeDirect(ps); | |
362 | ||
363 | break; | |
168dfda9 | 364 | } |
62e76326 | 365 | |
511f47bb | 366 | peerSelectCallback(ps); |
db1cd23c | 367 | } |
368 | ||
d67acb4e AJ |
369 | /* |
370 | * peerSelectPinned | |
371 | * | |
372 | * Selects a pinned connection | |
373 | */ | |
374 | int peerAllowedToUse(const peer * p, HttpRequest * request); | |
375 | static void | |
376 | peerSelectPinned(ps_state * ps) | |
377 | { | |
378 | HttpRequest *request = ps->request; | |
379 | peer *peer; | |
380 | if (!request->pinnedConnection()) | |
381 | return; | |
382 | if (request->pinnedConnection()->validatePinnedConnection(request) != -1) { | |
26ac0430 | 383 | peer = request->pinnedConnection()->pinnedPeer(); |
d67acb4e AJ |
384 | if (peer && peerAllowedToUse(peer, request)) { |
385 | peerAddFwdServer(&ps->servers, peer, PINNED); | |
386 | if (ps->entry) | |
387 | ps->entry->ping_status = PING_DONE; /* Skip ICP */ | |
388 | } else if (!peer && ps->direct != DIRECT_NO) { | |
389 | peerAddFwdServer(&ps->servers, NULL, PINNED); | |
390 | if (ps->entry) | |
391 | ps->entry->ping_status = PING_DONE; /* Skip ICP */ | |
392 | } | |
393 | } | |
394 | } | |
395 | ||
db1cd23c | 396 | /* |
397 | * peerGetSomeNeighbor | |
26ac0430 | 398 | * |
db1cd23c | 399 | * Selects a neighbor (parent or sibling) based on one of the |
400 | * following methods: | |
401 | * Cache Digests | |
402 | * CARP | |
9b5c4a9a | 403 | * ICMP Netdb RTT estimates |
db1cd23c | 404 | * ICP/HTCP queries |
405 | */ | |
406 | static void | |
407 | peerGetSomeNeighbor(ps_state * ps) | |
408 | { | |
409 | StoreEntry *entry = ps->entry; | |
190154cf | 410 | HttpRequest *request = ps->request; |
db1cd23c | 411 | peer *p; |
412 | hier_code code = HIER_NONE; | |
413 | assert(entry->ping_status == PING_NONE); | |
62e76326 | 414 | |
db1cd23c | 415 | if (ps->direct == DIRECT_YES) { |
62e76326 | 416 | entry->ping_status = PING_DONE; |
417 | return; | |
124511e5 | 418 | } |
62e76326 | 419 | |
6cfa8966 | 420 | #if USE_CACHE_DIGESTS |
f66a9ef4 | 421 | if ((p = neighborsDigestSelect(request))) { |
62e76326 | 422 | if (neighborType(p, request) == PEER_PARENT) |
423 | code = CD_PARENT_HIT; | |
424 | else | |
425 | code = CD_SIBLING_HIT; | |
db1cd23c | 426 | } else |
c127134a | 427 | #endif |
62e76326 | 428 | if ((p = netdbClosestParent(request))) { |
429 | code = CLOSEST_PARENT; | |
430 | } else if (peerSelectIcpPing(request, ps->direct, entry)) { | |
bf8fe701 | 431 | debugs(44, 3, "peerSelect: Doing ICP pings"); |
62e76326 | 432 | ps->ping.start = current_time; |
433 | ps->ping.n_sent = neighborsUdpPing(request, | |
434 | entry, | |
435 | peerHandlePingReply, | |
436 | ps, | |
437 | &ps->ping.n_replies_expected, | |
438 | &ps->ping.timeout); | |
439 | ||
440 | if (ps->ping.n_sent == 0) | |
bf8fe701 | 441 | debugs(44, 0, "WARNING: neighborsUdpPing returned 0"); |
26ac0430 AJ |
442 | debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected << |
443 | " ICP replies expected, RTT " << ps->ping.timeout << | |
444 | " msec"); | |
62e76326 | 445 | |
62e76326 | 446 | |
447 | if (ps->ping.n_replies_expected > 0) { | |
448 | entry->ping_status = PING_WAITING; | |
449 | eventAdd("peerPingTimeout", | |
450 | peerPingTimeout, | |
451 | ps, | |
452 | 0.001 * ps->ping.timeout, | |
453 | 0); | |
454 | return; | |
455 | } | |
456 | } | |
457 | ||
db1cd23c | 458 | if (code != HIER_NONE) { |
62e76326 | 459 | assert(p); |
bf8fe701 | 460 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host); |
62e76326 | 461 | peerAddFwdServer(&ps->servers, p, code); |
db1cd23c | 462 | } |
62e76326 | 463 | |
db1cd23c | 464 | entry->ping_status = PING_DONE; |
465 | } | |
466 | ||
467 | /* | |
468 | * peerGetSomeNeighborReplies | |
26ac0430 | 469 | * |
db1cd23c | 470 | * Selects a neighbor (parent or sibling) based on ICP/HTCP replies. |
471 | */ | |
472 | static void | |
473 | peerGetSomeNeighborReplies(ps_state * ps) | |
474 | { | |
190154cf | 475 | HttpRequest *request = ps->request; |
db1cd23c | 476 | peer *p = NULL; |
477 | hier_code code = HIER_NONE; | |
4c5a1592 | 478 | assert(ps->entry->ping_status == PING_WAITING); |
db1cd23c | 479 | assert(ps->direct != DIRECT_YES); |
62e76326 | 480 | |
db1cd23c | 481 | if (peerCheckNetdbDirect(ps)) { |
62e76326 | 482 | code = CLOSEST_DIRECT; |
cc192b50 | 483 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->GetHost()); |
62e76326 | 484 | peerAddFwdServer(&ps->servers, NULL, code); |
485 | return; | |
db1cd23c | 486 | } |
62e76326 | 487 | |
db1cd23c | 488 | if ((p = ps->hit)) { |
62e76326 | 489 | code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT; |
26ac0430 | 490 | } else { |
cc192b50 | 491 | if (!ps->closest_parent_miss.IsAnyAddr()) { |
492 | p = whichPeer(ps->closest_parent_miss); | |
493 | code = CLOSEST_PARENT_MISS; | |
494 | } else if (!ps->first_parent_miss.IsAnyAddr()) { | |
495 | p = whichPeer(ps->first_parent_miss); | |
496 | code = FIRST_PARENT_MISS; | |
497 | } | |
498 | } | |
db1cd23c | 499 | if (p && code != HIER_NONE) { |
bf8fe701 | 500 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host); |
62e76326 | 501 | peerAddFwdServer(&ps->servers, p, code); |
db1cd23c | 502 | } |
503 | } | |
504 | ||
505 | ||
506 | /* | |
507 | * peerGetSomeDirect | |
26ac0430 | 508 | * |
db1cd23c | 509 | * Simply adds a 'direct' entry to the FwdServers list if this |
510 | * request can be forwarded directly to the origin server | |
511 | */ | |
512 | static void | |
513 | peerGetSomeDirect(ps_state * ps) | |
514 | { | |
515 | if (ps->direct == DIRECT_NO) | |
62e76326 | 516 | return; |
517 | ||
db80e881 | 518 | /* WAIS is not implemented natively */ |
db1cd23c | 519 | if (ps->request->protocol == PROTO_WAIS) |
26ac0430 | 520 | return; |
db80e881 | 521 | |
522 | peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT); | |
db1cd23c | 523 | } |
524 | ||
525 | static void | |
526 | peerGetSomeParent(ps_state * ps) | |
527 | { | |
528 | peer *p; | |
190154cf | 529 | HttpRequest *request = ps->request; |
db1cd23c | 530 | hier_code code = HIER_NONE; |
60745f24 | 531 | debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr(request->method) << " " << request->GetHost()); |
62e76326 | 532 | |
6b8e7481 | 533 | if (ps->direct == DIRECT_YES) |
62e76326 | 534 | return; |
535 | ||
db1cd23c | 536 | if ((p = getDefaultParent(request))) { |
62e76326 | 537 | code = DEFAULT_PARENT; |
f7e1d9ce HN |
538 | } else if ((p = peerUserHashSelectParent(request))) { |
539 | code = USERHASH_PARENT; | |
540 | } else if ((p = peerSourceHashSelectParent(request))) { | |
541 | code = SOURCEHASH_PARENT; | |
b3995439 | 542 | } else if ((p = carpSelectParent(request))) { |
62e76326 | 543 | code = CARP; |
db1cd23c | 544 | } else if ((p = getRoundRobinParent(request))) { |
62e76326 | 545 | code = ROUNDROBIN_PARENT; |
d1b63fc8 | 546 | } else if ((p = getWeightedRoundRobinParent(request))) { |
62e76326 | 547 | code = ROUNDROBIN_PARENT; |
db1cd23c | 548 | } else if ((p = getFirstUpParent(request))) { |
62e76326 | 549 | code = FIRSTUP_PARENT; |
db1cd23c | 550 | } else if ((p = getAnyParent(request))) { |
62e76326 | 551 | code = ANY_OLD_PARENT; |
db1cd23c | 552 | } |
62e76326 | 553 | |
db1cd23c | 554 | if (code != HIER_NONE) { |
bf8fe701 | 555 | debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host); |
62e76326 | 556 | peerAddFwdServer(&ps->servers, p, code); |
062e2281 | 557 | } |
558 | } | |
559 | ||
168dfda9 | 560 | /* Adds alive parents. Used as a last resort for never_direct. |
561 | */ | |
562 | static void | |
563 | peerGetAllParents(ps_state * ps) | |
564 | { | |
565 | peer *p; | |
190154cf | 566 | HttpRequest *request = ps->request; |
168dfda9 | 567 | /* Add all alive parents */ |
62e76326 | 568 | |
168dfda9 | 569 | for (p = Config.peers; p; p = p->next) { |
62e76326 | 570 | /* XXX: neighbors.c lacks a public interface for enumerating |
571 | * parents to a request so we have to dig some here.. | |
572 | */ | |
573 | ||
574 | if (neighborType(p, request) != PEER_PARENT) | |
575 | continue; | |
576 | ||
577 | if (!peerHTTPOkay(p, request)) | |
578 | continue; | |
579 | ||
bf8fe701 | 580 | debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host); |
62e76326 | 581 | |
582 | peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT); | |
168dfda9 | 583 | } |
62e76326 | 584 | |
168dfda9 | 585 | /* XXX: should add dead parents here, but it is currently |
586 | * not possible to find out which parents are dead or which | |
587 | * simply are not configured to handle the request. | |
588 | */ | |
589 | /* Add default parent as a last resort */ | |
590 | if ((p = getDefaultParent(request))) { | |
62e76326 | 591 | peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT); |
168dfda9 | 592 | } |
593 | } | |
594 | ||
d9586c3c | 595 | static void |
93775f90 | 596 | peerPingTimeout(void *data) |
062e2281 | 597 | { |
e6ccf245 | 598 | ps_state *psstate = (ps_state *)data; |
b6c0e933 | 599 | StoreEntry *entry = psstate->entry; |
62e76326 | 600 | |
fefb0227 | 601 | if (entry) |
bf8fe701 | 602 | debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" ); |
62e76326 | 603 | |
fa80a8ef | 604 | if (!cbdataReferenceValid(psstate->callback_data)) { |
62e76326 | 605 | /* request aborted */ |
606 | entry->ping_status = PING_DONE; | |
607 | cbdataReferenceDone(psstate->callback_data); | |
608 | peerSelectStateFree(psstate); | |
609 | return; | |
73a201f8 | 610 | } |
62e76326 | 611 | |
75e88d56 | 612 | PeerStats.timeouts++; |
44e237d0 | 613 | psstate->ping.timedout = 1; |
b6c0e933 | 614 | peerSelectFoo(psstate); |
85034133 | 615 | } |
616 | ||
617 | void | |
618 | peerSelectInit(void) | |
619 | { | |
75e88d56 | 620 | memset(&PeerStats, '\0', sizeof(PeerStats)); |
9bdbfe33 | 621 | assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *)); |
062e2281 | 622 | } |
93775f90 | 623 | |
b3264694 | 624 | static void |
625 | peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps) | |
626 | { | |
627 | int rtt; | |
62e76326 | 628 | |
9b5c4a9a | 629 | #if USE_ICMP |
b3264694 | 630 | if (Config.onoff.query_icmp) { |
62e76326 | 631 | if (header->flags & ICP_FLAG_SRC_RTT) { |
632 | rtt = header->pad & 0xFFFF; | |
9b5c4a9a | 633 | int hops = (header->pad >> 16) & 0xFFFF; |
62e76326 | 634 | |
635 | if (rtt > 0 && rtt < 0xFFFF) | |
636 | netdbUpdatePeer(ps->request, p, rtt, hops); | |
637 | ||
638 | if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) { | |
639 | ps->closest_parent_miss = p->in_addr; | |
640 | ps->ping.p_rtt = rtt; | |
641 | } | |
642 | } | |
b3264694 | 643 | } |
9b5c4a9a | 644 | #endif /* USE_ICMP */ |
62e76326 | 645 | |
18ec72b2 | 646 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 647 | if (p->options.closest_only) |
62e76326 | 648 | return; |
649 | ||
85223cd7 | 650 | /* set FIRST_MISS if there is no CLOSEST parent */ |
cc192b50 | 651 | if (!ps->closest_parent_miss.IsAnyAddr()) |
62e76326 | 652 | return; |
653 | ||
d1b63fc8 | 654 | rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 655 | |
d1b63fc8 | 656 | if (rtt < 1) |
62e76326 | 657 | rtt = 1; |
658 | ||
cc192b50 | 659 | if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) { |
62e76326 | 660 | ps->first_parent_miss = p->in_addr; |
661 | ps->ping.w_rtt = rtt; | |
b3264694 | 662 | } |
663 | } | |
93775f90 | 664 | |
b6c0e933 | 665 | static void |
b3264694 | 666 | peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data) |
93775f90 | 667 | { |
e6ccf245 | 668 | ps_state *psstate = (ps_state *)data; |
669 | icp_opcode op = header->getOpCode(); | |
bf8fe701 | 670 | debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() ); |
69c95dd3 | 671 | #if USE_CACHE_DIGESTS && 0 |
26b164ac | 672 | /* do cd lookup to count false misses */ |
62e76326 | 673 | |
3ab66981 | 674 | if (p && request) |
62e76326 | 675 | peerNoteDigestLookup(request, p, |
676 | peerDigestLookup(p, request, psstate->entry)); | |
677 | ||
26b164ac | 678 | #endif |
62e76326 | 679 | |
44e237d0 | 680 | psstate->ping.n_recv++; |
62e76326 | 681 | |
27cd7235 | 682 | if (op == ICP_MISS || op == ICP_DECHO) { |
62e76326 | 683 | if (type == PEER_PARENT) |
684 | peerIcpParentMiss(p, header, psstate); | |
a7c05555 | 685 | } else if (op == ICP_HIT) { |
62e76326 | 686 | psstate->hit = p; |
687 | psstate->hit_type = type; | |
688 | peerSelectFoo(psstate); | |
689 | return; | |
db1cd23c | 690 | } |
62e76326 | 691 | |
44e237d0 | 692 | if (psstate->ping.n_recv < psstate->ping.n_replies_expected) |
62e76326 | 693 | return; |
694 | ||
b6c0e933 | 695 | peerSelectFoo(psstate); |
93775f90 | 696 | } |
86aebcda | 697 | |
698 | #if USE_HTCP | |
699 | static void | |
44e237d0 | 700 | peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data) |
86aebcda | 701 | { |
e6ccf245 | 702 | ps_state *psstate = (ps_state *)data; |
26ac0430 AJ |
703 | debugs(44, 3, "peerHandleHtcpReply: " << |
704 | (htcp->hit ? "HIT" : "MISS") << " " << | |
705 | psstate->entry->url() ); | |
44e237d0 | 706 | psstate->ping.n_recv++; |
62e76326 | 707 | |
44e237d0 | 708 | if (htcp->hit) { |
62e76326 | 709 | psstate->hit = p; |
710 | psstate->hit_type = type; | |
711 | peerSelectFoo(psstate); | |
712 | return; | |
44e237d0 | 713 | } |
62e76326 | 714 | |
44e237d0 | 715 | if (type == PEER_PARENT) |
62e76326 | 716 | peerHtcpParentMiss(p, htcp, psstate); |
717 | ||
44e237d0 | 718 | if (psstate->ping.n_recv < psstate->ping.n_replies_expected) |
62e76326 | 719 | return; |
720 | ||
44e237d0 | 721 | peerSelectFoo(psstate); |
722 | } | |
723 | ||
724 | static void | |
725 | peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps) | |
726 | { | |
727 | int rtt; | |
62e76326 | 728 | |
9b5c4a9a | 729 | #if USE_ICMP |
44e237d0 | 730 | if (Config.onoff.query_icmp) { |
62e76326 | 731 | if (htcp->cto.rtt > 0) { |
732 | rtt = (int) htcp->cto.rtt * 1000; | |
9b5c4a9a | 733 | int hops = (int) htcp->cto.hops * 1000; |
62e76326 | 734 | netdbUpdatePeer(ps->request, p, rtt, hops); |
735 | ||
736 | if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) { | |
737 | ps->closest_parent_miss = p->in_addr; | |
738 | ps->ping.p_rtt = rtt; | |
739 | } | |
740 | } | |
44e237d0 | 741 | } |
9b5c4a9a | 742 | #endif /* USE_ICMP */ |
62e76326 | 743 | |
44e237d0 | 744 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 745 | if (p->options.closest_only) |
62e76326 | 746 | return; |
747 | ||
44e237d0 | 748 | /* set FIRST_MISS if there is no CLOSEST parent */ |
cc192b50 | 749 | if (!ps->closest_parent_miss.IsAnyAddr()) |
62e76326 | 750 | return; |
751 | ||
d1b63fc8 | 752 | rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 753 | |
d1b63fc8 | 754 | if (rtt < 1) |
62e76326 | 755 | rtt = 1; |
756 | ||
cc192b50 | 757 | if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) { |
62e76326 | 758 | ps->first_parent_miss = p->in_addr; |
759 | ps->ping.w_rtt = rtt; | |
44e237d0 | 760 | } |
86aebcda | 761 | } |
62e76326 | 762 | |
86aebcda | 763 | #endif |
764 | ||
765 | static void | |
9ef28b60 | 766 | peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data) |
86aebcda | 767 | { |
768 | if (proto == PROTO_ICP) | |
62e76326 | 769 | peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data); |
770 | ||
86aebcda | 771 | #if USE_HTCP |
62e76326 | 772 | |
86aebcda | 773 | else if (proto == PROTO_HTCP) |
62e76326 | 774 | peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data); |
775 | ||
86aebcda | 776 | #endif |
62e76326 | 777 | |
86aebcda | 778 | else |
4a7a3d56 | 779 | debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto); |
86aebcda | 780 | } |
db1cd23c | 781 | |
782 | static void | |
b73f343e | 783 | peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code) |
db1cd23c | 784 | { |
e6ccf245 | 785 | FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER); |
26ac0430 AJ |
786 | debugs(44, 5, "peerAddFwdServer: adding " << |
787 | (p ? p->host : "DIRECT") << " " << | |
788 | hier_strings[code] ); | |
29b8d8d6 | 789 | fs->_peer = cbdataReference(p); |
db1cd23c | 790 | fs->code = code; |
62e76326 | 791 | |
b73f343e | 792 | while (*FSVR) |
793 | FSVR = &(*FSVR)->next; | |
62e76326 | 794 | |
b73f343e | 795 | *FSVR = fs; |
db1cd23c | 796 | } |
b24880fe | 797 | |
798 | void * | |
799 | ps_state::operator new(size_t) | |
800 | { | |
aa839030 | 801 | CBDATA_INIT_TYPE(ps_state); |
b24880fe | 802 | return cbdataAlloc(ps_state); |
803 | } | |
804 | ||
805 | ps_state::ps_state() : request (NULL), | |
806 | entry (NULL), | |
807 | always_direct (0), | |
808 | never_direct (0), | |
809 | direct (0), | |
810 | callback (NULL), | |
811 | callback_data (NULL), | |
812 | servers (NULL), | |
cc192b50 | 813 | first_parent_miss(), |
814 | closest_parent_miss(), | |
b24880fe | 815 | hit(NULL), |
816 | hit_type(PEER_NONE), | |
b24880fe | 817 | acl_checklist (NULL) |
818 | { | |
cc192b50 | 819 | ; // no local defaults. |
b24880fe | 820 | } |
821 | ||
822 | ping_data::ping_data() : | |
823 | n_sent(0), | |
824 | n_recv(0), | |
825 | n_replies_expected(0), | |
826 | timeout(0), | |
827 | timedout(0), | |
828 | w_rtt(0), | |
829 | p_rtt(0) | |
830 | { | |
831 | start.tv_sec = 0; | |
832 | start.tv_usec = 0; | |
833 | stop.tv_sec = 0; | |
834 | stop.tv_usec = 0; | |
835 | } |