#if USE_OPENSSL
if (request->flags.sslPeek && request->clientConnectionManager.valid()) {
CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
- ConnStateData::httpsPeeked, Comm::ConnectionPointer(NULL));
+ ConnStateData::httpsPeeked, ConnStateData::PinnedIdleContext(Comm::ConnectionPointer(nullptr), request));
}
#endif
} else {
#if USE_OPENSSL
if (request->flags.sslPeek) {
CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
- ConnStateData::httpsPeeked, serverConnection());
+ ConnStateData::httpsPeeked, ConnStateData::PinnedIdleContext(serverConnection(), request));
unregister(serverConn); // async call owns it now
complete(); // destroys us
return;
C & operator * () const {return *const_cast<C *>(p_); }
- C const * getRaw() const {return p_; }
-
- C * getRaw() {return const_cast<C *>(p_); }
+ C * getRaw() const { return const_cast<C *>(p_); }
bool operator == (const RefCount& p) const {
return p.p_ == p_;
assert(areAllContextsForThisConnection());
freeAllContexts();
+ // XXX: Closing pinned conn is too harsh: The Client may want to continue!
unpinConnection(true);
if (Comm::IsConnOpen(clientConnection))
debugs(33, 3, HERE << "ConnnStateData(" << conn->clientConnection << "), Context(" << clientConnection << ")");
connIsFinished();
+ conn->kick();
+}
+
+void
+ConnStateData::kick()
+{
+ ConnStateData * conn = this; // XXX: Remove this diff minimization hack
if (conn->pinning.pinned && !Comm::IsConnOpen(conn->pinning.serverConnection)) {
debugs(33, 2, HERE << conn->clientConnection << " Connection was pinned but server side gone. Terminating client connection");
if (in.buf.isEmpty())
break;
+ // Prohibit concurrent requests when using a pinned to-server connection
+ // because our Client classes do not support request pipelining.
+ if (pinning.pinned && !pinning.readHandler) {
+ debugs(33, 3, clientConnection << " waits for busy " << pinning.serverConnection);
+ break;
+ }
+
/* Limit the number of concurrent requests */
if (concurrentRequestQueueFilled())
break;
}
void
-ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
+ConnStateData::httpsPeeked(PinnedIdleContext pic)
{
Must(sslServerBump != NULL);
+ Must(sslServerBump->request == pic.request);
+ Must(currentobject == NULL || currentobject->http == NULL || currentobject->http->request == pic.request.getRaw());
- if (Comm::IsConnOpen(serverConnection)) {
- pinConnection(serverConnection, NULL, NULL, false);
+ if (Comm::IsConnOpen(pic.connection)) {
+ notePinnedConnectionBecameIdle(pic);
debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
- } else {
+ } else
debugs(33, 5, HERE << "Error while bumping: " << sslConnectHostOrIp);
- // copy error detail from bump-server-first request to CONNECT request
- if (currentobject != NULL && currentobject->http != NULL && currentobject->http->request)
- currentobject->http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
- }
-
getSslContextStart();
}
}
void
-ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth, bool monitor)
+ConnStateData::pinBusyConnection(const Comm::ConnectionPointer &pinServer, const HttpRequest::Pointer &request)
{
- if (!Comm::IsConnOpen(pinning.serverConnection) ||
- pinning.serverConnection->fd != pinServer->fd)
- pinNewConnection(pinServer, request, aPeer, auth);
+ pinConnection(pinServer, request);
+}
- if (monitor)
- startPinnedConnectionMonitoring();
+void
+ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext pic)
+{
+ Must(pic.connection != NULL);
+ Must(pic.request != NULL);
+ pinConnection(pic.connection, pic.request);
+
+ // monitor pinned server connection for remote-end closures.
+ startPinnedConnectionMonitoring();
+
+ if (!currentobject)
+ kick(); // in case clientParseRequests() was blocked by a busy pic.connection
}
+/// Forward future client requests using the given server connection.
void
-ConnStateData::pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth)
+ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, const HttpRequest::Pointer &request)
{
+ if (Comm::IsConnOpen(pinning.serverConnection) &&
+ pinning.serverConnection->fd == pinServer->fd) {
+ debugs(33, 3, "already pinned" << pinServer);
+ return;
+ }
+
unpinConnection(true); // closes pinned connection, if any, and resets fields
pinning.serverConnection = pinServer;
Must(pinning.serverConnection != NULL);
- // when pinning an SSL bumped connection, the request may be NULL
const char *pinnedHost = "[unknown]";
- if (request) {
+ if (request != NULL) {
pinning.host = xstrdup(request->GetHost());
pinning.port = request->port;
pinnedHost = pinning.host;
+ pinning.auth = request->flags.connectionAuth;
} else {
pinning.port = pinServer->remote.port();
}
pinning.pinned = true;
- if (aPeer)
- pinning.peer = cbdataReference(aPeer);
- pinning.auth = auth;
+ pinning.peer = cbdataReference(pinServer->getPeer());
char stmp[MAX_IPSTRLEN];
char desc[FD_DESC_SZ];
snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
- (auth || !aPeer) ? pinnedHost : aPeer->name,
+ (pinning.auth || !pinning.peer) ? pinnedHost : pinning.peer->name,
clientConnection->remote.toUrl(stmp,MAX_IPSTRLEN),
clientConnection->fd);
fd_note(pinning.serverConnection->fd, desc);
* connection has gone away */
}
+std::ostream &
+operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
+{
+ return os << pic.connection << ", request=" << pic.request;
+}
#include "ssl/support.h"
#endif
+#include <iosfwd>
+
class ConnStateData;
class ClientHttpRequest;
class clientStreamNode;
/// Traffic parsing
bool clientParseRequests();
void readNextRequest();
+
+ // In v3.5, usually called via ClientSocketContext::keepaliveNextRequest().
+ /// try to make progress on a transaction or read more I/O
+ void kick();
+
ClientSocketContext::Pointer getCurrentContext() const;
void addContextToQueue(ClientSocketContext * context);
int getConcurrentRequestCount() const;
bool handleReadData();
bool handleRequestBodyData();
- /// Forward future client requests using the given server connection.
- /// Optionally, monitor pinned server connection for remote-end closures.
- void pinConnection(const Comm::ConnectionPointer &pinServerConn, HttpRequest *request, CachePeer *peer, bool auth, bool monitor = true);
+ /// parameters for the async notePinnedConnectionBecameIdle() call
+ class PinnedIdleContext
+ {
+ public:
+ PinnedIdleContext(const Comm::ConnectionPointer &conn, const HttpRequest::Pointer &req): connection(conn), request(req) {}
+
+ Comm::ConnectionPointer connection; ///< to-server connection to be pinned
+ HttpRequest::Pointer request; ///< to-server request that initiated serverConnection
+ };
+
+ /// Called when a pinned connection becomes available for forwarding the next request.
+ void notePinnedConnectionBecameIdle(PinnedIdleContext pic);
+ /// Forward future client requests using the given to-server connection.
+ /// The connection is still being used by the current client request.
+ void pinBusyConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request);
/// Undo pinConnection() and, optionally, close the pinned connection.
void unpinConnection(const bool andClose);
/// Returns validated pinnned server connection (and stops its monitoring).
/// generated
void doPeekAndSpliceStep();
/// called by FwdState when it is done bumping the server
- void httpsPeeked(Comm::ConnectionPointer serverConnection);
+ void httpsPeeked(PinnedIdleContext pic);
/// Start to create dynamic SSL_CTX for host or uses static port SSL context.
void getSslContextStart();
void clientAfterReadingRequests();
bool concurrentRequestQueueFilled() const;
- void pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth);
+ void pinConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request);
/* PROXY protocol functionality */
bool proxyProtocolValidateClient();
void clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, Http::ProtocolVersion http_ver);
void clientPostHttpsAccept(ConnStateData *connState);
+std::ostream &operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic);
+
#endif /* SQUID_CLIENTSIDE_H */
mgr->unpinConnection(false);
ctrl.close();
} else {
- mgr->pinConnection(ctrl.conn, fwd->request,
- ctrl.conn->getPeer(),
- fwd->request->flags.connectionAuth);
+ CallJobHere1(9, 4, mgr,
+ ConnStateData,
+ notePinnedConnectionBecameIdle,
+ ConnStateData::PinnedIdleContext(ctrl.conn, fwd->request));
ctrl.forget();
}
}
void
HttpStateData::processReplyBody()
{
- Ip::Address client_addr;
- bool ispinned = false;
-
if (!flags.headers_parsed) {
flags.do_next_read = true;
maybeReadVirginBody();
}
break;
- case COMPLETE_PERSISTENT_MSG:
+ case COMPLETE_PERSISTENT_MSG: {
debugs(11, 5, "processReplyBody: COMPLETE_PERSISTENT_MSG from " << serverConnection);
- /* yes we have to clear all these! */
+
+ // TODO: Remove serverConnectionSaved but preserve exception safety.
+
commUnsetConnTimeout(serverConnection);
flags.do_next_read = false;
comm_remove_close_handler(serverConnection->fd, closeHandler);
closeHandler = NULL;
- fwd->unregister(serverConnection);
+ Ip::Address client_addr; // XXX: Remove as unused. Why was it added?
if (request->flags.spoofClientIp)
client_addr = request->client_addr;
+ Comm::ConnectionPointer serverConnectionSaved = serverConnection;
+ fwd->unregister(serverConnection);
+ serverConnection = nullptr;
+
+ bool ispinned = false; // TODO: Rename to isOrShouldBePinned
if (request->flags.pinned) {
ispinned = true;
} else if (request->flags.connectionAuth && request->flags.authSent) {
ispinned = true;
}
- if (ispinned && request->clientConnectionManager.valid()) {
- request->clientConnectionManager->pinConnection(serverConnection, request, _peer,
- (request->flags.connectionAuth));
+ if (ispinned) {
+ if (request->clientConnectionManager.valid()) {
+ CallJobHere1(11, 4, request->clientConnectionManager,
+ ConnStateData,
+ notePinnedConnectionBecameIdle,
+ ConnStateData::PinnedIdleContext(serverConnectionSaved, request));
+ } else {
+ // must not pool/share ispinned connections, even orphaned ones
+ serverConnectionSaved->close();
+ }
} else {
- fwd->pconnPush(serverConnection, request->GetHost());
+ fwd->pconnPush(serverConnectionSaved, request->GetHost());
}
- serverConnection = NULL;
serverComplete();
return;
+ }
case COMPLETE_NONPERSISTENT_MSG:
debugs(11, 5, "processReplyBody: COMPLETE_NONPERSISTENT_MSG from " << serverConnection);
Must(http != NULL);
HttpRequest *const request = http->request;
Must(request != NULL);
-
- // this is not an idle connection, so we do not want I/O monitoring
- const bool monitor = false;
-
// make FTP peer connection exclusive to our request
- pinConnection(conn, request, conn->getPeer(), false, monitor);
+ pinBusyConnection(conn, request);
}
void
void ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer) STUB
bool ConnStateData::handleReadData() STUB_RETVAL(false)
bool ConnStateData::handleRequestBodyData() STUB_RETVAL(false)
-void ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServerConn, HttpRequest *request, CachePeer *peer, bool auth, bool monitor) STUB
+void ConnStateData::pinBusyConnection(const Comm::ConnectionPointer &, const HttpRequest::Pointer &) STUB
+void ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext) STUB
void ConnStateData::unpinConnection(const bool andClose) STUB
const Comm::ConnectionPointer ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *peer) STUB_RETVAL(NULL)
void ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io) STUB
void ConnStateData::swanSong() STUB
void ConnStateData::quitAfterError(HttpRequest *request) STUB
#if USE_OPENSSL
-void ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection) STUB
+void ConnStateData::httpsPeeked(PinnedIdleContext) STUB
void ConnStateData::getSslContextStart() STUB
void ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew) STUB
void ConnStateData::sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply) STUB