]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/neighbors.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / neighbors.cc
index eeef85682fd3df1dc4aaa5b7774576c611edc9ca..ce2e6182ba35fe83490842224dac45cf07c3bd14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -11,6 +11,7 @@
 #include "squid.h"
 #include "acl/FilledChecklist.h"
 #include "anyp/PortCfg.h"
+#include "base/EnumIterator.h"
 #include "CacheDigest.h"
 #include "CachePeer.h"
 #include "comm/Connection.h"
 #include "Store.h"
 #include "store_key_md5.h"
 #include "tools.h"
-#include "URL.h"
 
 /* count mcast group peers every 15 minutes */
 #define MCAST_COUNT_RATE 900
 
-bool peerAllowedToUse(const CachePeer *, HttpRequest *);
-static int peerWouldBePinged(const CachePeer *, HttpRequest *);
+bool peerAllowedToUse(const CachePeer *, PeerSelector *);
+static int peerWouldBePinged(const CachePeer *, PeerSelector *);
 static void neighborRemove(CachePeer *);
 static void neighborAlive(CachePeer *, const MemObject *, const icp_common_t *);
 #if USE_HTCP
@@ -58,11 +58,13 @@ static void neighborAliveHtcp(CachePeer *, const MemObject *, const HtcpReplyDat
 static void neighborCountIgnored(CachePeer *);
 static void peerRefreshDNS(void *);
 static IPH peerDNSConfigure;
-static bool peerProbeConnect(CachePeer *);
+static void peerProbeConnect(CachePeer *, const bool reprobeIfBusy = false);
 static CNCB peerProbeConnectDone;
 static void peerCountMcastPeersDone(void *data);
 static void peerCountMcastPeersStart(void *data);
 static void peerCountMcastPeersSchedule(CachePeer * p, time_t when);
+static void peerCountMcastPeersAbort(PeerSelector *);
+static void peerCountMcastPeersCreateAndSend(CachePeer *p);
 static IRCB peerCountHandleIcpReply;
 
 static void neighborIgnoreNonPeer(const Ip::Address &, icp_opcode);
@@ -110,13 +112,13 @@ whichPeer(const Ip::Address &from)
 }
 
 peer_t
