]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Moved peer*(ps_state) functions into ps_state renamed PeerSelector (#113)
authorAlex Rousskov <rousskov@measurement-factory.com>
Tue, 2 Jan 2018 16:16:52 +0000 (09:16 -0700)
committerGitHub <noreply@github.com>
Tue, 2 Jan 2018 16:16:52 +0000 (09:16 -0700)
No functionality changes intended (other than debug message variations).

Also polished related documentation and debug messages.

Also converted "struct _icp_common_t" into an icp_common_t class, to
make its forward declarations simple. As a side effect, removed
__cplusplus ifdefs, addressing an old TODO.

src/ICP.h
src/PeerSelectState.h
src/icp_v2.cc
src/neighbors.cc
src/peer_select.cc
src/tests/stub_icp.cc

index 2a4bd52518c0c597c93c5b251fdb51a267ce7e8c..74ce944433a8ca22d8a00fcb3c948fe43b8aeafd 100644 (file)
--- a/src/ICP.h
+++ b/src/ICP.h
 class HttpRequest;
 
 /**
- \ingroup ServerProtocolICPAPI
- *
- * This struct is the wire-level header.
- * DO NOT add more move fields on pain of breakage.
+ * Wire-level ICP header.
+ * DO NOT add or move fields.
  * DO NOT add virtual methods.
  */
-struct _icp_common_t {
+class icp_common_t {
+public:
     /** opcode */
     unsigned char opcode;
     /** version number */
@@ -44,21 +43,15 @@ struct _icp_common_t {
     /** sender host id */
     uint32_t shostid;
 
-/// \todo I don't believe this header is included in non-c++ code anywhere
-///     the struct should become a public POD class and kill these ifdef.
-#ifdef __cplusplus
-
-    _icp_common_t();
-    _icp_common_t(char *buf, unsigned int len);
+    icp_common_t();
+    icp_common_t(char *buf, unsigned int len);
 
     void handleReply(char *buf, Ip::Address &from);
-    static _icp_common_t *createMessage(icp_opcode opcode, int flags, const char *url, int reqnum, int pad);
     icp_opcode getOpCode() const;
-#endif
-};
-typedef struct _icp_common_t icp_common_t;
 
-#ifdef __cplusplus
+    /// \returns newly allocated buffer with an ICP message, including header
+    static icp_common_t *CreateMessage(icp_opcode opcode, int flags, const char *url, int reqnum, int pad);
+};
 
 /**
  \ingroup ServerProtocolICPAPI
@@ -78,8 +71,6 @@ public:
     char *url;
 };
 
-#endif
-
 /// \ingroup ServerProtocolICPAPI
 struct icpUdpData {
 
index 24c544a5055c5c09e95ffa3f916aa920461bbb7d..c682b094c211e31700b6b8216554d4fc8fafc5d2 100644 (file)
 #include "mem/forward.h"
 #include "PingData.h"
 
+class ErrorState;
+class HtcpReplyData;
 class HttpRequest;
+class icp_common_t;
 class StoreEntry;
-class ErrorState;
 
 void peerSelectInit(void);
 
@@ -51,13 +53,15 @@ public:
 
 class FwdServer;
 
-class ps_state: public Dns::IpReceiver
+/// Finds peer (including origin server) IPs for forwarding a single request.
+/// Gives PeerSelectionInitiator each found destination, in the right order.
+class PeerSelector: public Dns::IpReceiver
 {
-    CBDATA_CHILD(ps_state);
+    CBDATA_CHILD(PeerSelector);
 
 public:
-    explicit ps_state(PeerSelectionInitiator *initiator);
-    virtual ~ps_state() override;
+    explicit PeerSelector(PeerSelectionInitiator*);
+    virtual ~PeerSelector() override;
 
     /* Dns::IpReceiver API */
     virtual void noteIp(const Ip::Address &ip) override;
@@ -77,17 +81,56 @@ public:
     /// processes a newly discovered/finalized path
     void handlePath(Comm::ConnectionPointer &path, FwdServer &fs);
 
+    /// a single selection loop iteration: attempts to add more destinations
+    void selectMore();
+
     HttpRequest *request;
     AccessLogEntry::Pointer al; ///< info for the future access.log entry
     StoreEntry *entry;
+
+    void *peerCountMcastPeerXXX = nullptr; ///< a hack to help peerCountMcastPeersStart()
+
+    ping_data ping;
+
+protected:
+    bool selectionAborted();
+
+    void handlePingTimeout();
+    void handleIcpReply(CachePeer*, const peer_t, icp_common_t *header);
+    void handleIcpParentMiss(CachePeer*, icp_common_t*);
+#if USE_HTCP
+    void handleHtcpParentMiss(CachePeer*, HtcpReplyData*);
+    void handleHtcpReply(CachePeer*, const peer_t, HtcpReplyData*);
+#endif
+
+    int checkNetdbDirect();
+    void checkAlwaysDirectDone(const allow_t answer);
+    void checkNeverDirectDone(const allow_t answer);
+
+    void selectSomeNeighbor();
+    void selectSomeNeighborReplies();
+    void selectSomeDirect();
+    void selectSomeParent();
+    void selectAllParents();
+    void selectPinned();
+
+    void addSelection(CachePeer*, const hier_code);
+
+    void resolveSelected();
+
+    static IRCB HandlePingReply;
+    static ACLCB CheckAlwaysDirectDone;
+    static ACLCB CheckNeverDirectDone;
+    static EVH HandlePingTimeout;
+
+private:
     allow_t always_direct;
     allow_t never_direct;
     int direct;   // TODO: fold always_direct/never_direct/prefer_direct into this now that ACL can do a multi-state result.
     size_t foundPaths = 0; ///< number of unique destinations identified so far
-    void *peerCountMcastPeerXXX = nullptr; ///< a hack to help peerCountMcastPeersStart()
     ErrorState *lastError;
 
-    FwdServer *servers;    ///< temporary linked list of peers we will pass back.
+    FwdServer *servers; ///< a linked list of (unresolved) selected peers
 
     /*
      * Why are these Ip::Address instead of CachePeer *?  Because a
@@ -106,15 +149,12 @@ public:
      */
     CachePeer *hit;
     peer_t hit_type;
-    ping_data ping;
     ACLChecklist *acl_checklist;
 
-    const InstanceId<ps_state> id; ///< unique identification in worker log
-
-private:
-
     typedef CbcPointer<PeerSelectionInitiator> Initiator;
     Initiator initiator_; ///< recipient of the destinations we select; use interestedInitiator() to access
+
+    const InstanceId<PeerSelector> id; ///< unique identification in worker log
 };
 
 #endif /* SQUID_PEERSELECTSTATE_H */
index 4cb551eb587240e194be7c5a3fe31eaf1bb9895f..5acf708899c0bb182072c71a622a2f71de62d2b1 100644 (file)
@@ -73,15 +73,15 @@ Comm::ConnectionPointer icpIncomingConn = NULL;
 Comm::ConnectionPointer icpOutgoingConn = NULL;
 
 /* icp_common_t */
-_icp_common_t::_icp_common_t() :
+icp_common_t::icp_common_t() :
     opcode(ICP_INVALID), version(0), length(0), reqnum(0),
     flags(0), pad(0), shostid(0)
 {}
 
-_icp_common_t::_icp_common_t(char *buf, unsigned int len) :
+icp_common_t::icp_common_t(char *buf, unsigned int len) :
     opcode(ICP_INVALID), version(0), reqnum(0), flags(0), pad(0), shostid(0)
 {
-    if (len < sizeof(_icp_common_t)) {
+    if (len < sizeof(icp_common_t)) {
         /* mark as invalid */
         length = len + 1;
         return;
@@ -98,7 +98,7 @@ _icp_common_t::_icp_common_t(char *buf, unsigned int len) :
 }
 
 icp_opcode
-_icp_common_t::getOpCode() const
+icp_common_t::getOpCode() const
 {
     if (opcode > static_cast<char>(icp_opcode::ICP_END))
         return ICP_INVALID;
@@ -228,8 +228,8 @@ icpUdpSendQueue(int fd, void *)
     }
 }
 
-_icp_common_t *
-_icp_common_t::createMessage(
+icp_common_t *
+icp_common_t::CreateMessage(
     icp_opcode opcode,
     int flags,
     const char *url,
@@ -387,7 +387,7 @@ icpLogFromICPCode(icp_opcode opcode)
 void
 icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const Ip::Address &from)
 {
-    icp_common_t *reply = _icp_common_t::createMessage(opcode, flags, url, reqnum, pad);
+    icp_common_t *reply = icp_common_t::CreateMessage(opcode, flags, url, reqnum, pad);
     icpUdpSend(fd, from, reply, icpLogFromICPCode(opcode), 0);
 }
 
@@ -493,7 +493,7 @@ doV2Query(int fd, Ip::Address &from, char *buf, icp_common_t header)
 }
 
 void
-_icp_common_t::handleReply(char *buf, Ip::Address &from)
+icp_common_t::handleReply(char *buf, Ip::Address &from)
 {
     if (neighbors_do_private_keys && reqnum == 0) {
         debugs(12, DBG_CRITICAL, "icpHandleIcpV2: Neighbor " << from << " returned reqnum = 0");
index db5831d551a60f6cd56994cb87baca899de2c86c..dfd16e0bbb3ee82c3407fa3ffacd177b57620ce2 100644 (file)
@@ -643,7 +643,7 @@ 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);
+                    query = icp_common_t::CreateMessage(ICP_DECHO, 0, url, reqnum, 0);
                     icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0);
                 } else {
                     flags = 0;
@@ -652,7 +652,7 @@ 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);
+                    query = icp_common_t::CreateMessage(ICP_QUERY, flags, url, reqnum, 0);
 
                     icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0.0);
                 }
