]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/peer_select.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / peer_select.cc
index d5b31b9c46447cc62bacc72ee7b29c3333295841..a5eb32c2613075830576882abcc7a85090054f09 100644 (file)
@@ -1,6 +1,5 @@
-
 /*
- * $Id: peer_select.cc,v 1.116 2001/10/17 20:25:02 hno Exp $
+ * $Id$
  *
  * DEBUG: section 44    Peer Selection Algorithm
  * AUTHOR: Duane Wessels
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  */
 
 #include "squid.h"
+#include "event.h"
+#include "PeerSelectState.h"
+#include "Store.h"
+#include "ICP.h"
+#include "HttpRequest.h"
+#include "ACLChecklist.h"
+#include "htcp.h"
+#include "forward.h"
+#include "SquidTime.h"
+#include "icmp/net_db.h"
 
-const char *hier_strings[] =
-{
+const char *hier_strings[] = {
     "NONE",
     "DIRECT",
     "SIBLING_HIT",
@@ -55,10 +63,10 @@ const char *hier_strings[] =
     "CD_PARENT_HIT",
     "CD_SIBLING_HIT",
 #endif
-#if USE_CARP
     "CARP",
-#endif
     "ANY_PARENT",
+    "USERHASH",
+    "SOURCEHASH",
     "INVALID CODE"
 };
 
@@ -66,8 +74,7 @@ static struct {
     int timeouts;
 } PeerStats;
 
-static const char *DirectStr[] =
-{
+static const char *DirectStr[] = {
     "DIRECT_UNKNOWN",
     "DIRECT_NO",
     "DIRECT_MAYBE",
@@ -91,75 +98,96 @@ static void peerGetSomeDirect(ps_state *);
 static void peerGetSomeParent(ps_state *);
 static void peerGetAllParents(ps_state *);
 static void peerAddFwdServer(FwdServer **, peer *, hier_code);
+static void peerSelectPinned(ps_state * ps);
+
+CBDATA_CLASS_INIT(ps_state);
 
 static void
 peerSelectStateFree(ps_state * psstate)
 {
     if (psstate->acl_checklist) {
-       debug(44, 1) ("calling aclChecklistFree() from peerSelectStateFree\n");
-       aclChecklistFree(psstate->acl_checklist);
+        debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree");
+        delete (psstate->acl_checklist);
     }
-    requestUnlink(psstate->request);
-    psstate->request = NULL;
+
+    HTTPMSGUNLOCK(psstate->request);
+
     if (psstate->entry) {
-       assert(psstate->entry->ping_status != PING_WAITING);
-       storeUnlockObject(psstate->entry);
-       psstate->entry = NULL;
+        assert(psstate->entry->ping_status != PING_WAITING);
+        psstate->entry->unlock();
+        psstate->entry = NULL;
     }
+
     cbdataFree(psstate);
 }
 
 static int
-peerSelectIcpPing(request_t * request, int direct, StoreEntry * entry)
+peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
 {
     int n;
     assert(entry);
     assert(entry->ping_status == PING_NONE);
     assert(direct != DIRECT_YES);
-    debug(44, 3) ("peerSelectIcpPing: %s\n", storeUrl(entry));
+    debugs(44, 3, "peerSelectIcpPing: " << entry->url()  );
+
     if (!request->flags.hierarchical && direct != DIRECT_NO)
-       return 0;
+        return 0;
+
     if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
-       if (direct != DIRECT_NO)
-           return 0;
+        if (direct != DIRECT_NO)
+            return 0;
+
     n = neighborsCount(request);
-    debug(44, 3) ("peerSelectIcpPing: counted %d neighbors\n", n);
+
+    debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
+
     return n;
 }
 
 
 void
-peerSelect(request_t * request,
-    StoreEntry * entry,
-    PSC * callback,
-    void *callback_data)
+peerSelect(HttpRequest * request,
+           StoreEntry * entry,
+           PSC * callback,
+           void *callback_data)
 {
     ps_state *psstate;
+
     if (entry)
-       debug(44, 3) ("peerSelect: %s\n", storeUrl(entry));
+        debugs(44, 3, "peerSelect: " << entry->url()  );
     else
-       debug(44, 3) ("peerSelect: %s\n", RequestMethodStr[request->method]);
-    psstate = cbdataAlloc(ps_state);
-    psstate->request = requestLink(request);
+        debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method));
+
+    psstate = new ps_state;
+
+    psstate->request = HTTPMSGLOCK(request);
+
     psstate->entry = entry;
+
     psstate->callback = callback;
-    psstate->callback_data = callback_data;
+
+    psstate->callback_data = cbdataReference(callback_data);
+
     psstate->direct = DIRECT_UNKNOWN;
+
 #if USE_CACHE_DIGESTS
+
     request->hier.peer_select_start = current_time;
+
 #endif
+
     if (psstate->entry)
-       storeLockObject(psstate->entry);
-    cbdataLock(callback_data);
+        psstate->entry->lock();
+
     peerSelectFoo(psstate);
 }
 
 static void
 peerCheckNeverDirectDone(int answer, void *data)
 {
-    ps_state *psstate = data;
+    ps_state *psstate = (ps_state *) data;
     psstate->acl_checklist = NULL;
-    debug(44, 3) ("peerCheckNeverDirectDone: %d\n", answer);
+    debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
     psstate->never_direct = answer ? 1 : -1;
     peerSelectFoo(psstate);
 }
@@ -167,9 +195,9 @@ peerCheckNeverDirectDone(int answer, void *data)
 static void
 peerCheckAlwaysDirectDone(int answer, void *data)
 {
-    ps_state *psstate = data;
+    ps_state *psstate = (ps_state *)data;
     psstate->acl_checklist = NULL;
-    debug(44, 3) ("peerCheckAlwaysDirectDone: %d\n", answer);
+    debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
     psstate->always_direct = answer ? 1 : -1;
     peerSelectFoo(psstate);
 }
@@ -179,56 +207,79 @@ peerSelectCallback(ps_state * psstate)
 {
     StoreEntry *entry = psstate->entry;
     FwdServer *fs = psstate->servers;
-    void *data = psstate->callback_data;
+    PSC *callback;
+    void *cbdata;
+
     if (entry) {
-       debug(44, 3) ("peerSelectCallback: %s\n", storeUrl(entry));
-       if (entry->ping_status == PING_WAITING)
-           eventDelete(peerPingTimeout, psstate);
-       entry->ping_status = PING_DONE;
+        debugs(44, 3, "peerSelectCallback: " << entry->url()  );
+
+        if (entry->ping_status == PING_WAITING)
+            eventDelete(peerPingTimeout, psstate);
+
+        entry->ping_status = PING_DONE;
     }
+
     if (fs == NULL) {
-       debug(44, 1) ("Failed to select source for '%s'\n", storeUrl(entry));
-       debug(44, 1) ("  always_direct = %d\n", psstate->always_direct);
-       debug(44, 1) ("   never_direct = %d\n", psstate->never_direct);
-       debug(44, 1) ("       timedout = %d\n", psstate->ping.timedout);
+        debugs(44, 1, "Failed to select source for '" << entry->url() << "'" );
+        debugs(44, 1, "  always_direct = " << psstate->always_direct  );
+        debugs(44, 1, "   never_direct = " << psstate->never_direct  );
+        debugs(44, 1, "       timedout = " << psstate->ping.timedout  );
     }
+
     psstate->ping.stop = current_time;
     psstate->request->hier.ping = psstate->ping;
-    if (cbdataValid(data)) {
-       psstate->servers = NULL;
-       psstate->callback(fs, data);
+    callback = psstate->callback;
+    psstate->callback = NULL;
+
+    if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
+        psstate->servers = NULL;
+        callback(fs, cbdata);
     }
-    cbdataUnlock(data);
+
     peerSelectStateFree(psstate);
 }
 
 static int
 peerCheckNetdbDirect(ps_state * psstate)
 {
+#if USE_ICMP
     peer *p;
     int myrtt;
     int myhops;
+
     if (psstate->direct == DIRECT_NO)
-       return 0;
-    myrtt = netdbHostRtt(psstate->request->host);
-    debug(44, 3) ("peerCheckNetdbDirect: MY RTT = %d msec\n", myrtt);
-    debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_rtt = %d msec\n",
-       Config.minDirectRtt);
+        return 0;
+
+    /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
+
+    myrtt = netdbHostRtt(psstate->request->GetHost());
+
+    debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
+    debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
+
     if (myrtt && myrtt <= Config.minDirectRtt)
-       return 1;
-    myhops = netdbHostHops(psstate->request->host);
-    debug(44, 3) ("peerCheckNetdbDirect: MY hops = %d\n", myhops);
-    debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_hops = %d\n",
-       Config.minDirectHops);
+        return 1;
+
+    myhops = netdbHostHops(psstate->request->GetHost());
+
+    debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
+    debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
+
     if (myhops && myhops <= Config.minDirectHops)
