summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/Http2Session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/http/Http2Session.cpp')
-rw-r--r--netwerk/protocol/http/Http2Session.cpp170
1 files changed, 154 insertions, 16 deletions
diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp
index a2721017d..4f350af83 100644
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -64,7 +64,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)
@@ -112,6 +112,7 @@ Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t versio
, mWaitingForSettingsAck(false)
, mGoAwayOnPush(false)
, mUseH2Deps(false)
+ , mAttemptingEarlyData(attemptingEarlyData)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -501,6 +502,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,
@@ -518,6 +525,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);
@@ -528,14 +543,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.
@@ -555,6 +574,12 @@ Http2Session::DontReuse()
}
uint32_t
+Http2Session::SpdyVersion()
+{
+ return HTTP_VERSION_2;
+}
+
+uint32_t
Http2Session::GetWriteQueueSize()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -1019,6 +1044,15 @@ Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
return;
}
+ Http2PushedStream *pushSource = aStream->PushSource();
+ if (pushSource) {
+ // aStream is a synthetic attached to an even push
+ MOZ_ASSERT(pushSource->GetConsumerStream() == aStream);
+ MOZ_ASSERT(!aStream->StreamID());
+ MOZ_ASSERT(!(pushSource->StreamID() & 0x1));
+ aStream->ClearPushSource();
+ }
+
if (aStream->DeferCleanup(aResult)) {
LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID()));
return;
@@ -1029,15 +1063,6 @@ Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
return;
}
- Http2PushedStream *pushSource = aStream->PushSource();
- if (pushSource) {
- // aStream is a synthetic attached to an even push
- MOZ_ASSERT(pushSource->GetConsumerStream() == aStream);
- MOZ_ASSERT(!aStream->StreamID());
- MOZ_ASSERT(!(pushSource->StreamID() & 0x1));
- pushSource->SetConsumerStream(nullptr);
- }
-
// don't reset a stream that has recevied a fin or rst
if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID() &&
!(mInputFrameFinal && (aStream == mInputFrameDataStream))) { // !(recvdfin with mark pending)
@@ -2248,8 +2273,19 @@ Http2Session::OnTransportStatus(nsITransport* aTransport,
case NS_NET_STATUS_RESOLVED_HOST:
case NS_NET_STATUS_CONNECTING_TO:
case NS_NET_STATUS_CONNECTED_TO:
+ case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
+ case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
{
Http2Stream *target = mStreamIDHash.Get(1);
+ if (!target) {
+ // any transaction will do if we can't find the low numbered one
+ // generally this happens when the initial transaction hasn't been
+ // assigned a stream id yet.
+ auto iter = mStreamTransactionHash.Iter();
+ if (!iter.Done()) {
+ target = iter.Data();
+ }
+ }
nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
if (transaction)
transaction->OnTransportStatus(aTransport, aStatus, aProgress);
@@ -2320,9 +2356,44 @@ Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,
if (!stream) {
LOG3(("Http2Session %p could not identify a stream to write; suspending.",
this));
+ uint32_t availBeforeFlush = mOutputQueueUsed - mOutputQueueSent;
FlushOutputQueue();
+ uint32_t availAfterFlush = mOutputQueueUsed - mOutputQueueSent;
+ if (availBeforeFlush != availAfterFlush) {
+ LOG3(("Http2Session %p ResumeRecv After early flush in ReadSegments", this));
+ Unused << 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 "
@@ -2331,6 +2402,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
@@ -2887,6 +2965,58 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
}
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,
uint32_t count, uint32_t *countWritten)
@@ -3094,7 +3224,9 @@ Http2Session::Close(nsresult aReason)
} else {
goAwayReason = INTERNAL_ERROR;
}
- GenerateGoAway(goAwayReason);
+ if (!mAttemptingEarlyData) {
+ GenerateGoAway(goAwayReason);
+ }
mConnection = nullptr;
mSegmentReader = nullptr;
mSegmentWriter = nullptr;
@@ -3144,7 +3276,7 @@ Http2Session::OnReadSegment(const char *buf,
// If we can release old queued data then we can try and write the new
// data directly to the network without using the output queue at all
- if (mOutputQueueUsed)
+ if (mOutputQueueUsed && !mAttemptingEarlyData)
FlushOutputQueue();
if (!mOutputQueueUsed && mSegmentReader) {
@@ -3515,12 +3647,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;