@@ -1375,7 +1375,6 @@ peerCountMcastPeersStart(void *data)
     // 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!
     CachePeer *p = (CachePeer *)data;
-    ps_state *psstate;
     MemObject *mem;
     icp_common_t *query;
     int reqnum;
@@ -1390,7 +1389,7 @@ peerCountMcastPeersStart(void *data)
     HttpRequest *req = HttpRequest::FromUrl(url, mx);
     assert(req != nullptr);
     StoreEntry *fake = storeCreateEntry(url, url, RequestFlags(), Http::METHOD_GET);
-    psstate = new ps_state(nullptr);
+    const auto psstate = new PeerSelector(nullptr);
     psstate->request = req;
     HTTPMSGLOCK(psstate->request);
     psstate->entry = fake;
@@ -1404,7 +1403,7 @@ peerCountMcastPeersStart(void *data)
     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);
+    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;
     eventAdd("peerCountMcastPeersDone",
@@ -1418,7 +1417,7 @@ peerCountMcastPeersStart(void *data)
 static void
 peerCountMcastPeersDone(void *data)
 {
-    ps_state *psstate = (ps_state *)data;
+    const auto psstate = static_cast<PeerSelector*>(data);
     StoreEntry *fake = psstate->entry;
 
     if (cbdataReferenceValid(psstate->peerCountMcastPeerXXX)) {
@@ -1442,7 +1441,7 @@ peerCountMcastPeersDone(void *data)
 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;
index 99d453794abdc3d0e753c47f8b8abaa10e94d8f3..bbe2a28cceb69a4dc632602483ccbb135dfc00f9 100644 (file)
@@ -76,32 +76,15 @@ static const char *DirectStr[] = {
 class PeerSelectionDumper
 {
 public:
-    PeerSelectionDumper(const ps_state * const aPs, const CachePeer * const aPeer, const hier_code aCode):
-        ps(aPs), peer(aPeer), code(aCode) {}
+    PeerSelectionDumper(const PeerSelector * const aSelector, const CachePeer * const aPeer, const hier_code aCode):
+        selector(aSelector), peer(aPeer), code(aCode) {}
 
-    const ps_state * const ps; ///< selection parameters
+    const PeerSelector * const selector; ///< selection parameters
     const CachePeer * const peer; ///< successful selection info
     const hier_code code; ///< selection algorithm
 };
 
-static void peerSelectFoo(ps_state *);
-static void peerPingTimeout(void *data);
-static IRCB peerHandlePingReply;
-static void peerIcpParentMiss(CachePeer *, icp_common_t *, ps_state *);
-#if USE_HTCP
-static void peerHtcpParentMiss(CachePeer *, HtcpReplyData *, ps_state *);
-static void peerHandleHtcpReply(CachePeer *, peer_t, HtcpReplyData *, void *);
-#endif
-static int peerCheckNetdbDirect(ps_state * psstate);
-static void peerGetSomeNeighbor(ps_state *);
-static void peerGetSomeNeighborReplies(ps_state *);
-static void peerGetSomeDirect(ps_state *);
-static void peerGetSomeParent(ps_state *);
-static void peerGetAllParents(ps_state *);
-static void peerAddFwdServer(ps_state*, CachePeer*, const hier_code);
-static void peerSelectPinned(ps_state * ps);
-
-CBDATA_CLASS_INIT(ps_state);
+CBDATA_CLASS_INIT(PeerSelector);
 
 /// prints PeerSelectionDumper (for debugging)
 static std::ostream &
@@ -111,13 +94,13 @@ operator <<(std::ostream &os, const PeerSelectionDumper &fsd)
 
     if (fsd.peer)
         os << '/' << fsd.peer->host;
-    else if (fsd.ps) // useful for DIRECT and gone PINNED destinations
-        os << '#' << fsd.ps->request->url.host();
+    else if (fsd.selector) // useful for DIRECT and gone PINNED destinations
+        os << '#' << fsd.selector->request->url.host();
 
     return os;
 }
 
-ps_state::~ps_state()
+PeerSelector::~PeerSelector()
 {
     while (servers) {
         FwdServer *next = servers->next;
@@ -129,13 +112,13 @@ ps_state::~ps_state()
         debugs(44, 3, entry->url());
 
         if (entry->ping_status == PING_WAITING)
-            eventDelete(peerPingTimeout, this);
+            eventDelete(HandlePingTimeout, this);
 
         entry->ping_status = PING_DONE;
     }
 
     if (acl_checklist) {
-        debugs(44, DBG_IMPORTANT, "calling aclChecklistFree() from ps_state destructor");
+        debugs(44, DBG_IMPORTANT, "BUG: peer selector gone while waiting for a slow ACL");
         delete acl_checklist;
     }
 
@@ -157,7 +140,7 @@ peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
     assert(entry);
     assert(entry->ping_status == PING_NONE);
     assert(direct != DIRECT_YES);
-    debugs(44, 3, "peerSelectIcpPing: " << entry->url());
+    debugs(44, 3, entry->url());
 
     if (!request->flags.hierarchical && direct != DIRECT_NO)
         return 0;
@@ -168,7 +151,7 @@ peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
 
     n = neighborsCount(request);
 
-    debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
+    debugs(44, 3, "counted " << n << " neighbors");
 
     return n;
 }
@@ -184,13 +167,13 @@ peerSelect(PeerSelectionInitiator *initiator,
     else
         debugs(44, 3, request->method);
 
-    const auto psstate = new ps_state(initiator);
+    const auto selector = new PeerSelector(initiator);
 
-    psstate->request = request;
-    HTTPMSGLOCK(psstate->request);
-    psstate->al = al;
+    selector->request = request;
+    HTTPMSGLOCK(selector->request);
+    selector->al = al;
 
-    psstate->entry = entry;
+    selector->entry = entry;
 
 #if USE_CACHE_DIGESTS
 
@@ -198,10 +181,10 @@ peerSelect(PeerSelectionInitiator *initiator,
 
 #endif
 
-    if (psstate->entry)
-        psstate->entry->lock("peerSelect");
+    if (selector->entry)
+        selector->entry->lock("peerSelect");
 
-    peerSelectFoo(psstate);
+    selector->selectMore();
 }
 
 void
@@ -212,18 +195,17 @@ PeerSelectionInitiator::startSelectingDestinations(HttpRequest *request, const A
     // and wait for noteDestination() and/or noteDestinationsEnd() calls
 }
 
-static void
-peerCheckNeverDirectDone(allow_t answer, void *data)
+void
+PeerSelector::checkNeverDirectDone(const allow_t answer)
 {
-    ps_state *psstate = (ps_state *) data;
-    psstate->acl_checklist = NULL;
-    debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
-    psstate->never_direct = answer;
+    acl_checklist = nullptr;
+    debugs(44, 3, answer);
+    never_direct = answer;
     switch (answer) {
     case ACCESS_ALLOWED:
         /** if never_direct says YES, do that. */
-        psstate->direct = DIRECT_NO;
-        debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (never_direct allow)");
+        direct = DIRECT_NO;
+        debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct allow)");
         break;
     case ACCESS_DENIED: // not relevant.
     case ACCESS_DUNNO:  // not relevant.
@@ -232,21 +214,26 @@ peerCheckNeverDirectDone(allow_t answer, void *data)
         debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
         break;
     }
-    peerSelectFoo(psstate);
+    selectMore();
 }
 
-static void
-peerCheckAlwaysDirectDone(allow_t answer, void *data)
+void
+PeerSelector::CheckNeverDirectDone(allow_t answer, void *data)
 {
-    ps_state *psstate = (ps_state *)data;
-    psstate->acl_checklist = NULL;
-    debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
-    psstate->always_direct = answer;
+    static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer);
+}
+
+void
+PeerSelector::checkAlwaysDirectDone(const allow_t answer)
+{
+    acl_checklist = nullptr;
+    debugs(44, 3, answer);
+    always_direct = answer;
     switch (answer) {
     case ACCESS_ALLOWED:
         /** if always_direct says YES, do that. */
-        psstate->direct = DIRECT_YES;
-        debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (always_direct allow)");
+        direct = DIRECT_YES;
+        debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct allow)");
         break;
     case ACCESS_DENIED: // not relevant.
     case ACCESS_DUNNO:  // not relevant.
@@ -255,35 +242,42 @@ peerCheckAlwaysDirectDone(allow_t answer, void *data)
         debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
         break;
     }
-    peerSelectFoo(psstate);
+    selectMore();
+}
+
+void
+PeerSelector::CheckAlwaysDirectDone(allow_t answer, void *data)
+{
+    static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer);
 }
 