-       return 1;
-    p = whichPeer(&psstate->closest_parent_miss);
+        return 1;
+
+    p = whichPeer(psstate->closest_parent_miss);
+
     if (p == NULL)
-       return 0;
-    debug(44, 3) ("peerCheckNetdbDirect: closest_parent_miss RTT = %d msec\n",
-       psstate->ping.p_rtt);
+        return 0;
+
+    debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
+
     if (myrtt && myrtt <= psstate->ping.p_rtt)
-       return 1;
+        return 1;
+
+#endif /* USE_ICMP */
+
     return 0;
 }
 
@@ -236,185 +287,225 @@ static void
 peerSelectFoo(ps_state * ps)
 {
     StoreEntry *entry = ps->entry;
-    request_t *request = ps->request;
-    debug(44, 3) ("peerSelectFoo: '%s %s'\n",
-       RequestMethodStr[request->method],
-       request->host);
+    HttpRequest *request = ps->request;
+    debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
+
     if (ps->direct == DIRECT_UNKNOWN) {
-       if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
-           ps->acl_checklist = aclChecklistCreate(
-               Config.accessList.AlwaysDirect,
-               request,
-               NULL);          /* ident */
-           aclNBCheck(ps->acl_checklist,
-               peerCheckAlwaysDirectDone,
-               ps);
-           return;
-       } else if (ps->always_direct > 0) {
-           ps->direct = DIRECT_YES;
-       } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
-           ps->acl_checklist = aclChecklistCreate(
-               Config.accessList.NeverDirect,
-               request,
-               NULL);          /* ident */
-           aclNBCheck(ps->acl_checklist,
-               peerCheckNeverDirectDone,
-               ps);
-           return;
-       } else if (ps->never_direct > 0) {
-           ps->direct = DIRECT_NO;
-       } else if (request->flags.loopdetect) {
-           ps->direct = DIRECT_YES;
-       } else if (peerCheckNetdbDirect(ps)) {
-           ps->direct = DIRECT_YES;
-       } else {
-           ps->direct = DIRECT_MAYBE;
-       }
-       debug(44, 3) ("peerSelectFoo: direct = %s\n",
-           DirectStr[ps->direct]);
+        if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
+            ps->acl_checklist = aclChecklistCreate(
+                                    Config.accessList.AlwaysDirect,
+                                    request,
+                                    NULL);             /* ident */
+            ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone,
+                                                ps);
+            return;
+        } else if (ps->always_direct > 0) {
+            ps->direct = DIRECT_YES;
+        } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
+            ps->acl_checklist = aclChecklistCreate(
+                                    Config.accessList.NeverDirect,
+                                    request,
+                                    NULL);             /* ident */
+            ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone,
+                                                ps);
+            return;
+        } else if (ps->never_direct > 0) {
+            ps->direct = DIRECT_NO;
+        } else if (request->flags.accelerated) {
+            ps->direct = DIRECT_NO;
+        } else if (request->flags.loopdetect) {
+            ps->direct = DIRECT_YES;
+        } else if (peerCheckNetdbDirect(ps)) {
+            ps->direct = DIRECT_YES;
+        } else {
+            ps->direct = DIRECT_MAYBE;
+        }
+
+        debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
     }
