bug 950768 - http/2-draft08 pref off r=hurley r=mcmanus
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 09 Oct 2013 17:21:49 -0700
changeset 163059 088897ae34f208d78f20a7469703d838321a300d
parent 163058 d48dc641d5c3116e26016e14cb6251e8ee97ec35
child 163060 b35dc4d2e1b9509ea58e0aa55ea5f861b2b6eafb
push id25979
push usercbook@mozilla.com
push dateMon, 13 Jan 2014 11:46:02 +0000
treeherdermozilla-central@ea6657f1d682 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershurley, mcmanus
bugs950768
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 950768 - http/2-draft08 pref off r=hurley r=mcmanus
modules/libpref/src/init/all.js
netwerk/base/src/Dashboard.cpp
netwerk/protocol/http/ASpdySession.cpp
netwerk/protocol/http/ASpdySession.h
netwerk/protocol/http/ConnectionDiagnostics.cpp
netwerk/protocol/http/Http2Compression.cpp
netwerk/protocol/http/Http2Compression.h
netwerk/protocol/http/Http2HuffmanIncoming.h
netwerk/protocol/http/Http2HuffmanOutgoing.h
netwerk/protocol/http/Http2Push.cpp
netwerk/protocol/http/Http2Push.h
netwerk/protocol/http/Http2Session.cpp
netwerk/protocol/http/Http2Session.h
netwerk/protocol/http/Http2Stream.cpp
netwerk/protocol/http/Http2Stream.h
netwerk/protocol/http/PSpdyPush.h
netwerk/protocol/http/SpdySession3.cpp
netwerk/protocol/http/SpdySession31.cpp
netwerk/protocol/http/huff_incoming.txt
netwerk/protocol/http/huff_outgoing.txt
netwerk/protocol/http/make_incoming_tables.py
netwerk/protocol/http/make_outgoing_tables.py
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttp.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -986,25 +986,27 @@ pref("network.http.bypass-cachelock-thre
 #else
 pref("network.http.bypass-cachelock-threshold", 250);
 #endif
 
 // Try and use SPDY when using SSL
 pref("network.http.spdy.enabled", true);
 pref("network.http.spdy.enabled.v3", true);
 pref("network.http.spdy.enabled.v3-1", true);
+pref("network.http.spdy.enabled.http2draft", false);
+pref("network.http.spdy.enforce-tls-profile", true);
 pref("network.http.spdy.chunk-size", 4096);
 pref("network.http.spdy.timeout", 180);
 pref("network.http.spdy.coalesce-hostnames", true);
 pref("network.http.spdy.persistent-settings", false);
 pref("network.http.spdy.ping-threshold", 58);
 pref("network.http.spdy.ping-timeout", 8);
 pref("network.http.spdy.send-buffer-size", 131072);
 pref("network.http.spdy.allow-push", true);
-pref("network.http.spdy.push-allowance", 65536);
+pref("network.http.spdy.push-allowance", 131072);
 
 pref("network.http.diagnostics", false);
 
 pref("network.http.pacing.requests.enabled", true);
 pref("network.http.pacing.requests.min-parallelism", 6);
 pref("network.http.pacing.requests.hz", 100);
 pref("network.http.pacing.requests.burst", 32);
 
--- a/netwerk/base/src/Dashboard.cpp
+++ b/netwerk/base/src/Dashboard.cpp
@@ -504,21 +504,23 @@ HttpConnInfo::SetHTTP1ProtocolVersion(ui
     default:
         protocolVersion.Assign(NS_LITERAL_STRING("unknown protocol version"));
     }
 }
 
 void
 HttpConnInfo::SetHTTP2ProtocolVersion(uint8_t pv)
 {
-    if (pv == SPDY_VERSION_3)
+    if (pv == SPDY_VERSION_3) {
         protocolVersion.Assign(NS_LITERAL_STRING("spdy/3"));
-    else {
-        MOZ_ASSERT (pv == SPDY_VERSION_31);
+    } else if (pv == SPDY_VERSION_31) {
         protocolVersion.Assign(NS_LITERAL_STRING("spdy/3.1"));
+    } else {
+        MOZ_ASSERT (pv == NS_HTTP2_DRAFT_VERSION);
+        protocolVersion.Assign(NS_LITERAL_STRING(NS_HTTP2_DRAFT_TOKEN));
     }
 }
 
 NS_IMETHODIMP
 Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort,
                              const char *aProtocol, uint32_t aTimeout,
                              NetDashboardCallback* cb)
 {
--- a/netwerk/protocol/http/ASpdySession.cpp
+++ b/netwerk/protocol/http/ASpdySession.cpp
@@ -3,79 +3,92 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 /*
-  Currently supported are spdy/3.1 and spdy/3
-
+  Currently supported are HTTP-draft-[see nshttp.h]/2.0 spdy/3.1 and spdy/3
 */
 
 #include "nsHttp.h"
 #include "nsHttpHandler.h"
 
 #include "ASpdySession.h"
 #include "PSpdyPush.h"
 #include "SpdyPush3.h"
 #include "SpdyPush31.h"
+#include "Http2Push.h"
 #include "SpdySession3.h"
 #include "SpdySession31.h"
+#include "Http2Session.h"
 
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
 namespace net {
 
 ASpdySession *
 ASpdySession::NewSpdySession(uint32_t version,
                              nsAHttpTransaction *aTransaction,
                              nsISocketTransport *aTransport,
                              int32_t aPriority)
 {
   // This is a necko only interface, so we can enforce version
   // requests as a precondition
   MOZ_ASSERT(version == SPDY_VERSION_3 ||
-             version == SPDY_VERSION_31 ,
+             version == SPDY_VERSION_31 ||
+             version == NS_HTTP2_DRAFT_VERSION,
              "Unsupported spdy version");
 
   // Don't do a runtime check of IsSpdyV?Enabled() here because pref value
   // may have changed since starting negotiation. The selected protocol comes
   // 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);
 
   if (version == SPDY_VERSION_3)
     return new SpdySession3(aTransaction, aTransport, aPriority);
 
-  return new SpdySession31(aTransaction, aTransport, aPriority);
+  if (version == SPDY_VERSION_31)
+    return new SpdySession31(aTransaction, aTransport, aPriority);
+
+  if (version == NS_HTTP2_DRAFT_VERSION)
+    return new Http2Session(aTransaction, aTransport, aPriority);
+
+  return nullptr;
 }
 
 SpdyInformation::SpdyInformation()
 {
   Version[0] = SPDY_VERSION_3;
   VersionString[0] = NS_LITERAL_CSTRING("spdy/3");
 
   Version[1] = SPDY_VERSION_31;
   VersionString[1] = NS_LITERAL_CSTRING("spdy/3.1");
+
+  Version[2] = NS_HTTP2_DRAFT_VERSION;
+  VersionString[2] = NS_LITERAL_CSTRING(NS_HTTP2_DRAFT_TOKEN);
 }
 
 bool
 SpdyInformation::ProtocolEnabled(uint32_t index)
 {
   MOZ_ASSERT(index < kCount, "index out of range");
 
   switch (index) {
   case 0:
     return gHttpHandler->IsSpdyV3Enabled();
   case 1:
     return gHttpHandler->IsSpdyV31Enabled();
+  case 2:
+    return gHttpHandler->IsHttp2DraftEnabled();
   }
   return false;
 }
 
 nsresult
 SpdyInformation::GetNPNVersionIndex(const nsACString &npnString,
                                     uint8_t *result)
 {
@@ -99,16 +112,17 @@ SpdyInformation::GetNPNVersionIndex(cons
 SpdyPushCache::SpdyPushCache()
 {
 }
 
 SpdyPushCache::~SpdyPushCache()
 {
   mHashSpdy3.Clear();
   mHashSpdy31.Clear();
+  mHashHttp2.Clear();
 }
 
 bool
 SpdyPushCache::RegisterPushedStreamSpdy3(nsCString key,
                                          SpdyPushedStream3 *stream)
 {
   LOG3(("SpdyPushCache::RegisterPushedStreamSpdy3 %s 0x%X\n",
         key.get(), stream->StreamID()));
@@ -147,11 +161,34 @@ SpdyPushCache::RemovePushedStreamSpdy31(
   SpdyPushedStream31 *rv = mHashSpdy31.Get(key);
   LOG3(("SpdyPushCache::RemovePushedStream %s 0x%X\n",
         key.get(), rv ? rv->StreamID() : 0));
   if (rv)
     mHashSpdy31.Remove(key);
   return rv;
 }
 
+bool
+SpdyPushCache::RegisterPushedStreamHttp2(nsCString key,
+                                         Http2PushedStream *stream)
+{
+  LOG3(("SpdyPushCache::RegisterPushedStreamHttp2 %s 0x%X\n",
+        key.get(), stream->StreamID()));
+  if(mHashHttp2.Get(key))
+    return false;
+  mHashHttp2.Put(key, stream);
+  return true;
+}
+
+Http2PushedStream *
+SpdyPushCache::RemovePushedStreamHttp2(nsCString key)
+{
+  Http2PushedStream *rv = mHashHttp2.Get(key);
+  LOG3(("SpdyPushCache::RemovePushedStreamHttp2 %s 0x%X\n",
+        key.get(), rv ? rv->StreamID() : 0));
+  if (rv)
+    mHashHttp2.Remove(key);
+  return rv;
+}
+
 } // namespace mozilla::net
 } // namespace mozilla
 
--- a/netwerk/protocol/http/ASpdySession.h
+++ b/netwerk/protocol/http/ASpdySession.h
@@ -31,17 +31,17 @@ public:
                                       int32_t);
 
   virtual void PrintDiagnostics (nsCString &log) = 0;
 
   bool ResponseTimeoutEnabled() const MOZ_OVERRIDE MOZ_FINAL {
     return true;
   }
 
-  const static uint32_t kSendingChunkSize = 4096;
+  const static uint32_t kSendingChunkSize = 4095;
   const static uint32_t kTCPSendBufferSize = 131072;
 
   // until we have an API that can push back on receiving data (right now
   // WriteSegments is obligated to accept data and buffer) there is no
   // reason to throttle with the rwin other than in server push
   // scenarios.
   const static uint32_t kInitialRwin = 256 * 1024 * 1024;
 
@@ -57,17 +57,17 @@ public:
 // It could be all static except using static ctors of XPCOM objects is a
 // bad idea.
 class SpdyInformation
 {
 public:
   SpdyInformation();
   ~SpdyInformation() {}
 
-  static const uint32_t kCount = 2;
+  static const uint32_t kCount = 3;
 
   // determine if a version of the protocol is enabled for index <= kCount
   bool ProtocolEnabled(uint32_t index);
 
   // lookup a version enum based on an npn string. returns NS_OK if
   // string was known.
   nsresult GetNPNVersionIndex(const nsACString &npnString, uint8_t *result);
 
--- a/netwerk/protocol/http/ConnectionDiagnostics.cpp
+++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp
@@ -6,16 +6,17 @@
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "nsHttpConnectionMgr.h"
 #include "nsHttpConnection.h"
 #include "SpdySession3.h"
 #include "SpdySession31.h"
+#include "Http2Session.h"
 #include "nsHttpHandler.h"
 #include "nsIConsoleService.h"
 #include "nsHttpRequestHead.h"
 
 extern PRThread *gSocketThread;
 
 namespace mozilla {
 namespace net {
@@ -231,16 +232,52 @@ SpdySession31::PrintDiagnostics(nsCStrin
     log.AppendPrintf("     Ping Outstanding (ping) = %ums, expired = %d\n",
                      PR_IntervalToMilliseconds(now - mPingSentEpoch),
                      now - mPingSentEpoch >= gHttpHandler->SpdyPingTimeout());
   else
     log.AppendPrintf("     No Ping Outstanding\n");
 }
 
 void
+Http2Session::PrintDiagnostics(nsCString &log)
+{
+  log.AppendPrintf("     ::: HTTP2\n");
+  log.AppendPrintf("     shouldgoaway = %d mClosed = %d CanReuse = %d nextID=0x%X\n",
+                   mShouldGoAway, mClosed, CanReuse(), mNextStreamID);
+
+  log.AppendPrintf("     concurrent = %d maxconcurrent = %d\n",
+                   mConcurrent, mMaxConcurrent);
+
+  log.AppendPrintf("     roomformorestreams = %d roomformoreconcurrent = %d\n",
+                   RoomForMoreStreams(), RoomForMoreConcurrent());
+
+  log.AppendPrintf("     transactionHashCount = %d streamIDHashCount = %d\n",
+                   mStreamTransactionHash.Count(),
+                   mStreamIDHash.Count());
+
+  log.AppendPrintf("     Queued Stream Size = %d\n", mQueuedStreams.GetSize());
+
+  PRIntervalTime now = PR_IntervalNow();
+  log.AppendPrintf("     Ping Threshold = %ums\n",
+                   PR_IntervalToMilliseconds(mPingThreshold));
+  log.AppendPrintf("     Ping Timeout = %ums\n",
+                   PR_IntervalToMilliseconds(gHttpHandler->SpdyPingTimeout()));
+  log.AppendPrintf("     Idle for Any Activity (ping) = %ums\n",
+                   PR_IntervalToMilliseconds(now - mLastReadEpoch));
+  log.AppendPrintf("     Idle for Data Activity = %ums\n",
+                   PR_IntervalToMilliseconds(now - mLastDataReadEpoch));
+  if (mPingSentEpoch)
+    log.AppendPrintf("     Ping Outstanding (ping) = %ums, expired = %d\n",
+                     PR_IntervalToMilliseconds(now - mPingSentEpoch),
+                     now - mPingSentEpoch >= gHttpHandler->SpdyPingTimeout());
+  else
+    log.AppendPrintf("     No Ping Outstanding\n");
+}
+
+void
 nsHttpTransaction::PrintDiagnostics(nsCString &log)
 {
   if (!mRequestHead)
     return;
 
   log.AppendPrintf("     ::: uri = %s\n",
                    nsAutoCString(mRequestHead->RequestURI()).get());
   log.AppendPrintf("     caps = 0x%x\n", mCaps);
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -0,0 +1,1279 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include "Http2Compression.h"
+#include "Http2HuffmanIncoming.h"
+#include "Http2HuffmanOutgoing.h"
+
+extern PRThread *gSocketThread;
+
+namespace mozilla {
+namespace net {
+
+static nsDeque *gStaticHeaders = nullptr;
+
+void
+Http2CompressionCleanup()
+{
+  // this happens after the socket thread has been destroyed
+  delete gStaticHeaders;
+  gStaticHeaders = nullptr;
+}
+
+static void
+AddStaticElement(const nsCString &name, const nsCString &value)
+{
+  nvPair *pair = new nvPair(name, value);
+  gStaticHeaders->Push(pair);
+}
+
+static void
+AddStaticElement(const nsCString &name)
+{
+  AddStaticElement(name, EmptyCString());
+}
+
+static void
+InitializeStaticHeaders()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  if (!gStaticHeaders) {
+    gStaticHeaders = new nsDeque();
+    AddStaticElement(NS_LITERAL_CSTRING(":authority"));
+    AddStaticElement(NS_LITERAL_CSTRING(":method"), NS_LITERAL_CSTRING("GET"));
+    AddStaticElement(NS_LITERAL_CSTRING(":method"), NS_LITERAL_CSTRING("POST"));
+    AddStaticElement(NS_LITERAL_CSTRING(":path"), NS_LITERAL_CSTRING("/"));
+    AddStaticElement(NS_LITERAL_CSTRING(":path"), NS_LITERAL_CSTRING("/index.html"));
+    AddStaticElement(NS_LITERAL_CSTRING(":scheme"), NS_LITERAL_CSTRING("http"));
+    AddStaticElement(NS_LITERAL_CSTRING(":scheme"), NS_LITERAL_CSTRING("https"));
+    AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("200"));
+    AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("500"));
+    AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("404"));
+    AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("403"));
+    AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("400"));
+    AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("401"));
+    AddStaticElement(NS_LITERAL_CSTRING("accept-charset"));
+    AddStaticElement(NS_LITERAL_CSTRING("accept-encoding"));
+    AddStaticElement(NS_LITERAL_CSTRING("accept-language"));
+    AddStaticElement(NS_LITERAL_CSTRING("accept-ranges"));
+    AddStaticElement(NS_LITERAL_CSTRING("accept"));
+    AddStaticElement(NS_LITERAL_CSTRING("access-control-allow-origin"));
+    AddStaticElement(NS_LITERAL_CSTRING("age"));
+    AddStaticElement(NS_LITERAL_CSTRING("allow"));
+    AddStaticElement(NS_LITERAL_CSTRING("authorization"));
+    AddStaticElement(NS_LITERAL_CSTRING("cache-control"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-disposition"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-encoding"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-language"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-length"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-location"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-range"));
+    AddStaticElement(NS_LITERAL_CSTRING("content-type"));
+    AddStaticElement(NS_LITERAL_CSTRING("cookie"));
+    AddStaticElement(NS_LITERAL_CSTRING("date"));
+    AddStaticElement(NS_LITERAL_CSTRING("etag"));
+    AddStaticElement(NS_LITERAL_CSTRING("expect"));
+    AddStaticElement(NS_LITERAL_CSTRING("expires"));
+    AddStaticElement(NS_LITERAL_CSTRING("from"));
+    AddStaticElement(NS_LITERAL_CSTRING("if-match"));
+    AddStaticElement(NS_LITERAL_CSTRING("if-modified-since"));
+    AddStaticElement(NS_LITERAL_CSTRING("if-none-match"));
+    AddStaticElement(NS_LITERAL_CSTRING("if-range"));
+    AddStaticElement(NS_LITERAL_CSTRING("if-unmodified-since"));
+    AddStaticElement(NS_LITERAL_CSTRING("last-modified"));
+    AddStaticElement(NS_LITERAL_CSTRING("link"));
+    AddStaticElement(NS_LITERAL_CSTRING("location"));
+    AddStaticElement(NS_LITERAL_CSTRING("max-forwards"));
+    AddStaticElement(NS_LITERAL_CSTRING("proxy-authenticate"));
+    AddStaticElement(NS_LITERAL_CSTRING("proxy-authorization"));
+    AddStaticElement(NS_LITERAL_CSTRING("range"));
+    AddStaticElement(NS_LITERAL_CSTRING("referer"));
+    AddStaticElement(NS_LITERAL_CSTRING("refresh"));
+    AddStaticElement(NS_LITERAL_CSTRING("retry-after"));
+    AddStaticElement(NS_LITERAL_CSTRING("server"));
+    AddStaticElement(NS_LITERAL_CSTRING("set-cookie"));
+    AddStaticElement(NS_LITERAL_CSTRING("strict-transport-security"));
+    AddStaticElement(NS_LITERAL_CSTRING("transfer-encoding"));
+    AddStaticElement(NS_LITERAL_CSTRING("user-agent"));
+    AddStaticElement(NS_LITERAL_CSTRING("vary"));
+    AddStaticElement(NS_LITERAL_CSTRING("via"));
+    AddStaticElement(NS_LITERAL_CSTRING("www-authenticate"));
+  }
+}
+
+nvFIFO::nvFIFO()
+  : mByteCount(0)
+  , mTable()
+{
+  InitializeStaticHeaders();
+}
+
+nvFIFO::~nvFIFO()
+{
+  Clear();
+}
+
+void
+nvFIFO::AddElement(const nsCString &name, const nsCString &value)
+{
+  mByteCount += name.Length() + value.Length() + 32;
+  nvPair *pair = new nvPair(name, value);
+  mTable.PushFront(pair);
+}
+
+void
+nvFIFO::AddElement(const nsCString &name)
+{
+  AddElement(name, EmptyCString());
+}
+
+void
+nvFIFO::RemoveElement()
+{
+  nvPair *pair = static_cast<nvPair *>(mTable.Pop());
+  if (pair) {
+    mByteCount -= pair->Size();
+    delete pair;
+  }
+}
+
+uint32_t
+nvFIFO::ByteCount() const
+{
+  return mByteCount;
+}
+
+uint32_t
+nvFIFO::Length() const
+{
+  return mTable.GetSize() + gStaticHeaders->GetSize();
+}
+
+uint32_t
+nvFIFO::VariableLength() const
+{
+  return mTable.GetSize();
+}
+
+void
+nvFIFO::Clear()
+{
+  mByteCount = 0;
+  while (mTable.GetSize())
+    delete static_cast<nvPair *>(mTable.Pop());
+}
+
+const nvPair *
+nvFIFO::operator[] (int32_t index) const
+{
+  if (index >= (mTable.GetSize() + gStaticHeaders->GetSize())) {
+    MOZ_ASSERT(false);
+    NS_WARNING("nvFIFO Table Out of Range");
+    return nullptr;
+  }
+  if (index >= mTable.GetSize()) {
+    return static_cast<nvPair *>(gStaticHeaders->ObjectAt(index - mTable.GetSize()));
+  }
+  return static_cast<nvPair *>(mTable.ObjectAt(index));
+}
+
+Http2BaseCompressor::Http2BaseCompressor()
+  : mOutput(nullptr)
+  , mMaxBuffer(kDefaultMaxBuffer)
+{
+}
+
+void
+Http2BaseCompressor::ClearHeaderTable()
+{
+  uint32_t dynamicCount = mHeaderTable.VariableLength();
+  mHeaderTable.Clear();
+
+  for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
+    if (mReferenceSet[i] < dynamicCount) {
+      mReferenceSet.RemoveElementAt(i);
+    } else {
+      mReferenceSet[i] -= dynamicCount;
+    }
+  }
+
+  for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
+    if (mAlternateReferenceSet[i] < dynamicCount) {
+      mAlternateReferenceSet.RemoveElementAt(i);
+    } else {
+      mAlternateReferenceSet[i] -= dynamicCount;
+    }
+  }
+}
+
+void
+Http2BaseCompressor::UpdateReferenceSet(int32_t delta)
+{
+  if (!delta)
+    return;
+
+  uint32_t headerTableSize = mHeaderTable.VariableLength();
+  uint32_t oldHeaderTableSize = headerTableSize + delta;
+
+  for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
+    uint32_t indexRef = mReferenceSet[i];
+    if (indexRef >= headerTableSize) {
+      if (indexRef < oldHeaderTableSize) {
+        // This one got dropped
+        LOG3(("HTTP base compressor reference to index %u removed.\n",
+              indexRef));
+        mReferenceSet.RemoveElementAt(i);
+      } else {
+        // This pointed to the static table, need to adjust
+        uint32_t newRef = indexRef - delta;
+        LOG3(("HTTP base compressor reference to index %u changed to %d (%s)\n",
+              mReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get()));
+        mReferenceSet[i] = newRef;
+      }
+    }
+  }
+
+  for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
+    uint32_t indexRef = mAlternateReferenceSet[i];
+    if (indexRef >= headerTableSize) {
+      if (indexRef < oldHeaderTableSize) {
+        // This one got dropped
+        LOG3(("HTTP base compressor new reference to index %u removed.\n",
+              indexRef));
+        mAlternateReferenceSet.RemoveElementAt(i);
+      } else {
+        // This pointed to the static table, need to adjust
+        uint32_t newRef = indexRef - delta;
+        LOG3(("HTTP base compressor new reference to index %u changed to %d (%s)\n",
+              mAlternateReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get()));
+        mAlternateReferenceSet[i] = newRef;
+      }
+    }
+  }
+}
+
+void
+Http2BaseCompressor::IncrementReferenceSetIndices()
+{
+  for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
+    mReferenceSet[i] = mReferenceSet[i] + 1;
+  }
+
+  for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
+    mAlternateReferenceSet[i] = mAlternateReferenceSet[i] + 1;
+  }
+}
+
+nsresult
+Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
+                                     nsACString &output)
+{
+  mAlternateReferenceSet.Clear();
+  mOffset = 0;
+  mData = data;
+  mDataLen = datalen;
+  mOutput = &output;
+  mOutput->Truncate();
+  mHeaderStatus.Truncate();
+  mHeaderHost.Truncate();
+  mHeaderScheme.Truncate();
+  mHeaderPath.Truncate();
+  mHeaderMethod.Truncate();
+
+  nsresult rv = NS_OK;
+  while (NS_SUCCEEDED(rv) && (mOffset < datalen)) {
+    if (mData[mOffset] & 0x80) {
+      rv = DoIndexed();
+    } else if (mData[mOffset] & 0x40) {
+      rv = DoLiteralWithoutIndex();
+    } else {
+      rv = DoLiteralWithIncremental();
+    }
+  }
+
+  // after processing the input the decompressor comapres the alternate
+  // set to the inherited reference set and generates headers for
+  // anything implicit in reference - alternate.
+
+  uint32_t setLen = mReferenceSet.Length();
+  for (uint32_t index = 0; index < setLen; ++index) {
+    if (!mAlternateReferenceSet.Contains(mReferenceSet[index])) {
+      LOG3(("HTTP decompressor carryover in reference set with index %u %s %s\n",
+            mReferenceSet[index],
+            mHeaderTable[mReferenceSet[index]]->mName.get(),
+            mHeaderTable[mReferenceSet[index]]->mValue.get()));
+      OutputHeader(mReferenceSet[index]);
+    }
+  }
+
+  mAlternateReferenceSet.Clear();
+  return rv;
+}
+
+nsresult
+Http2Decompressor::DecodeInteger(uint32_t prefixLen, uint32_t &accum)
+{
+  accum = 0;
+
+  if (prefixLen) {
+    uint32_t mask = (1 << prefixLen) - 1;
+
+    accum = mData[mOffset] & mask;
+    ++mOffset;
+
+    if (accum != mask) {
+      // the simple case for small values
+      return NS_OK;
+    }
+  }
+
+  uint32_t factor = 1; // 128 ^ 0
+
+  // we need a series of bytes. The high bit signifies if we need another one.
+  // The first one is a a factor of 128 ^ 0, the next 128 ^1, the next 128 ^2, ..
+
+  if (mOffset >= mDataLen) {
+    NS_WARNING("Ran out of data to decode integer");
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  bool chainBit = mData[mOffset] & 0x80;
+  accum += (mData[mOffset] & 0x7f) * factor;
+
+  ++mOffset;
+  factor = factor * 128;
+
+  while (chainBit) {
+    // really big offsets are just trawling for overflows
+    if (accum >= 0x800000) {
+      NS_WARNING("Decoding integer >= 0x800000");
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+
+    if (mOffset >= mDataLen) {
+      NS_WARNING("Ran out of data to decode integer");
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+    chainBit = mData[mOffset] & 0x80;
+    accum += (mData[mOffset] & 0x7f) * factor;
+    ++mOffset;
+    factor = factor * 128;
+  }
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::OutputHeader(const nsACString &name, const nsACString &value)
+{
+    // exclusions
+  if (name.Equals(NS_LITERAL_CSTRING("connection")) ||
+      name.Equals(NS_LITERAL_CSTRING("host")) ||
+      name.Equals(NS_LITERAL_CSTRING("keep-alive")) ||
+      name.Equals(NS_LITERAL_CSTRING("proxy-connection")) ||
+      name.Equals(NS_LITERAL_CSTRING("te")) ||
+      name.Equals(NS_LITERAL_CSTRING("transfer-encoding")) ||
+      name.Equals(NS_LITERAL_CSTRING("upgrade")) ||
+      name.Equals(("accept-encoding"))) {
+    nsCString toLog(name);
+    LOG3(("HTTP Decompressor illegal response header found : %s",
+          toLog.get()));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  // Look for upper case characters in the name.
+  for (const char *cPtr = name.BeginReading();
+       cPtr && cPtr < name.EndReading();
+       ++cPtr) {
+    if (*cPtr <= 'Z' && *cPtr >= 'A') {
+      nsCString toLog(name);
+      LOG3(("HTTP Decompressor upper case response header found. [%s]\n",
+            toLog.get()));
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+  }
+
+  // Look for CR OR LF in value - could be smuggling Sec 10.3
+  // can map to space safely
+  for (const char *cPtr = value.BeginReading();
+       cPtr && cPtr < value.EndReading();
+       ++cPtr) {
+    if (*cPtr == '\r' || *cPtr== '\n') {
+      char *wPtr = const_cast<char *>(cPtr);
+      *wPtr = ' ';
+    }
+  }
+
+  // Status comes first
+  if (name.Equals(NS_LITERAL_CSTRING(":status"))) {
+    nsAutoCString status(NS_LITERAL_CSTRING("HTTP/1.1 "));
+    status.Append(value);
+    status.Append(NS_LITERAL_CSTRING("\r\n"));
+    mOutput->Insert(status, 0);
+    mHeaderStatus = value;
+  } else if (name.Equals(NS_LITERAL_CSTRING(":authority"))) {
+    mHeaderHost = value;
+  } else if (name.Equals(NS_LITERAL_CSTRING(":scheme"))) {
+    mHeaderScheme = value;
+  } else if (name.Equals(NS_LITERAL_CSTRING(":path"))) {
+    mHeaderPath = value;
+  } else if (name.Equals(NS_LITERAL_CSTRING(":method"))) {
+    mHeaderMethod = value;
+  }
+
+  // http/2 transport level headers shouldn't be gatewayed into http/1
+  if(*(name.BeginReading()) == ':') {
+    LOG3(("HTTP Decompressor not gatewaying %s into http/1",
+          name.BeginReading()));
+    return NS_OK;
+  }
+
+  mOutput->Append(name);
+  mOutput->Append(NS_LITERAL_CSTRING(": "));
+  mOutput->Append(value);
+  mOutput->Append(NS_LITERAL_CSTRING("\r\n"));
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::OutputHeader(uint32_t index)
+{
+  // bounds check
+  if (mHeaderTable.Length() <= index)
+    return NS_ERROR_ILLEGAL_VALUE;
+
+  return OutputHeader(mHeaderTable[index]->mName,
+                      mHeaderTable[index]->mValue);
+}
+
+nsresult
+Http2Decompressor::CopyHeaderString(uint32_t index, nsACString &name)
+{
+  // bounds check
+  if (mHeaderTable.Length() <= index)
+    return NS_ERROR_ILLEGAL_VALUE;
+
+  name = mHeaderTable[index]->mName;
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::CopyStringFromInput(uint32_t bytes, nsACString &val)
+{
+  if (mOffset + bytes > mDataLen)
+    return NS_ERROR_ILLEGAL_VALUE;
+
+  val.Assign(reinterpret_cast<const char *>(mData) + mOffset, bytes);
+  mOffset += bytes;
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::DecodeFinalHuffmanCharacter(HuffmanIncomingTable *table,
+                                               uint8_t &c, uint8_t &bitsLeft)
+{
+  uint8_t idxLen = table->mPrefixLen;
+  uint8_t mask = (1 << bitsLeft) - 1;
+  uint8_t idx = mData[mOffset - 1] & mask;
+  if (idxLen < bitsLeft) {
+    mask &= ~((1 << (bitsLeft - idxLen)) - 1);
+    idx &= mask;
+    idx >>= (bitsLeft - idxLen);
+    idx &= ((1 << idxLen) - 1);
+  } else {
+    idx <<= (idxLen - bitsLeft);
+  }
+  // Don't update bitsLeft yet, because we need to check that value against the
+  // number of bits used by our encoding later on. We'll update when we are sure
+  // how many bits we've actually used.
+
+  if (table->mEntries[idx].mPtr) {
+    // Can't chain to another table when we're all out of bits in the encoding
+    LOG3(("DecodeFinalHuffmanCharacter trying to chain when we're out of bits"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  if (bitsLeft < table->mEntries[idx].mPrefixLen) {
+    // We don't have enough bits to actually make a match, this is some sort of
+    // invalid coding
+    LOG3(("DecodeFinalHuffmanCharacter does't have enough bits to match"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  // This is a character!
+  if (table->mEntries[idx].mValue == 256) {
+    // EOS
+    LOG3(("DecodeFinalHuffmanCharacter actually decoded an EOS"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  c = static_cast<uint8_t>(table->mEntries[idx].mValue & 0xFF);
+  bitsLeft -= table->mEntries[idx].mPrefixLen;
+
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::DecodeHuffmanCharacter(HuffmanIncomingTable *table,
+                                          uint8_t &c, uint32_t &bytesConsumed,
+                                          uint8_t &bitsLeft)
+{
+  uint8_t idxLen = table->mPrefixLen;
+  uint8_t idx;
+  uint8_t mask;
+
+  if (idxLen < bitsLeft) {
+    // Only need to consume part of the rest of the previous byte
+    mask = (1 << bitsLeft) - 1;
+    bitsLeft -= idxLen;
+    mask &= ~((1 << bitsLeft) - 1);
+    idx = (mData[mOffset - 1] & mask) >> bitsLeft;
+    idx &= ((1 << idxLen) - 1);
+  } else if (bitsLeft) {
+    // Need to consume all of the rest of the previous byte, and possibly some
+    // of the current byte
+    mask = (1 << bitsLeft) - 1;
+    idxLen -= bitsLeft;
+    idx = (mData[mOffset - 1] & mask) << idxLen;
+    bitsLeft = 0;
+    if (idxLen) {
+      // Need to consume some of the current byte
+      bitsLeft = 8 - idxLen;
+      mask = ~((1 << bitsLeft) - 1);
+      uint8_t lastBits = (mData[mOffset] & mask) >> bitsLeft;
+      idx |= (lastBits & ((1 << idxLen) - 1));
+      bytesConsumed++;
+      mOffset++;
+    }
+  } else {
+    // byte-aligned already
+    mask = (1 << 8) - 1;
+    bitsLeft = 8 - idxLen;
+    mask &= ~((1 << bitsLeft) - 1);
+    idx = (mData[mOffset] & mask) >> bitsLeft;
+    idx &= ((1 << idxLen) - 1);
+    bytesConsumed++;
+    mOffset++;
+  }
+
+  if (table->mEntries[idx].mPtr) {
+    if (bytesConsumed >= mDataLen) {
+      if (!bitsLeft || (bytesConsumed > mDataLen)) {
+        // No info left in input to try to consume, we're done
+        LOG3(("DecodeHuffmanCharacter all out of bits to consume, can't chain"));
+        return NS_ERROR_ILLEGAL_VALUE;
+      }
+
+      // We might get lucky here!
+      return DecodeFinalHuffmanCharacter(table->mEntries[idx].mPtr, c,
+                                         bitsLeft);
+    }
+
+    // We're sorry, Mario, but your princess is in another castle
+    return DecodeHuffmanCharacter(table->mEntries[idx].mPtr, c, bytesConsumed,
+                                  bitsLeft);
+  }
+
+  if (table->mEntries[idx].mValue == 256) {
+    LOG3(("DecodeHuffmanCharacter found an actual EOS"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  c = static_cast<uint8_t>(table->mEntries[idx].mValue & 0xFF);
+
+  // Need to adjust bitsLeft (and possibly other values) because we may not have
+  // consumed all of the bits that the table requires for indexing.
+  bitsLeft += (table->mPrefixLen - table->mEntries[idx].mPrefixLen);
+  if (bitsLeft >= 8) {
+    mOffset--;
+    bytesConsumed--;
+    bitsLeft -= 8;
+  }
+  MOZ_ASSERT(bitsLeft < 8);
+
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::CopyHuffmanStringFromInput(uint32_t bytes, nsACString &val)
+{
+  if (mOffset + bytes > mDataLen) {
+    LOG3(("CopyHuffmanStringFromInput not enough data"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  uint32_t bytesRead = 0;
+  uint8_t bitsLeft = 0;
+  nsAutoCString buf;
+  nsresult rv;
+  uint8_t c;
+
+  while (bytesRead < bytes) {
+    uint32_t bytesConsumed = 0;
+    rv = DecodeHuffmanCharacter(&HuffmanIncomingRoot, c, bytesConsumed,
+                                bitsLeft);
+    if (NS_FAILED(rv)) {
+      LOG3(("CopyHuffmanStringFromInput failed to decode a character"));
+      return rv;
+    }
+
+    bytesRead += bytesConsumed;
+    buf.Append(c);
+  }
+
+  if (bytesRead > bytes) {
+    LOG3(("CopyHuffmanStringFromInput read more bytes than was allowed!"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  if (bitsLeft) {
+    // The shortest valid code is 4 bits, so we know there can be at most one
+    // character left that our loop didn't decode. Check to see if that's the
+    // case, and if so, add it to our output.
+    rv = DecodeFinalHuffmanCharacter(&HuffmanIncomingRoot, c, bitsLeft);
+    if (NS_SUCCEEDED(rv)) {
+      buf.Append(c);
+    }
+  }
+
+  if (bitsLeft) {
+    // Any bits left at this point must belong to the EOS symbol, so make sure
+    // they make sense (ie, are all ones)
+    uint8_t mask = (1 << bitsLeft) - 1;
+    uint8_t bits = mData[mOffset - 1] & mask;
+    if (bits != mask) {
+      LOG3(("CopyHuffmanStringFromInput ran out of data but found possible "
+            "non-EOS symbol"));
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+  }
+
+  val = buf;
+  LOG3(("CopyHuffmanStringFromInput decoded a full string!"));
+  return NS_OK;
+}
+
+void
+Http2Decompressor::MakeRoom(uint32_t amount)
+{
+  // make room in the header table
+  uint32_t removedCount = 0;
+  while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
+    uint32_t index = mHeaderTable.VariableLength() - 1;
+    mHeaderTable.RemoveElement();
+    ++removedCount;
+    LOG3(("HTTP decompressor header table index %u removed for size.\n",
+          index));
+  }
+
+  // adjust references to header table
+  UpdateReferenceSet(removedCount);
+}
+
+nsresult
+Http2Decompressor::DoIndexed()
+{
+  // this starts with a 1 bit pattern
+  MOZ_ASSERT(mData[mOffset] & 0x80);
+
+  // Indexed entries toggle the reference set
+  // This is a 7 bit prefix
+
+  uint32_t index;
+  nsresult rv = DecodeInteger(7, index);
+  if (NS_FAILED(rv))
+    return rv;
+
+  LOG3(("HTTP decompressor indexed entry %u\n", index));
+
+  // Toggle this in the reference set..
+  // if its not currently in the reference set then add it and
+  // also emit it. If it is currently in the reference set then just
+  // remove it from there.
+  if (mReferenceSet.RemoveElement(index)) {
+    mAlternateReferenceSet.RemoveElement(index);
+    return NS_OK;
+  }
+
+  rv = OutputHeader(index);
+  if (index >= mHeaderTable.VariableLength()) {
+    const nvPair *pair = mHeaderTable[index];
+    uint32_t room = pair->Size();
+
+    if (room > mMaxBuffer) {
+      ClearHeaderTable();
+      LOG3(("HTTP decompressor index not referenced due to size %u %s\n",
+            room, pair->mName.get()));
+      return rv;
+    }
+
+    MakeRoom(room);
+    mHeaderTable.AddElement(pair->mName, pair->mValue);
+    IncrementReferenceSetIndices();
+    index = 0;
+  }
+
+  mReferenceSet.AppendElement(index);
+  mAlternateReferenceSet.AppendElement(index);
+  return rv;
+}
+
+nsresult
+Http2Decompressor::DoLiteralInternal(nsACString &name, nsACString &value)
+{
+  // guts of doliteralwithoutindex and doliteralwithincremental
+  MOZ_ASSERT(((mData[mOffset] & 0xC0) == 0x40) ||  // withoutindex
+             ((mData[mOffset] & 0xC0) == 0x00));   // withincremental
+
+  // first let's get the name
+  uint32_t index;
+  nsresult rv = DecodeInteger(6, index);
+  if (NS_FAILED(rv))
+    return rv;
+
+  bool isHuffmanEncoded;
+
+  if (!index) {
+    // name is embedded as a literal
+    uint32_t nameLen;
+    isHuffmanEncoded = mData[mOffset] & (1 << 7);
+    rv = DecodeInteger(7, nameLen);
+    if (NS_SUCCEEDED(rv)) {
+      if (isHuffmanEncoded) {
+        rv = CopyHuffmanStringFromInput(nameLen, name);
+      } else {
+        rv = CopyStringFromInput(nameLen, name);
+      }
+    }
+  } else {
+    // name is from headertable
+    rv = CopyHeaderString(index - 1, name);
+  }
+  if (NS_FAILED(rv))
+    return rv;
+
+  // now the value
+  uint32_t valueLen;
+  isHuffmanEncoded = mData[mOffset] & (1 << 7);
+  rv = DecodeInteger(7, valueLen);
+  if (NS_SUCCEEDED(rv)) {
+    if (isHuffmanEncoded) {
+      rv = CopyHuffmanStringFromInput(valueLen, value);
+    } else {
+      rv = CopyStringFromInput(valueLen, value);
+    }
+  }
+  if (NS_FAILED(rv))
+    return rv;
+  return NS_OK;
+}
+
+nsresult
+Http2Decompressor::DoLiteralWithoutIndex()
+{
+  // this starts with 01 bit pattern
+  MOZ_ASSERT((mData[mOffset] & 0xC0) == 0x40);
+
+  // This is not indexed so there is no adjustment to the
+  // persistent reference set
+  nsAutoCString name, value;
+  nsresult rv = DoLiteralInternal(name, value);
+
+  LOG3(("HTTP decompressor literal without index %s %s\n",
+        name.get(), value.get()));
+
+  // Output the header now because we don't keep void
+  // indicies in the reference set
+  if (NS_SUCCEEDED(rv))
+    rv = OutputHeader(name, value);
+  return rv;
+}
+
+nsresult
+Http2Decompressor::DoLiteralWithIncremental()
+{
+  // this starts with 00 bit pattern
+  MOZ_ASSERT((mData[mOffset] & 0xC0) == 0x00);
+
+  nsAutoCString name, value;
+  nsresult rv = DoLiteralInternal(name, value);
+  if (NS_SUCCEEDED(rv))
+    rv = OutputHeader(name, value);
+  if (NS_FAILED(rv))
+    return rv;
+
+  uint32_t room = nvPair(name, value).Size();
+  if (room > mMaxBuffer) {
+    ClearHeaderTable();
+    LOG3(("HTTP decompressor literal with index not referenced due to size %u %s\n",
+          room, name.get()));
+    return NS_OK;
+  }
+
+  MakeRoom(room);
+
+  // Incremental Indexing implicitly adds a row to the header table.
+  // It also adds the new row to the Reference Set
+  mHeaderTable.AddElement(name, value);
+  IncrementReferenceSetIndices();
+  mReferenceSet.AppendElement(0);
+  mAlternateReferenceSet.AppendElement(0);
+
+  LOG3(("HTTP decompressor literal with index 0 %s %s\n",
+        name.get(), value.get()));
+
+  return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////
+
+nsresult
+Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
+                                   const nsACString &method, const nsACString &path,
+                                   const nsACString &host, const nsACString &scheme,
+                                   nsACString &output)
+{
+  mAlternateReferenceSet.Clear();
+  mImpliedReferenceSet.Clear();
+  mOutput = &output;
+  output.SetCapacity(1024);
+  output.Truncate();
+  mParsedContentLength = -1;
+
+  // colon headers first
+  ProcessHeader(nvPair(NS_LITERAL_CSTRING(":method"), method));
+  ProcessHeader(nvPair(NS_LITERAL_CSTRING(":path"), path));
+  ProcessHeader(nvPair(NS_LITERAL_CSTRING(":authority"), host));
+  ProcessHeader(nvPair(NS_LITERAL_CSTRING(":scheme"), scheme));
+
+  // now the non colon headers
+  const char *beginBuffer = nvInput.BeginReading();
+
+  int32_t crlfIndex = nvInput.Find("\r\n");
+  while (true) {
+    int32_t startIndex = crlfIndex + 2;
+
+    crlfIndex = nvInput.Find("\r\n", false, startIndex);
+    if (crlfIndex == -1)
+      break;
+
+    int32_t colonIndex = nvInput.Find(":", false, startIndex,
+                                      crlfIndex - startIndex);
+    if (colonIndex == -1)
+      break;
+
+    nsDependentCSubstring name = Substring(beginBuffer + startIndex,
+                                           beginBuffer + colonIndex);
+    // all header names are lower case in http/2
+    ToLowerCase(name);
+
+    // exclusions
+    if (name.Equals("connection") ||
+        name.Equals("host") ||
+        name.Equals("keep-alive") ||
+        name.Equals("proxy-connection") ||
+        name.Equals("te") ||
+        name.Equals("transfer-encoding") ||
+        name.Equals("upgrade") ||
+        name.Equals("accept-encoding")) {
+      continue;
+    }
+
+    // colon headers are for http/2 and this is http/1 input, so that
+    // is probably a smuggling attack of some kind
+    if(*(name.BeginReading()) == ':') {
+      continue;
+    }
+
+    int32_t valueIndex = colonIndex + 1;
+
+    // if we have Expect: *100-continue,*" redact the 100-continue
+    // as we don't have a good mechanism for clients to make use of it
+    // anyhow
+    if (name.Equals("expect")) {
+      const char *continueHeader =
+        nsHttp::FindToken(beginBuffer + valueIndex, "100-continue",
+                          HTTP_HEADER_VALUE_SEPS);
+      if (continueHeader) {
+        char *writableVal = const_cast<char *>(continueHeader);
+        memset(writableVal, 0, 12);
+        writableVal += 12;
+        // this will terminate safely because CRLF EOL has been confirmed
+        while ((*writableVal == ' ') || (*writableVal == '\t') ||
+               (*writableVal == ',')) {
+          *writableVal = ' ';
+          ++writableVal;
+        }
+      }
+    }
+
+    while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
+      ++valueIndex;
+
+    nsDependentCSubstring value = Substring(beginBuffer + valueIndex,
+                                            beginBuffer + crlfIndex);
+
+    if (name.Equals("content-length")) {
+      int64_t len;
+      nsCString tmp(value);
+      if (nsHttp::ParseInt64(tmp.get(), nullptr, &len))
+        mParsedContentLength = len;
+    }
+
+    ProcessHeader(nvPair(name, value));
+  }
+
+  // iterate mreference set and if !alternate.contains(old[i])
+  // toggle off
+  uint32_t setLen = mReferenceSet.Length();
+  for (uint32_t index = 0; index < setLen; ++index) {
+    if (!mAlternateReferenceSet.Contains(mReferenceSet[index])) {
+      DoOutput(kToggleOff, mHeaderTable[mReferenceSet[index]],
+               mReferenceSet[index]);
+    }
+  }
+
+  mReferenceSet = mAlternateReferenceSet;
+  mAlternateReferenceSet.Clear();
+  mImpliedReferenceSet.Clear();
+  mOutput = nullptr;
+  return NS_OK;
+}
+
+void
+Http2Compressor::DoOutput(Http2Compressor::outputCode code,
+                          const class nvPair *pair, uint32_t index)
+{
+  // start Byte needs to be calculated from the offset after
+  // the opcode has been written out in case the output stream
+  // buffer gets resized/relocated
+  uint32_t offset = mOutput->Length();
+  uint8_t *startByte;
+
+  switch (code) {
+  case kPlainLiteral:
+    LOG3(("HTTP compressor %p noindex literal with name reference %u %s: %s\n",
+          this, index, pair->mName.get(), pair->mValue.get()));
+
+    EncodeInteger(6, index); // 01 2 bit prefix
+    startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
+    *startByte = (*startByte & 0x3f) | 0x40;
+
+    if (!index) {
+      HuffmanAppend(pair->mName);
+    }
+
+    HuffmanAppend(pair->mValue);
+    break;
+
+  case kIndexedLiteral:
+    LOG3(("HTTP compressor %p literal with name reference %u %s: %s\n",
+          this, index, pair->mName.get(), pair->mValue.get()));
+
+    EncodeInteger(6, index); // 00 2 bit prefix
+    startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
+    *startByte = *startByte & 0x3f;
+
+    if (!index) {
+      HuffmanAppend(pair->mName);
+    }
+
+    HuffmanAppend(pair->mValue);
+    break;
+
+  case kToggleOff:
+  case kToggleOn:
+    LOG3(("HTTP compressor %p toggle %s index %u %s\n",
+          this, (code == kToggleOff) ? "off" : "on",
+          index, pair->mName.get()));
+    EncodeInteger(7, index);
+    startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
+    *startByte = *startByte | 0x80; // 1 1 bit prefix
+    break;
+
+  case kNop:
+    LOG3(("HTTP compressor %p implied in reference set index %u %s\n",
+          this, index, pair->mName.get()));
+    break;
+  }
+}
+
+// writes the encoded integer onto the output
+void
+Http2Compressor::EncodeInteger(uint32_t prefixLen, uint32_t val)
+{
+  uint32_t mask = (1 << prefixLen) - 1;
+  uint8_t tmp;
+
+  if (val < mask) {
+    // 1 byte encoding!
+    tmp = val;
+    mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
+    return;
+  }
+
+  if (mask) {
+    val -= mask;
+    tmp = mask;
+    mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
+  }
+
+  uint32_t q, r;
+  do {
+    q = val / 128;
+    r = val % 128;
+    tmp = r;
+    if (q)
+      tmp |= 0x80; // chain bit
+    val = q;
+    mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
+  } while (q);
+}
+
+void
+Http2Compressor::ClearHeaderTable()
+{
+  uint32_t dynamicCount = mHeaderTable.VariableLength();
+
+  Http2BaseCompressor::ClearHeaderTable();
+
+  for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
+    if (mImpliedReferenceSet[i] < dynamicCount) {
+      mImpliedReferenceSet.RemoveElementAt(i);
+    } else {
+      mImpliedReferenceSet[i] -= dynamicCount;
+    }
+  }
+}
+
+
+void
+Http2Compressor::UpdateReferenceSet(int32_t delta)
+{
+  if (!delta)
+    return;
+
+  Http2BaseCompressor::UpdateReferenceSet(delta);
+
+  uint32_t headerTableSize = mHeaderTable.VariableLength();
+  uint32_t oldHeaderTableSize = headerTableSize + delta;
+
+  for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
+    uint32_t indexRef = mImpliedReferenceSet[i];
+    if (indexRef >= headerTableSize) {
+      if (indexRef < oldHeaderTableSize) {
+        // This one got dropped
+        LOG3(("HTTP compressor implied reference to index %u removed.\n",
+              indexRef));
+        mImpliedReferenceSet.RemoveElementAt(i);
+      } else {
+        // This pointed to the static table, need to adjust
+        uint32_t newRef = indexRef - delta;
+        LOG3(("HTTP compressor implied reference to index %u changed to %d (%s)\n",
+              mImpliedReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get()));
+        mImpliedReferenceSet[i] = newRef;
+      }
+    }
+  }
+}
+
+void
+Http2Compressor::IncrementReferenceSetIndices()
+{
+  Http2BaseCompressor::IncrementReferenceSetIndices();
+
+  for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
+    mImpliedReferenceSet[i] = mImpliedReferenceSet[i] + 1;
+  }
+}
+
+void
+Http2Compressor::MakeRoom(uint32_t amount)
+{
+  // make room in the header table
+  uint32_t removedCount = 0;
+  while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
+
+    // if there is a reference to removedCount (~0) in the implied reference set we need,
+    // to toggle it off/on so that the implied reference is not lost when the
+    // table is trimmed
+    uint32_t index = mHeaderTable.VariableLength() - 1;
+    if (mImpliedReferenceSet.Contains(index) ) {
+      LOG3(("HTTP compressor header table index %u %s about to be "
+            "removed for size but has an implied reference. Will Toggle.\n",
+            index, mHeaderTable[index]->mName.get()));
+
+      DoOutput(kToggleOff, mHeaderTable[index], index);
+      DoOutput(kToggleOn, mHeaderTable[index], index);
+    }
+
+    LOG3(("HTTP compressor header table index %u %s removed for size.\n",
+          index, mHeaderTable[index]->mName.get()));
+    mHeaderTable.RemoveElement();
+    ++removedCount;
+  }
+
+  // adjust references to header table
+  UpdateReferenceSet(removedCount);
+}
+
+void
+Http2Compressor::HuffmanAppend(const nsCString &value)
+{
+  nsAutoCString buf;
+  uint8_t bitsLeft = 8;
+  uint32_t length = value.Length();
+  uint32_t offset;
+  uint8_t *startByte;
+
+  for (uint32_t i = 0; i < length; ++i) {
+    uint8_t idx = static_cast<uint8_t>(value[i]);
+    uint8_t huffLength = HuffmanOutgoing[idx].mLength;
+    uint32_t huffValue = HuffmanOutgoing[idx].mValue;
+
+    if (bitsLeft < 8) {
+      // Fill in the least significant <bitsLeft> bits of the previous byte
+      // first
+      uint32_t val;
+      if (huffLength >= bitsLeft) {
+        val = huffValue & ~((1 << (huffLength - bitsLeft)) - 1);
+        val >>= (huffLength - bitsLeft);
+      } else {
+        val = huffValue << (bitsLeft - huffLength);
+      }
+      val &= ((1 << bitsLeft) - 1);
+      offset = buf.Length() - 1;
+      startByte = reinterpret_cast<unsigned char *>(buf.BeginWriting()) + offset;
+      *startByte = *startByte | static_cast<uint8_t>(val & 0xFF);
+      if (huffLength >= bitsLeft) {
+        huffLength -= bitsLeft;
+        bitsLeft = 8;
+      } else {
+        bitsLeft -= huffLength;
+        huffLength = 0;
+      }
+    }
+
+    while (huffLength > 8) {
+      uint32_t mask = ~((1 << (huffLength - 8)) - 1);
+      uint8_t val = ((huffValue & mask) >> (huffLength - 8)) & 0xFF;
+      buf.Append(reinterpret_cast<char *>(&val), 1);
+      huffLength -= 8;
+    }
+
+    if (huffLength) {
+      // Fill in the most significant <huffLength> bits of the next byte
+      bitsLeft = 8 - huffLength;
+      uint8_t val = (huffValue & ((1 << huffLength) - 1)) << bitsLeft;
+      buf.Append(reinterpret_cast<char *>(&val), 1);
+    }
+  }
+
+  if (bitsLeft != 8) {
+    // Pad the last <bitsLeft> bits with ones, which corresponds to the EOS
+    // encoding
+    uint8_t val = (1 << bitsLeft) - 1;
+    offset = buf.Length() - 1;
+    startByte = reinterpret_cast<unsigned char *>(buf.BeginWriting()) + offset;
+    *startByte = *startByte | val;
+  }
+
+  // Now we know how long our encoded string is, we can fill in our length
+  uint32_t bufLength = buf.Length();
+  offset = mOutput->Length();
+  EncodeInteger(7, bufLength);
+  startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
+  *startByte = *startByte | 0x80;
+
+  // Finally, we can add our REAL data!
+  mOutput->Append(buf);
+}
+
+void
+Http2Compressor::ProcessHeader(const nvPair inputPair)
+{
+  uint32_t newSize = inputPair.Size();
+  uint32_t headerTableSize = mHeaderTable.Length();
+  uint32_t matchedIndex;
+  uint32_t nameReference = 0;
+  bool match = false;
+
+  for (uint32_t index = 0; index < headerTableSize; ++index) {
+    if (mHeaderTable[index]->mName.Equals(inputPair.mName)) {
+      nameReference = index + 1;
+      if (mHeaderTable[index]->mValue.Equals(inputPair.mValue)) {
+        match = true;
+        matchedIndex = index;
+        break;
+      }
+    }
+  }
+
+  // We need to emit a new literal
+  if (!match) {
+    if ((newSize > (mMaxBuffer / 2)) || (mMaxBuffer < 128)) {
+      DoOutput(kPlainLiteral, &inputPair, nameReference);
+      return;
+    }
+
+    // make sure to makeroom() first so that any implied items
+    // get preserved.
+    MakeRoom(newSize);
+    DoOutput(kIndexedLiteral, &inputPair, nameReference);
+
+    mHeaderTable.AddElement(inputPair.mName, inputPair.mValue);
+    IncrementReferenceSetIndices();
+    LOG3(("HTTP compressor %p new literal placed at index 0\n",
+          this));
+    mAlternateReferenceSet.AppendElement(0);
+    return;
+  }
+
+  // It is in the reference set. just check to see if it is
+  // a duplicate for output purposes
+  if (mReferenceSet.Contains(matchedIndex)) {
+    if (mAlternateReferenceSet.Contains(matchedIndex)) {
+      DoOutput(kToggleOff, &inputPair, matchedIndex);
+      DoOutput(kToggleOn, &inputPair, matchedIndex);
+    } else {
+      DoOutput(kNop, &inputPair, matchedIndex);
+      if (!mImpliedReferenceSet.Contains(matchedIndex))
+        mImpliedReferenceSet.AppendElement(matchedIndex);
+      mAlternateReferenceSet.AppendElement(matchedIndex);
+    }
+    return;
+  }
+
+  // emit an index to add to reference set
+  DoOutput(kToggleOn, &inputPair, matchedIndex);
+  if (matchedIndex >= mHeaderTable.VariableLength()) {
+    MakeRoom(newSize);
+    mHeaderTable.AddElement(inputPair.mName, inputPair.mValue);
+    IncrementReferenceSetIndices();
+    mAlternateReferenceSet.AppendElement(0);
+  } else {
+    mAlternateReferenceSet.AppendElement(matchedIndex);
+  }
+  return;
+}
+
+void
+Http2Compressor::SetMaxBufferSize(uint32_t maxBufferSize)
+{
+  uint32_t removedCount = 0;
+
+  LOG3(("Http2Compressor::SetMaxBufferSize %u called", maxBufferSize));
+
+  while (mHeaderTable.VariableLength() && (mHeaderTable.ByteCount() > maxBufferSize)) {
+    mHeaderTable.RemoveElement();
+    ++removedCount;
+  }
+  UpdateReferenceSet(removedCount);
+
+  mMaxBuffer = maxBufferSize;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Compression.h
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_Http2Compression_Internal_h
+#define mozilla_net_Http2Compression_Internal_h
+
+// HPACK
+// tools.ietf.org/html/draft-ietf-httpbis-header-compression-04
+
+#include "mozilla/Attributes.h"
+#include "nsDeque.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+struct HuffmanIncomingTable;
+
+void Http2CompressionCleanup();
+
+class nvPair
+{
+public:
+nvPair(const nsACString &name, const nsACString &value)
+  : mName(name)
+  , mValue(value)
+  { }
+
+  uint32_t Size() const { return mName.Length() + mValue.Length() + 32; }
+
+  nsCString mName;
+  nsCString mValue;
+};
+
+class nvFIFO
+{
+public:
+  nvFIFO();
+  ~nvFIFO();
+  void AddElement(const nsCString &name, const nsCString &value);
+  void AddElement(const nsCString &name);
+  void RemoveElement();
+  uint32_t ByteCount() const;
+  uint32_t Length() const;
+  uint32_t VariableLength() const;
+  void Clear();
+  const nvPair *operator[] (int32_t index) const;
+
+private:
+  uint32_t mByteCount;
+  nsDeque  mTable;
+};
+
+class Http2BaseCompressor
+{
+public:
+  Http2BaseCompressor();
+  virtual ~Http2BaseCompressor() { };
+
+protected:
+  // this will become a HTTP/2 SETTINGS value in a future draft
+  const static uint32_t kDefaultMaxBuffer = 4096;
+
+  virtual void ClearHeaderTable();
+  virtual void UpdateReferenceSet(int32_t delta);
+  virtual void IncrementReferenceSetIndices();
+  virtual void MakeRoom(uint32_t amount) = 0;
+
+  nsAutoTArray<uint32_t, 64> mReferenceSet; // list of indicies
+
+  // the alternate set is used to track the emitted headers when
+  // processing input for a header set. The input to the compressor
+  // is a series of nvpairs, the input to the decompressor is the
+  // series of op codes that make up the header block.
+  //
+  // after processing the input the compressor compares the alternate
+  // set to the inherited reference set and generates indicies to
+  // toggle off any members of alternate - inherited. the alternate
+  // then becomes the inherited set for the next header set.
+  //
+  // after processing the input the decompressor comapres the alternate
+  // set to the inherited reference set and generates headers for
+  // anything implicit in reference - alternate.
+  nsAutoTArray<uint32_t, 64> mAlternateReferenceSet; // list of indicies
+
+  nsACString *mOutput;
+  nvFIFO mHeaderTable;
+
+  uint32_t mMaxBuffer;
+};
+
+class Http2Decompressor MOZ_FINAL : public Http2BaseCompressor
+{
+public:
+  Http2Decompressor() { };
+  virtual ~Http2Decompressor() { } ;
+
+  // NS_OK: Produces the working set of HTTP/1 formatted headers
+  nsresult DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
+                             nsACString &output);
+
+  void GetStatus(nsACString &hdr) { hdr = mHeaderStatus; }
+  void GetHost(nsACString &hdr) { hdr = mHeaderHost; }
+  void GetScheme(nsACString &hdr) { hdr = mHeaderScheme; }
+  void GetPath(nsACString &hdr) { hdr = mHeaderPath; }
+  void GetMethod(nsACString &hdr) { hdr = mHeaderMethod; }
+
+protected:
+  virtual void MakeRoom(uint32_t amount) MOZ_OVERRIDE;
+
+private:
+  nsresult DoIndexed();
+  nsresult DoLiteralWithoutIndex();
+  nsresult DoLiteralWithIncremental();
+  nsresult DoLiteralInternal(nsACString &, nsACString &);
+
+  nsresult DecodeInteger(uint32_t prefixLen, uint32_t &result);
+  nsresult OutputHeader(uint32_t index);
+  nsresult OutputHeader(const nsACString &name, const nsACString &value);
+
+  nsresult CopyHeaderString(uint32_t index, nsACString &name);
+  nsresult CopyStringFromInput(uint32_t index, nsACString &val);
+  nsresult CopyHuffmanStringFromInput(uint32_t index, nsACString &val);
+  nsresult DecodeHuffmanCharacter(HuffmanIncomingTable *table, uint8_t &c,
+                                  uint32_t &bytesConsumed, uint8_t &bitsLeft);
+  nsresult DecodeFinalHuffmanCharacter(HuffmanIncomingTable *table, uint8_t &c,
+                                       uint8_t &bitsLeft);
+
+  nsCString mHeaderStatus;
+  nsCString mHeaderHost;
+  nsCString mHeaderScheme;
+  nsCString mHeaderPath;
+  nsCString mHeaderMethod;
+
+  // state variables when DecodeBlock() is on the stack
+  uint32_t mOffset;
+  const uint8_t *mData;
+  uint32_t mDataLen;
+};
+
+
+class Http2Compressor MOZ_FINAL : public Http2BaseCompressor
+{
+public:
+  Http2Compressor() : mParsedContentLength(-1) { };
+  virtual ~Http2Compressor() { }
+
+  // HTTP/1 formatted header block as input - HTTP/2 formatted
+  // header block as output
+  nsresult EncodeHeaderBlock(const nsCString &nvInput,
+                             const nsACString &method, const nsACString &path,
+                             const nsACString &host, const nsACString &scheme,
+                             nsACString &output);
+
+  int64_t GetParsedContentLength() { return mParsedContentLength; } // -1 on not found
+
+  void SetMaxBufferSize(uint32_t maxBufferSize);
+
+protected:
+  virtual void ClearHeaderTable() MOZ_OVERRIDE;
+  virtual void UpdateReferenceSet(int32_t delta) MOZ_OVERRIDE;
+  virtual void IncrementReferenceSetIndices() MOZ_OVERRIDE;
+  virtual void MakeRoom(uint32_t amount) MOZ_OVERRIDE;
+
+private:
+  enum outputCode {
+    kPlainLiteral,
+    kIndexedLiteral,
+    kToggleOff,
+    kToggleOn,
+    kNop
+  };
+
+  void DoOutput(Http2Compressor::outputCode code,
+                const class nvPair *pair, uint32_t index);
+  void EncodeInteger(uint32_t prefixLen, uint32_t val);
+  void ProcessHeader(const nvPair inputPair);
+  void HuffmanAppend(const nsCString &value);
+
+  int64_t mParsedContentLength;
+
+  nsAutoTArray<uint32_t, 64> mImpliedReferenceSet;
+};
+
+} // namespace mozilla::net
+} // namespace mozilla
+
+#endif // mozilla_net_Http2Compression_Internal_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2HuffmanIncoming.h
@@ -0,0 +1,1250 @@
+/*
+ * THIS FILE IS AUTO-GENERATED. DO NOT EDIT!
+ */
+#ifndef mozilla__net__Http2HuffmanIncoming_h
+#define mozilla__net__Http2HuffmanIncoming_h
+
+namespace mozilla {
+namespace net {
+
+struct HuffmanIncomingTable;
+
+struct HuffmanIncomingEntry {
+  uint8_t mPrefixLen;
+  uint16_t mValue;
+  HuffmanIncomingTable *mPtr;
+};
+
+struct HuffmanIncomingTable {
+  uint8_t mPrefixLen;
+  HuffmanIncomingEntry *mEntries;
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries246[] = {
+  { 1, 37, nullptr },
+  { 1, 40, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming246 = {
+  1,
+  HuffmanIncomingEntries246
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries247[] = {
+  { 1, 41, nullptr },
+  { 1, 59, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming247 = {
+  1,
+  HuffmanIncomingEntries247
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries248[] = {
+  { 1, 72, nullptr },
+  { 1, 76, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming248 = {
+  1,
+  HuffmanIncomingEntries248
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries249[] = {
+  { 1, 80, nullptr },
+  { 1, 82, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming249 = {
+  1,
+  HuffmanIncomingEntries249
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries250[] = {
+  { 1, 85, nullptr },
+  { 1, 86, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming250 = {
+  1,
+  HuffmanIncomingEntries250
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries251[] = {
+  { 1, 89, nullptr },
+  { 1, 95, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming251 = {
+  1,
+  HuffmanIncomingEntries251
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries252[] = {
+  { 1, 106, nullptr },
+  { 1, 107, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming252 = {
+  1,
+  HuffmanIncomingEntries252
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries253[] = {
+  { 1, 113, nullptr },
+  { 1, 122, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming253 = {
+  1,
+  HuffmanIncomingEntries253
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries254[] = {
+  { 2, 38, nullptr },
+  { 2, 75, nullptr },
+  { 2, 81, nullptr },
+  { 2, 88, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming254 = {
+  2,
+  HuffmanIncomingEntries254
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255222[] = {
+  { 1, 0, nullptr },
+  { 1, 1, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255222 = {
+  1,
+  HuffmanIncomingEntries255255222
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255223[] = {
+  { 1, 2, nullptr },
+  { 1, 3, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255223 = {
+  1,
+  HuffmanIncomingEntries255255223
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255224[] = {
+  { 1, 4, nullptr },
+  { 1, 5, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255224 = {
+  1,
+  HuffmanIncomingEntries255255224
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255225[] = {
+  { 1, 6, nullptr },
+  { 1, 7, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255225 = {
+  1,
+  HuffmanIncomingEntries255255225
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255226[] = {
+  { 1, 8, nullptr },
+  { 1, 9, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255226 = {
+  1,
+  HuffmanIncomingEntries255255226
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255227[] = {
+  { 1, 10, nullptr },
+  { 1, 11, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255227 = {
+  1,
+  HuffmanIncomingEntries255255227
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255228[] = {
+  { 1, 12, nullptr },
+  { 1, 13, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255228 = {
+  1,
+  HuffmanIncomingEntries255255228
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255229[] = {
+  { 1, 14, nullptr },
+  { 1, 15, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255229 = {
+  1,
+  HuffmanIncomingEntries255255229
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255230[] = {
+  { 1, 16, nullptr },
+  { 1, 17, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255230 = {
+  1,
+  HuffmanIncomingEntries255255230
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255231[] = {
+  { 1, 18, nullptr },
+  { 1, 19, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255231 = {
+  1,
+  HuffmanIncomingEntries255255231
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255232[] = {
+  { 1, 20, nullptr },
+  { 1, 21, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255232 = {
+  1,
+  HuffmanIncomingEntries255255232
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255233[] = {
+  { 1, 22, nullptr },
+  { 1, 23, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255233 = {
+  1,
+  HuffmanIncomingEntries255255233
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255234[] = {
+  { 1, 24, nullptr },
+  { 1, 25, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255234 = {
+  1,
+  HuffmanIncomingEntries255255234
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255235[] = {
+  { 1, 26, nullptr },
+  { 1, 27, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255235 = {
+  1,
+  HuffmanIncomingEntries255255235
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255236[] = {
+  { 1, 28, nullptr },
+  { 1, 29, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255236 = {
+  1,
+  HuffmanIncomingEntries255255236
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255237[] = {
+  { 1, 30, nullptr },
+  { 1, 31, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255237 = {
+  1,
+  HuffmanIncomingEntries255255237
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255238[] = {
+  { 1, 127, nullptr },
+  { 1, 128, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255238 = {
+  1,
+  HuffmanIncomingEntries255255238
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255239[] = {
+  { 1, 129, nullptr },
+  { 1, 130, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255239 = {
+  1,
+  HuffmanIncomingEntries255255239
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255240[] = {
+  { 1, 131, nullptr },
+  { 1, 132, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255240 = {
+  1,
+  HuffmanIncomingEntries255255240
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255241[] = {
+  { 1, 133, nullptr },
+  { 1, 134, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255241 = {
+  1,
+  HuffmanIncomingEntries255255241
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255242[] = {
+  { 1, 135, nullptr },
+  { 1, 136, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255242 = {
+  1,
+  HuffmanIncomingEntries255255242
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255243[] = {
+  { 1, 137, nullptr },
+  { 1, 138, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255243 = {
+  1,
+  HuffmanIncomingEntries255255243
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255244[] = {
+  { 1, 139, nullptr },
+  { 1, 140, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255244 = {
+  1,
+  HuffmanIncomingEntries255255244
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255245[] = {
+  { 1, 141, nullptr },
+  { 1, 142, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255245 = {
+  1,
+  HuffmanIncomingEntries255255245
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255246[] = {
+  { 1, 143, nullptr },
+  { 1, 144, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255246 = {
+  1,
+  HuffmanIncomingEntries255255246
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255247[] = {
+  { 1, 145, nullptr },
+  { 1, 146, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255247 = {
+  1,
+  HuffmanIncomingEntries255255247
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255248[] = {
+  { 1, 147, nullptr },
+  { 1, 148, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255248 = {
+  1,
+  HuffmanIncomingEntries255255248
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255249[] = {
+  { 1, 149, nullptr },
+  { 1, 150, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255249 = {
+  1,
+  HuffmanIncomingEntries255255249
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255250[] = {
+  { 1, 151, nullptr },
+  { 1, 152, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255250 = {
+  1,
+  HuffmanIncomingEntries255255250
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255251[] = {
+  { 1, 153, nullptr },
+  { 1, 154, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255251 = {
+  1,
+  HuffmanIncomingEntries255255251
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255252[] = {
+  { 1, 155, nullptr },
+  { 1, 156, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255252 = {
+  1,
+  HuffmanIncomingEntries255255252
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255253[] = {
+  { 1, 157, nullptr },
+  { 1, 158, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255253 = {
+  1,
+  HuffmanIncomingEntries255255253
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255254[] = {
+  { 1, 159, nullptr },
+  { 1, 160, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255254 = {
+  1,
+  HuffmanIncomingEntries255255254
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255255[] = {
+  { 1, 161, nullptr },
+  { 1, 162, nullptr },
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255255 = {
+  1,
+  HuffmanIncomingEntries255255255
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255255[] = {
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 1, 96, nullptr },
+  { 8, 163, nullptr },
+  { 8, 164, nullptr },
+  { 8, 165, nullptr },
+  { 8, 166, nullptr },
+  { 8, 167, nullptr },
+  { 8, 168, nullptr },
+  { 8, 169, nullptr },
+  { 8, 170, nullptr },
+  { 8, 171, nullptr },
+  { 8, 172, nullptr },
+  { 8, 173, nullptr },
+  { 8, 174, nullptr },
+  { 8, 175, nullptr },
+  { 8, 176, nullptr },
+  { 8, 177, nullptr },
+  { 8, 178, nullptr },
+  { 8, 179, nullptr },
+  { 8, 180, nullptr },
+  { 8, 181, nullptr },
+  { 8, 182, nullptr },
+  { 8, 183, nullptr },
+  { 8, 184, nullptr },
+  { 8, 185, nullptr },
+  { 8, 186, nullptr },
+  { 8, 187, nullptr },
+  { 8, 188, nullptr },
+  { 8, 189, nullptr },
+  { 8, 190, nullptr },
+  { 8, 191, nullptr },
+  { 8, 192, nullptr },
+  { 8, 193, nullptr },
+  { 8, 194, nullptr },
+  { 8, 195, nullptr },
+  { 8, 196, nullptr },
+  { 8, 197, nullptr },
+  { 8, 198, nullptr },
+  { 8, 199, nullptr },
+  { 8, 200, nullptr },
+  { 8, 201, nullptr },
+  { 8, 202, nullptr },
+  { 8, 203, nullptr },
+  { 8, 204, nullptr },
+  { 8, 205, nullptr },
+  { 8, 206, nullptr },
+  { 8, 207, nullptr },
+  { 8, 208, nullptr },
+  { 8, 209, nullptr },
+  { 8, 210, nullptr },
+  { 8, 211, nullptr },
+  { 8, 212, nullptr },
+  { 8, 213, nullptr },
+  { 8, 214, nullptr },
+  { 8, 215, nullptr },
+  { 8, 216, nullptr },
+  { 8, 217, nullptr },
+  { 8, 218, nullptr },
+  { 8, 219, nullptr },
+  { 8, 220, nullptr },
+  { 8, 221, nullptr },
+  { 8, 222, nullptr },
+  { 8, 223, nullptr },
+  { 8, 224, nullptr },
+  { 8, 225, nullptr },
+  { 8, 226, nullptr },
+  { 8, 227, nullptr },
+  { 8, 228, nullptr },
+  { 8, 229, nullptr },
+  { 8, 230, nullptr },
+  { 8, 231, nullptr },
+  { 8, 232, nullptr },
+  { 8, 233, nullptr },
+  { 8, 234, nullptr },
+  { 8, 235, nullptr },
+  { 8, 236, nullptr },
+  { 8, 237, nullptr },
+  { 8, 238, nullptr },
+  { 8, 239, nullptr },
+  { 8, 240, nullptr },
+  { 8, 241, nullptr },
+  { 8, 242, nullptr },
+  { 8, 243, nullptr },
+  { 8, 244, nullptr },
+  { 8, 245, nullptr },
+  { 8, 246, nullptr },
+  { 8, 247, nullptr },
+  { 8, 248, nullptr },
+  { 8, 249, nullptr },
+  { 8, 250, nullptr },
+  { 8, 251, nullptr },
+  { 8, 252, nullptr },
+  { 8, 253, nullptr },
+  { 8, 254, nullptr },
+  { 8, 255, nullptr },
+  { 8, 256, nullptr },
+  { 0, 0, &HuffmanIncoming255255222 },
+  { 0, 0, &HuffmanIncoming255255223 },
+  { 0, 0, &HuffmanIncoming255255224 },
+  { 0, 0, &HuffmanIncoming255255225 },
+  { 0, 0, &HuffmanIncoming255255226 },
+  { 0, 0, &HuffmanIncoming255255227 },
+  { 0, 0, &HuffmanIncoming255255228 },
+  { 0, 0, &HuffmanIncoming255255229 },
+  { 0, 0, &HuffmanIncoming255255230 },
+  { 0, 0, &HuffmanIncoming255255231 },
+  { 0, 0, &HuffmanIncoming255255232 },
+  { 0, 0, &HuffmanIncoming255255233 },
+  { 0, 0, &HuffmanIncoming255255234 },
+  { 0, 0, &HuffmanIncoming255255235 },
+  { 0, 0, &HuffmanIncoming255255236 },
+  { 0, 0, &HuffmanIncoming255255237 },
+  { 0, 0, &HuffmanIncoming255255238 },
+  { 0, 0, &HuffmanIncoming255255239 },
+  { 0, 0, &HuffmanIncoming255255240 },
+  { 0, 0, &HuffmanIncoming255255241 },
+  { 0, 0, &HuffmanIncoming255255242 },
+  { 0, 0, &HuffmanIncoming255255243 },
+  { 0, 0, &HuffmanIncoming255255244 },
+  { 0, 0, &HuffmanIncoming255255245 },
+  { 0, 0, &HuffmanIncoming255255246 },
+  { 0, 0, &HuffmanIncoming255255247 },
+  { 0, 0, &HuffmanIncoming255255248 },
+  { 0, 0, &HuffmanIncoming255255249 },
+  { 0, 0, &HuffmanIncoming255255250 },
+  { 0, 0, &HuffmanIncoming255255251 },
+  { 0, 0, &HuffmanIncoming255255252 },
+  { 0, 0, &HuffmanIncoming255255253 },
+  { 0, 0, &HuffmanIncoming255255254 },
+  { 0, 0, &HuffmanIncoming255255255 }
+};
+
+static HuffmanIncomingTable HuffmanIncoming255255 = {
+  8,
+  HuffmanIncomingEntries255255
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntries255[] = {
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 2, 90, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 43, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 91, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 3, 93, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 33, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 42, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 4, 63, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 35, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 39, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 62, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 5, 92, nullptr },
+  { 6, 36, nullptr },
+  { 6, 36, nullptr },
+  { 6, 36, nullptr },
+  { 6, 36, nullptr },
+  { 6, 124, nullptr },
+  { 6, 124, nullptr },
+  { 6, 124, nullptr },
+  { 6, 124, nullptr },
+  { 7, 94, nullptr },
+  { 7, 94, nullptr },
+  { 8, 60, nullptr },
+  { 8, 64, nullptr },
+  { 8, 123, nullptr },
+  { 8, 125, nullptr },
+  { 8, 126, nullptr },
+  { 0, 0, &HuffmanIncoming255255 }
+};
+
+static HuffmanIncomingTable HuffmanIncoming255 = {
+  8,
+  HuffmanIncomingEntries255
+};
+
+static HuffmanIncomingEntry HuffmanIncomingEntriesRoot[] = {
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 32, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 48, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 49, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 4, 50, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 51, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 52, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 53, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 56, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 57, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 58, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 84, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 97, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 5, 101, nullptr },
+  { 6, 44, nullptr },
+  { 6, 44, nullptr },
+  { 6, 44, nullptr },
+  { 6, 44, nullptr },
+  { 6, 45, nullptr },
+  { 6, 45, nullptr },
+  { 6, 45, nullptr },
+  { 6, 45, nullptr },
+  { 6, 46, nullptr },
+  { 6, 46, nullptr },
+  { 6, 46, nullptr },
+  { 6, 46, nullptr },
+  { 6, 54, nullptr },
+  { 6, 54, nullptr },
+  { 6, 54, nullptr },
+  { 6, 54, nullptr },
+  { 6, 55, nullptr },
+  { 6, 55, nullptr },
+  { 6, 55, nullptr },
+  { 6, 55, nullptr },
+  { 6, 71, nullptr },
+  { 6, 71, nullptr },
+  { 6, 71, nullptr },
+  { 6, 71, nullptr },
+  { 6, 77, nullptr },
+  { 6, 77, nullptr },
+  { 6, 77, nullptr },
+  { 6, 77, nullptr },
+  { 6, 83, nullptr },
+  { 6, 83, nullptr },
+  { 6, 83, nullptr },
+  { 6, 83, nullptr },
+  { 6, 99, nullptr },
+  { 6, 99, nullptr },
+  { 6, 99, nullptr },
+  { 6, 99, nullptr },
+  { 6, 100, nullptr },
+  { 6, 100, nullptr },
+  { 6, 100, nullptr },
+  { 6, 100, nullptr },
+  { 6, 105, nullptr },
+  { 6, 105, nullptr },
+  { 6, 105, nullptr },
+  { 6, 105, nullptr },
+  { 6, 109, nullptr },
+  { 6, 109, nullptr },
+  { 6, 109, nullptr },
+  { 6, 109, nullptr },
+  { 6, 110, nullptr },
+  { 6, 110, nullptr },
+  { 6, 110, nullptr },
+  { 6, 110, nullptr },
+  { 6, 111, nullptr },
+  { 6, 111, nullptr },
+  { 6, 111, nullptr },
+  { 6, 111, nullptr },
+  { 6, 112, nullptr },
+  { 6, 112, nullptr },
+  { 6, 112, nullptr },
+  { 6, 112, nullptr },
+  { 6, 114, nullptr },
+  { 6, 114, nullptr },
+  { 6, 114, nullptr },
+  { 6, 114, nullptr },
+  { 6, 115, nullptr },
+  { 6, 115, nullptr },
+  { 6, 115, nullptr },
+  { 6, 115, nullptr },
+  { 6, 116, nullptr },
+  { 6, 116, nullptr },
+  { 6, 116, nullptr },
+  { 6, 116, nullptr },
+  { 6, 117, nullptr },
+  { 6, 117, nullptr },
+  { 6, 117, nullptr },
+  { 6, 117, nullptr },
+  { 7, 34, nullptr },
+  { 7, 34, nullptr },
+  { 7, 47, nullptr },
+  { 7, 47, nullptr },
+  { 7, 61, nullptr },
+  { 7, 61, nullptr },
+  { 7, 65, nullptr },
+  { 7, 65, nullptr },
+  { 7, 98, nullptr },
+  { 7, 98, nullptr },
+  { 7, 102, nullptr },
+  { 7, 102, nullptr },
+  { 7, 103, nullptr },
+  { 7, 103, nullptr },
+  { 7, 104, nullptr },
+  { 7, 104, nullptr },
+  { 7, 108, nullptr },
+  { 7, 108, nullptr },
+  { 7, 118, nullptr },
+  { 7, 118, nullptr },
+  { 7, 120, nullptr },
+  { 7, 120, nullptr },
+  { 8, 66, nullptr },
+  { 8, 67, nullptr },
+  { 8, 68, nullptr },
+  { 8, 69, nullptr },
+  { 8, 70, nullptr },
+  { 8, 73, nullptr },
+  { 8, 74, nullptr },
+  { 8, 78, nullptr },
+  { 8, 79, nullptr },
+  { 8, 87, nullptr },
+  { 8, 119, nullptr },
+  { 8, 121, nullptr },
+  { 0, 0, &HuffmanIncoming246 },
+  { 0, 0, &HuffmanIncoming247 },
+  { 0, 0, &HuffmanIncoming248 },
+  { 0, 0, &HuffmanIncoming249 },
+  { 0, 0, &HuffmanIncoming250 },
+  { 0, 0, &HuffmanIncoming251 },
+  { 0, 0, &HuffmanIncoming252 },
+  { 0, 0, &HuffmanIncoming253 },
+  { 0, 0, &HuffmanIncoming254 },
+  { 0, 0, &HuffmanIncoming255 }
+};
+
+static HuffmanIncomingTable HuffmanIncomingRoot = {
+  8,
+  HuffmanIncomingEntriesRoot
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla__net__Http2HuffmanIncoming_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2HuffmanOutgoing.h
@@ -0,0 +1,278 @@
+/*
+ * THIS FILE IS AUTO-GENERATED. DO NOT EDIT!
+ */
+#ifndef mozilla__net__Http2HuffmanOutgoing_h
+#define mozilla__net__Http2HuffmanOutging_h
+
+namespace mozilla {
+namespace net {
+
+struct HuffmanOutgoingEntry {
+  uint8_t mLength;
+  uint32_t mValue;
+};
+
+static HuffmanOutgoingEntry HuffmanOutgoing[] = {
+  { 27, 0x07ffffba },
+  { 27, 0x07ffffbb },
+  { 27, 0x07ffffbc },
+  { 27, 0x07ffffbd },
+  { 27, 0x07ffffbe },
+  { 27, 0x07ffffbf },
+  { 27, 0x07ffffc0 },
+  { 27, 0x07ffffc1 },
+  { 27, 0x07ffffc2 },
+  { 27, 0x07ffffc3 },
+  { 27, 0x07ffffc4 },
+  { 27, 0x07ffffc5 },
+  { 27, 0x07ffffc6 },
+  { 27, 0x07ffffc7 },
+  { 27, 0x07ffffc8 },
+  { 27, 0x07ffffc9 },
+  { 27, 0x07ffffca },
+  { 27, 0x07ffffcb },
+  { 27, 0x07ffffcc },
+  { 27, 0x07ffffcd },
+  { 27, 0x07ffffce },
+  { 27, 0x07ffffcf },
+  { 27, 0x07ffffd0 },
+  { 27, 0x07ffffd1 },
+  { 27, 0x07ffffd2 },
+  { 27, 0x07ffffd3 },
+  { 27, 0x07ffffd4 },
+  { 27, 0x07ffffd5 },
+  { 27, 0x07ffffd6 },
+  { 27, 0x07ffffd7 },
+  { 27, 0x07ffffd8 },
+  { 27, 0x07ffffd9 },
+  { 8, 0x000000e8 },
+  { 12, 0x00000ffc },
+  { 14, 0x00003ffa },
+  { 15, 0x00007ffc },
+  { 15, 0x00007ffd },
+  { 6, 0x00000024 },
+  { 7, 0x0000006e },
+  { 15, 0x00007ffe },
+  { 11, 0x000007fa },
+  { 11, 0x000007fb },
+  { 10, 0x000003fa },
+  { 11, 0x000007fc },
+  { 8, 0x000000e9 },
+  { 6, 0x00000025 },
+  { 5, 0x00000004 },
+  { 4, 0x00000000 },
+  { 5, 0x00000005 },
+  { 5, 0x00000006 },
+  { 5, 0x00000007 },
+  { 6, 0x00000026 },
+  { 6, 0x00000027 },
+  { 6, 0x00000028 },
+  { 6, 0x00000029 },
+  { 6, 0x0000002a },
+  { 6, 0x0000002b },
+  { 6, 0x0000002c },
+  { 9, 0x000001ec },
+  { 8, 0x000000ea },
+  { 18, 0x0003fffe },
+  { 6, 0x0000002d },
+  { 17, 0x0001fffc },
+  { 9, 0x000001ed },
+  { 14, 0x00003ffb },
+  { 7, 0x0000006f },
+  { 8, 0x000000eb },
+  { 8, 0x000000ec },
+  { 8, 0x000000ed },
+  { 8, 0x000000ee },
+  { 7, 0x00000070 },
+  { 9, 0x000001ee },
+  { 9, 0x000001ef },
+  { 9, 0x000001f0 },
+  { 9, 0x000001f1 },
+  { 10, 0x000003fb },
+  { 9, 0x000001f2 },
+  { 8, 0x000000ef },
+  { 9, 0x000001f3 },
+  { 9, 0x000001f4 },
+  { 9, 0x000001f5 },
+  { 9, 0x000001f6 },
+  { 9, 0x000001f7 },
+  { 8, 0x000000f0 },
+  { 8, 0x000000f1 },
+  { 9, 0x000001f8 },
+  { 9, 0x000001f9 },
+  { 9, 0x000001fa },
+  { 9, 0x000001fb },
+  { 9, 0x000001fc },
+  { 10, 0x000003fc },
+  { 14, 0x00003ffc },
+  { 27, 0x07ffffda },
+  { 13, 0x00001ffc },
+  { 14, 0x00003ffd },
+  { 6, 0x0000002e },
+  { 19, 0x0007fffe },
+  { 5, 0x00000008 },
+  { 6, 0x0000002f },
+  { 5, 0x00000009 },
+  { 6, 0x00000030 },
+  { 4, 0x00000001 },
+  { 6, 0x00000031 },
+  { 6, 0x00000032 },
+  { 6, 0x00000033 },
+  { 5, 0x0000000a },
+  { 7, 0x00000071 },
+  { 7, 0x00000072 },
+  { 5, 0x0000000b },
+  { 6, 0x00000034 },
+  { 5, 0x0000000c },
+  { 5, 0x0000000d },
+  { 5, 0x0000000e },
+  { 8, 0x000000f2 },
+  { 5, 0x0000000f },
+  { 5, 0x00000010 },
+  { 5, 0x00000011 },
+  { 6, 0x00000035 },
+  { 7, 0x00000073 },
+  { 6, 0x00000036 },
+  { 8, 0x000000f3 },
+  { 8, 0x000000f4 },
+  { 8, 0x000000f5 },
+  { 17, 0x0001fffd },
+  { 11, 0x000007fd },
+  { 17, 0x0001fffe },
+  { 12, 0x00000ffd },
+  { 27, 0x07ffffdb },
+  { 27, 0x07ffffdc },
+  { 27, 0x07ffffdd },
+  { 27, 0x07ffffde },
+  { 27, 0x07ffffdf },
+  { 27, 0x07ffffe0 },
+  { 27, 0x07ffffe1 },
+  { 27, 0x07ffffe2 },
+  { 27, 0x07ffffe3 },
+  { 27, 0x07ffffe4 },
+  { 27, 0x07ffffe5 },
+  { 27, 0x07ffffe6 },
+  { 27, 0x07ffffe7 },
+  { 27, 0x07ffffe8 },
+  { 27, 0x07ffffe9 },
+  { 27, 0x07ffffea },
+  { 27, 0x07ffffeb },
+  { 27, 0x07ffffec },
+  { 27, 0x07ffffed },
+  { 27, 0x07ffffee },
+  { 27, 0x07ffffef },
+  { 27, 0x07fffff0 },
+  { 27, 0x07fffff1 },
+  { 27, 0x07fffff2 },
+  { 27, 0x07fffff3 },
+  { 27, 0x07fffff4 },
+  { 27, 0x07fffff5 },
+  { 27, 0x07fffff6 },
+  { 27, 0x07fffff7 },
+  { 27, 0x07fffff8 },
+  { 27, 0x07fffff9 },
+  { 27, 0x07fffffa },
+  { 27, 0x07fffffb },
+  { 27, 0x07fffffc },
+  { 27, 0x07fffffd },
+  { 27, 0x07fffffe },
+  { 27, 0x07ffffff },
+  { 26, 0x03ffff80 },
+  { 26, 0x03ffff81 },
+  { 26, 0x03ffff82 },
+  { 26, 0x03ffff83 },
+  { 26, 0x03ffff84 },
+  { 26, 0x03ffff85 },
+  { 26, 0x03ffff86 },
+  { 26, 0x03ffff87 },
+  { 26, 0x03ffff88 },
+  { 26, 0x03ffff89 },
+  { 26, 0x03ffff8a },
+  { 26, 0x03ffff8b },
+  { 26, 0x03ffff8c },
+  { 26, 0x03ffff8d },
+  { 26, 0x03ffff8e },
+  { 26, 0x03ffff8f },
+  { 26, 0x03ffff90 },
+  { 26, 0x03ffff91 },
+  { 26, 0x03ffff92 },
+  { 26, 0x03ffff93 },
+  { 26, 0x03ffff94 },
+  { 26, 0x03ffff95 },
+  { 26, 0x03ffff96 },
+  { 26, 0x03ffff97 },
+  { 26, 0x03ffff98 },
+  { 26, 0x03ffff99 },
+  { 26, 0x03ffff9a },
+  { 26, 0x03ffff9b },
+  { 26, 0x03ffff9c },
+  { 26, 0x03ffff9d },
+  { 26, 0x03ffff9e },
+  { 26, 0x03ffff9f },
+  { 26, 0x03ffffa0 },
+  { 26, 0x03ffffa1 },
+  { 26, 0x03ffffa2 },
+  { 26, 0x03ffffa3 },
+  { 26, 0x03ffffa4 },
+  { 26, 0x03ffffa5 },
+  { 26, 0x03ffffa6 },
+  { 26, 0x03ffffa7 },
+  { 26, 0x03ffffa8 },
+  { 26, 0x03ffffa9 },
+  { 26, 0x03ffffaa },
+  { 26, 0x03ffffab },
+  { 26, 0x03ffffac },
+  { 26, 0x03ffffad },
+  { 26, 0x03ffffae },
+  { 26, 0x03ffffaf },
+  { 26, 0x03ffffb0 },
+  { 26, 0x03ffffb1 },
+  { 26, 0x03ffffb2 },
+  { 26, 0x03ffffb3 },
+  { 26, 0x03ffffb4 },
+  { 26, 0x03ffffb5 },
+  { 26, 0x03ffffb6 },
+  { 26, 0x03ffffb7 },
+  { 26, 0x03ffffb8 },
+  { 26, 0x03ffffb9 },
+  { 26, 0x03ffffba },
+  { 26, 0x03ffffbb },
+  { 26, 0x03ffffbc },
+  { 26, 0x03ffffbd },
+  { 26, 0x03ffffbe },
+  { 26, 0x03ffffbf },
+  { 26, 0x03ffffc0 },
+  { 26, 0x03ffffc1 },
+  { 26, 0x03ffffc2 },
+  { 26, 0x03ffffc3 },
+  { 26, 0x03ffffc4 },
+  { 26, 0x03ffffc5 },
+  { 26, 0x03ffffc6 },
+  { 26, 0x03ffffc7 },
+  { 26, 0x03ffffc8 },
+  { 26, 0x03ffffc9 },
+  { 26, 0x03ffffca },
+  { 26, 0x03ffffcb },
+  { 26, 0x03ffffcc },
+  { 26, 0x03ffffcd },
+  { 26, 0x03ffffce },
+  { 26, 0x03ffffcf },
+  { 26, 0x03ffffd0 },
+  { 26, 0x03ffffd1 },
+  { 26, 0x03ffffd2 },
+  { 26, 0x03ffffd3 },
+  { 26, 0x03ffffd4 },
+  { 26, 0x03ffffd5 },
+  { 26, 0x03ffffd6 },
+  { 26, 0x03ffffd7 },
+  { 26, 0x03ffffd8 },
+  { 26, 0x03ffffd9 },
+  { 26, 0x03ffffda },
+  { 26, 0x03ffffdb },
+  { 26, 0x03ffffdc }
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla__net__Http2HuffmanOutgoing_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Push.cpp
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include <algorithm>
+
+#include "Http2Push.h"
+
+#include "nsDependentString.h"
+
+namespace mozilla {
+namespace net {
+
+//////////////////////////////////////////
+// Http2PushedStream
+//////////////////////////////////////////
+
+Http2PushedStream::Http2PushedStream(Http2PushTransactionBuffer *aTransaction,
+                                     Http2Session *aSession,
+                                     Http2Stream *aAssociatedStream,
+                                     uint32_t aID)
+  :Http2Stream(aTransaction, aSession, 0)
+  , mConsumerStream(nullptr)
+  , mBufferedPush(aTransaction)
+  , mStatus(NS_OK)
+  , mPushCompleted(false)
+  , mDeferCleanupOnSuccess(true)
+{
+  LOG3(("Http2PushedStream ctor this=%p 0x%X\n", this, aID));
+  mStreamID = aID;
+  MOZ_ASSERT(!(aID & 1)); // must be even to be a pushed stream
+  mBufferedPush->SetPushStream(this);
+  mLoadGroupCI = aAssociatedStream->LoadGroupConnectionInfo();
+  mLastRead = TimeStamp::Now();
+  SetPriority(aAssociatedStream->Priority() + 1);
+}
+
+bool
+Http2PushedStream::GetPushComplete()
+{
+  return mPushCompleted;
+}
+
+nsresult
+Http2PushedStream::WriteSegments(nsAHttpSegmentWriter *writer,
+                                 uint32_t count, uint32_t *countWritten)
+{
+  nsresult rv = Http2Stream::WriteSegments(writer, count, countWritten);
+  if (NS_SUCCEEDED(rv) && *countWritten) {
+    mLastRead = TimeStamp::Now();
+  }
+
+  if (rv == NS_BASE_STREAM_CLOSED) {
+    mPushCompleted = true;
+    rv = NS_OK; // this is what a normal HTTP transaction would do
+  }
+  if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv))
+    mStatus = rv;
+  return rv;
+}
+
+nsresult
+Http2PushedStream::ReadSegments(nsAHttpSegmentReader *,
+                                uint32_t, uint32_t *count)
+{
+  // The request headers for this has been processed, so we need to verify
+  // that :authority, :scheme, and :path MUST be present. :method MUST NOT be
+  // present
+  CreatePushHashKey(mHeaderScheme, mHeaderHost,
+                    mSession->Serial(), mHeaderPath,
+                    mOrigin, mHashKey);
+
+  LOG3(("Http2PushStream 0x%X hash key %s\n", mStreamID, mHashKey.get()));
+
+  // the write side of a pushed transaction just involves manipulating a little state
+  SetSentFin(true);
+  Http2Stream::mAllHeadersSent = 1;
+  Http2Stream::ChangeState(UPSTREAM_COMPLETE);
+  *count = 0;
+  return NS_OK;
+}
+
+bool
+Http2PushedStream::GetHashKey(nsCString &key)
+{
+  if (mHashKey.IsEmpty())
+    return false;
+
+  key = mHashKey;
+  return true;
+}
+
+void
+Http2PushedStream::ConnectPushedStream(Http2Stream *stream)
+{
+  mSession->ConnectPushedStream(stream);
+}
+
+bool
+Http2PushedStream::IsOrphaned(TimeStamp now)
+{
+  MOZ_ASSERT(!now.IsNull());
+
+  // if session is not transmitting, and is also not connected to a consumer
+  // stream, and its been like that for too long then it is oprhaned
+
+  if (mConsumerStream)
+    return false;
+
+  bool rv = ((now - mLastRead).ToSeconds() > 30.0);
+  if (rv) {
+    LOG3(("Http2PushedStream:IsOrphaned 0x%X IsOrphaned %3.2f\n",
+          mStreamID, (now - mLastRead).ToSeconds()));
+  }
+  return rv;
+}
+
+nsresult
+Http2PushedStream::GetBufferedData(char *buf,
+                                   uint32_t count, uint32_t *countWritten)
+{
+  if (NS_FAILED(mStatus))
+    return mStatus;
+
+  nsresult rv = mBufferedPush->GetBufferedData(buf, count, countWritten);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (!*countWritten)
+    rv = GetPushComplete() ? NS_BASE_STREAM_CLOSED : NS_BASE_STREAM_WOULD_BLOCK;
+
+  return rv;
+}
+
+//////////////////////////////////////////
+// Http2PushTransactionBuffer
+// This is the nsAHttpTransction owned by the stream when the pushed
+// stream has not yet been matched with a pull request
+//////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS0(Http2PushTransactionBuffer)
+
+Http2PushTransactionBuffer::Http2PushTransactionBuffer()
+  : mStatus(NS_OK)
+  , mRequestHead(nullptr)
+  , mPushStream(nullptr)
+  , mIsDone(false)
+  , mBufferedHTTP1Size(kDefaultBufferSize)
+  , mBufferedHTTP1Used(0)
+  , mBufferedHTTP1Consumed(0)
+{
+  mBufferedHTTP1 = new char[mBufferedHTTP1Size];
+}
+
+Http2PushTransactionBuffer::~Http2PushTransactionBuffer()
+{
+  delete mRequestHead;
+}
+
+void
+Http2PushTransactionBuffer::SetConnection(nsAHttpConnection *conn)
+{
+}
+
+nsAHttpConnection *
+Http2PushTransactionBuffer::Connection()
+{
+  return nullptr;
+}
+
+void
+Http2PushTransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
+{
+  *outCB = nullptr;
+}
+
+void
+Http2PushTransactionBuffer::OnTransportStatus(nsITransport* transport,
+                                              nsresult status, uint64_t progress)
+{
+}
+
+bool
+Http2PushTransactionBuffer::IsDone()
+{
+  return mIsDone;
+}
+
+nsresult
+Http2PushTransactionBuffer::Status()
+{
+  return mStatus;
+}
+
+uint32_t
+Http2PushTransactionBuffer::Caps()
+{
+  return 0;
+}
+
+void
+Http2PushTransactionBuffer::SetDNSWasRefreshed()
+{
+}
+
+uint64_t
+Http2PushTransactionBuffer::Available()
+{
+  return mBufferedHTTP1Used - mBufferedHTTP1Consumed;
+}
+
+nsresult
+Http2PushTransactionBuffer::ReadSegments(nsAHttpSegmentReader *reader,
+                                         uint32_t count, uint32_t *countRead)
+{
+  *countRead = 0;
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+Http2PushTransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer,
+                                          uint32_t count, uint32_t *countWritten)
+{
+  if ((mBufferedHTTP1Size - mBufferedHTTP1Used) < 20480) {
+    Http2Session::EnsureBuffer(mBufferedHTTP1,
+                                mBufferedHTTP1Size + kDefaultBufferSize,
+                                mBufferedHTTP1Used,
+                                mBufferedHTTP1Size);
+  }
+
+  count = std::min(count, mBufferedHTTP1Size - mBufferedHTTP1Used);
+  nsresult rv = writer->OnWriteSegment(mBufferedHTTP1 + mBufferedHTTP1Used,
+                                       count, countWritten);
+  if (NS_SUCCEEDED(rv)) {
+    mBufferedHTTP1Used += *countWritten;
+  }
+  else if (rv == NS_BASE_STREAM_CLOSED) {
+    mIsDone = true;
+  }
+
+  if (Available()) {
+    Http2Stream *consumer = mPushStream->GetConsumerStream();
+
+    if (consumer) {
+      LOG3(("Http2PushTransactionBuffer::WriteSegments notifying connection "
+            "consumer data available 0x%X [%u]\n",
+            mPushStream->StreamID(), Available()));
+      mPushStream->ConnectPushedStream(consumer);
+    }
+  }
+
+  return rv;
+}
+
+uint32_t
+Http2PushTransactionBuffer::Http1xTransactionCount()
+{
+  return 0;
+}
+
+nsHttpRequestHead *
+Http2PushTransactionBuffer::RequestHead()
+{
+  if (!mRequestHead)
+    mRequestHead = new nsHttpRequestHead();
+  return mRequestHead;
+}
+
+nsresult
+Http2PushTransactionBuffer::TakeSubTransactions(
+  nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+Http2PushTransactionBuffer::SetProxyConnectFailed()
+{
+}
+
+void
+Http2PushTransactionBuffer::Close(nsresult reason)
+{
+  mStatus = reason;
+  mIsDone = true;
+}
+
+nsresult
+Http2PushTransactionBuffer::AddTransaction(nsAHttpTransaction *trans)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+uint32_t
+Http2PushTransactionBuffer::PipelineDepth()
+{
+  return 0;
+}
+
+nsresult
+Http2PushTransactionBuffer::SetPipelinePosition(int32_t position)
+{
+  return NS_OK;
+}
+
+int32_t
+Http2PushTransactionBuffer::PipelinePosition()
+{
+  return 1;
+}
+
+nsresult
+Http2PushTransactionBuffer::GetBufferedData(char *buf,
+                                            uint32_t count,
+                                            uint32_t *countWritten)
+{
+  *countWritten = std::min(count, static_cast<uint32_t>(Available()));
+  if (*countWritten) {
+    memcpy(buf, mBufferedHTTP1 + mBufferedHTTP1Consumed, *countWritten);
+    mBufferedHTTP1Consumed += *countWritten;
+  }
+
+  // If all the data has been consumed then reset the buffer
+  if (mBufferedHTTP1Consumed == mBufferedHTTP1Used) {
+    mBufferedHTTP1Consumed = 0;
+    mBufferedHTTP1Used = 0;
+  }
+
+  return NS_OK;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Push.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_Http2Push_Internal_h
+#define mozilla_net_Http2Push_Internal_h
+
+#include "Http2Session.h"
+#include "Http2Stream.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/TimeStamp.h"
+#include "nsHttpRequestHead.h"
+#include "nsILoadGroup.h"
+#include "nsString.h"
+#include "PSpdyPush.h"
+
+namespace mozilla {
+namespace net {
+
+class Http2PushTransactionBuffer;
+
+class Http2PushedStream MOZ_FINAL : public Http2Stream
+{
+public:
+  Http2PushedStream(Http2PushTransactionBuffer *aTransaction,
+                    Http2Session *aSession,
+                    Http2Stream *aAssociatedStream,
+                    uint32_t aID);
+  virtual ~Http2PushedStream() {}
+
+  bool GetPushComplete();
+  Http2Stream *GetConsumerStream() { return mConsumerStream; };
+  void SetConsumerStream(Http2Stream *aStream) { mConsumerStream = aStream; }
+  bool GetHashKey(nsCString &key);
+
+  // override of Http2Stream
+  nsresult ReadSegments(nsAHttpSegmentReader *,  uint32_t, uint32_t *);
+  nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
+
+  nsILoadGroupConnectionInfo *LoadGroupConnectionInfo() { return mLoadGroupCI; };
+  void ConnectPushedStream(Http2Stream *consumer);
+
+  bool DeferCleanupOnSuccess() { return mDeferCleanupOnSuccess; }
+  void SetDeferCleanupOnSuccess(bool val) { mDeferCleanupOnSuccess = val; }
+
+  bool IsOrphaned(TimeStamp now);
+
+  nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
+
+  // overload of Http2Stream
+  virtual bool HasSink() { return !!mConsumerStream; }
+
+private:
+
+  Http2Stream *mConsumerStream; // paired request stream that consumes from
+                                // real http/2 one.. null until a match is made.
+
+  nsCOMPtr<nsILoadGroupConnectionInfo> mLoadGroupCI;
+
+  Http2PushTransactionBuffer *mBufferedPush;
+  mozilla::TimeStamp mLastRead;
+
+  nsCString mHashKey;
+  nsresult mStatus;
+  bool mPushCompleted; // server push FIN received
+  bool mDeferCleanupOnSuccess;
+};
+
+class Http2PushTransactionBuffer MOZ_FINAL : public nsAHttpTransaction
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSAHTTPTRANSACTION
+
+  Http2PushTransactionBuffer();
+  virtual ~Http2PushTransactionBuffer();
+
+  nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
+  void SetPushStream(Http2PushedStream *stream) { mPushStream = stream; }
+
+private:
+  const static uint32_t kDefaultBufferSize = 4096;
+
+  nsresult mStatus;
+  nsHttpRequestHead *mRequestHead;
+  Http2PushedStream *mPushStream;
+  bool mIsDone;
+
+  nsAutoArrayPtr<char> mBufferedHTTP1;
+  uint32_t mBufferedHTTP1Size;
+  uint32_t mBufferedHTTP1Used;
+  uint32_t mBufferedHTTP1Consumed;
+};
+
+} // namespace mozilla::net
+} // namespace mozilla
+
+#endif // mozilla_net_Http2Push_Internal_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -0,0 +1,2875 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include <algorithm>
+
+#include "Http2Session.h"
+#include "Http2Stream.h"
+#include "Http2Push.h"
+
+#include "mozilla/Telemetry.h"
+#include "mozilla/Preferences.h"
+#include "nsHttp.h"
+#include "nsHttpHandler.h"
+#include "nsHttpConnection.h"
+#include "nsILoadGroup.h"
+#include "nsISSLSocketControl.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#ifdef DEBUG
+// defined by the socket transport service while active
+extern PRThread *gSocketThread;
+#endif
+
+namespace mozilla {
+namespace net {
+
+// Http2Session has multiple inheritance of things that implement
+// nsISupports, so this magic is taken from nsHttpPipeline that
+// implements some of the same abstract classes.
+NS_IMPL_ADDREF(Http2Session)
+NS_IMPL_RELEASE(Http2Session)
+NS_INTERFACE_MAP_BEGIN(Http2Session)
+NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
+NS_INTERFACE_MAP_END
+
+// "magic" refers to the string that preceeds HTTP/2 on the wire
+// to help find any intermediaries speaking an older version of HTTP
+const uint8_t Http2Session::kMagicHello[] = {
+  0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
+  0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
+};
+
+#define RETURN_SESSION_ERROR(o,x)  \
+do {                             \
+  (o)->mGoAwayReason = (x);      \
+  return NS_ERROR_ILLEGAL_VALUE; \
+  } while (0)
+
+Http2Session::Http2Session(nsAHttpTransaction *aHttpTransaction,
+                           nsISocketTransport *aSocketTransport,
+                           int32_t firstPriority)
+  : mSocketTransport(aSocketTransport),
+  mSegmentReader(nullptr),
+  mSegmentWriter(nullptr),
+  mNextStreamID(3), // 1 is reserved for Updgrade handshakes
+  mConcurrentHighWater(0),
+  mDownstreamState(BUFFERING_OPENING_SETTINGS),
+  mInputFrameBufferSize(kDefaultBufferSize),
+  mInputFrameBufferUsed(0),
+  mInputFrameFinal(false),
+  mInputFrameDataStream(nullptr),
+  mNeedsCleanup(nullptr),
+  mDownstreamRstReason(NO_HTTP_ERROR),
+  mExpectedHeaderID(0),
+  mExpectedPushPromiseID(0),
+  mContinuedPromiseStream(0),
+  mShouldGoAway(false),
+  mClosed(false),
+  mCleanShutdown(false),
+  mServerUsesFlowControl(true),
+  mTLSProfileConfirmed(false),
+  mGoAwayReason(NO_HTTP_ERROR),
+  mGoAwayID(0),
+  mOutgoingGoAwayID(0),
+  mMaxConcurrent(kDefaultMaxConcurrent),
+  mConcurrent(0),
+  mServerPushedResources(0),
+  mServerInitialStreamWindow(kDefaultRwin),
+  mLocalSessionWindow(kDefaultRwin),
+  mServerSessionWindow(kDefaultRwin),
+  mOutputQueueSize(kDefaultQueueSize),
+  mOutputQueueUsed(0),
+  mOutputQueueSent(0),
+  mLastReadEpoch(PR_IntervalNow()),
+  mPingSentEpoch(0)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+    static uint64_t sSerial;
+    mSerial = ++sSerial;
+
+    LOG3(("Http2Session::Http2Session %p transaction 1 = %p serial=0x%X\n",
+          this, aHttpTransaction, mSerial));
+
+    mConnection = aHttpTransaction->Connection();
+    mInputFrameBuffer = new char[mInputFrameBufferSize];
+    mOutputQueueBuffer = new char[mOutputQueueSize];
+    mDecompressBuffer.SetCapacity(kDefaultBufferSize);
+
+    mPushAllowance = gHttpHandler->SpdyPushAllowance();
+
+    mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
+    SendHello();
+
+    if (!aHttpTransaction->IsNullTransaction())
+      AddStream(aHttpTransaction, firstPriority);
+    mLastDataReadEpoch = mLastReadEpoch;
+
+    mPingThreshold = gHttpHandler->SpdyPingThreshold();
+}
+
+PLDHashOperator
+Http2Session::ShutdownEnumerator(nsAHttpTransaction *key,
+                                 nsAutoPtr<Http2Stream> &stream,
+                                 void *closure)
+{
+  Http2Session *self = static_cast<Http2Session *>(closure);
+
+  // On a clean server hangup the server sets the GoAwayID to be the ID of
+  // the last transaction it processed. If the ID of stream in the
+  // local stream is greater than that it can safely be restarted because the
+  // server guarantees it was not partially processed. Streams that have not
+  // registered an ID haven't actually been sent yet so they can always be
+  // restarted.
+  if (self->mCleanShutdown &&
+      (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID())) {
+    self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted
+  } else {
+    self->CloseStream(stream, NS_ERROR_ABORT);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+PLDHashOperator
+Http2Session::GoAwayEnumerator(nsAHttpTransaction *key,
+                               nsAutoPtr<Http2Stream> &stream,
+                               void *closure)
+{
+  Http2Session *self = static_cast<Http2Session *>(closure);
+
+  // these streams were not processed by the server and can be restarted.
+  // Do that after the enumerator completes to avoid the risk of
+  // a restart event re-entrantly modifying this hash. Be sure not to restart
+  // a pushed (even numbered) stream
+  if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) ||
+      !stream->HasRegisteredID()) {
+    self->mGoAwayStreamsToRestart.Push(stream);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+Http2Session::~Http2Session()
+{
+  LOG3(("Http2Session::~Http2Session %p mDownstreamState=%X",
+        this, mDownstreamState));
+
+  mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
+  Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
+  Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
+  Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
+                        mServerPushedResources);
+}
+
+void
+Http2Session::LogIO(Http2Session *self, Http2Stream *stream,
+                    const char *label,
+                    const char *data, uint32_t datalen)
+{
+  if (!LOG4_ENABLED())
+    return;
+
+  LOG4(("Http2Session::LogIO %p stream=%p id=0x%X [%s]",
+        self, stream, stream ? stream->StreamID() : 0, label));
+
+  // Max line is (16 * 3) + 10(prefix) + newline + null
+  char linebuf[128];
+  uint32_t index;
+  char *line = linebuf;
+
+  linebuf[127] = 0;
+
+  for (index = 0; index < datalen; ++index) {
+    if (!(index % 16)) {
+      if (index) {
+        *line = 0;
+        LOG4(("%s", linebuf));
+      }
+      line = linebuf;
+      PR_snprintf(line, 128, "%08X: ", index);
+      line += 10;
+    }
+    PR_snprintf(line, 128 - (line - linebuf), "%02X ",
+                (reinterpret_cast<const uint8_t *>(data))[index]);
+    line += 3;
+  }
+  if (index) {
+    *line = 0;
+    LOG4(("%s", linebuf));
+  }
+}
+
+typedef nsresult (*Http2ControlFx) (Http2Session *self);
+static Http2ControlFx sControlFunctions[] = {
+  nullptr, // type 0 data is not a control function
+  Http2Session::RecvHeaders,
+  Http2Session::RecvPriority,
+  Http2Session::RecvRstStream,
+  Http2Session::RecvSettings,
+  Http2Session::RecvPushPromise,
+  Http2Session::RecvPing,
+  Http2Session::RecvGoAway,
+  Http2Session::RecvUnused1,
+  Http2Session::RecvWindowUpdate,
+  Http2Session::RecvContinuation
+};
+
+bool
+Http2Session::RoomForMoreConcurrent()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  return (mConcurrent < mMaxConcurrent);
+}
+
+bool
+Http2Session::RoomForMoreStreams()
+{
+  if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
+    return false;
+
+  return !mShouldGoAway;
+}
+
+PRIntervalTime
+Http2Session::IdleTime()
+{
+  return PR_IntervalNow() - mLastDataReadEpoch;
+}
+
+void
+Http2Session::ReadTimeoutTick(PRIntervalTime now)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  LOG3(("Http2Session::ReadTimeoutTick %p delta since last read %ds\n",
+       this, PR_IntervalToSeconds(now - mLastReadEpoch)));
+
+  if (!mPingThreshold)
+    return;
+
+  if ((now - mLastReadEpoch) < mPingThreshold) {
+    // recent activity means ping is not an issue
+    if (mPingSentEpoch)
+      mPingSentEpoch = 0;
+    return;
+  }
+
+  if (mPingSentEpoch) {
+    LOG3(("Http2Session::ReadTimeoutTick %p handle outstanding ping\n"));
+    if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
+      LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n",
+           this));
+      mPingSentEpoch = 0;
+      Close(NS_ERROR_NET_TIMEOUT);
+    }
+    return;
+  }
+
+  LOG3(("Http2Session::ReadTimeoutTick %p generating ping\n", this));
+
+  mPingSentEpoch = PR_IntervalNow();
+  if (!mPingSentEpoch)
+    mPingSentEpoch = 1; // avoid the 0 sentinel value
+  GeneratePing(false);
+  ResumeRecv(); // read the ping reply
+
+  // Check for orphaned push streams. This looks expensive, but generally the
+  // list is empty.
+  Http2PushedStream *deleteMe;
+  TimeStamp timestampNow;
+  do {
+    deleteMe = nullptr;
+
+    for (uint32_t index = mPushedStreams.Length();
+         index > 0 ; --index) {
+      Http2PushedStream *pushedStream = mPushedStreams[index - 1];
+
+      if (timestampNow.IsNull())
+        timestampNow = TimeStamp::Now(); // lazy initializer
+
+      // if stream finished, but is not connected, and its been like that for
+      // long then cleanup the stream.
+      if (pushedStream->IsOrphaned(timestampNow))
+      {
+        LOG3(("Http2Session Timeout Pushed Stream %p 0x%X\n",
+              this, pushedStream->StreamID()));
+        deleteMe = pushedStream;
+        break; // don't CleanupStream() while iterating this vector
+      }
+    }
+    if (deleteMe)
+      CleanupStream(deleteMe, NS_ERROR_ABORT, CANCEL_ERROR);
+
+  } while (deleteMe);
+}
+
+uint32_t
+Http2Session::RegisterStreamID(Http2Stream *stream, uint32_t aNewID)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(mNextStreamID < 0xfffffff0,
+             "should have stopped admitting streams");
+  MOZ_ASSERT(!(aNewID & 1),
+             "0 for autoassign pull, otherwise explicit even push assignment");
+
+  if (!aNewID) {
+    // auto generate a new pull stream ID
+    aNewID = mNextStreamID;
+    MOZ_ASSERT(aNewID & 1, "pull ID must be odd.");
+    mNextStreamID += 2;
+  }
+
+  LOG3(("Http2Session::RegisterStreamID session=%p stream=%p id=0x%X "
+        "concurrent=%d",this, stream, aNewID, mConcurrent));
+
+  // We've used up plenty of ID's on this session. Start
+  // moving to a new one before there is a crunch involving
+  // server push streams or concurrent non-registered submits
+  if (aNewID >= kMaxStreamID)
+    mShouldGoAway = true;
+
+  // integrity check
+  if (mStreamIDHash.Get(aNewID)) {
+    LOG3(("   New ID already present\n"));
+    MOZ_ASSERT(false, "New ID already present in mStreamIDHash");
+    mShouldGoAway = true;
+    return kDeadStreamID;
+  }
+
+  mStreamIDHash.Put(aNewID, stream);
+  return aNewID;
+}
+
+bool
+Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
+                        int32_t aPriority)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  // integrity check
+  if (mStreamTransactionHash.Get(aHttpTransaction)) {
+    LOG3(("   New transaction already present\n"));
+    MOZ_ASSERT(false, "AddStream duplicate transaction pointer");
+    return false;
+  }
+
+  aHttpTransaction->SetConnection(this);
+  Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority);
+
+  LOG3(("Http2Session::AddStream session=%p stream=%p NextID=0x%X (tentative)",
+        this, stream, mNextStreamID));
+
+  mStreamTransactionHash.Put(aHttpTransaction, stream);
+
+  if (RoomForMoreConcurrent()) {
+    LOG3(("Http2Session::AddStream %p stream %p activated immediately.",
+          this, stream));
+    ActivateStream(stream);
+  } else {
+    LOG3(("Http2Session::AddStream %p stream %p queued.", this, stream));
+    mQueuedStreams.Push(stream);
+  }
+
+  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
+    LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n",
+          this, aHttpTransaction));
+    DontReuse();
+  }
+
+  return true;
+}
+
+void
+Http2Session::ActivateStream(Http2Stream *stream)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
+             "Do not activate pushed streams");
+
+  MOZ_ASSERT(!stream->CountAsActive());
+  stream->SetCountAsActive(true);
+  ++mConcurrent;
+
+  if (mConcurrent > mConcurrentHighWater)
+    mConcurrentHighWater = mConcurrent;
+  LOG3(("Http2Session::AddStream %p activating stream %p Currently %d "
+        "streams in session, high water mark is %d",
+        this, stream, mConcurrent, mConcurrentHighWater));
+
+  mReadyForWrite.Push(stream);
+  SetWriteCallbacks();
+
+  // Kick off the headers transmit without waiting for the poll loop
+  // This won't work for stream id=1 because there is no segment reader
+  // yet.
+  if (mSegmentReader) {
+    uint32_t countRead;
+    ReadSegments(nullptr, kDefaultBufferSize, &countRead);
+  }
+}
+
+void
+Http2Session::ProcessPending()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  while (RoomForMoreConcurrent()) {
+    Http2Stream *stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront());
+    if (!stream)
+      return;
+    LOG3(("Http2Session::ProcessPending %p stream %p activated from queue.",
+          this, stream));
+    ActivateStream(stream);
+  }
+}
+
+nsresult
+Http2Session::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
+                          uint32_t count, uint32_t *countWritten)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  if (!count) {
+    *countWritten = 0;
+    return NS_OK;
+  }
+
+  nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
+  if (NS_SUCCEEDED(rv) && *countWritten > 0)
+    mLastReadEpoch = PR_IntervalNow();
+  return rv;
+}
+
+void
+Http2Session::SetWriteCallbacks()
+{
+  if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
+    mConnection->ResumeSend();
+}
+
+void
+Http2Session::RealignOutputQueue()
+{
+  mOutputQueueUsed -= mOutputQueueSent;
+  memmove(mOutputQueueBuffer.get(),
+          mOutputQueueBuffer.get() + mOutputQueueSent,
+          mOutputQueueUsed);
+  mOutputQueueSent = 0;
+}
+
+void
+Http2Session::FlushOutputQueue()
+{
+  if (!mSegmentReader || !mOutputQueueUsed)
+    return;
+
+  nsresult rv;
+  uint32_t countRead;
+  uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
+
+  rv = mSegmentReader->
+    OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
+                  &countRead);
+  LOG3(("Http2Session::FlushOutputQueue %p sz=%d rv=%x actual=%d",
+        this, avail, rv, countRead));
+
+  // Dont worry about errors on write, we will pick this up as a read error too
+  if (NS_FAILED(rv))
+    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.
+
+  if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
+      ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
+    RealignOutputQueue();
+  }
+}
+
+void
+Http2Session::DontReuse()
+{
+  mShouldGoAway = true;
+  if (!mStreamTransactionHash.Count())
+    Close(NS_OK);
+}
+
+uint32_t
+Http2Session::GetWriteQueueSize()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  return mReadyForWrite.GetSize();
+}
+
+void
+Http2Session::ChangeDownstreamState(enum internalStateType newState)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  LOG3(("Http2Stream::ChangeDownstreamState() %p from %X to %X",
+        this, mDownstreamState, newState));
+  mDownstreamState = newState;
+}
+
+void
+Http2Session::ResetDownstreamState()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  LOG3(("Http2Stream::ResetDownstreamState() %p", this));
+  ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+
+  if (mInputFrameFinal && mInputFrameDataStream) {
+    mInputFrameFinal = false;
+    LOG3(("  SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
+    mInputFrameDataStream->SetRecvdFin(true);
+    MaybeDecrementConcurrent(mInputFrameDataStream);
+  }
+  mInputFrameBufferUsed = 0;
+  mInputFrameDataStream = nullptr;
+}
+
+template<typename T> void
+Http2Session::EnsureBuffer(nsAutoArrayPtr<T> &buf, uint32_t newSize,
+                           uint32_t preserve, uint32_t &objSize)
+{
+  if (objSize >= newSize)
+    return;
+
+  // Leave a little slop on the new allocation - add 2KB to
+  // what we need and then round the result up to a 4KB (page)
+  // boundary.
+
+  objSize = (newSize + 2048 + 4095) & ~4095;
+
+  static_assert(sizeof(T) == 1, "sizeof(T) must be 1");
+  nsAutoArrayPtr<T> tmp(new T[objSize]);
+  memcpy(tmp, buf, preserve);
+  buf = tmp;
+}
+
+// Instantiate supported templates explicitly.
+template void
+Http2Session::EnsureBuffer(nsAutoArrayPtr<char> &buf, uint32_t newSize,
+                                  uint32_t preserve, uint32_t &objSize);
+
+template void
+Http2Session::EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf, uint32_t newSize,
+                                  uint32_t preserve, uint32_t &objSize);
+
+// call with data length (i.e. 0 for 0 data bytes - ignore 8 byte header)
+// dest must have 8 bytes of allocated space
+template<typename charType> void
+Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength,
+                                uint8_t frameType, uint8_t frameFlags,
+                                uint32_t streamID)
+{
+  MOZ_ASSERT(frameLength <= kMaxFrameData, "framelength too large");
+  MOZ_ASSERT(!(streamID & 0x80000000));
+
+  frameLength = PR_htons(frameLength);
+  streamID = PR_htonl(streamID);
+
+  memcpy(dest, &frameLength, 2);
+  dest[2] = frameType;
+  dest[3] = frameFlags;
+  memcpy(dest + 4, &streamID, 4);
+}
+
+char *
+Http2Session::EnsureOutputBuffer(uint32_t spaceNeeded)
+{
+  // this is an infallible allocation (if an allocation is
+  // needed, which is probably isn't)
+  EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + spaceNeeded,
+               mOutputQueueUsed, mOutputQueueSize);
+  return mOutputQueueBuffer.get() + mOutputQueueUsed;
+}
+
+template void
+Http2Session::CreateFrameHeader(char *dest, uint16_t frameLength,
+                                uint8_t frameType, uint8_t frameFlags,
+                                uint32_t streamID);
+
+template void
+Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength,
+                                uint8_t frameType, uint8_t frameFlags,
+                                uint32_t streamID);
+
+void
+Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream)
+{
+  LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n",
+        this, aStream->StreamID(), mConcurrent, aStream->CountAsActive()));
+
+  if (!aStream->CountAsActive())
+    return;
+
+  MOZ_ASSERT(mConcurrent);
+  aStream->SetCountAsActive(false);
+  --mConcurrent;
+  ProcessPending();
+}
+
+// Need to decompress some data in order to keep the compression
+// context correct, but we really don't care what the result is
+nsresult
+Http2Session::UncompressAndDiscard()
+{
+  nsresult rv;
+  nsAutoCString trash;
+
+  rv = mDecompressor.DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(mDecompressBuffer.BeginReading()),
+                                       mDecompressBuffer.Length(), trash);
+  mDecompressBuffer.Truncate();
+  if (NS_FAILED(rv)) {
+    LOG3(("Http2Session::UncompressAndDiscard %p Compression Error\n",
+          this));
+    mGoAwayReason = COMPRESSION_ERROR;
+    return rv;
+  }
+  return NS_OK;
+}
+
+void
+Http2Session::GeneratePing(bool isAck)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::GeneratePing %p isAck=%d\n", this, isAck));
+
+  char *packet = EnsureOutputBuffer(16);
+  mOutputQueueUsed += 16;
+
+  if (isAck) {
+    CreateFrameHeader(packet, 8, FRAME_TYPE_PING, kFlag_ACK, 0);
+    memcpy(packet + 8, mInputFrameBuffer.get() + 8, 8);
+  } else {
+    CreateFrameHeader(packet, 8, FRAME_TYPE_PING, 0, 0);
+    memset(packet + 8, 0, 8);
+  }
+
+  LogIO(this, nullptr, "Generate Ping", packet, 16);
+  FlushOutputQueue();
+}
+
+void
+Http2Session::GenerateSettingsAck()
+{
+  // need to generate ack of this settings frame
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::GenerateSettingsAck %p\n", this));
+
+  char *packet = EnsureOutputBuffer(8);
+  mOutputQueueUsed += 8;
+  CreateFrameHeader(packet, 0, FRAME_TYPE_SETTINGS, kFlag_ACK, 0);
+  LogIO(this, nullptr, "Generate Settings ACK", packet, 8);
+  FlushOutputQueue();
+}
+
+void
+Http2Session::GeneratePriority(uint32_t aID, uint32_t aPriority)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::GeneratePriority %p %X %X\n",
+        this, aID, aPriority));
+
+  char *packet = EnsureOutputBuffer(12);
+  mOutputQueueUsed += 12;
+
+  CreateFrameHeader(packet, 4, FRAME_TYPE_PRIORITY, 0, aID);
+  aPriority = PR_htonl(aPriority);
+  memcpy(packet + 8, &aPriority, 4);
+  LogIO(this, nullptr, "Generate Priority", packet, 12);
+  FlushOutputQueue();
+}
+
+void
+Http2Session::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  // make sure we don't do this twice for the same stream (at least if we
+  // have a stream entry for it)
+  Http2Stream *stream = mStreamIDHash.Get(aID);
+  if (stream) {
+    if (stream->SentReset())
+      return;
+    stream->SetSentReset(true);
+  }
+
+  LOG3(("Http2Session::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
+
+  char *packet = EnsureOutputBuffer(12);
+  mOutputQueueUsed += 12;
+  CreateFrameHeader(packet, 4, FRAME_TYPE_RST_STREAM, 0, aID);
+
+  aStatusCode = PR_htonl(aStatusCode);
+  memcpy(packet + 8, &aStatusCode, 4);
+
+  LogIO(this, nullptr, "Generate Reset", packet, 12);
+  FlushOutputQueue();
+}
+
+void
+Http2Session::GenerateGoAway(uint32_t aStatusCode)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode));
+
+  char *packet = EnsureOutputBuffer(16);
+  mOutputQueueUsed += 16;
+
+  memset(packet + 8, 0, 8);
+  CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0);
+
+  // last-good-stream-id are bytes 8-11 reflecting pushes
+  uint32_t goAway = PR_htonl(mOutgoingGoAwayID);
+  memcpy (packet + 7, &goAway, 4);
+
+  // bytes 12-15 are the status code.
+  aStatusCode = PR_htonl(aStatusCode);
+  memcpy(packet + 12, &aStatusCode, 4);
+
+  LogIO(this, nullptr, "Generate GoAway", packet, 16);
+  FlushOutputQueue();
+}
+
+// The Hello is comprised of 24 octets of magic, which are designed to
+// flush out silent but broken intermediaries, followed by a settings
+// frame which sets a small flow control window for pushes and a
+// window update frame which creates a large session flow control window
+void
+Http2Session::SendHello()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::SendHello %p\n", this));
+
+  // sized for magic + 2 settings and a session window update to follow
+  // 24 magic, 32 for settings (8 header + 3 settings @8), 12 for window update
+  static const uint32_t maxSettings = 3;
+  static const uint32_t maxDataLen = 24 + 8 + maxSettings * 8 + 12;
+  char *packet = EnsureOutputBuffer(maxDataLen);
+  memcpy(packet, kMagicHello, 24);
+  mOutputQueueUsed += 24;
+  LogIO(this, nullptr, "Magic Connection Header", packet, 24);
+
+  packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  memset(packet, 0, maxDataLen - 24);
+
+  // frame header will be filled in after we know how long the frame is
+  uint8_t numberOfEntries = 0;
+
+  // entries need to be listed in order by ID
+  // 1st entry is bytes 8 to 15
+  // 2nd entry is bytes 16 to 23
+  // 3rd entry is bytes 24 to 31
+
+  if (!gHttpHandler->AllowPush()) {
+    // If we don't support push then set MAX_CONCURRENT to 0 and also
+    // set ENABLE_PUSH to 0
+    packet[11 + 8 * numberOfEntries] = SETTINGS_TYPE_ENABLE_PUSH;
+    // The value portion of the setting pair is already initialized to 0
+    numberOfEntries++;
+
+    packet[11 + 8 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT;
+    // The value portion of the setting pair is already initialized to 0
+    numberOfEntries++;
+  }
+
+  // Advertise the Push RWIN for the session, and on each new pull stream
+  // send a window update with END_FLOW_CONTROL
+  packet[11 + 8 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW;
+  uint32_t rwin = PR_htonl(mPushAllowance);
+  memcpy(packet + 12 + 8 * numberOfEntries, &rwin, 4);
+  numberOfEntries++;
+
+  MOZ_ASSERT(numberOfEntries <= maxSettings);
+  uint32_t dataLen = 8 * numberOfEntries;
+  CreateFrameHeader(packet, dataLen, FRAME_TYPE_SETTINGS, 0, 0);
+  mOutputQueueUsed += 8 + dataLen;
+
+  LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen);
+
+  // now bump the local session window from 64KB
+  uint32_t sessionWindowBump = ASpdySession::kInitialRwin - kDefaultRwin;
+  if (kDefaultRwin >= ASpdySession::kInitialRwin)
+    goto sendHello_complete;
+
+  // send a window update for the session (Stream 0) for something large
+  sessionWindowBump = PR_htonl(sessionWindowBump);
+  mLocalSessionWindow = ASpdySession::kInitialRwin;
+
+  packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
+  mOutputQueueUsed += 12;
+  memcpy(packet + 8, &sessionWindowBump, 4);
+
+  LOG3(("Session Window increase at start of session %p %u\n",
+        this, PR_ntohl(sessionWindowBump)));
+  LogIO(this, nullptr, "Session Window Bump ", packet, 12);
+
+sendHello_complete:
+  FlushOutputQueue();
+}
+
+// perform a bunch of integrity checks on the stream.
+// returns true if passed, false (plus LOG and ABORT) if failed.
+bool
+Http2Session::VerifyStream(Http2Stream *aStream, uint32_t aOptionalID = 0)
+{
+  // This is annoying, but at least it is O(1)
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+#ifndef DEBUG
+  // Only do the real verification in debug builds
+  return true;
+#endif
+
+  if (!aStream)
+    return true;
+
+  uint32_t test = 0;
+
+  do {
+    if (aStream->StreamID() == kDeadStreamID)
+      break;
+
+    nsAHttpTransaction *trans = aStream->Transaction();
+
+    test++;
+    if (!trans)
+      break;
+
+    test++;
+    if (mStreamTransactionHash.Get(trans) != aStream)
+      break;
+
+    if (aStream->StreamID()) {
+      Http2Stream *idStream = mStreamIDHash.Get(aStream->StreamID());
+
+      test++;
+      if (idStream != aStream)
+        break;
+
+      if (aOptionalID) {
+        test++;
+        if (idStream->StreamID() != aOptionalID)
+          break;
+      }
+    }
+
+    // tests passed
+    return true;
+  } while (0);
+
+  LOG3(("Http2Session %p VerifyStream Failure %p stream->id=0x%X "
+       "optionalID=0x%X trans=%p test=%d\n",
+       this, aStream, aStream->StreamID(),
+       aOptionalID, aStream->Transaction(), test));
+
+  MOZ_ASSERT(false, "VerifyStream");
+  return false;
+}
+
+void
+Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
+                            errorType aResetCode)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::CleanupStream %p %p 0x%X %X\n",
+        this, aStream, aStream->StreamID(), aResult));
+
+  Http2PushedStream *pushSource = nullptr;
+
+  if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) {
+    LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID()));
+    return;
+  }
+
+  if (!VerifyStream(aStream)) {
+    LOG3(("Http2Session::CleanupStream failed to verify stream\n"));
+    return;
+  }
+
+  pushSource = aStream->PushSource();
+
+  if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID()) {
+    LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
+          aResetCode));
+    GenerateRstStream(aResetCode, aStream->StreamID());
+  }
+
+  CloseStream(aStream, aResult);
+
+  // Remove the stream from the ID hash table and, if an even id, the pushed
+  // table too.
+  uint32_t id = aStream->StreamID();
+  if (id > 0) {
+    mStreamIDHash.Remove(id);
+    if (!(id & 1))
+      mPushedStreams.RemoveElement(aStream);
+  }
+
+  RemoveStreamFromQueues(aStream);
+
+  // removing from the stream transaction hash will
+  // delete the Http2Stream and drop the reference to
+  // its transaction
+  mStreamTransactionHash.Remove(aStream->Transaction());
+
+  if (mShouldGoAway && !mStreamTransactionHash.Count())
+    Close(NS_OK);
+
+  if (pushSource) {
+    pushSource->SetDeferCleanupOnSuccess(false);
+    CleanupStream(pushSource, aResult, aResetCode);
+  }
+}
+
+static void RemoveStreamFromQueue(Http2Stream *aStream, nsDeque &queue)
+{
+  uint32_t size = queue.GetSize();
+  for (uint32_t count = 0; count < size; ++count) {
+    Http2Stream *stream = static_cast<Http2Stream *>(queue.PopFront());
+    if (stream != aStream)
+      queue.Push(stream);
+  }
+}
+
+void
+Http2Session::RemoveStreamFromQueues(Http2Stream *aStream)
+{
+  RemoveStreamFromQueue(aStream, mReadyForWrite);
+  RemoveStreamFromQueue(aStream, mQueuedStreams);
+  RemoveStreamFromQueue(aStream, mReadyForRead);
+}
+
+void
+Http2Session::CloseStream(Http2Stream *aStream, nsresult aResult)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::CloseStream %p %p 0x%x %X\n",
+        this, aStream, aStream->StreamID(), aResult));
+
+  MaybeDecrementConcurrent(aStream);
+
+  // Check if partial frame reader
+  if (aStream == mInputFrameDataStream) {
+    LOG3(("Stream had active partial read frame on close"));
+    ChangeDownstreamState(DISCARDING_DATA_FRAME);
+    mInputFrameDataStream = nullptr;
+  }
+
+  RemoveStreamFromQueues(aStream);
+
+  // Send the stream the close() indication
+  aStream->Close(aResult);
+}
+
+nsresult
+Http2Session::SetInputFrameDataStream(uint32_t streamID)
+{
+  mInputFrameDataStream = mStreamIDHash.Get(streamID);
+  if (VerifyStream(mInputFrameDataStream, streamID))
+    return NS_OK;
+
+  LOG3(("Http2Session::SetInputFrameDataStream failed to verify 0x%X\n",
+       streamID));
+  mInputFrameDataStream = nullptr;
+  return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+Http2Session::RecvHeaders(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_HEADERS);
+
+  // If this doesn't have END_HEADERS set on it then require the next
+  // frame to be HEADERS of the same ID
+  bool endHeadersFlag = self->mInputFrameFlags & kFlag_END_HEADERS;
+
+  if (endHeadersFlag)
+    self->mExpectedHeaderID = 0;
+  else
+    self->mExpectedHeaderID = self->mInputFrameID;
+
+  uint32_t priorityLen = (self->mInputFrameFlags & kFlag_PRIORITY) ? 4 : 0;
+  self->SetInputFrameDataStream(self->mInputFrameID);
+
+  LOG3(("Http2Session::RecvHeaders %p stream 0x%X priorityLen=%d stream=%p "
+        "end_stream=%d end_headers=%d priority_flag=%d\n",
+        self, self->mInputFrameID, priorityLen, self->mInputFrameDataStream,
+        self->mInputFrameFlags & kFlag_END_STREAM,
+        self->mInputFrameFlags & kFlag_END_HEADERS,
+        self->mInputFrameFlags & kFlag_PRIORITY));
+
+  if (!self->mInputFrameDataStream) {
+    // Cannot find stream. We can continue the session, but we need to
+    // uncompress the header block to maintain the correct compression context
+
+    LOG3(("Http2Session::RecvHeaders %p lookup mInputFrameID stream "
+          "0x%X failed. NextStreamID = 0x%X\n",
+          self, self->mInputFrameID, self->mNextStreamID));
+
+    if (self->mInputFrameID >= self->mNextStreamID)
+      self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
+
+    self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + priorityLen,
+                                   self->mInputFrameDataSize - priorityLen);
+
+    if (self->mInputFrameFlags & kFlag_END_HEADERS) {
+      nsresult rv = self->UncompressAndDiscard();
+      if (NS_FAILED(rv)) {
+        LOG3(("Http2Session::RecvHeaders uncompress failed\n"));
+        // this is fatal to the session
+        self->mGoAwayReason = COMPRESSION_ERROR;
+        return rv;
+      }
+    }
+
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  // queue up any compression bytes
+  self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + priorityLen,
+                                 self->mInputFrameDataSize - priorityLen);
+
+  self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
+  self->mLastDataReadEpoch = self->mLastReadEpoch;
+
+  if (!endHeadersFlag) { // more are coming - don't process yet
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  nsresult rv = self->ResponseHeadersComplete();
+  if (rv == NS_ERROR_ILLEGAL_VALUE) {
+    LOG3(("Http2Session::RecvHeaders %p PROTOCOL_ERROR detected stream 0x%X\n",
+          self, self->mInputFrameID));
+    self->CleanupStream(self->mInputFrameDataStream, rv, PROTOCOL_ERROR);
+    self->ResetDownstreamState();
+    rv = NS_OK;
+  }
+  return rv;
+}
+
+// ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream
+// should be reset with a PROTOCOL_ERROR, NS_OK when the response headers were
+// fine, and any other error is fatal to the session.
+nsresult
+Http2Session::ResponseHeadersComplete()
+{
+  LOG3(("Http2Session::ResponseHeadersComplete %p for 0x%X fin=%d",
+        this, mInputFrameDataStream->StreamID(), mInputFrameFinal));
+
+  // only do this once, afterwards ignore trailers
+  if (mInputFrameDataStream->AllHeadersReceived())
+    return NS_OK;
+  mInputFrameDataStream->SetAllHeadersReceived(true);
+
+  // The stream needs to see flattened http headers
+  // Uncompressed http/2 format headers currently live in
+  // Http2Stream::mDecompressBuffer - convert that to HTTP format in
+  // mFlatHTTPResponseHeaders via ConvertHeaders()
+
+  mFlatHTTPResponseHeadersOut = 0;
+  nsresult rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
+                                                              mDecompressBuffer,
+                                                              mFlatHTTPResponseHeaders);
+  if (NS_FAILED(rv))
+    return rv;
+
+  ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvPriority(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PRIORITY);
+
+  if (self->mInputFrameDataSize != 4) {
+    LOG3(("Http2Session::RecvPriority %p wrong length data=%d\n",
+          self, self->mInputFrameDataSize));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  if (!self->mInputFrameID) {
+    LOG3(("Http2Session::RecvPriority %p stream ID of 0.\n", self));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  uint32_t newPriority =
+    PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
+  newPriority &= 0x7fffffff;
+
+  nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (self->mInputFrameDataStream)
+    self->mInputFrameDataStream->SetPriority(newPriority);
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvRstStream(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_RST_STREAM);
+
+  if (self->mInputFrameDataSize != 4) {
+    LOG3(("Http2Session::RecvRstStream %p RST_STREAM wrong length data=%d",
+          self, self->mInputFrameDataSize));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  if (!self->mInputFrameID) {
+    LOG3(("Http2Session::RecvRstStream %p stream ID of 0.\n", self));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  self->mDownstreamRstReason =
+    PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
+
+  LOG3(("Http2Session::RecvRstStream %p RST_STREAM Reason Code %u ID %x\n",
+        self, self->mDownstreamRstReason, self->mInputFrameID));
+
+  self->SetInputFrameDataStream(self->mInputFrameID);
+  if (!self->mInputFrameDataStream) {
+    // if we can't find the stream just ignore it (4.2 closed)
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  self->mInputFrameDataStream->SetRecvdReset(true);
+  self->MaybeDecrementConcurrent(self->mInputFrameDataStream);
+  self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
+  return NS_OK;
+}
+
+PLDHashOperator
+Http2Session::UpdateServerRwinEnumerator(nsAHttpTransaction *key,
+                                         nsAutoPtr<Http2Stream> &stream,
+                                         void *closure)
+{
+  int32_t delta = *(static_cast<int32_t *>(closure));
+  stream->UpdateServerReceiveWindow(delta);
+  return PL_DHASH_NEXT;
+}
+
+nsresult
+Http2Session::RecvSettings(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_SETTINGS);
+
+  if (self->mInputFrameID) {
+    LOG3(("Http2Session::RecvSettings %p needs stream ID of 0. 0x%X\n",
+          self, self->mInputFrameID));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  if (self->mInputFrameDataSize & 7) {
+    // Number of Setting is determined by dividing by each 8 byte setting
+    // entry. So the payload must be a multiple of 8.
+    LOG3(("Http2Session::RecvSettings %p SETTINGS wrong length data=%d",
+          self, self->mInputFrameDataSize));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  uint32_t numEntries = self->mInputFrameDataSize >> 3;
+  LOG3(("Http2Session::RecvSettings %p SETTINGS Control Frame "
+        "with %d entries ack=%X", self, numEntries,
+        self->mInputFrameFlags & kFlag_ACK));
+
+  if ((self->mInputFrameFlags & kFlag_ACK) && self->mInputFrameDataSize) {
+    LOG3(("Http2Session::RecvSettings %p ACK with non zero payload is err\n"));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  for (uint32_t index = 0; index < numEntries; ++index) {
+    uint8_t *setting = reinterpret_cast<uint8_t *>
+      (self->mInputFrameBuffer.get()) + 8 + index * 8;
+
+    uint32_t id = PR_ntohl(reinterpret_cast<uint32_t *>(setting)[0]) & 0xffffff;
+    uint32_t value = PR_ntohl(reinterpret_cast<uint32_t *>(setting)[1]);
+    LOG3(("Settings ID %d, Value %d", id, value));
+
+    switch (id)
+    {
+    case SETTINGS_TYPE_HEADER_TABLE_SIZE:
+      LOG3(("Compression header table setting received: %d\n", value));
+      self->mCompressor.SetMaxBufferSize(value);
+      break;
+
+    case SETTINGS_TYPE_ENABLE_PUSH:
+      LOG3(("Client received an ENABLE Push SETTING. Odd.\n"));
+      // nop
+      break;
+
+    case SETTINGS_TYPE_MAX_CONCURRENT:
+      self->mMaxConcurrent = value;
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
+      break;
+
+    case SETTINGS_TYPE_INITIAL_WINDOW:
+      {
+        Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
+        int32_t delta = value - self->mServerInitialStreamWindow;
+        self->mServerInitialStreamWindow = value;
+
+        // SETTINGS only adjusts stream windows. Leave the sesison window alone.
+        // we need to add the delta to all open streams (delta can be negative)
+        self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator,
+                                               &delta);
+      }
+      break;
+
+    case SETTINGS_TYPE_FLOW_CONTROL:
+      if (value & 1) {
+        LOG3(("Http2Session::RecvSettings %p DISABLE FLOW CONTROL\n", self));
+        self->mServerUsesFlowControl = false;
+
+        // we need to touch all existing streams to unpause any that were
+        // flow control restricted
+        int32_t delta = 0;
+        self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator,
+                                               &delta);
+      } else {
+        if (!self->mServerUsesFlowControl) {
+          // clearing this setting is a protocol error
+          LOG3(("Http2Session::RecvSettings %p "
+                "Cannot Clear FLOW_CONTROL SETTINGS bits\n",
+                self));
+          RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR);
+        }
+      }
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  self->ResetDownstreamState();
+
+  if (!(self->mInputFrameFlags & kFlag_ACK))
+    self->GenerateSettingsAck();
+
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvPushPromise(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PUSH_PROMISE);
+
+  // If this doesn't have END_PUSH_PROMISE set on it then require the next
+  // frame to be PUSH_PROMISE of the same ID
+  uint32_t promiseLen;
+  uint32_t promisedID;
+
+  if (self->mExpectedPushPromiseID) {
+    promiseLen = 0; // really a continuation frame
+    promisedID = self->mContinuedPromiseStream;
+  } else {
+    promiseLen = 4;
+    promisedID =
+      PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
+    promisedID &= 0x7fffffff;
+  }
+
+  uint32_t associatedID = self->mInputFrameID;
+
+  if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
+    self->mExpectedPushPromiseID = 0;
+    self->mContinuedPromiseStream = 0;
+  } else {
+    self->mExpectedPushPromiseID = self->mInputFrameID;
+    self->mContinuedPromiseStream = promisedID;
+  }
+
+  LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X.\n",
+        self, promisedID, associatedID));
+
+  if (!associatedID || !promisedID || (promisedID & 1)) {
+    LOG3(("Http2Session::RecvPushPromise %p ID invalid.\n", self));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  // confirm associated-to
+  nsresult rv = self->SetInputFrameDataStream(associatedID);
+  if (NS_FAILED(rv))
+    return rv;
+
+  Http2Stream *associatedStream = self->mInputFrameDataStream;
+  ++(self->mServerPushedResources);
+
+  // Anytime we start using the high bit of stream ID (either client or server)
+  // begin to migrate to a new session.
+  if (promisedID >= kMaxStreamID)
+    self->mShouldGoAway = true;
+
+  bool resetStream = true;
+  SpdyPushCache *cache = nullptr;
+
+  if (self->mShouldGoAway) {
+    LOG3(("Http2Session::RecvPushPromise %p push while in GoAway "
+          "mode refused.\n", self));
+    self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
+  } else if (!gHttpHandler->AllowPush()) {
+    // MAX_CONCURRENT_STREAMS of 0 in settings disabled push
+    LOG3(("Http2Session::RecvPushPromise Push Recevied when Disabled\n"));
+    self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
+  } else if (!(self->mInputFrameFlags & kFlag_END_PUSH_PROMISE)) {
+    LOG3(("Http2Session::RecvPushPromise no support for multi frame push\n"));
+    self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
+  } else if (!associatedStream) {
+    LOG3(("Http2Session::RecvPushPromise %p lookup associated ID failed.\n", self));
+    self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
+  } else {
+    nsILoadGroupConnectionInfo *loadGroupCI = associatedStream->LoadGroupConnectionInfo();
+    if (loadGroupCI) {
+      loadGroupCI->GetSpdyPushCache(&cache);
+      if (!cache) {
+        cache = new SpdyPushCache();
+        if (!cache || NS_FAILED(loadGroupCI->SetSpdyPushCache(cache))) {
+          delete cache;
+          cache = nullptr;
+        }
+      }
+    }
+    if (!cache) {
+      // this is unexpected, but we can handle it just by refusing the push
+      LOG3(("Http2Session::RecvPushPromise Push Recevied without loadgroup cache\n"));
+      self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
+    } else {
+      resetStream = false;
+    }
+  }
+
+  if (resetStream) {
+    // Need to decompress the headers even though we aren't using them yet in
+    // order to keep the compression context consistent for other frames
+    self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + promiseLen,
+                                   self->mInputFrameDataSize - promiseLen);
+    if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
+      rv = self->UncompressAndDiscard();
+      if (NS_FAILED(rv)) {
+        LOG3(("Http2Session::RecvPushPromise uncompress failed\n"));
+        self->mGoAwayReason = COMPRESSION_ERROR;
+        return rv;
+      }
+    }
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  // Create the buffering transaction and push stream
+  nsRefPtr<Http2PushTransactionBuffer> transactionBuffer =
+    new Http2PushTransactionBuffer();
+  transactionBuffer->SetConnection(self);
+  Http2PushedStream *pushedStream =
+    new Http2PushedStream(transactionBuffer, self,
+                                 associatedStream, promisedID);
+
+  // Ownership of the pushed stream is by the transaction hash, just as it
+  // is for a client initiated stream. Errors that aren't fatal to the
+  // whole session must call cleanupStream() after this point in order
+  // to remove the stream from that hash.
+  self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
+  self->mPushedStreams.AppendElement(pushedStream);
+
+  self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + promiseLen,
+                                 self->mInputFrameDataSize - promiseLen);
+
+  nsAutoCString requestHeaders;
+  rv = pushedStream->ConvertPushHeaders(&self->mDecompressor,
+                                        self->mDecompressBuffer, requestHeaders);
+
+  if (rv == NS_ERROR_NOT_IMPLEMENTED) {
+    LOG3(("Http2Session::PushPromise Semantics not Implemented\n"));
+    self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
+    return NS_OK;
+  }
+
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) {
+    LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n"));
+    self->mGoAwayReason = INTERNAL_ERROR;
+    return NS_ERROR_FAILURE;
+  }
+
+  if (promisedID > self->mOutgoingGoAwayID)
+    self->mOutgoingGoAwayID = promisedID;
+
+  // Fake the request side of the pushed HTTP transaction. Sets up hash
+  // key and origin
+  uint32_t notUsed;
+  pushedStream->ReadSegments(nullptr, 1, &notUsed);
+
+  nsAutoCString key;
+  if (!pushedStream->GetHashKey(key)) {
+    LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n"));
+    self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR);
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  if (!associatedStream->Origin().Equals(pushedStream->Origin())) {
+    LOG3(("Http2Session::RecvPushPromise pushed stream mismatched origin\n"));
+    self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) {
+    LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n"));
+    self->CleanupStream(pushedStream, NS_ERROR_FAILURE, INTERNAL_ERROR);
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  static_assert(Http2Stream::kWorstPriority >= 0,
+                "kWorstPriority out of range");
+  uint32_t unsignedPriority = static_cast<uint32_t>(Http2Stream::kWorstPriority);
+  pushedStream->SetPriority(unsignedPriority);
+  self->GeneratePriority(promisedID, unsignedPriority);
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvPing(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PING);
+
+  LOG3(("Http2Session::RecvPing %p PING Flags 0x%X.", self,
+        self->mInputFrameFlags));
+
+  if (self->mInputFrameDataSize != 8) {
+    LOG3(("Http2Session::RecvPing %p PING had wrong amount of data %d",
+          self, self->mInputFrameDataSize));
+    RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
+  }
+
+  if (self->mInputFrameID) {
+    LOG3(("Http2Session::RecvPing %p PING needs stream ID of 0. 0x%X\n",
+          self, self->mInputFrameID));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  if (self->mInputFrameFlags & kFlag_ACK) {
+    // presumably a reply to our timeout ping.. don't reply to it
+    self->mPingSentEpoch = 0;
+  } else {
+    // reply with a ack'd ping
+    self->GeneratePing(true);
+  }
+
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvGoAway(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_GOAWAY);
+
+  if (self->mInputFrameDataSize < 8) {
+    // data > 8 is an opaque token that we can't interpret. NSPR Logs will
+    // have the hex of all packets so there is no point in separately logging.
+    LOG3(("Http2Session::RecvGoAway %p GOAWAY had wrong amount of data %d",
+          self, self->mInputFrameDataSize));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  if (self->mInputFrameID) {
+    LOG3(("Http2Session::RecvGoAway %p GOAWAY had non zero stream ID 0x%X\n",
+          self, self->mInputFrameID));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  self->mShouldGoAway = true;
+  self->mGoAwayID =
+    PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
+  self->mGoAwayID &= 0x7fffffff;
+  self->mCleanShutdown = true;
+  uint32_t statusCode =
+    PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]);
+
+  // Find streams greater than the last-good ID and mark them for deletion
+  // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The
+  // underlying transaction can be restarted.
+  self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self);
+
+  // Process the streams marked for deletion and restart.
+  uint32_t size = self->mGoAwayStreamsToRestart.GetSize();
+  for (uint32_t count = 0; count < size; ++count) {
+    Http2Stream *stream =
+      static_cast<Http2Stream *>(self->mGoAwayStreamsToRestart.PopFront());
+
+    self->CloseStream(stream, NS_ERROR_NET_RESET);
+    if (stream->HasRegisteredID())
+      self->mStreamIDHash.Remove(stream->StreamID());
+    self->mStreamTransactionHash.Remove(stream->Transaction());
+  }
+
+  // Queued streams can also be deleted from this session and restarted
+  // in another one. (they were never sent on the network so they implicitly
+  // are not covered by the last-good id.
+  size = self->mQueuedStreams.GetSize();
+  for (uint32_t count = 0; count < size; ++count) {
+    Http2Stream *stream =
+      static_cast<Http2Stream *>(self->mQueuedStreams.PopFront());
+    self->CloseStream(stream, NS_ERROR_NET_RESET);
+    self->mStreamTransactionHash.Remove(stream->Transaction());
+  }
+
+  LOG3(("Http2Session::RecvGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X "
+        "live streams=%d\n", self, self->mGoAwayID, statusCode,
+        self->mStreamTransactionHash.Count()));
+
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvUnused1(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_UNUSED1);
+  LOG3(("Http2Session::RecvUnused1 %p NOP.", self));
+  // Section 4.1 says to ignore this
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
+PLDHashOperator
+Http2Session::RestartBlockedOnRwinEnumerator(nsAHttpTransaction *key,
+                                             nsAutoPtr<Http2Stream> &stream,
+                                             void *closure)
+{
+  Http2Session *self = static_cast<Http2Session *>(closure);
+  MOZ_ASSERT(self->mServerSessionWindow > 0);
+
+  if (!stream->BlockedOnRwin() || stream->ServerReceiveWindow() <= 0)
+    return PL_DHASH_NEXT;
+
+  self->mReadyForWrite.Push(stream);
+  self->SetWriteCallbacks();
+  return PL_DHASH_NEXT;
+}
+
+nsresult
+Http2Session::RecvWindowUpdate(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_WINDOW_UPDATE);
+
+  if (self->mInputFrameDataSize != 4) {
+    LOG3(("Http2Session::RecvWindowUpdate %p Window Update wrong length %d\n",
+          self, self->mInputFrameDataSize));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  uint32_t delta =
+    PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
+  delta &= 0x7fffffff;
+
+  LOG3(("Http2Session::RecvWindowUpdate %p len=%d Stream 0x%X.\n",
+        self, delta, self->mInputFrameID));
+
+  // if flow control is disabled then reset the stream
+  if (!self->mServerUsesFlowControl) {
+    LOG3(("Http2Session::RecvWindowUpdate %p stream window update "
+          "when peer had disabled flow control\n", self));
+    RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR);
+    return NS_OK;
+  }
+
+  if (self->mInputFrameID) { // stream window
+    nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
+    if (NS_FAILED(rv))
+      return rv;
+
+    if (!self->mInputFrameDataStream) {
+      LOG3(("Http2Session::RecvWindowUpdate %p lookup streamID 0x%X failed.\n",
+            self, self->mInputFrameID));
+      // only resest the session if the ID is one we haven't ever opened
+      if (self->mInputFrameID >= self->mNextStreamID)
+        self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
+      self->ResetDownstreamState();
+      return NS_OK;
+    }
+
+    int64_t oldRemoteWindow = self->mInputFrameDataStream->ServerReceiveWindow();
+    self->mInputFrameDataStream->UpdateServerReceiveWindow(delta);
+    if (self->mInputFrameDataStream->ServerReceiveWindow() >= 0x80000000) {
+      // a window cannot reach 2^31 and be in compliance. Our calculations
+      // are 64 bit safe though.
+      LOG3(("Http2Session::RecvWindowUpdate %p stream window "
+            "exceeds 2^31 - 1\n", self));
+      self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
+                          FLOW_CONTROL_ERROR);
+      self->ResetDownstreamState();
+      return NS_OK;
+    }
+
+    LOG3(("Http2Session::RecvWindowUpdate %p stream 0x%X window "
+          "%d increased by %d now %d.\n", self, self->mInputFrameID,
+          oldRemoteWindow, delta, oldRemoteWindow + delta));
+
+  } else { // session window update
+    int64_t oldRemoteWindow = self->mServerSessionWindow;
+    self->mServerSessionWindow += delta;
+
+    if (self->mServerSessionWindow >= 0x80000000) {
+      // a window cannot reach 2^31 and be in compliance. Our calculations
+      // are 64 bit safe though.
+      LOG3(("Http2Session::RecvWindowUpdate %p session window "
+            "exceeds 2^31 - 1\n", self));
+      RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR);
+    }
+
+    if ((oldRemoteWindow <= 0) && (self->mServerSessionWindow > 0)) {
+      LOG3(("Http2Session::RecvWindowUpdate %p restart session window\n",
+            self));
+      self->mStreamTransactionHash.Enumerate(RestartBlockedOnRwinEnumerator, self);
+    }
+    LOG3(("Http2Session::RecvWindowUpdate %p session window "
+          "%d increased by %d now %d.\n", self,
+          oldRemoteWindow, delta, oldRemoteWindow + delta));
+  }
+
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
+nsresult
+Http2Session::RecvContinuation(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_CONTINUATION);
+  MOZ_ASSERT(self->mInputFrameID);
+  MOZ_ASSERT(self->mExpectedPushPromiseID || self->mExpectedHeaderID);
+  MOZ_ASSERT(!(self->mExpectedPushPromiseID && self->mExpectedHeaderID));
+
+  LOG3(("Http2Session::RecvContinuation %p Flags 0x%X id 0x%X "
+        "promise id 0x%X header id 0x%X\n",
+        self, self->mInputFrameFlags, self->mInputFrameID,
+        self->mExpectedPushPromiseID, self->mExpectedHeaderID));
+
+  self->SetInputFrameDataStream(self->mInputFrameID);
+
+  if (!self->mInputFrameDataStream) {
+    LOG3(("Http2Session::RecvContination stream ID 0x%X not found.",
+          self->mInputFrameID));
+    RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
+  }
+
+  // continued headers
+  if (self->mExpectedHeaderID) {
+    self->mInputFrameFlags &= ~kFlag_PRIORITY;
+    return RecvHeaders(self);
+  }
+
+  // continued push promise
+  if (self->mInputFrameFlags & kFlag_END_HEADERS) {
+    self->mInputFrameFlags &= ~kFlag_END_HEADERS;
+    self->mInputFrameFlags |= kFlag_END_PUSH_PROMISE;
+  }
+  return RecvPushPromise(self);
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpTransaction. It is expected that nsHttpConnection is the caller
+// of these methods
+//-----------------------------------------------------------------------------
+
+void
+Http2Session::OnTransportStatus(nsITransport* aTransport,
+                                nsresult aStatus, uint64_t aProgress)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  switch (aStatus) {
+    // These should appear only once, deliver to the first
+    // transaction on the session.
+  case NS_NET_STATUS_RESOLVING_HOST:
+  case NS_NET_STATUS_RESOLVED_HOST:
+  case NS_NET_STATUS_CONNECTING_TO:
+  case NS_NET_STATUS_CONNECTED_TO:
+  {
+    Http2Stream *target = mStreamIDHash.Get(1);
+    nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
+    if (transaction)
+      transaction->OnTransportStatus(aTransport, aStatus, aProgress);
+    break;
+  }
+
+  default:
+    // The other transport events are ignored here because there is no good
+    // way to map them to the right transaction in http/2. Instead, the events
+    // are generated again from the http/2 code and passed directly to the
+    // correct transaction.
+
+    // NS_NET_STATUS_SENDING_TO:
+    // This is generated by the socket transport when (part) of
+    // a transaction is written out
+    //
+    // There is no good way to map it to the right transaction in http/2,
+    // so it is ignored here and generated separately when the request
+    // is sent from Http2Stream::TransmitFrame
+
+    // NS_NET_STATUS_WAITING_FOR:
+    // Created by nsHttpConnection when the request has been totally sent.
+    // There is no good way to map it to the right transaction in http/2,
+    // so it is ignored here and generated separately when the same
+    // condition is complete in Http2Stream when there is no more
+    // request body left to be transmitted.
+
+    // NS_NET_STATUS_RECEIVING_FROM
+    // Generated in session whenever we read a data frame or a HEADERS
+    // that can be attributed to a particular stream/transaction
+
+    break;
+  }
+}
+
+// ReadSegments() is used to write data to the network. Generally, HTTP
+// request data is pulled from the approriate transaction and
+// converted to http/2 data. Sometimes control data like window-update are
+// generated instead.
+
+nsresult
+Http2Session::ReadSegments(nsAHttpSegmentReader *reader,
+                           uint32_t count, uint32_t *countRead)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader),
+             "Inconsistent Write Function Callback");
+
+  nsresult rv = ConfirmTLSProfile();
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (reader)
+    mSegmentReader = reader;
+
+  *countRead = 0;
+
+  LOG3(("Http2Session::ReadSegments %p", this));
+
+  Http2Stream *stream = static_cast<Http2Stream *>(mReadyForWrite.PopFront());
+  if (!stream) {
+    LOG3(("Http2Session %p could not identify a stream to write; suspending.",
+          this));
+    FlushOutputQueue();
+    SetWriteCallbacks();
+    return NS_BASE_STREAM_WOULD_BLOCK;
+  }
+
+  LOG3(("Http2Session %p will write from Http2Stream %p 0x%X "
+        "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
+        stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
+
+  rv = stream->ReadSegments(this, count, countRead);
+
+  // 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
+  // to flush.
+  FlushOutputQueue();
+
+  // Allow new server reads - that might be data or control information
+  // (e.g. window updates or http replies) that are responses to these writes
+  ResumeRecv();
+
+  if (stream->RequestBlockedOnRead()) {
+
+    // We are blocked waiting for input - either more http headers or
+    // any request body data. When more data from the request stream
+    // becomes available the httptransaction will call conn->ResumeSend().
+
+    LOG3(("Http2Session::ReadSegments %p dealing with block on read", this));
+
+    // call readsegments again if there are other streams ready
+    // to run in this session
+    if (GetWriteQueueSize()) {
+      rv = NS_OK;
+    } else {
+      rv = NS_BASE_STREAM_WOULD_BLOCK;
+    }
+    SetWriteCallbacks();
+    return rv;
+  }
+
+  if (NS_FAILED(rv)) {
+    LOG3(("Http2Session::ReadSegments %p returning FAIL code %X",
+          this, rv));
+    if (rv != NS_BASE_STREAM_WOULD_BLOCK)
+      CleanupStream(stream, rv, CANCEL_ERROR);
+    return rv;
+  }
+
+  if (*countRead > 0) {
+    LOG3(("Http2Session::ReadSegments %p stream=%p countread=%d",
+          this, stream, *countRead));
+    mReadyForWrite.Push(stream);
+    SetWriteCallbacks();
+    return rv;
+  }
+
+  if (stream->BlockedOnRwin()) {
+    LOG3(("Http2Session %p will stream %p 0x%X suspended for flow control\n",
+          this, stream, stream->StreamID()));
+    return NS_BASE_STREAM_WOULD_BLOCK;
+  }
+
+  LOG3(("Http2Session::ReadSegments %p stream=%p stream send complete",
+        this, stream));
+
+  // call readsegments again if there are other streams ready
+  // to go in this session
+  SetWriteCallbacks();
+
+  return rv;
+}
+
+// WriteSegments() is used to read data off the socket. Generally this is
+// just the http2 frame header and from there the appropriate *Stream
+// is identified from the Stream-ID. The http transaction associated with
+// that read then pulls in the data directly, which it will feed to
+// OnWriteSegment(). That function will gateway it into http and feed
+// it to the appropriate transaction.
+
+// we call writer->OnWriteSegment via NetworkRead() to get a http2 header..
+// and decide if it is data or control.. if it is control, just deal with it.
+// if it is data, identify the stream
+// call stream->WriteSegments which can call this::OnWriteSegment to get the
+// data. It always gets full frames if they are part of the stream
+
+nsresult
+Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
+                            uint32_t count, uint32_t *countWritten)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  LOG3(("Http2Session::WriteSegments %p InternalState %X\n",
+        this, mDownstreamState));
+
+  *countWritten = 0;
+
+  if (mClosed)
+    return NS_ERROR_FAILURE;
+
+  nsresult rv = ConfirmTLSProfile();
+  if (NS_FAILED(rv))
+    return rv;
+
+  SetWriteCallbacks();
+
+  // If there are http transactions attached to a push stream with filled buffers
+  // trigger that data pump here. This only reads from buffers (not the network)
+  // so mDownstreamState doesn't matter.
+  Http2Stream *pushConnectedStream =
+    static_cast<Http2Stream *>(mReadyForRead.PopFront());
+  if (pushConnectedStream) {
+    LOG3(("Http2Session::WriteSegments %p processing pushed stream 0x%X\n",
+          this, pushConnectedStream->StreamID()));
+    mSegmentWriter = writer;
+    rv = pushConnectedStream->WriteSegments(this, count, countWritten);
+    mSegmentWriter = nullptr;
+
+    // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
+    // so we need this check to determine the truth.
+    if (NS_SUCCEEDED(rv) && !*countWritten &&
+        pushConnectedStream->PushSource() &&
+        pushConnectedStream->PushSource()->GetPushComplete()) {
+      rv = NS_BASE_STREAM_CLOSED;
+    }
+
+    if (rv == NS_BASE_STREAM_CLOSED) {
+      CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR);
+      rv = NS_OK;
+    }
+
+    // if we return OK to nsHttpConnection it will use mSocketInCondition
+    // to determine whether to schedule more reads, incorrectly
+    // assuming that nsHttpConnection::OnSocketWrite() was called.
+    if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
+      rv = NS_BASE_STREAM_WOULD_BLOCK;
+      ResumeRecv();
+    }
+
+    return rv;
+  }
+
+  // The BUFFERING_OPENING_SETTINGS state is just like any BUFFERING_FRAME_HEADER
+  // except the only frame type it will allow is SETTINGS
+
+  // The session layer buffers the leading 8 byte header of every frame.
+  // Non-Data frames are then buffered for their full length, but data
+  // frames (type 0) are passed through to the http stack unprocessed
+
+  if (mDownstreamState == BUFFERING_OPENING_SETTINGS ||
+      mDownstreamState == BUFFERING_FRAME_HEADER) {
+    // The first 8 bytes of every frame is header information that
+    // we are going to want to strip before passing to http. That is
+    // true of both control and data packets.
+
+    MOZ_ASSERT(mInputFrameBufferUsed < 8,
+               "Frame Buffer Used Too Large for State");
+
+    rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
+                     8 - mInputFrameBufferUsed, countWritten);
+
+    if (NS_FAILED(rv)) {
+      LOG3(("Http2Session %p buffering frame header read failure %x\n",
+            this, rv));
+      // maybe just blocked reading from network
+      if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+        rv = NS_OK;
+      return rv;
+    }
+
+    LogIO(this, nullptr, "Reading Frame Header",
+          mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
+
+    mInputFrameBufferUsed += *countWritten;
+
+    if (mInputFrameBufferUsed < 8)
+    {
+      LOG3(("Http2Session::WriteSegments %p "
+            "BUFFERING FRAME HEADER incomplete size=%d",
+            this, mInputFrameBufferUsed));
+      return rv;
+    }
+
+    // 2 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID
+    mInputFrameDataSize =
+      PR_ntohs(reinterpret_cast<uint16_t *>(mInputFrameBuffer.get())[0]);
+    mInputFrameType = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[2];
+    mInputFrameFlags = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[3];
+    mInputFrameID =
+      PR_ntohl(reinterpret_cast<uint32_t *>(mInputFrameBuffer.get())[1]);
+    mInputFrameID &= 0x7fffffff;
+    mInputFrameDataRead = 0;
+
+    if (mInputFrameType == FRAME_TYPE_DATA || mInputFrameType == FRAME_TYPE_HEADERS)  {
+      mInputFrameFinal = mInputFrameFlags & kFlag_END_STREAM;
+    } else {
+      mInputFrameFinal = 0;
+    }
+
+    if (mInputFrameDataSize >= 0x4000) {
+      // Section 9.1 HTTP frames cannot exceed 2^14 - 1 but receviers must ignore
+      // those bits
+      LOG3(("Http2Session::WriteSegments %p WARNING Frame Length bits past 14 are not 0 %08X\n",
+            this, mInputFrameDataSize));
+      mInputFrameDataSize &= 0x3fff;
+    }
+
+    LOG3(("Http2Session::WriteSegments[%p::%x] Frame Header Read "
+          "type %X data len %u flags %x id 0x%X",
+          this, mSerial, mInputFrameType, mInputFrameDataSize, mInputFrameFlags,
+          mInputFrameID));
+
+    // if mExpectedHeaderID is non 0, it means this frame must be a CONTINUATION of
+    // a HEADERS frame with a matching ID (section 6.2)
+    if (mExpectedHeaderID &&
+        ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
+         (mExpectedHeaderID != mInputFrameID))) {
+      LOG3(("Expected CONINUATION OF HEADERS for ID 0x%X\n", mExpectedHeaderID));
+      RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
+    }
+
+    // if mExpectedPushPromiseID is non 0, it means this frame must be a
+    // CONTINUATION of a PUSH_PROMISE with a matching ID (section 6.2)
+    if (mExpectedPushPromiseID &&
+        ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
+         (mExpectedPushPromiseID != mInputFrameID))) {
+      LOG3(("Expected CONTINUATION of PUSH PROMISE for ID 0x%X\n",
+            mExpectedPushPromiseID));
+      RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
+    }
+
+    if (mDownstreamState == BUFFERING_OPENING_SETTINGS &&
+        mInputFrameType != FRAME_TYPE_SETTINGS) {
+      LOG3(("First Frame Type Must Be Settings\n"));
+      RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
+    }
+
+    if (mInputFrameType != FRAME_TYPE_DATA) { // control frame
+      EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
+                   mInputFrameBufferSize);
+      ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
+    } else {
+      ChangeDownstreamState(PROCESSING_DATA_FRAME);
+
+      Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
+                            mInputFrameDataSize >> 10);
+      mLastDataReadEpoch = mLastReadEpoch;
+
+      if (!mInputFrameID) {
+        LOG3(("Http2Session::WriteSegments %p data frame stream 0\n", this));
+        RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
+      }
+
+      rv = SetInputFrameDataStream(mInputFrameID);
+      if (NS_FAILED(rv)) {
+        LOG3(("Http2Session::WriteSegments %p lookup streamID 0x%X failed. "
+              "probably due to verification.\n", this, mInputFrameID));
+        return rv;
+      }
+      if (!mInputFrameDataStream) {
+        LOG3(("Http2Session::WriteSegments %p lookup streamID 0x%X failed. "
+              "Next = 0x%X", this, mInputFrameID, mNextStreamID));
+        if (mInputFrameID >= mNextStreamID)
+          GenerateRstStream(PROTOCOL_ERROR, mInputFrameID);
+        ChangeDownstreamState(DISCARDING_DATA_FRAME);
+      } else if (mInputFrameDataStream->RecvdFin() ||
+               mInputFrameDataStream->RecvdReset() ||
+               mInputFrameDataStream->SentReset()) {
+        LOG3(("Http2Session::WriteSegments %p streamID 0x%X "
+              "Data arrived for already server closed stream.\n",
+              this, mInputFrameID));
+        if (mInputFrameDataStream->RecvdFin() || mInputFrameDataStream->RecvdReset())
+          GenerateRstStream(STREAM_CLOSED_ERROR, mInputFrameID);
+        ChangeDownstreamState(DISCARDING_DATA_FRAME);
+      }
+
+      LOG3(("Start Processing Data Frame. "
+            "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d",
+            this, mInputFrameID, mInputFrameDataStream, mInputFrameFinal,
+            mInputFrameDataSize));
+      UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize);
+    }
+  }
+
+  if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
+    nsresult streamCleanupCode;
+
+    // There is no bounds checking on the error code.. we provide special
+    // handling for a couple of cases and all others (including unknown) are
+    // equivalent to cancel.
+    if (mDownstreamRstReason == REFUSED_STREAM_ERROR) {
+      streamCleanupCode = NS_ERROR_NET_RESET;      // can retry this 100% safely
+    } else {
+      streamCleanupCode = NS_ERROR_NET_INTERRUPT;
+    }
+
+    if (mDownstreamRstReason == COMPRESSION_ERROR)
+      mShouldGoAway = true;
+
+    // mInputFrameDataStream is reset by ChangeDownstreamState
+    Http2Stream *stream = mInputFrameDataStream;
+    ResetDownstreamState();
+    LOG3(("Http2Session::WriteSegments cleanup stream on recv of rst "
+          "session=%p stream=%p 0x%X\n", this, stream,
+          stream ? stream->StreamID() : 0));
+    CleanupStream(stream, streamCleanupCode, CANCEL_ERROR);
+    return NS_OK;
+  }
+
+  if (mDownstreamState == PROCESSING_DATA_FRAME ||
+      mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
+
+    // The cleanup stream should only be set while stream->WriteSegments is
+    // on the stack and then cleaned up in this code block afterwards.
+    MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
+    mNeedsCleanup = nullptr;                     /* just in case */
+
+    mSegmentWriter = writer;
+    rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
+    mSegmentWriter = nullptr;
+
+    mLastDataReadEpoch = mLastReadEpoch;
+
+    if (SoftStreamError(rv)) {
+      // This will happen when the transaction figures out it is EOF, generally
+      // due to a content-length match being made. Return OK from this function
+      // otherwise the whole session would be torn down.
+      Http2Stream *stream = mInputFrameDataStream;
+
+      // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
+      // back to PROCESSING_DATA_FRAME where we came from
+      mDownstreamState = PROCESSING_DATA_FRAME;
+
+      if (mInputFrameDataRead == mInputFrameDataSize)
+        ResetDownstreamState();
+      LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
+            "needscleanup=%p. cleanup stream based on "
+            "stream->writeSegments returning code %x\n",
+            this, stream, stream ? stream->StreamID() : 0,
+            mNeedsCleanup, rv));
+      CleanupStream(stream, NS_OK, CANCEL_ERROR);
+      MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
+      mNeedsCleanup = nullptr;                     /* just in case */
+      return NS_OK;
+    }
+
+    if (mNeedsCleanup) {
+      LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
+            "cleanup stream based on mNeedsCleanup.\n",
+            this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
+      CleanupStream(mNeedsCleanup, NS_OK, CANCEL_ERROR);
+      mNeedsCleanup = nullptr;
+    }
+
+    if (NS_FAILED(rv)) {
+      LOG3(("Http2Session %p data frame read failure %x\n", this, rv));
+      // maybe just blocked reading from network
+      if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+        rv = NS_OK;
+    }
+
+    return rv;
+  }
+
+  if (mDownstreamState == DISCARDING_DATA_FRAME) {
+    char trash[4096];
+    uint32_t count = std::min(4096U, mInputFrameDataSize - mInputFrameDataRead);
+
+    if (!count) {
+      ResetDownstreamState();
+      ResumeRecv();
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    rv = NetworkRead(writer, trash, count, countWritten);
+
+    if (NS_FAILED(rv)) {
+      LOG3(("Http2Session %p discard frame read failure %x\n", this, rv));
+      // maybe just blocked reading from network
+      if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+        rv = NS_OK;
+      return rv;
+    }
+
+    LogIO(this, nullptr, "Discarding Frame", trash, *countWritten);
+
+    mInputFrameDataRead += *countWritten;
+
+    if (mInputFrameDataRead == mInputFrameDataSize)
+      ResetDownstreamState();
+    return rv;
+  }
+
+  if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
+    MOZ_ASSERT(false); // this cannot happen
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  MOZ_ASSERT(mInputFrameBufferUsed == 8, "Frame Buffer Header Not Present");
+  MOZ_ASSERT(mInputFrameDataSize + 8 <= mInputFrameBufferSize,
+             "allocation for control frame insufficient");
+
+  rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
+                   mInputFrameDataSize - mInputFrameDataRead, countWritten);
+
+  if (NS_FAILED(rv)) {
+    LOG3(("Http2Session %p buffering control frame read failure %x\n",
+          this, rv));
+    // maybe just blocked reading from network
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+      rv = NS_OK;
+    return rv;
+  }
+
+  LogIO(this, nullptr, "Reading Control Frame",
+        mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
+
+  mInputFrameDataRead += *countWritten;
+
+  if (mInputFrameDataRead != mInputFrameDataSize)
+    return NS_OK;
+
+  MOZ_ASSERT(mInputFrameType != FRAME_TYPE_DATA);
+  if (mInputFrameType < FRAME_TYPE_LAST) {
+    rv = sControlFunctions[mInputFrameType](this);
+  } else {
+    // Section 4.1 requires this to be ignored; though protocol_error would
+    // be better
+    LOG3(("Http2Session %p unknow frame type %x ignored\n",
+          this, mInputFrameType));
+    ResetDownstreamState();
+    rv = NS_OK;
+  }
+
+  MOZ_ASSERT(NS_FAILED(rv) ||
+             mDownstreamState != BUFFERING_CONTROL_FRAME,
+             "Control Handler returned OK but did not change state");
+
+  if (mShouldGoAway && !mStreamTransactionHash.Count())
+    Close(NS_OK);
+  return rv;
+}
+
+void
+Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes)
+{
+  if (!stream) // this is ok - it means there was a data frame for a rst stream
+    return;
+
+  // If this data packet was not for a valid or live stream then there
+  // is no reason to mess with the flow control
+  if (!stream || stream->RecvdFin() || stream->RecvdReset() ||
+      mInputFrameFinal) {
+    return;
+  }
+
+  stream->DecrementClientReceiveWindow(bytes);
+
+  // Don't necessarily ack every data packet. Only do it
+  // after a significant amount of data.
+  uint64_t unacked = stream->LocalUnAcked();
+  int64_t  localWindow = stream->ClientReceiveWindow();
+
+  LOG3(("Http2Session::UpdateLocalStreamWindow this=%p id=0x%X newbytes=%u "
+        "unacked=%llu localWindow=%lld\n",
+        this, stream->StreamID(), bytes, unacked, localWindow));
+
+  if (!unacked)
+    return;
+
+  if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold))
+    return;
+
+  if (!stream->HasSink()) {
+    LOG3(("Http2Session::UpdateLocalStreamWindow %p 0x%X Pushed Stream Has No Sink\n",
+          this, stream->StreamID()));
+    return;
+  }
+
+  // Generate window updates directly out of session instead of the stream
+  // in order to avoid queue delays in getting the 'ACK' out.
+  uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU;
+
+  LOG3(("Http2Session::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n",
+        this, stream->StreamID(), toack));
+  stream->IncrementClientReceiveWindow(toack);
+
+  // room for this packet needs to be ensured before calling this function
+  char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  mOutputQueueUsed += 12;
+  MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
+
+  CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, stream->StreamID());
+  toack = PR_htonl(toack);
+  memcpy(packet + 8, &toack, 4);
+
+  LogIO(this, stream, "Stream Window Update", packet, 12);
+  // dont flush here, this write can commonly be coalesced with a
+  // session window update to immediately follow.
+}
+
+void
+Http2Session::UpdateLocalSessionWindow(uint32_t bytes)
+{
+  if (!bytes)
+    return;
+
+  mLocalSessionWindow -= bytes;
+
+  LOG3(("Http2Session::UpdateLocalSessionWindow this=%p newbytes=%u "
+        "localWindow=%lld\n", this, bytes, mLocalSessionWindow));
+
+  // Don't necessarily ack every data packet. Only do it
+  // after a significant amount of data.
+  if ((mLocalSessionWindow > (ASpdySession::kInitialRwin - kMinimumToAck)) &&
+      (mLocalSessionWindow > kEmergencyWindowThreshold))
+    return;
+
+  // Only send max  bits of window updates at a time.
+  uint64_t toack64 = ASpdySession::kInitialRwin - mLocalSessionWindow;
+  uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU;
+
+  LOG3(("Http2Session::UpdateLocalSessionWindow Ack this=%p acksize=%u\n",
+        this, toack));
+  mLocalSessionWindow += toack;
+
+  // room for this packet needs to be ensured before calling this function
+  char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  mOutputQueueUsed += 12;
+  MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
+
+  CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
+  toack = PR_htonl(toack);
+  memcpy(packet + 8, &toack, 4);
+
+  LogIO(this, nullptr, "Session Window Update", packet, 12);
+  // dont flush here, this write can commonly be coalesced with others
+}
+
+void
+Http2Session::UpdateLocalRwin(Http2Stream *stream, uint32_t bytes)
+{
+  // make sure there is room for 2 window updates even though
+  // we may not generate any.
+  EnsureOutputBuffer(16 * 2);
+
+  UpdateLocalStreamWindow(stream, bytes);
+  UpdateLocalSessionWindow(bytes);
+  FlushOutputQueue();
+}
+
+void
+Http2Session::Close(nsresult aReason)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  if (mClosed)
+    return;
+
+  LOG3(("Http2Session::Close %p %X", this, aReason));
+
+  mClosed = true;
+
+  mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
+  mStreamIDHash.Clear();
+  mStreamTransactionHash.Clear();
+
+  uint32_t goAwayReason;
+  if (mGoAwayReason != NO_HTTP_ERROR) {
+    goAwayReason = mGoAwayReason;
+  } else if (NS_SUCCEEDED(aReason)) {
+    goAwayReason = NO_HTTP_ERROR;
+  } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
+    goAwayReason = PROTOCOL_ERROR;
+  } else {
+    goAwayReason = INTERNAL_ERROR;
+  }
+  GenerateGoAway(goAwayReason);
+  mConnection = nullptr;
+  mSegmentReader = nullptr;
+  mSegmentWriter = nullptr;
+}
+
+void
+Http2Session::CloseTransaction(nsAHttpTransaction *aTransaction,
+                               nsresult aResult)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::CloseTransaction %p %p %x", this, aTransaction, aResult));
+
+  // Generally this arrives as a cancel event from the connection manager.
+
+  // need to find the stream and call CleanupStream() on it.
+  Http2Stream *stream = mStreamTransactionHash.Get(aTransaction);
+  if (!stream) {
+    LOG3(("Http2Session::CloseTransaction %p %p %x - not found.",
+          this, aTransaction, aResult));
+    return;
+  }
+  LOG3(("Http2Session::CloseTranscation probably a cancel. "
+        "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
+        this, aTransaction, aResult, stream->StreamID(), stream));
+  CleanupStream(stream, aResult, CANCEL_ERROR);
+  ResumeRecv();
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentReader
+//-----------------------------------------------------------------------------
+
+nsresult
+Http2Session::OnReadSegment(const char *buf,
+                            uint32_t count, uint32_t *countRead)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  nsresult rv;
+
+  // 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)
+    FlushOutputQueue();
+
+  if (!mOutputQueueUsed && mSegmentReader) {
+    // try and write directly without output queue
+    rv = mSegmentReader->OnReadSegment(buf, count, countRead);
+
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+      *countRead = 0;
+    } else if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    if (*countRead < count) {
+      uint32_t required = count - *countRead;
+      // assuming a commitment() happened, this ensurebuffer is a nop
+      // but just in case the queuesize is too small for the required data
+      // call ensurebuffer().
+      EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
+      memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
+      mOutputQueueUsed = required;
+    }
+
+    *countRead = count;
+    return NS_OK;
+  }
+
+  // At this point we are going to buffer the new data in the output
+  // queue if it fits. By coalescing multiple small submissions into one larger
+  // buffer we can get larger writes out to the network later on.
+
+  // This routine should not be allowed to fill up the output queue
+  // all on its own - at least kQueueReserved bytes are always left
+  // for other routines to use - but this is an all-or-nothing function,
+  // so if it will not all fit just return WOULD_BLOCK
+
+  if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
+    return NS_BASE_STREAM_WOULD_BLOCK;
+
+  memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
+  mOutputQueueUsed += count;
+  *countRead = count;
+
+  FlushOutputQueue();
+
+  return NS_OK;
+}
+
+nsresult
+Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment)
+{
+  if (mOutputQueueUsed)
+    FlushOutputQueue();
+
+  // would there be enough room to buffer this if needed?
+  if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
+    return NS_OK;
+
+  // if we are using part of our buffers already, try again later unless
+  // forceCommitment is set.
+  if (mOutputQueueUsed && !forceCommitment)
+    return NS_BASE_STREAM_WOULD_BLOCK;
+
+  if (mOutputQueueUsed) {
+    // normally we avoid the memmove of RealignOutputQueue, but we'll try
+    // it if forceCommitment is set before growing the buffer.
+    RealignOutputQueue();
+
+    // is there enough room now?
+    if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
+      return NS_OK;
+  }
+
+  // resize the buffers as needed
+  EnsureOutputBuffer(count + kQueueReserved);
+
+  MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved),
+             "buffer not as large as expected");
+
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentWriter
+//-----------------------------------------------------------------------------
+
+nsresult
+Http2Session::OnWriteSegment(char *buf,
+                             uint32_t count, uint32_t *countWritten)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  nsresult rv;
+
+  if (!mSegmentWriter) {
+    // the only way this could happen would be if Close() were called on the
+    // stack with WriteSegments()
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mDownstreamState == PROCESSING_DATA_FRAME) {
+
+    if (mInputFrameFinal &&
+        mInputFrameDataRead == mInputFrameDataSize) {
+      *countWritten = 0;
+      SetNeedsCleanup();
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    count = std::min(count, mInputFrameDataSize - mInputFrameDataRead);
+    rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
+    if (NS_FAILED(rv))
+      return rv;
+
+    LogIO(this, mInputFrameDataStream, "Reading Data Frame",
+          buf, *countWritten);
+
+    mInputFrameDataRead += *countWritten;
+
+    mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
+    if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameFinal)
+      ResetDownstreamState();
+
+    return rv;
+  }
+
+  if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
+
+    if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
+        mInputFrameFinal) {
+      *countWritten = 0;
+      SetNeedsCleanup();
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    count = std::min(count,
+                     mFlatHTTPResponseHeaders.Length() -
+                     mFlatHTTPResponseHeadersOut);
+    memcpy(buf,
+           mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
+           count);
+    mFlatHTTPResponseHeadersOut += count;
+    *countWritten = count;
+
+    if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) {
+      if (!mInputFrameFinal) {
+        // If more frames are expected in this stream, then reset the state so they can be
+        // handled. Otherwise (e.g. a 0 length response with the fin on the incoming headers)
+        // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can
+        // cleanup the stream.
+        ResetDownstreamState();
+      }
+    }
+
+    return NS_OK;
+  }
+
+  return NS_ERROR_UNEXPECTED;
+}
+
+void
+Http2Session::SetNeedsCleanup()
+{
+  LOG3(("Http2Session::SetNeedsCleanup %p - recorded downstream fin of "
+        "stream %p 0x%X", this, mInputFrameDataStream,
+        mInputFrameDataStream->StreamID()));
+
+  // This will result in Close() being called
+  MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
+  mNeedsCleanup = mInputFrameDataStream;
+  ResetDownstreamState();
+}
+
+void
+Http2Session::ConnectPushedStream(Http2Stream *stream)
+{
+  mReadyForRead.Push(stream);
+  ForceRecv();
+}
+
+nsresult
+Http2Session::ConfirmTLSProfile()
+{
+  if (mTLSProfileConfirmed)
+    return NS_OK;
+
+  LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n",
+        this, mConnection.get()));
+
+  if (!gHttpHandler->EnforceHttp2TlsProfile()) {
+    LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this));
+    mTLSProfileConfirmed = true;
+    return NS_OK;
+  }
+
+  if (!mConnection)
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsISupports> securityInfo;
+  mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
+  nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
+  LOG3(("Http2Session::ConfirmTLSProfile %p sslsocketcontrol=%p\n", this, ssl.get()));
+  if (!ssl)
+    return NS_ERROR_FAILURE;
+
+  int16_t version = ssl->GetSSLVersionUsed();
+  LOG3(("Http2Session::ConfirmTLSProfile %p version=%x\n", this, version));
+  if (version < nsISSLSocketControl::TLS_VERSION_1_1) {
+    LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of TLS1.1\n", this));
+    RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
+  }
+
+  mTLSProfileConfirmed = true;
+  return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Modified methods of nsAHttpConnection
+//-----------------------------------------------------------------------------
+
+void
+Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::TransactionHasDataToWrite %p trans=%p", this, caller));
+
+  // a trapped signal from the http transaction to the connection that
+  // it is no longer blocked on read.
+
+  Http2Stream *stream = mStreamTransactionHash.Get(caller);
+  if (!stream || !VerifyStream(stream)) {
+    LOG3(("Http2Session::TransactionHasDataToWrite %p caller %p not found",
+          this, caller));
+    return;
+  }
+
+  LOG3(("Http2Session::TransactionHasDataToWrite %p ID is 0x%X\n",
+        this, stream->StreamID()));
+
+  mReadyForWrite.Push(stream);
+}
+
+void
+Http2Session::TransactionHasDataToWrite(Http2Stream *stream)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  LOG3(("Http2Session::TransactionHasDataToWrite %p stream=%p ID=0x%x",
+        this, stream, stream->StreamID()));
+
+  mReadyForWrite.Push(stream);
+  SetWriteCallbacks();
+}
+
+bool
+Http2Session::IsPersistent()
+{
+  return true;
+}
+
+nsresult
+Http2Session::TakeTransport(nsISocketTransport **,
+                            nsIAsyncInputStream **, nsIAsyncOutputStream **)
+{
+  MOZ_ASSERT(false, "TakeTransport of Http2Session");
+  return NS_ERROR_UNEXPECTED;
+}
+
+nsHttpConnection *
+Http2Session::TakeHttpConnection()
+{
+  MOZ_ASSERT(false, "TakeHttpConnection of Http2Session");
+  return nullptr;
+}
+
+uint32_t
+Http2Session::CancelPipeline(nsresult reason)
+{
+  // we don't pipeline inside http/2, so this isn't an issue
+  return 0;
+}
+
+nsAHttpTransaction::Classifier
+Http2Session::Classification()
+{
+  if (!mConnection)
+    return nsAHttpTransaction::CLASS_GENERAL;
+  return mConnection->Classification();
+}
+
+//-----------------------------------------------------------------------------
+// unused methods of nsAHttpTransaction
+// We can be sure of this because Http2Session is only constructed in
+// nsHttpConnection and is never passed out of that object
+//-----------------------------------------------------------------------------
+
+void
+Http2Session::SetConnection(nsAHttpConnection *)
+{
+  // This is unexpected
+  MOZ_ASSERT(false, "Http2Session::SetConnection()");
+}
+
+void
+Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **)
+{
+  // This is unexpected
+  MOZ_ASSERT(false, "Http2Session::GetSecurityCallbacks()");
+}
+
+void
+Http2Session::SetProxyConnectFailed()
+{
+  MOZ_ASSERT(false, "Http2Session::SetProxyConnectFailed()");
+}
+
+bool
+Http2Session::IsDone()
+{
+  return !mStreamTransactionHash.Count();
+}
+
+nsresult
+Http2Session::Status()
+{
+  MOZ_ASSERT(false, "Http2Session::Status()");
+  return NS_ERROR_UNEXPECTED;
+}
+
+uint32_t
+Http2Session::Caps()
+{
+  MOZ_ASSERT(false, "Http2Session::Caps()");
+  return 0;
+}
+
+void
+Http2Session::SetDNSWasRefreshed()
+{
+  MOZ_ASSERT(false, "Http2Session::SetDNSWasRefreshed()");
+}
+
+uint64_t
+Http2Session::Available()
+{
+  MOZ_ASSERT(false, "Http2Session::Available()");
+  return 0;
+}
+
+nsHttpRequestHead *
+Http2Session::RequestHead()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(false,
+             "Http2Session::RequestHead() "
+             "should not be called after http/2 is setup");
+  return NULL;
+}
+
+uint32_t
+Http2Session::Http1xTransactionCount()
+{
+  return 0;
+}
+
+// used as an enumerator by TakeSubTransactions()
+static PLDHashOperator
+  TakeStream(nsAHttpTransaction *key,
+             nsAutoPtr<Http2Stream> &stream,
+             void *closure)
+{
+  nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
+    static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
+
+  list->AppendElement(key);
+
+  // removing the stream from the hash will delete the stream
+  // and drop the transaction reference the hash held
+  return PL_DHASH_REMOVE;
+}
+
+nsresult
+Http2Session::TakeSubTransactions(
+  nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
+{
+  // Generally this cannot be done with http/2 as transactions are
+  // started right away.
+
+  LOG3(("Http2Session::TakeSubTransactions %p\n", this));
+
+  if (mConcurrentHighWater > 0)
+    return NS_ERROR_ALREADY_OPENED;
+
+  LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
+
+  mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
+  return NS_OK;
+}
+
+nsresult
+Http2Session::AddTransaction(nsAHttpTransaction *)
+{
+  // This API is meant for pipelining, Http2Session's should be
+  // extended with AddStream()
+
+  MOZ_ASSERT(false,
+             "Http2Session::AddTransaction() should not be called");
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+uint32_t
+Http2Session::PipelineDepth()
+{
+  return IsDone() ? 0 : 1;
+}
+
+nsresult
+Http2Session::SetPipelinePosition(int32_t position)
+{
+  // This API is meant for pipelining, Http2Session's should be
+  // extended with AddStream()
+
+  MOZ_ASSERT(false,
+             "Http2Session::SetPipelinePosition() should not be called");
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+int32_t
+Http2Session::PipelinePosition()
+{
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Pass through methods of nsAHttpConnection
+//-----------------------------------------------------------------------------
+
+nsAHttpConnection *
+Http2Session::Connection()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  return mConnection;
+}
+
+nsresult
+Http2Session::OnHeadersAvailable(nsAHttpTransaction *transaction,
+                                 nsHttpRequestHead *requestHead,
+                                 nsHttpResponseHead *responseHead, bool *reset)
+{
+  return mConnection->OnHeadersAvailable(transaction,
+                                         requestHead,
+                                         responseHead,
+                                         reset);
+}
+
+bool
+Http2Session::IsReused()
+{
+  return mConnection->IsReused();
+}
+
+nsresult
+Http2Session::PushBack(const char *buf, uint32_t len)
+{
+  return mConnection->PushBack(buf, len);
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Session.h
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_Http2Session_h
+#define mozilla_net_Http2Session_h
+
+// HTTP/2
+
+#include "ASpdySession.h"
+#include "mozilla/Attributes.h"
+#include "nsAHttpConnection.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsDeque.h"
+#include "nsHashKeys.h"
+
+#include "Http2Compression.h"
+
+class nsISocketTransport;
+
+namespace mozilla {
+namespace net {
+
+class Http2PushedStream;
+class Http2Stream;
+
+class Http2Session MOZ_FINAL : public ASpdySession
+  , public nsAHttpConnection
+  , public nsAHttpSegmentReader
+  , public nsAHttpSegmentWriter
+{
+public:
+  NS_DECL_ISUPPORTS
+    NS_DECL_NSAHTTPTRANSACTION
+    NS_DECL_NSAHTTPCONNECTION(mConnection)
+    NS_DECL_NSAHTTPSEGMENTREADER
+    NS_DECL_NSAHTTPSEGMENTWRITER
+
+   Http2Session(nsAHttpTransaction *, nsISocketTransport *, int32_t);
+  ~Http2Session();
+
+  bool AddStream(nsAHttpTransaction *, int32_t);
+  bool CanReuse() { return !mShouldGoAway && !mClosed; }
+  bool RoomForMoreStreams();
+
+  // When the connection is active this is called every 1 second
+  void ReadTimeoutTick(PRIntervalTime now);
+
+  // Idle time represents time since "goodput".. e.g. a data or header frame
+  PRIntervalTime IdleTime();
+
+  // Registering with a newID of 0 means pick the next available odd ID
+  uint32_t RegisterStreamID(Http2Stream *, uint32_t aNewID = 0);
+
+/*
+  HTTP/2 framing
+
+  0                   1                   2                   3
+  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  |         Length (16)           |   Type (8)    |   Flags (8)   |
+  +-+-------------+---------------+-------------------------------+
+  |R|                 Stream Identifier (31)                      |
+  +-+-------------------------------------------------------------+
+  |                     Frame Data (0...)                       ...
+  +---------------------------------------------------------------+
+*/
+
+  enum frameType {
+    FRAME_TYPE_DATA = 0,
+    FRAME_TYPE_HEADERS = 1,
+    FRAME_TYPE_PRIORITY = 2,
+    FRAME_TYPE_RST_STREAM = 3,
+    FRAME_TYPE_SETTINGS = 4,
+    FRAME_TYPE_PUSH_PROMISE = 5,
+    FRAME_TYPE_PING = 6,
+    FRAME_TYPE_GOAWAY = 7,
+    FRAME_TYPE_UNUSED1 = 8,
+    FRAME_TYPE_WINDOW_UPDATE = 9,
+    FRAME_TYPE_CONTINUATION = 10,
+    FRAME_TYPE_LAST = 11
+  };
+
+  // NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway
+  // code NO_ERROR to be NO_HTTP_ERROR
+  enum errorType {
+    NO_HTTP_ERROR = 0,
+    PROTOCOL_ERROR = 1,
+    INTERNAL_ERROR = 2,
+    FLOW_CONTROL_ERROR = 3,
+    SETTINGS_TIMEOUT_ERROR = 4,
+    STREAM_CLOSED_ERROR = 5,
+    FRAME_SIZE_ERROR = 6,
+    REFUSED_STREAM_ERROR = 7,
+    CANCEL_ERROR = 8,
+    COMPRESSION_ERROR = 9,
+    CONNECT_ERROR = 10,
+    ENHANCE_YOUR_CALM = 420
+  };
+
+  // These are frame flags. If they, or other undefined flags, are
+  // used on frames other than the comments indicate they MUST be ignored.
+  const static uint8_t kFlag_END_STREAM = 0x01; // data, headers
+  const static uint8_t kFlag_END_HEADERS = 0x04; // headers, continuation
+  const static uint8_t kFlag_PRIORITY = 0x08; //headers
+  const static uint8_t kFlag_END_PUSH_PROMISE = 0x04; // push promise
+  const static uint8_t kFlag_ACK = 0x01; // ping and settings
+
+  enum {
+    SETTINGS_TYPE_HEADER_TABLE_SIZE = 1, // compression table size
+    SETTINGS_TYPE_ENABLE_PUSH = 2,     // can be used to disable push
+    SETTINGS_TYPE_MAX_CONCURRENT = 4,  // streams recvr allowed to initiate
+    SETTINGS_TYPE_INITIAL_WINDOW = 7,  // bytes for flow control default
+    SETTINGS_TYPE_FLOW_CONTROL = 10    // flow control details
+  };
+
+  // This should be big enough to hold all of your control packets,
+  // but if it needs to grow for huge headers it can do so dynamically.
+  const static uint32_t kDefaultBufferSize = 2048;
+
+  // kDefaultQueueSize must be >= other queue size constants
+  const static uint32_t kDefaultQueueSize =  32768;
+  const static uint32_t kQueueMinimumCleanup = 24576;
+  const static uint32_t kQueueTailRoom    =  4096;
+  const static uint32_t kQueueReserved    =  1024;
+
+  const static uint32_t kDefaultMaxConcurrent = 100;
+  const static uint32_t kMaxStreamID = 0x7800000;
+
+  // This is a sentinel for a deleted stream. It is not a valid
+  // 31 bit stream ID.
+  const static uint32_t kDeadStreamID = 0xffffdead;
+
+  // below the emergency threshold of local window we ack every received
+  // byte. Above that we coalesce bytes into the MinimumToAck size.
+  const static int32_t  kEmergencyWindowThreshold = 256 * 1024;
+  const static uint32_t kMinimumToAck = 4 * 1024 * 1024;
+
+  // The default rwin is 64KB - 1 unless updated by a settings frame
+  const static uint32_t kDefaultRwin = 65535;
+
+  // Frames with HTTP semantics are limited to 2^14 - 1 bytes of length in
+  // order to preserve responsiveness
+  const static uint32_t kMaxFrameData = 16383;
+
+  static nsresult RecvHeaders(Http2Session *);
+  static nsresult RecvPriority(Http2Session *);
+  static nsresult RecvRstStream(Http2Session *);
+  static nsresult RecvSettings(Http2Session *);
+  static nsresult RecvPushPromise(Http2Session *);
+  static nsresult RecvPing(Http2Session *);
+  static nsresult RecvGoAway(Http2Session *);
+  static nsresult RecvUnused1(Http2Session *);
+  static nsresult RecvWindowUpdate(Http2Session *);
+  static nsresult RecvContinuation(Http2Session *);
+
+  template<typename T>
+  static void EnsureBuffer(nsAutoArrayPtr<T> &,
+                           uint32_t, uint32_t, uint32_t &);
+  char       *EnsureOutputBuffer(uint32_t needed);
+
+  template<typename charType>
+  void CreateFrameHeader(charType dest, uint16_t frameLength,
+                         uint8_t frameType, uint8_t frameFlags,
+                         uint32_t streamID);
+
+  // For writing the data stream to LOG4
+  static void LogIO(Http2Session *, Http2Stream *, const char *,
+                    const char *, uint32_t);
+
+  // an overload of nsAHttpConnection
+  void TransactionHasDataToWrite(nsAHttpTransaction *);
+
+  // a similar version for Http2Stream
+  void TransactionHasDataToWrite(Http2Stream *);
+
+  // an overload of nsAHttpSegementReader
+  virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment);
+
+  uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; }
+
+  void ConnectPushedStream(Http2Stream *stream);
+
+  nsresult ConfirmTLSProfile();
+
+  uint64_t Serial() { return mSerial; }
+
+  void PrintDiagnostics (nsCString &log);
+
+  // Streams need access to these
+  uint32_t SendingChunkSize() { return mSendingChunkSize; }
+  uint32_t PushAllowance() { return mPushAllowance; }
+  Http2Compressor *Compressor() { return &mCompressor; }
+  nsISocketTransport *SocketTransport() { return mSocketTransport; }
+  int64_t ServerSessionWindow() { return mServerSessionWindow; }
+  bool ServerUsesFlowControl() { return mServerUsesFlowControl; }
+  void DecrementServerSessionWindow (uint32_t bytes) { mServerSessionWindow -= bytes; }
+
+private:
+
+  // These internal states do not correspond to the states of the HTTP/2 specification
+  enum internalStateType {
+    BUFFERING_OPENING_SETTINGS,
+    BUFFERING_FRAME_HEADER,
+    BUFFERING_CONTROL_FRAME,
+    PROCESSING_DATA_FRAME,
+    DISCARDING_DATA_FRAME,
+    PROCESSING_COMPLETE_HEADERS,
+    PROCESSING_CONTROL_RST_STREAM
+  };
+
+  static const uint8_t kMagicHello[24];
+
+  nsresult    ResponseHeadersComplete();
+  uint32_t    GetWriteQueueSize();
+  void        ChangeDownstreamState(enum internalStateType);
+  void        ResetDownstreamState();
+  nsresult    UncompressAndDiscard();
+  void        MaybeDecrementConcurrent(Http2Stream *);
+  void        GeneratePing(bool);
+  void        GenerateSettingsAck();
+  void        GeneratePriority(uint32_t, uint32_t);
+  void        GenerateRstStream(uint32_t, uint32_t);
+  void        GenerateGoAway(uint32_t);
+  void        CleanupStream(Http2Stream *, nsresult, errorType);
+  void        CloseStream(Http2Stream *, nsresult);
+  void        SendHello();
+  void        RemoveStreamFromQueues(Http2Stream *);
+
+  void        SetWriteCallbacks();
+  void        FlushOutputQueue();
+  void        RealignOutputQueue();
+
+  bool        RoomForMoreConcurrent();
+  void        ActivateStream(Http2Stream *);
+  void        ProcessPending();
+  nsresult    SetInputFrameDataStream(uint32_t);
+  bool        VerifyStream(Http2Stream *, uint32_t);
+  void        SetNeedsCleanup();
+
+  void        UpdateLocalRwin(Http2Stream *stream, uint32_t bytes);
+  void        UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes);
+  void        UpdateLocalSessionWindow(uint32_t bytes);
+
+  // a wrapper for all calls to the nshttpconnection level segment writer. Used
+  // to track network I/O for timeout purposes
+  nsresult   NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *);
+
+  static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *,
+                                            nsAutoPtr<Http2Stream> &,
+                                            void *);
+
+  static PLDHashOperator GoAwayEnumerator(nsAHttpTransaction *,
+                                          nsAutoPtr<Http2Stream> &,
+                                          void *);
+
+  static PLDHashOperator UpdateServerRwinEnumerator(nsAHttpTransaction *,
+                                                    nsAutoPtr<Http2Stream> &,
+                                                    void *);
+
+  static PLDHashOperator RestartBlockedOnRwinEnumerator(nsAHttpTransaction *,
+                                                        nsAutoPtr<Http2Stream> &,
+                                                        void *);
+
+  // This is intended to be nsHttpConnectionMgr:nsConnectionHandle taken
+  // from the first transaction on this session. That object contains the
+  // pointer to the real network-level nsHttpConnection object.
+  nsRefPtr<nsAHttpConnection> mConnection;
+
+  // The underlying socket transport object is needed to propogate some events
+  nsISocketTransport         *mSocketTransport;
+
+  // These are temporary state variables to hold the argument to
+  // Read/WriteSegments so it can be accessed by On(read/write)segment
+  // further up the stack.
+  nsAHttpSegmentReader       *mSegmentReader;
+  nsAHttpSegmentWriter       *mSegmentWriter;
+
+  uint32_t          mSendingChunkSize;        /* the transmission chunk size */
+  uint32_t          mNextStreamID;            /* 24 bits */
+  uint32_t          mConcurrentHighWater;     /* max parallelism on session */
+  uint32_t          mPushAllowance;           /* rwin for unmatched pushes */
+
+  internalStateType mDownstreamState; /* in frame, between frames, etc..  */
+
+  // Maintain 2 indexes - one by stream ID, one by transaction pointer.
+  // There are also several lists of streams: ready to write, queued due to
+  // max parallelism, streams that need to force a read for push, and the full
+  // set of pushed streams.
+  // The objects are not ref counted - they get destroyed
+  // by the nsClassHashtable implementation when they are removed from
+  // the transaction hash.
+  nsDataHashtable<nsUint32HashKey, Http2Stream *>     mStreamIDHash;
+  nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
+    Http2Stream>                                      mStreamTransactionHash;
+
+  nsDeque                                             mReadyForWrite;
+  nsDeque                                             mQueuedStreams;
+  nsDeque                                             mReadyForRead;
+  nsTArray<Http2PushedStream *>                       mPushedStreams;
+
+  // Compression contexts for header transport.
+  // HTTP/2 compresses only HTTP headers and does not reset the context in between
+  // frames. Even data that is not associated with a stream (e.g invalid
+  // stream ID) is passed through these contexts to keep the compression
+  // context correct.
+  Http2Compressor     mCompressor;
+  Http2Decompressor   mDecompressor;
+  nsCString           mDecompressBuffer;
+
+  // mInputFrameBuffer is used to store received control packets and the 8 bytes
+  // of header on data packets
+  uint32_t             mInputFrameBufferSize; // buffer allocation
+  uint32_t             mInputFrameBufferUsed; // amt of allocation used
+  nsAutoArrayPtr<char> mInputFrameBuffer;
+
+  // mInputFrameDataSize/Read are used for tracking the amount of data consumed
+  // in a frame after the 8 byte header. Control frames are always fully buffered
+  // and the fixed 8 byte leading header is at mInputFrameBuffer + 0, the first
+  // data byte (i.e. the first settings/goaway/etc.. specific byte) is at
+  // mInputFrameBuffer + 8
+  // The frame size is mInputFrameDataSize + the constant 8 byte header
+  uint32_t             mInputFrameDataSize;
+  uint32_t             mInputFrameDataRead;
+  bool                 mInputFrameFinal; // This frame was marked FIN
+  uint8_t              mInputFrameType;
+  uint8_t              mInputFrameFlags;
+  uint32_t             mInputFrameID;
+
+  // When a frame has been received that is addressed to a particular stream
+  // (e.g. a data frame after the stream-id has been decoded), this points
+  // to the stream.
+  Http2Stream          *mInputFrameDataStream;
+
+  // mNeedsCleanup is a state variable to defer cleanup of a closed stream
+  // If needed, It is set in session::OnWriteSegments() and acted on and
+  // cleared when the stack returns to session::WriteSegments(). The stream
+  // cannot be destroyed directly out of OnWriteSegments because
+  // stream::writeSegments() is on the stack at that time.
+  Http2Stream          *mNeedsCleanup;
+
+  // This reason code in the last processed RESET frame
+  uint32_t             mDownstreamRstReason;
+
+  // When HEADERS/PROMISE are chained together, this is the expected ID of the next
+  // recvd frame which must be the same type
+  uint32_t             mExpectedHeaderID;
+  uint32_t             mExpectedPushPromiseID;
+  uint32_t             mContinuedPromiseStream;
+
+  // for the conversion of downstream http headers into http/2 formatted headers
+  // The data here does not persist between frames
+  nsCString            mFlatHTTPResponseHeaders;
+  uint32_t             mFlatHTTPResponseHeadersOut;
+
+  // when set, the session will go away when it reaches 0 streams. This flag
+  // is set when: the stream IDs are running out (at either the client or the
+  // server), when DontReuse() is called, a RST that is not specific to a
+  // particular stream is received, a GOAWAY frame has been received from
+  // the server.
+  bool                 mShouldGoAway;
+
+  // the session has received a nsAHttpTransaction::Close()  call
+  bool                 mClosed;
+
+  // the session received a GoAway frame with a valid GoAwayID
+  bool                 mCleanShutdown;
+
+  // if server has enabled flow control for all streams in this session.
+  // If disabled it does not generate window updates and does not expect
+  // the client to ever block on sending data frames
+  bool                 mServerUsesFlowControl;
+
+  // The TLS comlpiance checks are not done in the ctor beacuse of bad
+  // exception handling - so we do them at IO time and cache the result
+  bool                 mTLSProfileConfirmed;
+
+  // A specifc reason code for the eventual GoAway frame. If set to NO_HTTP_ERROR
+  // only NO_HTTP_ERROR, PROTOCOL_ERROR, or INTERNAL_ERROR will be sent.
+  errorType            mGoAwayReason;
+
+  // If a GoAway message was received this is the ID of the last valid
+  // stream. 0 otherwise. (0 is never a valid stream id.)
+  uint32_t             mGoAwayID;
+
+  // The last stream processed ID we will send in our GoAway frame.
+  uint32_t             mOutgoingGoAwayID;
+
+  // The limit on number of concurrent streams for this session. Normally it
+  // is basically unlimited, but the SETTINGS control message from the
+  // server might bring it down.
+  uint32_t             mMaxConcurrent;
+
+  // The actual number of concurrent streams at this moment. Generally below
+  // mMaxConcurrent, but the max can be lowered in real time to a value
+  // below the current value
+  uint32_t             mConcurrent;
+
+  // The number of server initiated promises, tracked for telemetry
+  uint32_t             mServerPushedResources;
+
+  // The server rwin for new streams as determined from a SETTINGS frame
+  uint32_t             mServerInitialStreamWindow;
+
+  // The Local Session window is how much data the server is allowed to send
+  // (across all streams) without getting a window update to stream 0. It is
+  // signed because asynchronous changes via SETTINGS can drive it negative.
+  int64_t              mLocalSessionWindow;
+
+  // The Remote Session Window is how much data the client is allowed to send
+  // (across all streams) without receiving a window update to stream 0. It is
+  // signed because asynchronous changes via SETTINGS can drive it negative.
+  int64_t              mServerSessionWindow;
+
+  // This is a output queue of bytes ready to be written to the SSL stream.
+  // When that streams returns WOULD_BLOCK on direct write the bytes get
+  // coalesced together here. This results in larger writes to the SSL layer.
+  // The buffer is not dynamically grown to accomodate stream writes, but
+  // does expand to accept infallible session wide frames like GoAway and RST.
+  uint32_t             mOutputQueueSize;
+  uint32_t             mOutputQueueUsed;
+  uint32_t             mOutputQueueSent;
+  nsAutoArrayPtr<char> mOutputQueueBuffer;
+
+  PRIntervalTime       mPingThreshold;
+  PRIntervalTime       mLastReadEpoch;     // used for ping timeouts
+  PRIntervalTime       mLastDataReadEpoch; // used for IdleTime()
+  PRIntervalTime       mPingSentEpoch;
+
+  // used as a temporary buffer while enumerating the stream hash during GoAway
+  nsDeque  mGoAwayStreamsToRestart;
+
+  // Each session gets a unique serial number because the push cache is correlated
+  // by the load group and the serial number can be used as part of the cache key
+  // to make sure streams aren't shared across sessions.
+  uint64_t        mSerial;
+};
+
+} // namespace mozilla::net
+} // namespace mozilla
+
+#endif // mozilla_net_Http2Session_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -0,0 +1,1091 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include <algorithm>
+
+#include "Http2Compression.h"
+#include "Http2Session.h"
+#include "Http2Stream.h"
+#include "Http2Push.h"
+
+#include "mozilla/Telemetry.h"
+#include "nsAlgorithm.h"
+#include "nsHttp.h"
+#include "nsHttpHandler.h"
+#include "nsHttpRequestHead.h"
+#include "nsISocketTransport.h"
+#include "prnetdb.h"
+
+#ifdef DEBUG
+// defined by the socket transport service while active
+extern PRThread *gSocketThread;
+#endif
+
+namespace mozilla {
+namespace net {
+
+Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
+                         Http2Session *session,
+                         int32_t priority)
+  : mStreamID(0),
+    mSession(session),
+    mUpstreamState(GENERATING_HEADERS),
+    mState(IDLE),
+    mAllHeadersSent(0),
+    mAllHeadersReceived(0),
+    mTransaction(httpTransaction),
+    mSocketTransport(session->SocketTransport()),
+    mSegmentReader(nullptr),
+    mSegmentWriter(nullptr),
+    mChunkSize(session->SendingChunkSize()),
+    mRequestBlockedOnRead(0),
+    mRecvdFin(0),
+    mRecvdReset(0),
+    mSentReset(0),
+    mCountAsActive(0),
+    mSentFin(0),
+    mSentWaitingFor(0),
+    mSetTCPSocketBuffer(0),
+    mTxInlineFrameSize(Http2Session::kDefaultBufferSize),
+    mTxInlineFrameUsed(0),
+    mTxStreamFrameSize(0),
+    mRequestBodyLenRemaining(0),
+    mLocalUnacked(0),
+    mBlockedOnRwin(false),
+    mTotalSent(0),
+    mTotalRead(0),
+    mPushSource(nullptr)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  LOG3(("Http2Stream::Http2Stream %p", this));
+
+  mServerReceiveWindow = session->GetServerInitialStreamWindow();
+  mClientReceiveWindow = session->PushAllowance();
+
+  mTxInlineFrame = new uint8_t[mTxInlineFrameSize];
+
+  PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_LOWEST <= kNormalPriority);
+
+  // values of priority closer to 0 are higher priority for both
+  // mPriority and the priority argument. They are relative but not
+  // proportional.
+  int32_t httpPriority;
+  if (priority >= nsISupportsPriority::PRIORITY_LOWEST) {
+    httpPriority = kWorstPriority;
+  } else if (priority <= nsISupportsPriority::PRIORITY_HIGHEST) {
+    httpPriority = kBestPriority;
+  } else {
+    httpPriority = kNormalPriority + priority;
+  }
+  MOZ_ASSERT(httpPriority >= 0);
+  mPriority = static_cast<uint32_t>(httpPriority);
+}
+
+Http2Stream::~Http2Stream()
+{
+  mStreamID = Http2Session::kDeadStreamID;
+}
+
+// ReadSegments() is used to write data down the socket. Generally, HTTP
+// request data is pulled from the approriate transaction and
+// converted to HTTP/2 data. Sometimes control data like a window-update is
+// generated instead.
+
+nsresult
+Http2Stream::ReadSegments(nsAHttpSegmentReader *reader,
+                          uint32_t count,
+                          uint32_t *countRead)
+{
+  LOG3(("Http2Stream %p ReadSegments reader=%p count=%d state=%x",
+        this, reader, count, mUpstreamState));
+
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+  nsresult rv = NS_ERROR_UNEXPECTED;
+  mRequestBlockedOnRead = 0;
+
+  if (mRecvdFin || mRecvdReset) {
+    // Don't transmit any request frames if the peer cannot respond
+    LOG3(("Http2Stream %p ReadSegments request stream aborted due to"
+          " response side closure\n", this));
+    return NS_ERROR_ABORT;
+  }
+
+  switch (mUpstreamState) {
+  case GENERATING_HEADERS:
+  case GENERATING_BODY:
+  case SENDING_BODY:
+    // Call into the HTTP Transaction to generate the HTTP request
+    // stream. That stream will show up in OnReadSegment().
+    mSegmentReader = reader;
+    rv = mTransaction->ReadSegments(this, count, countRead);
+    mSegmentReader = nullptr;
+
+    // Check to see if the transaction's request could be written out now.
+    // If not, mark the stream for callback when writing can proceed.
+    if (NS_SUCCEEDED(rv) &&
+        mUpstreamState == GENERATING_HEADERS &&
+        !mAllHeadersSent)
+      mSession->TransactionHasDataToWrite(this);
+
+    // mTxinlineFrameUsed represents any queued un-sent frame. It might
+    // be 0 if there is no such frame, which is not a gurantee that we
+    // don't have more request body to send - just that any data that was
+    // sent comprised a complete HTTP/2 frame. Likewise, a non 0 value is
+    // a queued, but complete, http/2 frame length.
+
+    // Mark that we are blocked on read if the http transaction needs to
+    // provide more of the request message body and there is nothing queued
+    // for writing
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
+      mRequestBlockedOnRead = 1;
+
+    // If the sending flow control window is open (!mBlockedOnRwin) then
+    // continue sending the request
+    if (!mBlockedOnRwin &&
+        !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
+      LOG3(("Http2Stream::ReadSegments %p 0x%X: Sending request data complete, "
+            "mUpstreamState=%x",this, mStreamID, mUpstreamState));
+      if (mSentFin) {
+        ChangeState(UPSTREAM_COMPLETE);
+      } else {
+        GenerateDataFrameHeader(0, true);
+        ChangeState(SENDING_FIN_STREAM);
+        mSession->TransactionHasDataToWrite(this);
+        rv = NS_BASE_STREAM_WOULD_BLOCK;
+      }
+    }
+    break;
+
+  case SENDING_FIN_STREAM:
+    // We were trying to send the FIN-STREAM but were blocked from
+    // sending it out - try again.
+    if (!mSentFin) {
+      mSegmentReader = reader;
+      rv = TransmitFrame(nullptr, nullptr, false);
+      mSegmentReader = nullptr;
+      MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
+                 "Transmit Frame should be all or nothing");
+      if (NS_SUCCEEDED(rv))
+        ChangeState(UPSTREAM_COMPLETE);
+    } else {
+      rv = NS_OK;
+      mTxInlineFrameUsed = 0;         // cancel fin data packet
+      ChangeState(UPSTREAM_COMPLETE);
+    }
+
+    *countRead = 0;
+
+    // don't change OK to WOULD BLOCK. we are really done sending if OK
+    break;
+
+  case UPSTREAM_COMPLETE:
+    *countRead = 0;
+    rv = NS_OK;
+    break;
+
+  default:
+    MOZ_ASSERT(false, "Http2Stream::ReadSegments unknown state");
+    break;
+  }
+
+  return rv;
+}
+
+// WriteSegments() is used to read data off the socket. Generally this is
+// just a call through to the associate nsHttpTransaciton for this stream
+// for the remaining data bytes indicated by the current DATA frame.
+
+nsresult
+Http2Stream::WriteSegments(nsAHttpSegmentWriter *writer,
+                           uint32_t count,
+                           uint32_t *countWritten)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(!mSegmentWriter, "segment writer in progress");
+
+  LOG3(("Http2Stream::WriteSegments %p count=%d state=%x",
+        this, count, mUpstreamState));
+
+  mSegmentWriter = writer;
+  nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
+  mSegmentWriter = nullptr;
+
+  return rv;
+}
+
+void
+Http2Stream::CreatePushHashKey(const nsCString &scheme,
+                               const nsCString &hostHeader,
+                               uint64_t serial,
+                               const nsCSubstring &pathInfo,
+                               nsCString &outOrigin,
+                               nsCString &outKey)
+{
+  outOrigin = scheme;
+  outOrigin.Append(NS_LITERAL_CSTRING("://"));
+  outOrigin.Append(hostHeader);
+
+  outKey = outOrigin;
+  outKey.Append(NS_LITERAL_CSTRING("/[http2."));
+  outKey.AppendInt(serial);
+  outKey.Append(NS_LITERAL_CSTRING("]"));
+  outKey.Append(pathInfo);
+}
+
+nsresult
+Http2Stream::ParseHttpRequestHeaders(const char *buf,
+                                     uint32_t avail,
+                                     uint32_t *countUsed)
+{
+  // Returns NS_OK even if the headers are incomplete
+  // set mAllHeadersSent flag if they are complete
+
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(mUpstreamState == GENERATING_HEADERS);
+
+  LOG3(("Http2Stream::ParseHttpRequestHeaders %p avail=%d state=%x",
+        this, avail, mUpstreamState));
+
+  mFlatHttpRequestHeaders.Append(buf, avail);
+
+  // We can use the simple double crlf because firefox is the
+  // only client we are parsing
+  int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
+
+  if (endHeader == kNotFound) {
+    // We don't have all the headers yet
+    LOG3(("Http2Stream::ParseHttpRequestHeaders %p "
+          "Need more header bytes. Len = %d",
+          this, mFlatHttpRequestHeaders.Length()));
+    *countUsed = avail;
+    return NS_OK;
+  }
+
+  // We have recvd all the headers, trim the local
+  // buffer of the final empty line, and set countUsed to reflect
+  // the whole header has been consumed.
+  uint32_t oldLen = mFlatHttpRequestHeaders.Length();
+  mFlatHttpRequestHeaders.SetLength(endHeader + 2);
+  *countUsed = avail - (oldLen - endHeader) + 4;
+  mAllHeadersSent = 1;
+
+  nsCString hostHeader;
+  nsCString hashkey;
+  mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
+
+  CreatePushHashKey(NS_LITERAL_CSTRING("https"),
+                    hostHeader, mSession->Serial(),
+                    mTransaction->RequestHead()->RequestURI(),
+                    mOrigin, hashkey);
+
+  // check the push cache for GET
+  if (mTransaction->RequestHead()->Method() == nsHttp::Get) {
+    // from :scheme, :authority, :path
+    nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
+    SpdyPushCache *cache = nullptr;
+    if (loadGroupCI)
+      loadGroupCI->GetSpdyPushCache(&cache);
+
+    Http2PushedStream *pushedStream = nullptr;
+    // we remove the pushedstream from the push cache so that
+    // it will not be used for another GET. This does not destroy the
+    // stream itself - that is done when the transactionhash is done with it.
+    if (cache)
+      pushedStream = cache->RemovePushedStreamHttp2(hashkey);
+
+    if (pushedStream) {
+      LOG3(("Pushed Stream Match located id=0x%X key=%s\n",
+            pushedStream->StreamID(), hashkey.get()));
+      pushedStream->SetConsumerStream(this);
+      mPushSource = pushedStream;
+      SetSentFin(true);
+      AdjustPushedPriority();
+
+      // There is probably pushed data buffered so trigger a read manually
+      // as we can't rely on future network events to do it
+      mSession->ConnectPushedStream(this);
+      return NS_OK;
+    }
+  }
+
+  // It is now OK to assign a streamID that we are assured will
+  // be monotonically increasing amongst new streams on this
+  // session
+  mStreamID = mSession->RegisterStreamID(this);
+  MOZ_ASSERT(mStreamID & 1, "Http2 Stream Channel ID must be odd");
+  LOG3(("Stream ID 0x%X [session=%p] for URI %s\n",
+        mStreamID, mSession,
+        nsCString(mTransaction->RequestHead()->RequestURI()).get()));
+
+  if (mStreamID >= 0x80000000) {
+    // streamID must fit in 31 bits. Evading This is theoretically possible
+    // because stream ID assignment is asynchronous to stream creation
+    // because of the protocol requirement that the new stream ID
+    // be monotonically increasing. In reality this is really not possible
+    // because new streams stop being added to a session with millions of
+    // IDs still available and no race condition is going to bridge that gap;
+    // so we can be comfortable on just erroring out for correctness in that
+    // case.
+    LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // Now we need to convert the flat http headers into a set
+  // of HTTP/2 headers by writing to mTxInlineFrame{sz}
+
+  nsCString compressedData;
+  mSession->Compressor()->EncodeHeaderBlock(mFlatHttpRequestHeaders,
+                                            nsCString(mTransaction->RequestHead()->Method().get()),
+                                            mTransaction->RequestHead()->RequestURI(),
+                                            hostHeader,
+                                            NS_LITERAL_CSTRING("https"),
+                                            compressedData);
+
+  // Determine whether to put the fin bit on the header frame or whether
+  // to wait for a data packet to put it on.
+  uint8_t firstFrameFlags =  Http2Session::kFlag_PRIORITY;
+
+  if (mTransaction->RequestHead()->Method() == nsHttp::Get ||
+      mTransaction->RequestHead()->Method() == nsHttp::Connect ||
+      mTransaction->RequestHead()->Method() == nsHttp::Head) {
+    // for GET, CONNECT, and HEAD place the fin bit right on the
+    // header packet
+
+    SetSentFin(true);
+    firstFrameFlags |= Http2Session::kFlag_END_STREAM;
+  } else if (mTransaction->RequestHead()->Method() == nsHttp::Post ||
+             mTransaction->RequestHead()->Method() == nsHttp::Put ||
+             mTransaction->RequestHead()->Method() == nsHttp::Options) {
+    // place fin in a data frame even for 0 length messages for iterop
+  } else if (!mRequestBodyLenRemaining) {
+    // for other HTTP extension methods, rely on the content-length
+    // to determine whether or not to put fin on headers
+    SetSentFin(true);
+    firstFrameFlags |= Http2Session::kFlag_END_STREAM;
+  }
+
+  // split this one HEADERS frame up into N HEADERS + CONTINUATION frames if it exceeds the
+  // 2^14-1 limit for 1 frame. Do it by inserting header size gaps in the existing
+  // frame for the new headers and for the first one a priority field. There is
+  // no question this is ugly, but a 16KB HEADERS frame should be a long
+  // tail event, so this is really just for correctness and a nop in the base case.
+  //
+
+  MOZ_ASSERT(!mTxInlineFrameUsed);
+
+  uint32_t dataLength = compressedData.Length();
+  uint32_t maxFrameData = Http2Session::kMaxFrameData - 4; // 4 byes for priority
+  uint32_t numFrames = 1;
+
+  if (dataLength > maxFrameData) {
+    numFrames += ((dataLength - maxFrameData) + Http2Session::kMaxFrameData - 1) /
+      Http2Session::kMaxFrameData;
+    MOZ_ASSERT (numFrames > 1);
+  }
+
+  // note that we could still have 1 frame for 0 bytes of data. that's ok.
+
+  uint32_t messageSize = dataLength;
+  messageSize += 12; // header frame overhead
+  messageSize += (numFrames - 1) * 8; // continuation frames overhead
+
+  Http2Session::EnsureBuffer(mTxInlineFrame,
+                             dataLength + messageSize,
+                             mTxInlineFrameUsed,
+                             mTxInlineFrameSize);
+
+  mTxInlineFrameUsed += messageSize;
+  LOG3(("%p Generating %d bytes of HEADERS for stream 0x%X at priority %u frames %u\n",
+        this, mTxInlineFrameUsed, mStreamID, mPriority, numFrames));
+
+  uint32_t outputOffset = 0;
+  uint32_t compressedDataOffset = 0;
+  for (uint32_t idx = 0; idx < numFrames; ++idx) {
+    uint32_t flags, frameLen;
+    bool lastFrame = (idx == numFrames - 1);
+
+    flags = 0;
+    frameLen = maxFrameData;
+    if (!idx) {
+      flags |= firstFrameFlags;
+      // Only the first frame needs the 4-byte offset
+      maxFrameData = Http2Session::kMaxFrameData;
+    }
+    if (lastFrame) {
+      frameLen = dataLength;
+      flags |= Http2Session::kFlag_END_HEADERS;
+    }
+    dataLength -= frameLen;
+
+    mSession->CreateFrameHeader(
+      mTxInlineFrame.get() + outputOffset,
+      frameLen + (idx ? 0 : 4),
+      (idx) ? Http2Session::FRAME_TYPE_CONTINUATION : Http2Session::FRAME_TYPE_HEADERS,
+      flags, mStreamID);
+    outputOffset += 8;
+
+    if (!idx) {
+      uint32_t priority = PR_htonl(mPriority);
+      memcpy (mTxInlineFrame.get() + outputOffset, &priority, 4);
+      outputOffset += 4;
+    }
+
+    memcpy(mTxInlineFrame.get() + outputOffset,
+           compressedData.BeginReading() + compressedDataOffset, frameLen);
+    compressedDataOffset += frameLen;
+    outputOffset += frameLen;
+  }
+
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, compressedData.Length());
+
+  // The size of the input headers is approximate
+  uint32_t ratio =
+    compressedData.Length() * 100 /
+    (11 + mTransaction->RequestHead()->RequestURI().Length() +
+     mFlatHttpRequestHeaders.Length());
+
+  const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
+  int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
+  while (true) {
+    int32_t startIndex = crlfIndex + 2;
+
+    crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
+    if (crlfIndex == -1)
+      break;
+
+    int32_t colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
+                                                      crlfIndex - startIndex);
+    if (colonIndex == -1)
+      break;
+
+    nsDependentCSubstring name = Substring(beginBuffer + startIndex,
+                                           beginBuffer + colonIndex);
+    // all header names are lower case in spdy
+    ToLowerCase(name);
+
+    if (name.Equals("content-length")) {
+      nsCString *val = new nsCString();
+      int32_t valueIndex = colonIndex + 1;
+      while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
+        ++valueIndex;
+
+      nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
+                                          beginBuffer + crlfIndex);
+      val->Append(v);
+
+      int64_t len;
+      if (nsHttp::ParseInt64(val->get(), nullptr, &len))
+        mRequestBodyLenRemaining = len;
+      break;
+    }
+  }
+
+  mFlatHttpRequestHeaders.Truncate();
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
+  return NS_OK;
+}
+
+void
+Http2Stream::AdjustInitialWindow()
+{
+  // The default initial_window is sized for pushed streams. When we
+  // generate a client pulled stream we want to disable flow control for
+  // the stream with a window update. Do the same for pushed streams
+  // when they connect to a pull.
+
+  // >0 even numbered IDs are pushed streams.
+  // odd numbered IDs are pulled streams.
+  // 0 is the sink for a pushed stream.
+  Http2Stream *stream = this;
+  if (!mStreamID) {
+    MOZ_ASSERT(mPushSource);
+    if (!mPushSource)
+      return;
+    stream = mPushSource;
+    MOZ_ASSERT(stream->mStreamID);
+    MOZ_ASSERT(!(stream->mStreamID & 1)); // is a push stream
+
+    // If the pushed stream has recvd a FIN, there is no reason to update
+    // the window
+    if (stream->RecvdFin() || stream->RecvdReset())
+      return;
+  }
+
+  uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
+  Http2Session::EnsureBuffer(mTxInlineFrame,
+                             mTxInlineFrameUsed + 12,
+                             mTxInlineFrameUsed,
+                             mTxInlineFrameSize);
+  mTxInlineFrameUsed += 12;
+
+  mSession->CreateFrameHeader(packet, 4,
+                              Http2Session::FRAME_TYPE_WINDOW_UPDATE,
+                              0, stream->mStreamID);
+
+  MOZ_ASSERT(mClientReceiveWindow <= ASpdySession::kInitialRwin);
+  uint32_t bump = ASpdySession::kInitialRwin - mClientReceiveWindow;
+  mClientReceiveWindow += bump;
+  bump = PR_htonl(bump);
+  memcpy(packet + 8, &bump, 4);
+  LOG3(("AdjustInitialwindow increased flow control window %p 0x%X\n",
+        this, stream->mStreamID));
+}
+
+void
+Http2Stream::AdjustPushedPriority()
+{
+  // >0 even numbered IDs are pushed streams. odd numbered IDs are pulled streams.
+  // 0 is the sink for a pushed stream.
+
+  if (mStreamID || !mPushSource)
+    return;
+
+  MOZ_ASSERT(mPushSource->mStreamID && !(mPushSource->mStreamID & 1));
+
+  // If the pushed stream has recvd a FIN, there is no reason to update
+  // the window
+  if (mPushSource->RecvdFin() || mPushSource->RecvdReset())
+    return;
+
+  uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
+  Http2Session::EnsureBuffer(mTxInlineFrame,
+                             mTxInlineFrameUsed + 12,
+                             mTxInlineFrameUsed,
+                             mTxInlineFrameSize);
+  mTxInlineFrameUsed += 12;
+
+  mSession->CreateFrameHeader(packet, 4,
+                              Http2Session::FRAME_TYPE_PRIORITY, 0,
+                              mPushSource->mStreamID);
+
+  uint32_t newPriority = PR_htonl(mPriority);
+  mPushSource->SetPriority(newPriority);
+  memcpy(packet + 8, &newPriority, 4);
+
+  LOG3(("AdjustPushedPriority %p id 0x%X to %X\n", this, mPushSource->mStreamID,
+        newPriority));
+}
+
+void
+Http2Stream::UpdateTransportReadEvents(uint32_t count)
+{
+  mTotalRead += count;
+  mTransaction->OnTransportStatus(mSocketTransport,
+                                  NS_NET_STATUS_RECEIVING_FROM,
+                                  mTotalRead);
+}
+
+void
+Http2Stream::UpdateTransportSendEvents(uint32_t count)
+{
+  mTotalSent += count;
+
+  // normally on non-windows platform we use TCP autotuning for
+  // the socket buffers, and this works well (managing enough
+  // buffers for BDP while conserving memory) for HTTP even when
+  // it creates really deep queues. However this 'buffer bloat' is
+  // a problem for http/2 because it ruins the low latency properties
+  // necessary for PING and cancel to work meaningfully.
+  //
+  // If this stream represents a large upload, disable autotuning for
+  // the session and cap the send buffers by default at 128KB.
+  // (10Mbit/sec @ 100ms)
+  //
+  uint32_t bufferSize = gHttpHandler->SpdySendBufferSize();
+  if ((mTotalSent > bufferSize) && !mSetTCPSocketBuffer) {
+    mSetTCPSocketBuffer = 1;
+    mSocketTransport->SetSendBufferSize(bufferSize);
+  }
+
+  if (mUpstreamState != SENDING_FIN_STREAM)
+    mTransaction->OnTransportStatus(mSocketTransport,
+                                    NS_NET_STATUS_SENDING_TO,
+                                    mTotalSent);
+
+  if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
+    mSentWaitingFor = 1;
+    mTransaction->OnTransportStatus(mSocketTransport,
+                                    NS_NET_STATUS_WAITING_FOR,
+                                    0);
+  }
+}
+
+nsresult
+Http2Stream::TransmitFrame(const char *buf,
+                           uint32_t *countUsed,
+                           bool forceCommitment)
+{
+  // If TransmitFrame returns SUCCESS than all the data is sent (or at least
+  // buffered at the session level), if it returns WOULD_BLOCK then none of
+  // the data is sent.
+
+  // You can call this function with no data and no out parameter in order to
+  // flush internal buffers that were previously blocked on writing. You can
+  // of course feed new data to it as well.
+
+  LOG3(("Http2Stream::TransmitFrame %p inline=%d stream=%d",
+        this, mTxInlineFrameUsed, mTxStreamFrameSize));
+  if (countUsed)
+    *countUsed = 0;
+
+  if (!mTxInlineFrameUsed) {
+    MOZ_ASSERT(!buf);
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mTxInlineFrameUsed, "empty stream frame in transmit");
+  MOZ_ASSERT(mSegmentReader, "TransmitFrame with null mSegmentReader");
+  MOZ_ASSERT((buf && countUsed) || (!buf && !countUsed),
+             "TransmitFrame arguments inconsistent");
+
+  uint32_t transmittedCount;
+  nsresult rv;
+
+  // In the (relatively common) event that we have a small amount of data
+  // split between the inlineframe and the streamframe, then move the stream
+  // data into the inlineframe via copy in order to coalesce into one write.
+  // Given the interaction with ssl this is worth the small copy cost.
+  if (mTxStreamFrameSize && mTxInlineFrameUsed &&
+      mTxStreamFrameSize < Http2Session::kDefaultBufferSize &&
+      mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
+    LOG3(("Coalesce Transmit"));
+    memcpy (mTxInlineFrame + mTxInlineFrameUsed,
+            buf, mTxStreamFrameSize);
+    if (countUsed)
+      *countUsed += mTxStreamFrameSize;
+    mTxInlineFrameUsed += mTxStreamFrameSize;
+    mTxStreamFrameSize = 0;
+  }
+
+  rv =
+    mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed,
+                                        forceCommitment);
+
+  if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+    MOZ_ASSERT(!forceCommitment, "forceCommitment with WOULD_BLOCK");
+    mSession->TransactionHasDataToWrite(this);
+  }
+  if (NS_FAILED(rv))     // this will include WOULD_BLOCK
+    return rv;
+
+  // This function calls mSegmentReader->OnReadSegment to report the actual http/2
+  // bytes through to the session object and then the HttpConnection which calls
+  // the socket write function. It will accept all of the inline and stream
+  // data because of the above 'commitment' even if it has to buffer
+
+  rv = mSegmentReader->OnReadSegment(reinterpret_cast<char*>(mTxInlineFrame.get()),
+                                     mTxInlineFrameUsed,
+                                     &transmittedCount);
+  LOG3(("Http2Stream::TransmitFrame for inline session=%p "
+        "stream=%p result %x len=%d",
+        mSession, this, rv, transmittedCount));
+
+  MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
+             "inconsistent inline commitment result");
+
+  if (NS_FAILED(rv))
+    return rv;
+
+  MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed,
+             "inconsistent inline commitment count");
+
+  Http2Session::LogIO(mSession, this, "Writing from Inline Buffer",
+                       reinterpret_cast<char*>(mTxInlineFrame.get()),
+                       transmittedCount);
+
+  if (mTxStreamFrameSize) {
+    if (!buf) {
+      // this cannot happen
+      MOZ_ASSERT(false, "Stream transmit with null buf argument to "
+                 "TransmitFrame()");
+      LOG3(("Stream transmit with null buf argument to TransmitFrame()\n"));
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize,
+                                       &transmittedCount);
+
+    LOG3(("Http2Stream::TransmitFrame for regular session=%p "
+          "stream=%p result %x len=%d",
+          mSession, this, rv, transmittedCount));
+
+    MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
+               "inconsistent stream commitment result");
+
+    if (NS_FAILED(rv))
+      return rv;
+
+    MOZ_ASSERT(transmittedCount == mTxStreamFrameSize,
+               "inconsistent stream commitment count");
+
+    Http2Session::LogIO(mSession, this, "Writing from Transaction Buffer",
+                         buf, transmittedCount);
+
+    *countUsed += mTxStreamFrameSize;
+  }
+
+  // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
+  UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
+
+  mTxInlineFrameUsed = 0;
+  mTxStreamFrameSize = 0;
+
+  return NS_OK;
+}
+
+void
+Http2Stream::ChangeState(enum upstreamStateType newState)
+{
+  LOG3(("Http2Stream::ChangeState() %p from %X to %X",
+        this, mUpstreamState, newState));
+  mUpstreamState = newState;
+}
+
+void
+Http2Stream::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
+{
+  LOG3(("Http2Stream::GenerateDataFrameHeader %p len=%d last=%d",
+        this, dataLength, lastFrame));
+
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty");
+  MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty");
+
+  uint8_t frameFlags = 0;
+  if (lastFrame) {
+    frameFlags |= Http2Session::kFlag_END_STREAM;
+    if (dataLength)
+      SetSentFin(true);
+  }
+
+  mSession->CreateFrameHeader(mTxInlineFrame.get(),
+                              dataLength,
+                              Http2Session::FRAME_TYPE_DATA,
+                              frameFlags, mStreamID);
+
+  mTxInlineFrameUsed = 8;
+  mTxStreamFrameSize = dataLength;
+}
+
+// ConvertHeaders is used to convert the response headers
+// into HTTP/1 format and report some telemetry
+nsresult
+Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
+                                    nsACString &aHeadersIn,
+                                    nsACString &aHeadersOut)
+{
+  aHeadersOut.Truncate();
+  aHeadersOut.SetCapacity(aHeadersIn.Length() + 512);
+
+  nsresult rv =
+    decompressor->DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(aHeadersIn.BeginReading()),
+                                    aHeadersIn.Length(),
+                                    aHeadersOut);
+  if (NS_FAILED(rv)) {
+    LOG3(("Http2Stream::ConvertHeaders %p decode Error\n", this));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  nsAutoCString status;
+  decompressor->GetStatus(status);
+  if (status.IsEmpty()) {
+    LOG3(("Http2Stream::ConvertHeaders %p Error - no status\n", this));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  if (aHeadersIn.Length() && aHeadersOut.Length()) {
+    Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
+    uint32_t ratio =
+      aHeadersIn.Length() * 100 / aHeadersOut.Length();
+    Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
+  }
+
+  aHeadersIn.Truncate();
+  aHeadersOut.Append(NS_LITERAL_CSTRING("X-Firefox-Spdy: " NS_HTTP2_DRAFT_TOKEN "\r\n\r\n"));
+  LOG (("decoded response headers are:\n%s", aHeadersOut.BeginReading()));
+  return NS_OK;
+}
+
+// ConvertHeaders is used to convert the response headers
+// into HTTP/1 format and report some telemetry
+nsresult
+Http2Stream::ConvertPushHeaders(Http2Decompressor *decompressor,
+                                nsACString &aHeadersIn,
+                                nsACString &aHeadersOut)
+{
+  aHeadersOut.Truncate();
+  nsresult rv =
+    decompressor->DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(aHeadersIn.BeginReading()),
+                                    aHeadersIn.Length(),
+                                    aHeadersOut);
+  if (NS_FAILED(rv)) {
+    LOG3(("Http2Stream::ConvertPushHeaders %p Error\n", this));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  nsCString method;
+  decompressor->GetHost(mHeaderHost);
+  decompressor->GetScheme(mHeaderScheme);
+  decompressor->GetPath(mHeaderPath);
+
+  if (mHeaderHost.IsEmpty() || mHeaderScheme.IsEmpty() || mHeaderPath.IsEmpty()) {
+    LOG3(("Http2Stream::ConvertPushHeaders %p Error - missing required "
+          "host=%s scheme=%s path=%s\n", this, mHeaderHost.get(), mHeaderScheme.get(),
+          mHeaderPath.get()));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  decompressor->GetMethod(method);
+  if (!method.Equals(NS_LITERAL_CSTRING("GET"))) {
+    LOG3(("Http2Stream::ConvertPushHeaders %p Error - method not supported: %s\n",
+          this, method.get()));
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  aHeadersIn.Truncate();
+  LOG (("decoded push headers are:\n%s", aHeadersOut.BeginReading()));
+  return NS_OK;
+}
+
+void
+Http2Stream::Close(nsresult reason)
+{
+  mTransaction->Close(reason);
+}
+
+bool
+Http2Stream::AllowFlowControlledWrite()
+{
+  if (!mSession->ServerUsesFlowControl())
+    return true;
+
+  return (mSession->ServerSessionWindow() > 0) && (mServerReceiveWindow > 0);
+}
+
+void
+Http2Stream::UpdateServerReceiveWindow(int32_t delta)
+{
+  mServerReceiveWindow += delta;
+
+  if (mBlockedOnRwin && AllowFlowControlledWrite()) {
+    LOG3(("Http2Stream::UpdateServerReceived UnPause %p 0x%X "
+          "Open stream window\n", this, mStreamID));
+    mSession->TransactionHasDataToWrite(this);  }
+}
+
+void
+Http2Stream::SetPriority(uint32_t newVal)
+{
+  mPriority = std::min(newVal, 0x7fffffffU);
+}
+
+void
+Http2Stream::SetRecvdFin(bool aStatus)
+{
+  mRecvdFin = aStatus ? 1 : 0;
+  if (!aStatus)
+    return;
+
+  if (mState == OPEN || mState == RESERVED_BY_REMOTE) {
+    mState = CLOSED_BY_REMOTE;
+  } else if (mState == CLOSED_BY_LOCAL) {
+    mState = CLOSED;
+  }
+}
+
+void
+Http2Stream::SetSentFin(bool aStatus)
+{
+  mSentFin = aStatus ? 1 : 0;
+  if (!aStatus)
+    return;
+
+  if (mState == OPEN || mState == RESERVED_BY_REMOTE) {
+    mState = CLOSED_BY_LOCAL;
+  } else if (mState == CLOSED_BY_REMOTE) {
+    mState = CLOSED;
+  }
+}
+
+void
+Http2Stream::SetRecvdReset(bool aStatus)
+{
+  mRecvdReset = aStatus ? 1 : 0;
+  if (!aStatus)
+    return;
+  mState = CLOSED;
+}
+
+void
+Http2Stream::SetSentReset(bool aStatus)
+{
+  mSentReset = aStatus ? 1 : 0;
+  if (!aStatus)
+    return;
+  mState = CLOSED;
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentReader
+//-----------------------------------------------------------------------------
+
+nsresult
+Http2Stream::OnReadSegment(const char *buf,
+                           uint32_t count,
+                           uint32_t *countRead)
+{
+  LOG3(("Http2Stream::OnReadSegment %p count=%d state=%x",
+        this, count, mUpstreamState));
+
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader");
+
+  nsresult rv = NS_ERROR_UNEXPECTED;
+  uint32_t dataLength;
+
+  switch (mUpstreamState) {
+  case GENERATING_HEADERS:
+    // The buffer is the HTTP request stream, including at least part of the
+    // HTTP request header. This state's job is to build a HEADERS frame
+    // from the header information. count is the number of http bytes available
+    // (which may include more than the header), and in countRead we return
+    // the number of those bytes that we consume (i.e. the portion that are
+    // header bytes)
+
+    rv = ParseHttpRequestHeaders(buf, count, countRead);
+    if (NS_FAILED(rv))
+      return rv;
+    LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
+          this, *countRead, count, mAllHeadersSent));
+    if (mAllHeadersSent) {
+      SetHTTPState(OPEN);
+      AdjustInitialWindow();
+      // This version of TransmitFrame cannot block
+      rv = TransmitFrame(nullptr, nullptr, true);
+      ChangeState(GENERATING_BODY);
+      break;
+    }
+    MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data");
+    break;
+
+  case GENERATING_BODY:
+    // if there is session flow control and either the stream window is active and
+    // exhaused or the session window is exhausted then suspend
+    if (!AllowFlowControlledWrite()) {
+      *countRead = 0;
+      LOG3(("Http2Stream this=%p, id 0x%X request body suspended because "
+            "remote window is stream=%ld session=%ld.\n", this, mStreamID,
+            mServerReceiveWindow, mSession->ServerSessionWindow()));
+      mBlockedOnRwin = true;
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+    mBlockedOnRwin = false;
+
+    // The chunk is the smallest of: availableData, configured chunkSize,
+    // stream window, session window, or 14 bit framing limit.
+    // Its amazing we send anything at all.
+    dataLength = std::min(count, mChunkSize);
+
+    if (dataLength > Http2Session::kMaxFrameData)
+      dataLength = Http2Session::kMaxFrameData;
+
+    if (mSession->ServerUsesFlowControl()) {
+      if (dataLength > mSession->ServerSessionWindow())
+        dataLength = static_cast<uint32_t>(mSession->ServerSessionWindow());
+
+      if (dataLength > mServerReceiveWindow)
+        dataLength = static_cast<uint32_t>(mServerReceiveWindow);
+    }
+
+    LOG3(("Http2Stream this=%p id 0x%X send calculation "
+          "avail=%d chunksize=%d stream window=%d session window=%d "
+          "max frame=%d USING=%d\n", this, mStreamID,
+          count, mChunkSize, mServerReceiveWindow, mSession->ServerSessionWindow(),
+          Http2Session::kMaxFrameData, dataLength));
+
+    if (mSession->ServerUsesFlowControl()) {
+      mSession->DecrementServerSessionWindow(dataLength);
+      mServerReceiveWindow -= dataLength;
+    }
+
+    LOG3(("Http2Stream %p id %x request len remaining %d, "
+          "count avail %d, chunk used %d",
+          this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
+    if (dataLength > mRequestBodyLenRemaining)
+      return NS_ERROR_UNEXPECTED;
+    mRequestBodyLenRemaining -= dataLength;
+    GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
+    ChangeState(SENDING_BODY);
+    // NO BREAK
+
+  case SENDING_BODY:
+    MOZ_ASSERT(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
+    rv = TransmitFrame(buf, countRead, false);
+    MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
+               "Transmit Frame should be all or nothing");
+
+    LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
+          "Header is %d Body is %d.",
+          rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
+
+    // normalize a partial write with a WOULD_BLOCK into just a partial write
+    // as some code will take WOULD_BLOCK to mean an error with nothing
+    // written (e.g. nsHttpTransaction::ReadRequestSegment()
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
+      rv = NS_OK;
+
+    // If that frame was all sent, look for another one
+    if (!mTxInlineFrameUsed)
+      ChangeState(GENERATING_BODY);
+    break;
+
+  case SENDING_FIN_STREAM:
+    MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment");
+    break;
+
+  default:
+    MOZ_ASSERT(false, "Http2Stream::OnReadSegment non-write state");
+    break;
+  }
+
+  return rv;
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentWriter
+//-----------------------------------------------------------------------------
+
+nsresult
+Http2Stream::OnWriteSegment(char *buf,
+                            uint32_t count,
+                            uint32_t *countWritten)
+{
+  LOG3(("Http2Stream::OnWriteSegment %p count=%d state=%x 0x%X\n",
+        this, count, mUpstreamState, mStreamID));
+
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(mSegmentWriter);
+
+  if (!mPushSource)
+    return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
+
+  nsresult rv;
+  rv = mPushSource->GetBufferedData(buf, count, countWritten);
+  if (NS_FAILED(rv))
+    return rv;
+
+  mSession->ConnectPushedStream(this);
+  return NS_OK;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/Http2Stream.h
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_Http2Stream_h
+#define mozilla_net_Http2Stream_h
+
+#include "mozilla/Attributes.h"
+#include "nsAHttpTransaction.h"
+#include "nsISupportsPriority.h"
+
+namespace mozilla {
+namespace net {
+
+class Http2Session;
+class Http2Decompressor;
+
+class Http2Stream
+  : public nsAHttpSegmentReader
+  , public nsAHttpSegmentWriter
+{
+public:
+  NS_DECL_NSAHTTPSEGMENTREADER
+  NS_DECL_NSAHTTPSEGMENTWRITER
+
+  enum stateType {
+    IDLE,
+    RESERVED_BY_REMOTE,
+    OPEN,
+    CLOSED_BY_LOCAL,
+    CLOSED_BY_REMOTE,
+    CLOSED
+  };
+
+  const static int32_t kNormalPriority = 0x1000;
+  const static int32_t kWorstPriority = kNormalPriority + nsISupportsPriority::PRIORITY_LOWEST;
+  const static int32_t kBestPriority = kNormalPriority + nsISupportsPriority::PRIORITY_HIGHEST;
+
+  Http2Stream(nsAHttpTransaction *, Http2Session *, int32_t);
+
+  uint32_t StreamID() { return mStreamID; }
+  Http2PushedStream *PushSource() { return mPushSource; }
+
+  stateType HTTPState() { return mState; }
+  void SetHTTPState(stateType val) { mState = val; }
+
+  virtual nsresult ReadSegments(nsAHttpSegmentReader *,  uint32_t, uint32_t *);
+  virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
+  virtual bool DeferCleanupOnSuccess() { return false; }
+
+  const nsAFlatCString &Origin() const { return mOrigin; }
+
+  bool RequestBlockedOnRead()
+  {
+    return static_cast<bool>(mRequestBlockedOnRead);
+  }
+
+  bool HasRegisteredID() { return mStreamID != 0; }
+
+  nsAHttpTransaction *Transaction() { return mTransaction; }
+  virtual nsILoadGroupConnectionInfo *LoadGroupConnectionInfo()
+  {
+    return mTransaction ? mTransaction->LoadGroupConnectionInfo() : nullptr;
+  }
+
+  void Close(nsresult reason);
+
+  void SetRecvdFin(bool aStatus);
+  bool RecvdFin() { return mRecvdFin; }
+
+  void SetSentFin(bool aStatus);
+  bool SentFin() { return mSentFin; }
+
+  void SetRecvdReset(bool aStatus);
+  bool RecvdReset() { return mRecvdReset; }
+
+  void SetSentReset(bool aStatus);
+  bool SentReset() { return mSentReset; }
+
+  void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; }
+  bool CountAsActive() { return mCountAsActive; }
+
+  void SetAllHeadersReceived(bool aStatus) { mAllHeadersReceived = aStatus ? 1 : 0; }
+  bool AllHeadersReceived() { return mAllHeadersReceived; }
+
+  void UpdateTransportSendEvents(uint32_t count);
+  void UpdateTransportReadEvents(uint32_t count);
+
+  nsresult ConvertResponseHeaders(Http2Decompressor *, nsACString &, nsACString &);
+  nsresult ConvertPushHeaders(Http2Decompressor *, nsACString &, nsACString &);
+
+  bool AllowFlowControlledWrite();
+  void UpdateServerReceiveWindow(int32_t delta);
+  int64_t ServerReceiveWindow() { return mServerReceiveWindow; }
+
+  void DecrementClientReceiveWindow(uint32_t delta) {
+    mClientReceiveWindow -= delta;
+    mLocalUnacked += delta;
+  }
+
+  void IncrementClientReceiveWindow(uint32_t delta) {
+    mClientReceiveWindow += delta;
+    mLocalUnacked -= delta;
+  }
+
+  uint64_t LocalUnAcked() { return mLocalUnacked; }
+  int64_t  ClientReceiveWindow()  { return mClientReceiveWindow; }
+
+  bool     BlockedOnRwin() { return mBlockedOnRwin; }
+
+  uint32_t Priority() { return mPriority; }
+  void SetPriority(uint32_t);
+
+  // A pull stream has an implicit sink, a pushed stream has a sink
+  // once it is matched to a pull stream.
+  virtual bool HasSink() { return true; }
+
+  virtual ~Http2Stream();
+
+protected:
+  static void CreatePushHashKey(const nsCString &scheme,
+                                const nsCString &hostHeader,
+                                uint64_t serial,
+                                const nsCSubstring &pathInfo,
+                                nsCString &outOrigin,
+                                nsCString &outKey);
+
+  // These internal states track request generation
+  enum upstreamStateType {
+    GENERATING_HEADERS,
+    GENERATING_BODY,
+    SENDING_BODY,
+    SENDING_FIN_STREAM,
+    UPSTREAM_COMPLETE
+  };
+
+  uint32_t mStreamID;
+
+  // The session that this stream is a subset of
+  Http2Session *mSession;
+
+  nsCString     mOrigin;
+  nsCString     mHeaderHost;
+  nsCString     mHeaderScheme;
+  nsCString     mHeaderPath;
+
+  // Each stream goes from generating_headers to upstream_complete, perhaps
+  // looping on multiple instances of generating_body and
+  // sending_body for each frame in the upload.
+  enum upstreamStateType mUpstreamState;
+
+  // The HTTP/2 state for the stream from section 5.1
+  enum stateType mState;
+
+  // Flag is set when all http request headers have been read and ID is stable
+  uint32_t                     mAllHeadersSent       : 1;
+
+  // Flag is set when all http request headers have been read and ID is stable
+  uint32_t                     mAllHeadersReceived   : 1;
+
+  void     ChangeState(enum upstreamStateType);
+
+private:
+  friend class nsAutoPtr<Http2Stream>;
+
+  nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *);
+  void     AdjustPushedPriority();
+  void     AdjustInitialWindow();
+  nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment);
+  void     GenerateDataFrameHeader(uint32_t, bool);
+
+  // The underlying HTTP transaction. This pointer is used as the key
+  // in the Http2Session mStreamTransactionHash so it is important to
+  // keep a reference to it as long as this stream is a member of that hash.
+  // (i.e. don't change it or release it after it is set in the ctor).
+  nsRefPtr<nsAHttpTransaction> mTransaction;
+
+  // The underlying socket transport object is needed to propogate some events
+  nsISocketTransport         *mSocketTransport;
+
+  // These are temporary state variables to hold the argument to
+  // Read/WriteSegments so it can be accessed by On(read/write)segment
+  // further up the stack.
+  nsAHttpSegmentReader        *mSegmentReader;
+  nsAHttpSegmentWriter        *mSegmentWriter;
+
+  // The quanta upstream data frames are chopped into
+  uint32_t                    mChunkSize;
+
+  // Flag is set when the HTTP processor has more data to send
+  // but has blocked in doing so.
+  uint32_t                     mRequestBlockedOnRead : 1;
+
+  // Flag is set after the response frame bearing the fin bit has
+  // been processed. (i.e. after the server has closed).
+  uint32_t                     mRecvdFin             : 1;
+
+  // Flag is set after RST_STREAM has been received for this stream
+  uint32_t                     mRecvdReset           : 1;
+
+  // Flag is set after RST_STREAM has been generated for this stream
+  uint32_t                     mSentReset            : 1;
+
+  // Flag is set when stream is counted towards MAX_CONCURRENT streams in session
+  uint32_t                     mCountAsActive        : 1;
+
+  // Flag is set when a FIN has been placed on a data or header frame
+  // (i.e after the client has closed)
+  uint32_t                     mSentFin              : 1;
+
+  // Flag is set after the WAITING_FOR Transport event has been generated
+  uint32_t                     mSentWaitingFor       : 1;
+
+  // Flag is set after TCP send autotuning has been disabled
+  uint32_t                     mSetTCPSocketBuffer   : 1;
+
+  // The InlineFrame and associated data is used for composing control
+  // frames and data frame headers.
+  nsAutoArrayPtr<uint8_t>      mTxInlineFrame;
+  uint32_t                     mTxInlineFrameSize;
+  uint32_t                     mTxInlineFrameUsed;
+
+  // mTxStreamFrameSize tracks the progress of
+  // transmitting a request body data frame. The data frame itself
+  // is never copied into the spdy layer.
+  uint32_t                     mTxStreamFrameSize;
+
+  // Buffer for request header compression.
+  nsCString                    mFlatHttpRequestHeaders;
+
+  // Track the content-length of a request body so that we can
+  // place the fin flag on the last data packet instead of waiting
+  // for a stream closed indication. Relying on stream close results
+  // in an extra 0-length runt packet and seems to have some interop
+  // problems with the google servers.
+  int64_t                      mRequestBodyLenRemaining;
+
+  // 0 is highest.. up to 2^31 - 1 as lowest
+  uint32_t                     mPriority;
+
+  // mClientReceiveWindow, mServerReceiveWindow, and mLocalUnacked are for flow control.
+  // *window are signed because the race conditions in asynchronous SETTINGS
+  // messages can force them temporarily negative.
+
+  // mClientReceiveWindow is how much data the server will send without getting a
+  //   window update
+  int64_t                      mClientReceiveWindow;
+
+  // mServerReceiveWindow is how much data the client is allowed to send without
+  //   getting a window update
+  int64_t                      mServerReceiveWindow;
+
+  // LocalUnacked is the number of bytes received by the client but not
+  //   yet reflected in a window update. Sending that update will increment
+  //   ClientReceiveWindow
+  uint64_t                     mLocalUnacked;
+
+  // True when sending is suspended becuase the server receive window is
+  //   <= 0
+  bool                         mBlockedOnRwin;
+
+  // For Progress Events
+  uint64_t                     mTotalSent;
+  uint64_t                     mTotalRead;
+
+  // For Http2Push
+  Http2PushedStream *mPushSource;
+};
+
+} // namespace mozilla::net
+} // namespace mozilla
+
+#endif // mozilla_net_Http2Stream_h
--- a/netwerk/protocol/http/PSpdyPush.h
+++ b/netwerk/protocol/http/PSpdyPush.h
@@ -31,16 +31,17 @@
 
 class nsCString;
 
 namespace mozilla {
 namespace net {
 
 class SpdyPushedStream3;
 class SpdyPushedStream31;
+class Http2PushedStream;
 
 // One cache per load group
 class SpdyPushCache
 {
 public:
   // The cache holds only weak pointers - no references
   SpdyPushCache();
   virtual ~SpdyPushCache();
@@ -56,14 +57,22 @@ private:
 
 // for spdy/3.1
 public:
   bool               RegisterPushedStreamSpdy31(nsCString key,
                                                 SpdyPushedStream31 *stream);
   SpdyPushedStream31 *RemovePushedStreamSpdy31(nsCString key);
 private:
   nsDataHashtable<nsCStringHashKey, SpdyPushedStream31 *> mHashSpdy31;
+
+// for http/2
+public:
+  bool               RegisterPushedStreamHttp2(nsCString key,
+                                               Http2PushedStream *stream);
+  Http2PushedStream *RemovePushedStreamHttp2(nsCString key);
+private:
+  nsDataHashtable<nsCStringHashKey, Http2PushedStream *> mHashHttp2;
 };
 
 } // namespace mozilla::net
 } // namespace mozilla
 
 #endif // mozilla_net_SpdyPush_Public_h
--- a/netwerk/protocol/http/SpdySession3.cpp
+++ b/netwerk/protocol/http/SpdySession3.cpp
@@ -730,17 +730,17 @@ SpdySession3::GenerateSettings()
 
   uint8_t numberOfEntries = 0;
 
   // entries need to be listed in order by ID
   // 1st entry is bytes 12 to 19
   // 2nd entry is bytes 20 to 27
   // 3rd entry is bytes 28 to 35
 
-  if (!gHttpHandler->AllowSpdyPush()) {
+  if (!gHttpHandler->AllowPush()) {
   // announcing that we accept 0 incoming streams is done to
   // disable server push
     packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT;
     // The value portion of the setting pair is already initialized to 0
     numberOfEntries++;
   }
 
   nsRefPtr<nsHttpConnectionInfo> ci;
@@ -975,17 +975,17 @@ SpdySession3::HandleSynStream(SpdySessio
     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
 
   } else if (!associatedID) {
     // associated stream 0 will never find a match, but the spec requires a
     // PROTOCOL_ERROR in this specific case
     LOG3(("SpdySession3::HandleSynStream %p associated ID of 0 failed.\n", self));
     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
 
-  } else if (!gHttpHandler->AllowSpdyPush()) {
+  } else if (!gHttpHandler->AllowPush()) {
     // MAX_CONCURRENT_STREAMS of 0 in settings should have disabled push,
     // but some servers are buggy about that.. or the config could have
     // been updated after the settings frame was sent. In both cases just
     // reject the pushed stream as refused
     LOG3(("SpdySession3::HandleSynStream Push Recevied when Disabled\n"));
     self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
 
   } else if (!associatedStream) {
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -741,17 +741,17 @@ SpdySession31::GenerateSettings()
 
   uint8_t numberOfEntries = 0;
 
   // entries need to be listed in order by ID
   // 1st entry is bytes 12 to 19
   // 2nd entry is bytes 20 to 27
   // 3rd entry is bytes 28 to 35
 
-  if (!gHttpHandler->AllowSpdyPush()) {
+  if (!gHttpHandler->AllowPush()) {
     // announcing that we accept 0 incoming streams is done to
     // disable server push
     packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT;
     // The value portion of the setting pair is already initialized to 0
     numberOfEntries++;
   }
 
   nsRefPtr<nsHttpConnectionInfo> ci;
@@ -798,17 +798,17 @@ SpdySession31::GenerateSettings()
   packet[3] = CONTROL_TYPE_WINDOW_UPDATE;
   packet[7] = 8; // 8 data bytes after 8 byte header
 
   // 8 to 11 stay 0 bytes for id = 0
   memcpy(packet + 12, &sessionWindowBump, 4);
 
   LOG3(("Session Window increase at start of session %p %u\n",
         this, PR_ntohl(sessionWindowBump)));
-  LogIO(this, nullptr, "Session Window Bump ", packet, 12);
+  LogIO(this, nullptr, "Session Window Bump ", packet, 16);
 
 generateSettings_complete:
   FlushOutputQueue();
 }
 
 // perform a bunch of integrity checks on the stream.
 // returns true if passed, false (plus LOG and ABORT) if failed.
 bool
@@ -1012,17 +1012,17 @@ SpdySession31::HandleSynStream(SpdySessi
     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
 
   } else if (!associatedID) {
     // associated stream 0 will never find a match, but the spec requires a
     // PROTOCOL_ERROR in this specific case
     LOG3(("SpdySession31::HandleSynStream %p associated ID of 0 failed.\n", self));
     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
 
-  } else if (!gHttpHandler->AllowSpdyPush()) {
+  } else if (!gHttpHandler->AllowPush()) {
     // MAX_CONCURRENT_STREAMS of 0 in settings should have disabled push,
     // but some servers are buggy about that.. or the config could have
     // been updated after the settings frame was sent. In both cases just
     // reject the pushed stream as refused
     LOG3(("SpdySession31::HandleSynStream Push Recevied when Disabled\n"));
     self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
 
   } else if (!associatedStream) {
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/huff_incoming.txt
@@ -0,0 +1,257 @@
+         (  0) |11111111|11111111|11011110|0 [25]        1ffffbc [25]
+         (  1) |11111111|11111111|11011110|1 [25]        1ffffbd [25]
+         (  2) |11111111|11111111|11011111|0 [25]        1ffffbe [25]
+         (  3) |11111111|11111111|11011111|1 [25]        1ffffbf [25]
+         (  4) |11111111|11111111|11100000|0 [25]        1ffffc0 [25]
+         (  5) |11111111|11111111|11100000|1 [25]        1ffffc1 [25]
+         (  6) |11111111|11111111|11100001|0 [25]        1ffffc2 [25]
+         (  7) |11111111|11111111|11100001|1 [25]        1ffffc3 [25]
+         (  8) |11111111|11111111|11100010|0 [25]        1ffffc4 [25]
+         (  9) |11111111|11111111|11100010|1 [25]        1ffffc5 [25]
+         ( 10) |11111111|11111111|11100011|0 [25]        1ffffc6 [25]
+         ( 11) |11111111|11111111|11100011|1 [25]        1ffffc7 [25]
+         ( 12) |11111111|11111111|11100100|0 [25]        1ffffc8 [25]
+         ( 13) |11111111|11111111|11100100|1 [25]        1ffffc9 [25]
+         ( 14) |11111111|11111111|11100101|0 [25]        1ffffca [25]
+         ( 15) |11111111|11111111|11100101|1 [25]        1ffffcb [25]
+         ( 16) |11111111|11111111|11100110|0 [25]        1ffffcc [25]
+         ( 17) |11111111|11111111|11100110|1 [25]        1ffffcd [25]
+         ( 18) |11111111|11111111|11100111|0 [25]        1ffffce [25]
+         ( 19) |11111111|11111111|11100111|1 [25]        1ffffcf [25]
+         ( 20) |11111111|11111111|11101000|0 [25]        1ffffd0 [25]
+         ( 21) |11111111|11111111|11101000|1 [25]        1ffffd1 [25]
+         ( 22) |11111111|11111111|11101001|0 [25]        1ffffd2 [25]
+         ( 23) |11111111|11111111|11101001|1 [25]        1ffffd3 [25]
+         ( 24) |11111111|11111111|11101010|0 [25]        1ffffd4 [25]
+         ( 25) |11111111|11111111|11101010|1 [25]        1ffffd5 [25]
+         ( 26) |11111111|11111111|11101011|0 [25]        1ffffd6 [25]
+         ( 27) |11111111|11111111|11101011|1 [25]        1ffffd7 [25]
+         ( 28) |11111111|11111111|11101100|0 [25]        1ffffd8 [25]
+         ( 29) |11111111|11111111|11101100|1 [25]        1ffffd9 [25]
+         ( 30) |11111111|11111111|11101101|0 [25]        1ffffda [25]
+         ( 31) |11111111|11111111|11101101|1 [25]        1ffffdb [25]
+     ' ' ( 32) |0000 [4]                                        0 [4]
+     '!' ( 33) |11111111|1010 [12]                           ffa [12]
+     '"' ( 34) |1101010 [7]                                    6a [7]
+     '#' ( 35) |11111111|11010 [13]                         1ffa [13]
+     '$' ( 36) |11111111|111100 [14]                        3ffc [14]
+     '%' ( 37) |11110110|0 [9]                                1ec [9]
+     '&' ( 38) |11111110|00 [10]                             3f8 [10]
+     ''' ( 39) |11111111|11011 [13]                         1ffb [13]
+     '(' ( 40) |11110110|1 [9]                                1ed [9]
+     ')' ( 41) |11110111|0 [9]                                1ee [9]
+     '*' ( 42) |11111111|1011 [12]                           ffb [12]
+     '+' ( 43) |11111111|010 [11]                            7fa [11]
+     ',' ( 44) |100010 [6]                                     22 [6]
+     '-' ( 45) |100011 [6]                                     23 [6]
+     '.' ( 46) |100100 [6]                                     24 [6]
+     '/' ( 47) |1101011 [7]                                    6b [7]
+     '0' ( 48) |0001 [4]                                        1 [4]
+     '1' ( 49) |0010 [4]                                        2 [4]
+     '2' ( 50) |0011 [4]                                        3 [4]
+     '3' ( 51) |01000 [5]                                       8 [5]
+     '4' ( 52) |01001 [5]                                       9 [5]
+     '5' ( 53) |01010 [5]                                       a [5]
+     '6' ( 54) |100101 [6]                                     25 [6]
+     '7' ( 55) |100110 [6]                                     26 [6]
+     '8' ( 56) |01011 [5]                                       b [5]
+     '9' ( 57) |01100 [5]                                       c [5]
+     ':' ( 58) |01101 [5]                                       d [5]
+     ';' ( 59) |11110111|1 [9]                                1ef [9]
+     '<' ( 60) |11111111|11111010| [16]                     fffa [16]
+     '=' ( 61) |1101100 [7]                                    6c [7]
+     '>' ( 62) |11111111|11100 [13]                         1ffc [13]
+     '?' ( 63) |11111111|1100 [12]                           ffc [12]
+     '@' ( 64) |11111111|11111011| [16]                     fffb [16]
+     'A' ( 65) |1101101 [7]                                    6d [7]
+     'B' ( 66) |11101010| [8]                                  ea [8]
+     'C' ( 67) |11101011| [8]                                  eb [8]
+     'D' ( 68) |11101100| [8]                                  ec [8]
+     'E' ( 69) |11101101| [8]                                  ed [8]
+     'F' ( 70) |11101110| [8]                                  ee [8]
+     'G' ( 71) |100111 [6]                                     27 [6]
+     'H' ( 72) |11111000|0 [9]                                1f0 [9]
+     'I' ( 73) |11101111| [8]                                  ef [8]
+     'J' ( 74) |11110000| [8]                                  f0 [8]
+     'K' ( 75) |11111110|01 [10]                             3f9 [10]
+     'L' ( 76) |11111000|1 [9]                                1f1 [9]
+     'M' ( 77) |101000 [6]                                     28 [6]
+     'N' ( 78) |11110001| [8]                                  f1 [8]
+     'O' ( 79) |11110010| [8]                                  f2 [8]
+     'P' ( 80) |11111001|0 [9]                                1f2 [9]
+     'Q' ( 81) |11111110|10 [10]                             3fa [10]
+     'R' ( 82) |11111001|1 [9]                                1f3 [9]
+     'S' ( 83) |101001 [6]                                     29 [6]
+     'T' ( 84) |01110 [5]                                       e [5]
+     'U' ( 85) |11111010|0 [9]                                1f4 [9]
+     'V' ( 86) |11111010|1 [9]                                1f5 [9]
+     'W' ( 87) |11110011| [8]                                  f3 [8]
+     'X' ( 88) |11111110|11 [10]                             3fb [10]
+     'Y' ( 89) |11111011|0 [9]                                1f6 [9]
+     'Z' ( 90) |11111111|00 [10]                             3fc [10]
+     '[' ( 91) |11111111|011 [11]                            7fb [11]
+     '\' ( 92) |11111111|11101 [13]                         1ffd [13]
+     ']' ( 93) |11111111|100 [11]                            7fc [11]
+     '^' ( 94) |11111111|1111100 [15]                       7ffc [15]
+     '_' ( 95) |11111011|1 [9]                                1f7 [9]
+     '`' ( 96) |11111111|11111111|0 [17]                   1fffe [17]
+     'a' ( 97) |01111 [5]                                       f [5]
+     'b' ( 98) |1101110 [7]                                    6e [7]
+     'c' ( 99) |101010 [6]                                     2a [6]
+     'd' (100) |101011 [6]                                     2b [6]
+     'e' (101) |10000 [5]                                      10 [5]
+     'f' (102) |1101111 [7]                                    6f [7]
+     'g' (103) |1110000 [7]                                    70 [7]
+     'h' (104) |1110001 [7]                                    71 [7]
+     'i' (105) |101100 [6]                                     2c [6]
+     'j' (106) |11111100|0 [9]                                1f8 [9]
+     'k' (107) |11111100|1 [9]                                1f9 [9]
+     'l' (108) |1110010 [7]                                    72 [7]
+     'm' (109) |101101 [6]                                     2d [6]
+     'n' (110) |101110 [6]                                     2e [6]
+     'o' (111) |101111 [6]                                     2f [6]
+     'p' (112) |110000 [6]                                     30 [6]
+     'q' (113) |11111101|0 [9]                                1fa [9]
+     'r' (114) |110001 [6]                                     31 [6]
+     's' (115) |110010 [6]                                     32 [6]
+     't' (116) |110011 [6]                                     33 [6]
+     'u' (117) |110100 [6]                                     34 [6]
+     'v' (118) |1110011 [7]                                    73 [7]
+     'w' (119) |11110100| [8]                                  f4 [8]
+     'x' (120) |1110100 [7]                                    74 [7]
+     'y' (121) |11110101| [8]                                  f5 [8]
+     'z' (122) |11111101|1 [9]                                1fb [9]
+     '{' (123) |11111111|11111100| [16]                     fffc [16]
+     '|' (124) |11111111|111101 [14]                        3ffd [14]
+     '}' (125) |11111111|11111101| [16]                     fffd [16]
+     '~' (126) |11111111|11111110| [16]                     fffe [16]
+         (127) |11111111|11111111|11101110|0 [25]        1ffffdc [25]
+         (128) |11111111|11111111|11101110|1 [25]        1ffffdd [25]
+         (129) |11111111|11111111|11101111|0 [25]        1ffffde [25]
+         (130) |11111111|11111111|11101111|1 [25]        1ffffdf [25]
+         (131) |11111111|11111111|11110000|0 [25]        1ffffe0 [25]
+         (132) |11111111|11111111|11110000|1 [25]        1ffffe1 [25]
+         (133) |11111111|11111111|11110001|0 [25]        1ffffe2 [25]
+         (134) |11111111|11111111|11110001|1 [25]        1ffffe3 [25]
+         (135) |11111111|11111111|11110010|0 [25]        1ffffe4 [25]
+         (136) |11111111|11111111|11110010|1 [25]        1ffffe5 [25]
+         (137) |11111111|11111111|11110011|0 [25]        1ffffe6 [25]
+         (138) |11111111|11111111|11110011|1 [25]        1ffffe7 [25]
+         (139) |11111111|11111111|11110100|0 [25]        1ffffe8 [25]
+         (140) |11111111|11111111|11110100|1 [25]        1ffffe9 [25]
+         (141) |11111111|11111111|11110101|0 [25]        1ffffea [25]
+         (142) |11111111|11111111|11110101|1 [25]        1ffffeb [25]
+         (143) |11111111|11111111|11110110|0 [25]        1ffffec [25]
+         (144) |11111111|11111111|11110110|1 [25]        1ffffed [25]
+         (145) |11111111|11111111|11110111|0 [25]        1ffffee [25]
+         (146) |11111111|11111111|11110111|1 [25]        1ffffef [25]
+         (147) |11111111|11111111|11111000|0 [25]        1fffff0 [25]
+         (148) |11111111|11111111|11111000|1 [25]        1fffff1 [25]
+         (149) |11111111|11111111|11111001|0 [25]        1fffff2 [25]
+         (150) |11111111|11111111|11111001|1 [25]        1fffff3 [25]
+         (151) |11111111|11111111|11111010|0 [25]        1fffff4 [25]
+         (152) |11111111|11111111|11111010|1 [25]        1fffff5 [25]
+         (153) |11111111|11111111|11111011|0 [25]        1fffff6 [25]
+         (154) |11111111|11111111|11111011|1 [25]        1fffff7 [25]
+         (155) |11111111|11111111|11111100|0 [25]        1fffff8 [25]
+         (156) |11111111|11111111|11111100|1 [25]        1fffff9 [25]
+         (157) |11111111|11111111|11111101|0 [25]        1fffffa [25]
+         (158) |11111111|11111111|11111101|1 [25]        1fffffb [25]
+         (159) |11111111|11111111|11111110|0 [25]        1fffffc [25]
+         (160) |11111111|11111111|11111110|1 [25]        1fffffd [25]
+         (161) |11111111|11111111|11111111|0 [25]        1fffffe [25]
+         (162) |11111111|11111111|11111111|1 [25]        1ffffff [25]
+         (163) |11111111|11111111|10000000| [24]          ffff80 [24]
+         (164) |11111111|11111111|10000001| [24]          ffff81 [24]
+         (165) |11111111|11111111|10000010| [24]          ffff82 [24]
+         (166) |11111111|11111111|10000011| [24]          ffff83 [24]
+         (167) |11111111|11111111|10000100| [24]          ffff84 [24]
+         (168) |11111111|11111111|10000101| [24]          ffff85 [24]
+         (169) |11111111|11111111|10000110| [24]          ffff86 [24]
+         (170) |11111111|11111111|10000111| [24]          ffff87 [24]
+         (171) |11111111|11111111|10001000| [24]          ffff88 [24]
+         (172) |11111111|11111111|10001001| [24]          ffff89 [24]
+         (173) |11111111|11111111|10001010| [24]          ffff8a [24]
+         (174) |11111111|11111111|10001011| [24]          ffff8b [24]
+         (175) |11111111|11111111|10001100| [24]          ffff8c [24]
+         (176) |11111111|11111111|10001101| [24]          ffff8d [24]
+         (177) |11111111|11111111|10001110| [24]          ffff8e [24]
+         (178) |11111111|11111111|10001111| [24]          ffff8f [24]
+         (179) |11111111|11111111|10010000| [24]          ffff90 [24]
+         (180) |11111111|11111111|10010001| [24]          ffff91 [24]
+         (181) |11111111|11111111|10010010| [24]          ffff92 [24]
+         (182) |11111111|11111111|10010011| [24]          ffff93 [24]
+         (183) |11111111|11111111|10010100| [24]          ffff94 [24]
+         (184) |11111111|11111111|10010101| [24]          ffff95 [24]
+         (185) |11111111|11111111|10010110| [24]          ffff96 [24]
+         (186) |11111111|11111111|10010111| [24]          ffff97 [24]
+         (187) |11111111|11111111|10011000| [24]          ffff98 [24]
+         (188) |11111111|11111111|10011001| [24]          ffff99 [24]
+         (189) |11111111|11111111|10011010| [24]          ffff9a [24]
+         (190) |11111111|11111111|10011011| [24]          ffff9b [24]
+         (191) |11111111|11111111|10011100| [24]          ffff9c [24]
+         (192) |11111111|11111111|10011101| [24]          ffff9d [24]
+         (193) |11111111|11111111|10011110| [24]          ffff9e [24]
+         (194) |11111111|11111111|10011111| [24]          ffff9f [24]
+         (195) |11111111|11111111|10100000| [24]          ffffa0 [24]
+         (196) |11111111|11111111|10100001| [24]          ffffa1 [24]
+         (197) |11111111|11111111|10100010| [24]          ffffa2 [24]
+         (198) |11111111|11111111|10100011| [24]          ffffa3 [24]
+         (199) |11111111|11111111|10100100| [24]          ffffa4 [24]
+         (200) |11111111|11111111|10100101| [24]          ffffa5 [24]
+         (201) |11111111|11111111|10100110| [24]          ffffa6 [24]
+         (202) |11111111|11111111|10100111| [24]          ffffa7 [24]
+         (203) |11111111|11111111|10101000| [24]          ffffa8 [24]
+         (204) |11111111|11111111|10101001| [24]          ffffa9 [24]
+         (205) |11111111|11111111|10101010| [24]          ffffaa [24]
+         (206) |11111111|11111111|10101011| [24]          ffffab [24]
+         (207) |11111111|11111111|10101100| [24]          ffffac [24]
+         (208) |11111111|11111111|10101101| [24]          ffffad [24]
+         (209) |11111111|11111111|10101110| [24]          ffffae [24]
+         (210) |11111111|11111111|10101111| [24]          ffffaf [24]
+         (211) |11111111|11111111|10110000| [24]          ffffb0 [24]
+         (212) |11111111|11111111|10110001| [24]          ffffb1 [24]
+         (213) |11111111|11111111|10110010| [24]          ffffb2 [24]
+         (214) |11111111|11111111|10110011| [24]          ffffb3 [24]
+         (215) |11111111|11111111|10110100| [24]          ffffb4 [24]
+         (216) |11111111|11111111|10110101| [24]          ffffb5 [24]
+         (217) |11111111|11111111|10110110| [24]          ffffb6 [24]
+         (218) |11111111|11111111|10110111| [24]          ffffb7 [24]
+         (219) |11111111|11111111|10111000| [24]          ffffb8 [24]
+         (220) |11111111|11111111|10111001| [24]          ffffb9 [24]
+         (221) |11111111|11111111|10111010| [24]          ffffba [24]
+         (222) |11111111|11111111|10111011| [24]          ffffbb [24]
+         (223) |11111111|11111111|10111100| [24]          ffffbc [24]
+         (224) |11111111|11111111|10111101| [24]          ffffbd [24]
+         (225) |11111111|11111111|10111110| [24]          ffffbe [24]
+         (226) |11111111|11111111|10111111| [24]          ffffbf [24]
+         (227) |11111111|11111111|11000000| [24]          ffffc0 [24]
+         (228) |11111111|11111111|11000001| [24]          ffffc1 [24]
+         (229) |11111111|11111111|11000010| [24]          ffffc2 [24]
+         (230) |11111111|11111111|11000011| [24]          ffffc3 [24]
+         (231) |11111111|11111111|11000100| [24]          ffffc4 [24]
+         (232) |11111111|11111111|11000101| [24]          ffffc5 [24]
+         (233) |11111111|11111111|11000110| [24]          ffffc6 [24]
+         (234) |11111111|11111111|11000111| [24]          ffffc7 [24]
+         (235) |11111111|11111111|11001000| [24]          ffffc8 [24]
+         (236) |11111111|11111111|11001001| [24]          ffffc9 [24]
+         (237) |11111111|11111111|11001010| [24]          ffffca [24]
+         (238) |11111111|11111111|11001011| [24]          ffffcb [24]
+         (239) |11111111|11111111|11001100| [24]          ffffcc [24]
+         (240) |11111111|11111111|11001101| [24]          ffffcd [24]
+         (241) |11111111|11111111|11001110| [24]          ffffce [24]
+         (242) |11111111|11111111|11001111| [24]          ffffcf [24]
+         (243) |11111111|11111111|11010000| [24]          ffffd0 [24]
+         (244) |11111111|11111111|11010001| [24]          ffffd1 [24]
+         (245) |11111111|11111111|11010010| [24]          ffffd2 [24]
+         (246) |11111111|11111111|11010011| [24]          ffffd3 [24]
+         (247) |11111111|11111111|11010100| [24]          ffffd4 [24]
+         (248) |11111111|11111111|11010101| [24]          ffffd5 [24]
+         (249) |11111111|11111111|11010110| [24]          ffffd6 [24]
+         (250) |11111111|11111111|11010111| [24]          ffffd7 [24]
+         (251) |11111111|11111111|11011000| [24]          ffffd8 [24]
+         (252) |11111111|11111111|11011001| [24]          ffffd9 [24]
+         (253) |11111111|11111111|11011010| [24]          ffffda [24]
+         (254) |11111111|11111111|11011011| [24]          ffffdb [24]
+         (255) |11111111|11111111|11011100| [24]          ffffdc [24]
+     EOS (256) |11111111|11111111|11011101| [24]          ffffdd [24]
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/huff_outgoing.txt
@@ -0,0 +1,257 @@
+         (  0) |11111111|11111111|11110111|010 [27]      7ffffba [27]
+         (  1) |11111111|11111111|11110111|011 [27]      7ffffbb [27]
+         (  2) |11111111|11111111|11110111|100 [27]      7ffffbc [27]
+         (  3) |11111111|11111111|11110111|101 [27]      7ffffbd [27]
+         (  4) |11111111|11111111|11110111|110 [27]      7ffffbe [27]
+         (  5) |11111111|11111111|11110111|111 [27]      7ffffbf [27]
+         (  6) |11111111|11111111|11111000|000 [27]      7ffffc0 [27]
+         (  7) |11111111|11111111|11111000|001 [27]      7ffffc1 [27]
+         (  8) |11111111|11111111|11111000|010 [27]      7ffffc2 [27]
+         (  9) |11111111|11111111|11111000|011 [27]      7ffffc3 [27]
+         ( 10) |11111111|11111111|11111000|100 [27]      7ffffc4 [27]
+         ( 11) |11111111|11111111|11111000|101 [27]      7ffffc5 [27]
+         ( 12) |11111111|11111111|11111000|110 [27]      7ffffc6 [27]
+         ( 13) |11111111|11111111|11111000|111 [27]      7ffffc7 [27]
+         ( 14) |11111111|11111111|11111001|000 [27]      7ffffc8 [27]
+         ( 15) |11111111|11111111|11111001|001 [27]      7ffffc9 [27]
+         ( 16) |11111111|11111111|11111001|010 [27]      7ffffca [27]
+         ( 17) |11111111|11111111|11111001|011 [27]      7ffffcb [27]
+         ( 18) |11111111|11111111|11111001|100 [27]      7ffffcc [27]
+         ( 19) |11111111|11111111|11111001|101 [27]      7ffffcd [27]
+         ( 20) |11111111|11111111|11111001|110 [27]      7ffffce [27]
+         ( 21) |11111111|11111111|11111001|111 [27]      7ffffcf [27]
+         ( 22) |11111111|11111111|11111010|000 [27]      7ffffd0 [27]
+         ( 23) |11111111|11111111|11111010|001 [27]      7ffffd1 [27]
+         ( 24) |11111111|11111111|11111010|010 [27]      7ffffd2 [27]
+         ( 25) |11111111|11111111|11111010|011 [27]      7ffffd3 [27]
+         ( 26) |11111111|11111111|11111010|100 [27]      7ffffd4 [27]
+         ( 27) |11111111|11111111|11111010|101 [27]      7ffffd5 [27]
+         ( 28) |11111111|11111111|11111010|110 [27]      7ffffd6 [27]
+         ( 29) |11111111|11111111|11111010|111 [27]      7ffffd7 [27]
+         ( 30) |11111111|11111111|11111011|000 [27]      7ffffd8 [27]
+         ( 31) |11111111|11111111|11111011|001 [27]      7ffffd9 [27]
+     ' ' ( 32) |11101000| [8]                                  e8 [8]
+     '!' ( 33) |11111111|1100 [12]                           ffc [12]
+     '"' ( 34) |11111111|111010 [14]                        3ffa [14]
+     '#' ( 35) |11111111|1111100 [15]                       7ffc [15]
+     '$' ( 36) |11111111|1111101 [15]                       7ffd [15]
+     '%' ( 37) |100100 [6]                                     24 [6]
+     '&' ( 38) |1101110 [7]                                    6e [7]
+     ''' ( 39) |11111111|1111110 [15]                       7ffe [15]
+     '(' ( 40) |11111111|010 [11]                            7fa [11]
+     ')' ( 41) |11111111|011 [11]                            7fb [11]
+     '*' ( 42) |11111110|10 [10]                             3fa [10]
+     '+' ( 43) |11111111|100 [11]                            7fc [11]
+     ',' ( 44) |11101001| [8]                                  e9 [8]
+     '-' ( 45) |100101 [6]                                     25 [6]
+     '.' ( 46) |00100 [5]                                       4 [5]
+     '/' ( 47) |0000 [4]                                        0 [4]
+     '0' ( 48) |00101 [5]                                       5 [5]
+     '1' ( 49) |00110 [5]                                       6 [5]
+     '2' ( 50) |00111 [5]                                       7 [5]
+     '3' ( 51) |100110 [6]                                     26 [6]
+     '4' ( 52) |100111 [6]                                     27 [6]
+     '5' ( 53) |101000 [6]                                     28 [6]
+     '6' ( 54) |101001 [6]                                     29 [6]
+     '7' ( 55) |101010 [6]                                     2a [6]
+     '8' ( 56) |101011 [6]                                     2b [6]
+     '9' ( 57) |101100 [6]                                     2c [6]
+     ':' ( 58) |11110110|0 [9]                                1ec [9]
+     ';' ( 59) |11101010| [8]                                  ea [8]
+     '<' ( 60) |11111111|11111111|10 [18]                  3fffe [18]
+     '=' ( 61) |101101 [6]                                     2d [6]
+     '>' ( 62) |11111111|11111110|0 [17]                   1fffc [17]
+     '?' ( 63) |11110110|1 [9]                                1ed [9]
+     '@' ( 64) |11111111|111011 [14]                        3ffb [14]
+     'A' ( 65) |1101111 [7]                                    6f [7]
+     'B' ( 66) |11101011| [8]                                  eb [8]
+     'C' ( 67) |11101100| [8]                                  ec [8]
+     'D' ( 68) |11101101| [8]                                  ed [8]
+     'E' ( 69) |11101110| [8]                                  ee [8]
+     'F' ( 70) |1110000 [7]                                    70 [7]
+     'G' ( 71) |11110111|0 [9]                                1ee [9]
+     'H' ( 72) |11110111|1 [9]                                1ef [9]
+     'I' ( 73) |11111000|0 [9]                                1f0 [9]
+     'J' ( 74) |11111000|1 [9]                                1f1 [9]
+     'K' ( 75) |11111110|11 [10]                             3fb [10]
+     'L' ( 76) |11111001|0 [9]                                1f2 [9]
+     'M' ( 77) |11101111| [8]                                  ef [8]
+     'N' ( 78) |11111001|1 [9]                                1f3 [9]
+     'O' ( 79) |11111010|0 [9]                                1f4 [9]
+     'P' ( 80) |11111010|1 [9]                                1f5 [9]
+     'Q' ( 81) |11111011|0 [9]                                1f6 [9]
+     'R' ( 82) |11111011|1 [9]                                1f7 [9]
+     'S' ( 83) |11110000| [8]                                  f0 [8]
+     'T' ( 84) |11110001| [8]                                  f1 [8]
+     'U' ( 85) |11111100|0 [9]                                1f8 [9]
+     'V' ( 86) |11111100|1 [9]                                1f9 [9]
+     'W' ( 87) |11111101|0 [9]                                1fa [9]
+     'X' ( 88) |11111101|1 [9]                                1fb [9]
+     'Y' ( 89) |11111110|0 [9]                                1fc [9]
+     'Z' ( 90) |11111111|00 [10]                             3fc [10]
+     '[' ( 91) |11111111|111100 [14]                        3ffc [14]
+     '\' ( 92) |11111111|11111111|11111011|010 [27]      7ffffda [27]
+     ']' ( 93) |11111111|11100 [13]                         1ffc [13]
+     '^' ( 94) |11111111|111101 [14]                        3ffd [14]
+     '_' ( 95) |101110 [6]                                     2e [6]
+     '`' ( 96) |11111111|11111111|110 [19]                 7fffe [19]
+     'a' ( 97) |01000 [5]                                       8 [5]
+     'b' ( 98) |101111 [6]                                     2f [6]
+     'c' ( 99) |01001 [5]                                       9 [5]
+     'd' (100) |110000 [6]                                     30 [6]
+     'e' (101) |0001 [4]                                        1 [4]
+     'f' (102) |110001 [6]                                     31 [6]
+     'g' (103) |110010 [6]                                     32 [6]
+     'h' (104) |110011 [6]                                     33 [6]
+     'i' (105) |01010 [5]                                       a [5]
+     'j' (106) |1110001 [7]                                    71 [7]
+     'k' (107) |1110010 [7]                                    72 [7]
+     'l' (108) |01011 [5]                                       b [5]
+     'm' (109) |110100 [6]                                     34 [6]
+     'n' (110) |01100 [5]                                       c [5]
+     'o' (111) |01101 [5]                                       d [5]
+     'p' (112) |01110 [5]                                       e [5]
+     'q' (113) |11110010| [8]                                  f2 [8]
+     'r' (114) |01111 [5]                                       f [5]
+     's' (115) |10000 [5]                                      10 [5]
+     't' (116) |10001 [5]                                      11 [5]
+     'u' (117) |110101 [6]                                     35 [6]
+     'v' (118) |1110011 [7]                                    73 [7]
+     'w' (119) |110110 [6]                                     36 [6]
+     'x' (120) |11110011| [8]                                  f3 [8]
+     'y' (121) |11110100| [8]                                  f4 [8]
+     'z' (122) |11110101| [8]                                  f5 [8]
+     '{' (123) |11111111|11111110|1 [17]                   1fffd [17]
+     '|' (124) |11111111|101 [11]                            7fd [11]
+     '}' (125) |11111111|11111111|0 [17]                   1fffe [17]
+     '~' (126) |11111111|1101 [12]                           ffd [12]
+         (127) |11111111|11111111|11111011|011 [27]      7ffffdb [27]
+         (128) |11111111|11111111|11111011|100 [27]      7ffffdc [27]
+         (129) |11111111|11111111|11111011|101 [27]      7ffffdd [27]
+         (130) |11111111|11111111|11111011|110 [27]      7ffffde [27]
+         (131) |11111111|11111111|11111011|111 [27]      7ffffdf [27]
+         (132) |11111111|11111111|11111100|000 [27]      7ffffe0 [27]
+         (133) |11111111|11111111|11111100|001 [27]      7ffffe1 [27]
+         (134) |11111111|11111111|11111100|010 [27]      7ffffe2 [27]
+         (135) |11111111|11111111|11111100|011 [27]      7ffffe3 [27]
+         (136) |11111111|11111111|11111100|100 [27]      7ffffe4 [27]
+         (137) |11111111|11111111|11111100|101 [27]      7ffffe5 [27]
+         (138) |11111111|11111111|11111100|110 [27]      7ffffe6 [27]
+         (139) |11111111|11111111|11111100|111 [27]      7ffffe7 [27]
+         (140) |11111111|11111111|11111101|000 [27]      7ffffe8 [27]
+         (141) |11111111|11111111|11111101|001 [27]      7ffffe9 [27]
+         (142) |11111111|11111111|11111101|010 [27]      7ffffea [27]
+         (143) |11111111|11111111|11111101|011 [27]      7ffffeb [27]
+         (144) |11111111|11111111|11111101|100 [27]      7ffffec [27]
+         (145) |11111111|11111111|11111101|101 [27]      7ffffed [27]
+         (146) |11111111|11111111|11111101|110 [27]      7ffffee [27]
+         (147) |11111111|11111111|11111101|111 [27]      7ffffef [27]
+         (148) |11111111|11111111|11111110|000 [27]      7fffff0 [27]
+         (149) |11111111|11111111|11111110|001 [27]      7fffff1 [27]
+         (150) |11111111|11111111|11111110|010 [27]      7fffff2 [27]
+         (151) |11111111|11111111|11111110|011 [27]      7fffff3 [27]
+         (152) |11111111|11111111|11111110|100 [27]      7fffff4 [27]
+         (153) |11111111|11111111|11111110|101 [27]      7fffff5 [27]
+         (154) |11111111|11111111|11111110|110 [27]      7fffff6 [27]
+         (155) |11111111|11111111|11111110|111 [27]      7fffff7 [27]
+         (156) |11111111|11111111|11111111|000 [27]      7fffff8 [27]
+         (157) |11111111|11111111|11111111|001 [27]      7fffff9 [27]
+         (158) |11111111|11111111|11111111|010 [27]      7fffffa [27]
+         (159) |11111111|11111111|11111111|011 [27]      7fffffb [27]
+         (160) |11111111|11111111|11111111|100 [27]      7fffffc [27]
+         (161) |11111111|11111111|11111111|101 [27]      7fffffd [27]
+         (162) |11111111|11111111|11111111|110 [27]      7fffffe [27]
+         (163) |11111111|11111111|11111111|111 [27]      7ffffff [27]
+         (164) |11111111|11111111|11100000|00 [26]       3ffff80 [26]
+         (165) |11111111|11111111|11100000|01 [26]       3ffff81 [26]
+         (166) |11111111|11111111|11100000|10 [26]       3ffff82 [26]
+         (167) |11111111|11111111|11100000|11 [26]       3ffff83 [26]
+         (168) |11111111|11111111|11100001|00 [26]       3ffff84 [26]
+         (169) |11111111|11111111|11100001|01 [26]       3ffff85 [26]
+         (170) |11111111|11111111|11100001|10 [26]       3ffff86 [26]
+         (171) |11111111|11111111|11100001|11 [26]       3ffff87 [26]
+         (172) |11111111|11111111|11100010|00 [26]       3ffff88 [26]
+         (173) |11111111|11111111|11100010|01 [26]       3ffff89 [26]
+         (174) |11111111|11111111|11100010|10 [26]       3ffff8a [26]
+         (175) |11111111|11111111|11100010|11 [26]       3ffff8b [26]
+         (176) |11111111|11111111|11100011|00 [26]       3ffff8c [26]
+         (177) |11111111|11111111|11100011|01 [26]       3ffff8d [26]
+         (178) |11111111|11111111|11100011|10 [26]       3ffff8e [26]
+         (179) |11111111|11111111|11100011|11 [26]       3ffff8f [26]
+         (180) |11111111|11111111|11100100|00 [26]       3ffff90 [26]
+         (181) |11111111|11111111|11100100|01 [26]       3ffff91 [26]
+         (182) |11111111|11111111|11100100|10 [26]       3ffff92 [26]
+         (183) |11111111|11111111|11100100|11 [26]       3ffff93 [26]
+         (184) |11111111|11111111|11100101|00 [26]       3ffff94 [26]
+         (185) |11111111|11111111|11100101|01 [26]       3ffff95 [26]
+         (186) |11111111|11111111|11100101|10 [26]       3ffff96 [26]
+         (187) |11111111|11111111|11100101|11 [26]       3ffff97 [26]
+         (188) |11111111|11111111|11100110|00 [26]       3ffff98 [26]
+         (189) |11111111|11111111|11100110|01 [26]       3ffff99 [26]
+         (190) |11111111|11111111|11100110|10 [26]       3ffff9a [26]
+         (191) |11111111|11111111|11100110|11 [26]       3ffff9b [26]
+         (192) |11111111|11111111|11100111|00 [26]       3ffff9c [26]
+         (193) |11111111|11111111|11100111|01 [26]       3ffff9d [26]
+         (194) |11111111|11111111|11100111|10 [26]       3ffff9e [26]
+         (195) |11111111|11111111|11100111|11 [26]       3ffff9f [26]
+         (196) |11111111|11111111|11101000|00 [26]       3ffffa0 [26]
+         (197) |11111111|11111111|11101000|01 [26]       3ffffa1 [26]
+         (198) |11111111|11111111|11101000|10 [26]       3ffffa2 [26]
+         (199) |11111111|11111111|11101000|11 [26]       3ffffa3 [26]
+         (200) |11111111|11111111|11101001|00 [26]       3ffffa4 [26]
+         (201) |11111111|11111111|11101001|01 [26]       3ffffa5 [26]
+         (202) |11111111|11111111|11101001|10 [26]       3ffffa6 [26]
+         (203) |11111111|11111111|11101001|11 [26]       3ffffa7 [26]
+         (204) |11111111|11111111|11101010|00 [26]       3ffffa8 [26]
+         (205) |11111111|11111111|11101010|01 [26]       3ffffa9 [26]
+         (206) |11111111|11111111|11101010|10 [26]       3ffffaa [26]
+         (207) |11111111|11111111|11101010|11 [26]       3ffffab [26]
+         (208) |11111111|11111111|11101011|00 [26]       3ffffac [26]
+         (209) |11111111|11111111|11101011|01 [26]       3ffffad [26]
+         (210) |11111111|11111111|11101011|10 [26]       3ffffae [26]
+         (211) |11111111|11111111|11101011|11 [26]       3ffffaf [26]
+         (212) |11111111|11111111|11101100|00 [26]       3ffffb0 [26]
+         (213) |11111111|11111111|11101100|01 [26]       3ffffb1 [26]
+         (214) |11111111|11111111|11101100|10 [26]       3ffffb2 [26]
+         (215) |11111111|11111111|11101100|11 [26]       3ffffb3 [26]
+         (216) |11111111|11111111|11101101|00 [26]       3ffffb4 [26]
+         (217) |11111111|11111111|11101101|01 [26]       3ffffb5 [26]
+         (218) |11111111|11111111|11101101|10 [26]       3ffffb6 [26]
+         (219) |11111111|11111111|11101101|11 [26]       3ffffb7 [26]
+         (220) |11111111|11111111|11101110|00 [26]       3ffffb8 [26]
+         (221) |11111111|11111111|11101110|01 [26]       3ffffb9 [26]
+         (222) |11111111|11111111|11101110|10 [26]       3ffffba [26]
+         (223) |11111111|11111111|11101110|11 [26]       3ffffbb [26]
+         (224) |11111111|11111111|11101111|00 [26]       3ffffbc [26]
+         (225) |11111111|11111111|11101111|01 [26]       3ffffbd [26]
+         (226) |11111111|11111111|11101111|10 [26]       3ffffbe [26]
+         (227) |11111111|11111111|11101111|11 [26]       3ffffbf [26]
+         (228) |11111111|11111111|11110000|00 [26]       3ffffc0 [26]
+         (229) |11111111|11111111|11110000|01 [26]       3ffffc1 [26]
+         (230) |11111111|11111111|11110000|10 [26]       3ffffc2 [26]
+         (231) |11111111|11111111|11110000|11 [26]       3ffffc3 [26]
+         (232) |11111111|11111111|11110001|00 [26]       3ffffc4 [26]
+         (233) |11111111|11111111|11110001|01 [26]       3ffffc5 [26]
+         (234) |11111111|11111111|11110001|10 [26]       3ffffc6 [26]
+         (235) |11111111|11111111|11110001|11 [26]       3ffffc7 [26]
+         (236) |11111111|11111111|11110010|00 [26]       3ffffc8 [26]
+         (237) |11111111|11111111|11110010|01 [26]       3ffffc9 [26]
+         (238) |11111111|11111111|11110010|10 [26]       3ffffca [26]
+         (239) |11111111|11111111|11110010|11 [26]       3ffffcb [26]
+         (240) |11111111|11111111|11110011|00 [26]       3ffffcc [26]
+         (241) |11111111|11111111|11110011|01 [26]       3ffffcd [26]
+         (242) |11111111|11111111|11110011|10 [26]       3ffffce [26]
+         (243) |11111111|11111111|11110011|11 [26]       3ffffcf [26]
+         (244) |11111111|11111111|11110100|00 [26]       3ffffd0 [26]
+         (245) |11111111|11111111|11110100|01 [26]       3ffffd1 [26]
+         (246) |11111111|11111111|11110100|10 [26]       3ffffd2 [26]
+         (247) |11111111|11111111|11110100|11 [26]       3ffffd3 [26]
+         (248) |11111111|11111111|11110101|00 [26]       3ffffd4 [26]
+         (249) |11111111|11111111|11110101|01 [26]       3ffffd5 [26]
+         (250) |11111111|11111111|11110101|10 [26]       3ffffd6 [26]
+         (251) |11111111|11111111|11110101|11 [26]       3ffffd7 [26]
+         (252) |11111111|11111111|11110110|00 [26]       3ffffd8 [26]
+         (253) |11111111|11111111|11110110|01 [26]       3ffffd9 [26]
+         (254) |11111111|11111111|11110110|10 [26]       3ffffda [26]
+         (255) |11111111|11111111|11110110|11 [26]       3ffffdb [26]
+     EOS (256) |11111111|11111111|11110111|00 [26]       3ffffdc [26]
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/make_incoming_tables.py
@@ -0,0 +1,151 @@
+# This script exists to auto-generate Http2HuffmanIncoming.h from the table
+# contained in the HPACK spec. It's pretty simple to run:
+#   python make_incoming_tables.py < huff_incoming.txt > Http2HuffmanIncoming.h
+# where huff_incoming.txt is copy/pasted text from the latest version of the
+# HPACK spec, with all non-relevant lines removed (the most recent version
+# of huff_incoming.txt also lives in this directory as an example).
+import sys
+
+def char_cmp(x, y):
+    rv = cmp(x['nbits'], y['nbits'])
+    if not rv:
+        rv = cmp(x['bpat'], y['bpat'])
+    if not rv:
+        rv = cmp(x['ascii'], y['ascii'])
+    return rv
+
+characters = []
+
+for line in sys.stdin:
+    line = line.rstrip()
+    obracket = line.rfind('[')
+    nbits = int(line[obracket + 1:-1])
+
+    ascii = int(line[10:13].strip())
+
+    bar = line.find('|', 9)
+    obracket = line.find('[', bar)
+    bpat = line[bar + 1:obracket - 1].strip().rstrip('|')
+
+    characters.append({'ascii': ascii, 'nbits': nbits, 'bpat': bpat})
+
+characters.sort(cmp=char_cmp)
+raw_entries = []
+for c in characters:
+    raw_entries.append((c['ascii'], c['bpat']))
+
+class DefaultList(list):
+    def __init__(self, default=None):
+        self.__default = default
+
+    def __ensure_size(self, sz):
+        while sz > len(self):
+            self.append(self.__default)
+
+    def __getitem__(self, idx):
+        self.__ensure_size(idx + 1)
+        rv = super(DefaultList, self).__getitem__(idx)
+        return rv
+
+    def __setitem__(self, idx, val):
+        self.__ensure_size(idx + 1)
+        super(DefaultList, self).__setitem__(idx, val)
+
+def expand_to_8bit(bstr):
+    while len(bstr) < 8:
+        bstr += '0'
+    return int(bstr, 2)
+
+table = DefaultList()
+for r in raw_entries:
+    ascii, bpat = r
+    ascii = int(ascii)
+    bstrs = bpat.split('|')
+    curr_table = table
+    while len(bstrs) > 1:
+        idx = expand_to_8bit(bstrs[0])
+        if curr_table[idx] is None:
+            curr_table[idx] = DefaultList()
+        curr_table = curr_table[idx]
+        bstrs.pop(0)
+
+    idx = expand_to_8bit(bstrs[0])
+    curr_table[idx] = {'prefix_len': len(bstrs[0]),
+                        'mask': int(bstrs[0], 2),
+                        'value': ascii}
+
+def make_entry_list(table, max_prefix_len):
+    if max_prefix_len == 8:
+        return table
+    return [t for t in table if isinstance(t, dict) and t['prefix_len'] > 0]
+
+def output_table(table, name_suffix=''):
+    max_prefix_len = 0
+    for i, t in enumerate(table):
+        if isinstance(t, dict):
+            if t['prefix_len'] > max_prefix_len:
+                max_prefix_len = t['prefix_len']
+        elif t is not None:
+            output_table(t, '%s%s' % (name_suffix, i))
+
+    tablename = 'HuffmanIncoming%s' % (name_suffix if name_suffix else 'Root',)
+    entriestable = tablename.replace('HuffmanIncoming', 'HuffmanIncomingEntries')
+    sys.stdout.write('static HuffmanIncomingEntry %s[] = {\n' % (entriestable,))
+    entries = make_entry_list(table, max_prefix_len)
+    prefix_len = 0
+    value = 0
+    ptr = 'nullptr'
+    for i, t in enumerate(entries):
+        if isinstance(t, dict):
+            prefix_len = t['prefix_len']
+            value = t['value']
+            ptr = 'nullptr'
+        elif t is not None:
+            prefix_len = 0
+            value = 0
+            subtable = '%s%s' % (name_suffix, i)
+            ptr = '&HuffmanIncoming%s' % (subtable,)
+        sys.stdout.write('  { %s, %s, %s }' %
+                         (prefix_len, value, ptr))
+        if i < (len(table) - 1):
+            sys.stdout.write(',')
+        sys.stdout.write('\n')
+    sys.stdout.write('};\n')
+    sys.stdout.write('\n')
+    sys.stdout.write('static HuffmanIncomingTable %s = {\n' % (tablename,))
+    sys.stdout.write('  %s,\n' % (max_prefix_len,))
+    sys.stdout.write('  %s\n' % (entriestable,))
+    sys.stdout.write('};\n')
+    sys.stdout.write('\n')
+
+sys.stdout.write('''/*
+ * THIS FILE IS AUTO-GENERATED. DO NOT EDIT!
+ */
+#ifndef mozilla__net__Http2HuffmanIncoming_h
+#define mozilla__net__Http2HuffmanIncoming_h
+
+namespace mozilla {
+namespace net {
+
+struct HuffmanIncomingTable;
+
+struct HuffmanIncomingEntry {
+  uint8_t mPrefixLen;
+  uint16_t mValue;
+  HuffmanIncomingTable *mPtr;
+};
+
+struct HuffmanIncomingTable {
+  uint8_t mPrefixLen;
+  HuffmanIncomingEntry *mEntries;
+};
+
+''')
+
+output_table(table)
+
+sys.stdout.write('''} // namespace net
+} // namespace mozilla
+
+#endif // mozilla__net__Http2HuffmanIncoming_h
+''')
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/make_outgoing_tables.py
@@ -0,0 +1,57 @@
+# This script exists to auto-generate Http2HuffmanOutgoing.h from the table
+# contained in the HPACK spec. It's pretty simple to run:
+#   python make_outgoing_tables.py < huff_outgoing.txt > Http2HuffmanOutgoing.h
+# where huff_outgoing.txt is copy/pasted text from the latest version of the
+# HPACK spec, with all non-relevant lines removed (the most recent version
+# of huff_outgoing.txt also lives in this directory as an example).
+import sys
+
+sys.stdout.write('''/*
+ * THIS FILE IS AUTO-GENERATED. DO NOT EDIT!
+ */
+#ifndef mozilla__net__Http2HuffmanOutgoing_h
+#define mozilla__net__Http2HuffmanOutging_h
+
+namespace mozilla {
+namespace net {
+
+struct HuffmanOutgoingEntry {
+  uint8_t mLength;
+  uint32_t mValue;
+};
+
+static HuffmanOutgoingEntry HuffmanOutgoing[] = {
+''')
+
+entries = []
+for line in sys.stdin:
+    line = line.strip()
+    obracket = line.rfind('[')
+    nbits = int(line[obracket + 1:-1])
+
+    encend = obracket - 1
+    hexits = nbits / 4
+    if hexits * 4 != nbits:
+        hexits += 1
+
+    enc = line[encend - hexits:encend]
+    val = int(enc, 16)
+
+    entries.append({'length': nbits, 'value': val})
+
+line = []
+for i, e in enumerate(entries):
+    sys.stdout.write('  { %s, 0x%08x }' %
+                     (e['length'], e['value']))
+    if i < (len(entries) - 1):
+        sys.stdout.write(',')
+    sys.stdout.write('\n')
+
+sys.stdout.write('''};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla__net__Http2HuffmanOutgoing_h
+''')
+
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -39,16 +39,20 @@ EXPORTS.mozilla.net += [
 
 # ASpdySession.cpp and nsHttpAuthCache cannot be built in unified mode because
 # they use plarena.h.
 # The rest of these files cannot be built in unified mode because they want to
 # force NSPR logging.
 SOURCES += [
     'ASpdySession.cpp',
     'ConnectionDiagnostics.cpp',
+    'Http2Compression.cpp',
+    'Http2Push.cpp',
+    'Http2Session.cpp',
+    'Http2Stream.cpp',
     'HttpBaseChannel.cpp',
     'HttpChannelChild.cpp',
     'HttpChannelParent.cpp',
     'HttpChannelParentListener.cpp',
     'HttpInfo.cpp',
     'nsHttp.cpp',
     'nsHttpActivityDistributor.cpp',
     'nsHttpAuthCache.cpp',
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -21,21 +21,29 @@
 namespace mozilla {
 
 class Mutex;
 
 namespace net {
     enum {
         SPDY_VERSION_2_REMOVED = 2,
         SPDY_VERSION_3 = 3,
-        SPDY_VERSION_31 = 4
+        SPDY_VERSION_31 = 4,
+
+        // leave room for official versions. telem goes to 48
+        // 24 was a internal spdy/3.1
+        // 25 was spdy/4a2
+        HTTP2_VERSION_DRAFT08 = 26
     };
 
 typedef uint8_t nsHttpVersion;
 
+#define NS_HTTP2_DRAFT_VERSION HTTP2_VERSION_DRAFT08
+#define NS_HTTP2_DRAFT_TOKEN "HTTP-draft-08/2.0"
+
 //-----------------------------------------------------------------------------
 // http connection capabilities
 //-----------------------------------------------------------------------------
 
 #define NS_HTTP_ALLOW_KEEPALIVE      (1<<0)
 #define NS_HTTP_ALLOW_PIPELINING     (1<<1)
 
 // a transaction with this caps flag will continue to own the connection,
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -18,16 +18,17 @@
 #include "nsISocketTransport.h"
 #include "nsISSLSocketControl.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/net/DashboardTypes.h"
 #include "NullHttpTransaction.h"
 #include "nsITransport.h"
 #include "nsISocketTransportService.h"
 #include <algorithm>
+#include "Http2Compression.h"
 
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
@@ -148,16 +149,17 @@ nsHttpConnectionMgr::Shutdown()
             NS_WARNING("unable to post SHUTDOWN message");
             return rv;
         }
     }
 
     // wait for shutdown event to complete
     while (!shutdown)
         NS_ProcessNextEvent(NS_GetCurrentThread());
+    Http2CompressionCleanup();
 
     return NS_OK;
 }
 
 nsresult
 nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, int32_t iparam, void *vparam)
 {
     EnsureSocketThreadTarget();
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -185,19 +185,21 @@ nsHttpHandler::nsHttpHandler()
     , mDoNotTrackEnabled(false)
     , mDoNotTrackValue(1)
     , mTelemetryEnabled(false)
     , mAllowExperiments(true)
     , mHandlerActive(false)
     , mEnableSpdy(false)
     , mSpdyV3(true)
     , mSpdyV31(true)
+    , mHttp2DraftEnabled(true)
+    , mEnforceHttp2TlsProfile(true)
     , mCoalesceSpdy(true)
     , mSpdyPersistentSettings(false)
-    , mAllowSpdyPush(true)
+    , mAllowPush(true)
     , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
     , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
     , mSpdyPushAllowance(32768)
     , mSpdyPingThreshold(PR_SecondsToInterval(58))
     , mSpdyPingTimeout(PR_SecondsToInterval(8))
     , mConnectTimeout(90000)
     , mBypassCacheLockThreshold(250.0)
     , mParallelSpeculativeConnectLimit(6)
@@ -1159,16 +1161,28 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
     }
 
     if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v3-1"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v3-1"), &cVar);
         if (NS_SUCCEEDED(rv))
             mSpdyV31 = cVar;
     }
 
+    if (PREF_CHANGED(HTTP_PREF("spdy.enabled.http2draft"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.http2draft"), &cVar);
+        if (NS_SUCCEEDED(rv))
+            mHttp2DraftEnabled = cVar;
+    }
+
+    if (PREF_CHANGED(HTTP_PREF("spdy.enforce-tls-profile"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("spdy.enforce-tls-profile"), &cVar);
+        if (NS_SUCCEEDED(rv))
+            mEnforceHttp2TlsProfile = cVar;
+    }
+
     if (PREF_CHANGED(HTTP_PREF("spdy.coalesce-hostnames"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("spdy.coalesce-hostnames"), &cVar);
         if (NS_SUCCEEDED(rv))
             mCoalesceSpdy = cVar;
     }
 
     if (PREF_CHANGED(HTTP_PREF("spdy.persistent-settings"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("spdy.persistent-settings"),
@@ -1179,19 +1193,20 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
 
     if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.timeout"), &val);
         if (NS_SUCCEEDED(rv))
             mSpdyTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
     }
 
     if (PREF_CHANGED(HTTP_PREF("spdy.chunk-size"))) {
+        // keep this within http/2 ranges of 1 to 2^14-1
         rv = prefs->GetIntPref(HTTP_PREF("spdy.chunk-size"), &val);
         if (NS_SUCCEEDED(rv))
-            mSpdySendingChunkSize = (uint32_t) clamped(val, 1, 0x7fffffff);
+            mSpdySendingChunkSize = (uint32_t) clamped(val, 1, 0x3fff);
     }
 
     // The amount of idle seconds on a spdy connection before initiating a
     // server ping. 0 will disable.
     if (PREF_CHANGED(HTTP_PREF("spdy.ping-threshold"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-threshold"), &val);
         if (NS_SUCCEEDED(rv))
             mSpdyPingThreshold =
@@ -1206,17 +1221,17 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
             mSpdyPingTimeout =
                 PR_SecondsToInterval((uint16_t) clamped(val, 0, 0x7fffffff));
     }
 
     if (PREF_CHANGED(HTTP_PREF("spdy.allow-push"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("spdy.allow-push"),
                                 &cVar);
         if (NS_SUCCEEDED(rv))
-            mAllowSpdyPush = cVar;
+            mAllowPush = cVar;
     }
 
     if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val);
         if (NS_SUCCEEDED(rv)) {
             mSpdyPushAllowance =
                 static_cast<uint32_t>
                 (clamped(val, 1024, static_cast<int32_t>(ASpdySession::kInitialRwin)));
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -93,24 +93,26 @@ public:
 
     bool           IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
     bool           IsTelemetryEnabled() { return mTelemetryEnabled; }
     bool           AllowExperiments() { return mTelemetryEnabled && mAllowExperiments; }
 
     bool           IsSpdyEnabled() { return mEnableSpdy; }
     bool           IsSpdyV3Enabled() { return mSpdyV3; }
     bool           IsSpdyV31Enabled() { return mSpdyV31; }
+    bool           IsHttp2DraftEnabled() { return mHttp2DraftEnabled; }
+    bool           EnforceHttp2TlsProfile() { return mEnforceHttp2TlsProfile; }
     bool           CoalesceSpdy() { return mCoalesceSpdy; }
     bool           UseSpdyPersistentSettings() { return mSpdyPersistentSettings; }
     uint32_t       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
     uint32_t       SpdySendBufferSize()      { return mSpdySendBufferSize; }
     uint32_t       SpdyPushAllowance()       { return mSpdyPushAllowance; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
-    bool           AllowSpdyPush()   { return mAllowSpdyPush; }
+    bool           AllowPush()   { return mAllowPush; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
     uint32_t       ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
     bool           CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
     double         BypassCacheLockThreshold() { return mBypassCacheLockThreshold; }
 
     bool           UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
     uint16_t       RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
     uint32_t       RequestTokenBucketHz() { return mRequestTokenBucketHz; }
@@ -404,32 +406,36 @@ private:
     // Persistent HTTPS caching flag
     bool           mEnablePersistentHttpsCaching;
 
     // For broadcasting tracking preference
     bool           mDoNotTrackEnabled;
     uint8_t        mDoNotTrackValue;
 
     // Whether telemetry is reported or not
-    bool           mTelemetryEnabled;
+    uint32_t           mTelemetryEnabled : 1;
 
     // The value of network.allow-experiments
-    bool           mAllowExperiments;
+    uint32_t           mAllowExperiments : 1;
 
     // true in between init and shutdown states
-    bool           mHandlerActive;
+    uint32_t           mHandlerActive : 1;
+
+    uint32_t           mEnableSpdy : 1;
+    uint32_t           mSpdyV3 : 1;
+    uint32_t           mSpdyV31 : 1;
+    uint32_t           mHttp2DraftEnabled : 1;
+    uint32_t           mEnforceHttp2TlsProfile : 1;
+    uint32_t           mCoalesceSpdy : 1;
+    uint32_t           mSpdyPersistentSettings : 1;
+    uint32_t           mAllowPush : 1;
 
     // Try to use SPDY features instead of HTTP/1.1 over SSL
-    SpdyInformation mSpdyInfo;
-    bool           mEnableSpdy;
-    bool           mSpdyV3;
-    bool           mSpdyV31;
-    bool           mCoalesceSpdy;
-    bool           mSpdyPersistentSettings;
-    bool           mAllowSpdyPush;
+    SpdyInformation    mSpdyInfo;
+
     uint32_t       mSpdySendingChunkSize;
     uint32_t       mSpdySendBufferSize;
     uint32_t       mSpdyPushAllowance;
     PRIntervalTime mSpdyPingThreshold;
     PRIntervalTime mSpdyPingTimeout;
 
     // The maximum amount of time to wait for socket transport to be
     // established. In milliseconds.