-/// \returns true (after destroying psstate) if the peer initiator is gone
+/// \returns true (after destroying "this") if the peer initiator is gone
 /// \returns false (without side effects) otherwise
-static bool
-peerSelectionAborted(ps_state *psstate)
+bool
+PeerSelector::selectionAborted()
 {
-    if (psstate->interestedInitiator())
+    if (interestedInitiator())
         return false;
 
     debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
-    delete psstate;
+    delete this;
     return true;
 }
 
+/// A single DNS resolution loop iteration: Converts selected FwdServer to IPs.
 void
-peerSelectDnsPaths(ps_state *psstate)
+PeerSelector::resolveSelected()
 {
-    if (peerSelectionAborted(psstate))
+    if (selectionAborted())
         return;
 
-    FwdServer *fs = psstate->servers;
+    FwdServer *fs = servers;
 
     // Bug 3243: CVE 2009-0801
     // Bypass of browser same-origin access control in intercepted communication
     // To resolve this we must use only the original client destination when going DIRECT
     // on intercepted traffic which failed Host verification
-    const HttpRequest *req = psstate->request;
+    const HttpRequest *req = request;
     const bool isIntercepted = !req->flags.redirected &&
                                (req->flags.intercepted || req->flags.interceptTproxy);
     const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
@@ -295,22 +289,22 @@ peerSelectDnsPaths(ps_state *psstate)
             Comm::ConnectionPointer p = new Comm::Connection();
             p->remote = req->clientConnectionManager->clientConnection->local;
             fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display.
-            psstate->handlePath(p, *fs);
+            handlePath(p, *fs);
         }
 
         // clear the used fs and continue