+
+    if (!entry || entry->ping_status == PING_NONE)
+        peerSelectPinned(ps);
     if (entry == NULL) {
-       (void) 0;
+        (void) 0;
     } else if (entry->ping_status == PING_NONE) {
-       peerGetSomeNeighbor(ps);
-       if (entry->ping_status == PING_WAITING)
-           return;
+        peerGetSomeNeighbor(ps);
+
+        if (entry->ping_status == PING_WAITING)
+            return;
     } else if (entry->ping_status == PING_WAITING) {
-       peerGetSomeNeighborReplies(ps);
-       entry->ping_status = PING_DONE;
+        peerGetSomeNeighborReplies(ps);
+        entry->ping_status = PING_DONE;
     }
+
     switch (ps->direct) {
+
     case DIRECT_YES:
-       peerGetSomeDirect(ps);
-       break;
+        peerGetSomeDirect(ps);
+        break;
+
     case DIRECT_NO:
-       peerGetSomeParent(ps);
-       peerGetAllParents(ps);
-       break;
+        peerGetSomeParent(ps);
+        peerGetAllParents(ps);
+        break;
+
     default:
-       if (Config.onoff.prefer_direct)
-           peerGetSomeDirect(ps);
-       if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct)
-           peerGetSomeParent(ps);
-       if (!Config.onoff.prefer_direct)
-           peerGetSomeDirect(ps);
-       break;
+
+        if (Config.onoff.prefer_direct)
+            peerGetSomeDirect(ps);
+
+        if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct)
+            peerGetSomeParent(ps);
+
+        if (!Config.onoff.prefer_direct)
+            peerGetSomeDirect(ps);
+
+        break;
     }
