bug 718206 fix spdy nsitransport event generation r=honzab
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 25 Jan 2012 15:21:13 -0500
changeset 86595 56ce42bc8afe429bcc69e2097834e90d23c418b2
parent 86594 a9823c18b423e97718435433866af1597126776e
child 86596 8eab8fdaa6757ed2c1cbdf126478d583fcd4ae5e
child 86617 e37dbe8d753462d24dc940c1e0d9a07be4b46cc8
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs718206
milestone12.0a1
bug 718206 fix spdy nsitransport event generation r=honzab
netwerk/protocol/http/SpdySession.cpp
netwerk/protocol/http/SpdyStream.cpp
netwerk/protocol/http/SpdyStream.h
--- a/netwerk/protocol/http/SpdySession.cpp
+++ b/netwerk/protocol/http/SpdySession.cpp
@@ -846,16 +846,18 @@ SpdySession::HandleSynReply(SpdySession 
     // For the most part we would like to ignore it, but the header needs to be
     // be parsed to keep the compression context synchronized
     self->DownstreamUncompress(self->mFrameBuffer + 14,
                                self->mFrameDataSize - 6);
     self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
     return NS_OK;
   }
   
+  self->mFrameDataStream->UpdateTransportReadEvents(self->mFrameDataSize);
+
   if (!self->mFrameDataStream->SetFullyOpen()) {
     // "If an endpoint receives multiple SYN_REPLY frames for the same active
     // stream ID, it must drop the stream, and send a RST_STREAM for the
     // stream with the error PROTOCOL_ERROR."
     //
     // In addition to that we abort the session - this is a serious protocol
     // violation.
 
@@ -1123,16 +1125,19 @@ SpdySession::HandleHeaders(SpdySession *
   }
 
   PRUint32 streamID =
     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
 
   // this is actually not legal in the HTTP mapping of SPDY. All
   // headers are in the syn or syn reply. Log and ignore it.
 
+  // in v3 this will be legal and we must remember to note
+  // NS_NET_STATUS_RECEIVING_FROM from it
+
   LOG3(("SpdySession::HandleHeaders %p HEADERS for Stream 0x%X. "
         "They are ignored in the HTTP/SPDY mapping.",
         self, streamID));
   self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
   return NS_OK;
 }
 
 nsresult
@@ -1143,66 +1148,69 @@ SpdySession::HandleWindowUpdate(SpdySess
   LOG3(("SpdySession::HandleWindowUpdate %p WINDOW UPDATE was "
         "received. WINDOW UPDATE is no longer defined in v2. Ignoring.",
         self));
 
   self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
   return NS_OK;
 }
 
-// Used for the hashtable enumeration to propogate OnTransportStatus events
-struct transportStatus
-{
-  nsITransport *transport;
-  nsresult status;
-  PRUint64 progress;
-};
-
-static PLDHashOperator
-StreamTransportStatus(nsAHttpTransaction *key,
-                      nsAutoPtr<SpdyStream> &stream,
-                      void *closure)
-{
-  struct transportStatus *status =
-    static_cast<struct transportStatus *>(closure);
-
-  stream->Transaction()->OnTransportStatus(status->transport,
-                                           status->status,
-                                           status->progress);
-  return PL_DHASH_NEXT;
-}
-
-
 //-----------------------------------------------------------------------------
 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
 // of these methods
 //-----------------------------------------------------------------------------
 
 void
 SpdySession::OnTransportStatus(nsITransport* aTransport,
                                nsresult aStatus,
                                PRUint64 aProgress)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
