bug 1072478 backout 28fff54451dd for 1119810 and 1119926 r=backout
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 09 Jan 2015 15:33:27 -0500
changeset 223121 0e94aaed6ea2554102911b507d180548ba1a149a
parent 223120 b098bff0d3411646121276f270b82f7b8f77fd74
child 223122 2487f8ac51dd31c1c39ac062a287f2287cb496f3
push id10769
push usercbook@mozilla.com
push dateMon, 12 Jan 2015 14:15:52 +0000
treeherderfx-team@0e9765732906 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1072478, 1119810, 1119926
milestone37.0a1
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