bug 1072478 backout 28fff54451dd for 1119810 and 1119926 r=backout
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 09 Jan 2015 15:33:27 -0500
changeset 248889 0e94aaed6ea2554102911b507d180548ba1a149a
parent 248888 b098bff0d3411646121276f270b82f7b8f77fd74
child 248890 2487f8ac51dd31c1c39ac062a287f2287cb496f3
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1072478, 1119810, 1119926
milestone37.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 1072478 backout 28fff54451dd for 1119810 and 1119926 r=backout
netwerk/protocol/http/Http2Push.cpp
netwerk/protocol/http/Http2Session.cpp
netwerk/protocol/http/Http2Session.h
netwerk/protocol/http/Http2Stream.cpp
netwerk/protocol/http/Http2Stream.h
netwerk/protocol/http/SpdyPush31.cpp
netwerk/protocol/http/SpdySession31.cpp
netwerk/protocol/http/SpdySession31.h
netwerk/protocol/http/SpdyStream31.cpp
netwerk/protocol/http/SpdyStream31.h
--- a/netwerk/protocol/http/Http2Push.cpp
+++ b/netwerk/protocol/http/Http2Push.cpp
@@ -161,18 +161,17 @@ Http2PushedStream::ReadSegments(nsAHttpS
   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::mRequestHeadersDone = 1;
-  Http2Stream::mOpenGenerated = 1;
+  Http2Stream::mAllHeadersSent = 1;
   Http2Stream::ChangeState(UPSTREAM_COMPLETE);
   *count = 0;
   return NS_OK;
 }
 
 void
 Http2PushedStream::SetConsumerStream(Http2Stream *consumer)
 {
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -433,62 +433,76 @@ Http2Session::AddStream(nsAHttpTransacti
 
   Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority);
 
   LOG3(("Http2Session::AddStream session=%p stream=%p serial=%u "
         "NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
 
   mStreamTransactionHash.Put(aHttpTransaction, stream);
 
-  mReadyForWrite.Push(stream);
-  SetWriteCallbacks();
-
-  // Kick off the SYN transmit without waiting for the poll loop
-  // This won't work for the first stream because there is no segment reader
-  // yet.
-  if (mSegmentReader) {
-    uint32_t countRead;
-    ReadSegments(nullptr, kDefaultBufferSize, &countRead);
+  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) &&
       !aHttpTransaction->IsNullTransaction()) {
     LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n",
           this, aHttpTransaction));
     DontReuse();
   }
 
   return true;
 }
 
 void
-Http2Session::QueueStream(Http2Stream *stream)
+Http2Session::ActivateStream(Http2Stream *stream)
 {
-  // will be removed via processpending or a shutdown path
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
+             "Do not activate pushed streams");
+
   MOZ_ASSERT(!stream->CountAsActive());
-
-  LOG3(("Http2Session::QueueStream %p stream %p queued.", this, stream));
-  mQueuedStreams.Push(stream);
+  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);
 
-  Http2Stream*stream;
-  while (RoomForMoreConcurrent() &&
-         (stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront()))) {
-
-    LOG3(("Http2Session::ProcessPending %p stream %p woken from queue.",
+  while (RoomForMoreConcurrent()) {
+    Http2Stream *stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront());
+    if (!stream)
+      return;
+    LOG3(("Http2Session::ProcessPending %p stream %p activated from queue.",
           this, stream));
-    MOZ_ASSERT(!stream->CountAsActive());
-    mReadyForWrite.Push(stream);
-    SetWriteCallbacks();
+    ActivateStream(stream);
   }
 }
 
 nsresult
 Http2Session::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
                           uint32_t count, uint32_t *countWritten)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -597,54 +611,16 @@ Http2Session::ResetDownstreamState()
     LOG3(("  SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
     mInputFrameDataStream->SetRecvdFin(true);
     MaybeDecrementConcurrent(mInputFrameDataStream);
   }
   mInputFrameBufferUsed = 0;
   mInputFrameDataStream = nullptr;
 }
 
