diff options
Diffstat (limited to 'netwerk/protocol/http/nsHttpConnection.cpp')
-rw-r--r-- | netwerk/protocol/http/nsHttpConnection.cpp | 233 |
1 files changed, 166 insertions, 67 deletions
diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index 916d1249c..95a06fd5c 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -87,6 +87,7 @@ nsHttpConnection::nsHttpConnection() , mWaitingFor0RTTResponse(false) , mContentBytesWritten0RTT(0) , mEarlyDataNegotiated(false) + , mDid0RTTSpdy(false) { LOG(("Creating nsHttpConnection @%p\n", this)); @@ -158,16 +159,113 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info, return NS_OK; } +nsresult +nsHttpConnection::TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> > &list) +{ + nsresult rv = mTransaction->TakeSubTransactions(list); + + if (rv == NS_ERROR_ALREADY_OPENED) { + // Has the interface for TakeSubTransactions() changed? + LOG(("TakeSubTransactions somehow called after " + "nsAHttpTransaction began processing\n")); + MOZ_ASSERT(false, + "TakeSubTransactions somehow called after " + "nsAHttpTransaction began processing"); + mTransaction->Close(NS_ERROR_ABORT); + return rv; + } + + if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { + // Has the interface for TakeSubTransactions() changed? + LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()")); + MOZ_ASSERT(false, + "unexpected result from " + "nsAHttpTransaction::TakeSubTransactions()"); + mTransaction->Close(NS_ERROR_ABORT); + return rv; + } + + return rv; +} + +nsresult +nsHttpConnection::MoveTransactionsToSpdy(nsresult status, nsTArray<RefPtr<nsAHttpTransaction> > &list) +{ + if (NS_FAILED(status)) { // includes NS_ERROR_NOT_IMPLEMENTED + MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty"); + + // This is ok - treat mTransaction as a single real request. + // Wrap the old http transaction into the new spdy session + // as the first stream. + LOG(("nsHttpConnection::MoveTransactionsToSpdy moves single transaction %p " + "into SpdySession %p\n", mTransaction.get(), mSpdySession.get())); + nsresult rv = AddTransaction(mTransaction, mPriority); + if (NS_FAILED(rv)) { + return rv; + } + } else { + int32_t count = list.Length(); + + LOG(("nsHttpConnection::MoveTransactionsToSpdy moving transaction list len=%d " + "into SpdySession %p\n", count, mSpdySession.get())); + + if (!count) { + mTransaction->Close(NS_ERROR_ABORT); + return NS_ERROR_ABORT; + } + + for (int32_t index = 0; index < count; ++index) { + nsresult rv = AddTransaction(list[index], mPriority); + if (NS_FAILED(rv)) { + return rv; + } + } + } + + return NS_OK; +} + +void +nsHttpConnection::Start0RTTSpdy(uint8_t spdyVersion) +{ + LOG(("nsHttpConnection::Start0RTTSpdy [this=%p]", this)); + mDid0RTTSpdy = true; + mUsingSpdyVersion = spdyVersion; + mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport, + true); + + nsTArray<RefPtr<nsAHttpTransaction> > list; + nsresult rv = TryTakeSubTransactions(list); + if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { + LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed taking " + "subtransactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv))); + return; + } + + rv = MoveTransactionsToSpdy(rv, list); + if (NS_FAILED(rv)) { + LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed moving " + "transactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv))); + return; + } + + mTransaction = mSpdySession; +} + void nsHttpConnection::StartSpdy(uint8_t spdyVersion) { - LOG(("nsHttpConnection::StartSpdy [this=%p]\n", this)); + LOG(("nsHttpConnection::StartSpdy [this=%p, mDid0RTTSpdy=%d]\n", this, mDid0RTTSpdy)); - MOZ_ASSERT(!mSpdySession); + MOZ_ASSERT(!mSpdySession || mDid0RTTSpdy); mUsingSpdyVersion = spdyVersion; mEverUsedSpdy = true; - mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport); + + if (!mDid0RTTSpdy) { + mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport, + false); + } if (!mReportedSpdy) { mReportedSpdy = true; @@ -185,27 +283,13 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion) // pack them all into a new spdy session. nsTArray<RefPtr<nsAHttpTransaction> > list; - nsresult rv = mTransaction->TakeSubTransactions(list); - - if (rv == NS_ERROR_ALREADY_OPENED) { - // Has the interface for TakeSubTransactions() changed? - LOG(("TakeSubTransactions somehow called after " - "nsAHttpTransaction began processing\n")); - MOZ_ASSERT(false, - "TakeSubTransactions somehow called after " - "nsAHttpTransaction began processing"); - mTransaction->Close(NS_ERROR_ABORT); - return; - } + nsresult rv = NS_OK; + if (!mDid0RTTSpdy) { + rv = TryTakeSubTransactions(list); - if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { - // Has the interface for TakeSubTransactions() changed? - LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()")); - MOZ_ASSERT(false, - "unexpected result from " - "nsAHttpTransaction::TakeSubTransactions()"); - mTransaction->Close(NS_ERROR_ABORT); - return; + if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { + return; + } } if (NeedSpdyTunnel()) { @@ -227,35 +311,11 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion) mConnInfo = wildCardProxyCi; } - if (NS_FAILED(rv)) { // includes NS_ERROR_NOT_IMPLEMENTED - MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty"); - - // This is ok - treat mTransaction as a single real request. - // Wrap the old http transaction into the new spdy session - // as the first stream. - LOG(("nsHttpConnection::StartSpdy moves single transaction %p " - "into SpdySession %p\n", mTransaction.get(), mSpdySession.get())); - rv = AddTransaction(mTransaction, mPriority); + if (!mDid0RTTSpdy) { + rv = MoveTransactionsToSpdy(rv, list); if (NS_FAILED(rv)) { return; } - } else { - int32_t count = list.Length(); - - LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d " - "into SpdySession %p\n", count, mSpdySession.get())); - - if (!count) { - mTransaction->Close(NS_ERROR_ABORT); - return; - } - - for (int32_t index = 0; index < count; ++index) { - rv = AddTransaction(list[index], mPriority); - if (NS_FAILED(rv)) { - return; - } - } } // Disable TCP Keepalives - use SPDY ping instead. @@ -313,6 +373,13 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, if (NS_FAILED(rv)) goto npnComplete; + if (!m0RTTChecked) { + // We reuse m0RTTChecked. We want to send this status only once. + mTransaction->OnTransportStatus(mSocketTransport, + NS_NET_STATUS_TLS_HANDSHAKE_STARTING, + 0); + } + rv = ssl->GetNegotiatedNPN(negotiatedNPN); if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) && !mConnInfo->UsingProxy()) { @@ -321,8 +388,7 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, // (AlpnEarlySelection), we are using HTTP/1, and the request data can // be safely retried. m0RTTChecked = true; - nsAutoCString earlyNegotiatedNPN; - nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN); + nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN); if (NS_FAILED(rvEarlyAlpn)) { // if ssl->DriveHandshake() has never been called the value // for AlpnEarlySelection is still not set. So call it here and @@ -339,7 +405,7 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, // Check NegotiatedNPN first. rv = ssl->GetNegotiatedNPN(negotiatedNPN); if (rv == NS_ERROR_NOT_CONNECTED) { - rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN); + rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN); } } @@ -349,19 +415,26 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, mEarlyDataNegotiated = false; } else { LOG(("nsHttpConnection::EnsureNPNComplete %p -" - "early selected alpn: %s", this, earlyNegotiatedNPN.get())); + "early selected alpn: %s", this, mEarlyNegotiatedALPN.get())); uint32_t infoIndex; const SpdyInformation *info = gHttpHandler->SpdyInfo(); - // We are doing 0RTT only with Http/1 right now! - if (NS_FAILED(info->GetNPNIndex(earlyNegotiatedNPN, &infoIndex))) { + if (NS_FAILED(info->GetNPNIndex(mEarlyNegotiatedALPN, &infoIndex))) { + // This is the HTTP/1 case. // Check if early-data is allowed for this transaction. if (mTransaction->Do0RTT()) { LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We " - "can do 0RTT!", this)); + "can do 0RTT (http/1)!", this)); mWaitingFor0RTTResponse = true; } - mEarlyDataNegotiated = true; + } else { + // We have h2, we can at least 0-RTT the preamble and opening + // SETTINGS, etc, and maybe some of the first request + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - Starting " + "0RTT for h2!", this)); + mWaitingFor0RTTResponse = true; + Start0RTTSpdy(info->Version[infoIndex]); } + mEarlyDataNegotiated = true; } } @@ -391,16 +464,17 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, this, mConnInfo->HashKey().get(), negotiatedNPN.get(), mTLSFilter ? " [Double Tunnel]" : "")); - bool ealyDataAccepted = false; + bool earlyDataAccepted = false; if (mWaitingFor0RTTResponse) { // Check if early data has been accepted. - rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted); + rv = ssl->GetEarlyDataAccepted(&earlyDataAccepted); LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data " - "that was sent during 0RTT %s been accepted.", - this, ealyDataAccepted ? "has" : "has not")); + "that was sent during 0RTT %s been accepted [rv=%" PRIx32 "].", + this, earlyDataAccepted ? "has" : "has not", static_cast<uint32_t>(rv))); if (NS_FAILED(rv) || - NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) { + NS_FAILED(mTransaction->Finish0RTT(!earlyDataAccepted, negotiatedNPN != mEarlyNegotiatedALPN))) { + LOG(("nsHttpConection::EnsureNPNComplete [this=%p] closing transaction %p", this, mTransaction.get())); mTransaction->Close(NS_ERROR_NET_RESET); goto npnComplete; } @@ -416,16 +490,17 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED)); if (mWaitingFor0RTTResponse) { Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED, - ealyDataAccepted); + earlyDataAccepted); } - if (ealyDataAccepted) { + if (earlyDataAccepted) { Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN, mContentBytesWritten0RTT); } } mWaitingFor0RTTResponse = false; - if (!ealyDataAccepted) { + if (!earlyDataAccepted) { + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] early data not accepted", this)); uint32_t infoIndex; const SpdyInformation *info = gHttpHandler->SpdyInfo(); if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { @@ -435,21 +510,45 @@ nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %d bytes " "has been sent during 0RTT.", this, mContentBytesWritten0RTT)); mContentBytesWritten = mContentBytesWritten0RTT; + if (mSpdySession) { + // We had already started 0RTT-spdy, now we need to fully set up + // spdy, since we know we're sticking with it. + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - finishing " + "StartSpdy for 0rtt spdy session %p", this, mSpdySession.get())); + StartSpdy(mSpdySession->SpdyVersion()); + } } Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy()); } npnComplete: - LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true")); + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] setting complete to true", this)); mNPNComplete = true; + + mTransaction->OnTransportStatus(mSocketTransport, + NS_NET_STATUS_TLS_HANDSHAKE_ENDED, + 0); if (mWaitingFor0RTTResponse) { + // Didn't get 0RTT OK, back out of the "attempting 0RTT" state mWaitingFor0RTTResponse = false; - if (NS_FAILED(mTransaction->Finish0RTT(true))) { + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] 0rtt failed", this)); + if (NS_FAILED(mTransaction->Finish0RTT(true, negotiatedNPN != mEarlyNegotiatedALPN))) { mTransaction->Close(NS_ERROR_NET_RESET); } mContentBytesWritten0RTT = 0; } + + if (mDid0RTTSpdy && negotiatedNPN != mEarlyNegotiatedALPN) { + // Reset the work done by Start0RTTSpdy + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] resetting Start0RTTSpdy", this)); + mUsingSpdyVersion = 0; + mTransaction = nullptr; + mSpdySession = nullptr; + // We have to reset this here, just in case we end up starting spdy again, + // so it can actually do everything it needs to do. + mDid0RTTSpdy = false; + } return true; } |