Bug 1296280 (part 2) - Add pref for our SETTINGS_MAX_TABLE_SIZE. r?mcmanus draft
authorNicholas Hurley <hurley@todesschaf.org>
Wed, 24 Aug 2016 14:35:43 -0700
changeset 416121 238521d95e5fdbae495e1af5aa3bfa881e69b809
parent 416120 cecab777fb8f69e8f283576c045e2356117373c9
child 416122 a07353273493696c596610d26320fa9b9ad31c36
push id30030
push userbmo:hurley@todesschaf.org
push dateWed, 21 Sep 2016 13:34:37 +0000
reviewersmcmanus
bugs1296280
milestone51.0a1
Bug 1296280 (part 2) - Add pref for our SETTINGS_MAX_TABLE_SIZE. r?mcmanus MozReview-Commit-ID: 44CYEvJOno0
netwerk/protocol/http/Http2Compression.cpp
netwerk/protocol/http/Http2Compression.h
netwerk/protocol/http/Http2Session.cpp
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -276,16 +276,17 @@ nvFIFO::operator[] (size_t index) const
   }
   return static_cast<nvPair *>(gStaticHeaders->ObjectAt(index));
 }
 
 Http2BaseCompressor::Http2BaseCompressor()
   : mOutput(nullptr)
   , mMaxBuffer(kDefaultMaxBuffer)
   , mMaxBufferSetting(kDefaultMaxBuffer)
+  , mSetInitialMaxBufferSizeAllowed(true)
   , mPeakSize(0)
   , mPeakCount(0)
 {
   mDynamicReporter = new HpackDynamicTableReporter(this);
   RegisterStrongMemoryReporter(mDynamicReporter);
 }
 
 Http2BaseCompressor::~Http2BaseCompressor()
@@ -379,19 +380,33 @@ Http2BaseCompressor::SetMaxBufferSizeInt
     mHeaderTable.RemoveElement();
     ++removedCount;
   }
 
   mMaxBuffer = maxBufferSize;
 }
 
 nsresult