-  // nsHttpChannel synthesizes progress events in OnDataAvailable
-  if (aStatus == nsISocketTransport::STATUS_RECEIVING_FROM)
-    return;
+  switch (aStatus) {
+    // These should appear only once, deliver to the first
+    // transaction on the session.
+  case NS_NET_STATUS_RESOLVING_HOST:
+  case NS_NET_STATUS_RESOLVED_HOST:
+  case NS_NET_STATUS_CONNECTING_TO:
+  case NS_NET_STATUS_CONNECTED_TO:
+  {
+    SpdyStream *target = mStreamIDHash.Get(1);
+    if (target)
+      target->Transaction()->OnTransportStatus(aTransport, aStatus, aProgress);
+    break;
+  }
 
-  // STATUS_SENDING_TO is handled by SpdyStream
-  if (aStatus == nsISocketTransport::STATUS_SENDING_TO)
-    return;
+  default:
+    // The other transport events are ignored here because there is no good
+    // way to map them to the right transaction in spdy. Instead, the events
+    // are generated again from the spdy code and passed directly to the
+    // correct transaction.
 
-  struct transportStatus status;
-  
-  status.transport = aTransport;
-  status.status = aStatus;
-  status.progress = aProgress;
+    // NS_NET_STATUS_SENDING_TO:
+    // This is generated by the socket transport when (part) of
+    // a transaction is written out
+    //
+    // There is no good way to map it to the right transaction in spdy,
+    // so it is ignored here and generated separately when the SYN_STREAM
+    // is sent from SpdyStream::TransmitFrame
 
-  mStreamTransactionHash.Enumerate(StreamTransportStatus, &status);
+    // NS_NET_STATUS_WAITING_FOR:
+    // Created by nsHttpConnection when the request has been totally sent.
+    // There is no good way to map it to the right transaction in spdy,
+    // so it is ignored here and generated separately when the same
+    // condition is complete in SpdyStream when there is no more
+    // request body left to be transmitted.
+
+    // NS_NET_STATUS_RECEIVING_FROM
+    // Generated in spdysession whenever we read a data frame or a syn_reply
+    // that can be attributed to a particular stream/transaction
+
+    break;
+  }
 }
 
 // ReadSegments() is used to write data to the network. Generally, HTTP
 // request data is pulled from the approriate transaction and
 // converted to SPDY data. Sometimes control data like window-update are
 // generated instead.
 
 nsresult