-// return true if activated (and counted against max)
-// otherwise return false and queue
-bool
-Http2Session::TryToActivate(Http2Stream *aStream)
-{
-  if (!RoomForMoreConcurrent()) {
-    LOG3(("Http2Session::TryToActivate %p stream=%p no room for more concurrent "
-          "streams %d\n", this, aStream));
-    QueueStream(aStream);
-    return false;
-  }
-  IncrementConcurrent(aStream);
-  return true;
-}
-
-void
-Http2Session::IncrementConcurrent(Http2Stream *stream)
-{
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-  MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
-             "Do not activate pushed streams");
-
-  nsAHttpTransaction *trans = stream->Transaction();
-  if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
-
-    MOZ_ASSERT(!stream->CountAsActive());
-    stream->SetCountAsActive(true);
-    ++mConcurrent;
-
-    if (mConcurrent > mConcurrentHighWater) {
-      mConcurrentHighWater = mConcurrent;
-    }
-    LOG3(("Http2Session::IncrementCounter %p counting stream %p Currently %d "
-          "streams in session, high water mark is %d\n",
-          this, stream, mConcurrent, mConcurrentHighWater));
-  }
-}
-
 // call with data length (i.e. 0 for 0 data bytes - ignore 9 byte header)
 // dest must have 9 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");
@@ -675,17 +651,16 @@ Http2Session::CreateFrameHeader(char *de
 template void
 Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength,
                                 uint8_t frameType, uint8_t frameFlags,
                                 uint32_t streamID);
 
 void
 Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream)
 {
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   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);
@@ -1470,17 +1445,16 @@ Http2Session::RecvSettings(Http2Session 
     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);
-      self->ProcessPending();
       break;
 
     case SETTINGS_TYPE_INITIAL_WINDOW:
       {
         Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
         int32_t delta = value - self->mServerInitialStreamWindow;
         self->mServerInitialStreamWindow = value;
 
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -200,18 +200,18 @@ public:
   // an overload of nsAHttpSegementReader
   virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment) MOZ_OVERRIDE;
   nsresult BufferOutput(const char *, uint32_t, uint32_t *);
   void     FlushOutputQueue();
   uint32_t AmountOfOutputBuffered() { return mOutputQueueUsed - mOutputQueueSent; }
 
   uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; }
 
-  bool TryToActivate(Http2Stream *stream);
   void ConnectPushedStream(Http2Stream *stream);
+  void MaybeDecrementConcurrent(Http2Stream *stream);
 
   nsresult ConfirmTLSProfile();
   static bool ALPNCallback(nsISupports *securityInfo);
 
   uint64_t Serial() { return mSerial; }
 
   void PrintDiagnostics (nsCString &log) MOZ_OVERRIDE;
 
@@ -261,31 +261,28 @@ private:
   void        CloseStream(Http2Stream *, nsresult);
   void        SendHello();
   void        RemoveStreamFromQueues(Http2Stream *);
   nsresult    ParsePadding(uint8_t &, uint16_t &);
 
   void        SetWriteCallbacks();
   void        RealignOutputQueue();
 
+  bool        RoomForMoreConcurrent();
+  void        ActivateStream(Http2Stream *);
   void        ProcessPending();
   nsresult    SetInputFrameDataStream(uint32_t);
   void        CreatePriorityNode(uint32_t, uint32_t, uint8_t, const char *);
   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);
 
-  void        MaybeDecrementConcurrent(Http2Stream *stream);
-  bool        RoomForMoreConcurrent();
-  void        IncrementConcurrent(Http2Stream *stream);
-  void        QueueStream(Http2Stream *stream);
-
   // 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 *);
 
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -41,18 +41,17 @@ namespace net {
 
 Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
                          Http2Session *session,
                          int32_t priority)
   : mStreamID(0)
   , mSession(session)
   , mUpstreamState(GENERATING_HEADERS)
   , mState(IDLE)
-  , mRequestHeadersDone(0)
-  , mOpenGenerated(0)
+  , mAllHeadersSent(0)
   , mAllHeadersReceived(0)
   , mTransaction(httpTransaction)
   , mSocketTransport(session->SocketTransport())
   , mSegmentReader(nullptr)
   , mSegmentWriter(nullptr)
   , mChunkSize(session->SendingChunkSize())
   , mRequestBlockedOnRead(0)
   , mRecvdFin(0)
@@ -150,17 +149,17 @@ Http2Stream::ReadSegments(nsAHttpSegment
 
     LOG3(("Http2Stream::ReadSegments %p trans readsegments rv %x read=%d\n",
           this, rv, *countRead));
 
     // 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 &&
-        !mRequestHeadersDone)
+        !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.
 
@@ -299,27 +298,26 @@ Http2Stream::CreatePushHashKey(const nsC
 }
 
 nsresult
 Http2Stream::ParseHttpRequestHeaders(const char *buf,
                                      uint32_t avail,
                                      uint32_t *countUsed)
 {
   // Returns NS_OK even if the headers are incomplete
-  // set mRequestHeadersDone flag if they are complete
+  // set mAllHeadersSent flag if they are complete
 
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   MOZ_ASSERT(mUpstreamState == GENERATING_HEADERS);
-  MOZ_ASSERT(!mRequestHeadersDone);
 
   LOG3(("Http2Stream::ParseHttpRequestHeaders %p avail=%d state=%x",
         this, avail, mUpstreamState));
 
   mFlatHttpRequestHeaders.Append(buf, avail);
-  const nsHttpRequestHead *head = mTransaction->RequestHead();
+  nsHttpRequestHead *head = mTransaction->RequestHead();
 
   // 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 "
@@ -330,17 +328,17 @@ Http2Stream::ParseHttpRequestHeaders(con
   }
 
   // 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;
-  mRequestHeadersDone = 1;
+  mAllHeadersSent = 1;
 
   nsAutoCString authorityHeader;
   nsAutoCString hashkey;
   head->GetHeader(nsHttp::Host, authorityHeader);
 
   CreatePushHashKey(nsDependentCString(head->IsHTTPS() ? "https" : "http"),
                     authorityHeader, mSession->Serial(),
                     head->RequestURI(),
@@ -385,42 +383,38 @@ Http2Stream::ParseHttpRequestHeaders(con
     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();
 
+      // This stream has been activated (and thus counts against the concurrency
+      // limit intentionally), but will not be registered via
+      // RegisterStreamID (below) because of the push match.
+      // Release that semaphore count immediately (instead of waiting for
+      // cleanup stream) so we can initiate more pull streams.
+      mSession->MaybeDecrementConcurrent(this);
+
       // 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);
-      mOpenGenerated = 1;
       return NS_OK;
     }
   }
-  return NS_OK;
-}
 
