#include "acl/Gadgets.h"
#include "CachePeer.h"
#include "defines.h"
+#include "neighbors.h"
#include "NeighborTypeDomainList.h"
#include "pconn.h"
#include "PeerPoolMgr.h"
xfree(domain);
}
+void
+CachePeer::noteSuccess()
+{
+ if (!tcp_up) {
+ debugs(15, 2, "connection to " << *this << " succeeded");
+ tcp_up = connect_fail_limit; // NP: so peerAlive() works properly.
+ peerAlive(this);
+ } else {
+ tcp_up = connect_fail_limit;
+ }
+}
+
+void
+CachePeer::noteFailure(const Http::StatusCode code)
+{
+ if (Http::Is4xx(code))
+ return; // this failure is not our fault
+
+ countFailure();
+}
+
+// TODO: Require callers to detail failures instead of using one (and often
+// misleading!) "connection failed" phrase for all of them.
+/// noteFailure() helper for handling failures attributed to this peer
+void
+CachePeer::countFailure()
+{
+ stats.last_connect_failure = squid_curtime;
+ if (tcp_up > 0)
+ --tcp_up;
+
+ const auto consideredAliveByAdmin = (stats.logged_state == PEER_ALIVE);
+ const auto level = consideredAliveByAdmin ? DBG_IMPORTANT : 2;
+ debugs(15, level, "ERROR: Connection to " << *this << " failed");
+
+ if (consideredAliveByAdmin) {
+ if (!tcp_up) {
+ debugs(15, DBG_IMPORTANT, "Detected DEAD " << neighborTypeStr(this) << ": " << name);
+ stats.logged_state = PEER_DEAD;
+ } else {
+ debugs(15, 2, "additional failures needed to mark this cache_peer DEAD: " << tcp_up);
+ }
+ } else {
+ assert(!tcp_up);
+ debugs(15, 2, "cache_peer " << *this << " is still DEAD");
+ }
+}
+
void
CachePeer::rename(const char * const newName)
{
#include "acl/forward.h"
#include "base/CbcPointer.h"
#include "enums.h"
+#include "http/StatusCode.h"
#include "icp_opcode.h"
#include "ip/Address.h"
#include "security/PeerOptions.h"
explicit CachePeer(const char *hostname);
~CachePeer();
+ /// reacts to a successful establishment of a connection to this cache_peer
+ void noteSuccess();
+
+ /// reacts to a failure on a connection to this cache_peer
+ /// \param code a received response status code, if any
+ void noteFailure(Http::StatusCode code);
+
/// (re)configure cache_peer name=value
void rename(const char *);
int front_end_https = 0; ///< 0 - off, 1 - on, 2 - auto
int connection_auth = 2; ///< 0 - off, 1 - on, 2 - auto
+
+private:
+ void countFailure();
};
+/// reacts to a successful establishment of a connection to an origin server or cache_peer
+/// \param peer nil if Squid established a connection to an origin server
+inline void
+NoteOutgoingConnectionSuccess(CachePeer * const peer)
+{
+ if (peer)
+ peer->noteSuccess();
+}
+
+/// reacts to a failure on a connection to an origin server or cache_peer
+/// \param peer nil if the connection is to an origin server
+/// \param code a received response status code, if any
+inline void
+NoteOutgoingConnectionFailure(CachePeer * const peer, const Http::StatusCode code)
+{
+ if (peer)
+ peer->noteFailure(code);
+}
+
/// identify the given cache peer in cache.log messages and such
std::ostream &operator <<(std::ostream &, const CachePeer &);
CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
ConnStateData::notePeerConnection, serverConnection());
- if (serverConnection()->getPeer())
- peerConnectSucceded(serverConnection()->getPeer());
+ NoteOutgoingConnectionSuccess(serverConnection()->getPeer());
dispatch();
}
class AccessLogEntry;
typedef RefCount<AccessLogEntry> AccessLogEntryPointer;
-class ErrorState;
class HttpRequest;
class PconnPool;
class ResolvedPeers;
}
debugs(17, 8, what << " failed: " << params.conn);
- if (const auto peer = params.conn->getPeer())
- peerConnectFailed(peer);
// remember the last failure (we forward it if we cannot connect anywhere)
lastFailedConnection = handledPath;
lastError = makeError(ERR_CONNECT_FAIL);
lastError->xerrno = params.xerrno;
+ NoteOutgoingConnectionFailure(params.conn->getPeer(), lastError->httpStatus);
+
if (spareWaiting)
updateSpareWaitAfterPrimeFailure();
}
if (params.flag != Comm::OK) {
- peerConnectFailed(peer);
+ NoteOutgoingConnectionFailure(peer, Http::scNone);
checkpoint("conn opening failure"); // may retry
return;
}
assert(!answer.tunneled);
if (answer.error.get()) {
assert(!answer.conn);
- // PeerConnector calls peerConnectFailed() for us;
+ // PeerConnector calls NoteOutgoingConnectionFailure() for us
checkpoint("conn securing failure"); // may retry
return;
}
{
closer = nullptr;
if (connection) {
- countFailingConnection();
+ countFailingConnection(nullptr);
connection->noteClosure();
connection = nullptr;
}
if (const auto failingConnection = connection) {
// TODO: Reuse to-peer connections after a CONNECT error response.
- countFailingConnection();
+ countFailingConnection(error);
disconnect();
failingConnection->close();
}
}
void
-Http::Tunneler::countFailingConnection()
+Http::Tunneler::countFailingConnection(const ErrorState * const error)
{
assert(connection);
- if (const auto p = connection->getPeer())
- peerConnectFailed(p);
+ NoteOutgoingConnectionFailure(connection->getPeer(), error ? error->httpStatus : Http::scNone);
if (noteFwdPconnUse && connection->isOpen())
fwdPconnPool->noteUses(fd_table[connection->fd].pconn.uses);
}
void disconnect();
/// updates connection usage history before the connection is closed
- void countFailingConnection();
+ void countFailingConnection(const ErrorState *);
AsyncCall::Pointer writer; ///< called when the request has been written
AsyncCall::Pointer reader; ///< called when the response should be read
class Error;
class ErrorDetail;
+class ErrorState;
typedef RefCount<ErrorDetail> ErrorDetailPointer;
const char *StatusCodeString(const Http::StatusCode status);
/// whether this is an informational 1xx response status code
inline bool Is1xx(const int sc) { return scContinue <= sc && sc < scOkay; }
+/// whether this is a client error 4xx response status code
+inline bool Is4xx(const int sc) { return scBadRequest <= sc && sc < scInternalServerError; }
/// whether this response status code prohibits sending Content-Length
inline bool ProhibitsContentLength(const StatusCode sc) { return sc == scNoContent || Is1xx(sc); }
/// whether to send the request to another peer based on the current response status code
}
}
-/**
- * Perform all actions when a CachePeer is detected revived.
- */
-static void
+void
peerAlive(CachePeer *p)
{
if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
p->stats.last_reply = squid_curtime;
p->stats.probe_start = 0;
+
+ // TODO: Remove or explain how we could detect an alive peer without IP addresses
+ if (!p->n_addresses)
+ ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
}
CachePeer *
eventAddIsh("peerRefreshDNS", peerRefreshDNS, nullptr, 3600.0, 1);
}
-// TODO: Move to CachePeer::noteFailure() or similar.
-// TODO: Require callers to detail failures instead of using one (and often
-// misleading!) "TCP" label for all of them.
-void
-peerConnectFailed(CachePeer * const p)
-{
- p->stats.last_connect_failure = squid_curtime;
- if (p->tcp_up > 0)
- --p->tcp_up;
-
- // TODO: Report peer name. Same-addresses peers often have different names.
-
- const auto consideredAliveByAdmin = p->stats.logged_state == PEER_ALIVE;
- const auto level = consideredAliveByAdmin ? DBG_IMPORTANT : 2;
- debugs(15, level, "ERROR: TCP connection to " << *p << " failed");
-
- if (consideredAliveByAdmin) {
- if (!p->tcp_up) {
- debugs(15, DBG_IMPORTANT, "Detected DEAD " << neighborTypeStr(p) << ": " << *p);
- p->stats.logged_state = PEER_DEAD;
- } else {
- debugs(15, 2, "additional failures needed to mark this cache_peer DEAD: " << p->tcp_up);
- }
- } else {
- assert(!p->tcp_up);
- debugs(15, 2, "cache_peer " << *p << " is still DEAD");
- }
-}
-
-void
-peerConnectSucceded(CachePeer * p)
-{
- if (!p->tcp_up) {
- debugs(15, 2, "TCP connection to " << *p << " succeeded");
- p->tcp_up = p->connect_fail_limit; // NP: so peerAlive(p) works properly.
- peerAlive(p);
- if (!p->n_addresses)
- ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
- } else
- p->tcp_up = p->connect_fail_limit;
-}
-
/// whether new TCP probes are currently banned
static bool
peerProbeIsBusy(const CachePeer *p)
{
CachePeer *p = (CachePeer*)data;
- if (status == Comm::OK) {
- peerConnectSucceded(p);
- } else {
- peerConnectFailed(p);
- }
+ if (status == Comm::OK)
+ p->noteSuccess();
+ else
+ p->noteFailure(Http::scNone);
-- p->testing_now;
conn->close();
CachePeer *getWeightedRoundRobinParent(PeerSelector*);
void peerClearRRStart(void);
void peerClearRR(void);
+
+// TODO: Move, together with its many dependencies and callers, into CachePeer.
+/// Updates protocol-agnostic CachePeer state after an indication of a
+/// successful contact with the given cache_peer.
+void peerAlive(CachePeer *);
+
lookup_t peerDigestLookup(CachePeer * p, PeerSelector *);
CachePeer *neighborsDigestSelect(PeerSelector *);
void peerNoteDigestLookup(HttpRequest * request, CachePeer * p, lookup_t lookup);
int neighborUp(const CachePeer * e);
const char *neighborTypeStr(const CachePeer * e);
peer_t neighborType(const CachePeer *, const AnyP::Uri &);
-void peerConnectFailed(CachePeer *);
-void peerConnectSucceded(CachePeer *);
void dump_peer_options(StoreEntry *, CachePeer *);
int peerHTTPOkay(const CachePeer *, PeerSelector *);
if (error) {
debugs(83, 5, "error=" << (void*)error);
- // XXX: forward.cc calls peerConnectSucceeded() after an OK TCP connect but
- // we call peerConnectFailed() if SSL failed afterwards. Is that OK?
- // It is not clear whether we should call peerConnectSucceeded/Failed()
+ // XXX: FwdState calls NoteOutgoingConnectionSuccess() after an OK TCP connect, but
+ // we call noteFailure() if SSL failed afterwards. Is that OK?
+ // It is not clear whether we should call noteSuccess()/noteFailure()/etc.
// based on TCP results, SSL results, or both. And the code is probably not
// consistent in this aspect across tunnelling and forwarding modules.
if (peer && peer->secure.encryptTransport)
- peerConnectFailed(peer);
+ peer->noteFailure(error->httpStatus);
return;
}
/// Return the configured TLS context object
virtual Security::ContextPointer getTlsContext();
- /// On error calls peerConnectFailed().
- /// On success store the used TLS session for later use.
+ /// On success, stores the used TLS session for later use.
+ /// On error, informs the peer.
virtual void noteNegotiationDone(ErrorState *);
};
#include "acl/FilledChecklist.h"
#include "base/AsyncCallbacks.h"
#include "base/IoManip.h"
+#include "CachePeer.h"
#include "comm/Loops.h"
#include "comm/Read.h"
#include "Downloader.h"
debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
closeHandler = nullptr;
+
+ const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
+ static const auto d = MakeNamedErrorDetail("TLS_CONNECT_CLOSE");
+ err->detailError(d);
+
if (serverConn) {
- countFailingConnection();
+ countFailingConnection(err);
serverConn->noteClosure();
serverConn = nullptr;
}
- const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
- static const auto d = MakeNamedErrorDetail("TLS_CONNECT_CLOSE");
- err->detailError(d);
bail(err);
}
answer().error = error;
if (const auto failingConnection = serverConn) {
- countFailingConnection();
+ countFailingConnection(error);
disconnect();
failingConnection->close();
}
}
void
-Security::PeerConnector::countFailingConnection()
+Security::PeerConnector::countFailingConnection(const ErrorState * const error)
{
assert(serverConn);
- if (const auto p = serverConn->getPeer())
- peerConnectFailed(p);
+ NoteOutgoingConnectionFailure(serverConn->getPeer(), error ? error->httpStatus : Http::scNone);
// TODO: Calling PconnPool::noteUses() should not be our responsibility.
if (noteFwdPconnUse && serverConn->isOpen())
fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
#include <iosfwd>
#include <queue>
-class ErrorState;
class Downloader;
class DownloaderAnswer;
class AccessLogEntry;
void disconnect();
/// updates connection usage history before the connection is closed
- void countFailingConnection();
+ void countFailingConnection(const ErrorState *);
/// If called the certificates validator will not used
void bypassCertValidator() {useCertValidator_ = false;}
void PeerConnector::sendSuccess() STUB
void PeerConnector::callBack() STUB
void PeerConnector::disconnect() STUB
-void PeerConnector::countFailingConnection() STUB
+void PeerConnector::countFailingConnection(const ErrorState *) STUB
void PeerConnector::recordNegotiationDetails() STUB
EncryptorAnswer &PeerConnector::answer() STUB_RETREF(EncryptorAnswer)
}