-        psstate->servers = fs->next;
+        servers = fs->next;
         delete fs;
-        peerSelectDnsPaths(psstate);
+        resolveSelected();
         return;
     }
 
     // convert the list of FwdServer destinations into destinations IP addresses
-    if (fs && psstate->wantsMoreDestinations()) {
+    if (fs && wantsMoreDestinations()) {
         // send the next one off for DNS lookup.
-        const char *host = fs->_peer.valid() ? fs->_peer->host : psstate->request->url.host();
-        debugs(44, 2, "Find IP destination for: " << psstate->url() << "' via " << host);
-        Dns::nbgethostbyname(host, psstate);
+        const char *host = fs->_peer.valid() ? fs->_peer->host : request->url.host();
+        debugs(44, 2, "Find IP destination for: " << url() << "' via " << host);
+        Dns::nbgethostbyname(host, this);
         return;
     }
 
@@ -318,43 +312,43 @@ peerSelectDnsPaths(ps_state *psstate)
     // due to the allocation method of fs, we must deallocate each manually.
     // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
     if (fs) {
-        assert(fs == psstate->servers);
+        assert(fs == servers);
         while (fs) {
-            psstate->servers = fs->next;
+            servers = fs->next;
             delete fs;
-            fs = psstate->servers;
+            fs = servers;
         }
     }
 
     // done with DNS lookups. pass back to caller
 
-    debugs(44, 2, psstate->id << " found all " << psstate->foundPaths << " destinations for " << psstate->url());
-    debugs(44, 2, "  always_direct = " << psstate->always_direct);
-    debugs(44, 2, "   never_direct = " << psstate->never_direct);
-    debugs(44, 2, "       timedout = " << psstate->ping.timedout);
+    debugs(44, 2, id << " found all " << foundPaths << " destinations for " << url());
+    debugs(44, 2, "  always_direct = " << always_direct);
+    debugs(44, 2, "   never_direct = " << never_direct);
+    debugs(44, 2, "       timedout = " << ping.timedout);
 
-    psstate->ping.stop = current_time;
-    psstate->request->hier.ping = psstate->ping; // final result
+    ping.stop = current_time;
+    request->hier.ping = ping; // final result
 
-    if (psstate->lastError && psstate->foundPaths) {
+    if (lastError && foundPaths) {
         // nobody cares about errors if we found destinations despite them
         debugs(44, 3, "forgetting the last error");
-        delete psstate->lastError;
-        psstate->lastError = nullptr;
+        delete lastError;
+        lastError = nullptr;
     }
 
-    if (const auto initiator = psstate->interestedInitiator())
-        initiator->noteDestinationsEnd(psstate->lastError);
-    psstate->lastError = nullptr; // initiator owns the ErrorState object now
-    delete psstate;
+    if (const auto initiator = interestedInitiator())
+        initiator->noteDestinationsEnd(lastError);
+    lastError = nullptr; // initiator owns the ErrorState object now
+    delete this;
 }
 
 void
-ps_state::noteLookup(const Dns::LookupDetails &details)
+PeerSelector::noteLookup(const Dns::LookupDetails &details)
 {
     /* ignore lookup delays that occurred after the initiator moved on */
 
-    if (peerSelectionAborted(this))
+    if (selectionAborted())
         return;
 
     if (!wantsMoreDestinations())
@@ -364,9 +358,9 @@ ps_state::noteLookup(const Dns::LookupDetails &details)
 }
 
 void
-ps_state::noteIp(const Ip::Address &ip)
+PeerSelector::noteIp(const Ip::Address &ip)
 {
-    if (peerSelectionAborted(this))
+    if (selectionAborted())
         return;
 
     if (!wantsMoreDestinations())
@@ -387,9 +381,9 @@ ps_state::noteIp(const Ip::Address &ip)
 }
 
 void
-ps_state::noteIps(const Dns::CachedIps *ia, const Dns::LookupDetails &details)
+PeerSelector::noteIps(const Dns::CachedIps *ia, const Dns::LookupDetails &details)
 {
-    if (peerSelectionAborted(this))
+    if (selectionAborted())
         return;
 
     FwdServer *fs = servers;
@@ -408,46 +402,46 @@ ps_state::noteIps(const Dns::CachedIps *ia, const Dns::LookupDetails &details)
     servers = fs->next;
     delete fs;
 
-    // see if more paths can be found
-    peerSelectDnsPaths(this);
+    // continue resolving selected peers
+    resolveSelected();
 }
 
-static int
-peerCheckNetdbDirect(ps_state * psstate)
+int
+PeerSelector::checkNetdbDirect()
 {
 #if USE_ICMP
     CachePeer *p;
     int myrtt;
     int myhops;
 
-    if (psstate->direct == DIRECT_NO)
+    if (direct == DIRECT_NO)
         return 0;
 
     /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
 
-    myrtt = netdbHostRtt(psstate->request->url.host());
+    myrtt = netdbHostRtt(request->url.host());
     debugs(44, 3, "MY RTT = " << myrtt << " msec");
     debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
 
     if (myrtt && myrtt <= Config.minDirectRtt)
         return 1;
 
-    myhops = netdbHostHops(psstate->request->url.host());
+    myhops = netdbHostHops(request->url.host());
 
-    debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
-    debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
+    debugs(44, 3, "MY hops = " << myhops);
+    debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops);
 
     if (myhops && myhops <= Config.minDirectHops)
         return 1;
 
-    p = whichPeer(psstate->closest_parent_miss);
+    p = whichPeer(closest_parent_miss);
 
     if (p == NULL)
         return 0;
 
-    debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
+    debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec");
 
-    if (myrtt && myrtt <= psstate->ping.p_rtt)
+    if (myrtt && myrtt <= ping.p_rtt)
         return 1;
 
 #endif /* USE_ICMP */
@@ -455,128 +449,119 @@ peerCheckNetdbDirect(ps_state * psstate)
     return 0;
 }
 
