mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-03 06:18:38 +09:00
Bug 1322373 - TLS 1.3 early-data for http/2
This commit is contained in:
parent
0f08cb0c4f
commit
202ca6d067
11 changed files with 342 additions and 82 deletions
|
|
@ -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.
|
||||
|
||||
|
|
@ -554,6 +573,12 @@ Http2Session::DontReuse()
|
|||
Close(NS_OK);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Http2Session::SpdyVersion()
|
||||
{
|
||||
return HTTP_VERSION_2;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Http2Session::GetWriteQueueSize()
|
||||
{
|
||||
|
|
@ -2328,7 +2353,36 @@ Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,
|
|||
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 "
|
||||
|
|
@ -2337,6 +2391,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
|
||||
|
|
@ -2892,6 +2953,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,
|
||||
|
|
@ -3100,7 +3213,9 @@ Http2Session::Close(nsresult aReason)
|
|||
} else {
|
||||
goAwayReason = INTERNAL_ERROR;
|
||||
}
|
||||
GenerateGoAway(goAwayReason);
|
||||
if (!mAttemptingEarlyData) {
|
||||
GenerateGoAway(goAwayReason);
|
||||
}
|
||||
mConnection = nullptr;
|
||||
mSegmentReader = nullptr;
|
||||
mSegmentWriter = nullptr;
|
||||
|
|
@ -3150,7 +3265,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) {
|
||||
|
|
@ -3521,12 +3636,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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue