<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>
<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.
#include "squid.h"
#include "fde.h"
+#include "comm/Connection.h"
#include "CommCalls.h"
/* CommCommonCbParams */
CommCommonCbParams::print(os);
if (nfd >= 0)
os << ", newFD " << nfd;
+ os << ", " << details;
}
CommConnectCbParams::print(std::ostream &os) const
{
CommCommonCbParams::print(os);
- os << ", " << dns;
+ if (conn != NULL)
+ os << ", from my " << conn->local << " to " << conn->remote;
}
/* CommIoCbParams */
void
CommAcceptCbPtrFun::dial()
{
- handler(params.fd, params.nfd, ¶ms.details, params.flag, params.xerrno, params.data);
+ handler(params.fd, params.nfd, params.details, params.flag, params.xerrno, params.data);
}
void
void
CommConnectCbPtrFun::dial()
{
- handler(params.fd, params.dns, params.flag, params.xerrno, params.data);
+ handler(params.conn, params.flag, params.xerrno, params.data);
}
void
#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:
* - 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
void print(std::ostream &os) const;
public:
- ConnectionDetail details;
+ Comm::ConnectionPointer details;
int nfd; // TODO: rename to fdNew or somesuch
};
void print(std::ostream &os) const;
public:
- DnsLookupDetails dns;
+ Comm::ConnectionPointer conn;
};
// read/write (I/O) parameters
+++ /dev/null
-/*
- * DEBUG: section 05 Socket Functions
- * AUTHOR: Robert Collins
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- *
- * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
- */
-
-#ifndef _SQUIDCONNECTIONDETAIL_H_
-#define _SQUIDCONNECTIONDETAIL_H_
-
-#include "ip/Address.h"
-
-class ConnectionDetail
-{
-
-public:
-
- ConnectionDetail();
-
- Ip::Address me;
-
- Ip::Address peer;
-};
-
-#endif
*/
#include "squid.h"
-#include "HttpRequest.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 "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)
{
ConfigOption.cc \
ConfigParser.cc \
ConfigParser.h \
- ConnectionDetail.h \
debug.cc \
Debug.h \
defines.h \
squid_LDADD = \
$(COMMON_LIBS) \
- comm/libcomm-listener.la \
+ comm/libcomm.la \
eui/libeui.la \
icmp/libicmp.la icmp/libicmp-core.la \
log/liblog.la \
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) \
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} \
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} \
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} \
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} \
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) \
#ifndef SQUID_PEERSELECTSTATE_H
#define SQUID_PEERSELECTSTATE_H
+#include "Array.h"
#include "cbdata.h"
-#include "PingData.h"
+#include "comm/forward.h"
+#include "hier_code.h"
#include "ip/Address.h"
+#include "PingData.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
{
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
CBDATA_CLASS(ps_state);
};
-
#endif /* SQUID_PEERSELECTSTATE_H */
#include "base/TextException.h"
#include "ChunkedCodingParser.h"
#include "comm.h"
+#include "comm/Connection.h"
#include "HttpMsg.h"
#include "HttpRequest.h"
#include "HttpReply.h"
void Adaptation::Icap::ModXact::startReading()
{
- Must(connection >= 0);
+ Must(haveConnection());
Must(!reader);
Must(!adapted.header);
Must(!adapted.body_pipe);
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())
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)
#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"
icapRequest(NULL),
icapReply(NULL),
attempts(0),
- connection(-1),
+ connection(NULL),
theService(aService),
commBuf(NULL), commBufSize(0),
commEof(false),
{
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);
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);
+
+ ConnOpener *cs = new ConnOpener(connection, connector);
+ cs->setHost(s.cfg().host.termedBuf());
+ cs->connect_timeout = TheConfig.connect_timeout(service().cfg().bypass);
+ cs->start();
}
/*
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;
}
//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 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();
}
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();
}
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?
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());
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();
}
void Adaptation::Icap::Xaction::cancelRead()
{
if (reader != NULL) {
- comm_read_cancel(connection, reader);
+ Must(haveConnection());
+ comm_read_cancel(connection->fd, reader);
reader = NULL;
}
}
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()
{
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);
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");
#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"
void openConnection();
void closeConnection();
void dieOnConnectionFailure();
+ bool haveConnection() const;
void scheduleRead();
void scheduleWrite(MemBuf &buf);
void maybeLog();
protected:
- int connection; // FD of the ICAP server connection
+ Comm::ConnectionPointer connection; ///< ICAP server connection
Adaptation::Icap::ServiceRep::Pointer theService;
/*
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);
p->icp.version = ICP_VERSION_CURRENT;
- p->test_fd = -1;
+ p->testing_now = false;
#if USE_CACHE_DIGESTS
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
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).
+ 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.
- 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.
+ A warning message will be generated if it is set to a too-high
+ value and the configured value will be over-ridden.
- Note: This is in addition to the request re-forwarding which
- takes place if Squid fails to get a satisfying response.
+ 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
#include "acl/FilledChecklist.h"
#include "auth/UserRequest.h"
+#include "base/TextException.h"
#include "ChunkedCodingParser.h"
#include "client_side.h"
#include "client_side_reply.h"
#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"
/** 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;
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",
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",
#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
connState->readSomeData();
- clientdbEstablished(details->peer, 1);
+ clientdbEstablished(details->remote, 1);
incoming_sockets_accepted++;
}
/** 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);
/** 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;
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",
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",
#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
commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
- clientdbEstablished(details->peer, 1);
+ clientdbEstablished(details->remote, 1);
incoming_sockets_accepted++;
}
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;
};
-class ConnectionDetail;
-
/** A connection to a socket */
class ConnStateData : public BodyProducer/*, public RefCountable*/
{
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);
}
}
*/
#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"
{
}
-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
static void commSetTcpNoDelay(int);
#endif
static void commSetTcpRcvbuf(int, int);
-static PF commConnectFree;
static PF commHandleWrite;
static IPH commConnectDnsHandle;
*/
}
-
-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 ¶ms = 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)
{
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)
}
-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);
cancelled = true;
}
-ConnectionDetail::ConnectionDetail() : me(), peer()
-{
-}
-
int
CommSelectEngine::checkEvents(int timeout)
{
#include "squid.h"
#include "AsyncEngine.h"
#include "base/AsyncCall.h"
-#include "StoreIOBuffer.h"
-#include "Array.h"
+#include "CommCalls.h"
+#include "comm/comm_err_t.h"
+#include "comm/forward.h"
#include "ip/Address.h"
+#include "StoreIOBuffer.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 */
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);
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 *);
--- /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)
+{
+ // 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 *
+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));
+
+ if (host != NULL)
+ ipcacheMarkGoodAddr(host, solo->remote);
+ callCallback(COMM_OK, 0);
+ break;
+
+ default:
+ debugs(5, 5, HERE "FD " << solo->fd << ": * - try again");
+ fail_retries++;
+ if (host != NULL)
+ 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();
+}
+
--- /dev/null
+#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"
+
+/**
+ * Async-opener of a Comm connection.
+ */
+class ConnOpener : public AsyncJob
+{
+public:
+ /** attempt to open a connection. */
+ ConnOpener(Comm::ConnectionPointer &, AsyncCall::Pointer handler);
+
+ ~ConnOpener();
+
+ /** 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. */
+ ConnOpener(const ConnOpener &);
+ /* These objects may NOT be copied. Do not define this operator. */
+ ConnOpener operator =(const ConnOpener &c);
+
+ /**
+ * Wrapper to start the connection attempts happening.
+ */
+ static void Connect(void *data);
+
+ /** retry */
+ static void ConnectRetry(int fd, void *data);
+
+ /**
+ * Temporary close handler used during connect.
+ * Handles the case(s) when a partially setup connection gets closed early.
+ */
+ 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:
+ /**
+ * 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:
+ 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 total_tries; ///< total number of connection attempts over all destinations so far.
+ 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(ConnOpener);
+};
+
+#endif /* _SQUID_SRC_COMM_CONNOPENER_H */
--- /dev/null
+#include "config.h"
+#include "cbdata.h"
+#include "comm.h"
+#include "comm/Connection.h"
+
+Comm::Connection::Connection() :
+ local(),
+ remote(),
+ peer_type(HIER_NONE),
+ fd(-1),
+ tos(0),
+ flags(COMM_NONBLOCKING),
+ _peer(NULL)
+{}
+
+Comm::Connection::~Connection()
+{
+ close();
+ if (_peer) {
+ cbdataReferenceDone(_peer);
+ }
+}
+
+Comm::ConnectionPointer &
+Comm::Connection::copyDetails() const
+{
+ ConnectionPointer c = new Comm::Connection;
+
+ 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;
+
+ // 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;
+}
+
+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);
+ }
+}
--- /dev/null
+/*
+ * DEBUG: section 05 Socket Functions
+ * AUTHOR: Amos Jeffries
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ * Copyright (c) 2010, Amos Jeffries <amosjeffries@squid-cache.org>
+ */
+
+#ifndef _SQUIDCONNECTIONDETAIL_H_
+#define _SQUIDCONNECTIONDETAIL_H_
+
+#include "hier_code.h"
+#include "ip/Address.h"
+#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:
+ /** standard empty connection creation */
+ Connection();
+
+ /** These objects may not be exactly duplicated. Use copyDetails() instead. */
+ Connection(const Connection &c);
+
+ /** 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;
+
+ /** 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. */
+ bool isOpen() const { return (fd >= 0); }
+
+ /** 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 peer_type;
+
+ /** 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;
+
+ /** 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:
+ /** cache_peer data object (if any) */
+ peer *_peer;
+};
+
+}; // namespace Comm
+
+#endif
#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"
*/
/* Accept a new connection */
- ConnectionDetail connDetails;
- int newfd = oldAccept(connDetails);
+ Connection *connDetails = new Connection();
+ int newfd = oldAccept(*connDetails);
/* Check for errors */
if (newfd < 0) {
}
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);
}
}
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...
* 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);
}
}
- 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);
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);
#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
{
+class Connection;
+
class ListenStateData
{
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;
static void doAccept(int fd, void *data);
void acceptOne();
- int oldAccept(ConnectionDetail &details);
+ int oldAccept(Comm::Connection &details);
AsyncCall::Pointer theCallback;
bool mayAcceptMore;
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
--- /dev/null
+#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 */
--- /dev/null
+#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 */
#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)
-
/*
* $Id$
*
*
*/
-#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
#endif
static void idnsCacheQuery(idns_query * q);
static void idnsSendQuery(idns_query * q);
+static CNCB idnsInitVCConnected;
static IOCB idnsReadVCHeader;
static void idnsDoSendQueryVC(nsvc *vc);
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)
}
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);
}
static void
idnsInitVC(int ns)
{
- char buf[MAX_IPSTRLEN];
-
nsvc *vc = cbdataAlloc(nsvc);
nameservers[ns].vc = vc;
vc->ns = ns;
-
- Ip::Address addr;
-
- if (!Config.Addrs.udp_outgoing.IsNoAddr())
- addr = Config.Addrs.udp_outgoing;
- else
- addr = Config.Addrs.udp_incoming;
-
vc->queue = new MemBuf;
-
vc->msg = new MemBuf;
+ vc->busy = 1;
- vc->fd = comm_open(SOCK_STREAM,
- IPPROTO_TCP,
- addr,
- COMM_NONBLOCKING,
- "DNS TCP Socket");
+ Comm::ConnectionPointer conn = new Comm::Connection;
- if (vc->fd < 0)
- fatal("Could not create a DNS socket");
+ if (!Config.Addrs.udp_outgoing.IsNoAddr())
+ conn->local = Config.Addrs.udp_outgoing;
+ else
+ conn->local = Config.Addrs.udp_incoming;
- comm_add_close_handler(vc->fd, idnsVCClosed, vc);
+ conn->remote = nameservers[ns].S;
- vc->busy = 1;
+ AsyncCall::Pointer call = commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected, vc));
- commConnectStart(vc->fd, nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN), nameservers[ns].S.GetPort(), idnsInitVCConnected, vc);
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->setHost("DNS TCP Socket");
+ cs->start();
}
static void
#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];
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;
- }
-
+ fwd->paths[0]->close();
fwd->self = NULL;
}
{
entry = e;
client_fd = fd;
- server_fd = -1;
request = HTTPMSGLOCK(r);
start_t = squid_curtime;
// 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
if (! flags.forward_completed)
completed();
- serversFree(&servers);
-
HTTPMSGUNLOCK(request);
if (err)
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 (paths[0]->fd > -1) {
+ comm_remove_close_handler(paths[0]->fd, fwdServerClosedWrapper, this);
+ debugs(17, 3, HERE << "closing FD " << paths[0]->fd);
+ paths[0]->close();
}
+ paths.clean();
+
debugs(17, 3, HERE << "FwdState destructor done");
}
}
}
- 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
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;
}
/* NOTREACHED */
}
+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)
{
FwdState::unregister(int fd)
{
debugs(17, 3, HERE << entry->url() );
- assert(fd == server_fd);
+ assert(fd == paths[0]->fd);
assert(fd > -1);
comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
- server_fd = -1;
}
/**
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();
logReplyStatus(n_tries, entry->getReply()->sline.status);
if (reforward()) {
- debugs(17, 3, "fwdComplete: re-forwarding " << entry->getReply()->sline.status << " " << e->url());
+ debugs(17, 3, HERE << "re-forwarding " << entry->getReply()->sline.status << " " << entry->url());
- if (server_fd > -1)
- unregister(server_fd);
+ if (paths[0]->fd > -1)
+ unregister(paths[0]->fd);
- e->reset();
+ entry->reset();
- startComplete(servers);
+ /* 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 " << paths[0]->fd << " not re-forwarding status " << entry->getReply()->sline.status);
EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
entry->complete();
- if (server_fd < 0)
+ if (paths[0]->fd < 0)
completed();
self = NULL; // refcounted
/**** 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
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)
+void
+fwdConnectDoneWrapper(Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, 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--;
+ fwd->connectDone(conn, status, xerrno);
}
/**** PRIVATE *****************************************************************/
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());
+ assert(paths[0]->fd == fd);
+
+ if (paths[0]->getPeer()) {
+ paths[0]->getPeer()->stats.conn_open--;
+ }
retryOrBail();
}
}
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)");
- /* 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;
- }
+ paths.shift(); // last one failed. try another.
- /* 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);
+ 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;
+ }
- return;
+ connectStart();
+ return;
+ }
+ // else bail. no more paths possible to try.
}
if (!err && shutting_down) {
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(paths[0]->fd < 0);
retryOrBail();
}
void
FwdState::negotiateSSL(int fd)
{
- FwdServer *fs = servers;
SSL *ssl = fd_table[fd].ssl;
int ret;
fail(anErr);
- if (fs->_peer) {
- peerConnectFailed(fs->_peer);
- fs->_peer->stats.conn_open--;
+ if (paths[0]->getPeer()) {
+ peerConnectFailed(paths[0]->getPeer());
+ paths[0]->getPeer()->stats.conn_open--;
}
- comm_close(fd);
+ paths[0]->close();
return;
}
}
- if (fs->_peer && !SSL_session_reused(ssl)) {
- if (fs->_peer->sslSession)
- SSL_SESSION_free(fs->_peer->sslSession);
+ if (paths[0]->getPeer() && !SSL_session_reused(ssl)) {
+ if (paths[0]->getPeer()->sslSession)
+ SSL_SESSION_free(paths[0]->getPeer()->sslSession);
- fs->_peer->sslSession = SSL_get1_session(ssl);
+ paths[0]->getPeer()->sslSession = SSL_get1_session(ssl);
}
dispatch();
void
FwdState::initiateSSL()
{
- FwdServer *fs = servers;
- int fd = server_fd;
SSL *ssl;
SSL_CTX *sslContext = NULL;
- peer *peer = fs->_peer;
+ const peer *peer = paths[0]->getPeer();
+ int fd = paths[0]->fd;
if (peer) {
assert(peer->use_ssl);
#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);
+ /* it might have been a timeout with a partially open link */
+ if (paths.size() > 0) {
+ if (paths[0]->getPeer())
+ peerConnectFailed(paths[0]->getPeer());
- comm_close(server_fd);
- } else {
- debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" );
+ paths[0]->close();
+ }
+ retryOrBail();
+ return;
+ }
- if (fs->_peer)
- peerConnectSucceded(fs->_peer);
+#if REDUNDANT_NOW
+ if (Config.onoff.log_ip_on_direct && paths[0]->peer_type == HIER_DIRECT)
+ updateHierarchyInfo();
+#endif
-#if USE_SSL
+ debugs(17, 3, "FD " << paths[0]->fd << ": '" << entry->url() << "'" );
- if ((fs->_peer && fs->_peer->use_ssl) ||
- (!fs->_peer && request->protocol == PROTO_HTTPS)) {
- initiateSSL();
- return;
- }
+ comm_add_close_handler(paths[0]->fd, fwdServerClosedWrapper, this);
-#endif
- dispatch();
+ if (paths[0]->getPeer())
+ peerConnectSucceded(paths[0]->getPeer());
+
+ updateHierarchyInfo();
+
+#if USE_SSL
+ if ((paths[0]->getPeer() && paths[0]->getPeer()->use_ssl) ||
+ (!paths[0]->getPeer() && request->protocol == PROTO_HTTPS)) {
+ initiateSSL();
+ return;
}
+#endif
+
+ dispatch();
}
void
FwdState::connectTimeout(int fd)
{
- FwdServer *fs = servers;
-
debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
- assert(fd == server_fd);
+ assert(fd == paths[0]->fd);
- if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
+ if (Config.onoff.log_ip_on_direct && paths[0]->peer_type == 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);
+ /* This marks the peer DOWN ... */
+ if (paths.size() > 0)
+ if (paths[0]->getPeer())
+ peerConnectFailed(paths[0]->getPeer());
}
- comm_close(fd);
+ paths[0]->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;
+ Comm::ConnectionPointer conn = paths[0];
+
+ /* connection timeout */
+ int ctimeout;
+ if (conn->getPeer()) {
+ ctimeout = conn->getPeer()->connect_timeout > 0 ? conn->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 (conn->peer_type == PINNED) {
ConnStateData *pinned_connection = request->pinnedConnection();
assert(pinned_connection);
- fd = pinned_connection->validatePinnedConnection(request, fs->_peer);
- if (fd >= 0) {
+ conn->fd = pinned_connection->validatePinnedConnection(request, conn->getPeer());
+ if (conn->isOpen()) {
pinned_connection->unpinConnection();
#if 0
- if (!fs->_peer)
- fs->code = HIER_DIRECT;
+ if (!conn->getPeer())
+ conn->peer_type = 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);
+ 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();
+ conn = NULL; // maybe release the conn memory. it's not needed by us anyway.
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 (conn->getPeer()) {
+ host = conn->getPeer()->host;
+ port = conn->getPeer()->http_port;
+ conn->fd = fwdPconnPool->pop(conn->getPeer()->name, conn->getPeer()->http_port, request->GetHost(), conn->local, checkRetriable());
} else {
host = request->GetHost();
port = request->port;
- fd = fwdPconnPool->pop(host, port, NULL, client_addr, checkRetriable());
+ conn->fd = fwdPconnPool->pop(host, port, NULL, conn->local, checkRetriable());
}
- if (fd >= 0) {
- debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
- server_fd = fd;
+ conn->remote.SetPort(port);
+
+ if (conn->isOpen()) {
+ debugs(17, 3, HERE << "reusing pconn FD " << conn->fd);
n_tries++;
- if (!fs->_peer)
+ if (!conn->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(conn->fd, fwdServerClosedWrapper, this);
dispatch();
-
return;
}
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));
+ ConnOpener *cs = new ConnOpener(paths[0], call);
+ cs->setHost(host);
+ cs->connect_timeout = ctimeout;
+ cs->start();
}
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);
+ assert(paths.size() > 0 && paths[0]->fd > -1);
- fd_note(server_fd, entry->url());
+ fd_note(paths[0]->fd, entry->url());
- fd_table[server_fd].noteUse(fwdPconnPool);
+ fd_table[paths[0]->fd].noteUse(fwdPconnPool);
/*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
assert(entry->ping_status != PING_WAITING);
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(paths[0]->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(paths[0]->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.
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 " << paths[0]->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 " << paths[0]->fd << " " << xstrerror());
}
}
#endif
- if (servers && (p = servers->_peer)) {
- p->stats.fetches++;
- request->peer_login = p->login;
- request->peer_domain = p->domain;
+ if (paths.size() > 0 && paths[0]->getPeer() != NULL) {
+ paths[0]->getPeer()->stats.fetches++;
+ request->peer_login = paths[0]->getPeer()->login;
+ request->peer_domain = paths[0]->getPeer()->domain;
httpStart(this);
} else {
request->peer_login = NULL;
* transient (network) error; its a bug.
*/
flags.dont_retry = 1;
- comm_close(server_fd);
+ paths[0]->close();
break;
}
}
FwdState::reforward()
{
StoreEntry *e = entry;
- FwdServer *fs = servers;
http_status s;
assert(e->store_status == STORE_PENDING);
assert(e->mem_obj);
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;
}
if (request->bodyNibbled())
return 0;
- assert(fs);
+ paths.shift();
- servers = fs->next;
-
- fwdServerFree(fs);
-
- if (servers == NULL) {
- debugs(17, 3, "fwdReforward: No forward-servers left");
+ 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);
}
* - 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)
if (status > HTTP_INVALID_HEADER)
return;
- assert(tries);
-
- tries--;
+ assert(tries >= 0);
if (tries > MAX_FWD_STATS_IDX)
tries = MAX_FWD_STATS_IDX;
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
{
assert(request);
- FwdServer *fs = servers;
- assert(fs);
+ assert(paths.size() > 0);
- const char *nextHop = NULL;
+ char nextHop[256]; //
- if (fs->_peer) {
+ if (paths[0]->getPeer()) {
// went to peer, log peer host name
- nextHop = fs->_peer->name;
+ snprintf(nextHop,256,"%s", paths[0]->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
+ paths[0]->remote.NtoA(nextHop, 256);
}
- assert(nextHop);
- hierarchyNote(&request->hier, fs->code, nextHop);
-}
+ request->hier.peer_local_port = paths[0]->local.GetPort();
-
-/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
-
-static void
-fwdServerFree(FwdServer * fs)
-{
- cbdataReferenceDone(fs->_peer);
- memFree(fs, MEM_FWD_SERVER);
+ assert(nextHop[0]);
+ hierarchyNote(&request->hier, paths[0]->peer_type, nextHop);
}
-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;
-}
+/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
/*
* DPW 2007-05-19
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
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
class HttpRequest;
#include "comm.h"
-#include "hier_code.h"
+#include "comm/Connection.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
{
static void initModule();
static void fwdStart(int fd, StoreEntry *, HttpRequest *);
- void startComplete(FwdServer *);
- void startFail();
+ void startComplete();
void fail(ErrorState *err);
void unregister(int fd);
void complete();
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; }
void ftpPasvFailed(bool val) { flags.ftp_pasv_failed = val; }
- static void serversFree(FwdServer **);
+ Comm::ConnectionPointer conn() const { return paths[0]; };
private:
// hidden for safer management of self; use static fwdStart
public:
StoreEntry *entry;
HttpRequest *request;
- int server_fd;
- FwdServer *servers;
static void abort(void*);
private:
unsigned int forward_completed:1;
} flags;
- Ip::Address src; /* Client address for this connection. Needed for transparent operations. */
+ /** possible paths which may be tried (in sequence stored) */
+ Comm::Paths paths;
// NP: keep this last. It plays with private/public
CBDATA_CLASS2(FwdState);
#include "squid.h"
#include "cbdata.h"
+#include "DnsLookupDetails.h"
#include "event.h"
#include "CacheManager.h"
#include "SquidTime.h"
#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"
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->conn()->fd, closer);
if (request->method == METHOD_PUT)
flags.put = 1;
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));
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->setHost(ftpState->data.host);
+ cs->start();
}
/** \ingroup ServerProtocolFTPInternal
/** 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);
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);
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));
+ ConnOpener *cs = new ConnOpener(conn, call);
+ cs->setHost(ftpState->data.host);
+ cs->connect_timeout = Config.Timeout.connect;
+ cs->start();
}
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.");
* 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;
* 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);
void
gopherStart(FwdState * fwd)
{
- int fd = fwd->server_fd;
StoreEntry *entry = fwd->entry;
GopherStateData *gopherState;
CBDATA_INIT_TYPE(GopherStateData);
gopher_request_parse(fwd->request,
&gopherState->type_id, gopherState->request);
- comm_add_close_handler(fd, gopherStateFree, gopherState);
+ comm_add_close_handler(fwd->conn()->fd, gopherStateFree, gopherState);
if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
&& (strchr(gopherState->request, '?') == NULL)) {
gopherToHTML(gopherState, (char *) NULL, 0);
fwd->complete();
- comm_close(fd);
+ fwd->conn()->close();
return;
}
- gopherState->fd = fd;
+ gopherState->fd = fwd->conn()->fd; // TODO: save the conn() in gopher instead of the FD
gopherState->fwd = fwd;
- gopherSendRequest(fd, gopherState);
- commSetTimeout(fd, Config.Timeout.read, gopherTimeout, gopherState);
+ gopherSendRequest(fwd->conn()->fd, gopherState);
+ commSetTimeout(fwd->conn()->fd, Config.Timeout.read, gopherTimeout, gopherState);
}
debugs(11,5,HERE << "HttpStateData " << this << " created");
ignoreCacheControl = false;
surrogateNoStore = false;
- fd = fwd->server_fd;
+ fd = fwd->conn()->fd; // TODO: store Comm::Connection instead of FD
readBuf = new MemBuf;
readBuf->init();
orig_request = HTTPMSGLOCK(fwd->request);
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->conn() != NULL)
+ _peer = cbdataReference(fwd->conn()->getPeer()); /* might be NULL */
if (_peer) {
const char *url;
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);
orig_request->pinnedConnection()->pinConnection(fd, orig_request, _peer,
(request->flags.connection_auth != 0));
} else {
- fwd->pconnPush(fd, _peer, request, orig_request->GetHost(), client_addr);
+ fwd->pconnPush(fwd->conn(), _peer, request, orig_request->GetHost(), client_addr);
}
fd = -1;
*/
#include "squid.h"
-#include "Store.h"
-#include "comm.h"
-#include "ICP.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 "acl/Acl.h"
+#include "acl/FilledChecklist.h"
+#include "comm/Connection.h"
+#include "HttpRequest.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,
#include "acl/RegexData.h"
#include "acl/UserData.h"
#include "client_side.h"
+#include "comm/Connection.h"
#include "ident/AclIdent.h"
#include "ident/Ident.h"
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::Connection cc; // IDENT will clone it's own copy for alterations.
+ cc.local = checklist->conn()->me;
+ cc.remote = checklist->conn()->peer;
+ 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);
#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"
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;
{
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);
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;
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
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;
}
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, ':'))) {
}
}
- comm_close(fd);
+ state->conn->close();
}
void
* 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) {
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));
+ ConnOpener *cs = new ConnOpener(state->conn, call);
+ cs->connect_timeout = Ident::TheConfig.timeout;
+ cs->start();
}
void
#if USE_IDENT
#include "cbdata.h"
-
-#include "ip/forward.h"
+#include "comm/forward.h"
namespace Ident
{
* 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
*/
#include "squid.h"
-#include "comm.h"
+#include "comm/Connection.h"
#include "fde.h"
#include "ip/Address.h"
#include "rfc1738.h"
#include "squid.h"
#include "cbdata.h"
-#include "event.h"
#include "CacheManager.h"
+#include "DnsLookupDetails.h"
+#include "event.h"
+#include "ip/Address.h"
#include "SquidTime.h"
#include "Store.h"
#include "wordlist.h"
-#include "ip/Address.h"
/**
\defgroup IPCacheAPI IP Cache API
* 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)
#include "squid.h"
#include "comm.h"
+#include "comm/Connection.h"
#include "log/File.h"
#include "log/ModTcp.h"
#include "Parsing.h"
#include "squid.h"
#include "comm.h"
+#include "comm/Connection.h"
#include "log/File.h"
#include "log/ModUdp.h"
#include "Parsing.h"
#include "MemPool.h"
#include "icmp/IcmpSquid.h"
#include "icmp/net_db.h"
-
+#include "PeerSelectState.h"
#if USE_LOADABLE_MODULES
#include "LoadableModules.h"
#endif
#include "squid.h"
#include "ProtoPort.h"
#include "acl/FilledChecklist.h"
-#include "event.h"
+#include "comm/Connection.h"
+#include "comm/ConnOpener.h"
#include "CacheManager.h"
+#include "event.h"
#include "htcp.h"
#include "HttpRequest.h"
#include "ICP.h"
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);
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));
+ /* 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);
- fd = comm_open(SOCK_STREAM, IPPROTO_TCP, temp, COMM_NONBLOCKING, p->host);
+ p->testing_now++;
- if (fd < 0)
- return ret;
-
- comm_add_close_handler(fd, &peerProbeClosed, p);
- commSetTimeout(fd, ctimeout, peerProbeConnectTimeout, p);
-
- p->test_fd = fd;
+ 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->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;
peerConnectFailedSilent(p);
}
- comm_remove_close_handler(fd, &peerProbeClosed, p);
- comm_close(fd);
- p->test_fd = -1;
+ conn->close();
+ p->testing_now--;
return;
}
*/
#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 "ICP.h"
-#include "HttpRequest.h"
-#include "acl/FilledChecklist.h"
#include "htcp.h"
-#include "forward.h"
-#include "SquidTime.h"
+#include "HttpRequest.h"
#include "icmp/net_db.h"
+#include "ICP.h"
+#include "PeerSelectState.h"
+#include "SquidTime.h"
+#include "Store.h"
static struct {
int timeouts;
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);
void
-peerSelect(HttpRequest * request,
+peerSelect(Comm::Paths * paths,
+ HttpRequest * request,
StoreEntry * entry,
PSC * callback,
void *callback_data)
psstate->entry = entry;
+ psstate->paths = paths;
+
psstate->callback = callback;
psstate->callback_data = cbdataReference(callback_data);
{
StoreEntry *entry = psstate->entry;
FwdServer *fs = psstate->servers;
- PSC *callback;
- void *cbdata;
if (entry) {
debugs(44, 3, "peerSelectCallback: " << entry->url() );
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.
+ Comm::ConnectionPointer p;
+ int ip = ia->cur;
+ for (int n = 0; n < ia->count; n++, ip++) {
+ 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->peer_type = 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)
{
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; */
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)
{
}
}
-/*
+/**
* peerGetSomeNeighbor
*
* Selects a neighbor (parent or sibling) based on one of the
peerSelectInit(void)
{
memset(&PeerStats, '\0', sizeof(PeerStats));
+ memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
}
static void
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);
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 *);
#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"
} onoff;
int forward_max_tries;
+ int connect_retries;
class ACL *aclList;
char *errorStylesheet;
struct {
- int maxtries;
int onerror;
} retry;
int n_addresses;
int rr_count;
peer *next;
- int test_fd;
+ int testing_now;
struct {
unsigned int hash;
-
/*
* $Id$
*
*/
#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 "MemBuf.h"
+#include "errorpage.h"
+#include "fde.h"
+#include "HttpRequest.h"
#include "http.h"
+#include "MemBuf.h"
+#include "PeerSelectState.h"
class TunnelStateData
{
char *host; /* either request->host or proxy host */
u_short port;
HttpRequest *request;
- FwdServer *servers;
+ Comm::Paths paths;
class Connection
{
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;
TunnelStateData::Connection::~Connection()
{
- safe_free (buf);
+ safe_free(buf);
}
int
comm_read(from.fd(), from.buf, from.bytesWanted(1, SQUID_TCP_SO_RCVBUF), completion, this);
}
-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)
{
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->peer_type, 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->peer_type, 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);
- } else {
- if (tunnelState->servers->_peer)
- tunnelProxyConnected(tunnelState->server.fd(), tunnelState);
- else {
- tunnelConnected(tunnelState->server.fd(), tunnelState);
+ hierarchyNote(&tunnelState->request->hier, conn->peer_type, 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));
+ 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;
+ }
+
+ 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 {
+ tunnelState->request->peer_login = NULL;
+ tunnelState->request->flags.proxying = 0;
+ }
- commSetTimeout(tunnelState->server.fd(),
- Config.Timeout.read,
- tunnelTimeout,
- tunnelState);
+ if (conn->getPeer())
+ tunnelProxyConnected(tunnelState->server.fd(), tunnelState);
+ else {
+ tunnelConnected(tunnelState->server.fd(), tunnelState);
}
+
+ commSetTimeout(tunnelState->server.fd(), Config.Timeout.read, tunnelTimeout, tunnelState);
}
void
{
/* Create state structure. */
TunnelStateData *tunnelState = NULL;
- int sock;
ErrorState *err = NULL;
int answer;
int fd = http->getConn()->fd;
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);
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.
}
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;
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));
+ ConnOpener *cs = new ConnOpener(tunnelState->paths[0], call);
+ cs->setHost(tunnelState->url);
+ cs->connect_timeout = Config.Timeout.connect;
+ cs->start();
}
CBDATA_CLASS_INIT(TunnelStateData);
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 */
*
*/
#include "squid.h"
-#include "comm.h"
-#include "event.h"
#if USE_WCCP
+#include "comm.h"
+#include "comm/Connection.h"
+#include "event.h"
+
#define WCCP_PORT 2048
#define WCCP_REVISION 0
#define WCCP_ACTIVE_CACHES 32
#if USE_WCCPv2
#include "comm.h"
+#include "comm/Connection.h"
#include "compat/strsep.h"
#include "event.h"
#include "ip/Address.h"
whoisStart(FwdState * fwd)
{
WhoisState *p;
- int fd = fwd->server_fd;
+ int fd = fwd->conn()->fd;
char *buf;
size_t l;
CBDATA_INIT_TYPE(WhoisState);