bug 822745 spdy settings type 5 cwnd support r=honzab
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 30 Jan 2013 20:34:25 -0500
changeset 130295 3bccc06c75b24b16fc8c0ebba435119747fdbb43
parent 130294 3867507b94198e409a5efa333c68f2f9bb9b4e16
child 130296 f59f66def52517f0d9c91828c920fd3231cdfd71
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs822745
milestone21.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 822745 spdy settings type 5 cwnd support r=honzab
modules/libpref/src/init/all.js
netwerk/protocol/http/SpdySession3.cpp
netwerk/protocol/http/SpdySession3.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -946,16 +946,17 @@ pref("network.http.fast-fallback-to-IPv4
 // Try and use SPDY when using SSL
 pref("network.http.spdy.enabled", true);
 pref("network.http.spdy.enabled.v2", true);
 pref("network.http.spdy.enabled.v3", 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.use-alternate-protocol", 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.diagnostics", false);
 
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
--- a/netwerk/protocol/http/SpdySession3.cpp
+++ b/netwerk/protocol/http/SpdySession3.cpp
@@ -671,32 +671,55 @@ SpdySession3::GenerateGoAway(uint32_t aS
 }
 
 void
 SpdySession3::GenerateSettings()
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   LOG3(("SpdySession3::GenerateSettings %p\n", this));
 
-  static const uint32_t dataLen = 12;
-  EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + dataLen,
+  static const uint32_t maxDataLen = 4 + 2 * 8; // sized for 2 settings
+  EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + maxDataLen,
                mOutputQueueUsed, mOutputQueueSize);
   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
-  mOutputQueueUsed += 8 + dataLen;
 
-  memset(packet, 0, 8 + dataLen);
+  memset(packet, 0, 8 + maxDataLen);
   packet[0] = kFlag_Control;
   packet[1] = kVersion;
   packet[3] = CONTROL_TYPE_SETTINGS;
-  packet[7] = dataLen;
+
+  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
+
+  nsRefPtr<nsHttpConnectionInfo> ci;
+  uint32_t cwnd = 0;
+  GetConnectionInfo(getter_AddRefs(ci));
+  if (ci)
+    cwnd = gHttpHandler->ConnMgr()->GetSpdyCWNDSetting(ci);
+  if (cwnd) {
+    packet[12 + 8 * numberOfEntries] = PERSISTED_VALUE;
+    packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_CWND;
+    LOG(("SpdySession3::GenerateSettings %p sending CWND %u\n", this, cwnd));
+    cwnd = PR_htonl(cwnd);
+    memcpy(packet + 16 + 8 * numberOfEntries, &cwnd, 4);
+    numberOfEntries++;
+  }
   
-  packet[11] = 1;                                 /* 1 setting */
-  packet[15] = SETTINGS_TYPE_INITIAL_WINDOW;
+  packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW;
   uint32_t rwin = PR_htonl(kInitialRwin);
-  memcpy(packet + 16, &rwin, 4);
+  memcpy(packet + 16 + 8 * numberOfEntries, &rwin, 4);
+  numberOfEntries++;
+
+  uint32_t dataLen = 4 + 8 * numberOfEntries;
+  mOutputQueueUsed += 8 + dataLen;
+  packet[7] = dataLen;
+  packet[11] = numberOfEntries;
 
   LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen);
   FlushOutputQueue();
 }
 
 // perform a bunch of integrity checks on the stream.
 // returns true if passed, false (plus LOG and ABORT) if failed.
 bool
@@ -1139,21 +1162,28 @@ SpdySession3::HandleSettings(SpdySession
     case SETTINGS_TYPE_RTT:
       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value);
       break;
       
     case SETTINGS_TYPE_MAX_CONCURRENT:
       self->mMaxConcurrent = value;
       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
       break;
-      
-    case SETTINGS_TYPE_CWND:
+
+    case SETTINGS_TYPE_CWND: 
+      if (flags & PERSIST_VALUE)
+      {
+        nsRefPtr<nsHttpConnectionInfo> ci;
+        self->GetConnectionInfo(getter_AddRefs(ci));
+        if (ci)
+          gHttpHandler->ConnMgr()->ReportSpdyCWNDSetting(ci, value);
+      }
       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value);
       break;