+Http2BaseCompressor::SetInitialMaxBufferSize(uint32_t maxBufferSize)
+{
+  MOZ_ASSERT(mSetInitialMaxBufferSizeAllowed);
+
+  if (mSetInitialMaxBufferSizeAllowed) {
+    mMaxBufferSetting = maxBufferSize;
+    return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
 Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
                                      nsACString &output, bool isPush)
 {
+  mSetInitialMaxBufferSizeAllowed = false;
   mOffset = 0;
   mData = data;
   mDataLen = datalen;
   mOutput = &output;
   mOutput->Truncate();
   mHeaderStatus.Truncate();
   mHeaderHost.Truncate();
   mHeaderScheme.Truncate();
@@ -1030,16 +1045,17 @@ Http2Decompressor::DoContextUpdate()
 /////////////////////////////////////////////////////////////////
 
 nsresult
 Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
                                    const nsACString &method, const nsACString &path,
                                    const nsACString &host, const nsACString &scheme,
                                    bool connectForm, nsACString &output)
 {
+  mSetInitialMaxBufferSizeAllowed = false;
   mOutput = &output;
   output.SetCapacity(1024);
   output.Truncate();
   mParsedContentLength = -1;
 
   // first thing's first - context size updates (if necessary)
   if (mBufferSizeChangeWaiting) {
     if (mLowestBufferSizeWaiting < mMaxBufferSetting) {
--- a/netwerk/protocol/http/Http2Compression.h
+++ b/netwerk/protocol/http/Http2Compression.h
@@ -61,30 +61,32 @@ private:
 class HpackDynamicTableReporter;
 
 class Http2BaseCompressor
 {
 public:
   Http2BaseCompressor();
   virtual ~Http2BaseCompressor();
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+  nsresult SetInitialMaxBufferSize(uint32_t maxBufferSize);
 
 protected:
   const static uint32_t kDefaultMaxBuffer = 4096;
 
   virtual void ClearHeaderTable();
   virtual void MakeRoom(uint32_t amount, const char *direction);
   virtual void DumpState();
   virtual void SetMaxBufferSizeInternal(uint32_t maxBufferSize);
 
   nsACString *mOutput;
   nvFIFO mHeaderTable;
 
   uint32_t mMaxBuffer;
   uint32_t mMaxBufferSetting;
+  bool mSetInitialMaxBufferSizeAllowed;
 
   uint32_t mPeakSize;
   uint32_t mPeakCount;
   Telemetry::ID mPeakSizeID;
   Telemetry::ID mPeakCountID;
 
 private:
   RefPtr<HpackDynamicTableReporter> mDynamicReporter;
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -827,20 +827,20 @@ Http2Session::GenerateGoAway(uint32_t aS
 //    these streams (3, 5, 7, 9, b) build a dependency tree that all other
 //    streams will be direct leaves of.
 void
 Http2Session::SendHello()
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   LOG3(("Http2Session::SendHello %p\n", this));
 
-  // sized for magic + 4 settings and a session window update and 5 priority frames
+  // sized for magic + 5 settings and a session window update and 5 priority frames
   // 24 magic, 33 for settings (9 header + 4 settings @6), 13 for window update,
   // 5 priority frames at 14 (9 + 5) each
-  static const uint32_t maxSettings = 4;
+  static const uint32_t maxSettings = 5;
   static const uint32_t prioritySize = 5 * (kFrameHeaderBytes + 5);
   static const uint32_t maxDataLen = 24 + kFrameHeaderBytes + maxSettings * 6 + 13 + prioritySize;
   char *packet = EnsureOutputBuffer(maxDataLen);
   memcpy(packet, kMagicHello, 24);
   mOutputQueueUsed += 24;
   LogIO(this, nullptr, "Magic Connection Header", packet, 24);
 
   packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
@@ -849,16 +849,24 @@ Http2Session::SendHello()
   // 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 9 to 14
   // 2nd entry is bytes 15 to 20
   // 3rd entry is bytes 21 to 26
   // 4th entry is bytes 27 to 32
+  // 5th entry is bytes 33 to 38
+
+  // Let the other endpoint know about our default HPACK decompress table size
+  uint32_t maxHpackBufferSize = gHttpHandler->DefaultHpackBuffer();
+  mDecompressor.SetInitialMaxBufferSize(maxHpackBufferSize);
+  NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_HEADER_TABLE_SIZE);
+  NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, maxHpackBufferSize);
+  numberOfEntries++;
 
   if (!gHttpHandler->AllowPush()) {
     // If we don't support push then set MAX_CONCURRENT to 0 and also
     // set ENABLE_PUSH to 0
     NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_ENABLE_PUSH);
     // The value portion of the setting pair is already initialized to 0
     numberOfEntries++;
 
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -234,16 +234,17 @@ nsHttpHandler::nsHttpHandler()
     , mCriticalRequestPrioritization(true)
     , mTCPKeepaliveShortLivedEnabled(false)
     , mTCPKeepaliveShortLivedTimeS(60)
     , mTCPKeepaliveShortLivedIdleTimeS(10)
     , mTCPKeepaliveLongLivedEnabled(false)
     , mTCPKeepaliveLongLivedIdleTimeS(600)
     , mEnforceH1Framing(FRAMECHECK_BARELY)
     , mKeepEmptyResponseHeadersAsEmtpyString(false)
+    , mDefaultHpackBuffer(4096)
 {
     LOG(("Creating nsHttpHandler [this=%p].\n", this));
 
     MOZ_ASSERT(!gHttpHandler, "HTTP handler already created!");
     gHttpHandler = this;
 }
 
 nsHttpHandler::~nsHttpHandler()
@@ -1688,16 +1689,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
     if (PREF_CHANGED(HTTP_PREF("keep_empty_response_headers_as_empty_string"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("keep_empty_response_headers_as_empty_string"),
                                 &cVar);
         if (NS_SUCCEEDED(rv)) {
             mKeepEmptyResponseHeadersAsEmtpyString = cVar;
         }
     }
 
+    if (PREF_CHANGED(HTTP_PREF("spdy.hpack-default-buffer"))) {
+        rv = prefs->GetIntPref(HTTP_PREF("spdy.default-hpack-buffer"), &val);
+        if (NS_SUCCEEDED(rv)) {
+            mDefaultHpackBuffer = val;
+        }
+    }
+
     // Enable HTTP response timeout if TCP Keepalives are disabled.
     mResponseTimeoutEnabled = !mTCPKeepaliveShortLivedEnabled &&
                               !mTCPKeepaliveLongLivedEnabled;
 
 #undef PREF_CHANGED
 #undef MULTI_PREF_CHANGED
 }
 
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -363,16 +363,21 @@ public:
 
     void ShutdownConnectionManager();
 
     bool KeepEmptyResponseHeadersAsEmtpyString() const
     {
         return mKeepEmptyResponseHeadersAsEmtpyString;
     }
 
+    uint32_t DefaultHpackBuffer() const
+    {
+        return mDefaultHpackBuffer;
+    }
+
 private:
     virtual ~nsHttpHandler();
 
     //
     // Useragent/prefs helper methods
     //
     void     BuildUserAgent();
     void     InitUserAgentComponents();
@@ -582,16 +587,19 @@ private:
 
     // If it is set to false, headers with empty value will not appear in the
     // header array - behavior as it used to be. If it is true: empty headers
     // coming from the network will exits in header array as empty string.
     // Call SetHeader with an empty value will still delete the header.
     // (Bug 6699259)
     bool mKeepEmptyResponseHeadersAsEmtpyString;
 
+    // The default size (in bytes) of the HPACK decompressor table.
+    uint32_t mDefaultHpackBuffer;
+
 private:
     // For Rate Pacing Certain Network Events. Only assign this pointer on
     // socket thread.
     void MakeNewRequestTokenBucket();
     RefPtr<EventTokenBucket> mRequestTokenBucket;
 
 public:
     // Socket thread only