-static void
-peerSelectFoo(ps_state * ps)
+void
+PeerSelector::selectMore()
 {
-    if (peerSelectionAborted(ps))
+    if (selectionAborted())
         return;
 
-    StoreEntry *entry = ps->entry;
-    HttpRequest *request = ps->request;
     debugs(44, 3, request->method << ' ' << request->url.host());
 
     /** If we don't know whether DIRECT is permitted ... */
-    if (ps->direct == DIRECT_UNKNOWN) {
-        if (ps->always_direct == ACCESS_DUNNO) {
-            debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (always_direct to be checked)");
+    if (direct == DIRECT_UNKNOWN) {
+        if (always_direct == ACCESS_DUNNO) {
+            debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)");
             /** check always_direct; */
             ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL);
-            ch->al = ps->al;
-            ps->acl_checklist = ch;
-            ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, ps);
+            ch->al = al;
+            acl_checklist = ch;
+            acl_checklist->nonBlockingCheck(CheckAlwaysDirectDone, this);
             return;
-        } else if (ps->never_direct == ACCESS_DUNNO) {
-            debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (never_direct to be checked)");
+        } else if (never_direct == ACCESS_DUNNO) {
+            debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)");
             /** check never_direct; */
             ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL);
-            ch->al = ps->al;
-            ps->acl_checklist = ch;
-            ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, ps);
+            ch->al = al;
+            acl_checklist = ch;
+            acl_checklist->nonBlockingCheck(CheckNeverDirectDone, this);
             return;
         } else if (request->flags.noDirect) {
             /** if we are accelerating, direct is not an option. */
-            ps->direct = DIRECT_NO;
-            debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forced non-direct)");
+            direct = DIRECT_NO;
+            debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)");
         } else if (request->flags.loopDetected) {
             /** if we are in a forwarding-loop, direct is not an option. */
-            ps->direct = DIRECT_YES;
-            debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forwarding loop detected)");
-        } else if (peerCheckNetdbDirect(ps)) {
-            ps->direct = DIRECT_YES;
-            debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (checkNetdbDirect)");
+            direct = DIRECT_YES;
+            debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)");
+        } else if (checkNetdbDirect()) {
+            direct = DIRECT_YES;
+            debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)");
         } else {
-            ps->direct = DIRECT_MAYBE;
-            debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (default)");
+            direct = DIRECT_MAYBE;
+            debugs(44, 3, "direct = " << DirectStr[direct] << " (default)");
         }
 
-        debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
+        debugs(44, 3, "direct = " << DirectStr[direct]);
     }
 
     if (!entry || entry->ping_status == PING_NONE)
-        peerSelectPinned(ps);
+        selectPinned();
     if (entry == NULL) {
         (void) 0;
     } else if (entry->ping_status == PING_NONE) {
-        peerGetSomeNeighbor(ps);
+        selectSomeNeighbor();
 
         if (entry->ping_status == PING_WAITING)
             return;
     } else if (entry->ping_status == PING_WAITING) {
-        peerGetSomeNeighborReplies(ps);
+        selectSomeNeighborReplies();
         entry->ping_status = PING_DONE;
     }
 
-    switch (ps->direct) {
+    switch (direct) {
 
     case DIRECT_YES:
-        peerGetSomeDirect(ps);
+        selectSomeDirect();
         break;
 
     case DIRECT_NO:
-        peerGetSomeParent(ps);
-        peerGetAllParents(ps);
+        selectSomeParent();
+        selectAllParents();
         break;
 
     default:
 
         if (Config.onoff.prefer_direct)
-            peerGetSomeDirect(ps);
+            selectSomeDirect();
 
         if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) {
-            peerGetSomeParent(ps);
-            peerGetAllParents(ps);
+            selectSomeParent();
+            selectAllParents();
         }
 
         if (!Config.onoff.prefer_direct)
-            peerGetSomeDirect(ps);
+            selectSomeDirect();
 
         break;
     }
 
-    // resolve the possible peers
-    peerSelectDnsPaths(ps);
+    // end peer selection; start resolving selected peers
+    resolveSelected();
 }
 
 bool peerAllowedToUse(const CachePeer * p, HttpRequest * request);
 
