theBusyConns(0),
theAllWaiters(0),
connOverloadReported(false),
- theIdleConns("ICAP Service",NULL),
+ theIdleConns(NULL),
isSuspended(0), notifying(false),
updateScheduled(false),
wasAnnouncedUp(true), // do not announce an "up" service at startup
isDetached(false)
{
setMaxConnections();
+ theIdleConns = new IdleConnList("ICAP Service", NULL);
}
Adaptation::Icap::ServiceRep::~ServiceRep()
{
+ delete theIdleConns;
Must(!theOptionsFetcher);
delete theOptions;
}
* In other words, (2) tells us to close one FD for each new one we open due to retriable.
*/
if (retriableXact)
- connection = theIdleConns.pop();
+ connection = theIdleConns->pop();
else
- theIdleConns.closeN(1);
-
- if (!(reused = Comm::IsConnOpen(connection)))
- connection = new Comm::Connection;
- else {
- debugs(93,3, HERE << "reused pconn " << connection);
- ++theBusyConns;
- }
+ theIdleConns->closeN(1);
+ reused = Comm::IsConnOpen(connection);
+ ++theBusyConns;
+ debugs(93,3, HERE << "got connection: " << connection);
return connection;
}
if (isReusable && excessConnections() == 0) {
debugs(93, 3, HERE << "pushing pconn" << comment);
commUnsetConnTimeout(conn);
- theIdleConns.push(conn);
+ theIdleConns->push(conn);
} else {
debugs(93, 3, HERE << "closing pconn" << comment);
// comm_close will clear timeout
fd_table[conn->fd].noteUse(NULL); // pconn re-use but not via PconnPool API
}
+void Adaptation::Icap::ServiceRep::noteConnectionFailed(const char *comment)
+{
+ debugs(93, 3, HERE << "Connection failed: " << comment);
+ --theBusyConns;
+}
+
void Adaptation::Icap::ServiceRep::setMaxConnections()
{
if (cfg().maxConn >= 0)
if (!available && !connOverloadReported) {
debugs(93, DBG_IMPORTANT, "WARNING: ICAP Max-Connections limit " <<
"exceeded for service " << cfg().uri << ". Open connections now: " <<
- theBusyConns + theIdleConns.count() << ", including " <<
- theIdleConns.count() << " idle persistent connections.");
+ theBusyConns + theIdleConns->count() << ", including " <<
+ theIdleConns->count() << " idle persistent connections.");
connOverloadReported = true;
}
// Waiters affect the number of needed connections but a needed
// connection may still be excessive from Max-Connections p.o.v.
// so we should not account for waiting transaction needs here.
- const int debt = theBusyConns + theIdleConns.count() - theMaxConnections;
+ const int debt = theBusyConns + theIdleConns->count() - theMaxConnections;
if (debt > 0)
return debt;
else
debugs(93,8, "ICAPServiceRep::callWhenAvailable");
Must(cb!=NULL);
Must(up());
- Must(!theIdleConns.count()); // or we should not be waiting
+ Must(!theIdleConns->count()); // or we should not be waiting
Client i;
i.service = Pointer(this);
setMaxConnections();
const int excess = excessConnections();
// if we owe connections and have idle pconns, close the latter
- // XXX: but ... idle pconn to *where*?
- if (excess && theIdleConns.count() > 0) {
- const int n = min(excess, theIdleConns.count());
+ if (excess && theIdleConns->count() > 0) {
+ const int n = min(excess, theIdleConns->count());
debugs(93,5, HERE << "closing " << n << " pconns to relief debt");
- theIdleConns.closeN(n);
+ theIdleConns->closeN(n);
}
scheduleNotification();
#include "pconn.h"
#include "HttpRequest.h"
#include "HttpReply.h"
+#include "ipcache.h"
#include "acl/FilledChecklist.h"
#include "icap_log.h"
#include "fde.h"
Must(static_cast<size_t>(readBuf.potentialSpaceSize()) <= commBufSize);
}
+static void
+icapLookupDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &, void *data)
+{
+ Adaptation::Icap::Xaction *xa = static_cast<Adaptation::Icap::Xaction *>(data);
+ xa->dnsLookupDone(ia);
+}
+
// TODO: obey service-specific, OPTIONS-reported connection limit
void
Adaptation::Icap::Xaction::openConnection()
if (wasReused && Comm::IsConnOpen(connection)) {
// Set comm Close handler
- 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->fd, closer);
-
// fake the connect callback
// TODO: can we sync call Adaptation::Icap::Xaction::noteCommConnected here instead?
typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
// Attempt to open a new connection...
debugs(93,3, typeName << " opens connection to " << s.cfg().host.termedBuf() << ":" << s.cfg().port);
- // TODO: find the IPs and attempt each one if this is a named service.
- connection->remote = s.cfg().host.termedBuf();
- connection->remote.SetPort(s.cfg().port);
+ // Locate the Service IP(s) to open
+ ipcache_nbgethostbyname(s.cfg().host.termedBuf(), icapLookupDnsResults, this);
+}
- // 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));
+void
+Adaptation::Icap::Xaction::dnsLookupDone(const ipcache_addrs *ia)
+{
+ Adaptation::Icap::ServiceRep &s = service();
- commSetTimeout(connection->fd, TheConfig.connect_timeout(
- service().cfg().bypass), timeoutCall);
+ if (ia == NULL) {
+ debugs(44, DBG_IMPORTANT, "ICAP: Unknown service host: " << s.cfg().host);
- 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->fd, closer);
+#if WHEN_IPCACHE_NBGETHOSTBYNAME_USES_ASYNC_CALLS
+ dieOnConnectionFailure(); // throws
+#else // take a step back into protected Async call dialing.
+ // fake the connect callback
+ typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> Dialer;
+ CbcPointer<Xaction> self(this);
+ Dialer dialer(self, &Adaptation::Icap::Xaction::noteCommConnected);
+ dialer.params.conn = connection;
+ dialer.params.flag = COMM_ERROR;
+ // fake other parameters by copying from the existing connection
+ connector = asyncCall(93,3, "Adaptation::Icap::Xaction::noteCommConnected", dialer);
+ ScheduleCallHere(connector);
+#endif
+ return;
+ }
+ assert(ia->cur < ia->count);
+
+ connection = new Comm::Connection;
+ connection->remote = ia->in_addrs[ia->cur];
+ connection->remote.SetPort(s.cfg().port);
+ getOutgoingAddress(NULL, connection);
+
+ // TODO: service bypass status may differ from that of a transaction
typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> ConnectDialer;
connector = JobCallback(93,3, ConnectDialer, this, Adaptation::Icap::Xaction::noteCommConnected);
Comm::ConnOpener *cs = new Comm::ConnOpener(connection, connector, TheConfig.connect_timeout(service().cfg().bypass));
if (io.flag != COMM_OK)
dieOnConnectionFailure(); // throws
+ typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
+ AsyncCall::Pointer timeoutCall = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommTimedout",
+ TimeoutDialer(this,&Adaptation::Icap::Xaction::noteCommTimedout));
+ commSetTimeout(io.conn->fd, 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));
{
debugs(93, 2, HERE << typeName <<
" failed to connect to " << service().cfg().uri);
+ service().noteConnectionFailed("failure");
detailError(ERR_DETAIL_ICAP_XACT_START);
throw TexcHere("cannot connect to the ICAP service");
}
theService->cfg().uri << status());
reuseConnection = false;
const bool whileConnecting = connector != NULL;
- closeConnection(); // so that late Comm callbacks do not disturb bypass
+ if (whileConnecting) {
+ assert(!haveConnection());
+ theService->noteConnectionFailed("timedout");
+ } else
+ closeConnection(); // so that late Comm callbacks do not disturb bypass
throw TexcHere(whileConnecting ?
"timed out while connecting to the ICAP service" :
"timed out while talking to the ICAP service");