Bug 1650388 - Send 0RTT data. r=kershaw

Differential Revision: https://phabricator.services.mozilla.com/D87231
This commit is contained in:
Dragana Damjanovic 2020-09-10 20:48:56 +00:00
parent 368bae162a
commit 05c4dcd62f
6 changed files with 150 additions and 14 deletions

View File

@ -156,6 +156,24 @@ nsresult Http3Session::Init(const nsACString& aOrigin,
if (NS_SUCCEEDED(SSLTokensCache::Get(peerId, token))) {
LOG(("Found a resumption token in the cache."));
mHttp3Connection->SetResumptionToken(token);
if (mHttp3Connection->IsZeroRtt()) {
LOG(("Can send ZeroRtt data"));
RefPtr<Http3Session> self(this);
mState = ZERORTT;
// Let the nsHttpConnectionMgr know that the connection can accept
// transactions.
// We need to dispatch the following function to this thread so that
// it is executed after the current function. At this point a
// Http3Session is still being initialized and ReportHttp3Connection
// will try to dispatch transaction on this session therefore it
// needs to be executed after the initializationg is done.
DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(NS_NewRunnableFunction(
"Http3Session::ReportHttp3Connection",
[self]() {
self->ReportHttp3Connection();
}));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
}
}
return NS_OK;
}
@ -360,11 +378,10 @@ nsresult Http3Session::ProcessEvents(uint32_t count) {
static_cast<uint32_t>(rv)));
return rv;
}
break;
}
case Http3Event::Tag::DataWritable:
MOZ_ASSERT(mState == CONNECTED);
MOZ_ASSERT(CanSandData());
LOG(("Http3Session::ProcessEvents - DataWritable"));
if (mReadyForWriteButBlocked.RemoveElement(
event.data_writable.stream_id)) {
@ -417,12 +434,23 @@ nsresult Http3Session::ProcessEvents(uint32_t count) {
CallCertVerification();
}
break;
case Http3Event::Tag::ZeroRttRejected:
LOG(("Http3Session::ProcessEvents - ZeroRttRejected"));
if (mState == ZERORTT) {
mState = INITIALIZING;
Finish0Rtt(true);
}
break;
case Http3Event::Tag::ConnectionConnected:
{
LOG(("Http3Session::ProcessEvents - ConnectionConnected"));
bool was0RTT = mState == ZERORTT;
mState = CONNECTED;
SetSecInfo();
mSocketControl->HandshakeCompleted();
if (was0RTT) {
Finish0Rtt(false);
}
nsTArray<uint8_t> token;
mHttp3Connection->GetResumptionToken(token);
if (!token.IsEmpty()) {
@ -436,8 +464,7 @@ nsresult Http3Session::ProcessEvents(uint32_t count) {
}
}
gHttpHandler->ConnMgr()->ReportHttp3Connection(mSegmentReaderWriter);
MaybeResumeSend();
ReportHttp3Connection();
}
break;
case Http3Event::Tag::GoawayReceived:
@ -600,13 +627,25 @@ bool Http3Session::AddStream(nsAHttpTransaction* aHttpTransaction,
Http3Stream* stream = new Http3Stream(aHttpTransaction, this);
mStreamTransactionHash.Put(aHttpTransaction, RefPtr{stream});
if (mState == ZERORTT) {
if (!stream->Do0RTT()) {
LOG(("Http3Session %p will not get early data from Http3Stream %p",
this, stream));
if (!mCannotDo0RTTStreams.Contains(stream)) {
mCannotDo0RTTStreams.AppendElement(stream);
}
return true;
} else {
m0RTTStreams.AppendElement(stream);
}
}
StreamReadyToWrite(stream);
return true;
}
bool Http3Session::CanReuse() {
return (mState == CONNECTED) && !(mGoawayReceived || mShouldClose);
return CanSandData() && !(mGoawayReceived || mShouldClose);
}
void Http3Session::QueueStream(Http3Stream* stream) {
@ -677,6 +716,13 @@ nsresult Http3Session::TryActivating(
return NS_BASE_STREAM_WOULD_BLOCK;
}
if (mState == ZERORTT) {
if (!aStream->Do0RTT()) {
MOZ_ASSERT(!mCannotDo0RTTStreams.Contains(aStream));
return NS_BASE_STREAM_WOULD_BLOCK;
}
}
nsresult rv = mHttp3Connection->Fetch(aMethod, aScheme, aAuthorityHeader,
aPath, aHeaders, aStreamId);
if (NS_FAILED(rv)) {
@ -808,10 +854,7 @@ nsresult Http3Session::ReadSegmentsAgain(nsAHttpSegmentReader* reader,
Http3Stream* stream = nullptr;
// Step 1)
while (
(mState ==
CONNECTED) && // Do not send transaction data untill we are connected.
(stream = mReadyForWrite.PopFront())) {
while (CanSandData() && (stream = mReadyForWrite.PopFront())) {
LOG(
("Http3Session::ReadSegmentsAgain call ReadSegments from stream=%p "
"[this=%p]",
@ -882,13 +925,13 @@ nsresult Http3Session::ReadSegmentsAgain(nsAHttpSegmentReader* reader,
void Http3Session::StreamReadyToWrite(Http3Stream* aStream) {
MOZ_ASSERT(aStream);
mReadyForWrite.Push(aStream);
if ((mState == CONNECTED) && mConnection) {
if (CanSandData() && mConnection) {
Unused << mConnection->ResumeSend();
}
}
void Http3Session::MaybeResumeSend() {
if ((mReadyForWrite.GetSize() > 0) && (mState == CONNECTED) && mConnection) {
if ((mReadyForWrite.GetSize() > 0) && CanSandData() && mConnection) {
Unused << mConnection->ResumeSend();
}
}
@ -1271,7 +1314,7 @@ bool Http3Session::JoinConnection(const nsACString& hostname, int32_t port) {
bool Http3Session::RealJoinConnection(const nsACString& hostname, int32_t port,
bool justKidding) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!mConnection || (mState != CONNECTED) || mShouldClose ||
if (!mConnection || !CanSandData() || mShouldClose ||
mGoawayReceived) {
return false;
}
@ -1374,7 +1417,7 @@ void Http3Session::CallCertVerification() {
void Http3Session::Authenticated(int32_t aError) {
LOG(("Http3Session::Authenticated error=0x%" PRIx32 " [this=%p].", aError,
this));
if (mState == INITIALIZING) {
if ((mState == INITIALIZING) || (mState == ZERORTT)) {
if (psm::IsNSSErrorCode(aError)) {
mError = psm::GetXPCOMFromNSSError(aError);
LOG(("Http3Session::Authenticated psm-error=0x%" PRIx32 " [this=%p].",
@ -1452,5 +1495,39 @@ void Http3Session::CloseConnectionTelemetry(CloseError& aError, bool aClosing) {
aClosing ? "closing"_ns : "closed"_ns, value);
}
void Http3Session::Finish0Rtt(bool aRestart) {
for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
if (m0RTTStreams[i]) {
if (aRestart) {
// When we need ot restart transactions remove them from all lists.
if (m0RTTStreams[i]->HasStreamId()) {
mStreamIdHash.Remove(m0RTTStreams[i]->StreamId());
}
RemoveStreamFromQueues(m0RTTStreams[i]);
// The stream is ready to write again.
mReadyForWrite.Push(m0RTTStreams[i]);
}
m0RTTStreams[i]->Finish0RTT(aRestart);
}
}
for (size_t i = 0; i < mCannotDo0RTTStreams.Length(); ++i) {
if (mCannotDo0RTTStreams[i]) {
mReadyForWrite.Push(mCannotDo0RTTStreams[i]);
}
}
m0RTTStreams.Clear();
mCannotDo0RTTStreams.Clear();
MaybeResumeSend();
}
void Http3Session::ReportHttp3Connection() {
if (CanSandData() && !mHttp3ConnectionReported) {
mHttp3ConnectionReported = true;
gHttpHandler->ConnMgr()->ReportHttp3Connection(mSegmentReaderWriter);
MaybeResumeSend();
}
}
} // namespace net
} // namespace mozilla

View File

@ -51,6 +51,7 @@ class Http3Session final : public nsAHttpTransaction,
HttpConnectionUDP* readerWriter);
bool IsConnected() const { return mState == CONNECTED; }
bool CanSandData() const { return (mState == CONNECTED) || (mState == ZERORTT); }
bool IsClosing() const { return (mState == CLOSING || mState == CLOSED); }
bool IsClosed() const { return mState == CLOSED; }
@ -100,6 +101,8 @@ class Http3Session final : public nsAHttpTransaction,
const nsCString& GetAlpnToken() { return mAlpnToken; }
void ReportHttp3Connection();
private:
~Http3Session();
@ -137,6 +140,7 @@ class Http3Session final : public nsAHttpTransaction,
void MaybeResumeSend();
void CloseConnectionTelemetry(CloseError& aError, bool aClosing);
void Finish0Rtt(bool aRestart);
RefPtr<NeqoHttp3Conn> mHttp3Connection;
RefPtr<nsAHttpConnection> mConnection;
@ -149,13 +153,14 @@ class Http3Session final : public nsAHttpTransaction,
nsTArray<RefPtr<Http3Stream>> mSlowConsumersReadyForRead;
nsDeque<Http3Stream> mQueuedStreams;
enum State { INITIALIZING, CONNECTED, CLOSING, CLOSED } mState;
enum State { INITIALIZING, ZERORTT, CONNECTED, CLOSING, CLOSED } mState;
bool mAuthenticationStarted;
bool mCleanShutdown;
bool mGoawayReceived;
bool mShouldClose;
bool mIsClosedByNeqo;
bool mHttp3ConnectionReported = false;
// mError is neqo error (a protocol error) and that may mean that we will
// send some packets after that.
nsresult mError;
@ -180,6 +185,12 @@ class Http3Session final : public nsAHttpTransaction,
nsCString mAlpnToken;
uint64_t mTransactionCount = 0;
// The stream(s) that we are getting 0RTT data from.
nsTArray<WeakPtr<Http3Stream>> m0RTTStreams;
// The stream(s) that are not able to send 0RTT data. We need to
// remember them put them into mReadyForWrite queue when 0RTT finishes.
nsTArray<WeakPtr<Http3Stream>> mCannotDo0RTTStreams;
};
NS_DEFINE_STATIC_IID_ACCESSOR(Http3Session, NS_HTTP3SESSION_IID);

View File

@ -375,5 +375,36 @@ nsresult Http3Stream::WriteSegments(nsAHttpSegmentWriter* writer,
return rv;
}
bool Http3Stream::Do0RTT() {
MOZ_ASSERT(mTransaction);
mAttempting0RTT = mTransaction->Do0RTT();
return mAttempting0RTT;
}
nsresult Http3Stream::Finish0RTT(bool aRestart) {
MOZ_ASSERT(mTransaction);
mAttempting0RTT = false;
nsresult rv = mTransaction->Finish0RTT(aRestart, false);
if (aRestart) {
nsHttpTransaction* trans = mTransaction->QueryHttpTransaction();
if (trans) {
trans->Refused0RTT();
}
}
mSendState = PREPARING_HEADERS;
mRecvState = READING_HEADERS;
mStreamId = UINT64_MAX;
mQueued = false;
mRequestBlockedOnRead = false;
mDataReceived = false;
mResetRecv = false;
mRequestBodyLenRemaining = 0;
mTotalSent = 0;
mTotalRead = 0;
mFin = false;
return rv;
}
} // namespace net
} // namespace mozilla

View File

@ -8,6 +8,7 @@
#include "nsAHttpTransaction.h"
#include "ARefBase.h"
#include "mozilla/WeakPtr.h"
namespace mozilla {
namespace net {
@ -16,6 +17,7 @@ class Http3Session;
class Http3Stream final : public nsAHttpSegmentReader,
public nsAHttpSegmentWriter,
public SupportsWeakPtr,
public ARefBase {
public:
NS_DECL_NSAHTTPSEGMENTREADER
@ -60,6 +62,10 @@ class Http3Stream final : public nsAHttpSegmentReader,
void SetResponseHeaders(nsTArray<uint8_t>& aResponseHeaders, bool fin);
// Mirrors nsAHttpTransaction
bool Do0RTT();
nsresult Finish0RTT(bool aRestart);
private:
~Http3Stream() = default;
@ -142,6 +148,8 @@ class Http3Stream final : public nsAHttpSegmentReader,
uint64_t mTotalRead;
bool mFin;
bool mAttempting0RTT = false;
};
} // namespace net

View File

@ -88,6 +88,10 @@ class NeqoHttp3Conn final {
neqo_http3conn_set_resumption_token(this, &aToken);
}
bool IsZeroRtt() {
return neqo_http3conn_is_zero_rtt(this);
}
nsrefcnt AddRef() { return neqo_http3conn_addref(this); }
nsrefcnt Release() { return neqo_http3conn_release(this); }

View File

@ -690,3 +690,8 @@ pub extern "C" fn neqo_http3conn_resumption_token(conn: &mut NeqoHttp3Conn, toke
pub extern "C" fn neqo_http3conn_set_resumption_token(conn: &mut NeqoHttp3Conn, token: &mut ThinVec<u8>,) {
let _ = conn.conn.set_resumption_token(Instant::now(), token);
}
#[no_mangle]
pub extern "C" fn neqo_http3conn_is_zero_rtt(conn: &mut NeqoHttp3Conn) -> bool {
conn.conn.state() == Http3State::ZeroRtt
}