CommCommonCbParams::print(os);
if (conn != NULL)
os << ", from my " << conn->local << " to " << conn->remote;
- else if (paths && paths->size() > 0) {
- // TODO: for each path. print the to => from path being attempted.
- }
}
/* CommIoCbParams */
void
CommConnectCbPtrFun::dial()
{
- handler(params.conn, params.paths, params.flag, params.xerrno, params.data);
+ handler(params.conn, params.flag, params.xerrno, params.data);
}
void
* - 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::PathsPointer paths, comm_err_t status, int xerrno, void *data);
+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);
/*
public:
Comm::ConnectionPointer conn;
- Comm::PathsPointer paths;
};
// read/write (I/O) parameters
class HttpRequest;
class StoreEntry;
-typedef void PSC(Comm::PathsPointer, void *);
+typedef void PSC(Comm::Paths *, void *);
-SQUIDCEXTERN void peerSelect(Comm::PathsPointer, HttpRequest *, StoreEntry *, PSC *, void *data);
+SQUIDCEXTERN void peerSelect(Comm::Paths *, HttpRequest *, StoreEntry *, PSC *, void *data);
SQUIDCEXTERN void peerSelectInit(void);
/**
PSC *callback;
void *callback_data;
- Comm::PathsPointer paths; ///< the callers paths array. to be filled with our final results.
- FwdServer *servers; ///< temporary linked list of peers we will pass back.
+ 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
CBDATA_CLASS(ps_state);
};
-
#endif /* SQUID_PEERSELECTSTATE_H */
void Adaptation::Icap::ModXact::startReading()
{
- Must(connection != NULL && connection->isOpen());
+ Must(haveConnection());
Must(!reader);
Must(!adapted.header);
Must(!adapted.body_pipe);
stopParsing();
stopWriting(true); // or should we force it?
- if (connection != NULL && connection->isOpen()) {
+ if (haveConnection()) {
reuseConnection = false; // be conservative
cancelRead(); // may not work; and we cannot stop connecting either
if (!doneWithIo())
if (virgin.body_pipe != NULL)
buf.append("R", 1);
- if (connection != NULL && connection->isOpen() && !doneReading())
+ if (haveConnection() && !doneReading())
buf.append("r", 1);
if (!state.doneWriting() && state.writing != State::writingInit)
#include "squid.h"
#include "comm.h"
#include "comm/Connection.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "CommCalls.h"
#include "HttpMsg.h"
#include "adaptation/icap/Xaction.h"
{
Ip::Address client_addr;
- Must(connection == NULL || !connection->isOpen());
+ Must(!haveConnection());
const Adaptation::Service &s = service();
/* NP: set these here because it applies whether a pconn or a new conn is used */
- // TODO: where do we get the DNS info for the ICAP server host ??
- // Ip::Address will do a BLOCKING lookup if s.cfg().host is a hostname
+ // TODO: Avoid blocking lookup if s.cfg().host is a hostname
connection->remote = s.cfg().host.termedBuf();
connection->remote.SetPort(s.cfg().port);
connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected",
ConnectDialer(this, &Adaptation::Icap::Xaction::noteCommConnected));
- ConnectStateData *cs = new ConnectStateData(connection, connector);
- cs->host = xstrdup(s.cfg().host.termedBuf());
+ ConnOpener *cs = new ConnOpener(connection, connector);
+ cs->setHost(s.cfg().host.termedBuf());
cs->connect_timeout = TheConfig.connect_timeout(service().cfg().bypass);
- cs->connect();
+ cs->start();
}
/*
void Adaptation::Icap::Xaction::closeConnection()
{
- if (connection != NULL && connection->isOpen()) {
+ if (haveConnection()) {
if (closer != NULL) {
comm_remove_close_handler(connection->fd, closer);
fd_table[io.conn->fd].noteUse(icapPconnPool);
- connection = io.conn;
handleCommConnected();
}
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",
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?
void Adaptation::Icap::Xaction::scheduleRead()
{
- Must(connection->isOpen());
+ Must(haveConnection());
Must(!reader);
Must(readBuf.hasSpace());
void Adaptation::Icap::Xaction::cancelRead()
{
if (reader != NULL) {
+ Must(haveConnection());
comm_read_cancel(connection->fd, reader);
reader = NULL;
}
bool Adaptation::Icap::Xaction::doneWithIo() const
{
- return connection != NULL && connection->isOpen() && // 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()
{
void Adaptation::Icap::Xaction::fillPendingStatus(MemBuf &buf) const
{
- if (connection->isOpen()) {
+ if (haveConnection()) {
buf.Printf("FD %d", connection->fd);
if (writer != NULL)
void Adaptation::Icap::Xaction::fillDoneStatus(MemBuf &buf) const
{
- if (connection->isOpen() && commEof)
+ if (haveConnection() && commEof)
buf.Printf("Comm(%d)", connection->fd);
if (stopReason != NULL)
void openConnection();
void closeConnection();
void dieOnConnectionFailure();
+ bool haveConnection() const;
void scheduleRead();
void scheduleWrite(MemBuf &buf);
void maybeLog();
protected:
- Comm::ConnectionPointer connection; // Handle to the ICAP server connection
+ Comm::ConnectionPointer connection; ///< ICAP server connection
Adaptation::Icap::ServiceRep::Pointer theService;
/*
time_t AuthUser::last_discard = 0;
-char *CredentialsState_str[] = { "Unchecked", "Ok", "Pending", "Handshake", "Failed" };
+const char *CredentialsState_str[] = { "Unchecked", "Ok", "Pending", "Handshake", "Failed" };
AuthUser::AuthUser(AuthConfig *aConfig) :
dlink_list ip_list;
};
-extern char *CredentialsState_str[];
+extern const char *CredentialsState_str[];
#if _USE_INLINE_
#include "auth/User.cci"
LOC: Config.connect_retries
DEFAULT: 0
DOC_START
- This sets the maximum number of connection attempts for each
- potential host address selected by forwarding.
+ 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.
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.
-
- The connect_retries * forward_max_tries attempts must all still
- complete within the connection timeout period.
DOC_END
NAME: retry_on_error
/** Handle a new connection on HTTP socket. */
void
-httpAccept(int sock, int newfd, Comm::ConnectionPointer 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;
--- /dev/null
+#include "config.h"
+#include "comm/ConnOpener.h"
+#include "comm/Connection.h"
+#include "comm.h"
+#include "CommCalls.h"
+#include "fde.h"
+#include "icmp/net_db.h"
+#include "SquidTime.h"
+
+CBDATA_CLASS_INIT(ConnOpener);
+
+ConnOpener::ConnOpener(Comm::ConnectionPointer &c, AsyncCall::Pointer handler) :
+ AsyncJob("ConnOpener"),
+ connect_timeout(Config.Timeout.connect),
+ host(NULL),
+ solo(c),
+ callback(handler),
+ total_tries(0),
+ fail_retries(0),
+ connstart(0)
+{}
+
+ConnOpener::~ConnOpener()
+{
+ safe_free(host);
+ solo = NULL;
+}
+
+void
+ConnOpener::setHost(const char * new_host)
+{
+ if (host == NULL) {
+ safe_free(host); // unset and erase if already set.
+ } else {
+ host = xstrdup(new_host);
+ }
+}
+
+const char *
+ConnOpener::getHost() const
+{
+ return host;
+}
+
+void
+ConnOpener::callCallback(comm_err_t status, int xerrno)
+{
+ /* remove handlers we don't want to happen now */
+ comm_remove_close_handler(solo->fd, ConnOpener::EarlyAbort, this);
+ commSetTimeout(solo->fd, -1, NULL, NULL);
+
+ typedef CommConnectCbParams Params;
+ Params ¶ms = GetCommParams<Params>(callback);
+ params.conn = solo;
+ params.flag = status;
+ params.xerrno = xerrno;
+ ScheduleCallHere(callback);
+
+ callback = NULL;
+ delete this;
+}
+
+void
+ConnOpener::start()
+{
+ /* handle connecting to one single path */
+ if (solo->fd < 0) {
+#if USE_IPV6
+ /* outbound sockets have no need to be protocol agnostic. */
+ if (solo->local.IsIPv6() && solo->local.IsIPv4()) {
+ solo->local.SetIPv4();
+ }
+#endif
+ solo->fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, solo->local, solo->flags, solo->tos, host);
+ if (solo->fd <= 0) {
+ callCallback(COMM_ERR_CONNECT, 0);
+ return;
+ }
+
+ AsyncCall::Pointer ea_call = commCbCall(5,4, "ConnOpener::EarlyAbort",
+ CommCloseCbPtrFun(ConnOpener::EarlyAbort, this));
+ comm_add_close_handler(solo->fd, ea_call);
+
+ AsyncCall::Pointer timeout_call = commCbCall(5,4, "ConnOpener::ConnectTimeout",
+ CommTimeoutCbPtrFun(ConnOpener::ConnectTimeout, this));
+ debugs(5, 3, HERE << "FD " << solo->fd << " timeout " << connect_timeout);
+ commSetTimeout(solo->fd, connect_timeout, timeout_call);
+
+ if (connstart == 0) {
+ connstart = squid_curtime;
+ }
+ }
+
+ total_tries++;
+
+ switch (comm_connect_addr(solo->fd, solo->remote) ) {
+
+ case COMM_INPROGRESS:
+ debugs(5, 5, HERE << "FD " << solo->fd << ": COMM_INPROGRESS");
+ commSetSelect(solo->fd, COMM_SELECT_WRITE, ConnOpener::ConnectRetry, this, 0);
+ break;
+
+ case COMM_OK:
+ debugs(5, 5, HERE << "FD " << solo->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 (solo->getPeer())
+ solo->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.
+ */
+ fd_table[solo->fd].flags.open = 1;
+ solo->local.SetPort(comm_local_port(solo->fd));
+
+ ipcacheMarkGoodAddr(host, solo->remote);
+ callCallback(COMM_OK, 0);
+ break;
+
+ default:
+ debugs(5, 5, HERE "FD " << solo->fd << ": * - try again");
+ fail_retries++;
+ ipcacheMarkBadAddr(host, solo->remote);
+#if USE_ICMP
+ if (Config.onoff.test_reachability)
+ netdbDeleteAddrNetwork(solo->remote);
+#endif
+
+ // check for timeout FIRST.
+ if(squid_curtime - connstart > connect_timeout) {
+ debugs(5, 5, HERE << "FD " << solo->fd << ": * - ERR took too long already.");
+ callCallback(COMM_TIMEOUT, errno);
+ } else if (fail_retries < Config.connect_retries) {
+ start();
+ } else {
+ // send ERROR back to the upper layer.
+ debugs(5, 5, HERE << "FD " << solo->fd << ": * - ERR tried too many times already.");
+ callCallback(COMM_ERR_CONNECT, errno);
+ }
+ }
+}
+
+void
+ConnOpener::EarlyAbort(int fd, void *data)
+{
+ ConnOpener *cs = static_cast<ConnOpener *>(data);
+ debugs(5, 3, HERE << "FD " << fd);
+ cs->callCallback(COMM_ERR_CLOSING, errno); // NP: is closing or shutdown better?
+
+ /* TODO split cases:
+ * remote end rejecting the connection is normal and one of the other paths may be taken.
+ * squid shutting down or forcing abort on the connection attempt(s) are the only real fatal cases.
+ * we may need separate error codes to send back for these two.
+ */
+}
+
+void
+ConnOpener::Connect(void *data)
+{
+ ConnOpener *cs = static_cast<ConnOpener *>(data);
+ cs->start();
+}
+
+void
+ConnOpener::ConnectRetry(int fd, void *data)
+{
+ ConnOpener *cs = static_cast<ConnOpener *>(data);
+ cs->start();
+}
+
+void
+ConnOpener::ConnectTimeout(int fd, void *data)
+{
+ ConnOpener *cs = static_cast<ConnOpener *>(data);
+ cs->start();
+}
+
-#ifndef _SQUID_SRC_COMM_CONNECTSTATEDATA_H
-#define _SQUID_SRC_COMM_CONNECTSTATEDATA_H
+#ifndef _SQUID_SRC_COMM_OPENERSTATEDATA_H
+#define _SQUID_SRC_COMM_OPENERSTATEDATA_H
#include "base/AsyncCall.h"
+#include "base/AsyncJob.h"
#include "cbdata.h"
#include "comm/comm_err_t.h"
#include "comm/forward.h"
/**
- * State engine handling the opening of a remote outbound connection
- * to one of multiple destinations.
+ * Async-opener of a Comm connection.
*/
-class ConnectStateData
+class ConnOpener : public AsyncJob
{
public:
- /** open first working of a set of connections */
- ConnectStateData(Comm::PathsPointer paths, AsyncCall::Pointer handler);
+ /** attempt to open a connection. */
+ ConnOpener(Comm::ConnectionPointer &, AsyncCall::Pointer handler);
- /** attempt to open one connection. */
- ConnectStateData(Comm::ConnectionPointer, AsyncCall::Pointer handler);
+ ~ConnOpener();
- ~ConnectStateData();
-
- /**
- * Actual connect start function.
- */
- void connect();
+ /** Actual start opening a TCP connection. */
+ void start();
private:
/* These objects may NOT be created without connections to act on. Do not define this operator. */
- ConnectStateData();
+ ConnOpener(const ConnOpener &);
/* These objects may NOT be copied. Do not define this operator. */
- const ConnectStateData operator =(const ConnectStateData &c);
+ ConnOpener operator =(const ConnOpener &c);
/**
* Wrapper to start the connection attempts happening.
*/
static void EarlyAbort(int fd, void *data);
+ /**
+ * Temporary timeout handler used during connect.
+ * Handles the case(s) when a partially setup connection gets timed out.
+ */
+ static void ConnectTimeout(int fd, void *data);
+
/**
* Connection attempt are completed. One way or the other.
* Pass the results back to the external handler.
void callCallback(comm_err_t status, int xerrno);
public:
- char *host; ///< domain name we are trying to connect to.
-
/**
* time at which to abandon the connection.
* the connection-done callback will be passed COMM_TIMEOUT
*/
time_t connect_timeout;
+ void setHost(const char *); ///< set the hostname note for this connection
+ const char * getHost(void) const; ///< get the hostname noted for this connection
+
private:
- Comm::PathsPointer paths; ///< forwarding paths to be tried. front of the list is the current being opened.
+ char *host; ///< domain name we are trying to connect to.
+
Comm::ConnectionPointer solo; ///< single connection currently being opened.
AsyncCall::Pointer callback; ///< handler to be called on connection completion.
int fail_retries; ///< number of retries current destination has been tried.
time_t connstart; ///< time at which this series of connection attempts was started.
- CBDATA_CLASS2(ConnectStateData);
+ CBDATA_CLASS2(ConnOpener);
};
-#endif /* _SQUID_SRC_COMM_CONNECTSTATEDATA_H */
+#endif /* _SQUID_SRC_COMM_CONNOPENER_H */
+++ /dev/null
-#include "config.h"
-#include "comm/ConnectStateData.h"
-#include "comm/Connection.h"
-#include "comm.h"
-#include "CommCalls.h"
-#include "fde.h"
-#include "icmp/net_db.h"
-#include "SquidTime.h"
-
-CBDATA_CLASS_INIT(ConnectStateData);
-
-ConnectStateData::ConnectStateData(Comm::PathsPointer paths, AsyncCall::Pointer handler) :
- host(NULL),
- connect_timeout(Config.Timeout.connect),
- paths(paths),
- solo(NULL),
- callback(handler),
- total_tries(0),
- fail_retries(0),
- connstart(0)
-{}
-
-ConnectStateData::ConnectStateData(Comm::ConnectionPointer c, AsyncCall::Pointer handler) :
- host(NULL),
- connect_timeout(Config.Timeout.connect),
- paths(NULL),
- solo(c),
- callback(handler),
- total_tries(0),
- fail_retries(0),
- connstart(0)
-{}
-
-ConnectStateData::~ConnectStateData()
-{
- safe_free(host);
- paths = NULL; // caller code owns them.
- solo = NULL;
-}
-
-void
-ConnectStateData::callCallback(comm_err_t status, int xerrno)
-{
- int fd = -1;
- if (paths != NULL && paths->size() > 0) {
- fd = (*paths)[0]->fd;
- debugs(5, 3, HERE << "FD " << fd);
- comm_remove_close_handler(fd, ConnectStateData::EarlyAbort, this);
- commSetTimeout(fd, -1, NULL, NULL);
- }
-
- typedef CommConnectCbParams Params;
- Params ¶ms = GetCommParams<Params>(callback);
- if (solo != NULL) {
- params.conn = solo;
- } else if (paths != NULL) {
- params.paths = paths;
- if (paths->size() > 0)
- params.conn = (*paths)[0];
- } else {
- /* catch the error case. */
- assert(paths != NULL && solo != NULL);
- }
- params.flag = status;
- params.xerrno = xerrno;
- ScheduleCallHere(callback);
-
- callback = NULL;
- safe_free(host);
- delete this;
-}
-
-void
-ConnectStateData::connect()
-{
- Comm::ConnectionPointer active;
-
- /* handle connecting to one single path */
- /* mainly used by components other than forwarding */
-
- /* handle connecting to one of multiple paths */
- /* mainly used by forwarding */
-
- if (solo != NULL) {
- active = solo;
- } else if (paths) {
- Comm::Paths::iterator i = paths->begin();
-
- if (connstart == 0) {
- connstart = squid_curtime;
- }
-
- /* find some socket we can use. will also bind the local address to it if needed. */
- while(paths->size() > 0 && (*i)->fd <= 0) {
-#if USE_IPV6
- /* outbound sockets have no need to be protocol agnostic. */
- if ((*i)->local.IsIPv6() && (*i)->local.IsIPv4()) {
- (*i)->local.SetIPv4();
- }
-#endif
- (*i)->fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, (*i)->local, (*i)->flags, (*i)->tos, host);
- if ((*i)->fd <= 0) {
- debugs(5 , 2, HERE << "Unable to connect " << (*i)->local << " -> " << (*i)->remote << " for " << host);
- paths->shift();
- i = paths->begin();
- }
- // else success will terminate the loop with: i->fd >0
- }
-
- /* we have nowhere left to try connecting */
- if (paths->size() < 1) {
- callCallback(COMM_ERR_CONNECT, 0);
- return;
- }
-
- active = (*i);
- }
-
- total_tries++;
-
- switch (comm_connect_addr(active->fd, active->remote) ) {
-
- case COMM_INPROGRESS:
- debugs(5, 5, HERE << "FD " << active->fd << ": COMM_INPROGRESS");
- commSetSelect(active->fd, COMM_SELECT_WRITE, ConnectStateData::ConnectRetry, this, 0);
- break;
-
- case COMM_OK:
- debugs(5, 5, HERE << "FD " << active->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 (active->getPeer())
- active->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.
- */
- fd_table[active->fd].flags.open = 1;
- active->local.SetPort(comm_local_port(active->fd));
-
- ipcacheMarkGoodAddr(host, active->remote);
- callCallback(COMM_OK, 0);
- break;
-
- default:
- debugs(5, 5, HERE "FD " << active->fd << ": * - try again");
- fail_retries++;
- ipcacheMarkBadAddr(host, active->remote);
-
-#if USE_ICMP
- if (Config.onoff.test_reachability)
- netdbDeleteAddrNetwork(active->remote);
-#endif
-
- // check for timeout FIRST.
- if(squid_curtime - connstart > connect_timeout) {
- debugs(5, 5, HERE << "FD " << active->fd << ": * - ERR took too long already.");
- callCallback(COMM_TIMEOUT, errno);
- } else if (fail_retries < Config.connect_retries) {
- // check if connect_retries extends the single IP re-try limit.
- eventAdd("ConnectStateData::Connect", ConnectStateData::Connect, this, 0.5, 0);
- } else if (paths && paths->size() > 0) {
- // check if we have more maybe-useful paths to try.
- paths->shift();
- fail_retries = 0;
- eventAdd("ConnectStateData::Connect", ConnectStateData::Connect, this, 0.0, 0);
- } else {
- // send ERROR back to the upper layer.
- debugs(5, 5, HERE << "FD " << active->fd << ": * - ERR tried too many times already.");
- callCallback(COMM_ERR_CONNECT, errno);
- }
- }
-}
-
-void
-ConnectStateData::EarlyAbort(int fd, void *data)
-{
- ConnectStateData *cs = static_cast<ConnectStateData *>(data);
- debugs(5, 3, HERE << "FD " << fd);
- cs->callCallback(COMM_ERR_CLOSING, errno); // NP: is closing or shutdown better?
-
- /* TODO split cases:
- * remote end rejecting the connection is normal and one of the other paths may be taken.
- * squid shutting down or forcing abort on the connection attempt(s) are the only real fatal cases.
- */
-}
-
-void
-ConnectStateData::Connect(void *data)
-{
- ConnectStateData *cs = static_cast<ConnectStateData *>(data);
- cs->connect();
-}
-
-void
-ConnectStateData::ConnectRetry(int fd, void *data)
-{
- ConnectStateData *cs = static_cast<ConnectStateData *>(data);
- cs->connect();
-}
_peer(NULL)
{}
-Comm::Connection::Connection(const Comm::Connection &c) :
- local(c.local),
- remote(c.remote),
- peer_type(c.peer_type),
- fd(c.fd),
- tos(c.tos),
- flags(c.flags)
+Comm::Connection::~Connection()
{
- _peer = cbdataReference(c._peer);
+ close();
+ if (_peer) {
+ cbdataReferenceDone(_peer);
+ }
}
-const Comm::Connection &
-Comm::Connection::operator =(const Comm::Connection &c)
+Comm::ConnectionPointer &
+Comm::Connection::copyDetails() const
{
- memcpy(this, &c, sizeof(Comm::Connection));
+ ConnectionPointer c = new Comm::Connection;
- /* ensure we have a cbdata reference to _peer not a straight ptr copy. */
- _peer = cbdataReference(c._peer);
+ c->local = local;
+ c->remote = remote;
+ c->peer_type = peer_type;
+ c->tos = tos;
+ c->flags = flags;
+
+ // ensure FD is not open in the new copy.
+ c->fd = -1;
- return *this;
-}
+ // ensure we have a cbdata reference to _peer not a straight ptr copy.
+ c->_peer = cbdataReference(_peer);
-Comm::Connection::~Connection()
-{
- close();
- if (_peer) {
- cbdataReferenceDone(_peer);
- }
+ return c;
}
void
/** standard empty connection creation */
Connection();
- /** Clear the conection properties and close any open socket. */
+ /** These objects may not be exactly duplicated. Use copyDetails() instead. */
+ Connection(const Connection &c);
+
+ /** Clear the connection properties and close any open socket. */
~Connection();
- /** Clone an existing connections properties.
- * This includes the FD, if one is open its a good idea to set it to -1 (unopen)
- * on one after copying to prevent both clones calling comm_close() when destructed.
+ /** Copy an existing connections IP and properties.
+ * This excludes the FD. The new copy will be a closed connection.
*/
- Connection(const Connection &c);
- /** see Comm::Connection::Connection */
- const Connection & operator =(const Connection &c);
+ ConnectionPointer & copyDetails() const;
+
+ /** These objects may not be exactly duplicated. Use clone() instead. */
+ Connection & operator =(const Connection &c);
+ /** Close any open socket. */
void close();
/** determine whether this object describes an active connection or not. */
ListenStateData.cc \
ListenStateData.h \
\
- ConnectStateData.cc \
- ConnectStateData.h \
+ ConnOpener.cc \
+ ConnOpener.h \
\
Connection.cc \
Connection.h \
typedef RefCount<Comm::Connection> ConnectionPointer;
typedef Vector<Comm::ConnectionPointer> Paths;
-typedef Vector<Comm::ConnectionPointer>* PathsPointer;
}; // namespace Comm
#include "squid.h"
#include "CacheManager.h"
#include "comm/Connection.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "comm.h"
#include "event.h"
#include "fde.h"
}
static void
-idnsInitVCConnected(Comm::ConnectionPointer conn, Comm::PathsPointer unused, comm_err_t status, int xerrno, void *data)
+idnsInitVCConnected(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
{
nsvc * vc = (nsvc *)data;
AsyncCall::Pointer call = commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected, vc));
- ConnectStateData *cs = new ConnectStateData(conn, call);
- cs->host = xstrdup("DNS TCP Socket");
- cs->connect();
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->setHost("DNS TCP Socket");
+ cs->start();
}
static void
#include "acl/Gadgets.h"
#include "CacheManager.h"
#include "comm/Connection.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "CommCalls.h"
#include "event.h"
#include "errorpage.h"
*/
AsyncCall::Pointer call = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
- ConnectStateData *cs = new ConnectStateData(&paths, call);
- cs->host = xstrdup(entry->url());
+ ConnOpener *cs = new ConnOpener(paths[0], call);
+ cs->setHost(entry->url());
cs->connect_timeout = Config.Timeout.connect;
- cs->connect();
+ cs->start();
} else {
debugs(17, 3, HERE << "server FD " << paths[0]->fd << " not re-forwarding status " << entry->getReply()->sline.status);
EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
/**** CALLBACK WRAPPERS ************************************************************/
static void
-fwdStartCompleteWrapper(Comm::PathsPointer unused, void *data)
+fwdStartCompleteWrapper(Comm::Paths * unused, void *data)
{
FwdState *fwd = (FwdState *) data;
fwd->startComplete();
#endif
void
-fwdConnectDoneWrapper(Comm::ConnectionPointer conn, Comm::PathsPointer paths, comm_err_t status, int xerrno, void *data)
+fwdConnectDoneWrapper(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
{
FwdState *fwd = (FwdState *) data;
- fwd->connectDone(conn, paths, status, xerrno);
+ fwd->connectDone(conn, status, xerrno);
}
/**** PRIVATE *****************************************************************/
* A new one will be created if there's another problem */
err = NULL;
- AsyncCall::Pointer call = commCbCall(17,3,"fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
- ConnectStateData *cs = new ConnectStateData(&paths, call);
- cs->host = xstrdup(entry->url());
- cs->connect_timeout = Config.Timeout.connect;
- cs->connect();
-
- /* use eventAdd to break potential call sequence loops and to slow things down a little */
- eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, (paths[0]->getPeer() == NULL) ? 0.05 : 0.005, 0);
+ connectStart();
return;
}
// else bail. no more paths possible to try.
#endif
void
-FwdState::connectDone(Comm::ConnectionPointer conn, Comm::PathsPointer result_paths, comm_err_t status, int xerrno)
+FwdState::connectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno)
{
- assert(result_paths == &paths);
-
if (status != COMM_OK) {
ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
anErr->xerrno = xerrno;
-
fail(anErr);
/* it might have been a timeout with a partially open link */
paths[0]->close();
}
-
+ retryOrBail();
return;
}
if (paths[0]->getPeer())
peerConnectSucceded(paths[0]->getPeer());
- // TODO: Avoid this if %<lp is not used? F->local_port is often cached.
- request->hier.peer_local_port = comm_local_port(paths[0]->fd);
-
updateHierarchyInfo();
#if USE_SSL
if (pinned_connection->pinnedAuth())
request->flags.auth = 1;
updateHierarchyInfo();
- FwdState::connectDone(conn, &paths, COMM_OK, 0);
+ FwdState::connectDone(conn, COMM_OK, 0);
return;
}
/* Failure. Fall back on next path */
comm_add_close_handler(conn->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(conn->fd);
-
dispatch();
return;
}
#endif
AsyncCall::Pointer call = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
- ConnectStateData *cs = new ConnectStateData(&paths, call);
- cs->host = xstrdup(host);
+ ConnOpener *cs = new ConnOpener(paths[0], call);
+ cs->setHost(host);
cs->connect_timeout = ctimeout;
- cs->connect();
+ cs->start();
}
void
paths[0]->remote.NtoA(nextHop, 256);
}
+ request->hier.peer_local_port = paths[0]->local.GetPort();
+
assert(nextHop[0]);
hierarchyNote(&request->hier, paths[0]->peer_type, nextHop);
}
bool reforwardableStatus(http_status s);
void serverClosed(int fd);
void connectStart();
- void connectDone(Comm::ConnectionPointer conn, Comm::PathsPointer paths, 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);
#include "squid.h"
#include "comm.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "comm/ListenStateData.h"
#include "compat/strtoll.h"
#include "errorpage.h"
conn->fd = fd;
AsyncCall::Pointer call = commCbCall(9,3, "FtpStateData::ftpPasvCallback", CommConnectCbPtrFun(FtpStateData::ftpPasvCallback, ftpState));
- ConnectStateData *cs = new ConnectStateData(conn, call);
- cs->host = xstrdup(fd_table[ftpState->ctrl.fd].ipaddr);
- cs->connect();
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->setHost(ftpState->data.host);
+ cs->start();
}
/** \ingroup ServerProtocolFTPInternal
conn->fd = ftpState->data.fd;
AsyncCall::Pointer call = commCbCall(9,3, "FtpStateData::ftpPasvCallback", CommConnectCbPtrFun(FtpStateData::ftpPasvCallback, ftpState));
- ConnectStateData *cs = new ConnectStateData(conn, call);
- cs->host = xstrdup(ftpState->data.host);
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->setHost(ftpState->data.host);
cs->connect_timeout = Config.Timeout.connect;
- cs->connect();
+ cs->start();
}
void
-FtpStateData::ftpPasvCallback(Comm::ConnectionPointer conn, Comm::PathsPointer unused, 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);
-// TODO: dead? ftpState->request->recordLookup(dns);
if (status != COMM_OK) {
debugs(9, 2, HERE << "Failed to connect. Retrying without PASV.");
Comm::Connection cc; // IDENT will clone it's own copy for alterations.
cc.local = checklist->conn()->me;
cc.remote = checklist->conn()->peer;
- Ident::Start(&cc, LookupDone, checklist);
+ Comm::ConnectionPointer ccp = &cc;
+ Ident::Start(ccp, LookupDone, checklist);
} else {
debugs(28, DBG_IMPORTANT, "IdentLookup::checkForAsync: Can't start ident lookup. No client connection" );
checklist->currentAnswer(ACCESS_DENIED);
#include "comm.h"
#include "comm/Connection.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "CommCalls.h"
#include "ident/Config.h"
#include "ident/Ident.h"
typedef struct _IdentStateData {
hash_link hash; /* must be first */
- Comm::Connection conn;
+ Comm::ConnectionPointer conn;
IdentClient *clients;
char buf[4096];
} IdentStateData;
{
IdentStateData *state = (IdentStateData *)data;
identCallback(state, NULL);
- state->conn.close();
+ state->conn->close();
hash_remove_link(ident_hash, (hash_link *) state);
xfree(state->hash.key);
cbdataFree(state);
Ident::Timeout(int fd, void *data)
{
IdentStateData *state = (IdentStateData *)data;
- debugs(30, 3, HERE << "FD " << fd << ", " << state->conn.remote);
- state->conn.close();
+ debugs(30, 3, HERE << "FD " << fd << ", " << state->conn->remote);
+ state->conn->close();
}
void
-Ident::ConnectDone(Comm::ConnectionPointer conn, Comm::PathsPointer unused, 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;
if (status != COMM_OK) {
if (status == COMM_TIMEOUT) {
- debugs(30, 3, "IDENT connection timeout to " << state->conn.remote);
+ debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
}
return;
}
- assert(conn != NULL && conn == &(state->conn));
+ assert(conn != NULL && conn == state->conn);
/*
* see if any of our clients still care
char *t = NULL;
assert(buf == state->buf);
- assert(fd == state->conn.fd);
+ assert(fd == state->conn->fd);
if (flag != COMM_OK || len <= 0) {
- state->conn.close();
+ state->conn->close();
return;
}
}
}
- state->conn.close();
+ state->conn->close();
}
void
* start a TCP connection to the peer host on port 113
*/
void
-Ident::Start(Comm::ConnectionPointer conn, IDCB * callback, void *data)
+Ident::Start(Comm::ConnectionPointer &conn, IDCB * callback, void *data)
{
IdentStateData *state;
char key1[IDENT_KEY_SZ];
CBDATA_INIT_TYPE(IdentStateData);
state = cbdataAlloc(IdentStateData);
state->hash.key = xstrdup(key);
- /* clone the conn. we are about to destroy the conn
- * for re-use of the addresses etc by IDENT. */
- state->conn = *conn;
- state->conn.local.SetPort(0); // NP: use random port for secure outbound to IDENT_PORT
- state->conn.flags |= COMM_NONBLOCKING;
+
+ // 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);
AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
- ConnectStateData *cs = new ConnectStateData(&(state->conn), call);
+ ConnOpener *cs = new ConnOpener(state->conn, call);
cs->connect_timeout = Ident::TheConfig.timeout;
- cs->connect();
+ cs->start();
}
void
* Self-registers with a global ident lookup manager,
* will call Ident::Init() itself if the manager has not been initialized already.
*/
-void Start(Comm::ConnectionPointer conn, IDCB * callback, void *cbdata);
+void Start(Comm::ConnectionPointer &conn, IDCB * callback, void *cbdata);
/**
\ingroup IdentAPI
#include "ProtoPort.h"
#include "acl/FilledChecklist.h"
#include "comm/Connection.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "CacheManager.h"
#include "event.h"
#include "htcp.h"
time_t ctimeout = p->connect_timeout > 0 ? p->connect_timeout : Config.Timeout.peer_connect;
bool ret = (squid_curtime - p->stats.last_connect_failure) > (ctimeout * 10);
- if (p->testing_now)
+ 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 */
/* for each IP address of this peer. find one that we can connect to and probe it. */
- Comm::PathsPointer paths = new Comm::Paths;
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);
- paths->push_back(conn);
+
+ p->testing_now++;
+
+ AsyncCall::Pointer call = commCbCall(15,3, "peerProbeConnectDone", CommConnectCbPtrFun(peerProbeConnectDone, p));
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->connect_timeout = ctimeout;
+ cs->setHost(p->host);
+ cs->start();
}
- p->testing_now = true;
p->stats.last_connect_probe = squid_curtime;
- AsyncCall::Pointer call = commCbCall(15,3, "peerProbeConnectDone", CommConnectCbPtrFun(peerProbeConnectDone, p));
- ConnectStateData *cs = new ConnectStateData(paths, call);
- cs->connect_timeout = ctimeout;
- cs->host = xstrdup(p->host);
- cs->connect();
-
return ret;
}
static void
-peerProbeConnectDone(Comm::ConnectionPointer conn, Comm::PathsPointer unused, comm_err_t status, int xerrno, void *data)
+peerProbeConnectDone(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
{
peer *p = (peer*)data;
}
conn->close();
- p->testing_now = false;
+ p->testing_now--;
return;
}
void
-peerSelect(Comm::PathsPointer paths,
+peerSelect(Comm::Paths * paths,
HttpRequest * request,
StoreEntry * entry,
PSC * callback,
int n_addresses;
int rr_count;
peer *next;
- bool testing_now;
+ int testing_now;
struct {
unsigned int hash;
#include "Array.h"
#include "comm.h"
#include "comm/Connection.h"
-#include "comm/ConnectStateData.h"
+#include "comm/ConnOpener.h"
#include "client_side.h"
#include "client_side_request.h"
#if DELAY_POOLS
char *host; /* either request->host or proxy host */
u_short port;
HttpRequest *request;
- Comm::PathsPointer paths;
+ Comm::Paths paths;
class Connection
{
assert(tunnelState != NULL);
assert(tunnelState->noConnections());
safe_free(tunnelState->url);
- if (tunnelState->paths) tunnelState->paths->clean();
+ tunnelState->paths.clean();
tunnelState->host = NULL;
HTTPMSGUNLOCK(tunnelState->request);
delete tunnelState;
comm_read(from.fd(), from.buf, from.bytesWanted(1, SQUID_TCP_SO_RCVBUF), completion, this);
}
-#if UNUSED //?
-static void
-tunnelConnectTimeout(int fd, void *data)
-{
- TunnelStateData *tunnelState = (TunnelStateData *)data;
- HttpRequest *request = tunnelState->request;
- ErrorState *err = NULL;
-
- if (tunnelState->paths != NULL && tunnelState->paths->size() > 0) {
- if ((*(tunnelState->paths))[0]->getPeer())
- hierarchyNote(&tunnelState->request->hier, (*(tunnelState->paths))[0]->peer_type,
- (*(tunnelState->paths))[0]->getPeer()->host);
- else if (Config.onoff.log_ip_on_direct)
- hierarchyNote(&tunnelState->request->hier, (*(tunnelState->paths))[0]->peer_type,
- fd_table[tunnelState->server.fd()].ipaddr);
- else
- hierarchyNote(&tunnelState->request->hier, (*(tunnelState->paths))[0]->peer_type,
- tunnelState->host);
- } else
- debugs(26, DBG_IMPORTANT, "tunnelConnectTimeout(): no forwarding destinations available.");
-
- 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);
-}
-#endif
-
static void
tunnelConnectedWriteDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
{
static void
-tunnelConnectDone(Comm::ConnectionPointer unused, Comm::PathsPointer paths, 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;
- Comm::ConnectionPointer conn = (*paths)[0];
-
- assert(tunnelState->paths == paths);
#if DELAY_POOLS
/* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
hierarchyNote(&tunnelState->request->hier, conn->peer_type, tunnelState->host);
if (status != COMM_OK) {
- 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);
+ /* 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));
+ ConnOpener *cs = new ConnOpener(tunnelState->paths[0], call);
+ cs->setHost(tunnelState->url);
+ cs->connect_timeout = Config.Timeout.connect;
+ cs->start();
+ } 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;
}
tunnelTimeout,
tunnelState);
- peerSelect(tunnelState->paths, request,
+ peerSelect(&(tunnelState->paths), request,
NULL,
tunnelPeerSelectComplete,
tunnelState);
}
static void
-tunnelPeerSelectComplete(Comm::PathsPointer peer_paths, void *data)
+tunnelPeerSelectComplete(Comm::Paths *peer_paths, void *data)
{
TunnelStateData *tunnelState = (TunnelStateData *)data;
HttpRequest *request = tunnelState->request;
}
AsyncCall::Pointer call = commCbCall(26,3, "tunnelConnectDone", CommConnectCbPtrFun(tunnelConnectDone, tunnelState));
- ConnectStateData *cs = new ConnectStateData(tunnelState->paths, call);
- cs->host = xstrdup(tunnelState->url);
+ ConnOpener *cs = new ConnOpener(tunnelState->paths[0], call);
+ cs->setHost(tunnelState->url);
cs->connect_timeout = Config.Timeout.connect;
- cs->connect();
+ cs->start();
}
CBDATA_CLASS_INIT(TunnelStateData);