-      
+
     case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE:
       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value);
       break;
       
     case SETTINGS_TYPE_INITIAL_WINDOW:
       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
       {
         int32_t delta = value - self->mServerInitialWindow;
--- a/netwerk/protocol/http/SpdySession3.h
+++ b/netwerk/protocol/http/SpdySession3.h
@@ -92,25 +92,31 @@ public:
   enum goawayReason
   {
     OK = 0,
     PROTOCOL_ERROR = 1,
     INTERNAL_ERROR = 2,    // sometimes misdocumented as 11
     NUM_STATUS_CODES = 3   // reserved by chromium but undocumented
   };
 
+  enum settingsFlags
+  {
+    PERSIST_VALUE = 1,
+    PERSISTED_VALUE = 2
+  };
+
   enum
   {
     SETTINGS_TYPE_UPLOAD_BW = 1, // kb/s
     SETTINGS_TYPE_DOWNLOAD_BW = 2, // kb/s
     SETTINGS_TYPE_RTT = 3, // ms
     SETTINGS_TYPE_MAX_CONCURRENT = 4, // streams
     SETTINGS_TYPE_CWND = 5, // packets
     SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
-    SETTINGS_TYPE_INITIAL_WINDOW = 7,  // bytes
+    SETTINGS_TYPE_INITIAL_WINDOW = 7,  // bytes for flow control
     SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
   };
 
   // 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.
   // About 1% of responses from SPDY google services seem to be > 1000
   // with all less than 2000 when compression is enabled.
   const static uint32_t kDefaultBufferSize = 2048;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -540,16 +540,73 @@ nsHttpConnectionMgr::ReportSpdyConnectio
         // coalesced through the preferred entry.
 
         conn->DontReuse();
     }
 
     ProcessAllSpdyPendingQ();
 }
 
