diff options
Diffstat (limited to 'netwerk/protocol/http/Http2Session.cpp')
-rw-r--r-- | netwerk/protocol/http/Http2Session.cpp | 170 |
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; |