Bug 1322373 - TLS 1.3 early-data for http/2

This commit is contained in:
janekptacijarabaci 2018-02-11 07:48:28 +01:00 committed by Roy Tam
commit 202ca6d067
11 changed files with 342 additions and 82 deletions

View file

@ -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);
nsresult rv = NS_OK;
if (!mDid0RTTSpdy) {
rv = TryTakeSubTransactions(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;
}
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.
@ -328,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
@ -346,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);
}
}
@ -356,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;
}
}
@ -398,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;
}
@ -423,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))) {
@ -442,13 +510,20 @@ 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,
@ -456,12 +531,25 @@ npnComplete:
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;
}