Skip to content

Commit

Permalink
#594: M1322373
Browse files Browse the repository at this point in the history
  • Loading branch information
classilla committed Mar 17, 2020
1 parent d7c27ac commit d0b344e
Show file tree
Hide file tree
Showing 12 changed files with 342 additions and 82 deletions.
7 changes: 4 additions & 3 deletions netwerk/protocol/http/ASpdySession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ ASpdySession::~ASpdySession()

ASpdySession *
ASpdySession::NewSpdySession(uint32_t version,
nsISocketTransport *aTransport)
nsISocketTransport *aTransport,
bool attemptingEarlyData)
{
// This is a necko only interface, so we can enforce version
// requests as a precondition
Expand All @@ -49,12 +50,12 @@ ASpdySession::NewSpdySession(uint32_t version,
// from a list provided in the SERVER HELLO filtered by our acceptable
// versions, so there is no risk of the server ignoring our prefs.

Telemetry::Accumulate(Telemetry::SPDY_VERSION2, version);
//Telemetry::Accumulate(Telemetry::SPDY_VERSION2, version);

if (version == SPDY_VERSION_31) {
return new SpdySession31(aTransport);
} else if (version == HTTP_VERSION_2) {
return new Http2Session(aTransport, version);
return new Http2Session(aTransport, version, attemptingEarlyData);
}

return nullptr;
Expand Down
3 changes: 2 additions & 1 deletion netwerk/protocol/http/ASpdySession.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ class ASpdySession : public nsAHttpTransaction
virtual PRIntervalTime IdleTime() = 0;
virtual uint32_t ReadTimeoutTick(PRIntervalTime now) = 0;
virtual void DontReuse() = 0;
virtual uint32_t SpdyVersion() = 0;

static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *);
static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *, bool);

// MaybeReTunnel() is called by the connection manager when it cannot
// dispatch a tunneled transaction. That might be because the tunnels it
Expand Down
135 changes: 128 additions & 7 deletions netwerk/protocol/http/Http2Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ do { \
return NS_ERROR_ILLEGAL_VALUE; \
} while (0)

Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t version)
Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t version, bool attemptingEarlyData)
: mSocketTransport(aSocketTransport)
, mSegmentReader(nullptr)
, mSegmentWriter(nullptr)
Expand Down Expand Up @@ -110,6 +110,7 @@ Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t versio
, mWaitingForSettingsAck(false)
, mGoAwayOnPush(false)
, mUseH2Deps(false)
, mAttemptingEarlyData(attemptingEarlyData)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);

Expand Down Expand Up @@ -510,6 +511,12 @@ Http2Session::SetWriteCallbacks()
void
Http2Session::RealignOutputQueue()
{
if (mAttemptingEarlyData) {
// We can't realign right now, because we may need what's in there if early
// data fails.
return;
}

mOutputQueueUsed -= mOutputQueueSent;
memmove(mOutputQueueBuffer.get(),
mOutputQueueBuffer.get() + mOutputQueueSent,
Expand All @@ -527,6 +534,14 @@ Http2Session::FlushOutputQueue()
uint32_t countRead;
uint32_t avail = mOutputQueueUsed - mOutputQueueSent;

if (!avail && mAttemptingEarlyData) {
// This is kind of a hack, but there are cases where we'll have already
// written the data we want whlie doing early data, but we get called again
// with a reader, and we need to avoid calling the reader when there's
// nothing for it to read.
return;
}

rv = mSegmentReader->
OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
&countRead);
Expand All @@ -537,14 +552,18 @@ Http2Session::FlushOutputQueue()
if (NS_FAILED(rv))
return;

mOutputQueueSent += countRead;

if (mAttemptingEarlyData) {
return;
}

if (countRead == avail) {
mOutputQueueUsed = 0;
mOutputQueueSent = 0;
return;
}

mOutputQueueSent += countRead;

// If the output queue is close to filling up and we have sent out a good
// chunk of data from the beginning then realign it.

Expand All @@ -562,6 +581,12 @@ Http2Session::DontReuse()
Close(NS_OK);
}

uint32_t
Http2Session::SpdyVersion()
{
return HTTP_VERSION_2;
}

uint32_t
Http2Session::GetWriteQueueSize()
{
Expand Down Expand Up @@ -2316,7 +2341,36 @@ Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,
ResumeRecv();
}
SetWriteCallbacks();
return NS_BASE_STREAM_WOULD_BLOCK;
if (mAttemptingEarlyData) {
// We can still try to send our preamble as early-data
*countRead = mOutputQueueUsed - mOutputQueueSent;
}
return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
}