+void
+nsHttpConnectionMgr::ReportSpdyCWNDSetting(nsHttpConnectionInfo *ci,
+                                           uint32_t cwndValue)
+{
+    if (!gHttpHandler->UseSpdyPersistentSettings())
+        return;
+
+    if (!ci)
+        return;
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (!ent)
+        return;
+
+    ent = GetSpdyPreferredEnt(ent);
+    if (!ent) // just to be thorough - but that map should always exist
+        return;
+
+    cwndValue = std::max(2U, cwndValue);
+    cwndValue = std::min(128U, cwndValue);
+
+    ent->mSpdyCWND = cwndValue;
+    ent->mSpdyCWNDTimeStamp = TimeStamp::Now();
+    return;
+}
+
+// a value of 0 means no setting is available
+uint32_t
+nsHttpConnectionMgr::GetSpdyCWNDSetting(nsHttpConnectionInfo *ci)
+{
+    if (!gHttpHandler->UseSpdyPersistentSettings())
+        return 0;
+
+    if (!ci)
+        return 0;
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (!ent)
+        return 0;
+
+    ent = GetSpdyPreferredEnt(ent);
+    if (!ent) // just to be thorough - but that map should always exist
+        return 0;
+
+    if (ent->mSpdyCWNDTimeStamp.IsNull())
+        return 0;
+
+    // For privacy tracking reasons, and the fact that CWND is not
+    // meaningful after some time, we don't honor stored CWND after 8
+    // hours.
+    TimeDuration age = TimeStamp::Now() - ent->mSpdyCWNDTimeStamp;
+    if (age.ToMilliseconds() > (1000 * 60 * 60 * 8))
+        return 0;
+
+    return ent->mSpdyCWND;
+}
+
 bool
 nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
 {
     if (!gHttpHandler->UseAlternateProtocol())
         return false;
 
     // The Alternate Protocol hash is protected under the monitor because
     // it is read from both the main and the network thread.
@@ -2953,16 +3010,17 @@ nsHttpConnectionMgr::
 nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
     : mConnInfo(ci)
     , mPipelineState(PS_YELLOW)
     , mYellowGoodEvents(0)
     , mYellowBadEvents(0)
     , mYellowConnection(nullptr)
     , mGreenDepth(kPipelineOpen)
     , mPipeliningPenalty(0)
+    , mSpdyCWND(0)
     , mUsingSpdy(false)
     , mTestedSpdy(false)
     , mSpdyPreferred(false)
     , mPreferIPv4(false)
     , mPreferIPv6(false)
 {
     NS_ADDREF(mConnInfo);
     if (gHttpHandler->GetPipelineAggressive()) {
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -216,16 +216,20 @@ public:
     // that the network peer has closed the transport.
     nsresult CloseIdleConnection(nsHttpConnection *);
 
     // The connection manager needs to know when a normal HTTP connection has been
     // upgraded to SPDY because the dispatch and idle semantics are a little
     // bit different.
     void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);
 
+    // A spdy server can supply cwnd information for the session that is used
+    // in future sessions to speed up the opening portions of the connection.
+    void ReportSpdyCWNDSetting(nsHttpConnectionInfo *host, uint32_t cwndValue);
+    uint32_t GetSpdyCWNDSetting(nsHttpConnectionInfo *host);
     
     bool     SupportsPipelining(nsHttpConnectionInfo *);
 
     bool GetConnectionData(nsTArray<mozilla::net::HttpRetParams> *);
 
     void ResetIPFamillyPreference(nsHttpConnectionInfo *);
 
 private:
@@ -329,16 +333,21 @@ private:
         // to build the hash key for hosts in the same ip pool.
         //
         // When a set of hosts are coalesced together one of them is marked
         // mSpdyPreferred. The mapping is maintained in the connection mananger
         // mSpdyPreferred hash.
         //
         nsCString mCoalescingKey;
 
+        // The value of a recevied SPDY settings type 5 previously received
+        // for this connection entry and the time it was set.
+        uint32_t            mSpdyCWND;
+        mozilla::TimeStamp  mSpdyCWNDTimeStamp;
+
         // To have the UsingSpdy flag means some host with the same connection
         // entry has done NPN=spdy/* at some point. It does not mean every
         // connection is currently using spdy.
         bool mUsingSpdy;
 
         // mTestedSpdy is set after NPN negotiation has occurred and we know
         // with confidence whether a host speaks spdy or not (which is reflected
         // in mUsingSpdy). Before mTestedSpdy is set, handshake parallelism is
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -175,16 +175,17 @@ nsHttpHandler::nsHttpHandler()
     , mTelemetryEnabled(false)
     , mAllowExperiments(true)
     , mHandlerActive(false)
     , mEnableSpdy(false)
     , mSpdyV2(true)
     , mSpdyV3(true)
     , mCoalesceSpdy(true)
     , mUseAlternateProtocol(false)
+    , mSpdyPersistentSettings(false)
     , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
     , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
     , mSpdyPingThreshold(PR_SecondsToInterval(58))
     , mSpdyPingTimeout(PR_SecondsToInterval(8))
     , mConnectTimeout(90000)
     , mParallelSpeculativeConnectLimit(6)
     , mCritialRequestPrioritization(true)
 {
@@ -1095,16 +1096,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
 
     if (PREF_CHANGED(HTTP_PREF("spdy.use-alternate-protocol"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("spdy.use-alternate-protocol"),
                                 &cVar);
         if (NS_SUCCEEDED(rv))
             mUseAlternateProtocol = cVar;
     }
 
+    if (PREF_CHANGED(HTTP_PREF("spdy.persistent-settings"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("spdy.persistent-settings"),
+                                &cVar);
+        if (NS_SUCCEEDED(rv))
+            mSpdyPersistentSettings = cVar;
+    }
+
     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"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.chunk-size"), &val);
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -87,16 +87,17 @@ public:
     bool           IsTelemetryEnabled() { return mTelemetryEnabled; }
     bool           AllowExperiments() { return mTelemetryEnabled && mAllowExperiments; }
 
     bool           IsSpdyEnabled() { return mEnableSpdy; }
     bool           IsSpdyV2Enabled() { return mSpdyV2; }
     bool           IsSpdyV3Enabled() { return mSpdyV3; }
     bool           CoalesceSpdy() { return mCoalesceSpdy; }
     bool           UseAlternateProtocol() { return mUseAlternateProtocol; }
+    bool           UseSpdyPersistentSettings() { return mSpdyPersistentSettings; }
     uint32_t       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
     uint32_t       SpdySendBufferSize()      { return mSpdySendBufferSize; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
     uint32_t       ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
     bool           CritialRequestPrioritization() { return mCritialRequestPrioritization; }
 
@@ -391,16 +392,17 @@ private:
 
     // Try to use SPDY features instead of HTTP/1.1 over SSL
     mozilla::net::SpdyInformation mSpdyInfo;
     bool           mEnableSpdy;
     bool           mSpdyV2;
     bool           mSpdyV3;
     bool           mCoalesceSpdy;
     bool           mUseAlternateProtocol;
+    bool           mSpdyPersistentSettings;
     uint32_t       mSpdySendingChunkSize;
     uint32_t       mSpdySendBufferSize;
     PRIntervalTime mSpdyPingThreshold;
     PRIntervalTime mSpdyPingTimeout;
 
     // The maximum amount of time to wait for socket transport to be
     // established. In milliseconds.
     uint32_t       mConnectTimeout;