Bug 712191 - Clean up websocket close codes and abort logic. r?mcmanus
authorJason Duell <jduell.mcbugs@gmail.com>
Wed, 11 Apr 2012 13:20:00 -0700
changeset 91483 2ffcf18c125335335eced33bfaa24b9778b88042
parent 91482 6d41ddb6b9bc49a9d02a4f5ffe2477256a5a8f22
child 91484 d9964e231f469686a109f1da0a30a107c79e2ace
push id22445
push usereakhgari@mozilla.com
push dateThu, 12 Apr 2012 16:19:55 +0000
treeherdermozilla-central@901dfde60183 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs712191
milestone14.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 712191 - Clean up websocket close codes and abort logic. r?mcmanus
netwerk/protocol/websocket/WebSocketChannel.cpp
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -723,17 +723,16 @@ WebSocketChannel::WebSocketChannel() :
 WebSocketChannel::~WebSocketChannel()
 {
   LOG(("WebSocketChannel::~WebSocketChannel() %p\n", this));
 
   if (sWebSocketAdmissions)
     sWebSocketAdmissions->DecrementSessionCount();
 
   // this stop is a nop if the normal connect/close is followed
-  mStopped = 1;
   StopSession(NS_ERROR_UNEXPECTED);
   NS_ABORT_IF_FALSE(!mOpenRunning && !mOpenBlocked, "op");
 
   moz_free(mBuffer);
   moz_free(mDynamicOutput);
   delete mCompressor;
   delete mCurrentOut;
 
@@ -797,17 +796,17 @@ WebSocketChannel::BeginOpen()
     rv = mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
     mRedirectCallback = nsnull;
     return rv;
   }
 
   nsCOMPtr<nsIChannel> localChannel = do_QueryInterface(mChannel, &rv);
   if (NS_FAILED(rv)) {
     LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
-    AbortSession(NS_ERROR_CONNECTION_REFUSED);
+    AbortSession(NS_ERROR_UNEXPECTED);
     return rv;
   }
 
   rv = localChannel->AsyncOpen(this, mHttpChannel);
   if (NS_FAILED(rv)) {
     LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
     AbortSession(NS_ERROR_CONNECTION_REFUSED);
     return rv;
@@ -901,17 +900,16 @@ WebSocketChannel::ProcessInput(PRUint8 *
 
   if (!mBuffered) {
     // Most of the time we can process right off the stack buffer without
     // having to accumulate anything
     mFramePtr = buffer;
     avail = count;
   } else {
     if (!UpdateReadBuffer(buffer, count, mFragmentAccumulator, &avail)) {
-      AbortSession(NS_ERROR_FILE_TOO_BIG);
       return NS_ERROR_FILE_TOO_BIG;
     }
   }
 
   PRUint8 *payload;
   PRUint32 totalAvail = avail;
 
   while (avail >= 2) {
@@ -940,34 +938,32 @@ WebSocketChannel::ProcessInput(PRUint8 *
       framingLength += 8;
       if (avail < framingLength)
         break;
 
       if (mFramePtr[2] & 0x80) {
         // Section 4.2 says that the most significant bit MUST be
         // 0. (i.e. this is really a 63 bit value)
         LOG(("WebSocketChannel:: high bit of 64 bit length set"));
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
       // copy this in case it is unaligned
       PRUint64 tempLen;
       memcpy(&tempLen, mFramePtr + 2, 8);
       payloadLength = PR_ntohll(tempLen);
     }
 
     payload = mFramePtr + framingLength;
     avail -= framingLength;
 
     LOG(("WebSocketChannel::ProcessInput: payload %lld avail %lu\n",
          payloadLength, avail));
 
     if (payloadLength + mFragmentAccumulator > mMaxMessageSize) {
-      AbortSession(NS_ERROR_FILE_TOO_BIG);
       return NS_ERROR_FILE_TOO_BIG;
     }
 
     if (avail < payloadLength)
       break;
 
     LOG(("WebSocketChannel::ProcessInput: Frame accumulated - opcode %d\n",
          opcode));
@@ -981,45 +977,41 @@ WebSocketChannel::ProcessInput(PRUint8 *
       memcpy(&mask, payload - 4, 4);
       mask = PR_ntohl(mask);
       ApplyMask(mask, payload, payloadLength);
     }
 
     // Control codes are required to have the fin bit set
     if (!finBit && (opcode & kControlFrameMask)) {
       LOG(("WebSocketChannel:: fragmented control frame code %d\n", opcode));
-      AbortSession(NS_ERROR_ILLEGAL_VALUE);
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (rsvBits) {
       LOG(("WebSocketChannel:: unexpected reserved bits %x\n", rsvBits));
-      AbortSession(NS_ERROR_ILLEGAL_VALUE);
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (!finBit || opcode == kContinuation) {
       // This is part of a fragment response
 
       // Only the first frame has a non zero op code: Make sure we don't see a
       // first frame while some old fragments are open
       if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) {
         LOG(("WebSocketChannel:: nested fragments\n"));
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
       LOG(("WebSocketChannel:: Accumulating Fragment %lld\n", payloadLength));
 
       if (opcode == kContinuation) {
 
         // Make sure this continuation fragment isn't the first fragment
         if (mFragmentOpcode == kContinuation) {
           LOG(("WebSocketHeandler:: continuation code in first fragment\n"));
-          AbortSession(NS_ERROR_ILLEGAL_VALUE);
           return NS_ERROR_ILLEGAL_VALUE;
         }
 
         // For frag > 1 move the data body back on top of the headers
         // so we have contiguous stream of data
         NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
                           "payload offset from frameptr wrong");
         ::memmove(mFramePtr, payload, avail);
@@ -1043,17 +1035,16 @@ WebSocketChannel::ProcessInput(PRUint8 *
         opcode = kContinuation;
         mFragmentAccumulator += payloadLength;
       }
     } else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) {
       // This frame is not part of a fragment sequence but we
       // have an open fragment.. it must be a control code or else
       // we have a problem
       LOG(("WebSocketChannel:: illegal fragment sequence\n"));
-      AbortSession(NS_ERROR_ILLEGAL_VALUE);
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (mServerClosed) {
       LOG(("WebSocketChannel:: ignoring read frame code %d after close\n",
                  opcode));
       // nop
     } else if (mStopped) {
@@ -1062,28 +1053,26 @@ WebSocketChannel::ProcessInput(PRUint8 *
     } else if (opcode == kText) {
       LOG(("WebSocketChannel:: text frame received\n"));
       if (mListener) {
         nsCString utf8Data((const char *)payload, payloadLength);
 
         // Section 8.1 says to fail connection if invalid utf-8 in text message
         if (!IsUTF8(utf8Data, false)) {
           LOG(("WebSocketChannel:: text frame invalid utf-8\n"));
-          AbortSession(NS_ERROR_CANNOT_CONVERT_DATA);
-          return NS_ERROR_ILLEGAL_VALUE;
+          return NS_ERROR_CANNOT_CONVERT_DATA;
         }
 
         NS_DispatchToMainThread(new CallOnMessageAvailable(this, utf8Data, -1));
       }
     } else if (opcode & kControlFrameMask) {
       // control frames
       if (payloadLength > 125) {
         LOG(("WebSocketChannel:: bad control frame code %d length %d\n",
              opcode, payloadLength));
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
       if (opcode == kClose) {
         LOG(("WebSocketChannel:: close received\n"));
         mServerClosed = 1;
 
         mServerCloseCode = CLOSE_NO_STATUS;
@@ -1098,18 +1087,17 @@ WebSocketChannel::ProcessInput(PRUint8 *
                    (const char *)payload + 2, msglen);
 
             // section 8.1 says to replace received non utf-8 sequences
             // (which are non-conformant to send) with u+fffd,
             // but secteam feels that silently rewriting messages is
             // inappropriate - so we will fail the connection instead.
             if (!IsUTF8(mServerCloseReason, false)) {
               LOG(("WebSocketChannel:: close frame invalid utf-8\n"));
-              AbortSession(NS_ERROR_ILLEGAL_VALUE);
-              return NS_ERROR_ILLEGAL_VALUE;
+              return NS_ERROR_CANNOT_CONVERT_DATA;
             }
 
             LOG(("WebSocketChannel:: close msg %s\n",
                  mServerCloseReason.get()));
           }
         }
 
         if (mCloseTimer) {
@@ -1128,17 +1116,16 @@ WebSocketChannel::ProcessInput(PRUint8 *
         GeneratePong(payload, payloadLength);
       } else if (opcode == kPong) {
         // opcode kPong: the mere act of receiving the packet is all we need
         // to do for the pong to trigger the activity timers
         LOG(("WebSocketChannel:: pong received\n"));
       } else {
         /* unknown control frame opcode */
         LOG(("WebSocketChannel:: unknown control op code %d\n", opcode));
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
       if (mFragmentAccumulator) {
         // Remove the control frame from the stream so we have a contiguous
         // data buffer of reassembled fragments
         LOG(("WebSocketChannel:: Removing Control From Read buffer\n"));
         NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
@@ -1155,17 +1142,16 @@ WebSocketChannel::ProcessInput(PRUint8 *
       if (mListener) {
         nsCString binaryData((const char *)payload, payloadLength);
         NS_DispatchToMainThread(new CallOnMessageAvailable(this, binaryData,
                                                            payloadLength));
       }
     } else if (opcode != kContinuation) {
       /* unknown opcode */
       LOG(("WebSocketChannel:: unknown op code %d\n", opcode));
-      AbortSession(NS_ERROR_ILLEGAL_VALUE);
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
     mFramePtr = payload + payloadLength;
     avail -= payloadLength;
     totalAvail = avail;
   }
 
@@ -1176,28 +1162,26 @@ WebSocketChannel::ProcessInput(PRUint8 *
   if (!IsPersistentFramePtr()) {
     mBuffered = 0;
 
     if (mFragmentAccumulator) {
       LOG(("WebSocketChannel:: Setup Buffer due to fragment"));
 
       if (!UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
                             totalAvail + mFragmentAccumulator, 0, nsnull)) {
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
-        return NS_ERROR_ILLEGAL_VALUE;
+        return NS_ERROR_FILE_TOO_BIG;
       }
 
       // UpdateReadBuffer will reset the frameptr to the beginning
       // of new saved state, so we need to skip past processed framgents
       mFramePtr += mFragmentAccumulator;
     } else if (totalAvail) {
       LOG(("WebSocketChannel:: Setup Buffer due to partial frame"));
       if (!UpdateReadBuffer(mFramePtr, totalAvail, 0, nsnull)) {
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
-        return NS_ERROR_ILLEGAL_VALUE;
+        return NS_ERROR_FILE_TOO_BIG;
       }
     }
   } else if (!mFragmentAccumulator && !totalAvail) {
     // If we were working off a saved buffer state and there is no partial
     // frame or fragment in process, then revert to stack behavior
     LOG(("WebSocketChannel:: Internal buffering not needed anymore"));
     mBuffered = 0;
 
@@ -1290,27 +1274,28 @@ WebSocketChannel::EnqueueOutgoingMessage
 }
 
 
 PRUint16
 WebSocketChannel::ResultToCloseCode(nsresult resultCode)
 {
   if (NS_SUCCEEDED(resultCode))
     return CLOSE_NORMAL;
-  if (resultCode == NS_ERROR_FILE_TOO_BIG)
-    return CLOSE_TOO_LARGE;
-  if (resultCode == NS_BASE_STREAM_CLOSED ||
-      resultCode == NS_ERROR_NET_TIMEOUT ||
-      resultCode == NS_ERROR_CONNECTION_REFUSED) {
-    return CLOSE_ABNORMAL;
+
+  switch (resultCode) {
+    case NS_ERROR_FILE_TOO_BIG:
+    case NS_ERROR_OUT_OF_MEMORY:
+      return CLOSE_TOO_LARGE;
+    case NS_ERROR_CANNOT_CONVERT_DATA:
+      return CLOSE_INVALID_PAYLOAD;
+    case NS_ERROR_UNEXPECTED:
+      return CLOSE_INTERNAL_ERROR;
+    default:
+      return CLOSE_PROTOCOL_ERROR;
   }
-  if (resultCode == NS_ERROR_CANNOT_CONVERT_DATA)
-    return CLOSE_INVALID_PAYLOAD;
-
-  return CLOSE_PROTOCOL_ERROR;
 }
 
 void
 WebSocketChannel::PrimeNewOutgoingMessage()
 {
   LOG(("WebSocketChannel::PrimeNewOutgoingMessage() %p\n", this));
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
   NS_ABORT_IF_FALSE(!mCurrentOut, "Current message in progress");
@@ -1409,17 +1394,17 @@ WebSocketChannel::PrimeNewOutgoingMessag
       mOutHeader[0] = kFinalFragBit | kText;
       break;
     case kMsgTypeStream:
       // HACK ALERT:  read in entire stream into string.
       // Will block socket transport thread if file is blocking.
       // TODO: bug 704447:  don't block socket thread!
       rv = mCurrentOut->ConvertStreamToString();
       if (NS_FAILED(rv)) {
-        AbortSession(rv);
+        AbortSession(NS_ERROR_FILE_TOO_BIG);
         return;
       }
       // Now we're a binary string
       msgType = kMsgTypeBinaryString;
 
       // no break: fall down into binary string case
 
     case kMsgTypeBinaryString:
@@ -1569,18 +1554,17 @@ WebSocketChannel::CleanupConnection()
 void
 WebSocketChannel::StopSession(nsresult reason)
 {
   LOG(("WebSocketChannel::StopSession() %p [%x]\n", this, reason));
 
   // normally this should be called on socket thread, but it is ok to call it
   // from OnStartRequest before the socket thread machine has gotten underway
 
-  NS_ABORT_IF_FALSE(mStopped,
-                    "stopsession() has not transitioned through abort or close");
+  mStopped = 1;
 
   if (!mChannelWasOpened) {
     // The HTTP channel information will never be used in this case
     mChannel = nsnull;
     mHttpChannel = nsnull;
     mLoadGroup = nsnull;
     mCallbacks = nsnull;
   }
@@ -1712,17 +1696,16 @@ void
 WebSocketChannel::ReleaseSession()
 {
   LOG(("WebSocketChannel::ReleaseSession() %p stopped = %d\n",
        this, mStopped));
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
 
   if (mStopped)
     return;
-  mStopped = 1;
   StopSession(NS_OK);
 }
 
 nsresult
 WebSocketChannel::HandleExtensions()
 {
   LOG(("WebSocketChannel::HandleExtensions() %p\n", this));
 
@@ -2488,17 +2471,17 @@ WebSocketChannel::OnStartRequest(nsIRequ
         }
       }
     }
   }
 
   if (NS_FAILED(rv)) {
     LOG(("WebSocketChannel::OnStartRequest: "
          "HTTP response header Upgrade: websocket not found\n"));
-    AbortSession(rv);
+    AbortSession(NS_ERROR_ILLEGAL_VALUE);
     return rv;
   }
 
   nsCAutoString respConnection;
   rv = mHttpChannel->GetResponseHeader(
     NS_LITERAL_CSTRING("Connection"), respConnection);
 
   if (NS_SUCCEEDED(rv)) {
@@ -2512,30 +2495,30 @@ WebSocketChannel::OnStartRequest(nsIRequ
         }
       }
     }
   }
 
   if (NS_FAILED(rv)) {
     LOG(("WebSocketChannel::OnStartRequest: "
          "HTTP response header 'Connection: Upgrade' not found\n"));
-    AbortSession(rv);
+    AbortSession(NS_ERROR_ILLEGAL_VALUE);
     return rv;
   }
 
   nsCAutoString respAccept;
   rv = mHttpChannel->GetResponseHeader(
                        NS_LITERAL_CSTRING("Sec-WebSocket-Accept"),
                        respAccept);
 
   if (NS_FAILED(rv) ||
     respAccept.IsEmpty() || !respAccept.Equals(mHashedSecret)) {
     LOG(("WebSocketChannel::OnStartRequest: "
          "HTTP response header Sec-WebSocket-Accept check failed\n"));
-    LOG(("WebSocketChannel::OnStartRequest: Expected %s recevied %s\n",
+    LOG(("WebSocketChannel::OnStartRequest: Expected %s received %s\n",
          mHashedSecret.get(), respAccept.get()));
     AbortSession(NS_ERROR_ILLEGAL_VALUE);
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   // If we sent a sub protocol header, verify the response matches
   // If it does not, set mProtocol to "" so the protocol attribute
   // of the WebSocket JS object reflects that
@@ -2765,35 +2748,39 @@ WebSocketChannel::OnDataAvailable(nsIReq
     // This is the deflate decoder
 
     LOG(("WebSocketChannel::OnDataAvailable: Deflate Data %u\n",
              aCount));
 
     PRUint8  buffer[2048];
     PRUint32 maxRead;
     PRUint32 count;
-    nsresult rv;
+    nsresult rv = NS_OK;  // aCount always > 0, so this just avoids warning
 
     while (aCount > 0) {
       if (mStopped)
         return NS_BASE_STREAM_CLOSED;
 
       maxRead = NS_MIN(2048U, aCount);
       rv = aInputStream->Read((char *)buffer, maxRead, &count);
       LOG(("WebSocketChannel::OnDataAvailable: InflateRead read %u rv %x\n",
            count, rv));
       if (NS_FAILED(rv) || count == 0) {
-        AbortSession(rv);
+        AbortSession(NS_ERROR_UNEXPECTED);
         break;
       }
 
       aCount -= count;
       rv = ProcessInput(buffer, count);
+      if (NS_FAILED(rv)) {
+        AbortSession(rv);
+        break;
+      }
     }
-    return NS_OK;
+    return rv;
   }
 
   if (aContext == mSocketOut) {
     // This is the deflate encoder
 
     PRUint32 maxRead;
     PRUint32 count;
     nsresult rv;