-/**
- * peerSelectPinned
- *
- * Selects a pinned connection.
- */
-static void
-peerSelectPinned(ps_state * ps)
+/// Selects a pinned connection if it exists, is valid, and is allowed.
+void
+PeerSelector::selectPinned()
 {
-    HttpRequest *request = ps->request;
+    // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough.
     if (!request->pinnedConnection())
         return;
     CachePeer *pear = request->pinnedConnection()->pinnedPeer();
     if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request, pear))) {
-        if (pear && peerAllowedToUse(pear, request)) {
-            peerAddFwdServer(ps, pear, PINNED);
-            if (ps->entry)
-                ps->entry->ping_status = PING_DONE;     /* Skip ICP */
-        } else if (!pear && ps->direct != DIRECT_NO) {
-            peerAddFwdServer(ps, nullptr, PINNED);
-            if (ps->entry)
-                ps->entry->ping_status = PING_DONE;     /* Skip ICP */
+        const bool usePinned = pear ? peerAllowedToUse(pear, request) : (direct != DIRECT_NO);
+        if (usePinned) {
+            addSelection(pear, PINNED);
+            if (entry)
+                entry->ping_status = PING_DONE; // skip ICP
         }
     }
+    // If the pinned connection is prohibited (for this request) or gone, then
+    // the initiator must decide whether it is OK to open a new one instead.
 }
 
 /**
- * peerGetSomeNeighbor
- *
  * Selects a neighbor (parent or sibling) based on one of the
  * following methods:
  *      Cache Digests
@@ -584,16 +569,14 @@ peerSelectPinned(ps_state * ps)
  *      ICMP Netdb RTT estimates
  *      ICP/HTCP queries
  */