+
     peerSelectCallback(ps);
 }
 
+/*
+ * peerSelectPinned
+ *
+ * Selects a pinned connection
+ */
+int peerAllowedToUse(const peer * p, HttpRequest * request);
+static void
+peerSelectPinned(ps_state * ps)
+{
+    HttpRequest *request = ps->request;
+    peer *peer;
+    if (!request->pinnedConnection())
+        return;
+    if (request->pinnedConnection()->validatePinnedConnection(request) != -1) {
+        peer = request->pinnedConnection()->pinnedPeer();
+        if (peer && peerAllowedToUse(peer, request)) {
+            peerAddFwdServer(&ps->servers, peer, PINNED);
+            if (ps->entry)
+                ps->entry->ping_status = PING_DONE;     /* Skip ICP */
+        } else if (!peer && ps->direct != DIRECT_NO) {
+            peerAddFwdServer(&ps->servers, NULL, PINNED);
+            if (ps->entry)
+                ps->entry->ping_status = PING_DONE;     /* Skip ICP */
+        }
+    }
+}
+
 /*
  * peerGetSomeNeighbor
- * 
+ *
  * Selects a neighbor (parent or sibling) based on one of the
  * following methods:
  *      Cache Digests
  *      CARP
- *      Netdb RTT estimates
+ *      ICMP Netdb RTT estimates
  *      ICP/HTCP queries
  */
 static void
 peerGetSomeNeighbor(ps_state * ps)
 {
     StoreEntry *entry = ps->entry;
-    request_t *request = ps->request;
+    HttpRequest *request = ps->request;
     peer *p;
     hier_code code = HIER_NONE;
     assert(entry->ping_status == PING_NONE);
+
     if (ps->direct == DIRECT_YES) {
-       entry->ping_status = PING_DONE;
-       return;
+        entry->ping_status = PING_DONE;
+        return;
     }
+
 #if USE_CACHE_DIGESTS
     if ((p = neighborsDigestSelect(request))) {
-       if (neighborType(p, request) == PEER_PARENT)
-           code = CD_PARENT_HIT;
-       else
-           code = CD_SIBLING_HIT;
+        if (neighborType(p, request) == PEER_PARENT)
+            code = CD_PARENT_HIT;
+        else
+            code = CD_SIBLING_HIT;
     } else
 #endif
-#if USE_CARP
-    if ((p = carpSelectParent(request))) {
-       code = CARP;
-    } else
-#endif
-    if ((p = netdbClosestParent(request))) {
-       code = CLOSEST_PARENT;
-    } else if (peerSelectIcpPing(request, ps->direct, entry)) {
-       debug(44, 3) ("peerSelect: Doing ICP pings\n");
-       ps->ping.start = current_time;
-       ps->ping.n_sent = neighborsUdpPing(request,
-           entry,
-           peerHandlePingReply,
-           ps,
-           &ps->ping.n_replies_expected,
-           &ps->ping.timeout);
-       if (ps->ping.n_sent == 0)
-           debug(44, 0) ("WARNING: neighborsUdpPing returned 0\n");
-       debug(44, 3) ("peerSelect: %d ICP replies expected, RTT %d msec\n",
-           ps->ping.n_replies_expected, ps->ping.timeout);
-       if (ps->ping.n_replies_expected > 0) {
-           entry->ping_status = PING_WAITING;
-           eventAdd("peerPingTimeout",
-               peerPingTimeout,
-               ps,
-               0.001 * ps->ping.timeout,
-               0);
-           return;
-       }
-    }
+        if ((p = netdbClosestParent(request))) {
+            code = CLOSEST_PARENT;
+        } else if (peerSelectIcpPing(request, ps->direct, entry)) {
+            debugs(44, 3, "peerSelect: Doing ICP pings");
+            ps->ping.start = current_time;
+            ps->ping.n_sent = neighborsUdpPing(request,
+                                               entry,
+                                               peerHandlePingReply,
+                                               ps,
+                                               &ps->ping.n_replies_expected,
+                                               &ps->ping.timeout);
+
+            if (ps->ping.n_sent == 0)
+                debugs(44, 0, "WARNING: neighborsUdpPing returned 0");
+            debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
+                   " ICP replies expected, RTT " << ps->ping.timeout <<
+                   " msec");
+
+
+            if (ps->ping.n_replies_expected > 0) {
+                entry->ping_status = PING_WAITING;
+                eventAdd("peerPingTimeout",
+                         peerPingTimeout,
+                         ps,
+                         0.001 * ps->ping.timeout,
+                         0);
+                return;
+            }
+        }
+
     if (code != HIER_NONE) {
-       assert(p);
-       debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
-       peerAddFwdServer(&ps->servers, p, code);
+        assert(p);
+        debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
+        peerAddFwdServer(&ps->servers, p, code);
     }