-// This is really a headers frame, but open is pretty clear from a workflow pov
-nsresult
-Http2Stream::GenerateOpen()
-{
   // 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");
-  MOZ_ASSERT(!mOpenGenerated);
-
-  mOpenGenerated = 1;
-
-  const nsHttpRequestHead *head = mTransaction->RequestHead();
-  LOG3(("Http2Stream %p Stream ID 0x%X [session=%p] for URI %s\n",
-        this, mStreamID, mSession, nsCString(head->RequestURI()).get()));
+  LOG3(("Stream ID 0x%X [session=%p] for URI %s\n",
+        mStreamID, mSession,
+        nsCString(head->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;
@@ -429,32 +423,28 @@ Http2Stream::GenerateOpen()
     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;
-  nsAutoCString authorityHeader;
-  head->GetHeader(nsHttp::Host, authorityHeader);
-
   nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
   if (head->IsConnect()) {
     MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction());
     mIsTunnel = true;
     mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
 
     // Our normal authority has an implicit port, best to use an
     // explicit one with a tunnel
     nsHttpConnectionInfo *ci = mTransaction->ConnectionInfo();
     if (!ci) {
       return NS_ERROR_UNEXPECTED;
     }
-
     authorityHeader = ci->GetHost();
     authorityHeader.Append(':');
     authorityHeader.AppendInt(ci->Port());
   }
 
   mSession->Compressor()->EncodeHeaderBlock(mFlatHttpRequestHeaders,
                                             head->Method(),
                                             head->Path(),
@@ -1205,36 +1195,22 @@ Http2Stream::OnReadSegment(const char *b
   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)
 
-    if (!mRequestHeadersDone) {
-      if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) {
-        return rv;
-      }
-    }
-
-    if (mRequestHeadersDone && !mOpenGenerated) {
-      if (!mSession->TryToActivate(this)) {
-        LOG3(("Http2Stream::OnReadSegment %p cannot activate now. queued.\n", this));
-        return NS_OK;
-      }
-      if (NS_FAILED(rv = GenerateOpen())) {
-        return rv;
-      }
-   }
-
-    LOG3(("ParseHttpRequestHeaders %p used %d of %d. "
-          "requestheadersdone = %d mOpenGenerated = %d\n",
-          this, *countRead, count, mRequestHeadersDone, mOpenGenerated));
-    if (mOpenGenerated) {
+    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");
--- a/netwerk/protocol/http/Http2Stream.h
+++ b/netwerk/protocol/http/Http2Stream.h
@@ -173,33 +173,28 @@ protected:
   // 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 ID is not stable
-  uint32_t                     mRequestHeadersDone   : 1;
+  // Flag is set when all http request headers have been read and ID is stable
+  uint32_t                     mAllHeadersSent       : 1;
 
-  // Flag is set when ID is stable and concurrency limits are met
-  uint32_t                     mOpenGenerated        : 1;
-
-  // Flag is set when all http response headers have been read
+  // 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 *);
-  nsresult GenerateOpen();
-
   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.
--- a/netwerk/protocol/http/SpdyPush31.cpp
+++ b/netwerk/protocol/http/SpdyPush31.cpp
@@ -101,18 +101,17 @@ SpdyPushedStream31::ReadSegments(nsAHttp
   CreatePushHashKey(nsCString(scheme), nsCString(host),
                     mSession->Serial(), path,
                     mOrigin, mHashKey);
 
   LOG3(("SpdyPushStream31 0x%X hash key %s\n", mStreamID, mHashKey.get()));
 
   // the write side of a pushed transaction just involves manipulating a little state
   SpdyStream31::mSentFinOnData = 1;
-  SpdyStream31::mRequestHeadersDone = 1;
-  SpdyStream31::mSynFrameGenerated = 1;
+  SpdyStream31::mSynFrameComplete = 1;
   SpdyStream31::ChangeState(UPSTREAM_COMPLETE);
   *count = 0;
   return NS_OK;
 }
 
 bool
 SpdyPushedStream31::GetHashKey(nsCString &key)
 {
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -374,62 +374,78 @@ SpdySession31::AddStream(nsAHttpTransact
 
   SpdyStream31 *stream = new SpdyStream31(aHttpTransaction, this, aPriority);
 
   LOG3(("SpdySession31::AddStream session=%p stream=%p serial=%u "
         "NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
 
   mStreamTransactionHash.Put(aHttpTransaction, stream);
 
-  mReadyForWrite.Push(stream);
-  SetWriteCallbacks();
-
-  // Kick off the SYN 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);
+  if (RoomForMoreConcurrent()) {
+    LOG3(("SpdySession31::AddStream %p stream %p activated immediately.",
+          this, stream));
+    ActivateStream(stream);
+  }
+  else {
+    LOG3(("SpdySession31::AddStream %p stream %p queued.", this, stream));
+    mQueuedStreams.Push(stream);
   }
 
   if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
       !aHttpTransaction->IsNullTransaction()) {
     LOG3(("SpdySession31::AddStream %p transaction %p forces keep-alive off.\n",
           this, aHttpTransaction));
     DontReuse();
   }
 
   return true;
 }
 
 void
-SpdySession31::QueueStream(SpdyStream31 *stream)
+SpdySession31::ActivateStream(SpdyStream31 *stream)
 {
-  // will be removed via processpending or a shutdown path
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-  MOZ_ASSERT(!stream->CountAsActive());
-
-  LOG3(("SpdySession31::QueueStream %p stream %p queued.", this, stream));
-  mQueuedStreams.Push(stream);
+  MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
+             "Do not activate pushed streams");
+
+  nsAHttpTransaction *trans = stream->Transaction();
+  if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
+    ++mConcurrent;
+    if (mConcurrent > mConcurrentHighWater) {
+      mConcurrentHighWater = mConcurrent;
+    }
+    LOG3(("SpdySession31::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 SYN 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
 SpdySession31::ProcessPending()
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
-  SpdyStream31 *stream;
-  while (RoomForMoreConcurrent() &&
-         (stream = static_cast<SpdyStream31 *>(mQueuedStreams.PopFront()))) {
-
-    LOG3(("SpdySession31::ProcessPending %p stream %p woken from queue.",
+  while (RoomForMoreConcurrent()) {
+    SpdyStream31 *stream = static_cast<SpdyStream31 *>(mQueuedStreams.PopFront());
+    if (!stream)
+      return;
+    LOG3(("SpdySession31::ProcessPending %p stream %p activated from queue.",
           this, stream));
-    MOZ_ASSERT(!stream->CountAsActive());
-    mReadyForWrite.Push(stream);
-    SetWriteCallbacks();
+    ActivateStream(stream);
   }
 }
 
 nsresult
 SpdySession31::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
                            uint32_t count, uint32_t *countWritten)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -540,70 +556,28 @@ SpdySession31::ResetDownstreamState()
       mInputFrameDataStream->SetRecvdFin(true);
       DecrementConcurrent(mInputFrameDataStream);
     }
   }
   mInputFrameBufferUsed = 0;
   mInputFrameDataStream = nullptr;
 }
 
-// return true if activated (and counted against max)
-// otherwise return false and queue
-bool
-SpdySession31::TryToActivate(SpdyStream31 *aStream)
-{
-  if (!RoomForMoreConcurrent()) {
-    LOG3(("SpdySession31::TryToActivate %p stream=%p no room for more concurrent "
-          "streams %d\n", this, aStream));
-    QueueStream(aStream);
-    return false;
-  }
-  IncrementConcurrent(aStream);
-  return true;
-}
-
-void
-SpdySession31::IncrementConcurrent(SpdyStream31 *stream)
-{
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-  MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
-             "Do not activate pushed streams");
-
-  nsAHttpTransaction *trans = stream->Transaction();
-  if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
-
-    MOZ_ASSERT(!stream->CountAsActive());
-    stream->SetCountAsActive(true);
-    ++mConcurrent;
-
-    if (mConcurrent > mConcurrentHighWater) {
-      mConcurrentHighWater = mConcurrent;
-    }
-    LOG3(("SpdySession31::AddStream %p counting stream %p Currently %d "
-          "streams in session, high water mark is %d",
-          this, stream, mConcurrent, mConcurrentHighWater));
-  }
-}
-
 void
 SpdySession31::DecrementConcurrent(SpdyStream31 *aStream)
 {
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
-  if (!aStream->CountAsActive()) {
-    return;
-  }
+  uint32_t id = aStream->StreamID();
+
+  if (id && !(id & 0x1))
+    return; // pushed streams aren't counted in concurrent limit
 
   MOZ_ASSERT(mConcurrent);
-  aStream->SetCountAsActive(false);
   --mConcurrent;
-
   LOG3(("DecrementConcurrent %p id=0x%X concurrent=%d\n",
-        this, aStream->StreamID(), mConcurrent));
-
+        this, id, mConcurrent));
   ProcessPending();
 }
 
 void
 SpdySession31::zlibInit()
 {
   mDownstreamZlib.zalloc = SpdyZlibReporter::Alloc;
   mDownstreamZlib.zfree = SpdyZlibReporter::Free;
@@ -1435,17 +1409,16 @@ SpdySession31::HandleSettings(SpdySessio
 
     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);
-      self->ProcessPending();
       break;
 
     case SETTINGS_TYPE_CWND:
       if (flags & PERSIST_VALUE)
       {
         nsRefPtr<nsHttpConnectionInfo> ci;
         self->GetConnectionInfo(getter_AddRefs(ci));
         if (ci)
--- a/netwerk/protocol/http/SpdySession31.h
+++ b/netwerk/protocol/http/SpdySession31.h
@@ -173,17 +173,16 @@ public:
   // an overload of nsAHttpSegementReader
   virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment) MOZ_OVERRIDE;
   nsresult BufferOutput(const char *, uint32_t, uint32_t *);
   void     FlushOutputQueue();
   uint32_t AmountOfOutputBuffered() { return mOutputQueueUsed - mOutputQueueSent; }
 
   uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; }
 
-  bool TryToActivate(SpdyStream31 *stream);
   void ConnectPushedStream(SpdyStream31 *stream);
   void DecrementConcurrent(SpdyStream31 *stream);
 
   uint64_t Serial() { return mSerial; }
 
   void     PrintDiagnostics (nsCString &log) MOZ_OVERRIDE;
 
   // Streams need access to these
@@ -219,29 +218,27 @@ private:
   void        CleanupStream(SpdyStream31 *, nsresult, rstReason);
   void        CloseStream(SpdyStream31 *, nsresult);
   void        GenerateSettings();
   void        RemoveStreamFromQueues(SpdyStream31 *);
 
   void        SetWriteCallbacks();
   void        RealignOutputQueue();
 
+  bool        RoomForMoreConcurrent();
+  void        ActivateStream(SpdyStream31 *);
   void        ProcessPending();
   nsresult    SetInputFrameDataStream(uint32_t);
   bool        VerifyStream(SpdyStream31 *, uint32_t);
   void        SetNeedsCleanup();
 
   void        UpdateLocalRwin(SpdyStream31 *stream, uint32_t bytes);
   void        UpdateLocalStreamWindow(SpdyStream31 *stream, uint32_t bytes);
   void        UpdateLocalSessionWindow(uint32_t bytes);
 
-  bool        RoomForMoreConcurrent();
-  void        IncrementConcurrent(SpdyStream31 *stream);
-  void        QueueStream(SpdyStream31 *stream);
-
   // 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<SpdyStream31> &,
                                             void *);
 
--- a/netwerk/protocol/http/SpdyStream31.cpp
+++ b/netwerk/protocol/http/SpdyStream31.cpp
@@ -37,31 +37,29 @@ namespace mozilla {
 namespace net {
 
 SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction,
                            SpdySession31 *spdySession,
                            int32_t priority)
   : mStreamID(0)
   , mSession(spdySession)
   , mUpstreamState(GENERATING_SYN_STREAM)
-  , mRequestHeadersDone(0)
-  , mSynFrameGenerated(0)
+  , mSynFrameComplete(0)
   , mSentFinOnData(0)
   , mTransaction(httpTransaction)
   , mSocketTransport(spdySession->SocketTransport())
   , mSegmentReader(nullptr)
   , mSegmentWriter(nullptr)
   , mChunkSize(spdySession->SendingChunkSize())
   , mRequestBlockedOnRead(0)
   , mRecvdFin(0)
   , mFullyOpen(0)
   , mSentWaitingFor(0)
   , mReceivedData(0)
   , mSetTCPSocketBuffer(0)
-  , mCountAsActive(0)
   , mTxInlineFrameSize(SpdySession31::kDefaultBufferSize)
   , mTxInlineFrameUsed(0)
   , mTxStreamFrameSize(0)
   , mZlib(spdySession->UpstreamZlib())
   , mDecompressBufferSize(SpdySession31::kDefaultBufferSize)
   , mDecompressBufferUsed(0)
   , mDecompressedBytes(0)
   , mRequestBodyLenRemaining(0)
@@ -126,17 +124,17 @@ SpdyStream31::ReadSegments(nsAHttpSegmen
     rv = mTransaction->ReadSegments(this, count, countRead);
     mSegmentReader = nullptr;
     LOG3(("SpdyStream31::ReadSegments %p trans readsegments rv %x read=%d\n",
           this, rv, *countRead));
     // 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_SYN_STREAM &&
-        !mRequestHeadersDone)
+        !mSynFrameComplete)
       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 SPDY frame. Likewise, a non 0 value is
     // a queued, but complete, spdy frame length.
 
@@ -256,21 +254,20 @@ SpdyStream31::CreatePushHashKey(const ns
 
 
 nsresult
 SpdyStream31::ParseHttpRequestHeaders(const char *buf,
                                       uint32_t avail,
                                       uint32_t *countUsed)
 {
   // Returns NS_OK even if the headers are incomplete
-  // set mRequestHeadersDone flag if they are complete
+  // set mSynFrameComplete flag if they are complete
 
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM);
-  MOZ_ASSERT(!mRequestHeadersDone);
 
   LOG3(("SpdyStream31::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
@@ -286,17 +283,17 @@ SpdyStream31::ParseHttpRequestHeaders(co
   }
 
   // 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;
-  mRequestHeadersDone = 1;
+  mSynFrameComplete = 1;
 
   nsAutoCString hostHeader;
   nsAutoCString hashkey;
   mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
 
   CreatePushHashKey(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"),
                     hostHeader, mSession->Serial(),
                     mTransaction->RequestHead()->RequestURI(),
@@ -328,34 +325,25 @@ SpdyStream31::ParseHttpRequestHeaders(co
       // limit intentionally), but will not be registered via
       // RegisterStreamID (below) because of the push match. Therefore the
       // concurrency sempahore needs to be balanced.
       mSession->DecrementConcurrent(this);
 
       // 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);
-      mSynFrameGenerated = 1;
       return NS_OK;
     }
   }
-  return NS_OK;
-}
 
-nsresult
-SpdyStream31::GenerateSynFrame()
-{
   // It is now OK to assign a streamID that we are assured will
   // be monotonically increasing amongst syn-streams on this
   // session
   mStreamID = mSession->RegisterStreamID(this);
   MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd");
-  MOZ_ASSERT(!mSynFrameGenerated);
-
-  mSynFrameGenerated = 1;
 
   if (mStreamID >= 0x80000000) {
     // streamID must fit in 31 bits. This is theoretically possible
     // because stream ID assignment is asynchronous to stream creation
     // because of the protocol requirement that the ID in syn-stream
     // be monotonically increasing. In reality this is really not possible
     // because new streams stop being added to a session with 0x10000000 / 2
     // IDs still available and no race condition is going to bridge that gap,
@@ -514,18 +502,16 @@ SpdyStream31::GenerateSynFrame()
     route.Append(':');
     route.AppendInt(ci->Port());
     CompressToFrame(route);
   }
 
   CompressToFrame(NS_LITERAL_CSTRING(":version"));
   CompressToFrame(versionHeader);
 
-  nsAutoCString hostHeader;
-  mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
   CompressToFrame(NS_LITERAL_CSTRING(":host"));
   CompressToFrame(hostHeader);
 
   if (!mTransaction->RequestHead()->IsConnect()) {
     // no :scheme with connect
     CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
     CompressToFrame(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"));
   }
@@ -1464,36 +1450,22 @@ SpdyStream31::OnReadSegment(const char *
   case GENERATING_SYN_STREAM:
     // The buffer is the HTTP request stream, including at least part of the
     // HTTP request header. This state's job is to build a SYN_STREAM 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)
 
-    if (!mRequestHeadersDone) {
-      if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) {
-        return rv;
-      }
-    }
-
-    if (mRequestHeadersDone && !mSynFrameGenerated) {
-      if (!mSession->TryToActivate(this)) {
-        LOG3(("SpdyStream31::OnReadSegment %p cannot activate now. queued.\n", this));
-        return NS_OK;
-      }
-      if (NS_FAILED(rv = GenerateSynFrame())) {
-        return rv;
-      }
-    }
-
-    LOG3(("ParseHttpRequestHeaders %p used %d of %d. "
-          "requestheadersdone = %d mSynFrameGenerated = %d\n",
-          this, *countRead, count, mRequestHeadersDone, mSynFrameGenerated));
-    if (mSynFrameGenerated) {
+    rv = ParseHttpRequestHeaders(buf, count, countRead);
+    if (NS_FAILED(rv))
+      return rv;
+    LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
+          this, *countRead, count, mSynFrameComplete));
+    if (mSynFrameComplete) {
       AdjustInitialWindow();
       rv = TransmitFrame(nullptr, nullptr, true);
       if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
         // this can't happen
         MOZ_ASSERT(false, "Transmit Frame SYN_FRAME must at least buffer data");
         rv = NS_ERROR_UNEXPECTED;
       }
 
--- a/netwerk/protocol/http/SpdyStream31.h
+++ b/netwerk/protocol/http/SpdyStream31.h
@@ -50,19 +50,16 @@ public:
   void Close(nsresult reason);
 
   void SetRecvdFin(bool aStatus) { mRecvdFin = aStatus ? 1 : 0; }
   bool RecvdFin() { return mRecvdFin; }
 
   void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; }
   bool RecvdData() { return mReceivedData; }
 
-  void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; }
-  bool CountAsActive() { return mCountAsActive; }
-
   void UpdateTransportSendEvents(uint32_t count);
   void UpdateTransportReadEvents(uint32_t count);
 
   // The zlib header compression dictionary defined by SPDY.
   static const unsigned char kDictionary[1423];
 
   nsresult Uncompress(z_stream *, char *, uint32_t);
   nsresult ConvertHeaders(nsACString &);
@@ -116,38 +113,33 @@ protected:
 
   nsCString     mOrigin;
 
   // Each stream goes from syn_stream to upstream_complete, perhaps
   // looping on multiple instances of generating_request_body and
   // sending_request_body for each SPDY chunk in the upload.
   enum stateType mUpstreamState;
 
-  // Flag is set when all http request headers have been read
-  uint32_t                     mRequestHeadersDone   : 1;
-
-  // Flag is set when stream ID is stable
-  uint32_t                     mSynFrameGenerated    : 1;
+  // Flag is set when all http request headers have been read and ID is stable
+  uint32_t                     mSynFrameComplete     : 1;
 
   // Flag is set when a FIN has been placed on a data or syn packet
   // (i.e after the client has closed)
   uint32_t                     mSentFinOnData        : 1;
 
   void     ChangeState(enum stateType);
 
 private:
   friend class nsAutoPtr<SpdyStream31>;
 
   static PLDHashOperator hdrHashEnumerate(const nsACString &,
                                           nsAutoPtr<nsCString> &,
                                           void *);
 
   nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *);
-  nsresult GenerateSynFrame();
-
   void     AdjustInitialWindow();
   nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment);
   void     GenerateDataFrameHeader(uint32_t, bool);
 
   void     CompressToFrame(const nsACString &);
   void     CompressToFrame(const nsACString *);
   void     CompressToFrame(const char *, uint32_t);
   void     CompressToFrame(uint32_t);
@@ -188,19 +180,16 @@ private:
 
   // Flag is set after 1st DATA frame has been passed to stream, after
   // which additional HEADERS data is invalid
   uint32_t                     mReceivedData         : 1;
 
   // Flag is set after TCP send autotuning has been disabled
   uint32_t                     mSetTCPSocketBuffer   : 1;
 
-  // Flag is set when stream is counted towards MAX_CONCURRENT streams in session
-  uint32_t                     mCountAsActive        : 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