-static void
-peerGetSomeNeighbor(ps_state * ps)
+void
+PeerSelector::selectSomeNeighbor()
 {
-    StoreEntry *entry = ps->entry;
-    HttpRequest *request = ps->request;
     CachePeer *p;
     hier_code code = HIER_NONE;
     assert(entry->ping_status == PING_NONE);
 
-    if (ps->direct == DIRECT_YES) {
+    if (direct == DIRECT_YES) {
         entry->ping_status = PING_DONE;
         return;
     }
@@ -608,28 +591,28 @@ peerGetSomeNeighbor(ps_state * ps)
 #endif
         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)
+        } else if (peerSelectIcpPing(request, direct, entry)) {
+            debugs(44, 3, "Doing ICP pings");
+            ping.start = current_time;
+            ping.n_sent = neighborsUdpPing(request,
+                                           entry,
+                                           HandlePingReply,
+                                           this,
+                                           &ping.n_replies_expected,
+                                           &ping.timeout);
+
+            if (ping.n_sent == 0)
                 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
-            debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
-                   " ICP replies expected, RTT " << ps->ping.timeout <<
+            debugs(44, 3, ping.n_replies_expected <<
+                   " ICP replies expected, RTT " << ping.timeout <<
                    " msec");
 
-            if (ps->ping.n_replies_expected > 0) {
+            if (ping.n_replies_expected > 0) {
                 entry->ping_status = PING_WAITING;
-                eventAdd("peerPingTimeout",
-                         peerPingTimeout,
-                         ps,
-                         0.001 * ps->ping.timeout,
+                eventAdd("PeerSelector::HandlePingTimeout",
+                         HandlePingTimeout,
+                         this,
+                         0.001 * ping.timeout,
                          0);
                 return;
             }
@@ -637,75 +620,65 @@ peerGetSomeNeighbor(ps_state * ps)
 
     if (code != HIER_NONE) {
         assert(p);
-        peerAddFwdServer(ps, p, code);
+        addSelection(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)
+/// Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
+void
+PeerSelector::selectSomeNeighborReplies()
 {
     CachePeer *p = NULL;
     hier_code code = HIER_NONE;
-    assert(ps->entry->ping_status == PING_WAITING);
-    assert(ps->direct != DIRECT_YES);
+    assert(entry->ping_status == PING_WAITING);
+    assert(direct != DIRECT_YES);
 
-    if (peerCheckNetdbDirect(ps)) {
+    if (checkNetdbDirect()) {
         code = CLOSEST_DIRECT;
-        peerAddFwdServer(ps, nullptr, code);
+        addSelection(nullptr, code);
         return;
     }
 
-    if ((p = ps->hit)) {
-        code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
+    if ((p = hit)) {
+        code = hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
     } else {
-        if (!ps->closest_parent_miss.isAnyAddr()) {
-            p = whichPeer(ps->closest_parent_miss);
+        if (!closest_parent_miss.isAnyAddr()) {
+            p = whichPeer(closest_parent_miss);
             code = CLOSEST_PARENT_MISS;
-        } else if (!ps->first_parent_miss.isAnyAddr()) {
-            p = whichPeer(ps->first_parent_miss);
+        } else if (!first_parent_miss.isAnyAddr()) {
+            p = whichPeer(first_parent_miss);
             code = FIRST_PARENT_MISS;
         }
     }
     if (p && code != HIER_NONE) {
-        peerAddFwdServer(ps, p, code);
+        addSelection(p, code);
     }
 }
 
-/*
- * peerGetSomeDirect
- *
- * Simply adds a 'direct' entry to the FwdServers list if this
- * request can be forwarded directly to the origin server
- */
-static void
-peerGetSomeDirect(ps_state * ps)
+/// Adds a "direct" entry if the request can be forwarded to the origin server.
+void
+PeerSelector::selectSomeDirect()
 {
-    if (ps->direct == DIRECT_NO)
+    if (direct == DIRECT_NO)
         return;
 
     /* WAIS is not implemented natively */
-    if (ps->request->url.getScheme() == AnyP::PROTO_WAIS)
+    if (request->url.getScheme() == AnyP::PROTO_WAIS)
         return;
 
-    peerAddFwdServer(ps, nullptr, HIER_DIRECT);
+    addSelection(nullptr, HIER_DIRECT);
 }
 
-static void
-peerGetSomeParent(ps_state * ps)
+void
+PeerSelector::selectSomeParent()
 {
     CachePeer *p;
-    HttpRequest *request = ps->request;
     hier_code code = HIER_NONE;
     debugs(44, 3, request->method << ' ' << request->url.host());
 
-    if (ps->direct == DIRECT_YES)
+    if (direct == DIRECT_YES)
         return;
 
     if ((p = peerSourceHashSelectParent(request))) {
@@ -727,17 +700,15 @@ peerGetSomeParent(ps_state * ps)
     }
 
     if (code != HIER_NONE) {
-        peerAddFwdServer(ps, p, code);
+        addSelection(p, code);
     }
 }
 
-/* Adds alive parents. Used as a last resort for never_direct.
- */
-static void
-peerGetAllParents(ps_state * ps)
+/// Adds alive parents. Used as a last resort for never_direct.
+void
+PeerSelector::selectAllParents()
 {
     CachePeer *p;
-    HttpRequest *request = ps->request;
     /* Add all alive parents */
 
     for (p = Config.peers; p; p = p->next) {
@@ -751,7 +722,7 @@ peerGetAllParents(ps_state * ps)
         if (!peerHTTPOkay(p, request))
             continue;
 
-        peerAddFwdServer(ps, p, ANY_OLD_PARENT);
+        addSelection(p, ANY_OLD_PARENT);
     }
 
     /* XXX: should add dead parents here, but it is currently
@@ -760,25 +731,30 @@ peerGetAllParents(ps_state * ps)
      */
     /* Add default parent as a last resort */
     if ((p = getDefaultParent(request))) {
-        peerAddFwdServer(ps, p, DEFAULT_PARENT);
+        addSelection(p, DEFAULT_PARENT);
     }
 }
 
-static void
-peerPingTimeout(void *data)
+void
+PeerSelector::handlePingTimeout()
 {
-    ps_state *psstate = (ps_state *)data;
-    debugs(44, 3, psstate->url());
+    debugs(44, 3, url());
 
-    if (StoreEntry *entry = psstate->entry)
+    if (entry)
         entry->ping_status = PING_DONE;
 
-    if (peerSelectionAborted(psstate))
+    if (selectionAborted())
         return;
 
     ++PeerStats.timeouts;
-    psstate->ping.timedout = 1;
-    peerSelectFoo(psstate);
+    ping.timedout = 1;
+    selectMore();
+}
+
+void
+PeerSelector::HandlePingTimeout(void *data)
+{
+    static_cast<PeerSelector*>(data)->handlePingTimeout();
 }
 
 void
@@ -787,8 +763,8 @@ peerSelectInit(void)
     memset(&PeerStats, '\0', sizeof(PeerStats));
 }
 
-static void
-peerIcpParentMiss(CachePeer * p, icp_common_t * header, ps_state * ps)
+void
+PeerSelector::handleIcpParentMiss(CachePeer *p, icp_common_t *header)
 {
     int rtt;
 
@@ -799,11 +775,11 @@ peerIcpParentMiss(CachePeer * p, icp_common_t * header, ps_state * ps)
             int hops = (header->pad >> 16) & 0xFFFF;
 
             if (rtt > 0 && rtt < 0xFFFF)
-                netdbUpdatePeer(ps->request->url, p, rtt, hops);
+                netdbUpdatePeer(request->url, 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 (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
+                closest_parent_miss = p->in_addr;
+                ping.p_rtt = rtt;
             }
         }
     }
@@ -814,79 +790,77 @@ peerIcpParentMiss(CachePeer * p, icp_common_t * header, ps_state * ps)
         return;
 
     /* set FIRST_MISS if there is no CLOSEST parent */
-    if (!ps->closest_parent_miss.isAnyAddr())
+    if (!closest_parent_miss.isAnyAddr())
         return;
 
-    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
+    rtt = (tvSubMsec(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;
+    if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
+        first_parent_miss = p->in_addr;
+        ping.w_rtt = rtt;
     }
 }
 
-static void
-peerHandleIcpReply(CachePeer * p, peer_t type, icp_common_t * header, void *data)
+void
+PeerSelector::handleIcpReply(CachePeer *p, const peer_t type, icp_common_t *header)
 {
-    ps_state *psstate = (ps_state *)data;
     icp_opcode op = header->getOpCode();
-    debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->url()  );
+    debugs(44, 3, icp_opcode_str[op] << ' ' << url());
 #if USE_CACHE_DIGESTS && 0
     /* do cd lookup to count false misses */
 
     if (p && request)
         peerNoteDigestLookup(request, p,
-                             peerDigestLookup(p, request, psstate->entry));
+                             peerDigestLookup(p, request, entry));
 
 #endif
 
-    ++ psstate->ping.n_recv;
+    ++ping.n_recv;
 
     if (op == ICP_MISS || op == ICP_DECHO) {
         if (type == PEER_PARENT)
-            peerIcpParentMiss(p, header, psstate);
+            handleIcpParentMiss(p, header);
     } else if (op == ICP_HIT) {
-        psstate->hit = p;
-        psstate->hit_type = type;
-        peerSelectFoo(psstate);
+        hit = p;
+        hit_type = type;
+        selectMore();
         return;
     }
 
-    if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
+    if (ping.n_recv < ping.n_replies_expected)
         return;
 
-    peerSelectFoo(psstate);
+    selectMore();
 }
 
 #if USE_HTCP
-static void
-peerHandleHtcpReply(CachePeer * p, peer_t type, HtcpReplyData * htcp, void *data)
+void
+PeerSelector::handleHtcpReply(CachePeer *p, const peer_t type, HtcpReplyData *htcp)
 {
-    ps_state *psstate = (ps_state *)data;
-    debugs(44, 3, "" << (htcp->hit ? "HIT" : "MISS") << " " << psstate->url());
-    ++ psstate->ping.n_recv;
+    debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url());
+    ++ping.n_recv;
 
     if (htcp->hit) {
-        psstate->hit = p;
-        psstate->hit_type = type;
-        peerSelectFoo(psstate);
+        hit = p;
+        hit_type = type;
+        selectMore();
         return;
     }
 
     if (type == PEER_PARENT)
-        peerHtcpParentMiss(p, htcp, psstate);
+        handleHtcpParentMiss(p, htcp);
 
-    if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
+    if (ping.n_recv < ping.n_replies_expected)
         return;
 
-    peerSelectFoo(psstate);
+    selectMore();
 }
 
-static void
-peerHtcpParentMiss(CachePeer * p, HtcpReplyData * htcp, ps_state * ps)
+void
+PeerSelector::handleHtcpParentMiss(CachePeer *p, HtcpReplyData *htcp)
 {
     int rtt;
 
@@ -895,11 +869,11 @@ peerHtcpParentMiss(CachePeer * p, HtcpReplyData * htcp, ps_state * ps)
         if (htcp->cto.rtt > 0) {
             rtt = (int) htcp->cto.rtt * 1000;
             int hops = (int) htcp->cto.hops * 1000;
-            netdbUpdatePeer(ps->request->url, p, rtt, hops);
+            netdbUpdatePeer(request->url, 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 (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
+                closest_parent_miss = p->in_addr;
+                ping.p_rtt = rtt;
             }
         }
     }
@@ -910,65 +884,63 @@ peerHtcpParentMiss(CachePeer * p, HtcpReplyData * htcp, ps_state * ps)
         return;
 
     /* set FIRST_MISS if there is no CLOSEST parent */
-    if (!ps->closest_parent_miss.isAnyAddr())
+    if (!closest_parent_miss.isAnyAddr())
         return;
 
-    rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
+    rtt = (tvSubMsec(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;
+    if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
+        first_parent_miss = p->in_addr;
+        ping.w_rtt = rtt;
     }
 }
 
 #endif
 
-static void
-peerHandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
+void
+PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
 {
     if (proto == AnyP::PROTO_ICP)
-        peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
+        static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata));
 
 #if USE_HTCP
 
     else if (proto == AnyP::PROTO_HTCP)
-        peerHandleHtcpReply(p, type, (HtcpReplyData *)pingdata, data);
+        static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata));
 
 #endif
 
     else
-        debugs(44, DBG_IMPORTANT, "peerHandlePingReply: unknown protocol " << proto);
+        debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto);
 }
 