+
     entry->ping_status = PING_DONE;
 }
 
 /*
  * peerGetSomeNeighborReplies
- * 
+ *
  * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
  */
 static void
 peerGetSomeNeighborReplies(ps_state * ps)
 {
-    request_t *request = ps->request;
+    HttpRequest *request = ps->request;
     peer *p = NULL;
     hier_code code = HIER_NONE;
     assert(ps->entry->ping_status == PING_WAITING);
     assert(ps->direct != DIRECT_YES);
+
     if (peerCheckNetdbDirect(ps)) {
-       code = CLOSEST_DIRECT;
-       debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
-       peerAddFwdServer(&ps->servers, NULL, code);
-       return;
+        code = CLOSEST_DIRECT;
+        debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->GetHost());
+        peerAddFwdServer(&ps->servers, NULL, code);
+        return;
     }
+
     if ((p = ps->hit)) {
-       code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
-    } else
-#if ALLOW_SOURCE_PING
-    if ((p = ps->secho)) {
-       code = SOURCE_FASTEST;
-    } else
-#endif
-    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
-       p = whichPeer(&ps->closest_parent_miss);
-       code = CLOSEST_PARENT_MISS;
-    } else if (ps->first_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
-       p = whichPeer(&ps->first_parent_miss);
-       code = FIRST_PARENT_MISS;
+        code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
+    } else {
+        if (!ps->closest_parent_miss.IsAnyAddr()) {
+            p = whichPeer(ps->closest_parent_miss);
+            code = CLOSEST_PARENT_MISS;
+        } else if (!ps->first_parent_miss.IsAnyAddr()) {
+            p = whichPeer(ps->first_parent_miss);
+            code = FIRST_PARENT_MISS;
+        }
     }
     if (p && code != HIER_NONE) {
-       debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
-       peerAddFwdServer(&ps->servers, p, code);
+        debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
+        peerAddFwdServer(&ps->servers, p, code);
     }
 }
 
 
 /*
  * peerGetSomeDirect
- * 
+ *
  * Simply adds a 'direct' entry to the FwdServers list if this
  * request can be forwarded directly to the origin server
  */
