# HG changeset patch
# User Dragana Damjanovic
# Parent 1fda52a1f3b81cf1a821155998dca637bb64e3d9
Bug 1362821 - Properly destroy a HalfOpeSocket that is used as a backup for a TFO connection if another H2 connection is established for the same host. r=mcmanus
diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -1713,18 +1713,22 @@ nsSocketTransport::RecoverFromError()
bool tryAgain = false;
if (mFDFastOpenInProgress &&
((mCondition == NS_ERROR_CONNECTION_REFUSED) ||
(mCondition == NS_ERROR_NET_TIMEOUT))) {
// TCP Fast Open can be blocked by middle boxes so we will retry
// without it.
tryAgain = true;
- MOZ_ASSERT(mFastOpenCallback);
- mFastOpenCallback->SetFastOpenConnected(mCondition);
+ // If we cancel the connection because backup socket was successfully
+ // connected, mFDFastOpenInProgress will be true but mFastOpenCallback
+ // will be nullptr.
+ if (mFastOpenCallback) {
+ mFastOpenCallback->SetFastOpenConnected(mCondition);
+ }
mFastOpenCallback = nullptr;
} else {
if ((mState == STATE_CONNECTING) && mDNSRecord &&
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
if (mNetAddr.raw.family == AF_INET) {
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
@@ -2461,16 +2465,20 @@ nsSocketTransport::OpenOutputStream(uint
}
NS_IMETHODIMP
nsSocketTransport::Close(nsresult reason)
{
if (NS_SUCCEEDED(reason))
reason = NS_BASE_STREAM_CLOSED;
+ if (mFastOpenCallback) {
+ mFastOpenCallback->SetFastOpenConnected(reason);
+ mFastOpenCallback = nullptr;
+ }
mInput.CloseWithStatus(reason);
mOutput.CloseWithStatus(reason);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
{
diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -784,18 +784,27 @@ nsHttpConnectionMgr::UpdateCoalescingFor
}
listOfWeakConns->AppendElement(
do_GetWeakReference(static_cast(newConn)));
}
// Cancel any other pending connections - their associated transactions
// are in the pending queue and will be dispatched onto this new connection
for (int32_t index = ent->mHalfOpens.Length() - 1; index >= 0; --index) {
+ nsHalfOpenSocket *half = ent->mHalfOpens[index];
LOG(("UpdateCoalescingForNewConn() forcing halfopen abandon %p\n",
- ent->mHalfOpens[index]));
+ half));
+
+ if (half->IsFastOpenBackupHalfOpen()) {
+ LOG(("UpdateCoalescingForNewConn() halfOpen %p is in Fast Open "
+ "state.\n", half));
+ half->CancelFastOpenConnection();
+ }
+ // A backup timer or a backup outputStream holds reference to half
+ // and not half->mSocketTransport, therefore we can call Abandon.
ent->mHalfOpens[index]->Abandon();
}
if (ent->mActiveConns.Length() > 1) {
// this is a new connection that can be coalesced onto. hooray!
// if there are other connection to this entry (e.g.
// some could still be handshaking, shutting down, etc..) then close
// them down after any transactions that are on them are complete.
@@ -2835,16 +2844,21 @@ nsHttpConnectionMgr::TimeoutTick()
if (ent->mHalfOpens.Length()) {
TimeStamp currentTime = TimeStamp::Now();
double maxConnectTime_ms = gHttpHandler->ConnectTimeout();
for (uint32_t index = ent->mHalfOpens.Length(); index > 0; ) {
index--;
nsHalfOpenSocket *half = ent->mHalfOpens[index];
+ if (half->IsFastOpenBackupHalfOpen()) {
+ // this transport belongs to a fast open connection.
+ // It will be canceled through that connection.
+ continue;
+ }
double delta = half->Duration(currentTime);
// If the socket has timed out, close it so the waiting
// transaction will get the proper signal.
if (delta > maxConnectTime_ms) {
LOG(("Force timeout of half open to %s after %.2fms.\n",
ent->mConnInfo->HashKey().get(), delta));
if (half->SocketTransport()) {
half->SocketTransport()->Close(NS_ERROR_NET_TIMEOUT);
@@ -3600,16 +3614,49 @@ nsHttpConnectionMgr::
nsHalfOpenSocket::SetFastOpenStatus(uint8_t tfoStatus)
{
mConnectionNegotiatingFastOpen->SetFastOpenStatus(tfoStatus);
mConnectionNegotiatingFastOpen->Transaction()->SetFastOpenStatus(tfoStatus);
}
void
nsHttpConnectionMgr::
+nsHalfOpenSocket::CancelFastOpenConnection()
+{
+ // This must be called on a halfOpenSocket that has
+ // mConnectionNegotiatingFastOpen.
+ if (!mConnectionNegotiatingFastOpen) {
+ return;
+ }
+
+ // This function will cancel the mConnectionNegotiatingFastOpen connection
+ // and return transaction to the pending queue.
+ mSocketTransport->SetFastOpenCallback(nullptr);
+ RefPtr trans =
+ mConnectionNegotiatingFastOpen->CloseConnectionFastOpenTakesTooLongOrError(true);
+ mConnectionNegotiatingFastOpen = nullptr;
+ mSocketTransport = nullptr;
+ mStreamOut = nullptr;
+ mStreamIn = nullptr;
+
+ if (trans && trans->QueryHttpTransaction()) {
+ RefPtr pendingTransInfo =
+ new PendingTransactionInfo(trans->QueryHttpTransaction());
+
+ if (trans->Caps() & NS_HTTP_URGENT_START) {
+ gHttpHandler->ConnMgr()->InsertTransactionSorted(mEnt->mUrgentStartQ,
+ pendingTransInfo);
+ } else {
+ mEnt->InsertTransaction(pendingTransInfo);
+ }
+ }
+}
+
+void
+nsHttpConnectionMgr::
nsHalfOpenSocket::FastOpenNotSupported()
{
MOZ_ASSERT(mUsingFastOpen);
gHttpHandler->SetFastOpenNotSupported();
}
nsresult
nsHttpConnectionMgr::
diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -374,16 +374,23 @@ private:
bool Claim();
void Unclaim();
bool FastOpenEnabled() override;
nsresult StartFastOpen() override;
void SetFastOpenConnected(nsresult) override;
void FastOpenNotSupported() override;
void SetFastOpenStatus(uint8_t tfoStatus) override;
+
+ bool IsFastOpenBackupHalfOpen()
+ {
+ return mConnectionNegotiatingFastOpen;
+ }
+
+ void CancelFastOpenConnection();
private:
nsresult SetupConn(nsIAsyncOutputStream *out,
bool aFastOpen);
// To find out whether |mTransaction| is still in the connection entry's
// pending queue. If the transaction is found and |removeWhenFound| is
// true, the transaction will be removed from the pending queue.
already_AddRefed