-static void
-peerAddFwdServer(ps_state *ps, CachePeer *peer, const hier_code code)
+void
+PeerSelector::addSelection(CachePeer *peer, const hier_code code)
 {
     // Find the end of the servers list. Bail on a duplicate destination.
-    assert(ps);
-    FwdServer **FSVR = &ps->servers;
-    while (const auto server = *FSVR) {
+    auto **serversTail = &servers;
+    while (const auto server = *serversTail) {
         // There can be at most one PINNED destination.
         // Non-PINNED destinations are uniquely identified by their CachePeer
         // (even though a DIRECT destination might match a cache_peer address).
         const bool duplicate = (server->code == PINNED) ?
-            (code == PINNED) : (server->_peer == peer);
+                               (code == PINNED) : (server->_peer == peer);
         if (duplicate) {
-            debugs(44, 3, "skipping " << PeerSelectionDumper(ps, peer, code) <<
-                   "; have " << PeerSelectionDumper(ps, server->_peer.get(), server->code));
+            debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) <<
+                   "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code));
             return;
         }
-        FSVR = &server->next;
+        serversTail = &server->next;
     }
 
-    debugs(44, 3, "adding " << PeerSelectionDumper(ps, peer, code));
-    FwdServer *fs = new FwdServer(peer, code);
-    *FSVR = fs;
+    debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code));
+    *serversTail = new FwdServer(peer, code);
 }
 
-ps_state::ps_state(PeerSelectionInitiator *initiator):
+PeerSelector::PeerSelector(PeerSelectionInitiator *initiator):
     request(nullptr),
     entry (NULL),
     always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
@@ -987,7 +959,7 @@ ps_state::ps_state(PeerSelectionInitiator *initiator):
 }
 
 const SBuf
-ps_state::url() const
+PeerSelector::url() const
 {
     if (entry)
         return SBuf(entry->url());
@@ -1001,7 +973,7 @@ ps_state::url() const
 
 /// \returns valid/interested peer initiator or nil
 PeerSelectionInitiator *
-ps_state::interestedInitiator()
+PeerSelector::interestedInitiator()
 {
     const auto initiator = initiator_.valid();
 
@@ -1020,14 +992,14 @@ ps_state::interestedInitiator()
 }
 
 bool
-ps_state::wantsMoreDestinations() const {
+PeerSelector::wantsMoreDestinations() const {
     const auto maxCount = Config.forward_max_tries;
     return maxCount >= 0 && foundPaths <
            static_cast<std::make_unsigned<decltype(maxCount)>::type>(maxCount);
 }
 
 void
-ps_state::handlePath(Comm::ConnectionPointer &path, FwdServer &fs)
+PeerSelector::handlePath(Comm::ConnectionPointer &path, FwdServer &fs)
 {
     ++foundPaths;
 
@@ -1048,7 +1020,7 @@ ps_state::handlePath(Comm::ConnectionPointer &path, FwdServer &fs)
         initiator->noteDestination(path);
 }
 
-InstanceIdDefinitions(ps_state, "PeerSelector");
+InstanceIdDefinitions(PeerSelector, "PeerSelector");
 
 ping_data::ping_data() :
     n_sent(0),
index d451392f1b266a6fb012fe940a579873e43b04f5..477c642e908a58f89ef1a85b60d685447d4ec11b 100644 (file)
 #define STUB_API "icp_*.cc"
 #include "tests/STUB.h"
 
-#ifdef __cplusplus
-_icp_common_t::_icp_common_t() STUB
-_icp_common_t::_icp_common_t(char *buf, unsigned int len) STUB
-void _icp_common_t::handleReply(char *buf, Ip::Address &from) STUB
-_icp_common_t *_icp_common_t::createMessage(icp_opcode opcode, int flags, const char *url, int reqnum, int pad) STUB_RETVAL(NULL)
-icp_opcode _icp_common_t::getOpCode() const STUB_RETVAL(ICP_INVALID)
+icp_common_t::icp_common_t() STUB
+icp_common_t::icp_common_t(char *buf, unsigned int len) STUB
+void icp_common_t::handleReply(char *buf, Ip::Address &from) STUB
+icp_common_t *icp_common_t::CreateMessage(icp_opcode opcode, int flags, const char *url, int reqnum, int pad) STUB_RETVAL(nullptr)
+icp_opcode icp_common_t::getOpCode() const STUB_RETVAL(ICP_INVALID)
 ICPState::ICPState(icp_common_t &aHeader, HttpRequest *aRequest) STUB
 ICPState::~ICPState() STUB
-#endif
 
 Comm::ConnectionPointer icpIncomingConn;
 Comm::ConnectionPointer icpOutgoingConn;