@@ -422,37 +513,47 @@ static void
 peerGetSomeDirect(ps_state * ps)
 {
     if (ps->direct == DIRECT_NO)
-       return;
+        return;
+
+    /* WAIS is not implemented natively */
     if (ps->request->protocol == PROTO_WAIS)
-       /* Its not really DIRECT, now is it? */
-       peerAddFwdServer(&ps->servers, Config.Wais.peer, DIRECT);
-    else
-       peerAddFwdServer(&ps->servers, NULL, DIRECT);
+        return;
+
+    peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
 }
 
 static void
 peerGetSomeParent(ps_state * ps)
 {
     peer *p;
-    request_t *request = ps->request;
+    HttpRequest *request = ps->request;
     hier_code code = HIER_NONE;
-    debug(44, 3) ("peerGetSomeParent: %s %s\n",
-       RequestMethodStr[request->method],
-       request->host);
+    debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr(request->method) << " " << request->GetHost());
+
     if (ps->direct == DIRECT_YES)
-       return;
+        return;
+
     if ((p = getDefaultParent(request))) {
-       code = DEFAULT_PARENT;
+        code = DEFAULT_PARENT;
+    } else if ((p = peerUserHashSelectParent(request))) {
+        code = USERHASH_PARENT;
+    } else if ((p = peerSourceHashSelectParent(request))) {
+        code = SOURCEHASH_PARENT;
+    } else if ((p = carpSelectParent(request))) {
+        code = CARP;
     } else if ((p = getRoundRobinParent(request))) {
-       code = ROUNDROBIN_PARENT;
+        code = ROUNDROBIN_PARENT;
+    } else if ((p = getWeightedRoundRobinParent(request))) {
+        code = ROUNDROBIN_PARENT;
     } else if ((p = getFirstUpParent(request))) {
-       code = FIRSTUP_PARENT;
+        code = FIRSTUP_PARENT;
     } else if ((p = getAnyParent(request))) {
-       code = ANY_OLD_PARENT;
+        code = ANY_OLD_PARENT;
     }
+
     if (code != HIER_NONE) {
-       debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
-       peerAddFwdServer(&ps->servers, p, code);
+        debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
+        peerAddFwdServer(&ps->servers, p, code);
     }
 }
 
@@ -462,43 +563,52 @@ static void
 peerGetAllParents(ps_state * ps)
 {
     peer *p;
-    request_t *request = ps->request;
+    HttpRequest *request = ps->request;
     /* Add all alive parents */
+
     for (p = Config.peers; p; p = p->next) {
-       /* XXX: neighbors.c lacks a public interface for enumerating
-        * parents to a request so we have to dig some here..
-        */
-       if (neighborType(p, request) != PEER_PARENT)
-           continue;
-       if (!peerHTTPOkay(p, request))
-           continue;
-       debug(15, 3) ("peerGetAllParents: adding alive parent %s\n", p->host);
-       peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
+        /* XXX: neighbors.c lacks a public interface for enumerating
+         * parents to a request so we have to dig some here..
+         */
+
+        if (neighborType(p, request) != PEER_PARENT)
+            continue;
+
+        if (!peerHTTPOkay(p, request))
+            continue;
+
+        debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
+
+        peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
     }
+
     /* XXX: should add dead parents here, but it is currently
      * not possible to find out which parents are dead or which
      * simply are not configured to handle the request.
      */
     /* Add default parent as a last resort */
     if ((p = getDefaultParent(request))) {
-       peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
+        peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
     }
 }
 
 static void
 peerPingTimeout(void *data)
 {
-    ps_state *psstate = data;
+    ps_state *psstate = (ps_state *)data;
     StoreEntry *entry = psstate->entry;
+
     if (entry)
-       debug(44, 3) ("peerPingTimeout: '%s'\n", storeUrl(entry));
-    if (!cbdataValid(psstate->callback_data)) {
-       /* request aborted */
-       entry->ping_status = PING_DONE;
-       cbdataUnlock(psstate->callback_data);
-       peerSelectStateFree(psstate);
-       return;
+        debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" );
+
+    if (!cbdataReferenceValid(psstate->callback_data)) {
+        /* request aborted */
+        entry->ping_status = PING_DONE;
+        cbdataReferenceDone(psstate->callback_data);
+        peerSelectStateFree(psstate);
+        return;
     }
+
     PeerStats.timeouts++;
     psstate->ping.timedout = 1;
     peerSelectFoo(psstate);
@@ -515,65 +625,73 @@ static void
 peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
 {
     int rtt;
-    int hops;
+
+#if USE_ICMP
     if (Config.onoff.query_icmp) {
-       if (header->flags & ICP_FLAG_SRC_RTT) {
-           rtt = header->pad & 0xFFFF;
-           hops = (header->pad >> 16) & 0xFFFF;
-           if (rtt > 0 && rtt < 0xFFFF)
-               netdbUpdatePeer(ps->request, p, rtt, hops);
-           if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
-               ps->closest_parent_miss = p->in_addr;
-               ps->ping.p_rtt = rtt;
-           }
-       }
+        if (header->flags & ICP_FLAG_SRC_RTT) {
+            rtt = header->pad & 0xFFFF;
+            int hops = (header->pad >> 16) & 0xFFFF;
+
+            if (rtt > 0 && rtt < 0xFFFF)
+                netdbUpdatePeer(ps->request, p, rtt, hops);
+
+            if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
+                ps->closest_parent_miss = p->in_addr;
+                ps->ping.p_rtt = rtt;
+            }
+        }
     }
+#endif /* USE_ICMP */
+
     /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
     if (p->options.closest_only)
-       return;
+        return;
+
     /* set FIRST_MISS if there is no CLOSEST parent */
-    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
-       return;
-    rtt = tvSubMsec(ps->ping.start, current_time) / p->weight;
-    if (ps->ping.w_rtt == 0 || rtt < ps->ping.w_rtt) {
-       ps->first_parent_miss = p->in_addr;
-       ps->ping.w_rtt = rtt;
+    if (!ps->closest_parent_miss.IsAnyAddr())
+        return;
+
+    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
+
+    if (rtt < 1)
+        rtt = 1;
+
+    if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
+        ps->first_parent_miss = p->in_addr;
+        ps->ping.w_rtt = rtt;
     }
 }
 
 static void
 peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
 {
-    ps_state *psstate = data;
-    icp_opcode op = header->opcode;
-    debug(44, 3) ("peerHandleIcpReply: %s %s\n",
-       icp_opcode_str[op],
-       storeUrl(psstate->entry));
+    ps_state *psstate = (ps_state *)data;
+    icp_opcode op = header->getOpCode();
+    debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url()  );
 #if USE_CACHE_DIGESTS && 0
     /* do cd lookup to count false misses */
