[PATCH] [RFC] cleanup-comm: round 3?

From: Amos Jeffries <squid3_at_treenet.co.nz>
Date: Thu, 22 Jul 2010 00:54:28 +1200

Here is todays code for your reference on where its at.

This covers all your audit requests so far. Including using Connection
instead of FD in the HTTP Server objects outside FwdState.
(I have yet to roll the same change into tunnel, ftp, gopher, etc
properly though).

Amos

-- 
Please be using
   Current Stable Squid 2.7.STABLE9 or 3.1.5

# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: squid3_at_treenet.co.nz-20100720143000-nyohuk4u3b0l22cc
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk/
# testament_sha1: b4ad8ed8a842771ea7377240b7f088df752d9601
# timestamp: 2010-07-22 00:32:59 +1200
# base_revision_id: rousskov_at_measurement-factory.com-20100716223742-\
# xiaw4wr97k3e8ttx
#
# Begin patch
=== modified file 'doc/release-notes/release-3.2.sgml'
--- doc/release-notes/release-3.2.sgml 2010-07-12 14:45:17 +0000
+++ doc/release-notes/release-3.2.sgml 2010-07-13 08:38:07 +0000
@@ -223,6 +223,10 @@
         <p>Access control based on altered HTTP request following adaptation alterations (ICAP, eCAP, URL rewriter).
         An upgraded drop-in replacement for <em>http_access2</em> found in Squid-2.
 
+ <tag>connect_retries</tag>
+ <p>Replacement for <em>maximum_single_addr_tries</em>, but instead of only applying to hosts with single addresses.
+ This directive applies to all hosts, extending the number of connection attempts to each IP address.
+
         <tag>else</tag>
         <p>Part of conditional SMP support syyntax. see <em>if</em>
 
@@ -335,6 +339,10 @@
         <tag>ftp_list_width</tag>
         <p>Obsolete.
 
+ <tag>maximum_single_addr_tries</tag>
+ <p>The behaviour controlled by this directive is no longer possible.
+ It has been replaced by <em>connect_retries</em> option which operates a little differently.
+
         <tag>url_rewrite_concurrency</tag>
         <p>Replaced by url_rewrite_children ... concurrency=N option.
 

=== modified file 'src/CommCalls.cc'
--- src/CommCalls.cc 2009-07-12 22:56:47 +0000
+++ src/CommCalls.cc 2010-07-18 08:29:43 +0000
@@ -1,5 +1,6 @@
 #include "squid.h"
 #include "fde.h"
+#include "comm/Connection.h"
 #include "CommCalls.h"
 
 /* CommCommonCbParams */
@@ -45,6 +46,7 @@
     CommCommonCbParams::print(os);
     if (nfd >= 0)
         os << ", newFD " << nfd;
+ os << ", " << details;
 }
 
 
@@ -71,7 +73,8 @@
 CommConnectCbParams::print(std::ostream &os) const
 {
     CommCommonCbParams::print(os);
- os << ", " << dns;
+ if (conn != NULL)
+ os << ", conn.local=" << conn->local << ", conn.remote=" << conn->remote;
 }
 
 /* CommIoCbParams */
@@ -133,7 +136,7 @@
 void
 CommAcceptCbPtrFun::dial()
 {
- handler(params.fd, params.nfd, &params.details, params.flag, params.xerrno, params.data);
+ handler(params.fd, params.nfd, params.details, params.flag, params.xerrno, params.data);
 }
 
 void
@@ -157,7 +160,7 @@
 void
 CommConnectCbPtrFun::dial()
 {
- handler(params.fd, params.dns, params.flag, params.xerrno, params.data);
+ handler(params.conn, params.flag, params.xerrno, params.data);
 }
 
 void

=== modified file 'src/CommCalls.h'
--- src/CommCalls.h 2010-07-14 01:00:21 +0000
+++ src/CommCalls.h 2010-07-15 10:04:21 +0000
@@ -6,11 +6,10 @@
 #ifndef SQUID_COMMCALLS_H
 #define SQUID_COMMCALLS_H
 
-#include "comm.h"
-#include "ConnectionDetail.h"
-#include "DnsLookupDetails.h"
 #include "base/AsyncCall.h"
 #include "base/AsyncJobCalls.h"
+#include "comm/comm_err_t.h"
+#include "comm/forward.h"
 
 /* CommCalls implement AsyncCall interface for comm_* callbacks.
  * The classes cover two call dialer kinds:
@@ -22,6 +21,10 @@
  * - I/O (IOCB).
  */
 
+typedef void IOACB(int fd, int nfd, Comm::ConnectionPointer &details, comm_err_t flag, int xerrno, void *data);
+typedef void CNCB(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data);
+typedef void IOCB(int fd, char *, size_t size, comm_err_t flag, int xerrno, void *data);
+
 /*
  * TODO: When there are no function-pointer-based callbacks left, all
  * this complexity can be removed. Jobs that need comm services will just
@@ -69,7 +72,7 @@
     void print(std::ostream &os) const;
 
 public:
- ConnectionDetail details;
+ Comm::ConnectionPointer details;
     int nfd; // TODO: rename to fdNew or somesuch
 };
 
@@ -84,7 +87,7 @@
     void print(std::ostream &os) const;
 
 public:
- DnsLookupDetails dns;
+ Comm::ConnectionPointer conn;
 };
 
 // read/write (I/O) parameters

=== modified file 'src/HttpRequest.cc'
--- src/HttpRequest.cc 2010-07-13 16:43:00 +0000
+++ src/HttpRequest.cc 2010-07-15 10:04:21 +0000
@@ -35,15 +35,16 @@
  */
 
 #include "squid.h"
+#include "acl/FilledChecklist.h"
+#if ICAP_CLIENT
+#include "adaptation/icap/icap_log.h"
+#endif
+#include "auth/UserRequest.h"
+#include "DnsLookupDetails.h"
 #include "HttpRequest.h"
-#include "auth/UserRequest.h"
 #include "HttpHeaderRange.h"
 #include "MemBuf.h"
 #include "Store.h"
-#if ICAP_CLIENT
-#include "adaptation/icap/icap_log.h"
-#endif
-#include "acl/FilledChecklist.h"
 
 HttpRequest::HttpRequest() : HttpMsg(hoRequest)
 {

=== modified file 'src/Makefile.am'
--- src/Makefile.am 2010-07-06 23:09:44 +0000
+++ src/Makefile.am 2010-07-13 08:38:07 +0000
@@ -290,7 +290,6 @@
         ConfigOption.cc \
         ConfigParser.cc \
         ConfigParser.h \
- ConnectionDetail.h \
         debug.cc \
         Debug.h \
         defines.h \
@@ -533,7 +532,7 @@
 
 squid_LDADD = \
         $(COMMON_LIBS) \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         eui/libeui.la \
         icmp/libicmp.la icmp/libicmp-core.la \
         log/liblog.la \
@@ -1236,10 +1235,10 @@
         wordlist.cc
 nodist_tests_testCacheManager_SOURCES = \
         $(BUILT_SOURCES)
-# comm.cc only requires comm/libcomm-listener.la until fdc_table is dead.
+# comm.cc only requires comm/libcomm.la until fdc_table is dead.
 tests_testCacheManager_LDADD = \
         $(COMMON_LIBS) \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         icmp/libicmp.la icmp/libicmp-core.la \
         log/liblog.la \
         $(REPL_OBJS) \
@@ -1422,7 +1421,7 @@
 tests_testEvent_LDADD = \
         $(COMMON_LIBS) \
         icmp/libicmp.la icmp/libicmp-core.la \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         log/liblog.la \
         $(REPL_OBJS) \
         ${ADAPTATION_LIBS} \
@@ -1577,7 +1576,7 @@
 tests_testEventLoop_LDADD = \
         $(COMMON_LIBS) \
         icmp/libicmp.la icmp/libicmp-core.la \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         log/liblog.la \
         $(REPL_OBJS) \
         ${ADAPTATION_LIBS} \
@@ -1727,7 +1726,7 @@
 tests_test_http_range_LDADD = \
         $(COMMON_LIBS) \
         icmp/libicmp.la icmp/libicmp-core.la \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         log/liblog.la \
         $(REPL_OBJS) \
         ${ADAPTATION_LIBS} \
@@ -1882,7 +1881,7 @@
 tests_testHttpRequest_LDADD = \
         $(COMMON_LIBS) \
         icmp/libicmp.la icmp/libicmp-core.la \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         log/liblog.la \
         $(REPL_OBJS) \
         ${ADAPTATION_LIBS} \
@@ -2254,7 +2253,7 @@
 tests_testURL_LDADD = \
         $(COMMON_LIBS) \
         icmp/libicmp.la icmp/libicmp-core.la \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
         log/liblog.la \
         $(REGEXLIB) \
         $(REPL_OBJS) \

=== modified file 'src/PeerSelectState.h'
--- src/PeerSelectState.h 2010-05-02 19:32:42 +0000
+++ src/PeerSelectState.h 2010-06-27 11:31:31 +0000
@@ -33,9 +33,37 @@
 #ifndef SQUID_PEERSELECTSTATE_H
 #define SQUID_PEERSELECTSTATE_H
 
+#include "Array.h"
 #include "cbdata.h"
+#include "comm/forward.h"
+#include "hier_code.h"
+#include "ip/Address.h"
 #include "PingData.h"
-#include "ip/Address.h"
+
+class HttpRequest;
+class StoreEntry;
+
+typedef void PSC(Comm::Paths *, void *);
+
+SQUIDCEXTERN void peerSelect(Comm::Paths *, HttpRequest *, StoreEntry *, PSC *, void *data);
+SQUIDCEXTERN void peerSelectInit(void);
+
+/**
+ * A peer which has been selected as a possible destination.
+ * Listed as pointers here so as to prevent duplicates being added but will
+ * be converted to a set of IP address path options before handing back out
+ * to the caller.
+ *
+ * Certain connection flags and outgoing settings will also be looked up and
+ * set based on the received request and peer settings before handing back.
+ */
+class FwdServer
+{
+public:
+ peer *_peer; /* NULL --> origin server */
+ hier_code code;
+ FwdServer *next;
+};
 
 class ps_state
 {
@@ -50,7 +78,10 @@
     int direct;
     PSC *callback;
     void *callback_data;
- FwdServer *servers;
+
+ Comm::Paths *paths; ///< the callers paths array. to be filled with our final results.
+ FwdServer *servers; ///< temporary linked list of peers we will pass back.
+
     /*
      * Why are these Ip::Address instead of peer *? Because a
      * peer structure can become invalid during the peer selection
@@ -74,5 +105,4 @@
     CBDATA_CLASS(ps_state);
 };
 
-
 #endif /* SQUID_PEERSELECTSTATE_H */

=== modified file 'src/adaptation/icap/ModXact.cc'
--- src/adaptation/icap/ModXact.cc 2010-06-15 07:21:57 +0000
+++ src/adaptation/icap/ModXact.cc 2010-06-27 11:34:40 +0000
@@ -16,6 +16,7 @@
 #include "base/TextException.h"
 #include "ChunkedCodingParser.h"
 #include "comm.h"
+#include "comm/Connection.h"
 #include "HttpMsg.h"
 #include "HttpRequest.h"
 #include "HttpReply.h"
@@ -463,7 +464,7 @@
 
 void Adaptation::Icap::ModXact::startReading()
 {
- Must(connection >= 0);
+ Must(haveConnection());
     Must(!reader);
     Must(!adapted.header);
     Must(!adapted.body_pipe);
@@ -621,7 +622,7 @@
     stopParsing();
 
     stopWriting(true); // or should we force it?
- if (connection >= 0) {
+ if (haveConnection()) {
         reuseConnection = false; // be conservative
         cancelRead(); // may not work; and we cannot stop connecting either
         if (!doneWithIo())
@@ -1550,7 +1551,7 @@
     if (virgin.body_pipe != NULL)
         buf.append("R", 1);
 
- if (connection > 0 && !doneReading())
+ if (haveConnection() && !doneReading())
         buf.append("r", 1);
 
     if (!state.doneWriting() && state.writing != State::writingInit)

=== modified file 'src/adaptation/icap/Xaction.cc'
--- src/adaptation/icap/Xaction.cc 2010-04-17 02:29:04 +0000
+++ src/adaptation/icap/Xaction.cc 2010-07-20 14:30:00 +0000
@@ -4,6 +4,8 @@
 
 #include "squid.h"
 #include "comm.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
 #include "CommCalls.h"
 #include "HttpMsg.h"
 #include "adaptation/icap/Xaction.h"
@@ -29,7 +31,7 @@
         icapRequest(NULL),
         icapReply(NULL),
         attempts(0),
- connection(-1),
+ connection(NULL),
         theService(aService),
         commBuf(NULL), commBufSize(0),
         commEof(false),
@@ -89,23 +91,31 @@
 {
     Ip::Address client_addr;
 
- Must(connection < 0);
+ Must(!haveConnection());
 
     const Adaptation::Service &s = service();
 
     if (!TheConfig.reuse_connections)
         disableRetries(); // this will also safely drain pconn pool
 
+ connection = new Comm::Connection;
+
+ /* NP: set these here because it applies whether a pconn or a new conn is used */
+
+ // TODO: Avoid blocking lookup if s.cfg().host is a hostname
+ connection->remote = s.cfg().host.termedBuf();
+ connection->remote.SetPort(s.cfg().port);
+
     // TODO: check whether NULL domain is appropriate here
- connection = icapPconnPool->pop(s.cfg().host.termedBuf(), s.cfg().port, NULL, client_addr, isRetriable);
- if (connection >= 0) {
- debugs(93,3, HERE << "reused pconn FD " << connection);
+ connection->fd = icapPconnPool->pop(s.cfg().host.termedBuf(), s.cfg().port, NULL, client_addr, isRetriable);
+ if (connection->isOpen()) {
+ debugs(93,3, HERE << "reused pconn FD " << connection->fd);
 
         // fake the connect callback
         // TODO: can we sync call Adaptation::Icap::Xaction::noteCommConnected here instead?
         typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
         Dialer dialer(this, &Adaptation::Icap::Xaction::noteCommConnected);
- dialer.params.fd = connection;
+ dialer.params.fd = connection->fd;
         dialer.params.flag = COMM_OK;
         // fake other parameters by copying from the existing connection
         connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
@@ -115,32 +125,13 @@
 
     disableRetries(); // we only retry pconn failures
 
- Ip::Address outgoing;
- connection = comm_open(SOCK_STREAM, 0, outgoing,
- COMM_NONBLOCKING, s.cfg().uri.termedBuf());
-
- if (connection < 0)
- dieOnConnectionFailure(); // throws
-
- debugs(93,3, typeName << " opens connection to " << s.cfg().host << ":" << s.cfg().port);
-
- // TODO: service bypass status may differ from that of a transaction
- typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
- AsyncCall::Pointer timeoutCall = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommTimedout",
- TimeoutDialer(this,&Adaptation::Icap::Xaction::noteCommTimedout));
-
- commSetTimeout(connection, TheConfig.connect_timeout(
- service().cfg().bypass), timeoutCall);
-
- typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommCloseCbParams> CloseDialer;
- closer = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommClosed",
- CloseDialer(this,&Adaptation::Icap::Xaction::noteCommClosed));
- comm_add_close_handler(connection, closer);
-
     typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> ConnectDialer;
     connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected",
                           ConnectDialer(this, &Adaptation::Icap::Xaction::noteCommConnected));
- commConnectStart(connection, s.cfg().host.termedBuf(), s.cfg().port, connector);
+
+ Comm::ConnOpener *cs = new Comm::ConnOpener(connection, connector, TheConfig.connect_timeout(service().cfg().bypass));
+ cs->setHost(s.cfg().host.termedBuf());
+ AsyncJob::AsyncStart(cs);
 }
 
 /*
@@ -159,10 +150,10 @@
 
 void Adaptation::Icap::Xaction::closeConnection()
 {
- if (connection >= 0) {
+ if (haveConnection()) {
 
         if (closer != NULL) {
- comm_remove_close_handler(connection, closer);
+ comm_remove_close_handler(connection->fd, closer);
             closer = NULL;
         }
 
@@ -179,34 +170,45 @@
             //status() adds leading spaces.
             debugs(93,3, HERE << "pushing pconn" << status());
             AsyncCall::Pointer call = NULL;
- commSetTimeout(connection, -1, call);
- icapPconnPool->push(connection, theService->cfg().host.termedBuf(),
+ commSetTimeout(connection->fd, -1, call);
+ icapPconnPool->push(connection->fd, theService->cfg().host.termedBuf(),
                                 theService->cfg().port, NULL, client_addr);
             disableRetries();
+ connection->fd = -1; // prevent premature real closing.
         } else {
             //status() adds leading spaces.
             debugs(93,3, HERE << "closing pconn" << status());
             // comm_close will clear timeout
- comm_close(connection);
+ connection->close();
         }
 
         writer = NULL;
         reader = NULL;
         connector = NULL;
- connection = -1;
+ connection = NULL;
     }
 }
 
 // connection with the ICAP service established
 void Adaptation::Icap::Xaction::noteCommConnected(const CommConnectCbParams &io)
 {
+ if (io.flag == COMM_TIMEOUT) {
+ handleCommTimedout();
+ return;
+ }
+
     Must(connector != NULL);
     connector = NULL;
 
     if (io.flag != COMM_OK)
         dieOnConnectionFailure(); // throws
 
- fd_table[connection].noteUse(icapPconnPool);
+ typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommCloseCbParams> CloseDialer;
+ closer = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommClosed",
+ CloseDialer(this,&Adaptation::Icap::Xaction::noteCommClosed));
+ comm_add_close_handler(io.conn->fd, closer);
+
+ fd_table[io.conn->fd].noteUse(icapPconnPool);
 
     handleCommConnected();
 }
@@ -220,12 +222,14 @@
 
 void Adaptation::Icap::Xaction::scheduleWrite(MemBuf &buf)
 {
+ Must(haveConnection());
+
     // comm module will free the buffer
     typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
     writer = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommWrote",
                        Dialer(this, &Adaptation::Icap::Xaction::noteCommWrote));
 
- comm_write_mbuf(connection, &buf, writer);
+ comm_write_mbuf(connection->fd, &buf, writer);
     updateTimeout();
 }
 
@@ -299,6 +303,8 @@
 
 void Adaptation::Icap::Xaction::updateTimeout()
 {
+ Must(haveConnection());
+
     if (reader != NULL || writer != NULL) {
         // restart the timeout before each I/O
         // XXX: why does Config.Timeout lacks a write timeout?
@@ -307,19 +313,19 @@
         AsyncCall::Pointer call = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommTimedout",
                                              TimeoutDialer(this,&Adaptation::Icap::Xaction::noteCommTimedout));
 
- commSetTimeout(connection,
+ commSetTimeout(connection->fd,
                        TheConfig.io_timeout(service().cfg().bypass), call);
     } else {
         // clear timeout when there is no I/O
         // Do we need a lifetime timeout?
         AsyncCall::Pointer call = NULL;
- commSetTimeout(connection, -1, call);
+ commSetTimeout(connection->fd, -1, call);
     }
 }
 
 void Adaptation::Icap::Xaction::scheduleRead()
 {
- Must(connection >= 0);
+ Must(haveConnection());
     Must(!reader);
     Must(readBuf.hasSpace());
 
@@ -331,7 +337,7 @@
     reader = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommRead",
                        Dialer(this, &Adaptation::Icap::Xaction::noteCommRead));
 
- comm_read(connection, commBuf, readBuf.spaceSize(), reader);
+ comm_read(connection->fd, commBuf, readBuf.spaceSize(), reader);
     updateTimeout();
 }
 
@@ -369,7 +375,8 @@
 void Adaptation::Icap::Xaction::cancelRead()
 {
     if (reader != NULL) {
- comm_read_cancel(connection, reader);
+ Must(haveConnection());
+ comm_read_cancel(connection->fd, reader);
         reader = NULL;
     }
 }
@@ -410,11 +417,16 @@
 
 bool Adaptation::Icap::Xaction::doneWithIo() const
 {
- return connection >= 0 && // or we could still be waiting to open it
+ return haveConnection() &&
            !connector && !reader && !writer && // fast checks, some redundant
            doneReading() && doneWriting();
 }
 
+bool Adaptation::Icap::Xaction::haveConnection() const
+{
+ return connection != NULL && connection->isOpen();
+}
+
 // initiator aborted
 void Adaptation::Icap::Xaction::noteInitiatorAborted()
 {
@@ -525,8 +537,8 @@
 
 void Adaptation::Icap::Xaction::fillPendingStatus(MemBuf &buf) const
 {
- if (connection >= 0) {
- buf.Printf("FD %d", connection);
+ if (haveConnection()) {
+ buf.Printf("FD %d", connection->fd);
 
         if (writer != NULL)
             buf.append("w", 1);
@@ -540,8 +552,8 @@
 
 void Adaptation::Icap::Xaction::fillDoneStatus(MemBuf &buf) const
 {
- if (connection >= 0 && commEof)
- buf.Printf("Comm(%d)", connection);
+ if (haveConnection() && commEof)
+ buf.Printf("Comm(%d)", connection->fd);
 
     if (stopReason != NULL)
         buf.Printf("Stopped");

=== modified file 'src/adaptation/icap/Xaction.h'
--- src/adaptation/icap/Xaction.h 2009-08-23 09:30:49 +0000
+++ src/adaptation/icap/Xaction.h 2010-06-27 11:31:31 +0000
@@ -34,7 +34,7 @@
 #ifndef SQUID_ICAPXACTION_H
 #define SQUID_ICAPXACTION_H
 
-#include "comm.h"
+#include "comm/forward.h"
 #include "CommCalls.h"
 #include "MemBuf.h"
 #include "adaptation/icap/ServiceRep.h"
@@ -99,6 +99,7 @@
     void openConnection();
     void closeConnection();
     void dieOnConnectionFailure();
+ bool haveConnection() const;
 
     void scheduleRead();
     void scheduleWrite(MemBuf &buf);
@@ -140,7 +141,7 @@
     void maybeLog();
 
 protected:
- int connection; // FD of the ICAP server connection
+ Comm::ConnectionPointer connection; ///< ICAP server connection
     Adaptation::Icap::ServiceRep::Pointer theService;
 
     /*

=== modified file 'src/base/AsyncJob.cc'
--- src/base/AsyncJob.cc 2010-05-26 03:06:02 +0000
+++ src/base/AsyncJob.cc 2010-07-17 08:15:08 +0000
@@ -27,6 +27,8 @@
 
 AsyncJob::~AsyncJob()
 {
+ debugs(93,3, "AsyncJob of type " << typeName << " destructed, this=" << this <<
+ " [async" << id << ']');
 }
 
 void AsyncJob::noteStart()

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc 2010-07-07 16:41:03 +0000
+++ src/cache_cf.cc 2010-07-13 08:38:07 +0000
@@ -650,12 +650,9 @@
     else
         Config.appendDomainLen = 0;
 
- if (Config.retry.maxtries > 10)
- fatal("maximum_single_addr_tries cannot be larger than 10");
-
- if (Config.retry.maxtries < 1) {
- debugs(3, 0, "WARNING: resetting 'maximum_single_addr_tries to 1");
- Config.retry.maxtries = 1;
+ if (Config.connect_retries > 10) {
+ debugs(0,DBG_CRITICAL, "WARNING: connect_retries cannot be larger than 10. Resetting to 10.");
+ Config.connect_retries = 10;
     }
 
     requirePathnameExists("MIME Config Table", Config.mimeTablePathname);
@@ -2053,7 +2050,7 @@
 
     p->icp.version = ICP_VERSION_CURRENT;
 
- p->test_fd = -1;
+ p->testing_now = false;
 
 #if USE_CACHE_DIGESTS
 

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2010-07-06 23:09:44 +0000
+++ src/cf.data.pre 2010-07-13 08:38:07 +0000
@@ -2269,6 +2269,9 @@
 DOC_START
         Controls how many different forward paths Squid will try
         before giving up. See also forward_timeout.
+
+ NOTE: connect_retries (default: none) can make each of these
+ possible forwarding paths be tried multiple times.
 DOC_END
 
 NAME: hierarchy_stoplist
@@ -6855,21 +6858,24 @@
         see also refresh_pattern for a more selective approach.
 DOC_END
 
-NAME: maximum_single_addr_tries
+NAME: connect_retries
 TYPE: int
-LOC: Config.retry.maxtries
-DEFAULT: 1
+LOC: Config.connect_retries
+DEFAULT: 0
 DOC_START
- This sets the maximum number of connection attempts for a
- host that only has one address (for multiple-address hosts,
- each address is tried once).
-
- The default value is one attempt, the (not recommended)
- maximum is 255 tries. A warning message will be generated
- if it is set to a value greater than ten.
-
- Note: This is in addition to the request re-forwarding which
- takes place if Squid fails to get a satisfying response.
+ This sets the maximum number of connection attempts made for each
+ TCP connection. The connect_retries attempts must all still
+ complete within the connection timeout period.
+
+ The default is not to re-try if the first connection attempt fails.
+ The (not recommended) maximum is 10 tries.
+
+ A warning message will be generated if it is set to a too-high
+ value and the configured value will be over-ridden.
+
+ Note: These re-tries are in addition to forward_max_tries
+ which limit how many different addresses may be tried to find
+ a useful server.
 DOC_END
 
 NAME: retry_on_error

=== modified file 'src/client_side.cc'
--- src/client_side.cc 2010-07-06 23:09:44 +0000
+++ src/client_side.cc 2010-07-17 06:08:57 +0000
@@ -85,6 +85,7 @@
 
 #include "acl/FilledChecklist.h"
 #include "auth/UserRequest.h"
+#include "base/TextException.h"
 #include "ChunkedCodingParser.h"
 #include "client_side.h"
 #include "client_side_reply.h"
@@ -92,9 +93,8 @@
 #include "ClientRequestContext.h"
 #include "clientStream.h"
 #include "comm.h"
+#include "comm/Connection.h"
 #include "comm/ListenStateData.h"
-#include "base/TextException.h"
-#include "ConnectionDetail.h"
 #include "eui/Config.h"
 #include "fde.h"
 #include "HttpHdrContRange.h"
@@ -3085,7 +3085,7 @@
 
 /** Handle a new connection on HTTP socket. */
 void
-httpAccept(int sock, int newfd, ConnectionDetail *details,
+httpAccept(int sock, int newfd, Comm::ConnectionPointer &details,
            comm_err_t flag, int xerrno, void *data)
 {
     http_port_list *s = (http_port_list *)data;
@@ -3098,7 +3098,7 @@
 
     debugs(33, 4, "httpAccept: FD " << newfd << ": accepted");
     fd_note(newfd, "client http connect");
- connState = connStateCreate(&details->peer, &details->me, newfd, s);
+ connState = connStateCreate(&details->remote, &details->local, newfd, s);
 
     typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
     AsyncCall::Pointer call = asyncCall(33, 5, "ConnStateData::connStateClosed",
@@ -3106,7 +3106,7 @@
     comm_add_close_handler(newfd, call);
 
     if (Config.onoff.log_fqdn)
- fqdncache_gethostbyaddr(details->peer, FQDN_LOOKUP_IF_MISS);
+ fqdncache_gethostbyaddr(details->remote, FQDN_LOOKUP_IF_MISS);
 
     typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = asyncCall(33, 5, "ConnStateData::requestTimeout",
@@ -3116,19 +3116,19 @@
 #if USE_IDENT
     if (Ident::TheConfig.identLookup) {
         ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
- identChecklist.src_addr = details->peer;
- identChecklist.my_addr = details->me;
+ identChecklist.src_addr = details->remote;
+ identChecklist.my_addr = details->local;
         if (identChecklist.fastCheck())
- Ident::Start(details->me, details->peer, clientIdentDone, connState);
+ Ident::Start(details, clientIdentDone, connState);
     }
 #endif
 
 #if USE_SQUID_EUI
     if (Eui::TheConfig.euiLookup) {
- if (details->peer.IsIPv4()) {
- connState->peer_eui48.lookup(details->peer);
- } else if (details->peer.IsIPv6()) {
- connState->peer_eui64.lookup(details->peer);
+ if (details->remote.IsIPv4()) {
+ connState->peer_eui48.lookup(details->remote);
+ } else if (details->remote.IsIPv6()) {
+ connState->peer_eui64.lookup(details->remote);
         }
     }
 #endif
@@ -3139,7 +3139,7 @@
 
     connState->readSomeData();
 
- clientdbEstablished(details->peer, 1);
+ clientdbEstablished(details->remote, 1);
 
     incoming_sockets_accepted++;
 }
@@ -3148,7 +3148,7 @@
 
 /** Create SSL connection structure and update fd_table */
 static SSL *
-httpsCreate(int newfd, ConnectionDetail *details, SSL_CTX *sslContext)
+httpsCreate(int newfd, Comm::ConnectionPointer details, SSL_CTX *sslContext)
 {
     SSL *ssl = SSL_new(sslContext);
 
@@ -3291,7 +3291,7 @@
 
 /** handle a new HTTPS connection */
 static void
-httpsAccept(int sock, int newfd, ConnectionDetail *details,
+httpsAccept(int sock, int newfd, Comm::ConnectionPointer& details,
             comm_err_t flag, int xerrno, void *data)
 {
     https_port_list *s = (https_port_list *)data;
@@ -3309,7 +3309,7 @@
 
     debugs(33, 5, "httpsAccept: FD " << newfd << " accepted, starting SSL negotiation.");
     fd_note(newfd, "client https connect");
- ConnStateData *connState = connStateCreate(details->peer, details->me,
+ ConnStateData *connState = connStateCreate(details->remote, details->local,
                                newfd, &s->http);
     typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
     AsyncCall::Pointer call = asyncCall(33, 5, "ConnStateData::connStateClosed",
@@ -3317,7 +3317,7 @@
     comm_add_close_handler(newfd, call);
 
     if (Config.onoff.log_fqdn)
- fqdncache_gethostbyaddr(details->peer, FQDN_LOOKUP_IF_MISS);
+ fqdncache_gethostbyaddr(details->remote, FQDN_LOOKUP_IF_MISS);
 
     typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = asyncCall(33, 5, "ConnStateData::requestTimeout",
@@ -3327,10 +3327,10 @@
 #if USE_IDENT
     if (Ident::TheConfig.identLookup) {
         ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
- identChecklist.src_addr = details->peer;
- identChecklist.my_addr = details->me;
+ identChecklist.src_addr = details->remote;
+ identChecklist.my_addr = details->local;
         if (identChecklist.fastCheck())
- Ident::Start(details->me, details->peer, clientIdentDone, connState);
+ Ident::Start(details, clientIdentDone, connState);
     }
 #endif
 
@@ -3340,7 +3340,7 @@
 
     commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
 
- clientdbEstablished(details->peer, 1);
+ clientdbEstablished(details->remote, 1);
 
     incoming_sockets_accepted++;
 }
@@ -3357,10 +3357,10 @@
 
     debugs(33, 5, HERE << "converting FD " << fd << " to SSL");
 
- // fake a ConnectionDetail object; XXX: make ConnState a ConnectionDetail?
- ConnectionDetail detail;
- detail.me = me;
- detail.peer = peer;
+ // fake a Comm::Connection object; XXX: make ConnState a Comm::Connection?
+ Comm::Connection detail;
+ detail.local = me;
+ detail.remote = peer;
 
     SSL_CTX *sslContext = port->sslContext;
     SSL *ssl = NULL;

=== modified file 'src/client_side.h'
--- src/client_side.h 2010-05-04 12:33:30 +0000
+++ src/client_side.h 2010-06-03 07:18:25 +0000
@@ -124,8 +124,6 @@
 };
 
 
-class ConnectionDetail;
-
 /** A connection to a socket */
 class ConnStateData : public BodyProducer/*, public RefCountable*/
 {

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc 2010-07-13 16:26:20 +0000
+++ src/client_side_request.cc 2010-07-15 10:04:21 +0000
@@ -541,7 +541,7 @@
         acl_checklist = clientAclChecklistCreate(Config.accessList.adapted_http, http);
         acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
     } else {
- debugs(85, 2, HERE << "No adapted_http_access configuration.");
+ debugs(85, 2, HERE << "No adapted_http_access configuration. default: ALLOW");
         clientAccessCheckDone(ACCESS_ALLOWED);
     }
 }

=== modified file 'src/comm.cc'
--- src/comm.cc 2010-07-06 23:09:44 +0000
+++ src/comm.cc 2010-07-18 08:21:22 +0000
@@ -33,16 +33,17 @@
  */
 
 #include "squid.h"
+#include "base/AsyncCall.h"
 #include "StoreIOBuffer.h"
 #include "comm.h"
 #include "event.h"
 #include "fde.h"
 #include "comm/AcceptLimiter.h"
 #include "comm/comm_internal.h"
+#include "comm/Connection.h"
 #include "comm/ListenStateData.h"
 #include "CommIO.h"
 #include "CommRead.h"
-#include "ConnectionDetail.h"
 #include "MemBuf.h"
 #include "pconn.h"
 #include "SquidTime.h"
@@ -197,38 +198,6 @@
 {
 }
 
-class ConnectStateData
-{
-
-public:
- void *operator new (size_t);
- void operator delete (void *);
- static void Connect (int fd, void *me);
- void connect();
- void callCallback(comm_err_t status, int xerrno);
- void defaults();
-
-// defaults given by client
- char *host;
- u_short default_port;
- Ip::Address default_addr;
- // NP: CANNOT store the default addr:port together as it gets set/reset differently.
-
- DnsLookupDetails dns; ///< host lookup details
- Ip::Address S;
- AsyncCall::Pointer callback;
-
- int fd;
- int tries;
- int addrcount;
- int connstart;
-
-private:
- int commResetFD();
- int commRetryConnect();
- CBDATA_CLASS(ConnectStateData);
-};
-
 /* STATIC */
 
 static DescriptorSet *TheHalfClosed = NULL; /// the set of half-closed FDs
@@ -243,7 +212,6 @@
 static void commSetTcpNoDelay(int);
 #endif
 static void commSetTcpRcvbuf(int, int);
-static PF commConnectFree;
 static PF commHandleWrite;
 static IPH commConnectDnsHandle;
 
@@ -553,16 +521,12 @@
 
     temp.FreeAddrInfo(addr);
 
- F->local_addr.SetPort(temp.GetPort());
-
-#if 0 // seems to undo comm_open actions on the FD ...
- // grab default socket information for this address
- temp.GetAddrInfo(addr);
-
- F->sock_family = addr->ai_family;
-
- temp.FreeAddrInfo(addr);
-#endif
+ if (F->local_addr.IsAnyAddr()) {
+ /* save the whole local address, not just the port. */
+ F->local_addr = temp;
+ } else {
+ F->local_addr.SetPort(temp.GetPort());
+ }
 
     debugs(5, 6, "comm_local_port: FD " << fd << ": port " << F->local_addr.GetPort() << "(family=" << F->sock_family << ")");
     return F->local_addr.GetPort();
@@ -896,111 +860,6 @@
      */
 }
 
-
-CBDATA_CLASS_INIT(ConnectStateData);
-
-void *
-ConnectStateData::operator new (size_t size)
-{
- CBDATA_INIT_TYPE(ConnectStateData);
- return cbdataAlloc(ConnectStateData);
-}
-
-void
-ConnectStateData::operator delete (void *address)
-{
- cbdataFree(address);
-}
-
-
-
-void
-commConnectStart(int fd, const char *host, u_short port, AsyncCall::Pointer &cb)
-{
- debugs(cb->debugSection, cb->debugLevel, "commConnectStart: FD " << fd <<
- ", cb " << cb << ", " << host << ":" << port); // TODO: just print *cb
-
- ConnectStateData *cs;
- cs = new ConnectStateData;
- cs->fd = fd;
- cs->host = xstrdup(host);
- cs->default_port = port;
- cs->callback = cb;
-
- comm_add_close_handler(fd, commConnectFree, cs);
- ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);
-}
-
-// TODO: Remove this and similar callback registration functions by replacing
-// (callback,data) parameters with an AsyncCall so that we do not have to use
-// a generic call name and debug level when creating an AsyncCall. This will
-// also cut the number of callback registration routines in half.
-void
-commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data)
-{
- debugs(5, 5, "commConnectStart: FD " << fd << ", data " << data << ", " << host << ":" << port);
- AsyncCall::Pointer call = commCbCall(5,3,
- "SomeCommConnectHandler", CommConnectCbPtrFun(callback, data));
- commConnectStart(fd, host, port, call);
-}
-
-static void
-commConnectDnsHandle(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data)
-{
- ConnectStateData *cs = (ConnectStateData *)data;
- cs->dns = details;
-
- if (ia == NULL) {
- debugs(5, 3, "commConnectDnsHandle: Unknown host: " << cs->host);
- cs->callCallback(COMM_ERR_DNS, 0);
- return;
- }
-
- assert(ia->cur < ia->count);
-
- cs->default_addr = ia->in_addrs[ia->cur];
-
- if (Config.onoff.balance_on_multiple_ip)
- ipcacheCycleAddr(cs->host, NULL);
-
- cs->addrcount = ia->count;
-
- cs->connstart = squid_curtime;
-
- cs->connect();
-}
-
-void
-ConnectStateData::callCallback(comm_err_t status, int xerrno)
-{
- debugs(5, 3, "commConnectCallback: FD " << fd);
-
- comm_remove_close_handler(fd, commConnectFree, this);
- commSetTimeout(fd, -1, NULL, NULL);
-
- typedef CommConnectCbParams Params;
- Params &params = GetCommParams<Params>(callback);
- params.fd = fd;
- params.dns = dns;
- params.flag = status;
- params.xerrno = xerrno;
- ScheduleCallHere(callback);
- callback = NULL;
-
- commConnectFree(fd, this);
-}
-
-static void
-commConnectFree(int fd, void *data)
-{
- ConnectStateData *cs = (ConnectStateData *)data;
- debugs(5, 3, "commConnectFree: FD " << fd);
-// delete cs->callback;
- cs->callback = NULL;
- safe_free(cs->host);
- delete cs;
-}
-
 static void
 copyFDFlags(int to, fde *F)
 {
@@ -1021,198 +880,6 @@
         commSetTcpRcvbuf(to, Config.tcpRcvBufsz);
 }
 
-/* Reset FD so that we can connect() again */
-int
-ConnectStateData::commResetFD()
-{
-
-// XXX: do we have to check this?
-//
-// if (!cbdataReferenceValid(callback.data))
-// return 0;
-
- statCounter.syscalls.sock.sockets++;
-
- fde *F = &fd_table[fd];
-
- struct addrinfo *AI = NULL;
- F->local_addr.GetAddrInfo(AI);
- int new_family = AI->ai_family;
-
- int fd2 = socket(new_family, AI->ai_socktype, AI->ai_protocol);
-
- if (fd2 < 0) {
- debugs(5, DBG_CRITICAL, HERE << "WARNING: FD " << fd2 << " socket failed to allocate: " << xstrerror());
-
- if (ENFILE == errno || EMFILE == errno)
- fdAdjustReserved();
-
- F->local_addr.FreeAddrInfo(AI);
- return 0;
- }
-
-#ifdef _SQUID_MSWIN_
-
- /* On Windows dup2() can't work correctly on Sockets, the */
- /* workaround is to close the destination Socket before call them. */
- close(fd);
-
-#endif
-
- if (dup2(fd2, fd) < 0) {
- debugs(5, DBG_CRITICAL, HERE << "WARNING: dup2(FD " << fd2 << ", FD " << fd << ") failed: " << xstrerror());
-
- if (ENFILE == errno || EMFILE == errno)
- fdAdjustReserved();
-
- close(fd2);
-
- F->local_addr.FreeAddrInfo(AI);
- return 0;
- }
- commResetSelect(fd);
-
- close(fd2);
-
- debugs(50, 3, "commResetFD: Reset socket FD " << fd << "->" << fd2 << " : family=" << new_family );
-
- /* INET6: copy the new sockets family type to the FDE table */
- F->sock_family = new_family;
-
- F->flags.called_connect = 0;
-
- /*
- * yuck, this has assumptions about comm_open() arguments for
- * the original socket
- */
-
- /* MUST be done before binding or face OS Error: "(99) Cannot assign requested address"... */
- if ( F->flags.transparent ) {
- comm_set_transparent(fd);
- }
-
- if (commBind(fd, *AI) != COMM_OK) {
- debugs(5, DBG_CRITICAL, "WARNING: Reset of FD " << fd << " for " << F->local_addr << " failed to bind: " << xstrerror());
- F->local_addr.FreeAddrInfo(AI);
- return 0;
- }
- F->local_addr.FreeAddrInfo(AI);
-
- if (F->tos)
- comm_set_tos(fd, F->tos);
-
-#if IPV6_SPECIAL_SPLITSTACK
- if ( F->local_addr.IsIPv6() )
- comm_set_v6only(fd, 1);
-#endif
-
- copyFDFlags(fd, F);
-
- return 1;
-}
-
-int
-ConnectStateData::commRetryConnect()
-{
- assert(addrcount > 0);
-
- if (addrcount == 1) {
- if (tries >= Config.retry.maxtries)
- return 0;
-
- if (squid_curtime - connstart > Config.Timeout.connect)
- return 0;
- } else {
- if (tries > addrcount) {
- /* Flush bad address count in case we are
- * skipping over incompatible protocol
- */
- ipcacheMarkAllGood(host);
- return 0;
- }
- }
-
- return commResetFD();
-}
-
-static void
-commReconnect(void *data)
-{
- ConnectStateData *cs = (ConnectStateData *)data;
- ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);
-}
-
-/** Connect SOCK to specified DEST_PORT at DEST_HOST. */
-void
-ConnectStateData::Connect(int fd, void *me)
-{
- ConnectStateData *cs = (ConnectStateData *)me;
- assert (cs->fd == fd);
- cs->connect();
-}
-
-void
-ConnectStateData::defaults()
-{
- S = default_addr;
- S.SetPort(default_port);
-}
-
-void
-ConnectStateData::connect()
-{
- defaults();
-
- debugs(5,5, HERE << "to " << S);
-
- switch (comm_connect_addr(fd, S) ) {
-
- case COMM_INPROGRESS:
- debugs(5, 5, HERE << "FD " << fd << ": COMM_INPROGRESS");
- commSetSelect(fd, COMM_SELECT_WRITE, ConnectStateData::Connect, this, 0);
- break;
-
- case COMM_OK:
- debugs(5, 5, HERE << "FD " << fd << ": COMM_OK - connected");
- ipcacheMarkGoodAddr(host, S);
- callCallback(COMM_OK, 0);
- break;
-
- case COMM_ERR_PROTOCOL:
- debugs(5, 5, HERE "FD " << fd << ": COMM_ERR_PROTOCOL - try again");
- /* problem using the desired protocol over this socket.
- * skip to the next address and hope it's more compatible
- * but do not mark the current address as bad
- */
- tries++;
- if (commRetryConnect()) {
- /* Force an addr cycle to move forward to the next possible address */
- ipcacheCycleAddr(host, NULL);
- eventAdd("commReconnect", commReconnect, this, this->addrcount == 1 ? 0.05 : 0.0, 0);
- } else {
- debugs(5, 5, HERE << "FD " << fd << ": COMM_ERR_PROTOCOL - ERR tried too many times already.");
- callCallback(COMM_ERR_CONNECT, errno);
- }
- break;
-
- default:
- debugs(5, 5, HERE "FD " << fd << ": * - try again");
- tries++;
- ipcacheMarkBadAddr(host, S);
-
-#if USE_ICMP
- if (Config.onoff.test_reachability)
- netdbDeleteAddrNetwork(S);
-#endif
-
- if (commRetryConnect()) {
- eventAdd("commReconnect", commReconnect, this, this->addrcount == 1 ? 0.05 : 0.0, 0);
- } else {
- debugs(5, 5, HERE << "FD " << fd << ": * - ERR tried too many times already.");
- callCallback(COMM_ERR_CONNECT, errno);
- }
- }
-}
 /*
 int
 commSetTimeout_old(int fd, int timeout, PF * handler, void *data)
@@ -1254,7 +921,8 @@
 }
 
 
-int commSetTimeout(int fd, int timeout, AsyncCall::Pointer &callback)
+int
+commSetTimeout(int fd, int timeout, AsyncCall::Pointer &callback)
 {
     debugs(5, 3, HERE << "FD " << fd << " timeout " << timeout);
     assert(fd >= 0);
@@ -2457,10 +2125,6 @@
     cancelled = true;
 }
 
-ConnectionDetail::ConnectionDetail() : me(), peer()
-{
-}
-
 int
 CommSelectEngine::checkEvents(int timeout)
 {

=== modified file 'src/comm.h'
--- src/comm.h 2010-07-06 23:09:44 +0000
+++ src/comm.h 2010-07-13 08:38:07 +0000
@@ -4,33 +4,15 @@
 #include "squid.h"
 #include "AsyncEngine.h"
 #include "base/AsyncCall.h"
+#include "CommCalls.h"
+#include "comm/comm_err_t.h"
+#include "comm/forward.h"
+#include "ip/Address.h"
 #include "StoreIOBuffer.h"
-#include "Array.h"
-#include "ip/Address.h"
 
 #define COMMIO_FD_READCB(fd) (&commfd_table[(fd)].readcb)
 #define COMMIO_FD_WRITECB(fd) (&commfd_table[(fd)].writecb)
 
-typedef enum {
- COMM_OK = 0,
- COMM_ERROR = -1,
- COMM_NOMESSAGE = -3,
- COMM_TIMEOUT = -4,
- COMM_SHUTDOWN = -5,
- COMM_IDLE = -6, /* there are no active fds and no pending callbacks. */
- COMM_INPROGRESS = -7,
- COMM_ERR_CONNECT = -8,
- COMM_ERR_DNS = -9,
- COMM_ERR_CLOSING = -10,
- COMM_ERR_PROTOCOL = -11, /* IPv4 or IPv6 cannot be used on the fd socket */
- COMM_ERR__END__ = -999999 /* Dummy entry to make syntax valid (comma on line above), do not use. New entries added above */
-} comm_err_t;
-
-class DnsLookupDetails;
-typedef void CNCB(int fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data);
-
-typedef void IOCB(int fd, char *, size_t size, comm_err_t flag, int xerrno, void *data);
-
 
 /* comm.c */
 extern bool comm_iocallbackpending(void); /* inline candidate */
@@ -40,13 +22,11 @@
 SQUIDCEXTERN void commSetCloseOnExec(int fd);
 SQUIDCEXTERN void commSetTcpKeepalive(int fd, int idle, int interval, int timeout);
 extern void _comm_close(int fd, char const *file, int line);
-#define comm_close(fd) (_comm_close((fd), __FILE__, __LINE__))
+#define comm_close(x) (_comm_close((x), __FILE__, __LINE__))
 SQUIDCEXTERN void comm_reset_close(int fd);
 #if LINGERING_CLOSE
 SQUIDCEXTERN void comm_lingering_close(int fd);
 #endif
-SQUIDCEXTERN void commConnectStart(int fd, const char *, u_short, CNCB *, void *);
-void commConnectStart(int fd, const char *, u_short, AsyncCall::Pointer &cb);
 
 SQUIDCEXTERN int comm_connect_addr(int sock, const Ip::Address &addr);
 SQUIDCEXTERN void comm_init(void);
@@ -101,8 +81,7 @@
 SQUIDCEXTERN comm_err_t comm_select(int);
 SQUIDCEXTERN void comm_quick_poll_required(void);
 
-class ConnectionDetail;
-typedef void IOACB(int fd, int nfd, ConnectionDetail *details, comm_err_t flag, int xerrno, void *data);
+//typedef void IOACB(int fd, int nfd, Comm::ConnectionPointer details, comm_err_t flag, int xerrno, void *data);
 extern void comm_add_close_handler(int fd, PF *, void *);
 extern void comm_add_close_handler(int fd, AsyncCall::Pointer &);
 extern void comm_remove_close_handler(int fd, PF *, void *);

=== added file 'src/comm/ConnOpener.cc'
--- src/comm/ConnOpener.cc 1970-01-01 00:00:00 +0000
+++ src/comm/ConnOpener.cc 2010-07-20 14:30:00 +0000
@@ -0,0 +1,267 @@
+/*
+ * DEBUG: section 05 Socket Connection Opener
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "comm/ConnOpener.h"
+#include "comm/Connection.h"
+#include "comm.h"
+#include "fde.h"
+#include "icmp/net_db.h"
+#include "SquidTime.h"
+
+namespace Comm {
+ CBDATA_CLASS_INIT(ConnOpener);
+};
+
+Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer &c, AsyncCall::Pointer &handler, time_t ctimeout) :
+ AsyncJob("Comm::ConnOpener"),
+ host_(NULL),
+ conn_(c),
+ callback_(handler),
+ totalTries_(0),
+ failRetries_(0),
+ connectTimeout_(ctimeout),
+ connStart_(0)
+{}
+
+Comm::ConnOpener::~ConnOpener()
+{
+ safe_free(host_);
+}
+
+bool
+Comm::ConnOpener::doneAll() const
+{
+ // is the conn_ to be opened still waiting?
+ if (conn_ != NULL) {
+ return false;
+ }
+
+ // is the callback still to be called?
+ if (callback_ != NULL) {
+ return false;
+ }
+
+ return AsyncJob::doneAll();
+}
+
+void
+Comm::ConnOpener::swanSong()
+{
+ // cancel any event watchers
+ // done here to get the "swanSong" mention in cancel debugging.
+ if (calls_.earlyAbort_ != NULL) {
+ calls_.earlyAbort_->cancel("Comm::ConnOpener::swanSong");
+ calls_.earlyAbort_ = NULL;
+ }
+ if (calls_.timeout_ != NULL) {
+ calls_.timeout_->cancel("Comm::ConnOpener::swanSong");
+ calls_.timeout_ = NULL;
+ }
+
+ // recover what we can from the job
+ if (conn_ != NULL && conn_->isOpen()) {
+ // it never reached fully open, so abort the FD
+ conn_->close();
+ fd_table[conn_->fd].flags.open = 0;
+ // inform the caller
+ doneConnecting(COMM_ERR_CONNECT, 0);
+ }
+}
+
+void
+Comm::ConnOpener::setHost(const char * new_host)
+{
+ // unset and erase if already set.
+ if (host_ != NULL)
+ safe_free(host_);
+
+ // set the new one if given.
+ if (new_host != NULL)
+ host_ = xstrdup(new_host);
+}
+
+const char *
+Comm::ConnOpener::getHost() const
+{
+ return host_;
+}
+
+/**
+ * Connection attempt are completed. One way or the other.
+ * Pass the results back to the external handler.
+ */
+void
+Comm::ConnOpener::doneConnecting(comm_err_t status, int xerrno)
+{
+ if (callback_ != NULL) {
+ typedef CommConnectCbParams Params;
+ Params &params = GetCommParams<Params>(callback_);
+ params.conn = conn_;
+ params.flag = status;
+ params.xerrno = xerrno;
+ ScheduleCallHere(callback_);
+ callback_ = NULL;
+ }
+
+ /* ensure cleared local state, we are done. */
+ conn_ = NULL;
+}
+
+void Comm::ConnOpener::start()
+{
+ Must(conn_ != NULL);
+
+ /* get a socket open ready for connecting with */
+ if (!conn_->isOpen()) {
+#if USE_IPV6
+ /* outbound sockets have no need to be protocol agnostic. */
+ if (conn_->remote.IsIPv4()) {
+ conn_->local.SetIPv4();
+ }
+#endif
+ conn_->fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, conn_->local, conn_->flags, conn_->tos, host_);
+ if (!conn_->isOpen()) {
+ doneConnecting(COMM_ERR_CONNECT, 0);
+ return;
+ }
+
+ if (calls_.earlyAbort_ == NULL) {
+ typedef CommCbMemFunT<Comm::ConnOpener, CommConnectCbParams> Dialer;
+ calls_.earlyAbort_ = asyncCall(5, 4, "Comm::ConnOpener::earlyAbort",
+ Dialer(this, &Comm::ConnOpener::earlyAbort));
+ comm_add_close_handler(conn_->fd, calls_.earlyAbort_);
+ }
+
+ if (calls_.timeout_ == NULL) {
+ typedef CommCbMemFunT<Comm::ConnOpener, CommTimeoutCbParams> Dialer;
+ calls_.timeout_ = asyncCall(5, 4, "Comm::ConnOpener::timeout",
+ Dialer(this, &Comm::ConnOpener::timeout));
+ debugs(5, 3, HERE << "FD " << conn_->fd << " timeout " << connectTimeout_);
+ commSetTimeout(conn_->fd, connectTimeout_, calls_.timeout_);
+ }
+
+ if (connStart_ == 0) {
+ connStart_ = squid_curtime;
+ }
+ }
+
+ typedef CommCbMemFunT<Comm::ConnOpener, CommConnectCbParams> Dialer;
+ calls_.connect_ = asyncCall(5, 4, "Comm::ConnOpener::connect",
+ Dialer(this, &Comm::ConnOpener::connect));
+ ScheduleCallHere(calls_.connect_);
+}
+
+/** Make an FD connection attempt.
+ * Handles the case(s) when a partially setup connection gets closed early.
+ */
+void
+Comm::ConnOpener::connect(const CommConnectCbParams &unused)
+{
+ Must(conn_ != NULL);
+
+ totalTries_++;
+
+ switch (comm_connect_addr(conn_->fd, conn_->remote) ) {
+
+ case COMM_INPROGRESS:
+ // check for timeout FIRST.
+ if(squid_curtime - connStart_ > connectTimeout_) {
+ debugs(5, 5, HERE << "FD " << conn_->fd << ": * - ERR took too long already.");
+ doneConnecting(COMM_TIMEOUT, errno);
+ return;
+ } else {
+ debugs(5, 5, HERE << "FD " << conn_->fd << ": COMM_INPROGRESS");
+ commSetSelect(conn_->fd, COMM_SELECT_WRITE, Comm::ConnOpener::ConnectRetry, this, 0);
+ }
+ break;
+
+ case COMM_OK:
+ debugs(5, 5, HERE << "FD " << conn_->fd << ": COMM_OK - connected");
+
+ /*
+ * stats.conn_open is used to account for the number of
+ * connections that we have open to the peer, so we can limit
+ * based on the max-conn option. We need to increment here,
+ * even if the connection may fail.
+ */
+ if (conn_->getPeer())
+ conn_->getPeer()->stats.conn_open++;
+
+ /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
+ * indicate the state of a raw fd object being passed around.
+ * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
+ * when those are done comm_local_port can become one of our member functions to do the below.
+ */
+ fd_table[conn_->fd].flags.open = 1;
+ conn_->local.SetPort(comm_local_port(conn_->fd));
+ if (conn_->local.IsAnyAddr()) {
+ conn_->local = fd_table[conn_->fd].local_addr;
+ }
+
+ if (host_ != NULL)
+ ipcacheMarkGoodAddr(host_, conn_->remote);
+ doneConnecting(COMM_OK, 0);
+ break;
+
+ default:
+ debugs(5, 5, HERE "FD " << conn_->fd << ": * - try again");
+ failRetries_++;
+ if (host_ != NULL)
+ ipcacheMarkBadAddr(host_, conn_->remote);
+#if USE_ICMP
+ if (Config.onoff.test_reachability)
+ netdbDeleteAddrNetwork(conn_->remote);
+#endif
+
+ // check for timeout FIRST.
+ if(squid_curtime - connStart_ > connectTimeout_) {
+ debugs(5, 5, HERE << "FD " << conn_->fd << ": * - ERR took too long already.");
+ doneConnecting(COMM_TIMEOUT, errno);
+ } else if (failRetries_ < Config.connect_retries) {
+ ScheduleCallHere(calls_.connect_);
+ } else {
+ // send ERROR back to the upper layer.
+ debugs(5, 5, HERE << "FD " << conn_->fd << ": * - ERR tried too many times already.");
+ doneConnecting(COMM_ERR_CONNECT, errno);
+ }
+ }
+}
+
+/** Abort connection attempt.
+ * Handles the case(s) when a partially setup connection gets closed early.
+ */
+void
+Comm::ConnOpener::earlyAbort(const CommConnectCbParams &io)
+{
+ debugs(5, 3, HERE << "FD " << io.conn->fd);
+ doneConnecting(COMM_ERR_CLOSING, io.xerrno); // NP: is closing or shutdown better?
+}
+
+/**
+ * Handles the case(s) when a partially setup connection gets timed out.
+ * NP: When commSetTimeout accepts generic CommCommonCbParams this can die.
+ */
+void
+Comm::ConnOpener::timeout(const CommTimeoutCbParams &unused)
+{
+ ScheduleCallHere(calls_.connect_);
+}
+
+/* Legacy Wrapper for the retry event after COMM_INPROGRESS
+ * TODO: As soon as comm IO accepts Async calls we can use a ConnOpener::connect call
+ */
+void
+Comm::ConnOpener::ConnectRetry(int fd, void *data)
+{
+ ConnOpener *cs = static_cast<Comm::ConnOpener *>(data);
+
+ // Ew. we are now outside the all AsyncJob protections.
+ // get back inside by scheduling another call...
+ typedef CommCbMemFunT<Comm::ConnOpener, CommConnectCbParams> Dialer;
+ AsyncCall::Pointer call = asyncCall(5, 4, "Comm::ConnOpener::connect",
+ Dialer(cs, &Comm::ConnOpener::connect));
+ ScheduleCallHere(call);
+}

=== added file 'src/comm/ConnOpener.h'
--- src/comm/ConnOpener.h 1970-01-01 00:00:00 +0000
+++ src/comm/ConnOpener.h 2010-07-20 14:30:00 +0000
@@ -0,0 +1,74 @@
+#ifndef _SQUID_SRC_COMM_OPENERSTATEDATA_H
+#define _SQUID_SRC_COMM_OPENERSTATEDATA_H
+
+#include "base/AsyncCall.h"
+#include "base/AsyncJob.h"
+#include "cbdata.h"
+#include "CommCalls.h"
+#include "comm/comm_err_t.h"
+#include "comm/forward.h"
+
+namespace Comm {
+
+/**
+ * Async-opener of a Comm connection.
+ */
+class ConnOpener : public AsyncJob
+{
+protected:
+ virtual void start();
+ virtual void swanSong();
+
+public:
+ virtual bool doneAll() const;
+
+ /** attempt to open a connection. */
+ ConnOpener(Comm::ConnectionPointer &, AsyncCall::Pointer &handler, time_t connect_timeout);
+ ~ConnOpener();
+
+ void setHost(const char *); ///< set the hostname note for this connection
+ const char * getHost() const; ///< get the hostname noted for this connection
+ void tryConnecting(); ///< actually start opening a TCP connection.
+
+private:
+ /* These objects may NOT be created without connections to act on. Do not define this operator. */
+ ConnOpener(const ConnOpener &);
+ /* These objects may NOT be copied. Do not define this operator. */
+ ConnOpener & operator =(const ConnOpener &c);
+
+ void connect(const CommConnectCbParams &unused);
+ void earlyAbort(const CommConnectCbParams &);
+ void timeout(const CommTimeoutCbParams &unused);
+ void doneConnecting(comm_err_t status, int xerrno);
+ static void ConnectRetry(int fd, void *data);
+
+private:
+ char *host_; ///< domain name we are trying to connect to.
+ Comm::ConnectionPointer conn_; ///< single connection currently being opened.
+ AsyncCall::Pointer callback_; ///< handler to be called on connection completion.
+
+ int totalTries_; ///< total number of connection attempts over all destinations so far.
+ int failRetries_; ///< number of retries current destination has been tried.
+
+ /**
+ * time at which to abandon the connection.
+ * the connection-done callback will be passed COMM_TIMEOUT
+ */
+ time_t connectTimeout_;
+
+ /// time at which this series of connection attempts was started.
+ time_t connStart_;
+
+ /// handles to calls which we may need to cancel.
+ struct Calls {
+ AsyncCall::Pointer connect_;
+ AsyncCall::Pointer earlyAbort_;
+ AsyncCall::Pointer timeout_;
+ } calls_;
+
+ CBDATA_CLASS2(ConnOpener);
+};
+
+}; // namespace Comm
+
+#endif /* _SQUID_SRC_COMM_CONNOPENER_H */

=== added file 'src/comm/Connection.cc'
--- src/comm/Connection.cc 1970-01-01 00:00:00 +0000
+++ src/comm/Connection.cc 2010-07-20 07:28:33 +0000
@@ -0,0 +1,70 @@
+#include "config.h"
+#include "cbdata.h"
+#include "comm.h"
+#include "comm/Connection.h"
+
+Comm::Connection::Connection() :
+ local(),
+ remote(),
+ peerType(HIER_NONE),
+ fd(-1),
+ tos(0),
+ flags(COMM_NONBLOCKING),
+ _peer(NULL)
+{}
+
+Comm::Connection::~Connection()
+{
+ close();
+ cbdataReferenceDone(_peer);
+}
+
+Comm::ConnectionPointer &
+Comm::Connection::copyDetails() const
+{
+ ConnectionPointer c = new Comm::Connection;
+
+ c->local = local;
+ c->remote = remote;
+ c->peerType = peerType;
+ c->tos = tos;
+ c->flags = flags;
+
+ // ensure FD is not open in the new copy.
+ c->fd = -1;
+
+ // ensure we have a cbdata reference to _peer not a straight ptr copy.
+ c->_peer = cbdataReference(_peer);
+
+ return c;
+}
+
+void
+Comm::Connection::close()
+{
+ if (isOpen()) {
+ comm_close(fd);
+ fd = -1;
+ if (_peer)
+ _peer->stats.conn_open--;
+ }
+}
+
+void
+Comm::Connection::setPeer(peer *p)
+{
+ /* set to self. nothing to do. */
+ if (_peer == p)
+ return;
+
+ /* clear any previous ptr */
+ if (_peer) {
+ cbdataReferenceDone(_peer);
+ _peer = NULL;
+ }
+
+ /* set the new one (unless it is NULL */
+ if (p) {
+ _peer = cbdataReference(p);
+ }
+}

=== renamed file 'src/ConnectionDetail.h' => 'src/comm/Connection.h'
--- src/ConnectionDetail.h 2010-05-26 03:06:02 +0000
+++ src/comm/Connection.h 2010-07-20 13:09:51 +0000
@@ -1,5 +1,6 @@
 /*
  * DEBUG: section 05 Socket Functions
+ * AUTHOR: Amos Jeffries
  * AUTHOR: Robert Collins
  *
  * SQUID Web Proxy Cache http://www.squid-cache.org/
@@ -30,23 +31,109 @@
  *
  *
  * Copyright (c) 2003, Robert Collins <robertc_at_squid-cache.org>
+ * Copyright (c) 2010, Amos Jeffries <amosjeffries_at_squid-cache.org>
  */
 
 #ifndef _SQUIDCONNECTIONDETAIL_H_
 #define _SQUIDCONNECTIONDETAIL_H_
 
+#include "comm/forward.h"
+#include "hier_code.h"
 #include "ip/Address.h"
-
-class ConnectionDetail
+#include "RefCount.h"
+
+struct peer;
+
+namespace Comm {
+
+/* TODO: make these a struct of boolean flags members in the connection instead of a bitmap.
+ * we can't do that until all non-comm code uses Commm::Connection objects to create FD
+ * currently there is code still using comm_open() and comm_openex() synchronously!!
+ */
+#define COMM_UNSET 0x00
+#define COMM_NONBLOCKING 0x01
+#define COMM_NOCLOEXEC 0x02
+#define COMM_REUSEADDR 0x04
+#define COMM_TRANSPARENT 0x08
+#define COMM_DOBIND 0x10
+
+/**
+ * Store data about the physical and logical attributes of a connection.
+ *
+ * Some link state can be infered from the data, however this is not an
+ * object for state data. But a semantic equivalent for FD with easily
+ * accessible cached properties not requiring repeated complex lookups.
+ *
+ * While the properties may be changed, this is for teh purpose of creating
+ * potential connection descriptors which may be opened. Properties should
+ * be considered read-only outside of the Comm layer code once the connection
+ * is open.
+ *
+ * These objects must not be passed around directly,
+ * but a Comm::ConnectionPointer must be passed instead.
+ */
+class Connection : public RefCountable
 {
-
-public:
-
- ConnectionDetail();
-
- Ip::Address me;
-
- Ip::Address peer;
+public:
+ /** standard empty connection creation */
+ Connection();
+
+ /** Clear the connection properties and close any open socket. */
+ ~Connection();
+
+ /** Copy an existing connections IP and properties.
+ * This excludes the FD. The new copy will be a closed connection.
+ */
+ ConnectionPointer & copyDetails() const;
+
+ /** Close any open socket. */
+ void close();
+
+ /** determine whether this object describes an active connection or not. */
+ bool isOpen() const { return (fd >= 0); }
+
+ /** retrieve the peer pointer for use.
+ * The caller is responsible for all CBDATA operations regarding the
+ * used of the pointer returned.
+ */
+ peer * const getPeer() const { return _peer; }
+
+ /** alter the stored peer pointer.
+ * Perform appropriate CBDATA operations for locking the peer pointer
+ */
+ void setPeer(peer * p);
+
+private:
+ /** These objects may not be exactly duplicated. Use copyDetails() instead. */
+ Connection(const Connection &c);
+
+ /** These objects may not be exactly duplicated. Use copyDetails() instead. */
+ Connection & operator =(const Connection &c);
+
+public:
+ /** Address/Port for the Squid end of a TCP link. */
+ Ip::Address local;
+
+ /** Address for the Remote end of a TCP link. */
+ Ip::Address remote;
+
+ /** Hierarchy code for this connection link */
+ hier_code peerType;
+
+ /** Socket used by this connection. -1 if no socket has been opened. */
+ int fd;
+
+ /** Quality of Service TOS values currently sent on this connection */
+ int tos;
+
+ /** COMM flags set on this connection */
+ int flags;
+
+private:
+ /** cache_peer data object (if any) */
+ peer *_peer;
 };
 
+}; // namespace Comm
+
 #endif

=== modified file 'src/comm/ListenStateData.cc'
--- src/comm/ListenStateData.cc 2010-07-07 00:45:34 +0000
+++ src/comm/ListenStateData.cc 2010-07-13 08:38:07 +0000
@@ -35,9 +35,9 @@
 #include "squid.h"
 #include "CommCalls.h"
 #include "comm/AcceptLimiter.h"
+#include "comm/Connection.h"
 #include "comm/comm_internal.h"
 #include "comm/ListenStateData.h"
-#include "ConnectionDetail.h"
 #include "fde.h"
 #include "protos.h"
 #include "SquidTime.h"
@@ -151,8 +151,8 @@
      */
 
     /* Accept a new connection */
- ConnectionDetail connDetails;
- int newfd = oldAccept(connDetails);
+ Connection *connDetails = new Connection();
+ int newfd = oldAccept(*connDetails);
 
     /* Check for errors */
     if (newfd < 0) {
@@ -172,7 +172,7 @@
     }
 
     debugs(5, 5, HERE << "accepted: FD " << fd <<
- " newfd: " << newfd << " from: " << connDetails.peer <<
+ " newfd: " << newfd << " from: " << connDetails->remote <<
            " handler: " << theCallback);
     notify(newfd, COMM_OK, 0, connDetails);
 }
@@ -186,7 +186,7 @@
 }
 
 void
-Comm::ListenStateData::notify(int newfd, comm_err_t errcode, int xerrno, const ConnectionDetail &connDetails)
+Comm::ListenStateData::notify(int newfd, comm_err_t errcode, int xerrno, Comm::ConnectionPointer connDetails)
 {
     // listener socket handlers just abandon the port with COMM_ERR_CLOSING
     // it should only happen when this object is deleted...
@@ -213,17 +213,17 @@
  * Wait for an incoming connection on FD.
  */
 int
-Comm::ListenStateData::oldAccept(ConnectionDetail &details)
+Comm::ListenStateData::oldAccept(Comm::Connection &details)
 {
     PROF_start(comm_accept);
     statCounter.syscalls.sock.accepts++;
     int sock;
     struct addrinfo *gai = NULL;
- details.me.InitAddrInfo(gai);
+ details.local.InitAddrInfo(gai);
 
     if ((sock = accept(fd, gai->ai_addr, &gai->ai_addrlen)) < 0) {
 
- details.me.FreeAddrInfo(gai);
+ details.local.FreeAddrInfo(gai);
 
         PROF_stop(comm_accept);
 
@@ -239,21 +239,21 @@
         }
     }
 
- details.peer = *gai;
+ details.remote = *gai;
 
     if ( Config.client_ip_max_connections >= 0) {
- if (clientdbEstablished(details.peer, 0) > Config.client_ip_max_connections) {
- debugs(50, DBG_IMPORTANT, "WARNING: " << details.peer << " attempting more than " << Config.client_ip_max_connections << " connections.");
- details.me.FreeAddrInfo(gai);
+ if (clientdbEstablished(details.remote, 0) > Config.client_ip_max_connections) {
+ debugs(50, DBG_IMPORTANT, "WARNING: " << details.remote << " attempting more than " << Config.client_ip_max_connections << " connections.");
+ details.local.FreeAddrInfo(gai);
             return COMM_ERROR;
         }
     }
 
- details.me.InitAddrInfo(gai);
+ details.local.InitAddrInfo(gai);
 
- details.me.SetEmpty();
+ details.local.SetEmpty();
     getsockname(sock, gai->ai_addr, &gai->ai_addrlen);
- details.me = *gai;
+ details.local = *gai;
 
     commSetCloseOnExec(sock);
 
@@ -264,15 +264,15 @@
     fdd_table[sock].close_line = 0;
 
     fde *F = &fd_table[sock];
- details.peer.NtoA(F->ipaddr,MAX_IPSTRLEN);
- F->remote_port = details.peer.GetPort();
- F->local_addr.SetPort(details.me.GetPort());
+ details.remote.NtoA(F->ipaddr,MAX_IPSTRLEN);
+ F->remote_port = details.remote.GetPort();
+ F->local_addr.SetPort(details.local.GetPort());
 #if USE_IPV6
     F->sock_family = AF_INET;
 #else
- F->sock_family = details.me.IsIPv4()?AF_INET:AF_INET6;
+ F->sock_family = details.local.IsIPv4()?AF_INET:AF_INET6;
 #endif
- details.me.FreeAddrInfo(gai);
+ details.local.FreeAddrInfo(gai);
 
     commSetNonBlocking(sock);
 

=== modified file 'src/comm/ListenStateData.h'
--- src/comm/ListenStateData.h 2010-07-07 00:45:34 +0000
+++ src/comm/ListenStateData.h 2010-07-19 13:18:06 +0000
@@ -3,13 +3,13 @@
 
 #include "config.h"
 #include "base/AsyncCall.h"
-#include "comm.h"
+#include "comm/comm_err_t.h"
+#include "comm/forward.h"
+
 #if HAVE_MAP
 #include <map>
 #endif
 
-class ConnectionDetail;
-
 namespace Comm
 {
 
@@ -23,7 +23,7 @@
 
     void subscribe(AsyncCall::Pointer &call);
     void acceptNext();
- void notify(int newfd, comm_err_t, int xerrno, const ConnectionDetail &);
+ void notify(int newfd, comm_err_t, int xerrno, Comm::ConnectionPointer);
 
     int fd;
 
@@ -42,7 +42,7 @@
     static void doAccept(int fd, void *data);
 
     void acceptOne();
- int oldAccept(ConnectionDetail &details);
+ int oldAccept(Comm::Connection &details);
 
     AsyncCall::Pointer theCallback;
     bool mayAcceptMore;

=== modified file 'src/comm/Makefile.am'
--- src/comm/Makefile.am 2009-12-31 02:35:01 +0000
+++ src/comm/Makefile.am 2010-06-27 11:31:31 +0000
@@ -1,13 +1,22 @@
 include $(top_srcdir)/src/Common.am
 include $(top_srcdir)/src/TestHeaders.am
 
-noinst_LTLIBRARIES = libcomm-listener.la
+noinst_LTLIBRARIES = libcomm.la
 
-## Library holding listener comm socket handlers
-libcomm_listener_la_SOURCES= \
+## First group are listener comm socket handlers
+## Second group are outbound connection setup handlers
+## Third group are misc shared comm objects
+libcomm_la_SOURCES= \
         AcceptLimiter.cc \
         AcceptLimiter.h \
         ListenStateData.cc \
         ListenStateData.h \
         \
- comm_internal.h
+ ConnOpener.cc \
+ ConnOpener.h \
+ \
+ Connection.cc \
+ Connection.h \
+ comm_err_t.h \
+ comm_internal.h \
+ forward.h

=== added file 'src/comm/comm_err_t.h'
--- src/comm/comm_err_t.h 1970-01-01 00:00:00 +0000
+++ src/comm/comm_err_t.h 2010-05-19 11:28:21 +0000
@@ -0,0 +1,21 @@
+#ifndef _SQUID_COMM_COMM_ERR_T_H
+#define _SQUID_COMM_COMM_ERR_T_H
+
+#include "config.h"
+
+typedef enum {
+ COMM_OK = 0,
+ COMM_ERROR = -1,
+ COMM_NOMESSAGE = -3,
+ COMM_TIMEOUT = -4,
+ COMM_SHUTDOWN = -5,
+ COMM_IDLE = -6, /* there are no active fds and no pending callbacks. */
+ COMM_INPROGRESS = -7,
+ COMM_ERR_CONNECT = -8,
+ COMM_ERR_DNS = -9,
+ COMM_ERR_CLOSING = -10,
+ COMM_ERR_PROTOCOL = -11, /* IPv4 or IPv6 cannot be used on the fd socket */
+ COMM_ERR__END__ = -999999 /* Dummy entry to make syntax valid (comma on line above), do not use. New entries added above */
+} comm_err_t;
+
+#endif /* _SQUID_COMM_COMM_ERR_T_H */

=== added file 'src/comm/forward.h'
--- src/comm/forward.h 1970-01-01 00:00:00 +0000
+++ src/comm/forward.h 2010-06-27 11:31:31 +0000
@@ -0,0 +1,17 @@
+#ifndef _SQUID_COMM_FORWARD_H
+#define _SQUID_COMM_FORWARD_H
+
+#include "Array.h"
+#include "RefCount.h"
+
+namespace Comm {
+
+class Connection;
+
+typedef RefCount<Comm::Connection> ConnectionPointer;
+
+typedef Vector<Comm::ConnectionPointer> Paths;
+
+}; // namespace Comm
+
+#endif /* _SQUID_COMM_FORWARD_H */

=== modified file 'src/defines.h'
--- src/defines.h 2009-09-16 07:34:24 +0000
+++ src/defines.h 2010-05-19 11:28:21 +0000
@@ -62,12 +62,6 @@
 #define COMM_SELECT_READ (0x1)
 #define COMM_SELECT_WRITE (0x2)
 
-#define COMM_NONBLOCKING 0x01
-#define COMM_NOCLOEXEC 0x02
-#define COMM_REUSEADDR 0x04
-#define COMM_TRANSPARENT 0x08
-#define COMM_DOBIND 0x10
-
 #define safe_free(x) if (x) { xxfree(x); x = NULL; }
 
 #define DISK_OK (0)

=== modified file 'src/dns_internal.cc'
--- src/dns_internal.cc 2010-06-15 21:46:43 +0000
+++ src/dns_internal.cc 2010-07-18 11:55:28 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -33,16 +32,16 @@
  *
  */
 
-#include "config.h"
 #include "squid.h"
-#include "event.h"
 #include "CacheManager.h"
-#include "SquidTime.h"
-#include "Store.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
 #include "comm.h"
+#include "event.h"
 #include "fde.h"
 #include "MemBuf.h"
-
+#include "SquidTime.h"
+#include "Store.h"
 #include "wordlist.h"
 
 #if HAVE_ARPA_NAMESER_H
@@ -176,6 +175,7 @@
 #endif
 static void idnsCacheQuery(idns_query * q);
 static void idnsSendQuery(idns_query * q);
+static CNCB idnsInitVCConnected;
 static IOCB idnsReadVCHeader;
 static void idnsDoSendQueryVC(nsvc *vc);
 
@@ -186,6 +186,7 @@
 static EVH idnsCheckQueue;
 static void idnsTickleQueue(void);
 static void idnsRcodeCount(int, int);
+static void idnsVCClosed(int fd, void *data);
 
 static void
 idnsAddNameserver(const char *buf)
@@ -698,18 +699,21 @@
 }
 
 static void
-idnsInitVCConnected(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
+idnsInitVCConnected(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
 {
     nsvc * vc = (nsvc *)data;
 
- if (status != COMM_OK) {
+ if (status != COMM_OK || !conn) {
         char buf[MAX_IPSTRLEN];
- debugs(78, 1, "idnsInitVCConnected: Failed to connect to nameserver " << nameservers[vc->ns].S.NtoA(buf,MAX_IPSTRLEN) << " using TCP!");
- comm_close(fd);
+ debugs(78, DBG_IMPORTANT, "Failed to connect to nameserver " << nameservers[vc->ns].S.NtoA(buf,MAX_IPSTRLEN) << " using TCP!");
+ conn = NULL;
         return;
     }
 
- comm_read(fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
+ vc->fd = conn->fd; // TODO: make the vc store the conn instead?
+
+ comm_add_close_handler(conn->fd, idnsVCClosed, vc);
+ comm_read(conn->fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
     vc->busy = 0;
     idnsDoSendQueryVC(vc);
 }
@@ -727,37 +731,27 @@
 static void
 idnsInitVC(int ns)
 {
- char buf[MAX_IPSTRLEN];
-
     nsvc *vc = cbdataAlloc(nsvc);
     nameservers[ns].vc = vc;
     vc->ns = ns;
+ vc->queue = new MemBuf;
+ vc->msg = new MemBuf;
+ vc->busy = 1;
 
- Ip::Address addr;
+ Comm::ConnectionPointer conn = new Comm::Connection;
 
     if (!Config.Addrs.udp_outgoing.IsNoAddr())
- addr = Config.Addrs.udp_outgoing;
+ conn->local = Config.Addrs.udp_outgoing;
     else
- addr = Config.Addrs.udp_incoming;
-
- vc->queue = new MemBuf;
-
- vc->msg = new MemBuf;
-
- vc->fd = comm_open(SOCK_STREAM,
- IPPROTO_TCP,
- addr,
- COMM_NONBLOCKING,
- "DNS TCP Socket");
-
- if (vc->fd < 0)
- fatal("Could not create a DNS socket");
-
- comm_add_close_handler(vc->fd, idnsVCClosed, vc);
-
- vc->busy = 1;
-
- commConnectStart(vc->fd, nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN), nameservers[ns].S.GetPort(), idnsInitVCConnected, vc);
+ conn->local = Config.Addrs.udp_incoming;
+
+ conn->remote = nameservers[ns].S;
+
+ AsyncCall::Pointer call = commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected, vc));
+
+ Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, Config.Timeout.connect);
+ cs->setHost("DNS TCP Socket");
+ AsyncJob::AsyncStart(cs);
 }
 
 static void

=== modified file 'src/forward.cc'
--- src/forward.cc 2010-07-13 16:49:48 +0000
+++ src/forward.cc 2010-07-20 07:28:33 +0000
@@ -32,34 +32,36 @@
 
 
 #include "squid.h"
-#include "forward.h"
 #include "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
 #include "CacheManager.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
+#include "CommCalls.h"
 #include "event.h"
 #include "errorpage.h"
 #include "fde.h"
+#include "forward.h"
 #include "hier_code.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "MemObject.h"
 #include "pconn.h"
+#include "PeerSelectState.h"
 #include "SquidTime.h"
 #include "Store.h"
 #include "icmp/net_db.h"
 #include "ip/Intercept.h"
 
+
 static PSC fwdStartCompleteWrapper;
 static PF fwdServerClosedWrapper;
 #if USE_SSL
 static PF fwdNegotiateSSLWrapper;
 #endif
-static PF fwdConnectTimeoutWrapper;
-static EVH fwdConnectStartWrapper;
 static CNCB fwdConnectDoneWrapper;
 
 static OBJH fwdStats;
-static void fwdServerFree(FwdServer * fs);
 
 #define MAX_FWD_STATS_IDX 9
 static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
@@ -78,11 +80,10 @@
     FwdState* fwd = (FwdState*)d;
     Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
 
- if (fwd->server_fd >= 0) {
- comm_close(fwd->server_fd);
- fwd->server_fd = -1;
+ if (fwd->isServerConnectionOpen()) {
+ comm_remove_close_handler(fwd->serverConnection()->fd, fwdServerClosedWrapper, fwd);
     }
-
+ fwd->paths.clean();
     fwd->self = NULL;
 }
 
@@ -92,7 +93,6 @@
 {
     entry = e;
     client_fd = fd;
- server_fd = -1;
     request = HTTPMSGLOCK(r);
     start_t = squid_curtime;
 
@@ -113,10 +113,7 @@
     // Otherwise we are going to leak our object.
 
     entry->registerAbort(FwdState::abort, this);
- peerSelect(request, entry, fwdStartCompleteWrapper, this);
-
- // TODO: set self _after_ the peer is selected because we do not need
- // self until we start talking to some Server.
+ peerSelect(&paths, request, entry, fwdStartCompleteWrapper, this);
 }
 
 void
@@ -162,8 +159,6 @@
     if (! flags.forward_completed)
         completed();
 
- serversFree(&servers);
-
     HTTPMSGUNLOCK(request);
 
     if (err)
@@ -175,15 +170,14 @@
 
     entry = NULL;
 
- int fd = server_fd;
-
- if (fd > -1) {
- server_fd = -1;
- comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
- debugs(17, 3, "fwdStateFree: closing FD " << fd);
- comm_close(fd);
+ if (isServerConnectionOpen()) {
+ comm_remove_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
+ debugs(17, 3, HERE << "closing FD " << serverConnection()->fd);
+ serverConnection()->close();
     }
 
+ paths.clean();
+
     debugs(17, 3, HERE << "FwdState destructor done");
 }
 
@@ -226,7 +220,7 @@
         }
     }
 
- debugs(17, 3, "FwdState::start() '" << entry->url() << "'");
+ debugs(17, 3, HERE << "'" << entry->url() << "'");
     /*
      * This seems like an odd place to bind mem_obj and request.
      * Might want to assert that request is NULL at this point
@@ -260,13 +254,6 @@
 
     default:
         FwdState::Pointer fwd = new FwdState(client_fd, entry, request);
-
- /* If we need to transparently proxy the request
- * then we need the client source protocol, address and port */
- if (request->flags.spoof_client_ip) {
- fwd->src = request->client_addr;
- }
-
         fwd->start(fwd);
         return;
     }
@@ -275,6 +262,22 @@
 }
 
 void
+FwdState::startComplete()
+{
+ debugs(17, 3, HERE << entry->url() );
+
+ if (paths.size() > 0) {
+ connectStart();
+ } else {
+ debugs(17, 3, HERE << entry->url() );
+ ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
+ anErr->xerrno = errno;
+ fail(anErr);
+ self = NULL; // refcounted
+ }
+}
+
+void
 FwdState::fail(ErrorState * errorState)
 {
     debugs(17, 3, HERE << err_type_str[errorState->type] << " \"" << httpStatusString(errorState->httpStatus) << "\"\n\t" << entry->url() );
@@ -292,13 +295,23 @@
  * Frees fwdState without closing FD or generating an abort
  */
 void
+FwdState::unregister(Comm::ConnectionPointer conn)
+{
+ debugs(17, 3, HERE << entry->url() );
+ assert(serverConnection() == conn);
+ assert(conn->isOpen());
+ comm_remove_close_handler(conn->fd, fwdServerClosedWrapper, this);
+}
+
+// Legacy method to be removed in favor of the above as soon as possible
+void
 FwdState::unregister(int fd)
 {
- debugs(17, 3, HERE << entry->url() );
- assert(fd == server_fd);
+ debugs(17, 3, HERE << entry->url() );
+ assert(fd == serverConnection()->fd);
     assert(fd > -1);
     comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
- server_fd = -1;
+ serverConnection()->fd = -1;
 }
 
 /**
@@ -310,9 +323,8 @@
 void
 FwdState::complete()
 {
- StoreEntry *e = entry;
     assert(entry->store_status == STORE_PENDING);
- debugs(17, 3, HERE << e->url() << "\n\tstatus " << entry->getReply()->sline.status );
+ debugs(17, 3, HERE << entry->url() << "\n\tstatus " << entry->getReply()->sline.status );
 #if URL_CHECKSUM_DEBUG
 
     entry->mem_obj->checkUrlChecksum();
@@ -321,20 +333,23 @@
     logReplyStatus(n_tries, entry->getReply()->sline.status);
 
     if (reforward()) {
- debugs(17, 3, "fwdComplete: re-forwarding " << entry->getReply()->sline.status << " " << e->url());
-
- if (server_fd > -1)
- unregister(server_fd);
-
- e->reset();
-
- startComplete(servers);
+ debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status << " " << entry->url());
+
+ if (isServerConnectionOpen())
+ unregister(serverConnection());
+
+ entry->reset();
+
+ /* the call to reforward() has already dropped the last path off the
+ * selection list. all we have now are the next path(s) to be tried.
+ */
+ connectStart();
     } else {
- debugs(17, 3, "fwdComplete: server FD " << server_fd << " not re-forwarding status " << entry->getReply()->sline.status);
+ debugs(17, 3, HERE << "server FD " << serverConnection()->fd << " not re-forwarding status " << entry->getReply()->sline.status);
         EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
         entry->complete();
 
- if (server_fd < 0)
+ if (!isServerConnectionOpen())
             completed();
 
         self = NULL; // refcounted
@@ -345,10 +360,10 @@
 /**** CALLBACK WRAPPERS ************************************************************/
 
 static void
-fwdStartCompleteWrapper(FwdServer * servers, void *data)
+fwdStartCompleteWrapper(Comm::Paths * unused, void *data)
 {
     FwdState *fwd = (FwdState *) data;
- fwd->startComplete(servers);
+ fwd->startComplete();
 }
 
 static void
@@ -358,12 +373,14 @@
     fwd->serverClosed(fd);
 }
 
+#if 0
 static void
 fwdConnectStartWrapper(void *data)
 {
     FwdState *fwd = (FwdState *) data;
     fwd->connectStart();
 }
+#endif
 
 #if USE_SSL
 static void
@@ -372,31 +389,13 @@
     FwdState *fwd = (FwdState *) data;
     fwd->negotiateSSL(fd);
 }
-
 #endif
 
-static void
-fwdConnectDoneWrapper(int server_fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
-{
- FwdState *fwd = (FwdState *) data;
- fwd->connectDone(server_fd, dns, status, xerrno);
-}
-
-static void
-fwdConnectTimeoutWrapper(int fd, void *data)
-{
- FwdState *fwd = (FwdState *) data;
- fwd->connectTimeout(fd);
-}
-
-/*
- * Accounts for closed persistent connections
- */
-static void
-fwdPeerClosed(int fd, void *data)
-{
- peer *p = (peer *)data;
- p->stats.conn_open--;
+void
+fwdConnectDoneWrapper(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
+{
+ FwdState *fwd = (FwdState *) data;
+ fwd->connectDone(conn, status, xerrno);
 }
 
 /**** PRIVATE *****************************************************************/
@@ -487,10 +486,7 @@
 void
 FwdState::serverClosed(int fd)
 {
- debugs(17, 2, "fwdServerClosed: FD " << fd << " " << entry->url());
- assert(server_fd == fd);
- server_fd = -1;
-
+ debugs(17, 2, HERE << "FD " << fd << " " << entry->url());
     retryOrBail();
 }
 
@@ -504,39 +500,22 @@
     }
 
     if (checkRetry()) {
- int originserver = (servers->_peer == NULL);
- debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
-
- if (servers->next) {
- /* use next, or cycle if origin server isn't last */
- FwdServer *fs = servers;
- FwdServer **T, *T2 = NULL;
- servers = fs->next;
-
- for (T = &servers; *T; T2 = *T, T = &(*T)->next);
- if (T2 && T2->_peer) {
- /* cycle */
- *T = fs;
- fs->next = NULL;
- } else {
- /* Use next. The last "direct" entry is retried multiple times */
- servers = fs->next;
- fwdServerFree(fs);
- originserver = 0;
+ debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
+
+ paths.shift(); // last one failed. try another.
+
+ if (paths.size() > 0) {
+ /* Ditch error page if it was created before.
+ * A new one will be created if there's another problem */
+ if (err) {
+ errorStateFree(err);
+ err = NULL;
             }
- }
-
- /* Ditch error page if it was created before.
- * A new one will be created if there's another problem */
- if (err) {
- errorStateFree(err);
- err = NULL;
- }
-
- /* use eventAdd to break potential call sequence loops and to slow things down a little */
- eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
-
- return;
+
+ connectStart();
+ return;
+ }
+ // else bail. no more paths possible to try.
     }
 
     if (!err && shutting_down) {
@@ -550,9 +529,8 @@
 void
 FwdState::handleUnregisteredServerEnd()
 {
- debugs(17, 2, "handleUnregisteredServerEnd: self=" << self <<
- " err=" << err << ' ' << entry->url());
- assert(server_fd < 0);
+ debugs(17, 2, HERE << "self=" << self << " err=" << err << ' ' << entry->url());
+ assert(!isServerConnectionOpen());
     retryOrBail();
 }
 
@@ -560,7 +538,6 @@
 void
 FwdState::negotiateSSL(int fd)
 {
- FwdServer *fs = servers;
     SSL *ssl = fd_table[fd].ssl;
     int ret;
 
@@ -592,21 +569,20 @@
 
             fail(anErr);
 
- if (fs->_peer) {
- peerConnectFailed(fs->_peer);
- fs->_peer->stats.conn_open--;
+ if (serverConnection()->getPeer()) {
+ peerConnectFailed(serverConnection()->getPeer());
             }
 
- comm_close(fd);
+ serverConnection()->close();
             return;
         }
     }
 
- if (fs->_peer && !SSL_session_reused(ssl)) {
- if (fs->_peer->sslSession)
- SSL_SESSION_free(fs->_peer->sslSession);
+ if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
+ if (serverConnection()->getPeer()->sslSession)
+ SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
 
- fs->_peer->sslSession = SSL_get1_session(ssl);
+ serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
     }
 
     dispatch();
@@ -615,11 +591,10 @@
 void
 FwdState::initiateSSL()
 {
- FwdServer *fs = servers;
- int fd = server_fd;
     SSL *ssl;
     SSL_CTX *sslContext = NULL;
- peer *peer = fs->_peer;
+ const peer *peer = serverConnection()->getPeer();
+ int fd = serverConnection()->fd;
 
     if (peer) {
         assert(peer->use_ssl);
@@ -679,189 +654,166 @@
 #endif
 
 void
-FwdState::connectDone(int aServerFD, const DnsLookupDetails &dns, comm_err_t status, int xerrno)
+FwdState::connectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno)
 {
- FwdServer *fs = servers;
- assert(server_fd == aServerFD);
-
- request->recordLookup(dns);
-
- if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
- updateHierarchyInfo();
-
- if (status == COMM_ERR_DNS) {
- /*
- * Only set the dont_retry flag if the DNS lookup fails on
- * a direct connection. If DNS lookup fails when trying
- * a neighbor cache, we may want to retry another option.
- */
-
- if (NULL == fs->_peer)
- flags.dont_retry = 1;
-
- debugs(17, 4, "fwdConnectDone: Unknown host: " << request->GetHost());
-
- ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
-
- anErr->dnsError = dns.error;
-
- fail(anErr);
-
- comm_close(server_fd);
- } else if (status != COMM_OK) {
- assert(fs);
+ if (status != COMM_OK) {
         ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
         anErr->xerrno = xerrno;
-
         fail(anErr);
 
- if (fs->_peer)
- peerConnectFailed(fs->_peer);
-
- comm_close(server_fd);
- } else {
- debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" );
-
- if (fs->_peer)
- peerConnectSucceded(fs->_peer);
+ /* it might have been a timeout with a partially open link */
+ if (paths.size() > 0) {
+ if (serverConnection()->getPeer())
+ peerConnectFailed(serverConnection()->getPeer());
+
+ serverConnection()->close();
+ }
+ retryOrBail();
+ return;
+ }
+
+#if REDUNDANT_NOW
+ if (Config.onoff.log_ip_on_direct && serverConnection()->peerType == HIER_DIRECT)
+ updateHierarchyInfo();
+#endif
+
+ debugs(17, 3, "FD " << serverConnection()->fd << ": '" << entry->url() << "'" );
+
+ comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
+
+ if (serverConnection()->getPeer())
+ peerConnectSucceded(serverConnection()->getPeer());
+
+ updateHierarchyInfo();
 
 #if USE_SSL
-
- if ((fs->_peer && fs->_peer->use_ssl) ||
- (!fs->_peer && request->protocol == PROTO_HTTPS)) {
- initiateSSL();
- return;
- }
-
+ if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
+ (!serverConnection()->getPeer() && request->protocol == PROTO_HTTPS)) {
+ initiateSSL();
+ return;
+ }
 #endif
- dispatch();
- }
+
+ dispatch();
 }
 
 void
 FwdState::connectTimeout(int fd)
 {
- FwdServer *fs = servers;
-
     debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
- assert(fd == server_fd);
+ assert(fd == serverConnection()->fd);
 
- if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
+ if (Config.onoff.log_ip_on_direct && serverConnection()->peerType == HIER_DIRECT)
         updateHierarchyInfo();
 
     if (entry->isEmpty()) {
         ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
         anErr->xerrno = ETIMEDOUT;
         fail(anErr);
- /*
- * This marks the peer DOWN ...
- */
-
- if (servers)
- if (servers->_peer)
- peerConnectFailed(servers->_peer);
- }
-
- comm_close(fd);
+
+ /* This marks the peer DOWN ... */
+ if (paths.size() > 0)
+ if (serverConnection()->getPeer())
+ peerConnectFailed(serverConnection()->getPeer());
+ }
+
+ if (isServerConnectionOpen()) {
+ serverConnection()->close();
+ }
 }
 
+/**
+ * Called after Forwarding path selection (via peer select) has taken place.
+ * And whenever forwarding needs to attempt a new connection (routing failover)
+ * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
+ */
 void
 FwdState::connectStart()
 {
- const char *url = entry->url();
- int fd = -1;
- FwdServer *fs = servers;
- const char *host;
- unsigned short port;
- int ctimeout;
- int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
-
- Ip::Address outgoing;
- unsigned short tos;
- Ip::Address client_addr;
- assert(fs);
- assert(server_fd == -1);
- debugs(17, 3, "fwdConnectStart: " << url);
+ debugs(17, 3, "fwdConnectStart: " << entry->url());
 
     if (n_tries == 0) // first attempt
         request->hier.first_conn_start = current_time;
 
- if (fs->_peer) {
- ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
- : Config.Timeout.peer_connect;
+ /* connection timeout */
+ int ctimeout;
+ if (serverConnection()->getPeer()) {
+ ctimeout = serverConnection()->getPeer()->connect_timeout > 0 ?
+ serverConnection()->getPeer()->connect_timeout : Config.Timeout.peer_connect;
     } else {
         ctimeout = Config.Timeout.connect;
     }
 
- if (request->flags.spoof_client_ip) {
- if (!fs->_peer || !fs->_peer->options.no_tproxy)
- client_addr = request->client_addr;
- // else no tproxy today ...
- }
-
+ /* calculate total forwarding timeout ??? */
+ int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
     if (ftimeout < 0)
         ftimeout = 5;
 
     if (ftimeout < ctimeout)
         ctimeout = ftimeout;
 
-
     request->flags.pinned = 0;
- if (fs->code == PINNED) {
+ if (serverConnection()->peerType == PINNED) {
         ConnStateData *pinned_connection = request->pinnedConnection();
         assert(pinned_connection);
- fd = pinned_connection->validatePinnedConnection(request, fs->_peer);
- if (fd >= 0) {
+ serverConnection()->fd = pinned_connection->validatePinnedConnection(request, serverConnection()->getPeer());
+ if (isServerConnectionOpen()) {
             pinned_connection->unpinConnection();
 #if 0
- if (!fs->_peer)
- fs->code = HIER_DIRECT;
+ if (!serverConnection()->getPeer())
+ serverConnection()->peerType = HIER_DIRECT;
 #endif
- server_fd = fd;
             n_tries++;
             request->flags.pinned = 1;
             if (pinned_connection->pinnedAuth())
                 request->flags.auth = 1;
- comm_add_close_handler(fd, fwdServerClosedWrapper, this);
             updateHierarchyInfo();
- connectDone(fd, DnsLookupDetails(), COMM_OK, 0);
+ Comm::ConnectionPointer conn = serverConnection();
+ FwdState::connectDone(conn, COMM_OK, 0);
             return;
         }
         /* Failure. Fall back on next path */
         debugs(17,2,HERE << " Pinned connection " << pinned_connection << " not valid. Releasing.");
         request->releasePinnedConnection();
- servers = fs->next;
- fwdServerFree(fs);
+ paths.shift();
         connectStart();
         return;
     }
 
- if (fs->_peer) {
- host = fs->_peer->host;
- port = fs->_peer->http_port;
- fd = fwdPconnPool->pop(fs->_peer->name, fs->_peer->http_port, request->GetHost(), client_addr, checkRetriable());
+// TODO: now that we are dealing with actual IP->IP links. should we still anchor pconn on hostname?
+// or on the remote IP+port?
+// that could reduce the pconns per virtual server a fair amount
+// but would prevent crossover between servers hosting the one domain
+// this currently opens the possibility that conn will lie about where the FD goes.
+
+ const char *host;
+ int port;
+ if (serverConnection()->getPeer()) {
+ host = serverConnection()->getPeer()->host;
+ port = serverConnection()->getPeer()->http_port;
+ serverConnection()->fd = fwdPconnPool->pop(serverConnection()->getPeer()->name,
+ serverConnection()->getPeer()->http_port,
+ request->GetHost(), serverConnection()->local,
+ checkRetriable());
     } else {
         host = request->GetHost();
         port = request->port;
- fd = fwdPconnPool->pop(host, port, NULL, client_addr, checkRetriable());
+ serverConnection()->fd = fwdPconnPool->pop(host, port, NULL, serverConnection()->local, checkRetriable());
     }
- if (fd >= 0) {
- debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
- server_fd = fd;
+ serverConnection()->remote.SetPort(port);
+
+ if (isServerConnectionOpen()) {
+ debugs(17, 3, HERE << "reusing pconn FD " << serverConnection()->fd);
         n_tries++;
 
- if (!fs->_peer)
+ if (!serverConnection()->getPeer())
             origin_tries++;
 
         updateHierarchyInfo();
 
- comm_add_close_handler(fd, fwdServerClosedWrapper, this);
-
- // TODO: Avoid this if %<lp is not used? F->local_port is often cached.
- request->hier.peer_local_port = comm_local_port(fd);
+ comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
 
         dispatch();
-
         return;
     }
 
@@ -869,98 +821,27 @@
     entry->mem_obj->checkUrlChecksum();
 #endif
 
- outgoing = getOutgoingAddr(request, fs->_peer);
-
- tos = getOutgoingTOS(request);
-
- debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << tos);
-
- int commFlags = COMM_NONBLOCKING;
- if (request->flags.spoof_client_ip) {
- if (!fs->_peer || !fs->_peer->options.no_tproxy)
- commFlags |= COMM_TRANSPARENT;
- // else no tproxy today ...
- }
-
- fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, commFlags, tos, url);
-
- debugs(17, 3, "fwdConnectStart: got TCP FD " << fd);
-
- if (fd < 0) {
- debugs(50, 4, "fwdConnectStart: " << xstrerror());
- ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
- anErr->xerrno = errno;
- fail(anErr);
- self = NULL; // refcounted
- return;
- }
-
- server_fd = fd;
- n_tries++;
-
- if (!fs->_peer)
- origin_tries++;
-
- request->hier.peer_local_port = comm_local_port(fd);
-
- /*
- * stats.conn_open is used to account for the number of
- * connections that we have open to the peer, so we can limit
- * based on the max-conn option. We need to increment here,
- * even if the connection may fail.
- */
-
- if (fs->_peer) {
- fs->_peer->stats.conn_open++;
- comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
- }
-
- comm_add_close_handler(fd, fwdServerClosedWrapper, this);
-
- commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
-
- updateHierarchyInfo();
- commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
-}
-
-void
-FwdState::startComplete(FwdServer * theServers)
-{
- debugs(17, 3, "fwdStartComplete: " << entry->url() );
-
- if (theServers != NULL) {
- servers = theServers;
- connectStart();
- } else {
- startFail();
- }
-}
-
-void
-FwdState::startFail()
-{
- debugs(17, 3, "fwdStartFail: " << entry->url() );
- ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
- anErr->xerrno = errno;
- fail(anErr);
- self = NULL; // refcounted
+ AsyncCall::Pointer call = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
+ Comm::ConnectionPointer conn = serverConnection();
+ Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, ctimeout);
+ cs->setHost(host);
+ AsyncJob::AsyncStart(cs);
 }
 
 void
 FwdState::dispatch()
 {
- peer *p = NULL;
     debugs(17, 3, "fwdDispatch: FD " << client_fd << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'" );
     /*
      * Assert that server_fd is set. This is to guarantee that fwdState
      * is attached to something and will be deallocated when server_fd
      * is closed.
      */
- assert(server_fd > -1);
-
- fd_note(server_fd, entry->url());
-
- fd_table[server_fd].noteUse(fwdPconnPool);
+ assert(isServerConnectionOpen());
+
+ fd_note(serverConnection()->fd, entry->url());
+
+ fd_table[serverConnection()->fd].noteUse(fwdPconnPool);
 
     /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
     assert(entry->ping_status != PING_WAITING);
@@ -983,10 +864,10 @@
         int tos = 1;
         int tos_len = sizeof(tos);
         clientFde->upstreamTOS = 0;
- if (setsockopt(server_fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0) {
+ if (setsockopt(serverConnection()->fd, SOL_IP, IP_RECVTOS, &tos, tos_len)==0) {
             unsigned char buf[512];
             int len = 512;
- if (getsockopt(server_fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0) {
+ if (getsockopt(serverConnection()->fd, SOL_IP, IP_PKTOPTIONS, buf, (socklen_t*)&len) == 0) {
                 /* Parse the PKTOPTIONS structure to locate the TOS data message
                  * prepared in the kernel by the ZPH incoming TCP TOS preserving
                  * patch.
@@ -1005,18 +886,18 @@
                     pbuf += CMSG_LEN(o->cmsg_len);
                 }
             } else {
- debugs(33, 1, "ZPH: error in getsockopt(IP_PKTOPTIONS) on FD "<<server_fd<<" "<<xstrerror());
+ debugs(33, DBG_IMPORTANT, "ZPH: error in getsockopt(IP_PKTOPTIONS) on FD " << serverConnection()->fd << " " << xstrerror());
             }
         } else {
- debugs(33, 1, "ZPH: error in setsockopt(IP_RECVTOS) on FD "<<server_fd<<" "<<xstrerror());
+ debugs(33, DBG_IMPORTANT, "ZPH: error in setsockopt(IP_RECVTOS) on FD " << serverConnection()->fd << " " << xstrerror());
         }
     }
 #endif
 
- if (servers && (p = servers->_peer)) {
- p->stats.fetches++;
- request->peer_login = p->login;
- request->peer_domain = p->domain;
+ if (serverConnection()->getPeer() != NULL) {
+ serverConnection()->getPeer()->stats.fetches++;
+ request->peer_login = serverConnection()->getPeer()->login;
+ request->peer_domain = serverConnection()->getPeer()->domain;
         httpStart(this);
     } else {
         request->peer_login = NULL;
@@ -1067,11 +948,13 @@
              */
             request->flags.proxy_keepalive = 0;
             /*
- * Set the dont_retry flag becuase this is not a
+ * Set the dont_retry flag because this is not a
              * transient (network) error; its a bug.
              */
             flags.dont_retry = 1;
- comm_close(server_fd);
+ if (isServerConnectionOpen()) {
+ serverConnection()->close();
+ }
             break;
         }
     }
@@ -1089,7 +972,6 @@
 FwdState::reforward()
 {
     StoreEntry *e = entry;
- FwdServer *fs = servers;
     http_status s;
     assert(e->store_status == STORE_PENDING);
     assert(e->mem_obj);
@@ -1098,10 +980,10 @@
     e->mem_obj->checkUrlChecksum();
 #endif
 
- debugs(17, 3, "fwdReforward: " << e->url() << "?" );
+ debugs(17, 3, HERE << e->url() << "?" );
 
     if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
- debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set");
+ debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set");
         return 0;
     }
 
@@ -1114,19 +996,15 @@
     if (request->bodyNibbled())
         return 0;
 
- assert(fs);
-
- servers = fs->next;
-
- fwdServerFree(fs);
-
- if (servers == NULL) {
- debugs(17, 3, "fwdReforward: No forward-servers left");
+ paths.shift();
+
+ if (paths.size() > 0) {
+ debugs(17, 3, HERE << "No alternative forwarding paths left");
         return 0;
     }
 
     s = e->getReply()->sline.status;
- debugs(17, 3, "fwdReforward: status " << s);
+ debugs(17, 3, HERE << "status " << s);
     return reforwardableStatus(s);
 }
 
@@ -1195,22 +1073,26 @@
  * - address of the client for which we made the connection
  */
 void
-FwdState::pconnPush(int fd, const peer *_peer, const HttpRequest *req, const char *domain, Ip::Address &client_addr)
+FwdState::pconnPush(Comm::ConnectionPointer conn, const peer *_peer, const HttpRequest *req, const char *domain, Ip::Address &client_addr)
 {
     if (_peer) {
- fwdPconnPool->push(fd, _peer->name, _peer->http_port, domain, client_addr);
+ fwdPconnPool->push(conn->fd, _peer->name, _peer->http_port, domain, client_addr);
     } else {
         /* small performance improvement, using NULL for domain instead of listing it twice */
         /* although this will leave a gap open for url-rewritten domains to share a link */
- fwdPconnPool->push(fd, req->GetHost(), req->port, NULL, client_addr);
+ fwdPconnPool->push(conn->fd, req->GetHost(), req->port, NULL, client_addr);
     }
+
+ /* XXX: remove this when Comm::Connection are stored in the pool
+ * this only prevents the persistent FD being closed when the
+ * Comm::Connection currently using it is destroyed.
+ */
+ conn->fd = -1;
 }
 
 void
 FwdState::initModule()
 {
- memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
-
 #if WIP_FWD_LOG
 
     if (logfile)
@@ -1238,9 +1120,7 @@
     if (status > HTTP_INVALID_HEADER)
         return;
 
- assert(tries);
-
- tries--;
+ assert(tries >= 0);
 
     if (tries > MAX_FWD_STATS_IDX)
         tries = MAX_FWD_STATS_IDX;
@@ -1248,17 +1128,6 @@
     FwdReplyCodes[tries][status]++;
 }
 
-void
-FwdState::serversFree(FwdServer ** FSVR)
-{
- FwdServer *fs;
-
- while ((fs = *FSVR)) {
- *FSVR = fs->next;
- fwdServerFree(fs);
- }
-}
-
 /** From Comment #5 by Henrik Nordstrom made at
 http://www.squid-cache.org/bugs/show_bug.cgi?id=2391 on 2008-09-19
 
@@ -1277,54 +1146,30 @@
 {
     assert(request);
 
- FwdServer *fs = servers;
- assert(fs);
-
- const char *nextHop = NULL;
-
- if (fs->_peer) {
+ assert(paths.size() > 0);
+
+ char nextHop[256];
+
+ if (serverConnection()->getPeer()) {
         // went to peer, log peer host name
- nextHop = fs->_peer->name;
+ snprintf(nextHop,256,"%s", serverConnection()->getPeer()->name);
     } else {
         // went DIRECT, must honor log_ip_on_direct
-
- // XXX: or should we use request->host_addr here? how?
- assert(server_fd >= 0);
- nextHop = fd_table[server_fd].ipaddr;
- if (!Config.onoff.log_ip_on_direct || !nextHop[0])
- nextHop = request->GetHost(); // domain name
+ if (!Config.onoff.log_ip_on_direct)
+ snprintf(nextHop,256,"%s",request->GetHost()); // domain name
+ else
+ serverConnection()->remote.NtoA(nextHop, 256);
     }
 
- assert(nextHop);
- hierarchyNote(&request->hier, fs->code, nextHop);
+ request->hier.peer_local_port = serverConnection()->local.GetPort();
+
+ assert(nextHop[0]);
+ hierarchyNote(&request->hier, serverConnection()->peerType, nextHop);
 }
 
 
 /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
 
-static void
-fwdServerFree(FwdServer * fs)
-{
- cbdataReferenceDone(fs->_peer);
- memFree(fs, MEM_FWD_SERVER);
-}
-
-static Ip::Address
-aclMapAddr(acl_address * head, ACLChecklist * ch)
-{
- acl_address *l;
-
- Ip::Address addr;
-
- for (l = head; l; l = l->next) {
- if (!l->aclList || ch->matchAclListFast(l->aclList))
- return l->addr;
- }
-
- addr.SetAnyAddr();
- return addr;
-}
-
 /*
  * DPW 2007-05-19
  * Formerly static, but now used by client_side_request.cc
@@ -1342,27 +1187,39 @@
     return 0;
 }
 
-Ip::Address
-getOutgoingAddr(HttpRequest * request, struct peer *dst_peer)
+void
+getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
 {
+ /* skip if an outgoing address is already set. */
+ if (!conn->local.IsAnyAddr()) return;
+
+ // maybe use TPROXY client address
     if (request && request->flags.spoof_client_ip) {
- if (!dst_peer || !dst_peer->options.no_tproxy) {
+ if (!conn->getPeer() || !conn->getPeer()->options.no_tproxy) {
 #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER
             if (Config.onoff.tproxy_uses_indirect_client)
- return request->indirect_client_addr;
+ conn->local = request->indirect_client_addr;
             else
 #endif
- return request->client_addr;
+ conn->local = request->client_addr;
+ // some flags need setting on the socket to use this address
+ conn->flags |= COMM_DOBIND;
+ conn->flags |= COMM_TRANSPARENT;
+ return;
         }
         // else no tproxy today ...
     }
 
     if (!Config.accessList.outgoing_address) {
- return Ip::Address(); // anything will do.
+ return; // anything will do.
     }
 
     ACLFilledChecklist ch(NULL, request, NULL);
- ch.dst_peer = dst_peer;
+ ch.dst_peer = conn->getPeer();
+ ch.dst_addr = conn->remote;
+
+ // TODO use the connection details in ACL.
+ // needs a bit of rework in ACLFilledChecklist to use Comm::Connection instead of ConnStateData
 
     if (request) {
 #if FOLLOW_X_FORWARDED_FOR
@@ -1374,7 +1231,18 @@
         ch.my_addr = request->my_addr;
     }
 
- return aclMapAddr(Config.accessList.outgoing_address, &ch);
+ acl_address *l;
+ for (l = Config.accessList.outgoing_address; l; l = l->next) {
+
+ /* check if the outgoing address is usable to the destination */
+ if (conn->remote.IsIPv4() != l->addr.IsIPv4()) continue;
+
+ /* check ACLs for this outgoing address */
+ if (!l->aclList || ch.matchAclListFast(l->aclList)) {
+ conn->local = l->addr;
+ return;
+ }
+ }
 }
 
 unsigned long

=== modified file 'src/forward.h'
--- src/forward.h 2010-05-02 19:32:42 +0000
+++ src/forward.h 2010-07-19 14:57:52 +0000
@@ -7,16 +7,10 @@
 class HttpRequest;
 
 #include "comm.h"
-#include "hier_code.h"
+#include "comm/Connection.h"
+#include "fde.h"
 #include "ip/Address.h"
-
-class FwdServer
-{
-public:
- peer *_peer; /* NULL --> origin server */
- hier_code code;
- FwdServer *next;
-};
+#include "Array.h"
 
 class FwdState : public RefCountable
 {
@@ -26,9 +20,9 @@
     static void initModule();
 
     static void fwdStart(int fd, StoreEntry *, HttpRequest *);
- void startComplete(FwdServer *);
- void startFail();
+ void startComplete();
     void fail(ErrorState *err);
+ void unregister(Comm::ConnectionPointer conn);
     void unregister(int fd);
     void complete();
     void handleUnregisteredServerEnd();
@@ -36,14 +30,14 @@
     bool reforwardableStatus(http_status s);
     void serverClosed(int fd);
     void connectStart();
- void connectDone(int server_fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno);
+ void connectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno);
     void connectTimeout(int fd);
     void initiateSSL();
     void negotiateSSL(int fd);
     bool checkRetry();
     bool checkRetriable();
     void dispatch();
- void pconnPush(int fd, const peer *_peer, const HttpRequest *req, const char *domain, Ip::Address &client_addr);
+ void pconnPush(Comm::ConnectionPointer conn, const peer *_peer, const HttpRequest *req, const char *domain, Ip::Address &client_addr);
 
     bool dontRetry() { return flags.dont_retry; }
 
@@ -53,7 +47,15 @@
 
     void ftpPasvFailed(bool val) { flags.ftp_pasv_failed = val; }
 
- static void serversFree(FwdServer **);
+ /** return a ConnectionPointer to the current server connection (may or may not be open) */
+ Comm::ConnectionPointer serverConnection() const { assert(paths.size() > 0); return paths[0]; };
+
+ /** test if the current server connection is open */
+ bool isServerConnectionOpen() const {
+ if (paths.size() > 0 && serverConnection()->fd >= 0)
+ assert(fd_table[serverConnection()->fd].flags.open == serverConnection()->isOpen());
+ return (paths.size() > 0 && serverConnection()->isOpen());
+ };
 
 private:
     // hidden for safer management of self; use static fwdStart
@@ -76,8 +78,6 @@
 public:
     StoreEntry *entry;
     HttpRequest *request;
- int server_fd;
- FwdServer *servers;
     static void abort(void*);
 
 private:
@@ -98,7 +98,8 @@
         unsigned int forward_completed:1;
     } flags;
 
- Ip::Address src; /* Client address for this connection. Needed for transparent operations. */
+ /** connections to open, in order, until successful */
+ Comm::Paths paths;
 
     // NP: keep this last. It plays with private/public
     CBDATA_CLASS2(FwdState);

=== modified file 'src/fqdncache.cc'
--- src/fqdncache.cc 2010-06-01 00:12:34 +0000
+++ src/fqdncache.cc 2010-06-03 07:18:25 +0000
@@ -34,6 +34,7 @@
 
 #include "squid.h"
 #include "cbdata.h"
+#include "DnsLookupDetails.h"
 #include "event.h"
 #include "CacheManager.h"
 #include "SquidTime.h"

=== modified file 'src/ftp.cc'
--- src/ftp.cc 2010-06-10 21:38:03 +0000
+++ src/ftp.cc 2010-07-19 13:18:06 +0000
@@ -34,9 +34,9 @@
 
 #include "squid.h"
 #include "comm.h"
+#include "comm/ConnOpener.h"
 #include "comm/ListenStateData.h"
 #include "compat/strtoll.h"
-#include "ConnectionDetail.h"
 #include "errorpage.h"
 #include "fde.h"
 #include "forward.h"
@@ -480,7 +480,7 @@
     typedef CommCbMemFunT<FtpStateData, CommCloseCbParams> Dialer;
     AsyncCall::Pointer closer = asyncCall(9, 5, "FtpStateData::ctrlClosed",
                                           Dialer(this, &FtpStateData::ctrlClosed));
- ctrl.opened(theFwdState->server_fd, closer);
+ ctrl.opened(theFwdState->serverConnection()->fd, closer);
 
     if (request->method == METHOD_PUT)
         flags.put = 1;
@@ -2412,7 +2412,15 @@
 
     debugs(9, 3, HERE << "connecting to " << ftpState->data.host << ", port " << ftpState->data.port);
 
- commConnectStart(fd, ftpState->data.host, port, FtpStateData::ftpPasvCallback, ftpState);
+ Comm::ConnectionPointer conn = new Comm::Connection;
+ conn->remote = fd_table[ftpState->ctrl.fd].ipaddr; // TODO: do we have a better info source than fd_table?
+ conn->remote.SetPort(port);
+ conn->fd = fd;
+
+ AsyncCall::Pointer call = commCbCall(9,3, "FtpStateData::ftpPasvCallback", CommConnectCbPtrFun(FtpStateData::ftpPasvCallback, ftpState));
+ Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, Config.Timeout.connect);
+ cs->setHost(ftpState->data.host);
+ AsyncJob::AsyncStart(cs);
 }
 
 /** \ingroup ServerProtocolFTPInternal
@@ -2545,10 +2553,11 @@
 
     /** Otherwise, Open data channel with the same local address as control channel (on a new random port!) */
     addr.SetPort(0);
- int fd = comm_open(SOCK_STREAM,
+ int fd = comm_openex(SOCK_STREAM,
                        IPPROTO_TCP,
                        addr,
                        COMM_NONBLOCKING,
+ 0,
                        ftpState->entry->url());
 
     debugs(9, 3, HERE << "Unconnected data socket created on FD " << fd << " from " << addr);
@@ -2610,7 +2619,6 @@
     int n;
     u_short port;
     Ip::Address ipa_remote;
- int fd = ftpState->data.fd;
     char *buf;
     LOCAL_ARRAY(char, ipaddr, 1024);
     debugs(9, 3, HERE);
@@ -2688,15 +2696,22 @@
 
     debugs(9, 3, HERE << "connecting to " << ftpState->data.host << ", port " << ftpState->data.port);
 
- commConnectStart(fd, ipaddr, port, FtpStateData::ftpPasvCallback, ftpState);
+ Comm::ConnectionPointer conn = new Comm::Connection;
+ conn->remote = ipaddr;
+ conn->remote.SetPort(port);
+ conn->fd = ftpState->data.fd;
+
+ AsyncCall::Pointer call = commCbCall(9,3, "FtpStateData::ftpPasvCallback", CommConnectCbPtrFun(FtpStateData::ftpPasvCallback, ftpState));
+ Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, Config.Timeout.connect);
+ cs->setHost(ftpState->data.host);
+ AsyncJob::AsyncStart(cs);
 }
 
 void
-FtpStateData::ftpPasvCallback(int fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
+FtpStateData::ftpPasvCallback(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
 {
     FtpStateData *ftpState = (FtpStateData *)data;
     debugs(9, 3, HERE);
- ftpState->request->recordLookup(dns);
 
     if (status != COMM_OK) {
         debugs(9, 2, HERE << "Failed to connect. Retrying without PASV.");
@@ -2937,16 +2952,17 @@
      * This prevents third-party hacks, but also third-party load balancing handshakes.
      */
     if (Config.Ftp.sanitycheck) {
- io.details.peer.NtoA(ntoapeer,MAX_IPSTRLEN);
+ io.details->remote.NtoA(ntoapeer,MAX_IPSTRLEN);
 
         if (strcmp(fd_table[ctrl.fd].ipaddr, ntoapeer) != 0) {
             debugs(9, DBG_IMPORTANT,
                    "FTP data connection from unexpected server (" <<
- io.details.peer << "), expecting " <<
+ io.details->remote << "), expecting " <<
                    fd_table[ctrl.fd].ipaddr);
 
- /* close the bad soures connection down ASAP. */
- comm_close(io.nfd);
+ /* close the bad sources connection down ASAP. */
+ Comm::ConnectionPointer nonConst = io.details;
+ nonConst->close();
 
             /* we are ony accepting once, so need to re-open the listener socket. */
             typedef CommCbMemFunT<FtpStateData, CommAcceptCbParams> acceptDialer;
@@ -2968,11 +2984,11 @@
      * Replace the Listen socket with the accepted data socket */
     data.close();
     data.opened(io.nfd, dataCloser());
- data.port = io.details.peer.GetPort();
- io.details.peer.NtoA(data.host,SQUIDHOSTNAMELEN);
+ data.port = io.details->remote.GetPort();
+ io.details->remote.NtoA(data.host,SQUIDHOSTNAMELEN);
 
     debugs(9, 3, "ftpAcceptDataConnection: Connected data socket on " <<
- "FD " << io.nfd << " to " << io.details.peer << " FD table says: " <<
+ "FD " << io.nfd << " to " << io.details->remote << " FD table says: " <<
            "ctrl-peer= " << fd_table[ctrl.fd].ipaddr << ", " <<
            "data-peer= " << fd_table[data.fd].ipaddr);
 

=== modified file 'src/gopher.cc'
--- src/gopher.cc 2010-02-06 06:32:11 +0000
+++ src/gopher.cc 2010-07-19 13:18:06 +0000
@@ -990,7 +990,6 @@
 void
 gopherStart(FwdState * fwd)
 {
- int fd = fwd->server_fd;
     StoreEntry *entry = fwd->entry;
     GopherStateData *gopherState;
     CBDATA_INIT_TYPE(GopherStateData);
@@ -1012,7 +1011,7 @@
     gopher_request_parse(fwd->request,
                          &gopherState->type_id, gopherState->request);
 
- comm_add_close_handler(fd, gopherStateFree, gopherState);
+ comm_add_close_handler(fwd->serverConnection()->fd, gopherStateFree, gopherState);
 
     if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
             && (strchr(gopherState->request, '?') == NULL)) {
@@ -1032,12 +1031,11 @@
 
         gopherToHTML(gopherState, (char *) NULL, 0);
         fwd->complete();
- comm_close(fd);
         return;
     }
 
- gopherState->fd = fd;
+ gopherState->fd = fwd->serverConnection()->fd; // TODO: save the serverConnection() in gopher instead of the FD
     gopherState->fwd = fwd;
- gopherSendRequest(fd, gopherState);
- commSetTimeout(fd, Config.Timeout.read, gopherTimeout, gopherState);
+ gopherSendRequest(fwd->serverConnection()->fd, gopherState);
+ commSetTimeout(fwd->serverConnection()->fd, Config.Timeout.read, gopherTimeout, gopherState);
 }

=== modified file 'src/http.cc'
--- src/http.cc 2010-06-28 05:13:07 +0000
+++ src/http.cc 2010-07-20 13:09:51 +0000
@@ -43,6 +43,7 @@
 #include "acl/FilledChecklist.h"
 #include "auth/UserRequest.h"
 #include "base/TextException.h"
+#include "comm/Connection.h"
 #if DELAY_POOLS
 #include "DelayPools.h"
 #endif
@@ -86,7 +87,7 @@
     debugs(11,5,HERE << "HttpStateData " << this << " created");
     ignoreCacheControl = false;
     surrogateNoStore = false;
- fd = fwd->server_fd;
+ serverConnection = fwd->serverConnection();
     readBuf = new MemBuf;
     readBuf->init();
     orig_request = HTTPMSGLOCK(fwd->request);
@@ -95,8 +96,8 @@
     orig_request->hier.peer_http_request_sent.tv_sec = 0;
     orig_request->hier.peer_http_request_sent.tv_usec = 0;
 
- if (fwd->servers)
- _peer = fwd->servers->_peer; /* might be NULL */
+ if (fwd->serverConnection() != NULL)
+ _peer = cbdataReference(fwd->serverConnection()->getPeer()); /* might be NULL */
 
     if (_peer) {
         const char *url;
@@ -106,8 +107,7 @@
         else
             url = entry->url();
 
- HttpRequest * proxy_req = new HttpRequest(orig_request->method,
- orig_request->protocol, url);
+ HttpRequest * proxy_req = new HttpRequest(orig_request->method, orig_request->protocol, url);
 
         proxy_req->SetHost(_peer->host);
 
@@ -144,7 +144,7 @@
     typedef CommCbMemFunT<HttpStateData, CommCloseCbParams> Dialer;
     closeHandler = asyncCall(9, 5, "httpStateData::httpStateConnClosed",
                              Dialer(this,&HttpStateData::httpStateConnClosed));
- comm_add_close_handler(fd, closeHandler);
+ comm_add_close_handler(serverConnection->fd, closeHandler);
 }
 
 HttpStateData::~HttpStateData()
@@ -163,14 +163,19 @@
 
     HTTPMSGUNLOCK(orig_request);
 
- debugs(11,5, HERE << "HttpStateData " << this << " destroyed; FD " << fd);
+ cbdataReferenceDone(_peer);
+
+ debugs(11,5, HERE << "HttpStateData " << this << " destroyed; FD " << dataDescriptor());
 }
 
 int
 HttpStateData::dataDescriptor() const
 {
- return fd;
+ if (serverConnection == NULL)
+ return -1;
+ return serverConnection->fd;
 }
+
 /*
 static void
 httpStateFree(int fd, void *data)
@@ -203,13 +208,13 @@
 void
 HttpStateData::httpTimeout(const CommTimeoutCbParams &params)
 {
- debugs(11, 4, "httpTimeout: FD " << fd << ": '" << entry->url() << "'" );
+ debugs(11, 4, "httpTimeout: FD " << serverConnection->fd << ": '" << entry->url() << "'" );
 
     if (entry->store_status == STORE_PENDING) {
         fwd->fail(errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT, fwd->request));
     }
 
- comm_close(fd);
+ serverConnection->close();
 }
 
 static void
@@ -954,7 +959,7 @@
 HttpStateData::ConnectionStatus
 HttpStateData::persistentConnStatus() const
 {
- debugs(11, 3, "persistentConnStatus: FD " << fd << " eof=" << eof);
+ debugs(11, 3, "persistentConnStatus: FD " << serverConnection->fd << " eof=" << eof);
     const HttpReply *vrep = virginReply();
     debugs(11, 5, "persistentConnStatus: content_length=" << vrep->content_length);
 
@@ -1012,7 +1017,7 @@
 HttpStateData::ReadReplyWrapper(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
 {
     HttpStateData *httpState = static_cast<HttpStateData *>(data);
- assert (fd == httpState->fd);
+ assert (fd == httpState->serverConnection->fd);
     // assert(buf == readBuf->content());
     PROF_start(HttpStateData_readReply);
     httpState->readReply(len, flag, xerrno);
@@ -1028,11 +1033,11 @@
     int clen;
     int len = io.size;
 
- assert(fd == io.fd);
+ assert(serverConnection->fd == io.fd);
 
     flags.do_next_read = 0;
 
- debugs(11, 5, "httpReadReply: FD " << fd << ": len " << len << ".");
+ debugs(11, 5, "httpReadReply: FD " << io.fd << ": len " << len << ".");
 
     // Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us
     if (io.flag == COMM_ERR_CLOSING) {
@@ -1047,7 +1052,7 @@
 
     // handle I/O errors
     if (io.flag != COMM_OK || len < 0) {
- debugs(11, 2, "httpReadReply: FD " << fd << ": read failure: " << xstrerror() << ".");
+ debugs(11, 2, "httpReadReply: FD " << io.fd << ": read failure: " << xstrerror() << ".");
 
         if (ignoreErrno(io.xerrno)) {
             flags.do_next_read = 1;
@@ -1057,9 +1062,8 @@
             err->xerrno = io.xerrno;
             fwd->fail(err);
             flags.do_next_read = 0;
- comm_close(fd);
+ serverConnection->close();
         }
-
         return;
     }
 
@@ -1095,7 +1099,7 @@
      * not allowing connection reuse in the first place.
      */
 #if DONT_DO_THIS
- if (!flags.headers_parsed && len > 0 && fd_table[fd].uses > 1) {
+ if (!flags.headers_parsed && len > 0 && fd_table[serverConnection->fd].uses > 1) {
         /* Skip whitespace between replies */
 
         while (len > 0 && xisspace(*buf))
@@ -1204,7 +1208,7 @@
     entry->reset();
     fwd->fail(errorCon(error, HTTP_BAD_GATEWAY, fwd->request));
     flags.do_next_read = 0;
- comm_close(fd);
+ serverConnection->close();
     return false; // quit on error
 }
 
@@ -1327,10 +1331,10 @@
             /* Wait for more data or EOF condition */
             if (flags.keepalive_broken) {
                 call = NULL;
- commSetTimeout(fd, 10, call);
+ commSetTimeout(serverConnection->fd, 10, call);
             } else {
                 call = NULL;
- commSetTimeout(fd, Config.Timeout.read, call);
+ commSetTimeout(serverConnection->fd, Config.Timeout.read, call);
             }
 
             flags.do_next_read = 1;
@@ -1340,12 +1344,12 @@
             debugs(11, 5, "processReplyBody: COMPLETE_PERSISTENT_MSG");
             /* yes we have to clear all these! */
             call = NULL;
- commSetTimeout(fd, -1, call);
+ commSetTimeout(serverConnection->fd, -1, call);
             flags.do_next_read = 0;
 
- comm_remove_close_handler(fd, closeHandler);
+ comm_remove_close_handler(serverConnection->fd, closeHandler);
             closeHandler = NULL;
- fwd->unregister(fd);
+ fwd->unregister(serverConnection);
 
             if (orig_request->flags.spoof_client_ip)
                 client_addr = orig_request->client_addr;
@@ -1358,13 +1362,13 @@
             }
 
             if (orig_request->pinnedConnection() && ispinned) {
- orig_request->pinnedConnection()->pinConnection(fd, orig_request, _peer,
+ orig_request->pinnedConnection()->pinConnection(serverConnection->fd, orig_request, _peer,
                         (request->flags.connection_auth != 0));
             } else {
- fwd->pconnPush(fd, _peer, request, orig_request->GetHost(), client_addr);
+ fwd->pconnPush(serverConnection, _peer, request, orig_request->GetHost(), client_addr);
             }
 
- fd = -1;
+ serverConnection = NULL;
 
             serverComplete();
             return;
@@ -1386,7 +1390,7 @@
     const int read_size = replyBodySpace(*readBuf, minRead);
 
     debugs(11,9, HERE << (flags.do_next_read ? "may" : "wont") <<
- " read up to " << read_size << " bytes from FD " << fd);
+ " read up to " << read_size << " bytes from FD " << serverConnection->fd);
 
     /*
      * why <2? Because delayAwareRead() won't actually read if
@@ -1402,7 +1406,7 @@
     if (flags.do_next_read) {
         flags.do_next_read = 0;
         typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
- entry->delayAwareRead(fd, readBuf->space(read_size), read_size,
+ entry->delayAwareRead(serverConnection->fd, readBuf->space(read_size), read_size,
                               asyncCall(11, 5, "HttpStateData::readReply",
                                         Dialer(this, &HttpStateData::readReply)));
     }
@@ -1414,14 +1418,14 @@
 void
 HttpStateData::sendComplete(const CommIoCbParams &io)
 {
- debugs(11, 5, "httpSendComplete: FD " << fd << ": size " << io.size << ": errflag " << io.flag << ".");
+ debugs(11, 5, "httpSendComplete: FD " << serverConnection->fd << ": size " << io.size << ": errflag " << io.flag << ".");
 #if URL_CHECKSUM_DEBUG
 
     entry->mem_obj->checkUrlChecksum();
 #endif
 
     if (io.size > 0) {
- fd_bytes(fd, io.size, FD_WRITE);
+ fd_bytes(io.fd, io.size, FD_WRITE);
         kb_incr(&statCounter.server.all.kbytes_out, io.size);
         kb_incr(&statCounter.server.http.kbytes_out, io.size);
     }
@@ -1434,7 +1438,7 @@
         err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request);
         err->xerrno = io.xerrno;
         fwd->fail(err);
- comm_close(fd);
+ serverConnection->close();
         return;
     }
 
@@ -1450,7 +1454,7 @@
     AsyncCall::Pointer timeoutCall = asyncCall(11, 5, "HttpStateData::httpTimeout",
                                       TimeoutDialer(this,&HttpStateData::httpTimeout));
 
- commSetTimeout(fd, Config.Timeout.read, timeoutCall);
+ commSetTimeout(serverConnection->fd, Config.Timeout.read, timeoutCall);
 
     flags.request_sent = 1;
 
@@ -1461,24 +1465,22 @@
 void
 HttpStateData::closeServer()
 {
- debugs(11,5, HERE << "closing HTTP server FD " << fd << " this " << this);
+ debugs(11,5, HERE << "closing HTTP server FD " << serverConnection->fd << " this " << this);
 
- if (fd >= 0) {
- fwd->unregister(fd);
- comm_remove_close_handler(fd, closeHandler);
+ if (serverConnection->isOpen()) {
+ fwd->unregister(serverConnection);
+ comm_remove_close_handler(serverConnection->fd, closeHandler);
         closeHandler = NULL;
- comm_close(fd);
- fd = -1;
+ serverConnection->close();
     }
 }
 
 bool
 HttpStateData::doneWithServer() const
 {
- return fd < 0;
+ return serverConnection == NULL || !serverConnection->isOpen();
 }
 
-
 /*
  * Fixup authentication request headers for special cases
  */
@@ -1983,10 +1985,10 @@
 {
     MemBuf mb;
 
- debugs(11, 5, "httpSendRequest: FD " << fd << ", request " << request << ", this " << this << ".");
+ debugs(11, 5, "httpSendRequest: FD " << serverConnection->fd << ", request " << request << ", this " << this << ".");
 
- if (!canSend(fd)) {
- debugs(11,3, HERE << "cannot send request to closing FD " << fd);
+ if (!canSend(serverConnection->fd)) {
+ debugs(11,3, HERE << "cannot send request to closing FD " << serverConnection->fd);
         assert(closeHandler != NULL);
         return false;
     }
@@ -1994,7 +1996,7 @@
     typedef CommCbMemFunT<HttpStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = asyncCall(11, 5, "HttpStateData::httpTimeout",
                                       TimeoutDialer(this,&HttpStateData::httpTimeout));
- commSetTimeout(fd, Config.Timeout.lifetime, timeoutCall);
+ commSetTimeout(serverConnection->fd, Config.Timeout.lifetime, timeoutCall);
     flags.do_next_read = 1;
     maybeReadVirginBody();
 
@@ -2050,8 +2052,8 @@
     mb.init();
     request->peer_host=_peer?_peer->host:NULL;
     buildRequestPrefix(request, orig_request, entry, &mb, flags);
- debugs(11, 6, "httpSendRequest: FD " << fd << ":\n" << mb.buf);
- comm_write_mbuf(fd, &mb, requestSender);
+ debugs(11, 6, "httpSendRequest: FD " << serverConnection->fd << ":\n" << mb.buf);
+ comm_write_mbuf(serverConnection->fd, &mb, requestSender);
 
     return true;
 }
@@ -2081,7 +2083,7 @@
 void
 HttpStateData::doneSendingRequestBody()
 {
- debugs(11,5, HERE << "doneSendingRequestBody: FD " << fd);
+ debugs(11,5, HERE << "doneSendingRequestBody: FD " << serverConnection->fd);
 
 #if HTTP_VIOLATIONS
     if (Config.accessList.brokenPosts) {
@@ -2089,14 +2091,14 @@
         if (!ch.fastCheck()) {
             debugs(11, 5, "doneSendingRequestBody: didn't match brokenPosts");
             CommIoCbParams io(NULL);
- io.fd=fd;
- io.flag=COMM_OK;
+ io.fd = serverConnection->fd;
+ io.flag = COMM_OK;
             sendComplete(io);
         } else {
             debugs(11, 2, "doneSendingRequestBody: matched brokenPosts");
 
- if (!canSend(fd)) {
- debugs(11,2, HERE << "cannot send CRLF to closing FD " << fd);
+ if (!canSend(serverConnection->fd)) {
+ debugs(11,2, HERE << "cannot send CRLF to closing FD " << serverConnection->fd);
                 assert(closeHandler != NULL);
                 return;
             }
@@ -2104,7 +2106,7 @@
             typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
             Dialer dialer(this, &HttpStateData::sendComplete);
             AsyncCall::Pointer call= asyncCall(11,5, "HttpStateData::SendComplete", dialer);
- comm_write(fd, "\r\n", 2, call);
+ comm_write(serverConnection->fd, "\r\n", 2, call);
         }
         return;
     }
@@ -2112,8 +2114,8 @@
 #endif /* HTTP_VIOLATIONS */
 
     CommIoCbParams io(NULL);
- io.fd=fd;
- io.flag=COMM_OK;
+ io.fd = serverConnection->fd;
+ io.flag = COMM_OK;
     sendComplete(io);
 }
 
@@ -2121,7 +2123,7 @@
 void
 HttpStateData::handleMoreRequestBodyAvailable()
 {
- if (eof || fd < 0) {
+ if (eof || !serverConnection->isOpen()) {
         // XXX: we should check this condition in other callbacks then!
         // TODO: Check whether this can actually happen: We should unsubscribe
         // as a body consumer when the above condition(s) are detected.
@@ -2139,7 +2141,7 @@
             debugs(11, 1, "http handleMoreRequestBodyAvailable: Likely proxy abuse detected '" << orig_request->client_addr << "' -> '" << entry->url() << "'" );
 
             if (virginReply()->sline.status == HTTP_INVALID_HEADER) {
- comm_close(fd);
+ serverConnection->close();
                 return;
             }
         }
@@ -2155,8 +2157,8 @@
     ServerStateData::handleRequestBodyProducerAborted();
     // XXX: SendComplete(COMM_ERR_CLOSING) does little. Is it enough?
     CommIoCbParams io(NULL);
- io.fd=fd;
- io.flag=COMM_ERR_CLOSING;
+ io.fd = serverConnection->fd;
+ io.flag = COMM_ERR_CLOSING;
     sendComplete(io);
 }
 
@@ -2177,10 +2179,10 @@
 HttpStateData::abortTransaction(const char *reason)
 {
     debugs(11,5, HERE << "aborting transaction for " << reason <<
- "; FD " << fd << ", this " << this);
+ "; FD " << serverConnection->fd << ", this " << this);
 
- if (fd >= 0) {
- comm_close(fd);
+ if (serverConnection->isOpen()) {
+ serverConnection->close();
         return;
     }
 

=== modified file 'src/http.h'
--- src/http.h 2009-07-02 16:36:36 +0000
+++ src/http.h 2010-07-19 14:57:52 +0000
@@ -36,6 +36,7 @@
 
 #include "StoreIOBuffer.h"
 #include "comm.h"
+#include "comm/forward.h"
 #include "forward.h"
 #include "Server.h"
 #include "ChunkedCodingParser.h"
@@ -66,7 +67,6 @@
     int eof; /* reached end-of-object? */
     int lastChunk; /* reached last chunk of a chunk-encoded reply */
     HttpRequest *orig_request;
- int fd;
     http_state_flags flags;
     size_t read_sz;
     int header_bytes_read; // to find end of response,
@@ -82,6 +82,12 @@
     virtual HttpRequest *originalRequest();
 
 private:
+ /**
+ * The current server connection.
+ * Maybe open, closed, or NULL.
+ * Use doneWithServer() to check if the server is available for use.
+ */
+ Comm::ConnectionPointer serverConnection;
     AsyncCall::Pointer closeHandler;
     enum ConnectionStatus {
         INCOMPLETE_MSG,

=== modified file 'src/icp_v2.cc'
--- src/icp_v2.cc 2010-07-06 23:09:44 +0000
+++ src/icp_v2.cc 2010-07-13 08:38:07 +0000
@@ -36,20 +36,20 @@
  */
 
 #include "squid.h"
-#include "Store.h"
-#include "comm.h"
-#include "ICP.h"
+#include "AccessLogEntry.h"
+#include "acl/Acl.h"
+#include "acl/FilledChecklist.h"
+#include "comm/Connection.h"
 #include "HttpRequest.h"
-#include "acl/FilledChecklist.h"
-#include "acl/Acl.h"
-#include "AccessLogEntry.h"
-#include "wordlist.h"
-#include "SquidTime.h"
-#include "SwapDir.h"
 #include "icmp/net_db.h"
+#include "ICP.h"
 #include "ip/Address.h"
 #include "ipc/StartListening.h"
 #include "rfc1738.h"
+#include "Store.h"
+#include "SquidTime.h"
+#include "SwapDir.h"
+#include "wordlist.h"
 
 /// dials icpIncomingConnectionOpened call
 class IcpListeningStartedDialer: public CallDialer,

=== modified file 'src/ident/AclIdent.cc'
--- src/ident/AclIdent.cc 2009-06-02 15:37:40 +0000
+++ src/ident/AclIdent.cc 2010-07-19 13:18:06 +0000
@@ -42,6 +42,7 @@
 #include "acl/RegexData.h"
 #include "acl/UserData.h"
 #include "client_side.h"
+#include "comm/Connection.h"
 #include "ident/AclIdent.h"
 #include "ident/Ident.h"
 
@@ -129,7 +130,11 @@
     if (checklist->conn() != NULL) {
         debugs(28, 3, HERE << "Doing ident lookup" );
         checklist->asyncInProgress(true);
- Ident::Start(checklist->conn()->me, checklist->conn()->peer, LookupDone, checklist);
+ // TODO: store a Comm::Connection in either checklist or ConnStateData one day.
+ Comm::ConnectionPointer cc = new Comm::Connection;
+ cc->local = checklist->conn()->me;
+ cc->remote = checklist->conn()->peer;
+ Ident::Start(cc, LookupDone, checklist);
     } else {
         debugs(28, DBG_IMPORTANT, "IdentLookup::checkForAsync: Can't start ident lookup. No client connection" );
         checklist->currentAnswer(ACCESS_DENIED);

=== modified file 'src/ident/Ident.cc'
--- src/ident/Ident.cc 2010-04-17 02:29:04 +0000
+++ src/ident/Ident.cc 2010-07-18 11:55:28 +0000
@@ -37,6 +37,9 @@
 #if USE_IDENT
 
 #include "comm.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
+#include "CommCalls.h"
 #include "ident/Config.h"
 #include "ident/Ident.h"
 #include "MemBuf.h"
@@ -56,10 +59,7 @@
 
 typedef struct _IdentStateData {
     hash_link hash; /* must be first */
- int fd; /* IDENT fd */
-
- Ip::Address me;
- Ip::Address my_peer;
+ Comm::ConnectionPointer conn;
     IdentClient *clients;
     char buf[4096];
 } IdentStateData;
@@ -103,7 +103,7 @@
 {
     IdentStateData *state = (IdentStateData *)data;
     identCallback(state, NULL);
- comm_close(state->fd);
+ state->conn->close();
     hash_remove_link(ident_hash, (hash_link *) state);
     xfree(state->hash.key);
     cbdataFree(state);
@@ -113,26 +113,28 @@
 Ident::Timeout(int fd, void *data)
 {
     IdentStateData *state = (IdentStateData *)data;
- debugs(30, 3, "identTimeout: FD " << fd << ", " << state->my_peer);
-
- comm_close(fd);
+ debugs(30, 3, HERE << "FD " << fd << ", " << state->conn->remote);
+ state->conn->close();
 }
 
 void
-Ident::ConnectDone(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
+Ident::ConnectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
 {
     IdentStateData *state = (IdentStateData *)data;
- IdentClient *c;
 
     if (status != COMM_OK) {
- /* Failed to connect */
- comm_close(fd);
+ if (status == COMM_TIMEOUT) {
+ debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
+ }
         return;
     }
 
+ assert(conn != NULL && conn == state->conn);
+
     /*
      * see if any of our clients still care
      */
+ IdentClient *c;
     for (c = state->clients; c; c = c->next) {
         if (cbdataReferenceValid(c->callback_data))
             break;
@@ -140,18 +142,20 @@
 
     if (c == NULL) {
         /* no clients care */
- comm_close(fd);
+ conn->close();
         return;
     }
 
+ comm_add_close_handler(conn->fd, Ident::Close, state);
+
     MemBuf mb;
     mb.init();
     mb.Printf("%d, %d\r\n",
- state->my_peer.GetPort(),
- state->me.GetPort());
- comm_write_mbuf(fd, &mb, NULL, state);
- comm_read(fd, state->buf, BUFSIZ, Ident::ReadReply, state);
- commSetTimeout(fd, Ident::TheConfig.timeout, Ident::Timeout, state);
+ conn->remote.GetPort(),
+ conn->local.GetPort());
+ comm_write_mbuf(conn->fd, &mb, NULL, state);
+ comm_read(conn->fd, state->buf, BUFSIZ, Ident::ReadReply, state);
+ commSetTimeout(conn->fd, Ident::TheConfig.timeout, Ident::Timeout, state);
 }
 
 void
@@ -161,10 +165,11 @@
     char *ident = NULL;
     char *t = NULL;
 
- assert (buf == state->buf);
+ assert(buf == state->buf);
+ assert(fd == state->conn->fd);
 
     if (flag != COMM_OK || len <= 0) {
- comm_close(fd);
+ state->conn->close();
         return;
     }
 
@@ -181,7 +186,7 @@
     if ((t = strchr(buf, '\n')))
         *t = '\0';
 
- debugs(30, 5, "identReadReply: FD " << fd << ": Read '" << buf << "'");
+ debugs(30, 5, HERE << "FD " << fd << ": Read '" << buf << "'");
 
     if (strstr(buf, "USERID")) {
         if ((ident = strrchr(buf, ':'))) {
@@ -190,7 +195,7 @@
         }
     }
 
- comm_close(fd);
+ state->conn->close();
 }
 
 void
@@ -213,17 +218,15 @@
  * start a TCP connection to the peer host on port 113
  */
 void
-Ident::Start(Ip::Address &me, Ip::Address &my_peer, IDCB * callback, void *data)
+Ident::Start(Comm::ConnectionPointer &conn, IDCB * callback, void *data)
 {
     IdentStateData *state;
- int fd;
     char key1[IDENT_KEY_SZ];
     char key2[IDENT_KEY_SZ];
     char key[IDENT_KEY_SZ];
- char ntoabuf[MAX_IPSTRLEN];
 
- me.ToURL(key1, IDENT_KEY_SZ);
- my_peer.ToURL(key2, IDENT_KEY_SZ);
+ conn->local.ToURL(key1, IDENT_KEY_SZ);
+ conn->remote.ToURL(key2, IDENT_KEY_SZ);
     snprintf(key, IDENT_KEY_SZ, "%s,%s", key1, key2);
 
     if (!ident_hash) {
@@ -234,33 +237,20 @@
         return;
     }
 
- Ip::Address addr = me;
- addr.SetPort(0); // NP: use random port for secure outbound to IDENT_PORT
-
- fd = comm_open_listener(SOCK_STREAM,
- IPPROTO_TCP,
- addr,
- COMM_NONBLOCKING,
- "ident");
-
- if (fd == COMM_ERROR) {
- /* Failed to get a local socket */
- callback(NULL, data);
- return;
- }
-
     CBDATA_INIT_TYPE(IdentStateData);
     state = cbdataAlloc(IdentStateData);
     state->hash.key = xstrdup(key);
- state->fd = fd;
- state->me = me;
- state->my_peer = my_peer;
+
+ // copy the conn details. We dont want the original FD to be re-used by IDENT.
+ state->conn = conn->copyDetails();
+ // NP: use random port for secure outbound to IDENT_PORT
+ state->conn->local.SetPort(0);
+
     ClientAdd(state, callback, data);
     hash_join(ident_hash, &state->hash);
- comm_add_close_handler(fd, Ident::Close, state);
- commSetTimeout(fd, Ident::TheConfig.timeout, Ident::Timeout, state);
- state->my_peer.NtoA(ntoabuf,MAX_IPSTRLEN);
- commConnectStart(fd, ntoabuf, IDENT_PORT, Ident::ConnectDone, state);
+
+ AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
+ AsyncJob::AsyncStart(new Comm::ConnOpener(state->conn, call, Ident::TheConfig.timeout));
 }
 
 void

=== modified file 'src/ident/Ident.h'
--- src/ident/Ident.h 2010-05-02 18:52:45 +0000
+++ src/ident/Ident.h 2010-06-27 11:31:31 +0000
@@ -14,8 +14,7 @@
 #if USE_IDENT
 
 #include "cbdata.h"
-
-#include "ip/forward.h"
+#include "comm/forward.h"
 
 namespace Ident
 {
@@ -28,7 +27,7 @@
  * Self-registers with a global ident lookup manager,
  * will call Ident::Init() itself if the manager has not been initialized already.
  */
-void Start(Ip::Address &me, Ip::Address &my_peer, IDCB * callback, void *cbdata);
+void Start(Comm::ConnectionPointer &conn, IDCB * callback, void *cbdata);
 
 /**
  \ingroup IdentAPI

=== modified file 'src/ipc.cc'
--- src/ipc.cc 2010-05-02 19:32:42 +0000
+++ src/ipc.cc 2010-06-08 14:03:24 +0000
@@ -31,7 +31,7 @@
  */
 
 #include "squid.h"
-#include "comm.h"
+#include "comm/Connection.h"
 #include "fde.h"
 #include "ip/Address.h"
 #include "rfc1738.h"

=== modified file 'src/ipc/Port.cc'
--- src/ipc/Port.cc 2010-07-06 18:58:38 +0000
+++ src/ipc/Port.cc 2010-07-15 11:50:37 +0000
@@ -5,9 +5,10 @@
  *
  */
 
-
 #include "config.h"
+#include "comm.h"
 #include "CommCalls.h"
+#include "comm/Connection.h"
 #include "ipc/Port.h"
 
 const char Ipc::coordinatorAddr[] = DEFAULT_PREFIX "/var/run/coordinator.ipc";

=== modified file 'src/ipc/UdsOp.cc'
--- src/ipc/UdsOp.cc 2010-07-06 23:09:44 +0000
+++ src/ipc/UdsOp.cc 2010-07-15 11:50:37 +0000
@@ -4,12 +4,11 @@
  * DEBUG: section 54 Interprocess Communication
  *
  */
-
-
 #include "config.h"
+#include "base/TextException.h"
 #include "comm.h"
 #include "CommCalls.h"
-#include "base/TextException.h"
+#include "comm/Connection.h"
 #include "ipc/UdsOp.h"
 
 

=== modified file 'src/ipcache.cc'
--- src/ipcache.cc 2010-06-01 00:12:34 +0000
+++ src/ipcache.cc 2010-06-09 09:50:31 +0000
@@ -32,12 +32,13 @@
 
 #include "squid.h"
 #include "cbdata.h"
+#include "CacheManager.h"
+#include "DnsLookupDetails.h"
 #include "event.h"
-#include "CacheManager.h"
+#include "ip/Address.h"
 #include "SquidTime.h"
 #include "Store.h"
 #include "wordlist.h"
-#include "ip/Address.h"
 
 /**
  \defgroup IPCacheAPI IP Cache API
@@ -621,9 +622,9 @@
  * of scheduling an async call. This reentrant behavior means that the
  * user job must be extra careful after calling ipcache_nbgethostbyname,
  * especially if the handler destroys the job. Moreover, the job has
- * no way of knowing whether the reentrant call happened. commConnectStart
- * protects the job by scheduling an async call, but some user code calls
- * ipcache_nbgethostbyname directly.
+ * no way of knowing whether the reentrant call happened.
+ * Comm::Connection setup usually protects the job by scheduling an async call,
+ * but some user code calls ipcache_nbgethostbyname directly.
  */
 void
 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)

=== modified file 'src/log/ModTcp.cc'
--- src/log/ModTcp.cc 2010-04-20 12:51:57 +0000
+++ src/log/ModTcp.cc 2010-06-08 14:03:24 +0000
@@ -33,6 +33,7 @@
 
 #include "squid.h"
 #include "comm.h"
+#include "comm/Connection.h"
 #include "log/File.h"
 #include "log/ModTcp.h"
 #include "Parsing.h"

=== modified file 'src/log/ModUdp.cc'
--- src/log/ModUdp.cc 2010-04-17 02:29:04 +0000
+++ src/log/ModUdp.cc 2010-06-08 14:03:24 +0000
@@ -32,6 +32,7 @@
 
 #include "squid.h"
 #include "comm.h"
+#include "comm/Connection.h"
 #include "log/File.h"
 #include "log/ModUdp.h"
 #include "Parsing.h"

=== modified file 'src/main.cc'
--- src/main.cc 2010-07-07 16:41:03 +0000
+++ src/main.cc 2010-07-13 08:38:07 +0000
@@ -77,7 +77,7 @@
 #include "MemPool.h"
 #include "icmp/IcmpSquid.h"
 #include "icmp/net_db.h"
-
+#include "PeerSelectState.h"
 #if USE_LOADABLE_MODULES
 #include "LoadableModules.h"
 #endif

=== modified file 'src/neighbors.cc'
--- src/neighbors.cc 2010-05-02 19:32:42 +0000
+++ src/neighbors.cc 2010-07-18 11:55:28 +0000
@@ -33,8 +33,10 @@
 #include "squid.h"
 #include "ProtoPort.h"
 #include "acl/FilledChecklist.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
+#include "CacheManager.h"
 #include "event.h"
-#include "CacheManager.h"
 #include "htcp.h"
 #include "HttpRequest.h"
 #include "ICP.h"
@@ -60,7 +62,7 @@
 static void neighborCountIgnored(peer *);
 static void peerRefreshDNS(void *);
 static IPH peerDNSConfigure;
-static int peerProbeConnect(peer *);
+static bool peerProbeConnect(peer *);
 static CNCB peerProbeConnectDone;
 static void peerCountMcastPeersDone(void *data);
 static void peerCountMcastPeersStart(void *data);
@@ -1342,68 +1344,43 @@
         p->tcp_up = p->connect_fail_limit;
 }
 
-/// called by Comm when test_fd is closed while connect is in progress
-static void
-peerProbeClosed(int fd, void *data)
-{
- peer *p = (peer*)data;
- p->test_fd = -1;
- // it is a failure because we failed to connect
- peerConnectFailedSilent(p);
-}
-
-static void
-peerProbeConnectTimeout(int fd, void *data)
-{
- peer * p = (peer *)data;
- comm_remove_close_handler(fd, &peerProbeClosed, p);
- comm_close(fd);
- p->test_fd = -1;
- peerConnectFailedSilent(p);
-}
-
 /*
 * peerProbeConnect will be called on dead peers by neighborUp
 */
-static int
+static bool
 peerProbeConnect(peer * p)
 {
- int fd;
- time_t ctimeout = p->connect_timeout > 0 ? p->connect_timeout
- : Config.Timeout.peer_connect;
- int ret = squid_curtime - p->stats.last_connect_failure > ctimeout * 10;
+ 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->test_fd != -1)
+ 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 */
 
- Ip::Address temp(getOutgoingAddr(NULL,p));
-
- fd = comm_open(SOCK_STREAM, IPPROTO_TCP, temp, COMM_NONBLOCKING, p->host);
-
- if (fd < 0)
- return ret;
-
- comm_add_close_handler(fd, &peerProbeClosed, p);
- commSetTimeout(fd, ctimeout, peerProbeConnectTimeout, p);
-
- p->test_fd = fd;
+ /* for each IP address of this peer. 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;
+ conn->remote = p->addresses[i];
+ conn->remote.SetPort(p->http_port);
+ getOutgoingAddress(NULL, conn);
+
+ p->testing_now++;
+
+ AsyncCall::Pointer call = commCbCall(15,3, "peerProbeConnectDone", CommConnectCbPtrFun(peerProbeConnectDone, p));
+ Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, ctimeout);
+ cs->setHost(p->host);
+ AsyncJob::AsyncStart(cs);
+ }
 
     p->stats.last_connect_probe = squid_curtime;
 
- commConnectStart(p->test_fd,
- p->host,
- p->http_port,
- peerProbeConnectDone,
- p);
-
     return ret;
 }
 
 static void
-peerProbeConnectDone(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
+peerProbeConnectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
 {
     peer *p = (peer*)data;
 
@@ -1413,9 +1390,8 @@
         peerConnectFailedSilent(p);
     }
 
- comm_remove_close_handler(fd, &peerProbeClosed, p);
- comm_close(fd);
- p->test_fd = -1;
+ conn->close();
+ p->testing_now--;
     return;
 }
 

=== modified file 'src/peer_select.cc'
--- src/peer_select.cc 2010-01-30 00:30:56 +0000
+++ src/peer_select.cc 2010-07-19 13:18:06 +0000
@@ -33,17 +33,18 @@
  */
 
 #include "squid.h"
+#include "acl/FilledChecklist.h"
+#include "DnsLookupDetails.h"
 #include "event.h"
-#include "PeerSelectState.h"
-#include "Store.h"
+#include "forward.h"
 #include "hier_code.h"
+#include "htcp.h"
+#include "HttpRequest.h"
+#include "icmp/net_db.h"
 #include "ICP.h"
-#include "HttpRequest.h"
-#include "acl/FilledChecklist.h"
-#include "htcp.h"
-#include "forward.h"
+#include "PeerSelectState.h"
 #include "SquidTime.h"
-#include "icmp/net_db.h"
+#include "Store.h"
 
 static struct {
     int timeouts;
@@ -74,6 +75,8 @@
 static void peerGetAllParents(ps_state *);
 static void peerAddFwdServer(FwdServer **, peer *, hier_code);
 static void peerSelectPinned(ps_state * ps);
+static void peerSelectDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data);
+
 
 CBDATA_CLASS_INIT(ps_state);
 
@@ -121,7 +124,8 @@
 
 
 void
-peerSelect(HttpRequest * request,
+peerSelect(Comm::Paths * paths,
+ HttpRequest * request,
            StoreEntry * entry,
            PSC * callback,
            void *callback_data)
@@ -139,6 +143,8 @@
 
     psstate->entry = entry;
 
+ psstate->paths = paths;
+
     psstate->callback = callback;
 
     psstate->callback_data = cbdataReference(callback_data);
@@ -182,8 +188,6 @@
 {
     StoreEntry *entry = psstate->entry;
     FwdServer *fs = psstate->servers;
- PSC *callback;
- void *cbdata;
 
     if (entry) {
         debugs(44, 3, "peerSelectCallback: " << entry->url() );
@@ -203,17 +207,93 @@
 
     psstate->ping.stop = current_time;
     psstate->request->hier.ping = psstate->ping;
- callback = psstate->callback;
+}
+
+void
+peerSelectDnsPaths(ps_state *psstate)
+{
+ FwdServer *fs = psstate->servers;
+
+ // convert the list of FwdServer destinations into destinations IP addresses
+ if (fs && psstate->paths->size() < (unsigned int)Config.forward_max_tries) {
+ // send the next one off for DNS lookup.
+ const char *host = fs->_peer ? fs->_peer->host : psstate->request->GetHost();
+ debugs(44, 2, "Find IP destination for: " << psstate->entry->url() << "' via " << host);
+ ipcache_nbgethostbyname(host, peerSelectDnsResults, psstate);
+ return;
+ }
+
+ // done with DNS lookups. pass back to caller
+ PSC *callback = psstate->callback;
     psstate->callback = NULL;
 
+ debugs(44, 2, "Found IP destination for: " << psstate->entry->url() << "'");
+
+ void *cbdata;
     if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
- psstate->servers = NULL;
- callback(fs, cbdata);
+ callback(psstate->paths, cbdata);
     }
 
     peerSelectStateFree(psstate);
 }
 
+static void
+peerSelectDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &details, void *data)
+{
+ ps_state *psstate = (ps_state *)data;
+
+ psstate->request->recordLookup(details);
+
+ FwdServer *fs = psstate->servers;
+ if (ia != NULL) {
+
+ assert(ia->cur < ia->count);
+
+ // loop over each result address, adding to the possible destinations.
+ int ip = ia->cur;
+ for (int n = 0; n < ia->count; n++, ip++) {
+ Comm::ConnectionPointer p;
+
+ if (ip >= ia->count) ip = 0; // looped back to zero.
+
+ // Enforce forward_max_tries configuration.
+ if (psstate->paths->size() >= (unsigned int)Config.forward_max_tries)
+ break;
+
+ // for TPROXY we must skip unusable addresses.
+ if (psstate->request->flags.spoof_client_ip && !(fs->_peer && fs->_peer->options.no_tproxy) ) {
+ if(ia->in_addrs[n].IsIPv4() != psstate->request->client_addr.IsIPv4()) {
+ // we CAN'T spoof the address on this link. find another.
+ continue;
+ }
+ }
+
+ p = new Comm::Connection();
+ p->remote = ia->in_addrs[n];
+ if (fs->_peer)
+ p->remote.SetPort(fs->_peer->http_port);
+ else
+ p->remote.SetPort(psstate->request->port);
+ p->peerType = fs->code;
+
+ // check for a configured outgoing address for this destination...
+ getOutgoingAddress(psstate->request, p);
+ p->tos = getOutgoingTOS(psstate->request);
+
+ psstate->paths->push_back(p);
+ }
+ } else {
+ debugs(44, 3, HERE << "Unknown host: " << fs->_peer ? fs->_peer->host : psstate->request->GetHost());
+ }
+
+ psstate->servers = fs->next;
+ cbdataReferenceDone(fs->_peer);
+ memFree(fs, MEM_FWD_SERVER);
+
+ // see if more paths can be found
+ peerSelectDnsPaths(psstate);
+}
+
 static int
 peerCheckNetdbDirect(ps_state * psstate)
 {
@@ -265,7 +345,7 @@
     HttpRequest *request = ps->request;
     debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
 
- /** If we don't known whether DIRECT is permitted ... */
+ /** If we don't know whether DIRECT is permitted ... */
     if (ps->direct == DIRECT_UNKNOWN) {
         if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
             /** check always_direct; */
@@ -344,15 +424,17 @@
         break;
     }
 
- peerSelectCallback(ps);
+ // resolve the possible peers
+ peerSelectDnsPaths(ps);
 }
 
-/*
+int peerAllowedToUse(const peer * p, HttpRequest * request);
+
+/**
  * peerSelectPinned
  *
- * Selects a pinned connection
+ * Selects a pinned connection.
  */
-int peerAllowedToUse(const peer * p, HttpRequest * request);
 static void
 peerSelectPinned(ps_state * ps)
 {
@@ -374,7 +456,7 @@
     }
 }
 
-/*
+/**
  * peerGetSomeNeighbor
  *
  * Selects a neighbor (parent or sibling) based on one of the
@@ -599,6 +681,7 @@
 peerSelectInit(void)
 {
     memset(&PeerStats, '\0', sizeof(PeerStats));
+ memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
 }
 
 static void

=== modified file 'src/protos.h'
--- src/protos.h 2010-07-06 23:09:44 +0000
+++ src/protos.h 2010-07-13 08:38:07 +0000
@@ -398,9 +398,6 @@
 
 SQUIDCEXTERN peer *whichPeer(const Ip::Address &from);
 
-SQUIDCEXTERN void peerSelect(HttpRequest *, StoreEntry *, PSC *, void *data);
-SQUIDCEXTERN void peerSelectInit(void);
-
 /* peer_digest.c */
 class PeerDigest;
 SQUIDCEXTERN PeerDigest *peerDigestCreate(peer * p);
@@ -408,7 +405,8 @@
 SQUIDCEXTERN void peerDigestNotePeerGone(PeerDigest * pd);
 SQUIDCEXTERN void peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e);
 
-extern Ip::Address getOutgoingAddr(HttpRequest * request, struct peer *dst_peer);
+#include "comm/forward.h"
+extern void getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn);
 unsigned long getOutgoingTOS(HttpRequest * request);
 
 SQUIDCEXTERN void urnStart(HttpRequest *, StoreEntry *);

=== modified file 'src/snmp_core.cc'
--- src/snmp_core.cc 2010-07-06 23:09:44 +0000
+++ src/snmp_core.cc 2010-07-13 08:38:07 +0000
@@ -33,6 +33,7 @@
 #include "acl/FilledChecklist.h"
 #include "cache_snmp.h"
 #include "comm.h"
+#include "comm/Connection.h"
 #include "ipc/StartListening.h"
 #include "compat/strsep.h"
 #include "ip/Address.h"

=== modified file 'src/structs.h'
--- src/structs.h 2010-07-06 23:09:44 +0000
+++ src/structs.h 2010-07-13 08:38:07 +0000
@@ -443,6 +443,7 @@
     } onoff;
 
     int forward_max_tries;
+ int connect_retries;
 
     class ACL *aclList;
 
@@ -521,7 +522,6 @@
     char *errorStylesheet;
 
     struct {
- int maxtries;
         int onerror;
     } retry;
 
@@ -912,7 +912,7 @@
     int n_addresses;
     int rr_count;
     peer *next;
- int test_fd;
+ int testing_now;
 
     struct {
         unsigned int hash;

=== modified file 'src/tunnel.cc'
--- src/tunnel.cc 2010-04-17 02:29:04 +0000
+++ src/tunnel.cc 2010-07-19 13:18:06 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -34,18 +33,22 @@
  */
 
 #include "squid.h"
-#include "errorpage.h"
-#include "HttpRequest.h"
-#include "fde.h"
+#include "acl/FilledChecklist.h"
+#include "Array.h"
 #include "comm.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
+#include "client_side.h"
 #include "client_side_request.h"
-#include "acl/FilledChecklist.h"
 #if DELAY_POOLS
 #include "DelayId.h"
 #endif
-#include "client_side.h"
+#include "errorpage.h"
+#include "fde.h"
+#include "HttpRequest.h"
+#include "http.h"
 #include "MemBuf.h"
-#include "http.h"
+#include "PeerSelectState.h"
 
 class TunnelStateData
 {
@@ -65,7 +68,7 @@
     char *host; /* either request->host or proxy host */
     u_short port;
     HttpRequest *request;
- FwdServer *servers;
+ Comm::Paths paths;
 
     class Connection
     {
@@ -173,7 +176,7 @@
     assert(tunnelState != NULL);
     assert(tunnelState->noConnections());
     safe_free(tunnelState->url);
- FwdState::serversFree(&tunnelState->servers);
+ tunnelState->paths.clean();
     tunnelState->host = NULL;
     HTTPMSGUNLOCK(tunnelState->request);
     delete tunnelState;
@@ -181,7 +184,7 @@
 
 TunnelStateData::Connection::~Connection()
 {
- safe_free (buf);
+ safe_free(buf);
 }
 
 int
@@ -464,42 +467,6 @@
 }
 
 static void
-tunnelConnectTimeout(int fd, void *data)
-{
- TunnelStateData *tunnelState = (TunnelStateData *)data;
- HttpRequest *request = tunnelState->request;
- ErrorState *err = NULL;
-
- if (tunnelState->servers) {
- if (tunnelState->servers->_peer)
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- tunnelState->servers->_peer->host);
- else if (Config.onoff.log_ip_on_direct)
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- fd_table[tunnelState->server.fd()].ipaddr);
- else
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- tunnelState->host);
- } else
- debugs(26, 1, "tunnelConnectTimeout(): tunnelState->servers is NULL");
-
- err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
-
- *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
-
- err->xerrno = ETIMEDOUT;
-
- err->port = tunnelState->port;
-
- err->callback = tunnelErrorComplete;
-
- err->callback_data = tunnelState;
-
- errorSend(tunnelState->client.fd(), err);
- comm_close(fd);
-}
-
-static void
 tunnelConnectedWriteDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
@@ -553,52 +520,72 @@
 
 
 static void
-tunnelConnectDone(int fdnotused, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data)
+tunnelConnectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     HttpRequest *request = tunnelState->request;
     ErrorState *err = NULL;
 
- request->recordLookup(dns);
+#if DELAY_POOLS
+ /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
+ if (conn->getPeer() && conn->getPeer()->options.no_delay)
+ tunnelState->server.setDelayId(DelayId());
+#endif
 
- if (tunnelState->servers->_peer)
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- tunnelState->servers->_peer->host);
+ if (conn != NULL && conn->getPeer())
+ hierarchyNote(&tunnelState->request->hier, conn->peerType, conn->getPeer()->host);
     else if (Config.onoff.log_ip_on_direct)
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- fd_table[tunnelState->server.fd()].ipaddr);
+ hierarchyNote(&tunnelState->request->hier, conn->peerType, fd_table[conn->fd].ipaddr);
     else
- hierarchyNote(&tunnelState->request->hier, tunnelState->servers->code,
- tunnelState->host);
-
- if (status == COMM_ERR_DNS) {
- debugs(26, 4, "tunnelConnect: Unknown host: " << tunnelState->host);
- err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request);
- *tunnelState->status_ptr = HTTP_NOT_FOUND;
- err->dnsError = dns.error;
- err->callback = tunnelErrorComplete;
- err->callback_data = tunnelState;
- errorSend(tunnelState->client.fd(), err);
- } else if (status != COMM_OK) {
- err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
- *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
- err->xerrno = xerrno;
- err->port = tunnelState->port;
- err->callback = tunnelErrorComplete;
- err->callback_data = tunnelState;
- errorSend(tunnelState->client.fd(), err);
+ hierarchyNote(&tunnelState->request->hier, conn->peerType, tunnelState->host);
+
+ if (status != COMM_OK) {
+ /* At this point only the TCP handshake has failed. no data has been passed.
+ * we are allowed to re-try the TCP-level connection to alternate IPs for CONNECT.
+ */
+ tunnelState->paths.shift();
+ if (status != COMM_TIMEOUT && tunnelState->paths.size() > 0) {
+ /* Try another IP of this destination host */
+ AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState));
+ Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->paths[0], call, Config.Timeout.connect);
+ cs->setHost(tunnelState->url);
+ AsyncJob::AsyncStart(cs);
+ } else {
+ err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+ *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
+ err->xerrno = xerrno;
+ // on timeout is this still: err->xerrno = ETIMEDOUT;
+ err->port = conn->remote.GetPort();
+ err->callback = tunnelErrorComplete;
+ err->callback_data = tunnelState;
+ errorSend(tunnelState->client.fd(), err);
+ }
+ return;
+ }
+
+ tunnelState->server.fd(conn->fd);
+ comm_add_close_handler(tunnelState->server.fd(), tunnelServerClosed, tunnelState);
+
+ // TODO: hold the conn. drop these fields.
+ tunnelState->host = conn->getPeer() ? conn->getPeer()->host : xstrdup(request->GetHost());
+ request->peer_host = conn->getPeer() ? conn->getPeer()->host : NULL;
+ tunnelState->port = conn->remote.GetPort();
+
+ if (conn->getPeer()) {
+ tunnelState->request->peer_login = conn->getPeer()->login;
+ tunnelState->request->flags.proxying = 1;
     } else {
- if (tunnelState->servers->_peer)
- tunnelProxyConnected(tunnelState->server.fd(), tunnelState);
- else {
- tunnelConnected(tunnelState->server.fd(), tunnelState);
- }
-
- commSetTimeout(tunnelState->server.fd(),
- Config.Timeout.read,
- tunnelTimeout,
- tunnelState);
- }
+ tunnelState->request->peer_login = NULL;
+ tunnelState->request->flags.proxying = 0;
+ }
+
+ if (conn->getPeer())
+ tunnelProxyConnected(tunnelState->server.fd(), tunnelState);
+ else {
+ tunnelConnected(tunnelState->server.fd(), tunnelState);
+ }
+
+ commSetTimeout(tunnelState->server.fd(), Config.Timeout.read, tunnelTimeout, tunnelState);
 }
 
 void
@@ -606,7 +593,6 @@
 {
     /* Create state structure. */
     TunnelStateData *tunnelState = NULL;
- int sock;
     ErrorState *err = NULL;
     int answer;
     int fd = http->getConn()->fd;
@@ -639,43 +625,16 @@
     debugs(26, 3, "tunnelStart: '" << RequestMethodStr(request->method) << " " << url << "'");
     statCounter.server.all.requests++;
     statCounter.server.other.requests++;
- /* Create socket. */
- Ip::Address temp = getOutgoingAddr(request,NULL);
- int flags = COMM_NONBLOCKING;
- if (request->flags.spoof_client_ip) {
- flags |= COMM_TRANSPARENT;
- }
- sock = comm_openex(SOCK_STREAM,
- IPPROTO_TCP,
- temp,
- flags,
- getOutgoingTOS(request),
- url);
-
- if (sock == COMM_ERROR) {
- debugs(26, 4, "tunnelStart: Failed because we're out of sockets.");
- err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
- *status_ptr = HTTP_INTERNAL_SERVER_ERROR;
- err->xerrno = errno;
- errorSend(fd, err);
- return;
- }
 
     tunnelState = new TunnelStateData;
 #if DELAY_POOLS
-
     tunnelState->server.setDelayId(DelayId::DelayClient(http));
 #endif
-
     tunnelState->url = xstrdup(url);
     tunnelState->request = HTTPMSGLOCK(request);
     tunnelState->server.size_ptr = size_ptr;
     tunnelState->status_ptr = status_ptr;
     tunnelState->client.fd(fd);
- tunnelState->server.fd(sock);
- comm_add_close_handler(tunnelState->server.fd(),
- tunnelServerClosed,
- tunnelState);
     comm_add_close_handler(tunnelState->client.fd(),
                            tunnelClientClosed,
                            tunnelState);
@@ -683,14 +642,12 @@
                    Config.Timeout.lifetime,
                    tunnelTimeout,
                    tunnelState);
- commSetTimeout(tunnelState->server.fd(),
- Config.Timeout.connect,
- tunnelConnectTimeout,
- tunnelState);
- peerSelect(request,
+
+ peerSelect(&(tunnelState->paths), request,
                NULL,
                tunnelPeerSelectComplete,
                tunnelState);
+
     /*
      * Disable the client read handler until peer selection is complete
      * Take control away from client_side.c.
@@ -727,13 +684,12 @@
 }
 
 static void
-tunnelPeerSelectComplete(FwdServer * fs, void *data)
+tunnelPeerSelectComplete(Comm::Paths *peer_paths, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     HttpRequest *request = tunnelState->request;
- peer *g = NULL;
 
- if (fs == NULL) {
+ if (peer_paths == NULL || peer_paths->size() < 1) {
         ErrorState *err;
         err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
         *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
@@ -743,40 +699,10 @@
         return;
     }
 
- tunnelState->servers = fs;
- tunnelState->host = fs->_peer ? fs->_peer->host : xstrdup(request->GetHost());
- request->peer_host = fs->_peer ? fs->_peer->host : NULL;
-
- if (fs->_peer == NULL) {
- tunnelState->port = request->port;
- } else if (fs->_peer->http_port != 0) {
- tunnelState->port = fs->_peer->http_port;
- } else if ((g = peerFindByName(fs->_peer->host))) {
- tunnelState->port = g->http_port;
- } else {
- tunnelState->port = CACHE_HTTP_PORT;
- }
-
- if (fs->_peer) {
- tunnelState->request->peer_login = fs->_peer->login;
- tunnelState->request->flags.proxying = 1;
- } else {
- tunnelState->request->peer_login = NULL;
- tunnelState->request->flags.proxying = 0;
- }
-
-#if DELAY_POOLS
- /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
- if (g && g->options.no_delay)
- tunnelState->server.setDelayId(DelayId());
-
-#endif
-
- commConnectStart(tunnelState->server.fd(),
- tunnelState->host,
- tunnelState->port,
- tunnelConnectDone,
- tunnelState);
+ AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState));
+ Comm::ConnOpener *cs = new Comm::ConnOpener(tunnelState->paths[0], call, Config.Timeout.connect);
+ cs->setHost(tunnelState->url);
+ AsyncJob::AsyncStart(cs);
 }
 
 CBDATA_CLASS_INIT(TunnelStateData);

=== modified file 'src/typedefs.h'
--- src/typedefs.h 2010-04-11 09:02:42 +0000
+++ src/typedefs.h 2010-06-03 07:18:25 +0000
@@ -196,8 +196,6 @@
 typedef void IPH(const ipcache_addrs *, const DnsLookupDetails &details, void *);
 typedef void IRCB(struct peer *, peer_t, protocol_t, void *, void *data);
 
-class FwdServer;
-typedef void PSC(FwdServer *, void *);
 typedef void RH(void *data, char *);
 /* in wordlist.h */
 

=== modified file 'src/wccp.cc'
--- src/wccp.cc 2010-04-27 13:24:55 +0000
+++ src/wccp.cc 2010-06-08 14:03:24 +0000
@@ -33,11 +33,13 @@
  *
  */
 #include "squid.h"
+
+#if USE_WCCP
+
 #include "comm.h"
+#include "comm/Connection.h"
 #include "event.h"
 
-#if USE_WCCP
-
 #define WCCP_PORT 2048
 #define WCCP_REVISION 0
 #define WCCP_ACTIVE_CACHES 32

=== modified file 'src/wccp2.cc'
--- src/wccp2.cc 2010-05-25 10:53:13 +0000
+++ src/wccp2.cc 2010-06-08 14:03:24 +0000
@@ -35,6 +35,7 @@
 #if USE_WCCPv2
 
 #include "comm.h"
+#include "comm/Connection.h"
 #include "compat/strsep.h"
 #include "event.h"
 #include "ip/Address.h"

=== modified file 'src/whois.cc'
--- src/whois.cc 2010-02-06 06:32:11 +0000
+++ src/whois.cc 2010-07-19 13:18:06 +0000
@@ -81,7 +81,7 @@
 whoisStart(FwdState * fwd)
 {
     WhoisState *p;
- int fd = fwd->server_fd;
+ int fd = fwd->serverConnection()->fd;
     char *buf;
     size_t l;
     CBDATA_INIT_TYPE(WhoisState);

# Begin bundle
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
e0AzsD1SeZtzklknPGtNwliiDYIkIb0GNMpREEs/JRNwX/3MymDZGgasxFGmylgCwQVxNaDwMiEJ
FGQQgRQ6vQ3eJA4YiYaQs5wxwT4BwDwIMCLEY4DuJtIEJJOlIgXQSj/Fz1x+JgP7N/QbKC6Twnkl
uI+hQgolCIa4O1GCNs1JkJ9jR1sfkxSgUBLv9FhzPdxHIUe06ARucKP0PGpEUXbzQlvASXYySiig
oaUpIxgV4ysEUQYTAAGzKslBgokGWV/nVsuDHs6vjMANn65d/t/Mx2Vc/Mx/TVqx10Z6hzvUdxJ5
QnOFE5Sg4+JvIhGUTlkdP5JPXJ/L/hs+13r7Sf56pUSbPiqSb6ZLqrLpvw0XduWz8Nk0WcLN2HDR
hdVRZ11oy3XaNmrVeDeJKssvHDGObYyl5ut8x3eeN/cS36xa1d9rMrOl2GXLVJVhqs88s3bNnLl6
uasOHCyyzdZq6VculGmO0nKVNHjdu2XWaukXZdLuVnTplyk4WYdMLGWibzya7Z2ywq3eMJJsLPgf
6d94eI1dPG6+N3D1o1UZcPPNmjZ4u3crNWyT86JrOH0oo5Ku03bV9LMv9OEP9rd8+dsOibds6Zar
vwyswkuqw+kmUnLx43TfSqjV9JJt2Giiq74w/4IRaIIa7++4UrllujhNuo3XXcNtu2hlNK7Cjpy3
UVcLPXDV644q3aNWzYqoksk7jV0qoprr27K5ck1Xpyuk7csspImSZTbLm23aqTddwmys1cPCST9U
Q4khVOrfexlLRNOGThyk5SNGzt4qdOVGzCjxqsy2TJKu3rR0ysk0aNknLhVx55PVJh04ZdJqPzyj
50k5ZT7YdKNGyzKrZhR+6IybPGTeI5eeYPjdo5bvU1VCqb46bJvHLLCTZqVVYiI2VbCjLVc0aMsN
GzZl6uk2dHDc666dvU2rdJ4qyakSApUrWpAyFiRM0MlKMfw8jr5VQGuZNi52k4UeKvUPpf4lN0km
+lVHCb336fTZw2farRV0uqanjpJ4o1VUfpBG7KyyhkQ3GBrOAN59vgfKiqPbZsvuiEgapxBNOY3u
OUq8n5ev4+JvH2+LssrPwmk6duU2VWyzLZZVdNqw0UaNHCSzRRJhZh0/r/F7x5QUuPkYNDJkydzx
8XFjYU5IQwZCZQm2cOVnrhJNNum+F47f52WbKtlHiP90/LD9iT9/CgR/oLD7mfyazCoaSurak4yx
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
Received on Wed Jul 21 2010 - 12:54:43 MDT

This archive was generated by hypermail 2.2.0 : Wed Jul 21 2010 - 12:00:09 MDT