]> git.ipfire.org Git - thirdparty/squid.git/blob - src/peer_select.cc
Merge from trunk
[thirdparty/squid.git] / src / peer_select.cc
1 /*
2 * $Id: peer_select.cc,v 1.149 2008/01/20 08:54:28 amosjeffries Exp $
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
46 const char *hier_strings[] =
47 {
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 {
75 int timeouts;
76 } PeerStats;
77
78 static const char *DirectStr[] =
79 {
80 "DIRECT_UNKNOWN",
81 "DIRECT_NO",
82 "DIRECT_MAYBE",
83 "DIRECT_YES"
84 };
85
86 static void peerSelectFoo(ps_state *);
87 static void peerPingTimeout(void *data);
88 static void peerSelectCallback(ps_state * psstate);
89 static IRCB peerHandlePingReply;
90 static void peerSelectStateFree(ps_state * psstate);
91 static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
92 #if USE_HTCP
93 static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
94 static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
95 #endif
96 static int peerCheckNetdbDirect(ps_state * psstate);
97 static void peerGetSomeNeighbor(ps_state *);
98 static void peerGetSomeNeighborReplies(ps_state *);
99 static void peerGetSomeDirect(ps_state *);
100 static void peerGetSomeParent(ps_state *);
101 static void peerGetAllParents(ps_state *);
102 static void peerAddFwdServer(FwdServer **, peer *, hier_code);
103 static void peerSelectPinned(ps_state * ps);
104
105 CBDATA_CLASS_INIT(ps_state);
106
107 static void
108 peerSelectStateFree(ps_state * psstate)
109 {
110 if (psstate->acl_checklist) {
111 debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree");
112 delete (psstate->acl_checklist);
113 }
114
115 HTTPMSGUNLOCK(psstate->request);
116
117 if (psstate->entry) {
118 assert(psstate->entry->ping_status != PING_WAITING);
119 psstate->entry->unlock();
120 psstate->entry = NULL;
121 }
122
123 cbdataFree(psstate);
124 }
125
126 static int
127 peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
128 {
129 int n;
130 assert(entry);
131 assert(entry->ping_status == PING_NONE);
132 assert(direct != DIRECT_YES);
133 debugs(44, 3, "peerSelectIcpPing: " << entry->url() );
134
135 if (!request->flags.hierarchical && direct != DIRECT_NO)
136 return 0;
137
138 if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
139 if (direct != DIRECT_NO)
140 return 0;
141
142 n = neighborsCount(request);
143
144 debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
145
146 return n;
147 }
148
149
150 void
151 peerSelect(HttpRequest * request,
152 StoreEntry * entry,
153 PSC * callback,
154 void *callback_data)
155 {
156 ps_state *psstate;
157
158 if (entry)
159 debugs(44, 3, "peerSelect: " << entry->url() );
160 else
161 debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method));
162
163 psstate = new ps_state;
164
165 psstate->request = HTTPMSGLOCK(request);
166
167 psstate->entry = entry;
168
169 psstate->callback = callback;
170
171 psstate->callback_data = cbdataReference(callback_data);
172
173 psstate->direct = DIRECT_UNKNOWN;
174
175 #if USE_CACHE_DIGESTS
176
177 request->hier.peer_select_start = current_time;
178
179 #endif
180
181 if (psstate->entry)
182 psstate->entry->lock();
183
184 peerSelectFoo(psstate);
185 }
186
187 static void
188 peerCheckNeverDirectDone(int answer, void *data)
189 {
190 ps_state *psstate = (ps_state *) data;
191 psstate->acl_checklist = NULL;
192 debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
193 psstate->never_direct = answer ? 1 : -1;
194 peerSelectFoo(psstate);
195 }
196
197 static void
198 peerCheckAlwaysDirectDone(int answer, void *data)
199 {
200 ps_state *psstate = (ps_state *)data;
201 psstate->acl_checklist = NULL;
202 debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
203 psstate->always_direct = answer ? 1 : -1;
204 peerSelectFoo(psstate);
205 }
206
207 static void
208 peerSelectCallback(ps_state * psstate)
209 {
210 StoreEntry *entry = psstate->entry;
211 FwdServer *fs = psstate->servers;
212 PSC *callback;
213 void *cbdata;
214
215 if (entry) {
216 debugs(44, 3, "peerSelectCallback: " << entry->url() );
217
218 if (entry->ping_status == PING_WAITING)
219 eventDelete(peerPingTimeout, psstate);
220
221 entry->ping_status = PING_DONE;
222 }
223
224 if (fs == NULL) {
225 debugs(44, 1, "Failed to select source for '" << entry->url() << "'" );
226 debugs(44, 1, " always_direct = " << psstate->always_direct );
227 debugs(44, 1, " never_direct = " << psstate->never_direct );
228 debugs(44, 1, " timedout = " << psstate->ping.timedout );
229 }
230
231 psstate->ping.stop = current_time;
232 psstate->request->hier.ping = psstate->ping;
233 callback = psstate->callback;
234 psstate->callback = NULL;
235
236 if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
237 psstate->servers = NULL;
238 callback(fs, cbdata);
239 }
240
241 peerSelectStateFree(psstate);
242 }
243
244 static int
245 peerCheckNetdbDirect(ps_state * psstate)
246 {
247 peer *p;
248 int myrtt;
249 int myhops;
250
251 if (psstate->direct == DIRECT_NO)
252 return 0;
253
254 myrtt = netdbHostRtt(psstate->request->GetHost());
255
256 debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
257 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
258
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
269 if (myhops && myhops <= Config.minDirectHops)
270 return 1;
271
272 p = whichPeer(psstate->closest_parent_miss);
273
274 if (p == NULL)
275 return 0;
276
277 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
278
279 if (myrtt && myrtt <= psstate->ping.p_rtt)
280 return 1;
281
282 return 0;
283 }
284
285 static void
286 peerSelectFoo(ps_state * ps)
287 {
288 StoreEntry *entry = ps->entry;
289 HttpRequest *request = ps->request;
290 debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
291
292 if (ps->direct == DIRECT_UNKNOWN) {
293 if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
294 ps->acl_checklist = aclChecklistCreate(
295 Config.accessList.AlwaysDirect,
296 request,
297 NULL); /* ident */
298 ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone,
299 ps);
300 return;
301 } else if (ps->always_direct > 0) {
302 ps->direct = DIRECT_YES;
303 } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
304 ps->acl_checklist = aclChecklistCreate(
305 Config.accessList.NeverDirect,
306 request,
307 NULL); /* ident */
308 ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone,
309 ps);
310 return;
311 } else if (ps->never_direct > 0) {
312 ps->direct = DIRECT_NO;
313 } else if (request->flags.accelerated) {
314 ps->direct = DIRECT_NO;
315 } else if (request->flags.loopdetect) {
316 ps->direct = DIRECT_YES;
317 } else if (peerCheckNetdbDirect(ps)) {
318 ps->direct = DIRECT_YES;
319 } else {
320 ps->direct = DIRECT_MAYBE;
321 }
322
323 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
324 }
325
326 if (!entry || entry->ping_status == PING_NONE)
327 peerSelectPinned(ps);
328 if (entry == NULL) {
329 (void) 0;
330 } else if (entry->ping_status == PING_NONE) {
331 peerGetSomeNeighbor(ps);
332
333 if (entry->ping_status == PING_WAITING)
334 return;
335 } else if (entry->ping_status == PING_WAITING) {
336 peerGetSomeNeighborReplies(ps);
337 entry->ping_status = PING_DONE;
338 }
339
340 switch (ps->direct) {
341
342 case DIRECT_YES:
343 peerGetSomeDirect(ps);
344 break;
345
346 case DIRECT_NO:
347 peerGetSomeParent(ps);
348 peerGetAllParents(ps);
349 break;
350
351 default:
352
353 if (Config.onoff.prefer_direct)
354 peerGetSomeDirect(ps);
355
356 if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct)
357 peerGetSomeParent(ps);
358
359 if (!Config.onoff.prefer_direct)
360 peerGetSomeDirect(ps);
361
362 break;
363 }
364
365 peerSelectCallback(ps);
366 }
367
368 /*
369 * peerSelectPinned
370 *
371 * Selects a pinned connection
372 */
373 int peerAllowedToUse(const peer * p, HttpRequest * request);
374 static void
375 peerSelectPinned(ps_state * ps)
376 {
377 HttpRequest *request = ps->request;
378 peer *peer;
379 if (!request->pinnedConnection())
380 return;
381 if (request->pinnedConnection()->validatePinnedConnection(request) != -1) {
382 peer = request->pinnedConnection()->pinnedPeer();
383 if (peer && peerAllowedToUse(peer, request)) {
384 peerAddFwdServer(&ps->servers, peer, PINNED);
385 if (ps->entry)
386 ps->entry->ping_status = PING_DONE; /* Skip ICP */
387 } else if (!peer && ps->direct != DIRECT_NO) {
388 peerAddFwdServer(&ps->servers, NULL, PINNED);
389 if (ps->entry)
390 ps->entry->ping_status = PING_DONE; /* Skip ICP */
391 }
392 }
393 }
394
395 /*
396 * peerGetSomeNeighbor
397 *
398 * Selects a neighbor (parent or sibling) based on one of the
399 * following methods:
400 * Cache Digests
401 * CARP
402 * Netdb RTT estimates
403 * ICP/HTCP queries
404 */
405 static void
406 peerGetSomeNeighbor(ps_state * ps)
407 {
408 StoreEntry *entry = ps->entry;
409 HttpRequest *request = ps->request;
410 peer *p;
411 hier_code code = HIER_NONE;
412 assert(entry->ping_status == PING_NONE);
413
414 if (ps->direct == DIRECT_YES) {
415 entry->ping_status = PING_DONE;
416 return;
417 }
418
419 #if USE_CACHE_DIGESTS
420 if ((p = neighborsDigestSelect(request))) {
421 if (neighborType(p, request) == PEER_PARENT)
422 code = CD_PARENT_HIT;
423 else
424 code = CD_SIBLING_HIT;
425 } else
426 #endif
427 if ((p = netdbClosestParent(request))) {
428 code = CLOSEST_PARENT;
429 } else if (peerSelectIcpPing(request, ps->direct, entry)) {
430 debugs(44, 3, "peerSelect: Doing ICP pings");
431 ps->ping.start = current_time;
432 ps->ping.n_sent = neighborsUdpPing(request,
433 entry,
434 peerHandlePingReply,
435 ps,
436 &ps->ping.n_replies_expected,
437 &ps->ping.timeout);
438
439 if (ps->ping.n_sent == 0)
440 debugs(44, 0, "WARNING: neighborsUdpPing returned 0");
441 debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
442 " ICP replies expected, RTT " << ps->ping.timeout <<
443 " msec");
444
445
446 if (ps->ping.n_replies_expected > 0) {
447 entry->ping_status = PING_WAITING;
448 eventAdd("peerPingTimeout",
449 peerPingTimeout,
450 ps,
451 0.001 * ps->ping.timeout,
452 0);
453 return;
454 }
455 }
456
457 if (code != HIER_NONE) {
458 assert(p);
459 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
460 peerAddFwdServer(&ps->servers, p, code);
461 }
462
463 entry->ping_status = PING_DONE;
464 }
465
466 /*
467 * peerGetSomeNeighborReplies
468 *
469 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
470 */
471 static void
472 peerGetSomeNeighborReplies(ps_state * ps)
473 {
474 HttpRequest *request = ps->request;
475 peer *p = NULL;
476 hier_code code = HIER_NONE;
477 assert(ps->entry->ping_status == PING_WAITING);
478 assert(ps->direct != DIRECT_YES);
479
480 if (peerCheckNetdbDirect(ps)) {
481 code = CLOSEST_DIRECT;
482 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->GetHost());
483 peerAddFwdServer(&ps->servers, NULL, code);
484 return;
485 }
486
487 if ((p = ps->hit)) {
488 code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
489 } else
490 {
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 int hops;
629
630 if (Config.onoff.query_icmp) {
631 if (header->flags & ICP_FLAG_SRC_RTT) {
632 rtt = header->pad & 0xFFFF;
633 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
645 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
646 if (p->options.closest_only)
647 return;
648
649 /* set FIRST_MISS if there is no CLOSEST parent */
650 if (!ps->closest_parent_miss.IsAnyAddr())
651 return;
652
653 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
654
655 if (rtt < 1)
656 rtt = 1;
657
658 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
659 ps->first_parent_miss = p->in_addr;
660 ps->ping.w_rtt = rtt;
661 }
662 }
663
664 static void
665 peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
666 {
667 ps_state *psstate = (ps_state *)data;
668 icp_opcode op = header->getOpCode();
669 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() );
670 #if USE_CACHE_DIGESTS && 0
671 /* do cd lookup to count false misses */
672
673 if (p && request)
674 peerNoteDigestLookup(request, p,
675 peerDigestLookup(p, request, psstate->entry));
676
677 #endif
678
679 psstate->ping.n_recv++;
680
681 if (op == ICP_MISS || op == ICP_DECHO) {
682 if (type == PEER_PARENT)
683 peerIcpParentMiss(p, header, psstate);
684 } else if (op == ICP_HIT) {
685 psstate->hit = p;
686 psstate->hit_type = type;
687 peerSelectFoo(psstate);
688 return;
689 }
690
691 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
692 return;
693
694 peerSelectFoo(psstate);
695 }
696
697 #if USE_HTCP
698 static void
699 peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
700 {
701 ps_state *psstate = (ps_state *)data;
702 debugs(44, 3, "peerHandleHtcpReply: " <<
703 (htcp->hit ? "HIT" : "MISS") << " " <<
704 psstate->entry->url() );
705 psstate->ping.n_recv++;
706
707 if (htcp->hit) {
708 psstate->hit = p;
709 psstate->hit_type = type;
710 peerSelectFoo(psstate);
711 return;
712 }
713
714 if (type == PEER_PARENT)
715 peerHtcpParentMiss(p, htcp, psstate);
716
717 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
718 return;
719
720 peerSelectFoo(psstate);
721 }
722
723 static void
724 peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
725 {
726 int rtt;
727 int hops;
728
729 if (Config.onoff.query_icmp) {
730 if (htcp->cto.rtt > 0) {
731 rtt = (int) htcp->cto.rtt * 1000;
732 hops = (int) htcp->cto.hops * 1000;
733 netdbUpdatePeer(ps->request, p, rtt, hops);
734
735 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
736 ps->closest_parent_miss = p->in_addr;
737 ps->ping.p_rtt = rtt;
738 }
739 }
740 }
741
742 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
743 if (p->options.closest_only)
744 return;
745
746 /* set FIRST_MISS if there is no CLOSEST parent */
747 if (!ps->closest_parent_miss.IsAnyAddr())
748 return;
749
750 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
751
752 if (rtt < 1)
753 rtt = 1;
754
755 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
756 ps->first_parent_miss = p->in_addr;
757 ps->ping.w_rtt = rtt;
758 }
759 }
760
761 #endif
762
763 static void
764 peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
765 {
766 if (proto == PROTO_ICP)
767 peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
768
769 #if USE_HTCP
770
771 else if (proto == PROTO_HTCP)
772 peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data);
773
774 #endif
775
776 else
777 debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto);
778 }
779
780 static void
781 peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code)
782 {
783 FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
784 debugs(44, 5, "peerAddFwdServer: adding " <<
785 (p ? p->host : "DIRECT") << " " <<
786 hier_strings[code] );
787 fs->_peer = cbdataReference(p);
788 fs->code = code;
789
790 while (*FSVR)
791 FSVR = &(*FSVR)->next;
792
793 *FSVR = fs;
794 }
795
796 void *
797 ps_state::operator new(size_t)
798 {
799 CBDATA_INIT_TYPE(ps_state);
800 return cbdataAlloc(ps_state);
801 }
802
803 ps_state::ps_state() : request (NULL),
804 entry (NULL),
805 always_direct (0),
806 never_direct (0),
807 direct (0),
808 callback (NULL),
809 callback_data (NULL),
810 servers (NULL),
811 first_parent_miss(),
812 closest_parent_miss(),
813 hit(NULL),
814 hit_type(PEER_NONE),
815 acl_checklist (NULL)
816 {
817 ; // no local defaults.
818 }
819
820 ping_data::ping_data() :
821 n_sent(0),
822 n_recv(0),
823 n_replies_expected(0),
824 timeout(0),
825 timedout(0),
826 w_rtt(0),
827 p_rtt(0)
828 {
829 start.tv_sec = 0;
830 start.tv_usec = 0;
831 stop.tv_sec = 0;
832 stop.tv_usec = 0;
833 }