request->flags.accelerated = http->flags.accel;
request->flags.sslBumped = conn->switchedToHttps();
+ request->flags.canRePin = request->flags.sslBumped && conn->pinning.pinned;
request->flags.ignore_cc = conn->port->ignore_cc;
// TODO: decouple http->flags.accel from request->flags.sslBumped
request->flags.no_direct = (request->flags.accelerated && !request->flags.sslBumped) ?
void
ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
{
- pinning.closeHandler = NULL; // Comm unregisters handlers before calling
- unpinConnection();
+ // it might be possible for FwdState to repin a failed connection sooner
+ // than this close callback is called for the failed connection
+ if (pinning.serverConnection == io.conn) {
+ pinning.closeHandler = NULL; // Comm unregisters handlers before calling
+ unpinConnection();
+ }
}
void
pinning.serverConnection = pinServer;
+ debugs(33, 3, HERE << pinning.serverConnection);
+
// when pinning an SSL bumped connection, the request may be NULL
const char *pinnedHost = "[unknown]";
if (request) {
typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
pinning.closeHandler = JobCallback(33, 5,
Dialer, this, ConnStateData::clientPinnedConnectionClosed);
+ // remember the pinned connection so that cb does not unpin a fresher one
+ typedef CommCloseCbParams Params;
+ Params ¶ms = GetCommParams<Params>(pinning.closeHandler);
+ params.conn = pinning.serverConnection;
comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
}
const Comm::ConnectionPointer
ConnStateData::validatePinnedConnection(HttpRequest *request, const struct peer *aPeer)
{
+ debugs(33, 7, HERE << pinning.serverConnection);
+
bool valid = true;
if (!Comm::IsConnOpen(pinning.serverConnection))
valid = false;
void
ConnStateData::unpinConnection()
{
+ debugs(33, 3, HERE << pinning.serverConnection);
+
if (pinning.peer)
cbdataReferenceDone(pinning.peer);
entry = e;
clientConn = client;
request = HTTPMSGLOCK(r);
+ pconnRace = raceImpossible;
start_t = squid_curtime;
serverDestinations.reserve(Config.forward_max_tries);
e->lock();
if (!errorState->request)
errorState->request = HTTPMSGLOCK(request);
+ if (pconnRace == racePossible && err->type == ERR_ZERO_SIZE_OBJECT) {
+ debugs(17, 5, HERE << "pconn race happened");
+ pconnRace = raceHappened;
+ }
+
#if USE_SSL
if (errorState->type == ERR_SECURE_CONNECT_FAIL && errorState->detail)
request->detailError(errorState->type, errorState->detail->errorNo());
{
if (checkRetry()) {
debugs(17, 3, HERE << "re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
- serverDestinations.shift(); // last one failed. try another.
+ // we should retry the same destination if it failed due to pconn race
+ if (pconnRace == raceHappened)
+ debugs(17, 4, HERE << "retrying the same destination");
+ else
+ serverDestinations.shift(); // last one failed. try another.
startConnectionOrFail();
return;
}
if (serverConnection()->getPeer())
peerConnectSucceded(serverConnection()->getPeer());
+ if (request->flags.canRePin && request->clientConnectionManager.valid()) {
+ debugs(17, 3, HERE << "repinning " << serverConn);
+ request->clientConnectionManager->pinConnection(serverConn,
+ request, serverConn->getPeer(), request->flags.auth);
+ }
+
#if USE_SSL
if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
(!serverConnection()->getPeer() && request->protocol == AnyP::PROTO_HTTPS)) {
// XXX: also, logs will now lie if pinning is broken and leads to an error message.
if (serverDestinations[0]->peerType == PINNED) {
ConnStateData *pinned_connection = request->pinnedConnection();
- assert(pinned_connection);
- serverConn = pinned_connection->validatePinnedConnection(request, serverDestinations[0]->getPeer());
+ // pinned_connection may become nil after a pconn race
+ if (pinned_connection)
+ serverConn = pinned_connection->validatePinnedConnection(request, serverDestinations[0]->getPeer());
+ else
+ serverConn = NULL;
if (Comm::IsConnOpen(serverConn)) {
#if 0
if (!serverConn->getPeer())
if (pinned_connection->pinnedAuth())
request->flags.auth = 1;
comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
+ // the server may close the pinned connection before this request
+ pconnRace = racePossible;
dispatch();
return;
}
- /* Failure. Fall back on next path */
- debugs(17,2,HERE << " Pinned connection " << pinned_connection << " not valid.");
- serverDestinations.shift();
- startConnectionOrFail();
- return;
+ /* Failure. Fall back on next path unless we can re-pin */
+ debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection);
+ if (pconnRace != raceHappened || !request->flags.canRePin) {
+ serverDestinations.shift();
+ pconnRace = raceImpossible;
+ startConnectionOrFail();
+ return;
+ }
+ debugs(17,3, HERE << "There was a pconn race. Will try to repin.");
+ // and fall through to regular handling
}
// Use pconn to avoid opening a new connection.
} else {
host = request->GetHost();
}
- Comm::ConnectionPointer temp = fwdPconnPool->pop(serverDestinations[0], host, checkRetriable());
+
+ Comm::ConnectionPointer temp;
+ // Avoid pconns after races so that the same client does not suffer twice.
+ // This does not increase the total number of connections because we just
+ // closed the connection that failed the race. And re-pinning assumes this.
+ if (pconnRace != raceHappened)
+ temp = fwdPconnPool->pop(serverDestinations[0], host, checkRetriable());
+
+ const bool openedPconn = Comm::IsConnOpen(temp);
+ pconnRace = openedPconn ? racePossible : raceImpossible;
// if we found an open persistent connection to use. use it.
- if (Comm::IsConnOpen(temp)) {
+ if (openedPconn) {
serverConn = temp;
debugs(17, 3, HERE << "reusing pconn " << serverConnection());
n_tries++;
return;
}
+ // We will try to open a new connection, possibly to the same destination.
+ // We reset serverDestinations[0] in case we are using it again because
+ // ConnOpener modifies its destination argument.
+ serverDestinations[0]->local.SetPort(0);
+ serverConn = NULL;
+
#if URL_CHECKSUM_DEBUG
entry->mem_obj->checkUrlChecksum();
#endif
struct request_flags {
- request_flags(): range(0),nocache(0),ims(0),auth(0),cachable(0),hierarchical(0),loopdetect(0),proxy_keepalive(0),proxying(0),refresh(0),redirected(0),need_validation(0),fail_on_validation_err(0),stale_if_hit(0),accelerated(0),ignore_cc(0),intercepted(0),spoof_client_ip(0),internal(0),internalclient(0),must_keepalive(0),chunked_reply(0),stream_error(0),sslPeek(0),sslBumped(0),destinationIPLookedUp_(0) {
+ request_flags(): range(0),nocache(0),ims(0),auth(0),cachable(0),hierarchical(0),loopdetect(0),proxy_keepalive(0),proxying(0),refresh(0),redirected(0),need_validation(0),fail_on_validation_err(0),stale_if_hit(0),accelerated(0),ignore_cc(0),intercepted(0),spoof_client_ip(0),internal(0),internalclient(0),must_keepalive(0),pinned(0),canRePin(0),chunked_reply(0),stream_error(0),sslPeek(0),sslBumped(0),destinationIPLookedUp_(0) {
#if USE_HTTP_VIOLATIONS
nocache_hack = 0;
#endif
unsigned int connection_auth_disabled:1; /** Connection oriented auth can not be supported */
unsigned int connection_proxy_auth:1; /** Request wants connection oriented auth */
unsigned int pinned:1; /* Request sent on a pinned connection */
+ unsigned int canRePin:1; ///< OK to reopen a failed pinned connection
unsigned int auth_sent:1; /* Authentication forwarded */
unsigned int no_direct:1; /* Deny direct forwarding unless overriden by always_direct. Used in accelerator mode */
unsigned int chunked_reply:1; /**< Reply with chunked transfer encoding */