request->flags.accelerated = http->flags.accel;
request->flags.sslBumped=conn->switchedToHttps();
- request->flags.canRePin = request->flags.sslBumped && conn->pinning.pinned;
request->flags.ignoreCc = conn->port->ignore_cc;
// TODO: decouple http->flags.accel from request->flags.sslBumped
request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
stoppedSending_(NULL),
stoppedReceiving_(NULL)
{
+ pinning.host = NULL;
+ pinning.port = -1;
pinning.pinned = false;
pinning.auth = false;
+ pinning.zeroReply = false;
+ pinning.peer = NULL;
}
bool
{
// FwdState might 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();
+ assert(pinning.serverConnection == io.conn);
+ pinning.closeHandler = NULL; // Comm unregisters handlers before calling
+ const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
+ unpinConnection();
+ if (sawZeroReply) {
+ debugs(33, 3, "Closing client connection on pinned zero reply.");
+ clientConnection->close();
}
}
safe_free(pinning.host);
+ pinning.zeroReply = false;
+
/* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
* connection has gone away */
}
// We do not really have to close, but we pretend we are a tunnel.
debugs(88, 3, "clientBuildReplyHeader: bumped reply forces close");
request->flags.proxyKeepalive = 0;
+ } else if (request->pinnedConnection() && !reply->persistent()) {
+ // The peer wants to close the pinned connection
+ debugs(88, 3, "pinned reply forces close");
+ request->flags.proxyKeepalive = 0;
}
// Decide if we send chunked reply
debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
return;
}
+ if (conn->pinning.zeroReply) {
+ debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
+ return;
+ }
char *buf = next()->readBuffer.data;
{
// use pinned connection if available
Comm::ConnectionPointer p;
- if (ConnStateData *client = request->pinnedConnection())
+ if (ConnStateData *client = request->pinnedConnection()) {
p = client->validatePinnedConnection(request, NULL);
+ if (Comm::IsConnOpen(p)) {
+ /* duplicate peerSelectPinned() effects */
+ p->peerType = PINNED;
+ entry->ping_status = PING_DONE; /* Skip ICP */
- if (Comm::IsConnOpen(p)) {
- /* duplicate peerSelectPinned() effects */
- p->peerType = PINNED;
- entry->ping_status = PING_DONE; /* Skip ICP */
-
- debugs(17, 3, HERE << "reusing a pinned conn: " << *p);
- serverDestinations.push_back(p);
+ debugs(17, 3, "reusing a pinned conn: " << *p);
+ serverDestinations.push_back(p);
+ } else {
+ debugs(17,2, "Pinned connection is not valid: " << p);
+ ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, request);
+ fail(anErr);
+ }
+ // Either use the valid pinned connection or fail if it is invalid.
+ return;
}
// use client original destination as second preferred choice
if (!errorState->request)
errorState->request = HTTPMSGLOCK(request);
- if (pconnRace == racePossible && err->type == ERR_ZERO_SIZE_OBJECT) {
+ if (err->type != ERR_ZERO_SIZE_OBJECT)
+ return;
+
+ if (pconnRace == racePossible) {
debugs(17, 5, HERE << "pconn race happened");
pconnRace = raceHappened;
}
+
+ if (ConnStateData *pinned_connection = request->pinnedConnection()) {
+ pinned_connection->pinning.zeroReply = true;
+ flags.dont_retry = true; // we want to propagate failure to the client
+ debugs(17, 4, "zero reply on pinned connection");
+ }
}
/**
if (serverConnection()->getPeer())
peerConnectSucceded(serverConnection()->getPeer());
- // some requests benefit from pinning but do not require it and can "repin"
- const bool rePin = request->flags.canRePin &&
- request->clientConnectionManager.valid();
- if (rePin) {
- debugs(17, 3, HERE << "repinning " << serverConn);
- request->clientConnectionManager->pinConnection(serverConn,
- request, serverConn->getPeer(), request->flags.auth);
- request->flags.pinned = 1;
- }
-
#if USE_SSL
- if (!request->flags.pinned || rePin) {
+ if (!request->flags.pinned) {
if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
(!serverConnection()->getPeer() && request->protocol == AnyP::PROTO_HTTPS) ||
request->flags.sslPeek) {
// 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();
+ debugs(17,7, "pinned peer connection: " << pinned_connection);
// pinned_connection may become nil after a pconn race
if (pinned_connection)
serverConn = pinned_connection->validatePinnedConnection(request, serverDestinations[0]->getPeer());
dispatch();
return;
}
- /* Failure. Fall back on next path unless we can re-pin */
+ // Pinned connection failure.
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
+ ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, request);
+ fail(anErr);
+ self = NULL; // refcounted
+ return;
}
// Use pconn to avoid opening a new connection.