From 89c248fae88a5040746f1b09bb641d2fc6a742ce Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Tue, 1 Mar 2016 16:28:39 -0800 Subject: [PATCH] Bug 1246761 - Properly handle non-terminal 0-length DATA frames. r=mcmanus --- netwerk/protocol/http/Http2Session.cpp | 6 +++++ netwerk/test/unit/test_http2.js | 7 ++++++ testing/xpcshell/moz-http2/moz-http2.js | 32 +++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index a8c1a6e871f..b15435c97c1 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -2410,6 +2410,12 @@ Http2Session::ReadyToProcessDataFrame(enum internalStateType newState) if (mInputFrameDataStream->RecvdFin() || mInputFrameDataStream->RecvdReset()) GenerateRstStream(STREAM_CLOSED_ERROR, mInputFrameID); ChangeDownstreamState(DISCARDING_DATA_FRAME); + } else if (mInputFrameDataSize == 0 && !mInputFrameFinal) { + // Only if non-final because the stream properly handles final frames of any + // size, and we want the stream to be able to notice its own end flag. + LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X " + "Ignoring 0-length non-terminal data frame.", this, mInputFrameID)); + ChangeDownstreamState(DISCARDING_DATA_FRAME); } LOG3(("Start Processing Data Frame. " diff --git a/netwerk/test/unit/test_http2.js b/netwerk/test/unit/test_http2.js index 2d32dcc4867..93db583a04f 100644 --- a/netwerk/test/unit/test_http2.js +++ b/netwerk/test/unit/test_http2.js @@ -871,6 +871,12 @@ function test_http2_folded_header() { chan.asyncOpen2(listener); } +function test_http2_empty_data() { + var chan = makeChan("https://localhost:" + serverPort + "/emptydata"); + var listener = new Http2CheckListener(); + chan.asyncOpen2(listener); +} + function test_complete() { resetPrefs(); do_test_pending(); @@ -915,6 +921,7 @@ var tests = [ test_http2_post_big , test_http2_illegalhpacksoft , test_http2_illegalhpackhard , test_http2_folded_header + , test_http2_empty_data // Add new tests above here - best to add new tests before h1 // streams get too involved // These next two must always come in this order diff --git a/testing/xpcshell/moz-http2/moz-http2.js b/testing/xpcshell/moz-http2/moz-http2.js index 61ca3beb2a8..c2a1d155d6c 100644 --- a/testing/xpcshell/moz-http2/moz-http2.js +++ b/testing/xpcshell/moz-http2/moz-http2.js @@ -45,6 +45,31 @@ Connection.prototype.close = function (error, lastId) { originalClose.apply(this, arguments); } +var framer_module = node_http2_root + "/lib/protocol/framer"; +var http2_framer = require(framer_module); +var Serializer = http2_framer.Serializer; +var originalTransform = Serializer.prototype._transform; +var newTransform = function (frame, encoding, done) { + if (frame.type == 'DATA') { + // Insert our empty DATA frame + emptyFrame = {}; + emptyFrame.type = 'DATA'; + emptyFrame.data = new Buffer(0); + emptyFrame.flags = []; + emptyFrame.stream = frame.stream; + var buffers = []; + Serializer['DATA'](emptyFrame, buffers); + Serializer.commonHeader(emptyFrame, buffers); + for (var i = 0; i < buffers.length; i++) { + this.push(buffers[i]); + } + + // Reset to the original version for later uses + Serializer.prototype._transform = originalTransform; + } + originalTransform.apply(this, arguments); +}; + function getHttpContent(path) { var content = '' + '' + @@ -663,6 +688,13 @@ function handleRequest(req, res) { // Fall through to the default response behavior } + else if (u.pathname === "/emptydata") { + // Overwrite the original transform with our version that will insert an + // empty DATA frame at the beginning of the stream response, then fall + // through to the default response behavior. + Serializer.prototype._transform = newTransform; + } + res.setHeader('Content-Type', 'text/html'); if (req.httpVersionMajor != 2) { res.setHeader('Connection', 'close');