+
     if (p && request)
-       peerNoteDigestLookup(request, p,
-           peerDigestLookup(p, request, psstate->entry));
+        peerNoteDigestLookup(request, p,
+                             peerDigestLookup(p, request, psstate->entry));
+
 #endif
+
     psstate->ping.n_recv++;
+
     if (op == ICP_MISS || op == ICP_DECHO) {
-       if (type == PEER_PARENT)
-           peerIcpParentMiss(p, header, psstate);
+        if (type == PEER_PARENT)
+            peerIcpParentMiss(p, header, psstate);
     } else if (op == ICP_HIT) {
-       psstate->hit = p;
-       psstate->hit_type = type;
-       peerSelectFoo(psstate);
-       return;
+        psstate->hit = p;
+        psstate->hit_type = type;
+        peerSelectFoo(psstate);
+        return;
     }
-#if ALLOW_SOURCE_PING
-    else if (op == ICP_SECHO) {
-       psstate->secho = p;
-       peerSelectFoo(psstate);
-       return;
-    }
-#endif
+
     if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
-       return;
+        return;
+
     peerSelectFoo(psstate);
 }
 
@@ -581,21 +699,25 @@ peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
 static void
 peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
 {
-    ps_state *psstate = data;
-    debug(44, 3) ("peerHandleIcpReply: %s %s\n",
-       htcp->hit ? "HIT" : "MISS",
-       storeUrl(psstate->entry));
+    ps_state *psstate = (ps_state *)data;
+    debugs(44, 3, "peerHandleHtcpReply: " <<
+           (htcp->hit ? "HIT" : "MISS") << " " <<
+           psstate->entry->url()  );
     psstate->ping.n_recv++;
+
     if (htcp->hit) {
-       psstate->hit = p;
-       psstate->hit_type = type;
-       peerSelectFoo(psstate);
-       return;
+        psstate->hit = p;
+        psstate->hit_type = type;
+        peerSelectFoo(psstate);
+        return;
     }
+
     if (type == PEER_PARENT)
-       peerHtcpParentMiss(p, htcp, psstate);
+        peerHtcpParentMiss(p, htcp, psstate);
+
     if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
-       return;
+        return;
+
     peerSelectFoo(psstate);
 }
 
@@ -603,56 +725,111 @@ static void
 peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
 {
     int rtt;
-    int hops;
+
+#if USE_ICMP
     if (Config.onoff.query_icmp) {
-       if (htcp->cto.rtt > 0) {
-           rtt = (int) htcp->cto.rtt * 1000;
-           hops = (int) htcp->cto.hops * 1000;
-           netdbUpdatePeer(ps->request, p, rtt, hops);
-           if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
-               ps->closest_parent_miss = p->in_addr;
-               ps->ping.p_rtt = rtt;
-           }
-       }
+        if (htcp->cto.rtt > 0) {
+            rtt = (int) htcp->cto.rtt * 1000;
+            int hops = (int) htcp->cto.hops * 1000;
+            netdbUpdatePeer(ps->request, p, rtt, hops);
+
+            if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
+                ps->closest_parent_miss = p->in_addr;
+                ps->ping.p_rtt = rtt;
+            }
+        }
     }
+#endif /* USE_ICMP */
+
     /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
     if (p->options.closest_only)
-       return;
+        return;
+
     /* set FIRST_MISS if there is no CLOSEST parent */
-    if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
-       return;
-    rtt = tvSubMsec(ps->ping.start, current_time) / p->weight;
-    if (ps->ping.w_rtt == 0 || rtt < ps->ping.w_rtt) {
-       ps->first_parent_miss = p->in_addr;
-       ps->ping.w_rtt = rtt;
+    if (!ps->closest_parent_miss.IsAnyAddr())
+        return;
+
+    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
+
+    if (rtt < 1)
+        rtt = 1;
+
+    if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
+        ps->first_parent_miss = p->in_addr;
+        ps->ping.w_rtt = rtt;
     }
 }
+
 #endif
 
 static void
 peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
 {
     if (proto == PROTO_ICP)
-       peerHandleIcpReply(p, type, pingdata, data);
+        peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
+
 #if USE_HTCP
+
     else if (proto == PROTO_HTCP)
-       peerHandleHtcpReply(p, type, pingdata, data);
+        peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data);
+
 #endif
+
     else
-       debug(44, 1) ("peerHandlePingReply: unknown protocol_t %d\n", (int) proto);
+        debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto);
 }
 
 static void
-peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code)
+peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code)
 {
-    FwdServer *fs = memAllocate(MEM_FWD_SERVER);
-    debug(44, 5) ("peerAddFwdServer: adding %s %s\n",
-       p ? p->host : "DIRECT",
-       hier_strings[code]);
-    fs->peer = p;
+    FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
+    debugs(44, 5, "peerAddFwdServer: adding " <<
+           (p ? p->host : "DIRECT")  << " " <<
+           hier_strings[code]  );
+    fs->_peer = cbdataReference(p);
     fs->code = code;
-    cbdataLock(fs->peer);
-    while (*FS)
-       FS = &(*FS)->next;
-    *FS = fs;
+
+    while (*FSVR)
+        FSVR = &(*FSVR)->next;
+
+    *FSVR = fs;
+}
+
+void *
+ps_state::operator new(size_t)
+{
+    CBDATA_INIT_TYPE(ps_state);
+    return cbdataAlloc(ps_state);
+}
+
+ps_state::ps_state() : request (NULL),
+        entry (NULL),
+        always_direct (0),
+        never_direct (0),
+        direct (0),
+        callback (NULL),
+        callback_data (NULL),
+        servers (NULL),
+        first_parent_miss(),
+        closest_parent_miss(),
+        hit(NULL),
+        hit_type(PEER_NONE),
+        acl_checklist (NULL)
+{
+    ; // no local defaults.
+}
+
+ping_data::ping_data() :
+        n_sent(0),
+        n_recv(0),
+        n_replies_expected(0),
+        timeout(0),
+        timedout(0),
+        w_rtt(0),
+        p_rtt(0)
+{
+    start.tv_sec = 0;
+    start.tv_usec = 0;
+    stop.tv_sec = 0;
+    stop.tv_usec = 0;
 }