@@ -1670,16 +1678,17 @@ SpdySession::OnWriteSegment(char *buf,
     rv = mSegmentWriter->OnWriteSegment(buf, count, countWritten);
     if (NS_FAILED(rv))
       return rv;
 
     LogIO(this, mFrameDataStream, "Reading Data Frame", buf, *countWritten);
 
     mFrameDataRead += *countWritten;
     
+    mFrameDataStream->UpdateTransportReadEvents(*countWritten);
     if ((mFrameDataRead == mFrameDataSize) && !mFrameDataLast)
       ChangeDownstreamState(BUFFERING_FRAME_HEADER);
 
     return rv;
   }
   
   if (mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
     
--- a/netwerk/protocol/http/SpdyStream.cpp
+++ b/netwerk/protocol/http/SpdyStream.cpp
@@ -70,24 +70,27 @@ SpdyStream::SpdyStream(nsAHttpTransactio
     mStreamID(0),
     mChunkSize(chunkSize),
     mSynFrameComplete(0),
     mBlockedOnWrite(0),
     mRequestBlockedOnRead(0),
     mSentFinOnData(0),
     mRecvdFin(0),
     mFullyOpen(0),
+    mSentWaitingFor(0),
     mTxInlineFrameAllocation(SpdySession::kDefaultBufferSize),
     mTxInlineFrameSize(0),
     mTxInlineFrameSent(0),
     mTxStreamFrameSize(0),
     mTxStreamFrameSent(0),
     mZlib(compressionContext),
     mRequestBodyLen(0),
-    mPriority(priority)
+    mPriority(priority),
+    mTotalSent(0),
+    mTotalRead(0)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
   LOG3(("SpdyStream::SpdyStream %p", this));
 
   mTxInlineFrame = new char[mTxInlineFrameAllocation];
 }
 
@@ -453,16 +456,46 @@ SpdyStream::ParseHttpRequestHeaders(cons
     (mTxInlineFrameSize - 18) * 100 /
     (11 + mTransaction->RequestHead()->RequestURI().Length() +
      mFlatHttpRequestHeaders.Length());
   
   Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
   return NS_OK;
 }
 
+void
+SpdyStream::UpdateTransportReadEvents(PRUint32 count)
+{
+  mTotalRead += count;
+
+  mTransaction->OnTransportStatus(mSocketTransport,
+                                  NS_NET_STATUS_RECEIVING_FROM,
+                                  mTotalRead);
+}
+
+void
+SpdyStream::UpdateTransportSendEvents(PRUint32 count)
+{
+  mTotalSent += count;
+
+  if (mUpstreamState != SENDING_FIN_STREAM)
+    mTransaction->OnTransportStatus(mSocketTransport,
+                                    NS_NET_STATUS_SENDING_TO,
+                                    mTotalSent);
+
+  if (!mSentWaitingFor && !mRequestBodyLen &&
+      mTxInlineFrameSent == mTxInlineFrameSize  &&
+      mTxStreamFrameSent == mTxStreamFrameSize) {
+    mSentWaitingFor = 1;
+    mTransaction->OnTransportStatus(mSocketTransport,
+                                    NS_NET_STATUS_WAITING_FOR,
+                                    LL_ZERO);
+  }
+}
+
 nsresult
 SpdyStream::TransmitFrame(const char *buf,
                           PRUint32 *countUsed)
 {
   NS_ABORT_IF_FALSE(mTxInlineFrameSize, "empty stream frame in transmit");
   NS_ABORT_IF_FALSE(mSegmentReader, "TransmitFrame with null mSegmentReader");
   
   PRUint32 transmittedCount;
@@ -533,26 +566,21 @@ SpdyStream::TransmitFrame(const char *bu
       mBlockedOnWrite = 1;
 
     if (NS_FAILED(rv))     // this will include WOULD_BLOCK
       return rv;
     
     SpdySession::LogIO(mSession, this, "Writing from Transaction Buffer",
                        buf + offset, transmittedCount);
 
-    if (mUpstreamState == SENDING_REQUEST_BODY) {
-      mTransaction->OnTransportStatus(mSocketTransport,
-                                      nsISocketTransport::STATUS_SENDING_TO,
-                                      transmittedCount);
-    }
-    
     *countUsed += transmittedCount;
     avail -= transmittedCount;
     offset += transmittedCount;
     mTxStreamFrameSent += transmittedCount;
+    UpdateTransportSendEvents(transmittedCount);
   }
 
   if (!avail) {
     mTxInlineFrameSent = 0;
     mTxInlineFrameSize = 0;
     mTxStreamFrameSent = 0;
     mTxStreamFrameSize = 0;
   }
--- a/netwerk/protocol/http/SpdyStream.h
+++ b/netwerk/protocol/http/SpdyStream.h
@@ -84,16 +84,19 @@ public:
     return mTransaction;
   }
 
   void Close(nsresult reason);
 
   void SetRecvdFin(bool aStatus) { mRecvdFin = aStatus ? 1 : 0; }
   bool RecvdFin() { return mRecvdFin; }
 
+  void UpdateTransportSendEvents(PRUint32 count);
+  void UpdateTransportReadEvents(PRUint32 count);
+
   // The zlib header compression dictionary defined by SPDY,
   // and hooks to the mozilla allocator for zlib to use.
   static const char *kDictionary;
   static void *zlib_allocator(void *, uInt, uInt);
   static void zlib_destructor(void *, void *);
 
 private:
 
@@ -166,16 +169,19 @@ private:
 
   // Flag is set after the response frame bearing the fin bit has
   // been processed. (i.e. after the server has closed).
   PRUint32                     mRecvdFin             : 1;
 
   // Flag is set after syn reply received
   PRUint32                     mFullyOpen            : 1;
 
+  // Flag is set after the WAITING_FOR Transport event has been generated
+  PRUint32                     mSentWaitingFor       : 1;
+
   // The InlineFrame and associated data is used for composing control
   // frames and data frame headers.
   nsAutoArrayPtr<char>         mTxInlineFrame;
   PRUint32                     mTxInlineFrameAllocation;
   PRUint32                     mTxInlineFrameSize;
   PRUint32                     mTxInlineFrameSent;
 
   // mTxStreamFrameSize and mTxStreamFrameSent track the progress of
@@ -195,13 +201,16 @@ private:
   // for a stream closed indication. Relying on stream close results
   // in an extra 0-length runt packet and seems to have some interop
   // problems with the google servers.
   PRInt64                      mRequestBodyLen;
 
   // based on nsISupportsPriority definitions
   PRInt32                      mPriority;
 
+  // For Progress Events
+  PRUint64                     mTotalSent;
+  PRUint64                     mTotalRead;
 };
 
 }} // namespace mozilla::net
 
 #endif // mozilla_net_SpdyStream_h