]> git.ipfire.org Git - thirdparty/squid.git/blob - src/peer_select.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / peer_select.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 44 Peer Selection Algorithm
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
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.
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.
23 *
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.
28 *
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
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35 #include "squid.h"
36 #include "event.h"
37 #include "PeerSelectState.h"
38 #include "Store.h"
39 #include "ICP.h"
40 #include "HttpRequest.h"
41 #include "ACLChecklist.h"
42 #include "htcp.h"
43 #include "forward.h"
44 #include "SquidTime.h"
45 #include "icmp/net_db.h"
46
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",
62 #if USE_CACHE_DIGESTS
63 "CD_PARENT_HIT",
64 "CD_SIBLING_HIT",
65 #endif
66 "CARP",
67 "ANY_PARENT",
68 "USERHASH",
69 "SOURCEHASH",
70 "INVALID CODE"
71 };
72
73 static struct {
74 int timeouts;
75 } PeerStats;
76
77 static const char *DirectStr[] = {
78 "DIRECT_UNKNOWN",
79 "DIRECT_NO",
80 "DIRECT_MAYBE",
81 "DIRECT_YES"
82 };
83
84 static void peerSelectFoo(ps_state *);
85 static void peerPingTimeout(void *data);
86 static void peerSelectCallback(ps_state * psstate);
87 static IRCB peerHandlePingReply;
88 static void peerSelectStateFree(ps_state * psstate);
89 static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
90 #if USE_HTCP
91 static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
92 static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
93 #endif
94 static int peerCheckNetdbDirect(ps_state * psstate);
95 static void peerGetSomeNeighbor(ps_state *);
96 static void peerGetSomeNeighborReplies(ps_state *);
97 static void peerGetSomeDirect(ps_state *);
98 static void peerGetSomeParent(ps_state *);
99 static void peerGetAllParents(ps_state *);
100 static void peerAddFwdServer(FwdServer **, peer *, hier_code);
101 static void peerSelectPinned(ps_state * ps);
102
103 CBDATA_CLASS_INIT(ps_state);
104
105 static void
106 peerSelectStateFree(ps_state * psstate)
107 {
108 if (psstate->acl_checklist) {
109 debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree");
110 delete (psstate->acl_checklist);
111 }
112
113 HTTPMSGUNLOCK(psstate->request);
114
115 if (psstate->entry) {
116 assert(psstate->entry->ping_status != PING_WAITING);
117 psstate->entry->unlock();
118 psstate->entry = NULL;
119 }
120
121 cbdataFree(psstate);
122 }
123
124 static int
125 peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
126 {
127 int n;
128 assert(entry);
129 assert(entry->ping_status == PING_NONE);
130 assert(direct != DIRECT_YES);
131 debugs(44, 3, "peerSelectIcpPing: " << entry->url() );
132
133 if (!request->flags.hierarchical && direct != DIRECT_NO)
134 return 0;
135
136 if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
137 if (direct != DIRECT_NO)
138 return 0;
139
140 n = neighborsCount(request);
141
142 debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
143
144 return n;
145 }
146
147
148 void
149 peerSelect(HttpRequest * request,
150 StoreEntry * entry,
151 PSC * callback,
152 void *callback_data)
153 {
154 ps_state *psstate;
155
156 if (entry)
157 debugs(44, 3, "peerSelect: " << entry->url() );
158 else
159 debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method));
160
161 psstate = new ps_state;
162
163 psstate->request = HTTPMSGLOCK(request);
164
165 psstate->entry = entry;
166
167 psstate->callback = callback;
168
169 psstate->callback_data = cbdataReference(callback_data);
170
171 psstate->direct = DIRECT_UNKNOWN;
172
173 #if USE_CACHE_DIGESTS
174
175 request->hier.peer_select_start = current_time;
176
177 #endif
178
179 if (psstate->entry)
180 psstate->entry->lock();
181
182 peerSelectFoo(psstate);
183 }
184
185 static void
186 peerCheckNeverDirectDone(int answer, void *data)
187 {
188 ps_state *psstate = (ps_state *) data;
189 psstate->acl_checklist = NULL;
190 debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
191 psstate->never_direct = answer ? 1 : -1;
192 peerSelectFoo(psstate);
193 }
194
195 static void
196 peerCheckAlwaysDirectDone(int answer, void *data)
197 {
198 ps_state *psstate = (ps_state *)data;
199 psstate->acl_checklist = NULL;
200 debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
201 psstate->always_direct = answer ? 1 : -1;
202 peerSelectFoo(psstate);
203 }
204
205 static void
206 peerSelectCallback(ps_state * psstate)
207 {
208 StoreEntry *entry = psstate->entry;
209 FwdServer *fs = psstate->servers;
210 PSC *callback;
211 void *cbdata;
212
213 if (entry) {
214 debugs(44, 3, "peerSelectCallback: " << entry->url() );
215
216 if (entry->ping_status == PING_WAITING)
217 eventDelete(peerPingTimeout, psstate);
218
219 entry->ping_status = PING_DONE;
220 }
221
222 if (fs == NULL) {
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 );
227 }
228
229 psstate->ping.stop = current_time;
230 psstate->request->hier.ping = psstate->ping;
231 callback = psstate->callback;
232 psstate->callback = NULL;
233
234 if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
235 psstate->servers = NULL;
236 callback(fs, cbdata);
237 }
238
239 peerSelectStateFree(psstate);
240 }
241
242 static int
243 peerCheckNetdbDirect(ps_state * psstate)
244 {
245 #if USE_ICMP
246 peer *p;
247 int myrtt;
248 int myhops;
249
250 if (psstate->direct == DIRECT_NO)
251 return 0;
252
253 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
254
255 myrtt = netdbHostRtt(psstate->request->GetHost());
256
257 debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
258 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
259
260 if (myrtt && myrtt <= Config.minDirectRtt)
261 return 1;
262
263 myhops = netdbHostHops(psstate->request->GetHost());
264
265 debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
266 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
267
268 if (myhops && myhops <= Config.minDirectHops)
269 return 1;
270
271 p = whichPeer(psstate->closest_parent_miss);
272
273 if (p == NULL)
274 return 0;
275
276 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
277
278 if (myrtt && myrtt <= psstate->ping.p_rtt)
279 return 1;
280
281 #endif /* USE_ICMP */
282
283 return 0;
284 }
285
286 static void
287 peerSelectFoo(ps_state * ps)
288 {
289 StoreEntry *entry = ps->entry;
290 HttpRequest *request = ps->request;
291 debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
292
293 if (ps->direct == DIRECT_UNKNOWN) {
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
324 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
325 }
326
327 if (!entry || entry->ping_status == PING_NONE)
328 peerSelectPinned(ps);
329 if (entry == NULL) {
330 (void) 0;
331 } else if (entry->ping_status == PING_NONE) {
332 peerGetSomeNeighbor(ps);
333
334 if (entry->ping_status == PING_WAITING)
335 return;
336 } else if (entry->ping_status == PING_WAITING) {
337 peerGetSomeNeighborReplies(ps);
338 entry->ping_status = PING_DONE;
339 }
340
341 switch (ps->direct) {
342
343 case DIRECT_YES:
344 peerGetSomeDirect(ps);
345 break;
346
347 case DIRECT_NO:
348 peerGetSomeParent(ps);
349 peerGetAllParents(ps);
350 break;
351
352 default:
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;
364 }
365
366 peerSelectCallback(ps);
367 }
368
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) {
383 peer = request->pinnedConnection()->pinnedPeer();
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
396 /*
397 * peerGetSomeNeighbor
398 *
399 * Selects a neighbor (parent or sibling) based on one of the
400 * following methods:
401 * Cache Digests
402 * CARP
403 * ICMP Netdb RTT estimates
404 * ICP/HTCP queries
405 */
406 static void
407 peerGetSomeNeighbor(ps_state * ps)
408 {
409 StoreEntry *entry = ps->entry;
410 HttpRequest *request = ps->request;
411 peer *p;
412 hier_code code = HIER_NONE;
413 assert(entry->ping_status == PING_NONE);
414
415 if (ps->direct == DIRECT_YES) {
416 entry->ping_status = PING_DONE;
417 return;
418 }
419
420 #if USE_CACHE_DIGESTS
421 if ((p = neighborsDigestSelect(request))) {
422 if (neighborType(p, request) == PEER_PARENT)
423 code = CD_PARENT_HIT;
424 else
425 code = CD_SIBLING_HIT;
426 } else
427 #endif
428 if ((p = netdbClosestParent(request))) {
429 code = CLOSEST_PARENT;
430 } else if (peerSelectIcpPing(request, ps->direct, entry)) {
431 debugs(44, 3, "peerSelect: Doing ICP pings");
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)
441 debugs(44, 0, "WARNING: neighborsUdpPing returned 0");
442 debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
443 " ICP replies expected, RTT " << ps->ping.timeout <<
444 " msec");
445
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
458 if (code != HIER_NONE) {
459 assert(p);
460 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
461 peerAddFwdServer(&ps->servers, p, code);
462 }
463
464 entry->ping_status = PING_DONE;
465 }
466
467 /*
468 * peerGetSomeNeighborReplies
469 *
470 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
471 */
472 static void
473 peerGetSomeNeighborReplies(ps_state * ps)
474 {
475 HttpRequest *request = ps->request;
476 peer *p = NULL;
477 hier_code code = HIER_NONE;
478 assert(ps->entry->ping_status == PING_WAITING);
479 assert(ps->direct != DIRECT_YES);
480
481 if (peerCheckNetdbDirect(ps)) {
482 code = CLOSEST_DIRECT;
483 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->GetHost());
484 peerAddFwdServer(&ps->servers, NULL, code);
485 return;
486 }
487
488 if ((p = ps->hit)) {
489 code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
490 } else {
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 }
499 if (p && code != HIER_NONE) {
500 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
501 peerAddFwdServer(&ps->servers, p, code);
502 }
503 }
504
505
506 /*
507 * peerGetSomeDirect
508 *
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)
516 return;
517
518 /* WAIS is not implemented natively */
519 if (ps->request->protocol == PROTO_WAIS)
520 return;
521
522 peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
523 }
524
525 static void
526 peerGetSomeParent(ps_state * ps)
527 {
528 peer *p;
529 HttpRequest *request = ps->request;
530 hier_code code = HIER_NONE;
531 debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr(request->method) << " " << request->GetHost());
532
533 if (ps->direct == DIRECT_YES)
534 return;
535
536 if ((p = getDefaultParent(request))) {
537 code = DEFAULT_PARENT;
538 } else if ((p = peerUserHashSelectParent(request))) {
539 code = USERHASH_PARENT;
540 } else if ((p = peerSourceHashSelectParent(request))) {
541 code = SOURCEHASH_PARENT;
542 } else if ((p = carpSelectParent(request))) {
543 code = CARP;
544 } else if ((p = getRoundRobinParent(request))) {
545 code = ROUNDROBIN_PARENT;
546 } else if ((p = getWeightedRoundRobinParent(request))) {
547 code = ROUNDROBIN_PARENT;
548 } else if ((p = getFirstUpParent(request))) {
549 code = FIRSTUP_PARENT;
550 } else if ((p = getAnyParent(request))) {
551 code = ANY_OLD_PARENT;
552 }
553
554 if (code != HIER_NONE) {
555 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
556 peerAddFwdServer(&ps->servers, p, code);
557 }
558 }
559
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;
566 HttpRequest *request = ps->request;
567 /* Add all alive parents */
568
569 for (p = Config.peers; p; p = p->next) {
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
580 debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
581
582 peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
583 }
584
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))) {
591 peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
592 }
593 }
594
595 static void
596 peerPingTimeout(void *data)
597 {
598 ps_state *psstate = (ps_state *)data;
599 StoreEntry *entry = psstate->entry;
600
601 if (entry)
602 debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" );
603
604 if (!cbdataReferenceValid(psstate->callback_data)) {
605 /* request aborted */
606 entry->ping_status = PING_DONE;
607 cbdataReferenceDone(psstate->callback_data);
608 peerSelectStateFree(psstate);
609 return;
610 }
611
612 PeerStats.timeouts++;
613 psstate->ping.timedout = 1;
614 peerSelectFoo(psstate);
615 }
616
617 void
618 peerSelectInit(void)
619 {
620 memset(&PeerStats, '\0', sizeof(PeerStats));
621 assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *));
622 }
623
624 static void
625 peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
626 {
627 int rtt;
628
629 #if USE_ICMP
630 if (Config.onoff.query_icmp) {
631 if (header->flags & ICP_FLAG_SRC_RTT) {
632 rtt = header->pad & 0xFFFF;
633 int hops = (header->pad >> 16) & 0xFFFF;
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 }
643 }
644 #endif /* USE_ICMP */
645
646 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
647 if (p->options.closest_only)
648 return;
649
650 /* set FIRST_MISS if there is no CLOSEST parent */
651 if (!ps->closest_parent_miss.IsAnyAddr())
652 return;
653
654 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
655
656 if (rtt < 1)
657 rtt = 1;
658
659 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
660 ps->first_parent_miss = p->in_addr;
661 ps->ping.w_rtt = rtt;
662 }
663 }
664
665 static void
666 peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
667 {
668 ps_state *psstate = (ps_state *)data;
669 icp_opcode op = header->getOpCode();
670 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() );
671 #if USE_CACHE_DIGESTS && 0
672 /* do cd lookup to count false misses */
673
674 if (p && request)
675 peerNoteDigestLookup(request, p,
676 peerDigestLookup(p, request, psstate->entry));
677
678 #endif
679
680 psstate->ping.n_recv++;
681
682 if (op == ICP_MISS || op == ICP_DECHO) {
683 if (type == PEER_PARENT)
684 peerIcpParentMiss(p, header, psstate);
685 } else if (op == ICP_HIT) {
686 psstate->hit = p;
687 psstate->hit_type = type;
688 peerSelectFoo(psstate);
689 return;
690 }
691
692 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
693 return;
694
695 peerSelectFoo(psstate);
696 }
697
698 #if USE_HTCP
699 static void
700 peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
701 {
702 ps_state *psstate = (ps_state *)data;
703 debugs(44, 3, "peerHandleHtcpReply: " <<
704 (htcp->hit ? "HIT" : "MISS") << " " <<
705 psstate->entry->url() );
706 psstate->ping.n_recv++;
707
708 if (htcp->hit) {
709 psstate->hit = p;
710 psstate->hit_type = type;
711 peerSelectFoo(psstate);
712 return;
713 }
714
715 if (type == PEER_PARENT)
716 peerHtcpParentMiss(p, htcp, psstate);
717
718 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
719 return;
720
721 peerSelectFoo(psstate);
722 }
723
724 static void
725 peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
726 {
727 int rtt;
728
729 #if USE_ICMP
730 if (Config.onoff.query_icmp) {
731 if (htcp->cto.rtt > 0) {
732 rtt = (int) htcp->cto.rtt * 1000;
733 int hops = (int) htcp->cto.hops * 1000;
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 }
741 }
742 #endif /* USE_ICMP */
743
744 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
745 if (p->options.closest_only)
746 return;
747
748 /* set FIRST_MISS if there is no CLOSEST parent */
749 if (!ps->closest_parent_miss.IsAnyAddr())
750 return;
751
752 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
753
754 if (rtt < 1)
755 rtt = 1;
756
757 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
758 ps->first_parent_miss = p->in_addr;
759 ps->ping.w_rtt = rtt;
760 }
761 }
762
763 #endif
764
765 static void
766 peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
767 {
768 if (proto == PROTO_ICP)
769 peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
770
771 #if USE_HTCP
772
773 else if (proto == PROTO_HTCP)
774 peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data);
775
776 #endif
777
778 else
779 debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto);
780 }
781
782 static void
783 peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code)
784 {
785 FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
786 debugs(44, 5, "peerAddFwdServer: adding " <<
787 (p ? p->host : "DIRECT") << " " <<
788 hier_strings[code] );
789 fs->_peer = cbdataReference(p);
790 fs->code = code;
791
792 while (*FSVR)
793 FSVR = &(*FSVR)->next;
794
795 *FSVR = fs;
796 }
797
798 void *
799 ps_state::operator new(size_t)
800 {
801 CBDATA_INIT_TYPE(ps_state);
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),
813 first_parent_miss(),
814 closest_parent_miss(),
815 hit(NULL),
816 hit_type(PEER_NONE),
817 acl_checklist (NULL)
818 {
819 ; // no local defaults.
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 }