uint32_t earlyDataUsed = 0;
if (mAttemptingEarlyData) {
if (!stream->Do0RTT()) {
LOG3(("Http2Session %p will not get early data from Http2Stream %p 0x%X",
this, stream, stream->StreamID()));
FlushOutputQueue();
SetWriteCallbacks();
// We can still send our preamble
*countRead = mOutputQueueUsed - mOutputQueueSent;
return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
}

if (!m0RTTStreams.Contains(stream->StreamID())) {
m0RTTStreams.AppendElement(stream->StreamID());
}

// Need to adjust this to only take as much as we can fit in with the
// preamble/settings/priority stuff
count -= (mOutputQueueUsed - mOutputQueueSent);

// Keep track of this to add it into countRead later, as
// stream->ReadSegments will likely change the value of mOutputQueueUsed.
earlyDataUsed = mOutputQueueUsed - mOutputQueueSent;
}

LOG3(("Http2Session %p will write from Http2Stream %p 0x%X "
Expand All @@ -2325,6 +2379,13 @@ Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,

rv = stream->ReadSegments(this, count, countRead);

if (earlyDataUsed) {
// Do this here because countRead could get reset somewhere down the rabbit
// hole of stream->ReadSegments, and we want to make sure we return the
// proper value to our caller.
*countRead += earlyDataUsed;
}

// Not every permutation of stream->ReadSegents produces data (and therefore
// tries to flush the output queue) - SENDING_FIN_STREAM can be an example
// of that. But we might still have old data buffered that would be good
Expand Down Expand Up @@ -2873,6 +2934,58 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
return WriteSegmentsAgain(writer, count, countWritten, &again);
}

nsresult
Http2Session::Finish0RTT(bool aRestart, bool aAlpnChanged)
{
MOZ_ASSERT(mAttemptingEarlyData);
LOG3(("Http2Session::Finish0RTT %p aRestart=%d aAlpnChanged=%d", this,
aRestart, aAlpnChanged));

for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
// Instead of passing (aRestart, aAlpnChanged) here, we use aAlpnChanged for
// both arguments because as long as the alpn token stayed the same, we can
// just reuse what we have in our buffer to send instead of having to have
// the transaction rewind and read it all over again. We only need to rewind
// the transaction if we're switching to a new protocol, because our buffer
// won't get used in that case.
Http2Stream *stream = mStreamIDHash.Get(m0RTTStreams[i]);
if (stream) {
stream->Finish0RTT(aAlpnChanged, aAlpnChanged);
}
}

if (aRestart) {
// 0RTT failed
if (aAlpnChanged) {
// This is a slightly more involved case - we need to get all our streams/
// transactions back in the queue so they can restart as http/1

// These must be set this way to ensure we gracefully restart all streams
mGoAwayID = 0;
mCleanShutdown = true;

// Close takes care of the rest of our work for us. The reason code here
// doesn't matter, as we aren't actually going to send a GOAWAY frame, but
// we use NS_ERROR_NET_RESET as it's closest to the truth.
Close(NS_ERROR_NET_RESET);
} else {
// This is the easy case - early data failed, but we're speaking h2, so
// we just need to rewind to the beginning of the preamble and try again.
mOutputQueueSent = 0;
}
} else {
// 0RTT succeeded
// Make sure we look for any incoming data in repsonse to our early data.
ResumeRecv();
}

mAttemptingEarlyData = false;
m0RTTStreams.Clear();
RealignOutputQueue();

return NS_OK;
}

nsresult
Http2Session::ProcessConnectedPush(Http2Stream *pushConnectedStream,
nsAHttpSegmentWriter * writer,
Expand Down Expand Up @@ -3080,7 +3193,9 @@ Http2Session::Close(nsresult aReason)
} else {
goAwayReason = INTERNAL_ERROR;
}
GenerateGoAway(goAwayReason);
if (!mAttemptingEarlyData) {
GenerateGoAway(goAwayReason);
}
mConnection = nullptr;
mSegmentReader = nullptr;
mSegmentWriter = nullptr;
Expand Down Expand Up @@ -3181,7 +3296,7 @@ Http2Session::OnReadSegment(const char *buf,
nsresult
Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment)
{
if (mOutputQueueUsed)
if (mOutputQueueUsed && !mAttemptingEarlyData)
FlushOutputQueue();

// would there be enough room to buffer this if needed?
Expand Down Expand Up @@ -3500,12 +3615,18 @@ Http2Session::ALPNCallback(nsISupports *securityInfo)
nsresult
Http2Session::ConfirmTLSProfile()
{
if (mTLSProfileConfirmed)
if (mTLSProfileConfirmed) {
return NS_OK;
}

LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n",
this, mConnection.get()));

