From: Christos Tsantilas Date: Fri, 15 Feb 2019 15:50:45 +0000 (+0000) Subject: Bug 4864: !Comm::MonitorsRead assertion in maybeReadVirginBody() (#351) X-Git-Tag: M-staged-PR351 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3dde9e52be90ec9bef2aac67a22f792a5f5dfdad;p=thirdparty%2Fsquid.git Bug 4864: !Comm::MonitorsRead assertion in maybeReadVirginBody() (#351) This assertion is probably triggered when Squid retries/reforwards server-first or step2+ bumped connections (after they fail). Retrying/reforwarding such pinned connections is wrong because the corresponding client-to-Squid TLS connection was negotiated based on the now-failed Squid-to-server TLS connection, and there is no mechanism to ensure that the new Squid-to-server TLS connection will have exactly the same properties. Squid should forward the error to client instead. Also fixed peer selection code that could return more than one PINNED paths with only the first path having the destination of the actual pinned connection. To reduce the chances of similar future bugs, and to polish the code, peer selection now returns a nil path to indicate a PINNED decision. After all, the selection code decides to use a pinned connection (whatever it is) rather than a specific pinned _destination_. This is a Measurement Factory project. --- diff --git a/src/FwdState.cc b/src/FwdState.cc index d4a30e93ec..301da5a4a6 100644 --- a/src/FwdState.cc +++ b/src/FwdState.cc @@ -165,6 +165,9 @@ void FwdState::start(Pointer aSelf) if (!request->flags.ftpNative) entry->registerAbort(FwdState::abort, this); + // just in case; should already be initialized to false + request->flags.pinned = false; + #if STRICT_ORIGINAL_DST // Bug 3243: CVE 2009-0801 // Bypass of browser same-origin access control in intercepted communication @@ -173,9 +176,7 @@ void FwdState::start(Pointer aSelf) const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified); if (isIntercepted && useOriginalDst) { selectPeerForIntercepted(); - // 3.2 does not suppro re-wrapping inside CONNECT. - // our only alternative is to fake destination "found" and continue with the forwarding. - startConnectionOrFail(); + useDestinations(); return; } #endif @@ -199,28 +200,20 @@ FwdState::stopAndDestroy(const char *reason) void FwdState::selectPeerForIntercepted() { + // We do not support re-wrapping inside CONNECT. + // Our only alternative is to fake a noteDestination() call. + // use pinned connection if available - Comm::ConnectionPointer p; 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 */ - - 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::scServiceUnavailable, request); - fail(anErr); - } - // Either use the valid pinned connection or fail if it is invalid. + // emulate the PeerSelector::selectPinned() "Skip ICP" effect + entry->ping_status = PING_DONE; + + serverDestinations.push_back(nullptr); return; } // use client original destination as second preferred choice - p = new Comm::Connection(); + const auto p = new Comm::Connection(); p->peerType = ORIGINAL_DST; p->remote = clientConn->local; getOutgoingAddress(request, p); @@ -421,24 +414,14 @@ FwdState::EnoughTimeToReForward(const time_t fwdStart) } void -FwdState::startConnectionOrFail() +FwdState::useDestinations() { - debugs(17, 3, HERE << entry->url()); - - if (serverDestinations.size() > 0) { - // Ditch error page if it was created before. - // A new one will be created if there's another problem - delete err; - err = NULL; - - // Update the logging information about this new server connection. - // Done here before anything else so the errors get logged for - // this server link regardless of what happens when connecting to it. - // IF sucessfuly connected this top destination will become the serverConnection(). - syncHierNote(serverDestinations[0], request->url.host()); - request->clearError(); - - connectStart(); + debugs(17, 3, serverDestinations.size() << " paths to " << entry->url()); + if (!serverDestinations.empty()) { + if (!serverDestinations[0]) + usePinned(); + else + connectStart(); } else { if (PeerSelectionInitiator::subscribed) { debugs(17, 4, "wait for more destinations to try"); @@ -476,7 +459,6 @@ FwdState::fail(ErrorState * errorState) 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"); } } @@ -532,7 +514,7 @@ FwdState::complete() // drop the last path off the selection list. try the next one. if (!serverDestinations.empty()) // paranoid serverDestinations.erase(serverDestinations.begin()); - startConnectionOrFail(); + useDestinations(); } else { if (Comm::IsConnOpen(serverConn)) @@ -552,9 +534,14 @@ void FwdState::noteDestination(Comm::ConnectionPointer path) { const bool wasBlocked = serverDestinations.empty(); + // XXX: Push even a nil path so that subsequent noteDestination() calls + // can rely on wasBlocked to detect ongoing/concurrent attempts. + // Upcoming Happy Eyeballs changes will handle this properly. serverDestinations.push_back(path); + assert(wasBlocked || path); // pinned destinations are always selected first + if (wasBlocked) - startConnectionOrFail(); + useDestinations(); // else continue to use one of the previously noted destinations; // if all of them fail, we may try this path } @@ -575,7 +562,7 @@ FwdState::noteDestinationsEnd(ErrorState *selectionError) else debugs(17, 3, "Will abort forwarding because path selection found no paths."); - startConnectionOrFail(); // will choose the OrFail code path + useDestinations(); // will detect and handle the lack of paths return; } // else continue to use one of the previously noted destinations; @@ -629,6 +616,9 @@ FwdState::checkRetry() if (exhaustedTries()) return false; + if (request->flags.pinned && !pinnedCanRetry()) + return false; + if (!EnoughTimeToReForward(start_t)) return false; @@ -683,7 +673,7 @@ FwdState::retryOrBail() debugs(17, 4, HERE << "retrying the same destination"); else serverDestinations.erase(serverDestinations.begin()); // last one failed. try another. - startConnectionOrFail(); + useDestinations(); return; } @@ -743,32 +733,34 @@ FwdState::connectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, in closeHandler = comm_add_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this); - if (!request->flags.pinned) { - const CachePeer *p = serverConnection()->getPeer(); - const bool peerWantsTls = p && p->secure.encryptTransport; - // userWillTlsToPeerForUs assumes CONNECT == HTTPS - const bool userWillTlsToPeerForUs = p && p->options.originserver && - request->method == Http::METHOD_CONNECT; - const bool needTlsToPeer = peerWantsTls && !userWillTlsToPeerForUs; - const bool needTlsToOrigin = !p && request->url.getScheme() == AnyP::PROTO_HTTPS; - if (needTlsToPeer || needTlsToOrigin || request->flags.sslPeek) { - HttpRequest::Pointer requestPointer = request; - AsyncCall::Pointer callback = asyncCall(17,4, - "FwdState::ConnectedToPeer", - FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this)); - // Use positive timeout when less than one second is left. - const time_t connTimeout = serverDestinations[0]->connectTimeout(start_t); - const time_t sslNegotiationTimeout = positiveTimeout(connTimeout); - Security::PeerConnector *connector = nullptr; + // request->flags.pinned cannot be true in connectDone(). The flag is + // only set when we dispatch the request to an existing (pinned) connection. + assert(!request->flags.pinned); + + const CachePeer *p = serverConnection()->getPeer(); + const bool peerWantsTls = p && p->secure.encryptTransport; + // userWillTlsToPeerForUs assumes CONNECT == HTTPS + const bool userWillTlsToPeerForUs = p && p->options.originserver && + request->method == Http::METHOD_CONNECT; + const bool needTlsToPeer = peerWantsTls && !userWillTlsToPeerForUs; + const bool needTlsToOrigin = !p && request->url.getScheme() == AnyP::PROTO_HTTPS; + if (needTlsToPeer || needTlsToOrigin || request->flags.sslPeek) { + HttpRequest::Pointer requestPointer = request; + AsyncCall::Pointer callback = asyncCall(17,4, + "FwdState::ConnectedToPeer", + FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this)); + // Use positive timeout when less than one second is left. + const time_t connTimeout = serverDestinations[0]->connectTimeout(start_t); + const time_t sslNegotiationTimeout = positiveTimeout(connTimeout); + Security::PeerConnector *connector = nullptr; #if USE_OPENSSL - if (request->flags.sslPeek) - connector = new Ssl::PeekingPeerConnector(requestPointer, serverConnection(), clientConn, callback, al, sslNegotiationTimeout); - else + if (request->flags.sslPeek) + connector = new Ssl::PeekingPeerConnector(requestPointer, serverConnection(), clientConn, callback, al, sslNegotiationTimeout); + else #endif - connector = new Security::BlindPeerConnector(requestPointer, serverConnection(), callback, al, sslNegotiationTimeout); - AsyncJob::Start(connector); // will call our callback - return; - } + connector = new Security::BlindPeerConnector(requestPointer, serverConnection(), callback, al, sslNegotiationTimeout); + AsyncJob::Start(connector); // will call our callback + return; } // if not encrypting just run the post-connect actions @@ -867,6 +859,22 @@ FwdState::connectStart() debugs(17, 3, "fwdConnectStart: " << entry->url()); + // pinned connections go through usePinned() rather than connectStart() + assert(serverDestinations[0] != nullptr); + request->flags.pinned = false; + + // Ditch the previous error if any. + // A new error page will be created if there is another problem. + delete err; + err = nullptr; + request->clearError(); + + // Update logging information with the upcoming server connection + // destination. Do this early so that any connection establishment errors + // are attributed to this destination. If successfully connected, this + // destination becomes serverConnection(). + syncHierNote(serverDestinations[0], request->url.host()); + request->hier.startPeerClock(); // Do not fowrward bumped connections to parent proxy unless it is an @@ -879,40 +887,6 @@ FwdState::connectStart() return; } - request->flags.pinned = false; // XXX: what if the ConnStateData set this to flag existing credentials? - // XXX: answer: the peer selection *should* catch it and give us only the pinned peer. so we reverse the =0 step below. - // 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 - serverConn = pinned_connection ? pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer()) : nullptr; - if (Comm::IsConnOpen(serverConn)) { - flags.connected_okay = true; - ++n_tries; - request->flags.pinned = true; - - if (pinned_connection->pinnedAuth()) - request->flags.auth = true; - - closeHandler = comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this); - - syncWithServerConn(pinned_connection->pinning.host); - - // the server may close the pinned connection before this request - pconnRace = racePossible; - dispatch(); - return; - } - - // Pinned connection failure. - debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection); - ErrorState *anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request); - fail(anErr); - stopAndDestroy("pinned connection failure"); - return; - } - // Use pconn to avoid opening a new connection. const char *host = NULL; if (!serverDestinations[0]->getPeer()) @@ -964,6 +938,51 @@ FwdState::connectStart() AsyncJob::Start(cs); } +/// send request on an existing connection dedicated to the requesting client +void +FwdState::usePinned() +{ + // we only handle pinned destinations; others are handled by connectStart() + assert(!serverDestinations.empty()); + assert(!serverDestinations[0]); + + const auto connManager = request->pinnedConnection(); + debugs(17, 7, "connection manager: " << connManager); + + // the client connection may close while we get here, nullifying connManager + const auto temp = connManager ? connManager->borrowPinnedConnection(request) : nullptr; + debugs(17, 5, "connection: " << temp); + + // the previously pinned idle peer connection may get closed (by the peer) + if (!Comm::IsConnOpen(temp)) { + syncHierNote(temp, connManager ? connManager->pinning.host : request->url.host()); + serverConn = nullptr; + const auto anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request); + fail(anErr); + // Connection managers monitor their idle pinned to-server + // connections and close from-client connections upon seeing + // a to-server connection closure. Retrying here is futile. + stopAndDestroy("pinned connection failure"); + return; + } + + serverConn = temp; + flags.connected_okay = true; + ++n_tries; + request->flags.pinned = true; + + if (connManager->pinnedAuth()) + request->flags.auth = true; + + closeHandler = comm_add_close_handler(temp->fd, fwdServerClosedWrapper, this); + + syncWithServerConn(connManager->pinning.host); + + // the server may close the pinned connection before this request + pconnRace = racePossible; + dispatch(); +} + void FwdState::dispatch() { @@ -1107,6 +1126,11 @@ FwdState::reforward() debugs(17, 3, HERE << e->url() << "?" ); + if (request->flags.pinned && !pinnedCanRetry()) { + debugs(17, 3, "pinned connection; cannot retry"); + return 0; + } + if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { debugs(17, 3, HERE << "No, ENTRY_FWD_HDR_WAIT isn't set"); return 0; @@ -1268,6 +1292,28 @@ FwdState::exhaustedTries() const return n_tries >= Config.forward_max_tries; } +bool +FwdState::pinnedCanRetry() const +{ + assert(request->flags.pinned); + + // pconn race on pinned connection: Currently we do not have any mechanism + // to retry current pinned connection path. + if (pconnRace == raceHappened) + return false; + + // If a bumped connection was pinned, then the TLS client was given our peer + // details. Do not retry because we do not ensure that those details stay + // constant. Step1-bumped connections do not get our TLS peer details, are + // never pinned, and, hence, never reach this method. + if (request->flags.sslBumped) + return false; + + // The other pinned cases are FTP proxying and connection-based HTTP + // authentication. TODO: Do these cases have restrictions? + return true; +} + /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/ /* diff --git a/src/FwdState.h b/src/FwdState.h index 943981a721..57707026b7 100644 --- a/src/FwdState.h +++ b/src/FwdState.h @@ -78,7 +78,7 @@ public: /// This is the real beginning of server connection. Call it whenever /// the forwarding server destination has changed and a new one needs to be opened. /// Produces the cannot-forward error on fail if no better error exists. - void startConnectionOrFail(); + void useDestinations(); void fail(ErrorState *err); void unregister(Comm::ConnectionPointer &conn); @@ -123,6 +123,13 @@ private: void doneWithRetries(); void completed(); void retryOrBail(); + + void usePinned(); + + /// whether a pinned to-peer connection can be replaced with another one + /// (in order to retry or reforward a failed request) + bool pinnedCanRetry() const; + ErrorState *makeConnectingError(const err_type type) const; void connectedToPeer(Security::EncryptorAnswer &answer); static void RegisterWithCacheManager(void); diff --git a/src/PeerSelectState.h b/src/PeerSelectState.h index 14b5402bff..f168a8158a 100644 --- a/src/PeerSelectState.h +++ b/src/PeerSelectState.h @@ -79,7 +79,7 @@ public: bool wantsMoreDestinations() const; /// processes a newly discovered/finalized path - void handlePath(Comm::ConnectionPointer &path, FwdServer &fs); + void handlePath(const Comm::ConnectionPointer &path, FwdServer &fs); /// a single selection loop iteration: attempts to add more destinations void selectMore(); diff --git a/src/client_side.cc b/src/client_side.cc index e4285eedd6..2ee7518c13 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -3875,7 +3875,7 @@ ConnStateData::clientPinnedConnectionRead(const CommIoCbParams &io) } const Comm::ConnectionPointer -ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *aPeer) +ConnStateData::validatePinnedConnection(HttpRequest *request) { debugs(33, 7, HERE << pinning.serverConnection); @@ -3888,8 +3888,6 @@ ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *a valid = false; else if (pinning.peer && !cbdataReferenceValid(pinning.peer)) valid = false; - else if (aPeer != pinning.peer) - valid = false; if (!valid) { /* The pinning info is not safe, remove any pinning info */ @@ -3900,10 +3898,10 @@ ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *a } Comm::ConnectionPointer -ConnStateData::borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer) +ConnStateData::borrowPinnedConnection(HttpRequest *request) { debugs(33, 7, pinning.serverConnection); - if (validatePinnedConnection(request, aPeer) != NULL) + if (validatePinnedConnection(request) != nullptr) stopPinnedConnectionMonitoring(); return pinning.serverConnection; // closed if validation failed diff --git a/src/client_side.h b/src/client_side.h index 19530e72c8..ba955dc8ae 100644 --- a/src/client_side.h +++ b/src/client_side.h @@ -182,15 +182,14 @@ public: /// Undo pinConnection() and, optionally, close the pinned connection. void unpinConnection(const bool andClose); /// Returns validated pinnned server connection (and stops its monitoring). - Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer); + Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *request); /** * Checks if there is pinning info if it is valid. It can close the server side connection * if pinned info is not valid. \param request if it is not NULL also checks if the pinning info refers to the request client side HttpRequest - \param CachePeer if it is not NULL also check if the CachePeer is the pinning CachePeer \return The details of the server side connection (may be closed if failures were present). */ - const Comm::ConnectionPointer validatePinnedConnection(HttpRequest *request, const CachePeer *peer); + const Comm::ConnectionPointer validatePinnedConnection(HttpRequest *request); /** * returts the pinned CachePeer if exists, NULL otherwise */ diff --git a/src/peer_select.cc b/src/peer_select.cc index 5d9769df64..298c785c6d 100644 --- a/src/peer_select.cc +++ b/src/peer_select.cc @@ -301,6 +301,17 @@ PeerSelector::resolveSelected() return; } + if (fs && fs->code == PINNED) { + // Nil path signals a PINNED destination selection. Our initiator should + // borrow and use clientConnectionManager's pinned connection object + // (regardless of that connection destination). + handlePath(nullptr, *fs); + servers = fs->next; + delete fs; + resolveSelected(); + return; + } + // convert the list of FwdServer destinations into destinations IP addresses if (fs && wantsMoreDestinations()) { // send the next one off for DNS lookup. @@ -552,8 +563,9 @@ PeerSelector::selectPinned() // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough. if (!request->pinnedConnection()) return; - CachePeer *pear = request->pinnedConnection()->pinnedPeer(); - if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request, pear))) { + + if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request))) { + const auto pear = request->pinnedConnection()->pinnedPeer(); const bool usePinned = pear ? peerAllowedToUse(pear, this) : (direct != DIRECT_NO); if (usePinned) { addSelection(pear, PINNED); @@ -1003,19 +1015,22 @@ PeerSelector::wantsMoreDestinations() const { } void -PeerSelector::handlePath(Comm::ConnectionPointer &path, FwdServer &fs) +PeerSelector::handlePath(const Comm::ConnectionPointer &path, FwdServer &fs) { ++foundPaths; - path->peerType = fs.code; - path->setPeer(fs._peer.get()); + if (path) { + path->peerType = fs.code; + path->setPeer(fs._peer.get()); - // check for a configured outgoing address for this destination... - getOutgoingAddress(request, path); + // check for a configured outgoing address for this destination... + getOutgoingAddress(request, path); + debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url()); + } else + debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url()); request->hier.ping = ping; // may be updated later - debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url()); debugs(44, 2, " always_direct = " << always_direct); debugs(44, 2, " never_direct = " << never_direct); debugs(44, 2, " timedout = " << ping.timedout); diff --git a/src/tests/stub_client_side.cc b/src/tests/stub_client_side.cc index d43202cfea..75f2d8deec 100644 --- a/src/tests/stub_client_side.cc +++ b/src/tests/stub_client_side.cc @@ -34,7 +34,7 @@ bool ConnStateData::handleRequestBodyData() STUB_RETVAL(false) void ConnStateData::pinBusyConnection(const Comm::ConnectionPointer &, const HttpRequest::Pointer &) STUB void ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext) STUB void ConnStateData::unpinConnection(const bool) STUB -const Comm::ConnectionPointer ConnStateData::validatePinnedConnection(HttpRequest *, const CachePeer *) STUB_RETVAL(NULL) +const Comm::ConnectionPointer ConnStateData::validatePinnedConnection(HttpRequest *) STUB_RETVAL(nullptr) void ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &) STUB void ConnStateData::connStateClosed(const CommCloseCbParams &) STUB void ConnStateData::requestTimeout(const CommTimeoutCbParams &) STUB diff --git a/src/tunnel.cc b/src/tunnel.cc index 01b140873f..5af4a5665d 100644 --- a/src/tunnel.cc +++ b/src/tunnel.cc @@ -217,6 +217,8 @@ private: Security::EncryptorAnswer answer_; }; + void usePinned(); + /// callback handler after connection setup (including any encryption) void connectedToPeer(Security::EncryptorAnswer &answer); @@ -1205,11 +1207,11 @@ tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) } static Comm::ConnectionPointer -borrowPinnedConnection(HttpRequest *request, Comm::ConnectionPointer &serverDestination) +borrowPinnedConnection(HttpRequest *request) { // pinned_connection may become nil after a pconn race if (ConnStateData *pinned_connection = request ? request->pinnedConnection() : nullptr) { - Comm::ConnectionPointer serverConn = pinned_connection->borrowPinnedConnection(request, serverDestination->getPeer()); + Comm::ConnectionPointer serverConn = pinned_connection->borrowPinnedConnection(request); return serverConn; } @@ -1220,7 +1222,19 @@ void TunnelStateData::noteDestination(Comm::ConnectionPointer path) { const bool wasBlocked = serverDestinations.empty(); + // XXX: Push even a nil path so that subsequent noteDestination() calls + // can rely on wasBlocked to detect ongoing/concurrent attempts. + // Upcoming Happy Eyeballs changes will handle this properly. serverDestinations.push_back(path); + + if (!path) { // decided to use a pinned connection + // We can call usePinned() without fear of clashing with an earlier + // forwarding attempt because PINNED must be the first destination. + assert(wasBlocked); + usePinned(); + return; + } + if (wasBlocked) startConnecting(); // else continue to use one of the previously noted destinations; @@ -1292,19 +1306,7 @@ TunnelStateData::startConnecting() assert(!serverDestinations.empty()); Comm::ConnectionPointer &dest = serverDestinations.front(); debugs(26, 3, "to " << dest); - - if (dest->peerType == PINNED) { - Comm::ConnectionPointer serverConn = borrowPinnedConnection(request.getRaw(), dest); - debugs(26,7, "pinned peer connection: " << serverConn); - if (Comm::IsConnOpen(serverConn)) { - tunnelConnectDone(serverConn, Comm::OK, 0, (void *)this); - return; - } - // a PINNED path failure is fatal; do not wait for more paths - sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw()), - "pinned path failure"); - return; - } + assert(dest != nullptr); GetMarkingsToServer(request.getRaw(), *dest); @@ -1315,6 +1317,22 @@ TunnelStateData::startConnecting() AsyncJob::Start(cs); } +/// send request on an existing connection dedicated to the requesting client +void +TunnelStateData::usePinned() +{ + const auto serverConn = borrowPinnedConnection(request.getRaw()); + debugs(26,7, "pinned peer connection: " << serverConn); + if (!Comm::IsConnOpen(serverConn)) { + // a PINNED path failure is fatal; do not wait for more paths + sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw()), + "pinned path failure"); + return; + } + + tunnelConnectDone(serverConn, Comm::OK, 0, (void *)this); +} + CBDATA_CLASS_INIT(TunnelStateData); bool