author | Dragana Damjanovic <dd.mozilla@gmail.com> |
Thu, 11 Aug 2016 08:28:00 -0400 | |
changeset 310728 | 16bcc176a1439cc6ba5b70cd62d0d049243c41b8 |
parent 310727 | c45edf68dc4c20ba9eb4f76820d2c410b9caa2f5 |
child 310729 | a1fcbe1fc10c17fb0b7c6e31a46ffc0d8f7f18ef |
push id | 30593 |
push user | ryanvm@gmail.com |
push date | Tue, 23 Aug 2016 14:05:29 +0000 |
treeherder | mozilla-central@052656fc513c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mcmanus |
bugs | 1264578 |
milestone | 51.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/netwerk/protocol/http/nsAHttpTransaction.h +++ b/netwerk/protocol/http/nsAHttpTransaction.h @@ -199,16 +199,32 @@ public: // authoritative. virtual nsresult GetTransactionSecurityInfo(nsISupports **) { return NS_ERROR_NOT_IMPLEMENTED; } virtual void DisableSpdy() { } virtual void ReuseConnectionOnRestartOK(bool) { } + + // Returns true if early-data is possible. + virtual bool Do0RTT() { + return false; + } + // This function will be called when a tls handshake has been finished and + // we know whether early-data that was sent has been accepted or not, e.g. + // do we need to restart a transaction. This will be called only if Do0RTT + // returns true. + // If aRestart parameter is true we need to restart the transaction, + // otherwise the erly-data has been accepted and we can continue the + // transaction. + // The function will return success or failure of the transaction restart. + virtual nsresult Finish0RTT(bool aRestart) { + return NS_ERROR_NOT_IMPLEMENTED; + } }; NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID) #define NS_DECL_NSAHTTPTRANSACTION \ void SetConnection(nsAHttpConnection *) override; \ nsAHttpConnection *Connection() override; \ void GetSecurityCallbacks(nsIInterfaceRequestor **) override; \
--- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -74,16 +74,19 @@ nsHttpConnection::nsHttpConnection() , mPriority(nsISupportsPriority::PRIORITY_NORMAL) , mReportedSpdy(false) , mEverUsedSpdy(false) , mLastHttpResponseVersion(NS_HTTP_VERSION_1_1) , mTransactionCaps(0) , mResponseTimeoutEnabled(false) , mTCPKeepaliveConfig(kTCPKeepaliveDisabled) , mForceSendPending(false) + , m0RTTChecked(false) + , mWaitingFor0RTTResponse(false) + , mContentBytesWritten0RTT(0) { LOG(("Creating nsHttpConnection @%p\n", this)); // the default timeout is for when this connection has not yet processed a // transaction static const PRIntervalTime k5Sec = PR_SecondsToInterval(5); mIdleTimeout = (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout(); @@ -266,21 +269,25 @@ nsHttpConnection::StartSpdy(uint8_t spdy mTLSFilter->SetProxiedTransaction(mSpdySession); } if (mDontReuse) { mSpdySession->DontReuse(); } } bool -nsHttpConnection::EnsureNPNComplete() +nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, + uint32_t &aOut0RTTBytesWritten) { // If for some reason the components to check on NPN aren't available, // this function will just return true to continue on and disable SPDY + aOut0RTTWriteHandshakeValue = NS_OK; + aOut0RTTBytesWritten = 0; + MOZ_ASSERT(mSocketTransport); if (!mSocketTransport) { // this cannot happen mNPNComplete = true; return true; } if (mNPNComplete) { @@ -297,45 +304,130 @@ nsHttpConnection::EnsureNPNComplete() goto npnComplete; } ssl = do_QueryInterface(securityInfo, &rv); if (NS_FAILED(rv)) goto npnComplete; rv = ssl->GetNegotiatedNPN(negotiatedNPN); + if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) && + !mConnInfo->UsingProxy()) { + // There is no ALPN info (yet!). We need to consider doing 0RTT. We + // will do so if there is ALPN information from a previous session + // (AlpnEarlySelection), we are using HTTP/1, and the request data can + // be safely retried. + m0RTTChecked = true; + nsAutoCString earlyNegotiatedNPN; + nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN); + if (NS_FAILED(rvEarlyAlpn)) { + // if ssl->DriveHandshake() has never been called the value + // for AlpnEarlySelection is still not set. So call it here and + // check again. + LOG(("nsHttpConnection::EnsureNPNComplete %p - " + "early selected alpn not available, we will try one more time.", + this)); + // Let's do DriveHandshake again. + rv = ssl->DriveHandshake(); + if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { + goto npnComplete; + } + + // Check NegotiatedNPN first. + rv = ssl->GetNegotiatedNPN(negotiatedNPN); + if (rv == NS_ERROR_NOT_CONNECTED) { + rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN); + } + } + + if (NS_FAILED(rvEarlyAlpn)) { + LOG(("nsHttpConnection::EnsureNPNComplete %p - " + "early selected alpn not available", this)); + } else { + LOG(("nsHttpConnection::EnsureNPNComplete %p -" + "early selected alpn: %s", this, earlyNegotiatedNPN.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))) { + // Check if early-data is allowed for this transaction. + if (mTransaction->Do0RTT()) { + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We " + "can do 0RTT!", this)); + mWaitingFor0RTTResponse = true; + } + } + } + } + if (rv == NS_ERROR_NOT_CONNECTED) { - // By writing 0 bytes to the socket the SSL handshake machine is - // pushed forward. - uint32_t count = 0; - rv = mSocketOut->Write("", 0, &count); + if (mWaitingFor0RTTResponse) { + aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this, + nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten); + if (NS_FAILED(aOut0RTTWriteHandshakeValue) && + aOut0RTTWriteHandshakeValue != NS_BASE_STREAM_WOULD_BLOCK) { + goto npnComplete; + } + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - written %d " + "bytes during 0RTT", this, aOut0RTTBytesWritten)); + mContentBytesWritten0RTT += aOut0RTTBytesWritten; + } + + rv = ssl->DriveHandshake(); if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { goto npnComplete; } return false; } if (NS_SUCCEEDED(rv)) { LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n", this, mConnInfo->HashKey().get(), negotiatedNPN.get(), mTLSFilter ? " [Double Tunnel]" : "")); - uint32_t infoIndex; - const SpdyInformation *info = gHttpHandler->SpdyInfo(); - if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { - StartSpdy(info->Version[infoIndex]); + bool ealyDataAccepted = false; + if (mWaitingFor0RTTResponse) { + mWaitingFor0RTTResponse = false; + // Check if early data has been accepted. + rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted); + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data " + "that was sent during 0RTT %s been accepted.", + this, ealyDataAccepted ? "has" : "has not")); + if (NS_FAILED(rv) || + NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) { + mTransaction->Close(NS_ERROR_NET_RESET); + goto npnComplete; + } + } + if (!ealyDataAccepted) { + uint32_t infoIndex; + const SpdyInformation *info = gHttpHandler->SpdyInfo(); + if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) { + StartSpdy(info->Version[infoIndex]); + } + } else { + LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %d bytes " + "has been sent during 0RTT.", this, mContentBytesWritten0RTT)); + mContentBytesWritten = mContentBytesWritten0RTT; } Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy()); } npnComplete: LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true")); mNPNComplete = true; + if (mWaitingFor0RTTResponse) { + mWaitingFor0RTTResponse = false; + if (NS_FAILED(mTransaction->Finish0RTT(true))) { + mTransaction->Close(NS_ERROR_NET_RESET); + } + mContentBytesWritten0RTT = 0; + } return true; } void nsHttpConnection::OnTunnelNudged(TLSFilterTransaction *trans) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("nsHttpConnection::OnTunnelNudged %p\n", this)); @@ -1581,27 +1673,32 @@ nsHttpConnection::OnSocketWritable() rv = mSocketOutCondition = NS_OK; transactionBytes = 0; // The SSL handshake must be completed before the transaction->readsegments() // processing can proceed because we need to know how to format the // request differently for http/1, http/2, spdy, etc.. and that is // negotiated with NPN/ALPN in the SSL handshake. - if (mConnInfo->UsingHttpsProxy() && !EnsureNPNComplete()) { + if (mConnInfo->UsingHttpsProxy() && + !EnsureNPNComplete(rv, transactionBytes)) { + MOZ_ASSERT(!transactionBytes); mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; } else if (mProxyConnectStream) { // If we're need an HTTP/1 CONNECT tunnel through a proxy // send it before doing the SSL handshake LOG((" writing CONNECT request stream\n")); rv = mProxyConnectStream->ReadSegments(ReadFromStream, this, nsIOService::gDefaultSegmentSize, &transactionBytes); - } else if (!EnsureNPNComplete()) { - mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; + } else if (!EnsureNPNComplete(rv, transactionBytes)) { + if (NS_SUCCEEDED(rv) && !transactionBytes && + NS_SUCCEEDED(mSocketOutCondition)) { + mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; + } } else { // for non spdy sessions let the connection manager know if (!mReportedSpdy) { mReportedSpdy = true; MOZ_ASSERT(!mEverUsedSpdy); gHttpHandler->ConnMgr()->ReportSpdyConnection(this, false); } @@ -1639,17 +1736,17 @@ nsHttpConnection::OnSocketWritable() } } else { rv = mSocketOutCondition; } again = false; } else if (!transactionBytes) { rv = NS_OK; - if (mTransaction) { // in case the ReadSegments stack called CloseTransaction() + if (mTransaction && !mWaitingFor0RTTResponse) { // in case the ReadSegments stack called CloseTransaction() // // at this point we've written out the entire transaction, and now we // must wait for the server's response. we manufacture a status message // here to reflect the fact that we are waiting. this message will be // trumped (overwritten) if the server responds quickly. // mTransaction->OnTransportStatus(mSocketTransport, NS_NET_STATUS_WAITING_FOR,
--- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -237,17 +237,18 @@ private: nsresult SetupProxyConnect(); PRIntervalTime IdleTime(); bool IsAlive(); bool SupportsPipelining(nsHttpResponseHead *); // Makes certain the SSL handshake is complete and NPN negotiation // has had a chance to happen - bool EnsureNPNComplete(); + bool EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue, + uint32_t &aOut0RTTBytesWritten); void SetupSSL(); // Start the Spdy transaction handler when NPN indicates spdy/* void StartSpdy(uint8_t versionLevel); // Directly Add a transaction to an active connection for SPDY nsresult AddTransaction(nsAHttpTransaction *, int32_t); @@ -327,17 +328,17 @@ private: // SPDY related bool mNPNComplete; bool mSetupSSLCalled; // version level in use, 0 if unused uint8_t mUsingSpdyVersion; - RefPtr<ASpdySession> mSpdySession; + RefPtr<ASpdySession> mSpdySession; int32_t mPriority; bool mReportedSpdy; // mUsingSpdyVersion is cleared when mSpdySession is freed, this is permanent bool mEverUsedSpdy; // mLastHttpResponseVersion stores the last response's http version seen. uint8_t mLastHttpResponseVersion; @@ -352,14 +353,25 @@ private: nsCOMPtr<nsITimer> mTCPKeepaliveTransitionTimer; private: // For ForceSend() static void ForceSendIO(nsITimer *aTimer, void *aClosure); nsresult MaybeForceSendIO(); bool mForceSendPending; nsCOMPtr<nsITimer> mForceSendTimer; + + // Helper variable for 0RTT handshake; + bool m0RTTChecked; // Possible 0RTT has been + // checked. + bool mWaitingFor0RTTResponse; // We have are + // sending 0RTT + // data and we + // are waiting + // for the end of + // the handsake. + int64_t mContentBytesWritten0RTT; }; } // namespace net } // namespace mozilla #endif // nsHttpConnection_h__
--- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -134,16 +134,17 @@ nsHttpTransaction::nsHttpTransaction() , mSubmittedRatePacing(false) , mPassedRatePacing(false) , mSynchronousRatePaceRequest(false) , mCountRecv(0) , mCountSent(0) , mAppId(NECKO_NO_APP_ID) , mIsInIsolatedMozBrowser(false) , mClassOfService(0) + , m0RTTInProgress(false) { LOG(("Creating nsHttpTransaction @%p\n", this)); gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize); #ifdef MOZ_VALGRIND memset(&mSelfAddr, 0, sizeof(NetAddr)); memset(&mPeerAddr, 0, sizeof(NetAddr)); #endif @@ -687,17 +688,17 @@ nsHttpTransaction::ReadSegments(nsAHttpS { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); if (mTransactionDone) { *countRead = 0; return mStatus; } - if (!mConnected) { + if (!mConnected && !m0RTTInProgress) { mConnected = true; mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); } mDeferredSendProgress = false; mReader = reader; nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead); mReader = nullptr; @@ -2315,10 +2316,38 @@ nsHttpTransaction::RestartVerifier::Set( void nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer) { MutexAutoLock lock(mLock); self = mSelfAddr; peer = mPeerAddr; } +bool +nsHttpTransaction::Do0RTT() +{ + if (mRequestHead->IsSafeMethod() && + !mConnection->IsProxyConnectInProgress()) { + m0RTTInProgress = true; + } + return m0RTTInProgress; +} + +nsresult +nsHttpTransaction::Finish0RTT(bool aRestart) +{ + MOZ_ASSERT(m0RTTInProgress); + m0RTTInProgress = false; + if (aRestart) { + // Reset request headers to be sent again. + nsCOMPtr<nsISeekableStream> seekable = + do_QueryInterface(mRequestStream); + if (seekable) { + seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + } else { + return NS_ERROR_FAILURE; + } + } + return NS_OK; +} + } // namespace net } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -161,16 +161,18 @@ public: mozilla::TimeStamp GetConnectStart(); mozilla::TimeStamp GetConnectEnd(); mozilla::TimeStamp GetRequestStart(); mozilla::TimeStamp GetResponseStart(); mozilla::TimeStamp GetResponseEnd(); int64_t GetTransferSize() { return mTransferSize; } + bool Do0RTT() override; + nsresult Finish0RTT(bool aRestart) override; private: friend class DeleteHttpTransaction; virtual ~nsHttpTransaction(); nsresult Restart(); nsresult RestartInProgress(); char *LocateHttpStart(char *buf, uint32_t len, bool aAllowPartialMatch); @@ -455,14 +457,16 @@ private: RefPtr<ASpdySession> mTunnelProvider; public: void GetNetworkAddresses(NetAddr &self, NetAddr &peer); private: NetAddr mSelfAddr; NetAddr mPeerAddr; + + bool m0RTTInProgress; }; } // namespace net } // namespace mozilla #endif // nsHttpTransaction_h__