if (mAttemptingEarlyData) {
LOG3(("Http2Session::ConfirmTLSProfile %p temporarily passing due to early data\n", this));
return NS_OK;
}

if (!gHttpHandler->EnforceHttp2TlsProfile()) {
LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this));
mTLSProfileConfirmed = true;
Expand Down
9 changes: 8 additions & 1 deletion netwerk/protocol/http/Http2Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ class Http2Session final : public ASpdySession
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER

Http2Session(nsISocketTransport *, uint32_t version);
Http2Session(nsISocketTransport *, uint32_t version, bool attemptingEarlyData);

bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *) override;
bool CanReuse() override { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams() override;
uint32_t SpdyVersion() override;

// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
Expand Down Expand Up @@ -235,6 +236,8 @@ class Http2Session final : public ASpdySession
// overload of nsAHttpTransaction
nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final;
nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final;
bool Do0RTT() override final { return true; }
nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) override final;

private:

Expand Down Expand Up @@ -505,6 +508,10 @@ class Http2Session final : public ASpdySession

bool mUseH2Deps;

bool mAttemptingEarlyData;
// The ID(s) of the stream(s) that we are getting 0RTT data from.
nsTArray<uint32_t> m0RTTStreams;

private:
/// connect tunnels
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
Expand Down
25 changes: 24 additions & 1 deletion netwerk/protocol/http/Http2Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
, mTotalSent(0)
, mTotalRead(0)
, mPushSource(nullptr)
, mAttempting0RTT(false)
, mIsTunnel(false)
, mPlainTextTunnel(false)
{
Expand Down Expand Up @@ -950,7 +951,9 @@ Http2Stream::TransmitFrame(const char *buf,
*countUsed += mTxStreamFrameSize;
}

mSession->FlushOutputQueue();
if (!mAttempting0RTT) {
mSession->FlushOutputQueue();
}

// calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
Expand Down Expand Up @@ -1485,6 +1488,26 @@ Http2Stream::MapStreamToHttpConnection()
mTransaction->ConnectionInfo());
}

// -----------------------------------------------------------------------------
// mirror nsAHttpTransaction
// -----------------------------------------------------------------------------

bool
Http2Stream::Do0RTT()
{
MOZ_ASSERT(mTransaction);
mAttempting0RTT = true;
return mTransaction->Do0RTT();
}

nsresult
Http2Stream::Finish0RTT(bool aRestart, bool aAlpnChanged)
{
MOZ_ASSERT(mTransaction);
mAttempting0RTT = false;
return mTransaction->Finish0RTT(aRestart, aAlpnChanged);
}

} // namespace net
} // namespace mozilla

6 changes: 6 additions & 0 deletions netwerk/protocol/http/Http2Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ class Http2Stream
const nsACString &origin,
RefPtr<nsStandardURL> &url);

// Mirrors nsAHttpTransaction
bool Do0RTT();
nsresult Finish0RTT(bool aRestart, bool aAlpnIgnored);

protected:
static void CreatePushHashKey(const nsCString &scheme,
const nsCString &hostHeader,
Expand Down Expand Up @@ -328,6 +332,8 @@ class Http2Stream
nsCOMPtr<nsIInputStream> mInputBufferIn;
nsCOMPtr<nsIOutputStream> mInputBufferOut;

bool mAttempting0RTT;

/// connect tunnels
public:
bool IsTunnel() { return mIsTunnel; }
Expand Down
1 change: 1 addition & 0 deletions netwerk/protocol/http/SpdySession31.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class SpdySession31 final : public ASpdySession
bool, nsIInterfaceRequestor *) override;
bool CanReuse() override { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams() override;
uint32_t SpdyVersion() override { return SPDY_VERSION_31; }

// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
Expand Down
5 changes: 4 additions & 1 deletion netwerk/protocol/http/nsAHttpTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,11 @@ class nsAHttpTransaction : public nsSupportsWeakReference
// If aRestart parameter is true we need to restart the transaction,
// otherwise the erly-data has been accepted and we can continue the
// transaction.
// If aAlpnChanged is true (and we were assuming http/2), we'll need to take
// the transactions out of the session, rewind them all, and start them back
// over as http/1 transactions
// The function will return success or failure of the transaction restart.
virtual nsresult Finish0RTT(bool aRestart) {
virtual nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) {
return NS_ERROR_NOT_IMPLEMENTED;
}
};
Expand Down
Loading

0 comments on commit d0b344e

Please sign in to comment.