-neighborType(const CachePeer * p, const HttpRequest * request)
+neighborType(const CachePeer * p, const AnyP::Uri &url)
 {
 
     const NeighborTypeDomainList *d = NULL;
 
     for (d = p->typelist; d; d = d->next) {
-        if (0 == matchDomainName(request->GetHost(), d->domain))
+        if (0 == matchDomainName(url.host(), d->domain))
             if (d->type != PEER_NONE)
                 return d->type;
     }
@@ -133,16 +135,17 @@ neighborType(const CachePeer * p, const HttpRequest * request)
  * \return Whether it is appropriate to fetch REQUEST from PEER.
  */
 bool
-peerAllowedToUse(const CachePeer * p, HttpRequest * request)
+peerAllowedToUse(const CachePeer * p, PeerSelector * ps)
 {
-
+    assert(ps);
+    HttpRequest *request = ps->request;
     assert(request != NULL);
 
-    if (neighborType(p, request) == PEER_SIBLING) {
+    if (neighborType(p, request->url) == PEER_SIBLING) {
 #if PEER_MULTICAST_SIBLINGS
         if (p->type == PEER_MULTICAST && p->options.mcast_siblings &&
                 (request->flags.noCache || request->flags.refresh || request->flags.loopDetected || request->flags.needValidation))
-            debugs(15, 2, "peerAllowedToUse(" << p->name << ", " << request->GetHost() << ") : multicast-siblings optimization match");
+            debugs(15, 2, "peerAllowedToUse(" << p->name << ", " << request->url.authority() << ") : multicast-siblings optimization match");
 #endif
         if (request->flags.noCache)
             return false;
@@ -159,21 +162,29 @@ peerAllowedToUse(const CachePeer * p, HttpRequest * request)
 
     // CONNECT requests are proxy requests. Not to be forwarded to origin servers.
     // Unless the destination port matches, in which case we MAY perform a 'DIRECT' to this CachePeer.
-    if (p->options.originserver && request->method == Http::METHOD_CONNECT && request->port != p->in_addr.port())
+    if (p->options.originserver && request->method == Http::METHOD_CONNECT && request->url.port() != p->http_port)
         return false;
 
     if (p->access == NULL)
         return true;
 
     ACLFilledChecklist checklist(p->access, request, NULL);
-
-    return (checklist.fastCheck() == ACCESS_ALLOWED);
+    checklist.al = ps->al;
+    if (ps->al && ps->al->reply) {
+        checklist.reply = ps->al->reply.getRaw();
+        HTTPMSGLOCK(checklist.reply);
+    }
+    checklist.syncAle(request, nullptr);
+    return checklist.fastCheck().allowed();
 }
 
 /* Return TRUE if it is okay to send an ICP request to this CachePeer.   */
 static int
-peerWouldBePinged(const CachePeer * p, HttpRequest * request)
+peerWouldBePinged(const CachePeer * p, PeerSelector * ps)
 {
+    assert(ps);
+    HttpRequest *request = ps->request;
+
     if (p->icp.port == 0)
         return 0;
 
@@ -195,7 +206,7 @@ peerWouldBePinged(const CachePeer * p, HttpRequest * request)
         if (!request->flags.hierarchical)
             return 0;
 
-    if (!peerAllowedToUse(p, request))
+    if (!peerAllowedToUse(p, ps))
         return 0;
 
     /* Ping dead peers every timeout interval */
@@ -244,12 +255,12 @@ peerConnClosed(CachePeer *p)
 
 /* Return TRUE if it is okay to send an HTTP request to this CachePeer. */
 int
-peerHTTPOkay(const CachePeer * p, HttpRequest * request)
+peerHTTPOkay(const CachePeer * p, PeerSelector * ps)
 {
     if (!peerCanOpenMore(p) && !peerHasConnAvailable(p))
         return 0;
 
-    if (!peerAllowedToUse(p, request))
+    if (!peerAllowedToUse(p, ps))
         return 0;
 
     if (!neighborUp(p))
@@ -259,13 +270,13 @@ peerHTTPOkay(const CachePeer * p, HttpRequest * request)
 }
 
 int
-neighborsCount(HttpRequest * request)
+neighborsCount(PeerSelector *ps)
 {
     CachePeer *p = NULL;
     int count = 0;
 
     for (p = Config.peers; p; p = p->next)
-        if (peerWouldBePinged(p, request))
+        if (peerWouldBePinged(p, ps))
             ++count;
 
     debugs(15, 3, "neighborsCount: " << count);
@@ -274,18 +285,21 @@ neighborsCount(HttpRequest * request)
 }
 
 CachePeer *
-getFirstUpParent(HttpRequest * request)
+getFirstUpParent(PeerSelector *ps)
 {
+    assert(ps);
+    HttpRequest *request = ps->request;
+
     CachePeer *p = NULL;
 
     for (p = Config.peers; p; p = p->next) {
         if (!neighborUp(p))
             continue;
 
-        if (neighborType(p, request) != PEER_PARENT)
+        if (neighborType(p, request->url) != PEER_PARENT)
             continue;
 
-        if (!peerHTTPOkay(p, request))
+        if (!peerHTTPOkay(p, ps))
             continue;
 
         break;
@@ -296,8 +310,11 @@ getFirstUpParent(HttpRequest * request)
 }
 
 CachePeer *
-getRoundRobinParent(HttpRequest * request)
+getRoundRobinParent(PeerSelector *ps)
 {
+    assert(ps);
+    HttpRequest *request = ps->request;
+
     CachePeer *p;
     CachePeer *q = NULL;
 
@@ -305,10 +322,10 @@ getRoundRobinParent(HttpRequest * request)
         if (!p->options.roundrobin)
             continue;
 
-        if (neighborType(p, request) != PEER_PARENT)
+        if (neighborType(p, request->url) != PEER_PARENT)
             continue;
 
-        if (!peerHTTPOkay(p, request))
+        if (!peerHTTPOkay(p, ps))
             continue;
 
         if (p->weight == 0)
@@ -335,8 +352,11 @@ getRoundRobinParent(HttpRequest * request)
 }
 
 CachePeer *
-getWeightedRoundRobinParent(HttpRequest * request)
+getWeightedRoundRobinParent(PeerSelector *ps)
 {
+    assert(ps);
+    HttpRequest *request = ps->request;
+
     CachePeer *p;
     CachePeer *q = NULL;
     int weighted_rtt;
@@ -345,10 +365,10 @@ getWeightedRoundRobinParent(HttpRequest * request)
         if (!p->options.weighted_roundrobin)
             continue;
 
-        if (neighborType(p, request) != PEER_PARENT)
+        if (neighborType(p, request->url) != PEER_PARENT)
             continue;
 
-        if (!peerHTTPOkay(p, request))
+        if (!peerHTTPOkay(p, ps))
             continue;
 
         if (q && q->rr_count < p->rr_count)
@@ -362,7 +382,7 @@ getWeightedRoundRobinParent(HttpRequest * request)
             if (!p->options.weighted_roundrobin)
                 continue;
 
-            if (neighborType(p, request) != PEER_PARENT)
+            if (neighborType(p, request->url) != PEER_PARENT)
                 continue;
 
             p->rr_count = 0;
@@ -390,8 +410,9 @@ getWeightedRoundRobinParent(HttpRequest * request)
  * period. The larger the number of requests between cycled resets the
  * more balanced the operations.
  *
- \param data    unused.
- \todo Make the reset timing a selectable parameter in squid.conf
+ * \param data    unused
+ *
+ * TODO: Make the reset timing a selectable parameter in squid.conf
  */
 static void
 peerClearRRLoop(void *data)
@@ -450,18 +471,21 @@ peerAlive(CachePeer *p)
 }
 
 CachePeer *
-getDefaultParent(HttpRequest * request)
+getDefaultParent(PeerSelector *ps)
 {
+    assert(ps);
+    HttpRequest *request = ps->request;
+
     CachePeer *p = NULL;
 
     for (p = Config.peers; p; p = p->next) {
-        if (neighborType(p, request) != PEER_PARENT)
+        if (neighborType(p, request->url) != PEER_PARENT)
             continue;
 
         if (!p->options.default_parent)
             continue;
 
-        if (!peerHTTPOkay(p, request))
+        if (!peerHTTPOkay(p, ps))
             continue;
 
         debugs(15, 3, "getDefaultParent: returning " << p->host);
@@ -548,9 +572,8 @@ neighbors_init(void)
                 if (thisPeer->http_port != s->s.port())
                     continue;
 
-                debugs(15, DBG_IMPORTANT, "WARNING: Peer looks like this host");
-
-                debugs(15, DBG_IMPORTANT, "         Ignoring " <<
+                debugs(15, DBG_IMPORTANT, "WARNING: Peer looks like this host." <<
+                       Debug::Extra << "Ignoring " <<
                        neighborTypeStr(thisPeer) << " " << thisPeer->host <<
                        "/" << thisPeer->http_port << "/" <<
                        thisPeer->icp.port);
@@ -572,7 +595,7 @@ int
 neighborsUdpPing(HttpRequest * request,
                  StoreEntry * entry,
                  IRCB * callback,
-                 void *callback_data,
+                 PeerSelector *ps,
                  int *exprep,
                  int *timeout)
 {
@@ -582,7 +605,6 @@ neighborsUdpPing(HttpRequest * request,
     int i;
     int reqnum = 0;
     int flags;
-    icp_common_t *query;
     int queries_sent = 0;
     int peers_pinged = 0;
     int parent_timeout = 0, parent_exprep = 0;
@@ -592,13 +614,13 @@ neighborsUdpPing(HttpRequest * request,
     if (Config.peers == NULL)
         return 0;
 
-    assert(entry->swap_status == SWAPOUT_NONE);
+    assert(!entry->hasDisk());
 
     mem->start_ping = current_time;
 
     mem->ping_reply_callback = callback;
 
-    mem->ircb_data = callback_data;
+    mem->ircb_data = ps;
 
     reqnum = icpSetCacheKey((const cache_key *)entry->key);
 
@@ -608,7 +630,7 @@ neighborsUdpPing(HttpRequest * request,
 
         debugs(15, 5, "neighborsUdpPing: Peer " << p->host);
 
-        if (!peerWouldBePinged(p, request))
+        if (!peerWouldBePinged(p, ps))
             continue;       /* next CachePeer */
 
         ++peers_pinged;
@@ -642,8 +664,9 @@ neighborsUdpPing(HttpRequest * request,
 
                 if (p->icp.port == echo_port) {
                     debugs(15, 4, "neighborsUdpPing: Looks like a dumb cache, send DECHO ping");
-                    query = _icp_common_t::createMessage(ICP_DECHO, 0, url, reqnum, 0);
-                    icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0);
+                    // TODO: Get ALE from callback_data if possible.
+                    icpCreateAndSend(ICP_DECHO, 0, url, reqnum, 0,
+                                     icpOutgoingConn->fd, p->in_addr, nullptr);
                 } else {
                     flags = 0;
 
@@ -651,9 +674,9 @@ neighborsUdpPing(HttpRequest * request,
                         if (p->icp.version == ICP_VERSION_2)
                             flags |= ICP_FLAG_SRC_RTT;
 
-                    query = _icp_common_t::createMessage(ICP_QUERY, flags, url, reqnum, 0);
-
-                    icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0.0);
+                    // TODO: Get ALE from callback_data if possible.
+                    icpCreateAndSend(ICP_QUERY, flags, url, reqnum, 0,
+                                     icpOutgoingConn->fd, p->in_addr, nullptr);
                 }
             }
         }
@@ -668,7 +691,7 @@ neighborsUdpPing(HttpRequest * request,
         } else if (neighborUp(p)) {
             /* its alive, expect a reply from it */
 
-            if (neighborType(p, request) == PEER_PARENT) {
+            if (neighborType(p, request->url) == PEER_PARENT) {
                 ++parent_exprep;
                 parent_timeout += p->stats.rtt;
             } else {
@@ -733,9 +756,11 @@ neighborsUdpPing(HttpRequest * request,
 
 /* lookup the digest of a given CachePeer */
 lookup_t
-peerDigestLookup(CachePeer * p, HttpRequest * request)
+peerDigestLookup(CachePeer * p, PeerSelector * ps)
 {
 #if USE_CACHE_DIGESTS
+    assert(ps);
+    HttpRequest *request = ps->request;
     const cache_key *key = request ? storeKeyPublicByRequest(request) : NULL;
     assert(p);
     assert(request);
@@ -745,7 +770,7 @@ peerDigestLookup(CachePeer * p, HttpRequest * request)
     if (!p->digest) {
         debugs(15, 5, "peerDigestLookup: gone!");
         return LOOKUP_NONE;
-    } else if (!peerHTTPOkay(p, request)) {
+    } else if (!peerHTTPOkay(p, ps)) {
         debugs(15, 5, "peerDigestLookup: !peerHTTPOkay");
         return LOOKUP_NONE;
     } else if (!p->digest->flags.needed) {
@@ -761,7 +786,7 @@ peerDigestLookup(CachePeer * p, HttpRequest * request)
     assert(p->digest->cd);
     /* does digest predict a hit? */
 
-    if (!cacheDigestTest(p->digest->cd, key))
+    if (!p->digest->cd->contains(key))
         return LOOKUP_MISS;
 
     debugs(15, 5, "peerDigestLookup: peer " << p->host << " says HIT!");
@@ -775,10 +800,12 @@ peerDigestLookup(CachePeer * p, HttpRequest * request)
 
 /* select best CachePeer based on cache digests */
 CachePeer *
-neighborsDigestSelect(HttpRequest * request)
+neighborsDigestSelect(PeerSelector *ps)
 {
     CachePeer *best_p = NULL;
 #if USE_CACHE_DIGESTS
+    assert(ps);
+    HttpRequest *request = ps->request;
 
     int best_rtt = 0;
     int choice_count = 0;
@@ -801,7 +828,7 @@ neighborsDigestSelect(HttpRequest * request)
         if (i == 1)
             first_ping = p;
 
-        lookup = peerDigestLookup(p, request);
+        lookup = peerDigestLookup(p, ps);
 
         if (lookup == LOOKUP_NONE)
             continue;
@@ -951,7 +978,7 @@ neighborIgnoreNonPeer(const Ip::Address &from, icp_opcode opcode)
  * * from being used
  */
 static int
-ignoreMulticastReply(CachePeer * p, MemObject * mem)
+ignoreMulticastReply(CachePeer * p, PeerSelector * ps)
 {
     if (p == NULL)
         return 0;
@@ -959,7 +986,7 @@ ignoreMulticastReply(CachePeer * p, MemObject * mem)
     if (!p->options.mcast_responder)
         return 0;
 
-    if (peerHTTPOkay(p, mem->request))
+    if (peerHTTPOkay(p, ps))
         return 0;
 
     return 1;
@@ -983,7 +1010,7 @@ neighborsUdpAck(const cache_key * key, icp_common_t * header, const Ip::Address
 
     debugs(15, 6, "neighborsUdpAck: opcode " << opcode << " '" << storeKeyText(key) << "'");
 
-    if (NULL != (entry = Store::Root().get(key)))
+    if ((entry = Store::Root().findCallbackXXX(key)))
         mem = entry->mem_obj;
 
     if ((p = whichPeer(from)))
@@ -1030,13 +1057,19 @@ neighborsUdpAck(const cache_key * key, icp_common_t * header, const Ip::Address
         return;
     }
 
+    if (!mem->ircb_data) {
+        debugs(12, DBG_IMPORTANT, "BUG: missing ICP callback data for " << *entry);
+        neighborCountIgnored(p);
+        return;
+    }
+
     debugs(15, 3, "neighborsUdpAck: " << opcode_d << " for '" << storeKeyText(key) << "' from " << (p ? p->host : "source") << " ");
 
     if (p) {
-        ntype = neighborType(p, mem->request);
+        ntype = neighborType(p, mem->request->url);
     }
 
-    if (ignoreMulticastReply(p, mem)) {
+    if (ignoreMulticastReply(p, mem->ircb_data)) {
         neighborCountIgnored(p);
     } else if (opcode == ICP_MISS) {
         if (p == NULL) {
@@ -1122,10 +1155,8 @@ int
 neighborUp(const CachePeer * p)
 {
     if (!p->tcp_up) {
-        if (!peerProbeConnect((CachePeer *) p)) {
-            debugs(15, 8, "neighborUp: DOWN (probed): " << p->host << " (" << p->in_addr << ")");
-            return 0;
-        }
+        peerProbeConnect(const_cast<CachePeer*>(p));
+        return 0;
     }
 
     /*
@@ -1152,12 +1183,10 @@ neighborUp(const CachePeer * p)
     return 1;
 }
 
-void
-peerNoteDigestGone(CachePeer * p)
+time_t
+positiveTimeout(const time_t timeout)
 {
-#if USE_CACHE_DIGESTS
-    cbdataReferenceDone(p->digest);
-#endif
+    return max(static_cast<time_t>(1), timeout);
 }
 
 static void
@@ -1168,8 +1197,6 @@ peerDNSConfigure(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data
 
     CachePeer *p = (CachePeer *)data;
 
-    int j;
-
     if (p->n_addresses == 0) {
         debugs(15, DBG_IMPORTANT, "Configuring " << neighborTypeStr(p) << " " << p->host << "/" << p->http_port << "/" << p->icp.port);
 
@@ -1184,23 +1211,28 @@ peerDNSConfigure(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data
         return;
     }
 
-    if ((int) ia->count < 1) {
+    if (ia->empty()) {
         debugs(0, DBG_CRITICAL, "WARNING: No IP address found for '" << p->host << "'!");
         return;
     }
 
-    p->tcp_up = p->connect_fail_limit;
-
-    for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; ++j) {
-        p->addresses[j] = ia->in_addrs[j];
-        debugs(15, 2, "--> IP address #" << j << ": " << p->addresses[j]);
-        ++ p->n_addresses;
+    for (const auto &ip: ia->goodAndBad()) { // TODO: Consider using just good().
+        if (p->n_addresses < PEER_MAX_ADDRESSES) {
+            const auto idx = p->n_addresses++;
+            p->addresses[idx] = ip;
+            debugs(15, 2, "--> IP address #" << idx << ": " << p->addresses[idx]);
+        } else {
+            debugs(15, 3, "ignoring remaining " << (ia->size() - p->n_addresses) << " ips");
+            break;
+        }
     }
 
     p->in_addr.setEmpty();
     p->in_addr = p->addresses[0];
     p->in_addr.port(p->icp.port);
 
+    peerProbeConnect(p, true); // detect any died or revived peers ASAP
+
     if (p->type == PEER_MULTICAST)
         peerCountMcastPeersSchedule(p, 10);
 
@@ -1265,7 +1297,7 @@ void
 peerConnectSucceded(CachePeer * p)
 {
     if (!p->tcp_up) {
-        debugs(15, 2, "TCP connection to " << p->host << "/" << p->http_port << " succeded");
+        debugs(15, 2, "TCP connection to " << p->host << "/" << p->http_port << " succeeded");
         p->tcp_up = p->connect_fail_limit; // NP: so peerAlive(p) works properly.
         peerAlive(p);
         if (!p->n_addresses)
@@ -1274,21 +1306,33 @@ peerConnectSucceded(CachePeer * p)
         p->tcp_up = p->connect_fail_limit;
 }
 
+/// whether new TCP probes are currently banned
+static bool
+peerProbeIsBusy(const CachePeer *p)
+{
+    if (p->testing_now > 0) {
+        debugs(15, 8, "yes, probing " << p);
+        return true;
+    }
+    if (squid_curtime - p->stats.last_connect_probe == 0) {
+        debugs(15, 8, "yes, just probed " << p);
+        return true;
+    }
+    return false;
+}
 /*
 * peerProbeConnect will be called on dead peers by neighborUp
 */
-static bool
-peerProbeConnect(CachePeer * p)
+static void
+peerProbeConnect(CachePeer *p, const bool reprobeIfBusy)
 {
-    time_t ctimeout = p->connect_timeout > 0 ? p->connect_timeout : Config.Timeout.peer_connect;
-    bool ret = (squid_curtime - p->stats.last_connect_failure) > (ctimeout * 10);
-
-    if (p->testing_now > 0)
-        return ret;/* probe already running */
-
-    if (squid_curtime - p->stats.last_connect_probe == 0)
-        return ret;/* don't probe to often */
+    if (peerProbeIsBusy(p)) {
+        p->reprobe = reprobeIfBusy;
+        return;
+    }
+    p->reprobe = false;
 
+    const auto ctimeout = p->connectTimeout();
     /* for each IP address of this CachePeer. find one that we can connect to and probe it. */
     for (int i = 0; i < p->n_addresses; ++i) {
         Comm::ConnectionPointer conn = new Comm::Connection;
@@ -1306,8 +1350,6 @@ peerProbeConnect(CachePeer * p)
     }
 
     p->stats.last_connect_probe = squid_curtime;
-
-    return ret;
 }
 
 static void
@@ -1324,6 +1366,9 @@ peerProbeConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int
     -- p->testing_now;
     conn->close();
     // TODO: log this traffic.
+
+    if (p->reprobe)
+        peerProbeConnect(p);
 }
 
 static void
@@ -1343,55 +1388,79 @@ peerCountMcastPeersSchedule(CachePeer * p, time_t when)
 static void
 peerCountMcastPeersStart(void *data)
 {
-    CachePeer *p = (CachePeer *)data;
-    ps_state *psstate;
-    StoreEntry *fake;
+    const auto peer = static_cast<CachePeer*>(data);
+    CallContextCreator([peer] {
+        peerCountMcastPeersCreateAndSend(peer);
+    });
+    peerCountMcastPeersSchedule(peer, MCAST_COUNT_RATE);
+}
+
+/// initiates an ICP transaction to a multicast peer
+static void
+peerCountMcastPeersCreateAndSend(CachePeer * const p)
+{
+    // XXX: Do not create lots of complex fake objects (while abusing their
+    // APIs) to pass around a few basic data points like start_ping and ping!
     MemObject *mem;
-    icp_common_t *query;
     int reqnum;
+    // TODO: use class AnyP::Uri instead of constructing and re-parsing a string
     LOCAL_ARRAY(char, url, MAX_URL);
     assert(p->type == PEER_MULTICAST);
     p->mcast.flags.count_event_pending = false;
     snprintf(url, MAX_URL, "http://");
     p->in_addr.toUrl(url+7, MAX_URL -8 );
     strcat(url, "/");
-    fake = storeCreateEntry(url, url, RequestFlags(), Http::METHOD_GET);
-    HttpRequest *req = HttpRequest::CreateFromUrl(url);
-    psstate = new ps_state;
+    const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initPeerMcast);
+    auto *req = HttpRequest::FromUrlXXX(url, mx);
+    assert(req != nullptr);
+    const AccessLogEntry::Pointer ale = new AccessLogEntry;
+    ale->request = req;
+    CodeContext::Reset(ale);
+    StoreEntry *fake = storeCreateEntry(url, url, RequestFlags(), Http::METHOD_GET);
+    const auto psstate = new PeerSelector(nullptr);
     psstate->request = req;
     HTTPMSGLOCK(psstate->request);
     psstate->entry = fake;
-    psstate->callback = NULL;
-    psstate->callback_data = cbdataReference(p);
+    psstate->peerCountMcastPeerXXX = cbdataReference(p);
     psstate->ping.start = current_time;
+    psstate->al = ale;
     mem = fake->mem_obj;
     mem->request = psstate->request;
-    HTTPMSGLOCK(mem->request);
     mem->start_ping = current_time;
     mem->ping_reply_callback = peerCountHandleIcpReply;
     mem->ircb_data = psstate;
     mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
     p->mcast.id = mem->id;
     reqnum = icpSetCacheKey((const cache_key *)fake->key);
-    query = _icp_common_t::createMessage(ICP_QUERY, 0, url, reqnum, 0);
-    icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0);
-    fake->ping_status = PING_WAITING;
+    icpCreateAndSend(ICP_QUERY, 0, url, reqnum, 0,
+                     icpOutgoingConn->fd, p->in_addr, psstate->al);
+    fake->ping_status = PING_WAITING; // TODO: refactor to use PeerSelector::startPingWaiting()
     eventAdd("peerCountMcastPeersDone",
              peerCountMcastPeersDone,
              psstate,
              Config.Timeout.mcast_icp_query / 1000.0, 1);
     p->mcast.flags.counting = true;
-    peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
 }
 
 static void
 peerCountMcastPeersDone(void *data)
 {
-    ps_state *psstate = (ps_state *)data;
+    const auto psstate = static_cast<PeerSelector*>(data);
+    CallBack(psstate->al, [psstate] {
+        peerCountMcastPeersAbort(psstate);
+        delete psstate;
+    });
+}
+
+/// ends counting of multicast ICP replies
+/// to the ICP query initiated by peerCountMcastPeersCreateAndSend()
+static void
+peerCountMcastPeersAbort(PeerSelector * const psstate)
+{
     StoreEntry *fake = psstate->entry;
 
-    if (cbdataReferenceValid(psstate->callback_data)) {
-        CachePeer *p = (CachePeer *)psstate->callback_data;
+    if (cbdataReferenceValid(psstate->peerCountMcastPeerXXX)) {
+        CachePeer *p = (CachePeer *)psstate->peerCountMcastPeerXXX;
         p->mcast.flags.counting = false;
         p->mcast.avg_n_members = Math::doubleAverage(p->mcast.avg_n_members, (double) psstate->ping.n_recv, ++p->mcast.n_times_counted, 10);
         debugs(15, DBG_IMPORTANT, "Group " << p->host  << ": " << psstate->ping.n_recv  <<
@@ -1400,18 +1469,17 @@ peerCountMcastPeersDone(void *data)
         p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
     }
 
-    cbdataReferenceDone(psstate->callback_data);
+    cbdataReferenceDone(psstate->peerCountMcastPeerXXX);
 
-    fake->abort(); // sets ENTRY_ABORTED and initiates releated cleanup
-    HTTPMSGUNLOCK(fake->mem_obj->request);
+    fake->abort(); // sets ENTRY_ABORTED and initiates related cleanup
+    fake->mem_obj->request = nullptr;
     fake->unlock("peerCountMcastPeersDone");
-    delete psstate;
 }
 
 static void
 peerCountHandleIcpReply(CachePeer * p, peer_t, AnyP::ProtocolType proto, void *, void *data)
 {
-    ps_state *psstate = (ps_state *)data;
+    const auto psstate = static_cast<PeerSelector*>(data);
     StoreEntry *fake = psstate->entry;
     assert(fake);
     MemObject *mem = fake->mem_obj;
@@ -1492,15 +1560,23 @@ dump_peer_options(StoreEntry * sentry, CachePeer * p)
     if (p->options.htcp) {
         storeAppendPrintf(sentry, " htcp");
         if (p->options.htcp_oldsquid || p->options.htcp_no_clr || p->options.htcp_no_purge_clr || p->options.htcp_only_clr) {
-            int doneopts=0;
-            if (p->options.htcp_oldsquid)
-                storeAppendPrintf(sentry, "%soldsquid",(doneopts++>0?",":"="));
-            if (p->options.htcp_no_clr)
-                storeAppendPrintf(sentry, "%sno-clr",(doneopts++>0?",":"="));
-            if (p->options.htcp_no_purge_clr)
-                storeAppendPrintf(sentry, "%sno-purge-clr",(doneopts++>0?",":"="));
-            if (p->options.htcp_only_clr)
-                storeAppendPrintf(sentry, "%sonly-clr",(doneopts++>0?",":"="));
+            bool doneopts = false;
+            if (p->options.htcp_oldsquid) {
+                storeAppendPrintf(sentry, "oldsquid");
+                doneopts = true;
+            }
+            if (p->options.htcp_no_clr) {
+                storeAppendPrintf(sentry, "%sno-clr",(doneopts?",":"="));
+                doneopts = true;
+            }
+            if (p->options.htcp_no_purge_clr) {
+                storeAppendPrintf(sentry, "%sno-purge-clr",(doneopts?",":"="));
+                doneopts = true;
+            }
+            if (p->options.htcp_only_clr) {
+                storeAppendPrintf(sentry, "%sonly-clr",(doneopts?",":"="));
+                //doneopts = true; // uncomment if more opts are added
+            }
         }
     }
 #endif
@@ -1519,8 +1595,8 @@ dump_peer_options(StoreEntry * sentry, CachePeer * p)
     if (p->mcast.ttl > 0)
         storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl);
 
-    if (p->connect_timeout > 0)
-        storeAppendPrintf(sentry, " connect-timeout=%d", (int) p->connect_timeout);
+    if (p->connect_timeout_raw > 0)
+        storeAppendPrintf(sentry, " connect-timeout=%d", (int)p->connect_timeout_raw);
 
     if (p->connect_fail_limit != PEER_TCP_MAGIC_COUNT)
         storeAppendPrintf(sentry, " connect-fail-limit=%d", p->connect_fail_limit);
@@ -1556,6 +1632,7 @@ dump_peer_options(StoreEntry * sentry, CachePeer * p)
     else if (p->connection_auth == 2)
         storeAppendPrintf(sentry, " connection-auth=auto");
 
+    p->secure.dumpCfg(sentry,"tls-");
     storeAppendPrintf(sentry, "\n");
 }
 
@@ -1563,7 +1640,6 @@ static void
 dump_peers(StoreEntry * sentry, CachePeer * peers)
 {
     char ntoabuf[MAX_IPSTRLEN];
-    icp_opcode op;
     int i;
 
     if (peers == NULL)
@@ -1625,7 +1701,7 @@ dump_peers(StoreEntry * sentry, CachePeer * peers)
             } else {
 #endif
 
-                for (op = ICP_INVALID; op < ICP_END; ++op) {
+                for (auto op : WholeEnum<icp_opcode>()) {
                     if (e->icp.counts[op] == 0)
                         continue;
 
@@ -1656,7 +1732,7 @@ dump_peers(StoreEntry * sentry, CachePeer * peers)
 void
 neighborsHtcpReply(const cache_key * key, HtcpReplyData * htcp, const Ip::Address &from)
 {
-    StoreEntry *e = Store::Root().get(key);
+    StoreEntry *e = Store::Root().findCallbackXXX(key);
     MemObject *mem = NULL;
     CachePeer *p;
     peer_t ntype = PEER_NONE;
@@ -1703,17 +1779,24 @@ neighborsHtcpReply(const cache_key * key, HtcpReplyData * htcp, const Ip::Addres
         return;
     }
 
+    if (!mem->ircb_data) {
+        debugs(12, DBG_IMPORTANT, "BUG: missing HTCP callback data for " << *e);
+        neighborCountIgnored(p);
+        return;
+    }
+
     if (p) {
-        ntype = neighborType(p, mem->request);
+        ntype = neighborType(p, mem->request->url);
         neighborUpdateRtt(p, mem);
     }
 
-    if (ignoreMulticastReply(p, mem)) {
+    if (ignoreMulticastReply(p, mem->ircb_data)) {
         neighborCountIgnored(p);
         return;
     }
 
     debugs(15, 3, "neighborsHtcpReply: e = " << e);
+    // TODO: Refactor (ping_reply_callback,ircb_data) to add CodeContext.
     mem->ping_reply_callback(p, ntype, AnyP::PROTO_HTCP, htcp, mem->ircb_data);
 }
 
@@ -1721,7 +1804,7 @@ neighborsHtcpReply(const cache_key * key, HtcpReplyData * htcp, const Ip::Addres
  * Send HTCP CLR messages to all peers configured to receive them.
  */
 void
-neighborsHtcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
+neighborsHtcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
 {
     CachePeer *p;
     char buf[128];
@@ -1737,7 +1820,7 @@ neighborsHtcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const Htt
             continue;
         }
         debugs(15, 3, "neighborsHtcpClear: sending CLR to " << p->in_addr.toUrl(buf, 128));
-        htcpClear(e, uri, req, method, p, reason);
+        htcpClear(e, req, method, p, reason);
     }
 }