LCOV - code coverage report
Current view: directory - netwerk/protocol/websocket - WebSocketChannel.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1362 4 0.3 %
Date: 2012-06-02 Functions: 102 1 1.0 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set sw=2 ts=8 et tw=80 : */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2010 2011
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
      25                 :  *   Patrick McManus <mcmanus@ducksong.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "WebSocketLog.h"
      42                 : #include "WebSocketChannel.h"
      43                 : 
      44                 : #include "nsISocketTransportService.h"
      45                 : #include "nsIURI.h"
      46                 : #include "nsIChannel.h"
      47                 : #include "nsICryptoHash.h"
      48                 : #include "nsIRunnable.h"
      49                 : #include "nsIPrefBranch.h"
      50                 : #include "nsIPrefService.h"
      51                 : #include "nsICancelable.h"
      52                 : #include "nsIDNSRecord.h"
      53                 : #include "nsIDNSService.h"
      54                 : #include "nsIStreamConverterService.h"
      55                 : #include "nsIIOService2.h"
      56                 : #include "nsIProtocolProxyService.h"
      57                 : 
      58                 : #include "nsAutoPtr.h"
      59                 : #include "nsStandardURL.h"
      60                 : #include "nsNetCID.h"
      61                 : #include "nsServiceManagerUtils.h"
      62                 : #include "nsXPIDLString.h"
      63                 : #include "nsCRT.h"
      64                 : #include "nsThreadUtils.h"
      65                 : #include "nsNetError.h"
      66                 : #include "nsStringStream.h"
      67                 : #include "nsAlgorithm.h"
      68                 : #include "nsProxyRelease.h"
      69                 : #include "nsNetUtil.h"
      70                 : 
      71                 : #include "plbase64.h"
      72                 : #include "prmem.h"
      73                 : #include "prnetdb.h"
      74                 : #include "prbit.h"
      75                 : #include "zlib.h"
      76                 : 
      77                 : extern PRThread *gSocketThread;
      78                 : 
      79                 : namespace mozilla {
      80                 : namespace net {
      81                 : 
      82               0 : NS_IMPL_THREADSAFE_ISUPPORTS11(WebSocketChannel,
      83                 :                                nsIWebSocketChannel,
      84                 :                                nsIHttpUpgradeListener,
      85                 :                                nsIRequestObserver,
      86                 :                                nsIStreamListener,
      87                 :                                nsIProtocolHandler,
      88                 :                                nsIInputStreamCallback,
      89                 :                                nsIOutputStreamCallback,
      90                 :                                nsITimerCallback,
      91                 :                                nsIDNSListener,
      92                 :                                nsIInterfaceRequestor,
      93                 :                                nsIChannelEventSink)
      94                 : 
      95                 : // We implement RFC 6455, which uses Sec-WebSocket-Version: 13 on the wire.
      96                 : #define SEC_WEBSOCKET_VERSION "13"
      97                 : 
      98                 : /*
      99                 :  * About SSL unsigned certificates
     100                 :  *
     101                 :  * wss will not work to a host using an unsigned certificate unless there
     102                 :  * is already an exception (i.e. it cannot popup a dialog asking for
     103                 :  * a security exception). This is similar to how an inlined img will
     104                 :  * fail without a dialog if fails for the same reason. This should not
     105                 :  * be a problem in practice as it is expected the websocket javascript
     106                 :  * is served from the same host as the websocket server (or of course,
     107                 :  * a valid cert could just be provided).
     108                 :  *
     109                 :  */
     110                 : 
     111                 : // some helper classes
     112                 : 
     113                 : //-----------------------------------------------------------------------------
     114                 : // CallOnMessageAvailable
     115                 : //-----------------------------------------------------------------------------
     116                 : 
     117                 : class CallOnMessageAvailable : public nsIRunnable
     118                 : {
     119                 : public:
     120                 :   NS_DECL_ISUPPORTS
     121                 : 
     122               0 :   CallOnMessageAvailable(WebSocketChannel *aChannel,
     123                 :                          nsCString        &aData,
     124                 :                          PRInt32           aLen)
     125                 :     : mChannel(aChannel),
     126                 :       mData(aData),
     127               0 :       mLen(aLen) {}
     128                 : 
     129               0 :   NS_IMETHOD Run()
     130                 :   {
     131               0 :     if (mLen < 0)
     132               0 :       mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData);
     133                 :     else
     134               0 :       mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext, mData);
     135               0 :     return NS_OK;
     136                 :   }
     137                 : 
     138                 : private:
     139               0 :   ~CallOnMessageAvailable() {}
     140                 : 
     141                 :   nsRefPtr<WebSocketChannel>        mChannel;
     142                 :   nsCString                         mData;
     143                 :   PRInt32                           mLen;
     144                 : };
     145               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnMessageAvailable, nsIRunnable)
     146                 : 
     147                 : //-----------------------------------------------------------------------------
     148                 : // CallOnStop
     149                 : //-----------------------------------------------------------------------------
     150                 : 
     151                 : class CallOnStop : public nsIRunnable
     152                 : {
     153                 : public:
     154                 :   NS_DECL_ISUPPORTS
     155                 : 
     156               0 :   CallOnStop(WebSocketChannel *aChannel,
     157                 :              nsresult          aData)
     158                 :     : mChannel(aChannel),
     159               0 :       mData(aData) {}
     160                 : 
     161               0 :   NS_IMETHOD Run()
     162                 :   {
     163               0 :     NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
     164               0 :     mChannel->mListener->OnStop(mChannel->mContext, mData);
     165               0 :     return NS_OK;
     166                 :   }
     167                 : 
     168                 : private:
     169               0 :   ~CallOnStop() {}
     170                 : 
     171                 :   nsRefPtr<WebSocketChannel>        mChannel;
     172                 :   nsresult                          mData;
     173                 : };
     174               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnStop, nsIRunnable)
     175                 : 
     176                 : //-----------------------------------------------------------------------------
     177                 : // CallOnServerClose
     178                 : //-----------------------------------------------------------------------------
     179                 : 
     180                 : class CallOnServerClose : public nsIRunnable
     181                 : {
     182                 : public:
     183                 :   NS_DECL_ISUPPORTS
     184                 : 
     185               0 :   CallOnServerClose(WebSocketChannel *aChannel,
     186                 :                     PRUint16          aCode,
     187                 :                     nsCString        &aReason)
     188                 :     : mChannel(aChannel),
     189                 :       mCode(aCode),
     190               0 :       mReason(aReason) {}
     191                 : 
     192               0 :   NS_IMETHOD Run()
     193                 :   {
     194               0 :     mChannel->mListener->OnServerClose(mChannel->mContext, mCode, mReason);
     195               0 :     return NS_OK;
     196                 :   }
     197                 : 
     198                 : private:
     199               0 :   ~CallOnServerClose() {}
     200                 : 
     201                 :   nsRefPtr<WebSocketChannel>        mChannel;
     202                 :   PRUint16                          mCode;
     203                 :   nsCString                         mReason;
     204                 : };
     205               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnServerClose, nsIRunnable)
     206                 : 
     207                 : //-----------------------------------------------------------------------------
     208                 : // CallAcknowledge
     209                 : //-----------------------------------------------------------------------------
     210                 : 
     211                 : class CallAcknowledge : public nsIRunnable
     212                 : {
     213                 : public:
     214                 :   NS_DECL_ISUPPORTS
     215                 : 
     216               0 :   CallAcknowledge(WebSocketChannel *aChannel,
     217                 :                   PRUint32          aSize)
     218                 :     : mChannel(aChannel),
     219               0 :       mSize(aSize) {}
     220                 : 
     221               0 :   NS_IMETHOD Run()
     222                 :   {
     223               0 :     LOG(("WebSocketChannel::CallAcknowledge: Size %u\n", mSize));
     224               0 :     mChannel->mListener->OnAcknowledge(mChannel->mContext, mSize);
     225               0 :     return NS_OK;
     226                 :   }
     227                 : 
     228                 : private:
     229               0 :   ~CallAcknowledge() {}
     230                 : 
     231                 :   nsRefPtr<WebSocketChannel>        mChannel;
     232                 :   PRUint32                          mSize;
     233                 : };
     234               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallAcknowledge, nsIRunnable)
     235                 : 
     236                 : //-----------------------------------------------------------------------------
     237                 : // OutboundMessage
     238                 : //-----------------------------------------------------------------------------
     239                 : 
     240                 : enum WsMsgType {
     241                 :   kMsgTypeString = 0,
     242                 :   kMsgTypeBinaryString,
     243                 :   kMsgTypeStream,
     244                 :   kMsgTypePing,
     245                 :   kMsgTypePong,
     246                 :   kMsgTypeFin
     247                 : };
     248                 : 
     249                 : static const char* msgNames[] = {
     250                 :   "text",
     251                 :   "binaryString",
     252                 :   "binaryStream",
     253                 :   "ping",
     254                 :   "pong",
     255                 :   "close"
     256                 : };
     257                 : 
     258                 : class OutboundMessage
     259                 : {
     260                 : public:
     261               0 :   OutboundMessage(WsMsgType type, nsCString *str)
     262               0 :     : mMsgType(type)
     263                 :   {
     264               0 :     MOZ_COUNT_CTOR(OutboundMessage);
     265               0 :     mMsg.pString = str;
     266               0 :     mLength = str ? str->Length() : 0;
     267               0 :   }
     268                 : 
     269               0 :   OutboundMessage(nsIInputStream *stream, PRUint32 length)
     270               0 :     : mMsgType(kMsgTypeStream), mLength(length)
     271                 :   {
     272               0 :     MOZ_COUNT_CTOR(OutboundMessage);
     273               0 :     mMsg.pStream = stream;
     274               0 :     mMsg.pStream->AddRef();
     275               0 :   }
     276                 : 
     277               0 :  ~OutboundMessage() {
     278               0 :     MOZ_COUNT_DTOR(OutboundMessage);
     279               0 :     switch (mMsgType) {
     280                 :       case kMsgTypeString:
     281                 :       case kMsgTypeBinaryString:
     282                 :       case kMsgTypePing:
     283                 :       case kMsgTypePong:
     284               0 :         delete mMsg.pString;
     285               0 :         break;
     286                 :       case kMsgTypeStream:
     287                 :         // for now this only gets hit if msg deleted w/o being sent
     288               0 :         if (mMsg.pStream) {
     289               0 :           mMsg.pStream->Close();
     290               0 :           mMsg.pStream->Release();
     291                 :         }
     292               0 :         break;
     293                 :       case kMsgTypeFin:
     294               0 :         break;    // do-nothing: avoid compiler warning
     295                 :     }
     296               0 :   }
     297                 : 
     298               0 :   WsMsgType GetMsgType() const { return mMsgType; }
     299               0 :   PRInt32 Length() const { return mLength; }
     300                 : 
     301               0 :   PRUint8* BeginWriting() {
     302               0 :     NS_ABORT_IF_FALSE(mMsgType != kMsgTypeStream,
     303                 :                       "Stream should have been converted to string by now");
     304               0 :     return (PRUint8 *)(mMsg.pString ? mMsg.pString->BeginWriting() : nsnull);
     305                 :   }
     306                 : 
     307               0 :   PRUint8* BeginReading() {
     308               0 :     NS_ABORT_IF_FALSE(mMsgType != kMsgTypeStream,
     309                 :                       "Stream should have been converted to string by now");
     310               0 :     return (PRUint8 *)(mMsg.pString ? mMsg.pString->BeginReading() : nsnull);
     311                 :   }
     312                 : 
     313               0 :   nsresult ConvertStreamToString()
     314                 :   {
     315               0 :     NS_ABORT_IF_FALSE(mMsgType == kMsgTypeStream, "Not a stream!");
     316                 : 
     317                 : #ifdef DEBUG
     318                 :     // Make sure we got correct length from Blob
     319                 :     PRUint32 bytes;
     320               0 :     mMsg.pStream->Available(&bytes);
     321               0 :     NS_ASSERTION(bytes == mLength, "Stream length != blob length!");
     322                 : #endif
     323                 : 
     324               0 :     nsAutoPtr<nsCString> temp(new nsCString());
     325               0 :     nsresult rv = NS_ReadInputStreamToString(mMsg.pStream, *temp, mLength);
     326                 : 
     327               0 :     NS_ENSURE_SUCCESS(rv, rv);
     328                 : 
     329               0 :     mMsg.pStream->Close();
     330               0 :     mMsg.pStream->Release();
     331               0 :     mMsg.pString = temp.forget();
     332               0 :     mMsgType = kMsgTypeBinaryString;
     333                 : 
     334               0 :     return NS_OK;
     335                 :   }
     336                 : 
     337                 : private:
     338                 :   union {
     339                 :     nsCString      *pString;
     340                 :     nsIInputStream *pStream;
     341                 :   }                           mMsg;
     342                 :   WsMsgType                   mMsgType;
     343                 :   PRUint32                    mLength;
     344                 : };
     345                 : 
     346                 : //-----------------------------------------------------------------------------
     347                 : // OutboundEnqueuer
     348                 : //-----------------------------------------------------------------------------
     349                 : 
     350                 : class OutboundEnqueuer : public nsIRunnable
     351                 : {
     352                 : public:
     353                 :   NS_DECL_ISUPPORTS
     354                 : 
     355               0 :   OutboundEnqueuer(WebSocketChannel *aChannel, OutboundMessage *aMsg)
     356               0 :     : mChannel(aChannel), mMessage(aMsg) {}
     357                 : 
     358               0 :   NS_IMETHOD Run()
     359                 :   {
     360               0 :     mChannel->EnqueueOutgoingMessage(mChannel->mOutgoingMessages, mMessage);
     361               0 :     return NS_OK;
     362                 :   }
     363                 : 
     364                 : private:
     365               0 :   ~OutboundEnqueuer() {}
     366                 : 
     367                 :   nsRefPtr<WebSocketChannel>  mChannel;
     368                 :   OutboundMessage            *mMessage;
     369                 : };
     370               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(OutboundEnqueuer, nsIRunnable)
     371                 : 
     372                 : //-----------------------------------------------------------------------------
     373                 : // nsWSAdmissionManager
     374                 : //-----------------------------------------------------------------------------
     375                 : 
     376                 : // Section 4.1 requires that only a single websocket at a time can be CONNECTING
     377                 : // to any given IP address (or hostname, if proxy doing DNS for us). This class
     378                 : // ensures that we delay connecting until any pending connection for the same
     379                 : // IP/addr is complete (i.e. until before the 101 upgrade complete response
     380                 : // comes back and an 'open' javascript event is created)
     381                 : 
     382                 : class nsWSAdmissionManager
     383                 : {
     384                 : public:
     385               0 :   nsWSAdmissionManager() : mSessionCount(0)
     386                 :   {
     387               0 :     MOZ_COUNT_CTOR(nsWSAdmissionManager);
     388               0 :   }
     389                 : 
     390                 :   class nsOpenConn
     391                 :   {
     392                 :   public:
     393               0 :     nsOpenConn(nsCString &addr, WebSocketChannel *channel)
     394               0 :       : mAddress(addr), mChannel(channel) { MOZ_COUNT_CTOR(nsOpenConn); }
     395               0 :     ~nsOpenConn() { MOZ_COUNT_DTOR(nsOpenConn); }
     396                 : 
     397                 :     nsCString mAddress;
     398                 :     WebSocketChannel *mChannel;
     399                 :   };
     400                 : 
     401               0 :   ~nsWSAdmissionManager()
     402               0 :   {
     403               0 :     MOZ_COUNT_DTOR(nsWSAdmissionManager);
     404               0 :     for (PRUint32 i = 0; i < mData.Length(); i++)
     405               0 :       delete mData[i];
     406               0 :   }
     407                 : 
     408               0 :   bool ConditionallyConnect(nsCString &aStr, WebSocketChannel *ws)
     409                 :   {
     410               0 :     NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
     411                 : 
     412                 :     // if aStr is not in mData then we return true, else false.
     413                 :     // in either case aStr is then added to mData - meaning
     414                 :     // there will be duplicates when this function has been
     415                 :     // called with the same parameter multiple times.
     416                 : 
     417                 :     // we could hash this, but the dataset is expected to be
     418                 :     // small
     419                 : 
     420                 :     // There may already be another WS channel connecting to this IP address, in
     421                 :     // which case we'll still create a new nsOpenConn but defer BeginOpen until
     422                 :     // that channel completes connecting.
     423               0 :     bool found = (IndexOf(aStr) >= 0);
     424               0 :     nsOpenConn *newdata = new nsOpenConn(aStr, ws);
     425               0 :     mData.AppendElement(newdata);
     426                 : 
     427               0 :     NS_ABORT_IF_FALSE (!ws->mOpenRunning && !ws->mOpenBlocked,
     428                 :                        "opening state");
     429                 : 
     430               0 :     if (!found) {
     431               0 :       ws->mOpenRunning = 1;
     432               0 :       ws->BeginOpen();
     433                 :     } else {
     434               0 :       ws->mOpenBlocked = 1;
     435                 :     }
     436                 : 
     437               0 :     return !found;
     438                 :   }
     439                 : 
     440               0 :   bool Complete(WebSocketChannel *aChannel)
     441                 :   {
     442               0 :     NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
     443               0 :     NS_ABORT_IF_FALSE(!aChannel->mOpenBlocked,
     444                 :                       "blocked, but complete nsOpenConn");
     445                 : 
     446                 :     // It is possible this has already been canceled
     447               0 :     if (!aChannel->mOpenRunning)
     448               0 :       return false;
     449                 : 
     450               0 :     PRInt32 index = IndexOf(aChannel);
     451               0 :     NS_ABORT_IF_FALSE(index >= 0, "completed connection not in open list");
     452                 : 
     453               0 :     aChannel->mOpenRunning = 0;
     454               0 :     nsOpenConn *olddata = mData[index];
     455               0 :     mData.RemoveElementAt(index);
     456               0 :     delete olddata;
     457                 : 
     458                 :     // are there more of the same address pending dispatch?
     459               0 :     return ConnectNext(aChannel->mAddress);
     460                 :   }
     461                 : 
     462               0 :   bool Cancel(WebSocketChannel *aChannel)
     463                 :   {
     464               0 :     NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
     465               0 :     PRInt32 index = IndexOf(aChannel);
     466               0 :     NS_ABORT_IF_FALSE(index >= 0, "Cancelled connection not in open list");
     467               0 :     NS_ABORT_IF_FALSE(aChannel->mOpenRunning ^ aChannel->mOpenBlocked,
     468                 :                       "cancel without running xor blocked");
     469                 : 
     470               0 :     bool wasRunning = aChannel->mOpenRunning;
     471               0 :     aChannel->mOpenRunning = 0;
     472               0 :     aChannel->mOpenBlocked = 0;
     473               0 :     nsOpenConn *olddata = mData[index];
     474               0 :     mData.RemoveElementAt(index);
     475               0 :     delete olddata;
     476                 : 
     477                 :     // if we are running we can run another one
     478               0 :     if (wasRunning)
     479               0 :       return ConnectNext(aChannel->mAddress);
     480                 : 
     481               0 :     return false;
     482                 :   }
     483                 : 
     484               0 :   bool ConnectNext(nsCString &hostName)
     485                 :   {
     486               0 :     PRInt32 index = IndexOf(hostName);
     487               0 :     if (index >= 0) {
     488               0 :       WebSocketChannel *chan = mData[index]->mChannel;
     489                 : 
     490               0 :       NS_ABORT_IF_FALSE(chan->mOpenBlocked,
     491                 :                         "transaction not blocked but in queue");
     492               0 :       NS_ABORT_IF_FALSE(!chan->mOpenRunning, "transaction already running");
     493                 : 
     494               0 :       chan->mOpenBlocked = 0;
     495               0 :       chan->mOpenRunning = 1;
     496               0 :       chan->BeginOpen();
     497               0 :       return true;
     498                 :     }
     499                 : 
     500               0 :     return false;
     501                 :   }
     502                 : 
     503               0 :   void IncrementSessionCount()
     504                 :   {
     505               0 :     PR_ATOMIC_INCREMENT(&mSessionCount);
     506               0 :   }
     507                 : 
     508               0 :   void DecrementSessionCount()
     509                 :   {
     510               0 :     PR_ATOMIC_DECREMENT(&mSessionCount);
     511               0 :   }
     512                 : 
     513               0 :   PRInt32 SessionCount()
     514                 :   {
     515               0 :     return mSessionCount;
     516                 :   }
     517                 : 
     518                 : private:
     519                 :   nsTArray<nsOpenConn *> mData;
     520                 : 
     521               0 :   PRInt32 IndexOf(nsCString &aStr)
     522                 :   {
     523               0 :     for (PRUint32 i = 0; i < mData.Length(); i++)
     524               0 :       if (aStr == (mData[i])->mAddress)
     525               0 :         return i;
     526               0 :     return -1;
     527                 :   }
     528                 : 
     529               0 :   PRInt32 IndexOf(WebSocketChannel *aChannel)
     530                 :   {
     531               0 :     for (PRUint32 i = 0; i < mData.Length(); i++)
     532               0 :       if (aChannel == (mData[i])->mChannel)
     533               0 :         return i;
     534               0 :     return -1;
     535                 :   }
     536                 : 
     537                 :   // SessionCount might be decremented from the main or the socket
     538                 :   // thread, so manage it with atomic counters
     539                 :   PRInt32 mSessionCount;
     540                 : };
     541                 : 
     542                 : //-----------------------------------------------------------------------------
     543                 : // nsWSCompression
     544                 : //
     545                 : // similar to nsDeflateConverter except for the mandatory FLUSH calls
     546                 : // required by websocket and the absence of the deflate termination
     547                 : // block which is appropriate because it would create data bytes after
     548                 : // sending the websockets CLOSE message.
     549                 : //-----------------------------------------------------------------------------
     550                 : 
     551                 : class nsWSCompression
     552                 : {
     553                 : public:
     554               0 :   nsWSCompression(nsIStreamListener *aListener,
     555                 :                   nsISupports *aContext)
     556                 :     : mActive(false),
     557                 :       mContext(aContext),
     558               0 :       mListener(aListener)
     559                 :   {
     560               0 :     MOZ_COUNT_CTOR(nsWSCompression);
     561                 : 
     562               0 :     mZlib.zalloc = allocator;
     563               0 :     mZlib.zfree = destructor;
     564               0 :     mZlib.opaque = Z_NULL;
     565                 : 
     566                 :     // Initialize the compressor - these are all the normal zlib
     567                 :     // defaults except window size is set to -15 instead of +15.
     568                 :     // This is the zlib way of specifying raw RFC 1951 output instead
     569                 :     // of the zlib rfc 1950 format which has a 2 byte header and
     570                 :     // adler checksum as a trailer
     571                 : 
     572                 :     nsresult rv;
     573               0 :     mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
     574               0 :     if (NS_SUCCEEDED(rv) && aContext && aListener &&
     575               0 :       deflateInit2(&mZlib, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8,
     576                 :                    Z_DEFAULT_STRATEGY) == Z_OK) {
     577               0 :       mActive = true;
     578                 :     }
     579               0 :   }
     580                 : 
     581               0 :   ~nsWSCompression()
     582               0 :   {
     583               0 :     MOZ_COUNT_DTOR(nsWSCompression);
     584                 : 
     585               0 :     if (mActive)
     586               0 :       deflateEnd(&mZlib);
     587               0 :   }
     588                 : 
     589               0 :   bool Active()
     590                 :   {
     591               0 :     return mActive;
     592                 :   }
     593                 : 
     594               0 :   nsresult Deflate(PRUint8 *buf1, PRUint32 buf1Len,
     595                 :                    PRUint8 *buf2, PRUint32 buf2Len)
     596                 :   {
     597               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
     598                 :                           "not socket thread");
     599               0 :     NS_ABORT_IF_FALSE(mActive, "not active");
     600                 : 
     601               0 :     mZlib.avail_out = kBufferLen;
     602               0 :     mZlib.next_out = mBuffer;
     603               0 :     mZlib.avail_in = buf1Len;
     604               0 :     mZlib.next_in = buf1;
     605                 : 
     606                 :     nsresult rv;
     607                 : 
     608               0 :     while (mZlib.avail_in > 0) {
     609               0 :       deflate(&mZlib, (buf2Len > 0) ? Z_NO_FLUSH : Z_SYNC_FLUSH);
     610               0 :       rv = PushData();
     611               0 :       if (NS_FAILED(rv))
     612               0 :         return rv;
     613               0 :       mZlib.avail_out = kBufferLen;
     614               0 :       mZlib.next_out = mBuffer;
     615                 :     }
     616                 : 
     617               0 :     mZlib.avail_in = buf2Len;
     618               0 :     mZlib.next_in = buf2;
     619                 : 
     620               0 :     while (mZlib.avail_in > 0) {
     621               0 :       deflate(&mZlib, Z_SYNC_FLUSH);
     622               0 :       rv = PushData();
     623               0 :       if (NS_FAILED(rv))
     624               0 :         return rv;
     625               0 :       mZlib.avail_out = kBufferLen;
     626               0 :       mZlib.next_out = mBuffer;
     627                 :     }
     628                 : 
     629               0 :     return NS_OK;
     630                 :   }
     631                 : 
     632                 : private:
     633                 : 
     634                 :   // use zlib data types
     635               0 :   static void *allocator(void *opaque, uInt items, uInt size)
     636                 :   {
     637               0 :     return moz_xmalloc(items * size);
     638                 :   }
     639                 : 
     640               0 :   static void destructor(void *opaque, void *addr)
     641                 :   {
     642               0 :     moz_free(addr);
     643               0 :   }
     644                 : 
     645               0 :   nsresult PushData()
     646                 :   {
     647               0 :     PRUint32 bytesToWrite = kBufferLen - mZlib.avail_out;
     648               0 :     if (bytesToWrite > 0) {
     649               0 :       mStream->ShareData(reinterpret_cast<char *>(mBuffer), bytesToWrite);
     650                 :       nsresult rv =
     651               0 :         mListener->OnDataAvailable(nsnull, mContext, mStream, 0, bytesToWrite);
     652               0 :       if (NS_FAILED(rv))
     653               0 :         return rv;
     654                 :     }
     655               0 :     return NS_OK;
     656                 :   }
     657                 : 
     658                 :   bool                            mActive;
     659                 :   z_stream                        mZlib;
     660                 :   nsCOMPtr<nsIStringInputStream>  mStream;
     661                 : 
     662                 :   nsISupports                    *mContext;     /* weak ref */
     663                 :   nsIStreamListener              *mListener;    /* weak ref */
     664                 : 
     665                 :   const static PRInt32            kBufferLen = 4096;
     666                 :   PRUint8                         mBuffer[kBufferLen];
     667                 : };
     668                 : 
     669                 : static nsWSAdmissionManager *sWebSocketAdmissions = nsnull;
     670                 : 
     671                 : //-----------------------------------------------------------------------------
     672                 : // WebSocketChannel
     673                 : //-----------------------------------------------------------------------------
     674                 : 
     675               0 : WebSocketChannel::WebSocketChannel() :
     676                 :   mCloseTimeout(20000),
     677                 :   mOpenTimeout(20000),
     678                 :   mPingTimeout(0),
     679                 :   mPingResponseTimeout(10000),
     680                 :   mMaxConcurrentConnections(200),
     681                 :   mRecvdHttpOnStartRequest(0),
     682                 :   mRecvdHttpUpgradeTransport(0),
     683                 :   mRequestedClose(0),
     684                 :   mClientClosed(0),
     685                 :   mServerClosed(0),
     686                 :   mStopped(0),
     687                 :   mCalledOnStop(0),
     688                 :   mPingOutstanding(0),
     689                 :   mAllowCompression(1),
     690                 :   mAutoFollowRedirects(0),
     691                 :   mReleaseOnTransmit(0),
     692                 :   mTCPClosed(0),
     693                 :   mOpenBlocked(0),
     694                 :   mOpenRunning(0),
     695                 :   mChannelWasOpened(0),
     696                 :   mMaxMessageSize(PR_INT32_MAX),
     697                 :   mStopOnClose(NS_OK),
     698                 :   mServerCloseCode(CLOSE_ABNORMAL),
     699                 :   mScriptCloseCode(0),
     700                 :   mFragmentOpcode(kContinuation),
     701                 :   mFragmentAccumulator(0),
     702                 :   mBuffered(0),
     703                 :   mBufferSize(kIncomingBufferInitialSize),
     704                 :   mCurrentOut(nsnull),
     705                 :   mCurrentOutSent(0),
     706                 :   mCompressor(nsnull),
     707                 :   mDynamicOutputSize(0),
     708               0 :   mDynamicOutput(nsnull)
     709                 : {
     710               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
     711                 : 
     712               0 :   LOG(("WebSocketChannel::WebSocketChannel() %p\n", this));
     713                 : 
     714               0 :   if (!sWebSocketAdmissions)
     715               0 :     sWebSocketAdmissions = new nsWSAdmissionManager();
     716                 : 
     717                 :   // The active session limit is enforced in AsyncOpen()
     718               0 :   sWebSocketAdmissions->IncrementSessionCount();
     719                 : 
     720               0 :   mFramePtr = mBuffer = static_cast<PRUint8 *>(moz_xmalloc(mBufferSize));
     721               0 : }
     722                 : 
     723               0 : WebSocketChannel::~WebSocketChannel()
     724                 : {
     725               0 :   LOG(("WebSocketChannel::~WebSocketChannel() %p\n", this));
     726                 : 
     727               0 :   if (sWebSocketAdmissions)
     728               0 :     sWebSocketAdmissions->DecrementSessionCount();
     729                 : 
     730                 :   // this stop is a nop if the normal connect/close is followed
     731               0 :   mStopped = 1;
     732               0 :   StopSession(NS_ERROR_UNEXPECTED);
     733               0 :   NS_ABORT_IF_FALSE(!mOpenRunning && !mOpenBlocked, "op");
     734                 : 
     735               0 :   moz_free(mBuffer);
     736               0 :   moz_free(mDynamicOutput);
     737               0 :   delete mCompressor;
     738               0 :   delete mCurrentOut;
     739                 : 
     740               0 :   while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
     741               0 :     delete mCurrentOut;
     742               0 :   while ((mCurrentOut = (OutboundMessage *) mOutgoingPongMessages.PopFront()))
     743               0 :     delete mCurrentOut;
     744               0 :   while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront()))
     745               0 :     delete mCurrentOut;
     746                 : 
     747               0 :   nsCOMPtr<nsIThread> mainThread;
     748                 :   nsIURI *forgettable;
     749               0 :   NS_GetMainThread(getter_AddRefs(mainThread));
     750                 : 
     751               0 :   if (mURI) {
     752               0 :     mURI.forget(&forgettable);
     753               0 :     NS_ProxyRelease(mainThread, forgettable, false);
     754                 :   }
     755                 : 
     756               0 :   if (mOriginalURI) {
     757               0 :     mOriginalURI.forget(&forgettable);
     758               0 :     NS_ProxyRelease(mainThread, forgettable, false);
     759                 :   }
     760                 : 
     761               0 :   if (mListener) {
     762                 :     nsIWebSocketListener *forgettableListener;
     763               0 :     mListener.forget(&forgettableListener);
     764               0 :     NS_ProxyRelease(mainThread, forgettableListener, false);
     765                 :   }
     766                 : 
     767               0 :   if (mContext) {
     768                 :     nsISupports *forgettableContext;
     769               0 :     mContext.forget(&forgettableContext);
     770               0 :     NS_ProxyRelease(mainThread, forgettableContext, false);
     771                 :   }
     772                 : 
     773               0 :   if (mLoadGroup) {
     774                 :     nsILoadGroup *forgettableGroup;
     775               0 :     mLoadGroup.forget(&forgettableGroup);
     776               0 :     NS_ProxyRelease(mainThread, forgettableGroup, false);
     777                 :   }
     778               0 : }
     779                 : 
     780                 : void
     781            1419 : WebSocketChannel::Shutdown()
     782                 : {
     783            1419 :   delete sWebSocketAdmissions;
     784            1419 :   sWebSocketAdmissions = nsnull;
     785            1419 : }
     786                 : 
     787                 : nsresult
     788               0 : WebSocketChannel::BeginOpen()
     789                 : {
     790               0 :   LOG(("WebSocketChannel::BeginOpen() %p\n", this));
     791               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
     792                 : 
     793                 :   nsresult rv;
     794                 : 
     795               0 :   if (mRedirectCallback) {
     796               0 :     LOG(("WebSocketChannel::BeginOpen: Resuming Redirect\n"));
     797               0 :     rv = mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
     798               0 :     mRedirectCallback = nsnull;
     799               0 :     return rv;
     800                 :   }
     801                 : 
     802               0 :   nsCOMPtr<nsIChannel> localChannel = do_QueryInterface(mChannel, &rv);
     803               0 :   if (NS_FAILED(rv)) {
     804               0 :     LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
     805               0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
     806               0 :     return rv;
     807                 :   }
     808                 : 
     809               0 :   rv = localChannel->AsyncOpen(this, mHttpChannel);
     810               0 :   if (NS_FAILED(rv)) {
     811               0 :     LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
     812               0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
     813               0 :     return rv;
     814                 :   }
     815               0 :   mChannelWasOpened = 1;
     816                 : 
     817               0 :   mOpenTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     818               0 :   if (NS_SUCCEEDED(rv))
     819               0 :     mOpenTimer->InitWithCallback(this, mOpenTimeout, nsITimer::TYPE_ONE_SHOT);
     820                 : 
     821               0 :   return rv;
     822                 : }
     823                 : 
     824                 : bool
     825               0 : WebSocketChannel::IsPersistentFramePtr()
     826                 : {
     827               0 :   return (mFramePtr >= mBuffer && mFramePtr < mBuffer + mBufferSize);
     828                 : }
     829                 : 
     830                 : // Extends the internal buffer by count and returns the total
     831                 : // amount of data available for read
     832                 : //
     833                 : // Accumulated fragment size is passed in instead of using the member
     834                 : // variable beacuse when transitioning from the stack to the persistent
     835                 : // read buffer we want to explicitly include them in the buffer instead
     836                 : // of as already existing data.
     837                 : bool
     838               0 : WebSocketChannel::UpdateReadBuffer(PRUint8 *buffer, PRUint32 count,
     839                 :                                    PRUint32 accumulatedFragments,
     840                 :                                    PRUint32 *available)
     841                 : {
     842               0 :   LOG(("WebSocketChannel::UpdateReadBuffer() %p [%p %u]\n",
     843                 :          this, buffer, count));
     844                 : 
     845               0 :   if (!mBuffered)
     846               0 :     mFramePtr = mBuffer;
     847                 : 
     848               0 :   NS_ABORT_IF_FALSE(IsPersistentFramePtr(), "update read buffer bad mFramePtr");
     849               0 :   NS_ABORT_IF_FALSE(mFramePtr - accumulatedFragments >= mBuffer,
     850                 :                     "reserved FramePtr bad");
     851                 : 
     852               0 :   if (mBuffered + count <= mBufferSize) {
     853                 :     // append to existing buffer
     854               0 :     LOG(("WebSocketChannel: update read buffer absorbed %u\n", count));
     855               0 :   } else if (mBuffered + count - 
     856               0 :              (mFramePtr - accumulatedFragments - mBuffer) <= mBufferSize) {
     857                 :     // make room in existing buffer by shifting unused data to start
     858               0 :     mBuffered -= (mFramePtr - mBuffer - accumulatedFragments);
     859               0 :     LOG(("WebSocketChannel: update read buffer shifted %u\n", mBuffered));
     860               0 :     ::memmove(mBuffer, mFramePtr - accumulatedFragments, mBuffered);
     861               0 :     mFramePtr = mBuffer + accumulatedFragments;
     862                 :   } else {
     863                 :     // existing buffer is not sufficient, extend it
     864               0 :     mBufferSize += count + 8192 + mBufferSize/3;
     865               0 :     LOG(("WebSocketChannel: update read buffer extended to %u\n", mBufferSize));
     866               0 :     PRUint8 *old = mBuffer;
     867               0 :     mBuffer = (PRUint8 *)moz_realloc(mBuffer, mBufferSize);
     868               0 :     if (!mBuffer) {
     869               0 :       mBuffer = old;
     870               0 :       return false;
     871                 :     }
     872               0 :     mFramePtr = mBuffer + (mFramePtr - old);
     873                 :   }
     874                 : 
     875               0 :   ::memcpy(mBuffer + mBuffered, buffer, count);
     876               0 :   mBuffered += count;
     877                 : 
     878               0 :   if (available)
     879               0 :     *available = mBuffered - (mFramePtr - mBuffer);
     880                 : 
     881               0 :   return true;
     882                 : }
     883                 : 
     884                 : nsresult
     885               0 : WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
     886                 : {
     887               0 :   LOG(("WebSocketChannel::ProcessInput %p [%d %d]\n", this, count, mBuffered));
     888               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
     889                 : 
     890                 :   // reset the ping timer
     891               0 :   if (mPingTimer) {
     892                 :     // The purpose of ping/pong is to actively probe the peer so that an
     893                 :     // unreachable peer is not mistaken for a period of idleness. This
     894                 :     // implementation accepts any application level read activity as a sign of
     895                 :     // life, it does not necessarily have to be a pong.
     896               0 :     mPingOutstanding = 0;
     897               0 :     mPingTimer->SetDelay(mPingTimeout);
     898                 :   }
     899                 : 
     900                 :   PRUint32 avail;
     901                 : 
     902               0 :   if (!mBuffered) {
     903                 :     // Most of the time we can process right off the stack buffer without
     904                 :     // having to accumulate anything
     905               0 :     mFramePtr = buffer;
     906               0 :     avail = count;
     907                 :   } else {
     908               0 :     if (!UpdateReadBuffer(buffer, count, mFragmentAccumulator, &avail)) {
     909               0 :       AbortSession(NS_ERROR_FILE_TOO_BIG);
     910               0 :       return NS_ERROR_FILE_TOO_BIG;
     911                 :     }
     912                 :   }
     913                 : 
     914                 :   PRUint8 *payload;
     915               0 :   PRUint32 totalAvail = avail;
     916                 : 
     917               0 :   while (avail >= 2) {
     918               0 :     PRInt64 payloadLength = mFramePtr[1] & 0x7F;
     919               0 :     PRUint8 finBit        = mFramePtr[0] & kFinalFragBit;
     920               0 :     PRUint8 rsvBits       = mFramePtr[0] & 0x70;
     921               0 :     PRUint8 maskBit       = mFramePtr[1] & kMaskBit;
     922               0 :     PRUint8 opcode        = mFramePtr[0] & 0x0F;
     923                 : 
     924               0 :     PRUint32 framingLength = 2;
     925               0 :     if (maskBit)
     926               0 :       framingLength += 4;
     927                 : 
     928               0 :     if (payloadLength < 126) {
     929               0 :       if (avail < framingLength)
     930               0 :         break;
     931               0 :     } else if (payloadLength == 126) {
     932                 :       // 16 bit length field
     933               0 :       framingLength += 2;
     934               0 :       if (avail < framingLength)
     935               0 :         break;
     936                 : 
     937               0 :       payloadLength = mFramePtr[2] << 8 | mFramePtr[3];
     938                 :     } else {
     939                 :       // 64 bit length
     940               0 :       framingLength += 8;
     941               0 :       if (avail < framingLength)
     942               0 :         break;
     943                 : 
     944               0 :       if (mFramePtr[2] & 0x80) {
     945                 :         // Section 4.2 says that the most significant bit MUST be
     946                 :         // 0. (i.e. this is really a 63 bit value)
     947               0 :         LOG(("WebSocketChannel:: high bit of 64 bit length set"));
     948               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
     949               0 :         return NS_ERROR_ILLEGAL_VALUE;
     950                 :       }
     951                 : 
     952                 :       // copy this in case it is unaligned
     953                 :       PRUint64 tempLen;
     954               0 :       memcpy(&tempLen, mFramePtr + 2, 8);
     955               0 :       payloadLength = PR_ntohll(tempLen);
     956                 :     }
     957                 : 
     958               0 :     payload = mFramePtr + framingLength;
     959               0 :     avail -= framingLength;
     960                 : 
     961               0 :     LOG(("WebSocketChannel::ProcessInput: payload %lld avail %lu\n",
     962                 :          payloadLength, avail));
     963                 : 
     964               0 :     if (payloadLength + mFragmentAccumulator > mMaxMessageSize) {
     965               0 :       AbortSession(NS_ERROR_FILE_TOO_BIG);
     966               0 :       return NS_ERROR_FILE_TOO_BIG;
     967                 :     }
     968                 : 
     969               0 :     if (avail < payloadLength)
     970               0 :       break;
     971                 : 
     972               0 :     LOG(("WebSocketChannel::ProcessInput: Frame accumulated - opcode %d\n",
     973                 :          opcode));
     974                 : 
     975               0 :     if (maskBit) {
     976                 :       // This is unexpected - the server does not generally send masked
     977                 :       // frames to the client, but it is allowed
     978               0 :       LOG(("WebSocketChannel:: Client RECEIVING masked frame."));
     979                 : 
     980                 :       PRUint32 mask;
     981               0 :       memcpy(&mask, payload - 4, 4);
     982               0 :       mask = PR_ntohl(mask);
     983               0 :       ApplyMask(mask, payload, payloadLength);
     984                 :     }
     985                 : 
     986                 :     // Control codes are required to have the fin bit set
     987               0 :     if (!finBit && (opcode & kControlFrameMask)) {
     988               0 :       LOG(("WebSocketChannel:: fragmented control frame code %d\n", opcode));
     989               0 :       AbortSession(NS_ERROR_ILLEGAL_VALUE);
     990               0 :       return NS_ERROR_ILLEGAL_VALUE;
     991                 :     }
     992                 : 
     993               0 :     if (rsvBits) {
     994               0 :       LOG(("WebSocketChannel:: unexpected reserved bits %x\n", rsvBits));
     995               0 :       AbortSession(NS_ERROR_ILLEGAL_VALUE);
     996               0 :       return NS_ERROR_ILLEGAL_VALUE;
     997                 :     }
     998                 : 
     999               0 :     if (!finBit || opcode == kContinuation) {
    1000                 :       // This is part of a fragment response
    1001                 : 
    1002                 :       // Only the first frame has a non zero op code: Make sure we don't see a
    1003                 :       // first frame while some old fragments are open
    1004               0 :       if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) {
    1005               0 :         LOG(("WebSocketChannel:: nested fragments\n"));
    1006               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1007               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1008                 :       }
    1009                 : 
    1010               0 :       LOG(("WebSocketChannel:: Accumulating Fragment %lld\n", payloadLength));
    1011                 : 
    1012               0 :       if (opcode == kContinuation) {
    1013                 : 
    1014                 :         // Make sure this continuation fragment isn't the first fragment
    1015               0 :         if (mFragmentOpcode == kContinuation) {
    1016               0 :           LOG(("WebSocketHeandler:: continuation code in first fragment\n"));
    1017               0 :           AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1018               0 :           return NS_ERROR_ILLEGAL_VALUE;
    1019                 :         }
    1020                 : 
    1021                 :         // For frag > 1 move the data body back on top of the headers
    1022                 :         // so we have contiguous stream of data
    1023               0 :         NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
    1024                 :                           "payload offset from frameptr wrong");
    1025               0 :         ::memmove(mFramePtr, payload, avail);
    1026               0 :         payload = mFramePtr;
    1027               0 :         if (mBuffered)
    1028               0 :           mBuffered -= framingLength;
    1029                 :       } else {
    1030               0 :         mFragmentOpcode = opcode;
    1031                 :       }
    1032                 : 
    1033               0 :       if (finBit) {
    1034               0 :         LOG(("WebSocketChannel:: Finalizing Fragment\n"));
    1035               0 :         payload -= mFragmentAccumulator;
    1036               0 :         payloadLength += mFragmentAccumulator;
    1037               0 :         avail += mFragmentAccumulator;
    1038               0 :         mFragmentAccumulator = 0;
    1039               0 :         opcode = mFragmentOpcode;
    1040                 :         // reset to detect if next message illegally starts with continuation
    1041               0 :         mFragmentOpcode = kContinuation;
    1042                 :       } else {
    1043               0 :         opcode = kContinuation;
    1044               0 :         mFragmentAccumulator += payloadLength;
    1045                 :       }
    1046               0 :     } else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) {
    1047                 :       // This frame is not part of a fragment sequence but we
    1048                 :       // have an open fragment.. it must be a control code or else
    1049                 :       // we have a problem
    1050               0 :       LOG(("WebSocketChannel:: illegal fragment sequence\n"));
    1051               0 :       AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1052               0 :       return NS_ERROR_ILLEGAL_VALUE;
    1053                 :     }
    1054                 : 
    1055               0 :     if (mServerClosed) {
    1056               0 :       LOG(("WebSocketChannel:: ignoring read frame code %d after close\n",
    1057                 :                  opcode));
    1058                 :       // nop
    1059               0 :     } else if (mStopped) {
    1060               0 :       LOG(("WebSocketChannel:: ignoring read frame code %d after completion\n",
    1061                 :            opcode));
    1062               0 :     } else if (opcode == kText) {
    1063               0 :       LOG(("WebSocketChannel:: text frame received\n"));
    1064               0 :       if (mListener) {
    1065               0 :         nsCString utf8Data((const char *)payload, payloadLength);
    1066                 : 
    1067                 :         // Section 8.1 says to fail connection if invalid utf-8 in text message
    1068               0 :         if (!IsUTF8(utf8Data, false)) {
    1069               0 :           LOG(("WebSocketChannel:: text frame invalid utf-8\n"));
    1070               0 :           AbortSession(NS_ERROR_CANNOT_CONVERT_DATA);
    1071               0 :           return NS_ERROR_ILLEGAL_VALUE;
    1072                 :         }
    1073                 : 
    1074               0 :         NS_DispatchToMainThread(new CallOnMessageAvailable(this, utf8Data, -1));
    1075                 :       }
    1076               0 :     } else if (opcode & kControlFrameMask) {
    1077                 :       // control frames
    1078               0 :       if (payloadLength > 125) {
    1079               0 :         LOG(("WebSocketChannel:: bad control frame code %d length %d\n",
    1080                 :              opcode, payloadLength));
    1081               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1082               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1083                 :       }
    1084                 : 
    1085               0 :       if (opcode == kClose) {
    1086               0 :         LOG(("WebSocketChannel:: close received\n"));
    1087               0 :         mServerClosed = 1;
    1088                 : 
    1089               0 :         mServerCloseCode = CLOSE_NO_STATUS;
    1090               0 :         if (payloadLength >= 2) {
    1091               0 :           memcpy(&mServerCloseCode, payload, 2);
    1092               0 :           mServerCloseCode = PR_ntohs(mServerCloseCode);
    1093               0 :           LOG(("WebSocketChannel:: close recvd code %u\n", mServerCloseCode));
    1094               0 :           PRUint16 msglen = payloadLength - 2;
    1095               0 :           if (msglen > 0) {
    1096               0 :             mServerCloseReason.SetLength(msglen);
    1097               0 :             memcpy(mServerCloseReason.BeginWriting(),
    1098               0 :                    (const char *)payload + 2, msglen);
    1099                 : 
    1100                 :             // section 8.1 says to replace received non utf-8 sequences
    1101                 :             // (which are non-conformant to send) with u+fffd,
    1102                 :             // but secteam feels that silently rewriting messages is
    1103                 :             // inappropriate - so we will fail the connection instead.
    1104               0 :             if (!IsUTF8(mServerCloseReason, false)) {
    1105               0 :               LOG(("WebSocketChannel:: close frame invalid utf-8\n"));
    1106               0 :               AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1107               0 :               return NS_ERROR_ILLEGAL_VALUE;
    1108                 :             }
    1109                 : 
    1110               0 :             LOG(("WebSocketChannel:: close msg %s\n",
    1111                 :                  mServerCloseReason.get()));
    1112                 :           }
    1113                 :         }
    1114                 : 
    1115               0 :         if (mCloseTimer) {
    1116               0 :           mCloseTimer->Cancel();
    1117               0 :           mCloseTimer = nsnull;
    1118                 :         }
    1119               0 :         if (mListener) {
    1120                 :           NS_DispatchToMainThread(new CallOnServerClose(this, mServerCloseCode,
    1121               0 :                                                         mServerCloseReason));
    1122                 :         }
    1123                 : 
    1124               0 :         if (mClientClosed)
    1125               0 :           ReleaseSession();
    1126               0 :       } else if (opcode == kPing) {
    1127               0 :         LOG(("WebSocketChannel:: ping received\n"));
    1128               0 :         GeneratePong(payload, payloadLength);
    1129               0 :       } else if (opcode == kPong) {
    1130                 :         // opcode kPong: the mere act of receiving the packet is all we need
    1131                 :         // to do for the pong to trigger the activity timers
    1132               0 :         LOG(("WebSocketChannel:: pong received\n"));
    1133                 :       } else {
    1134                 :         /* unknown control frame opcode */
    1135               0 :         LOG(("WebSocketChannel:: unknown control op code %d\n", opcode));
    1136               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1137               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1138                 :       }
    1139                 : 
    1140               0 :       if (mFragmentAccumulator) {
    1141                 :         // Remove the control frame from the stream so we have a contiguous
    1142                 :         // data buffer of reassembled fragments
    1143               0 :         LOG(("WebSocketChannel:: Removing Control From Read buffer\n"));
    1144               0 :         NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
    1145                 :                           "payload offset from frameptr wrong");
    1146               0 :         ::memmove(mFramePtr, payload + payloadLength, avail - payloadLength);
    1147               0 :         payload = mFramePtr;
    1148               0 :         avail -= payloadLength;
    1149               0 :         if (mBuffered)
    1150               0 :           mBuffered -= framingLength + payloadLength;
    1151               0 :         payloadLength = 0;
    1152                 :       }
    1153               0 :     } else if (opcode == kBinary) {
    1154               0 :       LOG(("WebSocketChannel:: binary frame received\n"));
    1155               0 :       if (mListener) {
    1156               0 :         nsCString binaryData((const char *)payload, payloadLength);
    1157                 :         NS_DispatchToMainThread(new CallOnMessageAvailable(this, binaryData,
    1158               0 :                                                            payloadLength));
    1159                 :       }
    1160               0 :     } else if (opcode != kContinuation) {
    1161                 :       /* unknown opcode */
    1162               0 :       LOG(("WebSocketChannel:: unknown op code %d\n", opcode));
    1163               0 :       AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1164               0 :       return NS_ERROR_ILLEGAL_VALUE;
    1165                 :     }
    1166                 : 
    1167               0 :     mFramePtr = payload + payloadLength;
    1168               0 :     avail -= payloadLength;
    1169               0 :     totalAvail = avail;
    1170                 :   }
    1171                 : 
    1172                 :   // Adjust the stateful buffer. If we were operating off the stack and
    1173                 :   // now have a partial message then transition to the buffer, or if
    1174                 :   // we were working off the buffer but no longer have any active state
    1175                 :   // then transition to the stack
    1176               0 :   if (!IsPersistentFramePtr()) {
    1177               0 :     mBuffered = 0;
    1178                 : 
    1179               0 :     if (mFragmentAccumulator) {
    1180               0 :       LOG(("WebSocketChannel:: Setup Buffer due to fragment"));
    1181                 : 
    1182               0 :       if (!UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
    1183               0 :                             totalAvail + mFragmentAccumulator, 0, nsnull)) {
    1184               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1185               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1186                 :       }
    1187                 : 
    1188                 :       // UpdateReadBuffer will reset the frameptr to the beginning
    1189                 :       // of new saved state, so we need to skip past processed framgents
    1190               0 :       mFramePtr += mFragmentAccumulator;
    1191               0 :     } else if (totalAvail) {
    1192               0 :       LOG(("WebSocketChannel:: Setup Buffer due to partial frame"));
    1193               0 :       if (!UpdateReadBuffer(mFramePtr, totalAvail, 0, nsnull)) {
    1194               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1195               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1196                 :       }
    1197                 :     }
    1198               0 :   } else if (!mFragmentAccumulator && !totalAvail) {
    1199                 :     // If we were working off a saved buffer state and there is no partial
    1200                 :     // frame or fragment in process, then revert to stack behavior
    1201               0 :     LOG(("WebSocketChannel:: Internal buffering not needed anymore"));
    1202               0 :     mBuffered = 0;
    1203                 : 
    1204                 :     // release memory if we've been processing a large message
    1205               0 :     if (mBufferSize > kIncomingBufferStableSize) {
    1206               0 :       mBufferSize = kIncomingBufferStableSize;
    1207               0 :       moz_free(mBuffer);
    1208               0 :       mBuffer = (PRUint8 *)moz_xmalloc(mBufferSize);
    1209                 :     }
    1210                 :   }
    1211               0 :   return NS_OK;
    1212                 : }
    1213                 : 
    1214                 : void
    1215               0 : WebSocketChannel::ApplyMask(PRUint32 mask, PRUint8 *data, PRUint64 len)
    1216                 : {
    1217               0 :   if (!data || len == 0)
    1218               0 :     return;
    1219                 : 
    1220                 :   // Optimally we want to apply the mask 32 bits at a time,
    1221                 :   // but the buffer might not be alligned. So we first deal with
    1222                 :   // 0 to 3 bytes of preamble individually
    1223                 : 
    1224               0 :   while (len && (reinterpret_cast<PRUptrdiff>(data) & 3)) {
    1225               0 :     *data ^= mask >> 24;
    1226               0 :     mask = PR_ROTATE_LEFT32(mask, 8);
    1227               0 :     data++;
    1228               0 :     len--;
    1229                 :   }
    1230                 : 
    1231                 :   // perform mask on full words of data
    1232                 : 
    1233               0 :   PRUint32 *iData = (PRUint32 *) data;
    1234               0 :   PRUint32 *end = iData + (len / 4);
    1235               0 :   mask = PR_htonl(mask);
    1236               0 :   for (; iData < end; iData++)
    1237               0 :     *iData ^= mask;
    1238               0 :   mask = PR_ntohl(mask);
    1239               0 :   data = (PRUint8 *)iData;
    1240               0 :   len  = len % 4;
    1241                 : 
    1242                 :   // There maybe up to 3 trailing bytes that need to be dealt with
    1243                 :   // individually 
    1244                 : 
    1245               0 :   while (len) {
    1246               0 :     *data ^= mask >> 24;
    1247               0 :     mask = PR_ROTATE_LEFT32(mask, 8);
    1248               0 :     data++;
    1249               0 :     len--;
    1250                 :   }
    1251                 : }
    1252                 : 
    1253                 : void
    1254               0 : WebSocketChannel::GeneratePing()
    1255                 : {
    1256               0 :   nsCString *buf = new nsCString();
    1257               0 :   buf->Assign("PING");
    1258                 :   EnqueueOutgoingMessage(mOutgoingPingMessages,
    1259               0 :                          new OutboundMessage(kMsgTypePing, buf));
    1260               0 : }
    1261                 : 
    1262                 : void
    1263               0 : WebSocketChannel::GeneratePong(PRUint8 *payload, PRUint32 len)
    1264                 : {
    1265               0 :   nsCString *buf = new nsCString();
    1266               0 :   buf->SetLength(len);
    1267               0 :   if (buf->Length() < len) {
    1268               0 :     LOG(("WebSocketChannel::GeneratePong Allocation Failure\n"));
    1269               0 :     delete buf;
    1270               0 :     return;
    1271                 :   }
    1272                 : 
    1273               0 :   memcpy(buf->BeginWriting(), payload, len);
    1274                 :   EnqueueOutgoingMessage(mOutgoingPongMessages,
    1275               0 :                          new OutboundMessage(kMsgTypePong, buf));
    1276                 : }
    1277                 : 
    1278                 : void
    1279               0 : WebSocketChannel::EnqueueOutgoingMessage(nsDeque &aQueue,
    1280                 :                                          OutboundMessage *aMsg)
    1281                 : {
    1282               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
    1283                 : 
    1284               0 :   LOG(("WebSocketChannel::EnqueueOutgoingMessage %p "
    1285                 :        "queueing msg %p [type=%s len=%d]\n",
    1286                 :        this, aMsg, msgNames[aMsg->GetMsgType()], aMsg->Length()));
    1287                 : 
    1288               0 :   aQueue.Push(aMsg);
    1289               0 :   OnOutputStreamReady(mSocketOut);
    1290               0 : }
    1291                 : 
    1292                 : 
    1293                 : PRUint16
    1294               0 : WebSocketChannel::ResultToCloseCode(nsresult resultCode)
    1295                 : {
    1296               0 :   if (NS_SUCCEEDED(resultCode))
    1297               0 :     return CLOSE_NORMAL;
    1298               0 :   if (resultCode == NS_ERROR_FILE_TOO_BIG)
    1299               0 :     return CLOSE_TOO_LARGE;
    1300               0 :   if (resultCode == NS_BASE_STREAM_CLOSED ||
    1301                 :       resultCode == NS_ERROR_NET_TIMEOUT ||
    1302                 :       resultCode == NS_ERROR_CONNECTION_REFUSED) {
    1303               0 :     return CLOSE_ABNORMAL;
    1304                 :   }
    1305               0 :   if (resultCode == NS_ERROR_CANNOT_CONVERT_DATA)
    1306               0 :     return CLOSE_INVALID_PAYLOAD;
    1307                 : 
    1308               0 :   return CLOSE_PROTOCOL_ERROR;
    1309                 : }
    1310                 : 
    1311                 : void
    1312               0 : WebSocketChannel::PrimeNewOutgoingMessage()
    1313                 : {
    1314               0 :   LOG(("WebSocketChannel::PrimeNewOutgoingMessage() %p\n", this));
    1315               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
    1316               0 :   NS_ABORT_IF_FALSE(!mCurrentOut, "Current message in progress");
    1317                 : 
    1318               0 :   nsresult rv = NS_OK;
    1319                 : 
    1320               0 :   mCurrentOut = (OutboundMessage *)mOutgoingPongMessages.PopFront();
    1321               0 :   if (mCurrentOut) {
    1322               0 :     NS_ABORT_IF_FALSE(mCurrentOut->GetMsgType() == kMsgTypePong,
    1323                 :                      "Not pong message!");
    1324                 :   } else {
    1325               0 :     mCurrentOut = (OutboundMessage *)mOutgoingPingMessages.PopFront();
    1326               0 :     if (mCurrentOut)
    1327               0 :       NS_ABORT_IF_FALSE(mCurrentOut->GetMsgType() == kMsgTypePing,
    1328                 :                         "Not ping message!");
    1329                 :     else
    1330               0 :       mCurrentOut = (OutboundMessage *)mOutgoingMessages.PopFront();
    1331                 :   }
    1332                 : 
    1333               0 :   if (!mCurrentOut)
    1334               0 :     return;
    1335                 : 
    1336               0 :   WsMsgType msgType = mCurrentOut->GetMsgType();
    1337                 : 
    1338               0 :   LOG(("WebSocketChannel::PrimeNewOutgoingMessage "
    1339                 :        "%p found queued msg %p [type=%s len=%d]\n",
    1340                 :        this, mCurrentOut, msgNames[msgType], mCurrentOut->Length()));
    1341                 : 
    1342               0 :   mCurrentOutSent = 0;
    1343               0 :   mHdrOut = mOutHeader;
    1344                 : 
    1345               0 :   PRUint8 *payload = nsnull;
    1346                 : 
    1347               0 :   if (msgType == kMsgTypeFin) {
    1348                 :     // This is a demand to create a close message
    1349               0 :     if (mClientClosed) {
    1350               0 :       DeleteCurrentOutGoingMessage();
    1351               0 :       PrimeNewOutgoingMessage();
    1352               0 :       return;
    1353                 :     }
    1354                 : 
    1355               0 :     mClientClosed = 1;
    1356               0 :     mOutHeader[0] = kFinalFragBit | kClose;
    1357               0 :     mOutHeader[1] = 0x02; // payload len = 2, maybe more for reason
    1358               0 :     mOutHeader[1] |= kMaskBit;
    1359                 : 
    1360                 :     // payload is offset 6 including 4 for the mask
    1361               0 :     payload = mOutHeader + 6;
    1362                 : 
    1363                 :     // length is 8 plus any reason information
    1364               0 :     mHdrOutToSend = 8;
    1365                 : 
    1366                 :     // The close reason code sits in the first 2 bytes of payload
    1367                 :     // If the channel user provided a code and reason during Close()
    1368                 :     // and there isn't an internal error, use that.
    1369               0 :     if (NS_SUCCEEDED(mStopOnClose) && mScriptCloseCode) {
    1370               0 :       *((PRUint16 *)payload) = PR_htons(mScriptCloseCode);
    1371               0 :       if (!mScriptCloseReason.IsEmpty()) {
    1372               0 :         NS_ABORT_IF_FALSE(mScriptCloseReason.Length() <= 123,
    1373                 :                           "Close Reason Too Long");
    1374               0 :         mOutHeader[1] += mScriptCloseReason.Length();
    1375               0 :         mHdrOutToSend += mScriptCloseReason.Length();
    1376               0 :         memcpy (payload + 2,
    1377               0 :                 mScriptCloseReason.BeginReading(),
    1378               0 :                 mScriptCloseReason.Length());
    1379                 :       }
    1380                 :     } else {
    1381               0 :       *((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
    1382                 :     }
    1383                 : 
    1384               0 :     if (mServerClosed) {
    1385                 :       /* bidi close complete */
    1386               0 :       mReleaseOnTransmit = 1;
    1387               0 :     } else if (NS_FAILED(mStopOnClose)) {
    1388                 :       /* result of abort session - give up */
    1389               0 :       StopSession(mStopOnClose);
    1390                 :     } else {
    1391                 :       /* wait for reciprocal close from server */
    1392               0 :       mCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1393               0 :       if (NS_SUCCEEDED(rv)) {
    1394               0 :         mCloseTimer->InitWithCallback(this, mCloseTimeout,
    1395               0 :                                       nsITimer::TYPE_ONE_SHOT);
    1396                 :       } else {
    1397               0 :         StopSession(rv);
    1398                 :       }
    1399                 :     }
    1400                 :   } else {
    1401               0 :     switch (msgType) {
    1402                 :     case kMsgTypePong:
    1403               0 :       mOutHeader[0] = kFinalFragBit | kPong;
    1404               0 :       break;
    1405                 :     case kMsgTypePing:
    1406               0 :       mOutHeader[0] = kFinalFragBit | kPing;
    1407               0 :       break;
    1408                 :     case kMsgTypeString:
    1409               0 :       mOutHeader[0] = kFinalFragBit | kText;
    1410               0 :       break;
    1411                 :     case kMsgTypeStream:
    1412                 :       // HACK ALERT:  read in entire stream into string.
    1413                 :       // Will block socket transport thread if file is blocking.
    1414                 :       // TODO: bug 704447:  don't block socket thread!
    1415               0 :       rv = mCurrentOut->ConvertStreamToString();
    1416               0 :       if (NS_FAILED(rv)) {
    1417               0 :         AbortSession(rv);
    1418               0 :         return;
    1419                 :       }
    1420                 :       // Now we're a binary string
    1421               0 :       msgType = kMsgTypeBinaryString;
    1422                 : 
    1423                 :       // no break: fall down into binary string case
    1424                 : 
    1425                 :     case kMsgTypeBinaryString:
    1426               0 :       mOutHeader[0] = kFinalFragBit | kBinary;
    1427               0 :       break;
    1428                 :     case kMsgTypeFin:
    1429               0 :       NS_ABORT_IF_FALSE(false, "unreachable");  // avoid compiler warning
    1430               0 :       break;
    1431                 :     }
    1432                 : 
    1433               0 :     if (mCurrentOut->Length() < 126) {
    1434               0 :       mOutHeader[1] = mCurrentOut->Length() | kMaskBit;
    1435               0 :       mHdrOutToSend = 6;
    1436               0 :     } else if (mCurrentOut->Length() <= 0xffff) {
    1437               0 :       mOutHeader[1] = 126 | kMaskBit;
    1438               0 :       ((PRUint16 *)mOutHeader)[1] =
    1439               0 :         PR_htons(mCurrentOut->Length());
    1440               0 :       mHdrOutToSend = 8;
    1441                 :     } else {
    1442               0 :       mOutHeader[1] = 127 | kMaskBit;
    1443               0 :       PRUint64 tempLen = mCurrentOut->Length();
    1444               0 :       tempLen = PR_htonll(tempLen);
    1445               0 :       memcpy(mOutHeader + 2, &tempLen, 8);
    1446               0 :       mHdrOutToSend = 14;
    1447                 :     }
    1448               0 :     payload = mOutHeader + mHdrOutToSend;
    1449                 :   }
    1450                 : 
    1451               0 :   NS_ABORT_IF_FALSE(payload, "payload offset not found");
    1452                 : 
    1453                 :   // Perform the sending mask. Never use a zero mask
    1454                 :   PRUint32 mask;
    1455               0 :   do {
    1456                 :     PRUint8 *buffer;
    1457               0 :     nsresult rv = mRandomGenerator->GenerateRandomBytes(4, &buffer);
    1458               0 :     if (NS_FAILED(rv)) {
    1459               0 :       LOG(("WebSocketChannel::PrimeNewOutgoingMessage(): "
    1460                 :            "GenerateRandomBytes failure %x\n", rv));
    1461               0 :       StopSession(rv);
    1462               0 :       return;
    1463                 :     }
    1464               0 :     mask = * reinterpret_cast<PRUint32 *>(buffer);
    1465               0 :     NS_Free(buffer);
    1466               0 :   } while (!mask);
    1467               0 :   *(((PRUint32 *)payload) - 1) = PR_htonl(mask);
    1468                 : 
    1469               0 :   LOG(("WebSocketChannel::PrimeNewOutgoingMessage() using mask %08x\n", mask));
    1470                 : 
    1471                 :   // We don't mask the framing, but occasionally we stick a little payload
    1472                 :   // data in the buffer used for the framing. Close frames are the current
    1473                 :   // example. This data needs to be masked, but it is never more than a
    1474                 :   // handful of bytes and might rotate the mask, so we can just do it locally.
    1475                 :   // For real data frames we ship the bulk of the payload off to ApplyMask()
    1476                 : 
    1477               0 :   while (payload < (mOutHeader + mHdrOutToSend)) {
    1478               0 :     *payload ^= mask >> 24;
    1479               0 :     mask = PR_ROTATE_LEFT32(mask, 8);
    1480               0 :     payload++;
    1481                 :   }
    1482                 : 
    1483                 :   // Mask the real message payloads
    1484                 : 
    1485               0 :   ApplyMask(mask, mCurrentOut->BeginWriting(), mCurrentOut->Length());
    1486                 : 
    1487                 :   // for small frames, copy it all together for a contiguous write
    1488               0 :   if (mCurrentOut->Length() <= kCopyBreak) {
    1489               0 :     memcpy(mOutHeader + mHdrOutToSend, mCurrentOut->BeginWriting(),
    1490               0 :            mCurrentOut->Length());
    1491               0 :     mHdrOutToSend += mCurrentOut->Length();
    1492               0 :     mCurrentOutSent = mCurrentOut->Length();
    1493                 :   }
    1494                 : 
    1495               0 :   if (mCompressor) {
    1496                 :     // assume a 1/3 reduction in size for sizing the buffer
    1497                 :     // the buffer is used multiple times if necessary
    1498               0 :     PRUint32 currentHeaderSize = mHdrOutToSend;
    1499               0 :     mHdrOutToSend = 0;
    1500                 : 
    1501                 :     EnsureHdrOut(32 +
    1502               0 :                  (currentHeaderSize + mCurrentOut->Length() - mCurrentOutSent)
    1503               0 :                  / 2 * 3);
    1504                 :     mCompressor->Deflate(mOutHeader, currentHeaderSize,
    1505               0 :                          mCurrentOut->BeginReading() + mCurrentOutSent,
    1506               0 :                          mCurrentOut->Length() - mCurrentOutSent);
    1507                 : 
    1508                 :     // All of the compressed data now resides in {mHdrOut, mHdrOutToSend}
    1509                 :     // so do not send the body again
    1510               0 :     mCurrentOutSent = mCurrentOut->Length();
    1511                 :   }
    1512                 : 
    1513                 :   // Transmitting begins - mHdrOutToSend bytes from mOutHeader and
    1514                 :   // mCurrentOut->Length() bytes from mCurrentOut. The latter may be
    1515                 :   // coaleseced into the former for small messages or as the result of the
    1516                 :   // compression process,
    1517                 : }
    1518                 : 
    1519                 : void
    1520               0 : WebSocketChannel::DeleteCurrentOutGoingMessage()
    1521                 : {
    1522               0 :   delete mCurrentOut;
    1523               0 :   mCurrentOut = nsnull;
    1524               0 :   mCurrentOutSent = 0;
    1525               0 : }
    1526                 : 
    1527                 : void
    1528               0 : WebSocketChannel::EnsureHdrOut(PRUint32 size)
    1529                 : {
    1530               0 :   LOG(("WebSocketChannel::EnsureHdrOut() %p [%d]\n", this, size));
    1531                 : 
    1532               0 :   if (mDynamicOutputSize < size) {
    1533               0 :     mDynamicOutputSize = size;
    1534                 :     mDynamicOutput =
    1535               0 :       (PRUint8 *) moz_xrealloc(mDynamicOutput, mDynamicOutputSize);
    1536                 :   }
    1537                 : 
    1538               0 :   mHdrOut = mDynamicOutput;
    1539               0 : }
    1540                 : 
    1541                 : void
    1542               0 : WebSocketChannel::CleanupConnection()
    1543                 : {
    1544               0 :   LOG(("WebSocketChannel::CleanupConnection() %p", this));
    1545                 : 
    1546               0 :   if (mLingeringCloseTimer) {
    1547               0 :     mLingeringCloseTimer->Cancel();
    1548               0 :     mLingeringCloseTimer = nsnull;
    1549                 :   }
    1550                 : 
    1551               0 :   if (mSocketIn) {
    1552               0 :     mSocketIn->AsyncWait(nsnull, 0, 0, nsnull);
    1553               0 :     mSocketIn = nsnull;
    1554                 :   }
    1555                 : 
    1556               0 :   if (mSocketOut) {
    1557               0 :     mSocketOut->AsyncWait(nsnull, 0, 0, nsnull);
    1558               0 :     mSocketOut = nsnull;
    1559                 :   }
    1560                 : 
    1561               0 :   if (mTransport) {
    1562               0 :     mTransport->SetSecurityCallbacks(nsnull);
    1563               0 :     mTransport->SetEventSink(nsnull, nsnull);
    1564               0 :     mTransport->Close(NS_BASE_STREAM_CLOSED);
    1565               0 :     mTransport = nsnull;
    1566                 :   }
    1567               0 : }
    1568                 : 
    1569                 : void
    1570               0 : WebSocketChannel::StopSession(nsresult reason)
    1571                 : {
    1572               0 :   LOG(("WebSocketChannel::StopSession() %p [%x]\n", this, reason));
    1573                 : 
    1574                 :   // normally this should be called on socket thread, but it is ok to call it
    1575                 :   // from OnStartRequest before the socket thread machine has gotten underway
    1576                 : 
    1577               0 :   NS_ABORT_IF_FALSE(mStopped,
    1578                 :                     "stopsession() has not transitioned through abort or close");
    1579                 : 
    1580               0 :   if (!mChannelWasOpened) {
    1581                 :     // The HTTP channel information will never be used in this case
    1582               0 :     mChannel = nsnull;
    1583               0 :     mHttpChannel = nsnull;
    1584               0 :     mLoadGroup = nsnull;
    1585               0 :     mCallbacks = nsnull;
    1586                 :   }
    1587                 : 
    1588               0 :   if (mOpenRunning || mOpenBlocked)
    1589               0 :     sWebSocketAdmissions->Cancel(this);
    1590                 : 
    1591               0 :   if (mCloseTimer) {
    1592               0 :     mCloseTimer->Cancel();
    1593               0 :     mCloseTimer = nsnull;
    1594                 :   }
    1595                 : 
    1596               0 :   if (mOpenTimer) {
    1597               0 :     mOpenTimer->Cancel();
    1598               0 :     mOpenTimer = nsnull;
    1599                 :   }
    1600                 : 
    1601               0 :   if (mPingTimer) {
    1602               0 :     mPingTimer->Cancel();
    1603               0 :     mPingTimer = nsnull;
    1604                 :   }
    1605                 : 
    1606               0 :   if (mSocketIn && !mTCPClosed) {
    1607                 :     // Drain, within reason, this socket. if we leave any data
    1608                 :     // unconsumed (including the tcp fin) a RST will be generated
    1609                 :     // The right thing to do here is shutdown(SHUT_WR) and then wait
    1610                 :     // a little while to see if any data comes in.. but there is no
    1611                 :     // reason to delay things for that when the websocket handshake
    1612                 :     // is supposed to guarantee a quiet connection except for that fin.
    1613                 : 
    1614                 :     char     buffer[512];
    1615               0 :     PRUint32 count = 0;
    1616               0 :     PRUint32 total = 0;
    1617                 :     nsresult rv;
    1618               0 :     do {
    1619               0 :       total += count;
    1620               0 :       rv = mSocketIn->Read(buffer, 512, &count);
    1621               0 :       if (rv != NS_BASE_STREAM_WOULD_BLOCK &&
    1622               0 :         (NS_FAILED(rv) || count == 0))
    1623               0 :         mTCPClosed = true;
    1624               0 :     } while (NS_SUCCEEDED(rv) && count > 0 && total < 32000);
    1625                 :   }
    1626                 : 
    1627               0 :   if (!mTCPClosed && mTransport && sWebSocketAdmissions &&
    1628               0 :       sWebSocketAdmissions->SessionCount() < kLingeringCloseThreshold) {
    1629                 : 
    1630                 :     // 7.1.1 says that the client SHOULD wait for the server to close the TCP
    1631                 :     // connection. This is so we can reuse port numbers before 2 MSL expires,
    1632                 :     // which is not really as much of a concern for us as the amount of state
    1633                 :     // that might be accrued by keeping this channel object around waiting for
    1634                 :     // the server. We handle the SHOULD by waiting a short time in the common
    1635                 :     // case, but not waiting in the case of high concurrency.
    1636                 :     //
    1637                 :     // Normally this will be taken care of in AbortSession() after mTCPClosed
    1638                 :     // is set when the server close arrives without waiting for the timeout to
    1639                 :     // expire.
    1640                 : 
    1641               0 :     LOG(("WebSocketChannel::StopSession: Wait for Server TCP close"));
    1642                 : 
    1643                 :     nsresult rv;
    1644               0 :     mLingeringCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1645               0 :     if (NS_SUCCEEDED(rv))
    1646               0 :       mLingeringCloseTimer->InitWithCallback(this, kLingeringCloseTimeout,
    1647               0 :                                              nsITimer::TYPE_ONE_SHOT);
    1648                 :     else
    1649               0 :       CleanupConnection();
    1650                 :   } else {
    1651               0 :     CleanupConnection();
    1652                 :   }
    1653                 : 
    1654               0 :   if (mDNSRequest) {
    1655               0 :     mDNSRequest->Cancel(NS_ERROR_UNEXPECTED);
    1656               0 :     mDNSRequest = nsnull;
    1657                 :   }
    1658                 : 
    1659               0 :   mInflateReader = nsnull;
    1660               0 :   mInflateStream = nsnull;
    1661                 : 
    1662               0 :   delete mCompressor;
    1663               0 :   mCompressor = nsnull;
    1664                 : 
    1665               0 :   if (!mCalledOnStop) {
    1666               0 :     mCalledOnStop = 1;
    1667               0 :     if (mListener)
    1668               0 :       NS_DispatchToMainThread(new CallOnStop(this, reason));
    1669                 :   }
    1670                 : 
    1671                 :   return;
    1672                 : }
    1673                 : 
    1674                 : void
    1675               0 : WebSocketChannel::AbortSession(nsresult reason)
    1676                 : {
    1677               0 :   LOG(("WebSocketChannel::AbortSession() %p [reason %x] stopped = %d\n",
    1678                 :        this, reason, mStopped));
    1679                 : 
    1680                 :   // normally this should be called on socket thread, but it is ok to call it
    1681                 :   // from the main thread before StartWebsocketData() has completed
    1682                 : 
    1683                 :   // When we are failing we need to close the TCP connection immediately
    1684                 :   // as per 7.1.1
    1685               0 :   mTCPClosed = true;
    1686                 : 
    1687               0 :   if (mLingeringCloseTimer) {
    1688               0 :     NS_ABORT_IF_FALSE(mStopped, "Lingering without Stop");
    1689               0 :     LOG(("WebSocketChannel:: Cleanup connection based on TCP Close"));
    1690               0 :     CleanupConnection();
    1691               0 :     return;
    1692                 :   }
    1693                 : 
    1694               0 :   if (mStopped)
    1695               0 :     return;
    1696               0 :   mStopped = 1;
    1697                 : 
    1698               0 :   if (mTransport && reason != NS_BASE_STREAM_CLOSED &&
    1699               0 :       !mRequestedClose && !mClientClosed && !mServerClosed) {
    1700               0 :     mRequestedClose = 1;
    1701               0 :     mStopOnClose = reason;
    1702               0 :     mSocketThread->Dispatch(
    1703               0 :       new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nsnull)),
    1704               0 :                            nsIEventTarget::DISPATCH_NORMAL);
    1705                 :   } else {
    1706               0 :     StopSession(reason);
    1707                 :   }
    1708                 : }
    1709                 : 
    1710                 : // ReleaseSession is called on orderly shutdown
    1711                 : void
    1712               0 : WebSocketChannel::ReleaseSession()
    1713                 : {
    1714               0 :   LOG(("WebSocketChannel::ReleaseSession() %p stopped = %d\n",
    1715                 :        this, mStopped));
    1716               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
    1717                 : 
    1718               0 :   if (mStopped)
    1719               0 :     return;
    1720               0 :   mStopped = 1;
    1721               0 :   StopSession(NS_OK);
    1722                 : }
    1723                 : 
    1724                 : nsresult
    1725               0 : WebSocketChannel::HandleExtensions()
    1726                 : {
    1727               0 :   LOG(("WebSocketChannel::HandleExtensions() %p\n", this));
    1728                 : 
    1729                 :   nsresult rv;
    1730               0 :   nsCAutoString extensions;
    1731                 : 
    1732               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    1733                 : 
    1734               0 :   rv = mHttpChannel->GetResponseHeader(
    1735               0 :     NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions);
    1736               0 :   if (NS_SUCCEEDED(rv)) {
    1737               0 :     if (!extensions.IsEmpty()) {
    1738               0 :       if (!extensions.Equals(NS_LITERAL_CSTRING("deflate-stream"))) {
    1739               0 :         LOG(("WebSocketChannel::OnStartRequest: "
    1740                 :              "HTTP Sec-WebSocket-Exensions negotiated unknown value %s\n",
    1741                 :              extensions.get()));
    1742               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1743               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1744                 :       }
    1745                 : 
    1746               0 :       if (!mAllowCompression) {
    1747               0 :         LOG(("WebSocketChannel::HandleExtensions: "
    1748                 :              "Recvd Compression Extension that wasn't offered\n"));
    1749               0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    1750               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1751                 :       }
    1752                 : 
    1753                 :       nsCOMPtr<nsIStreamConverterService> serv =
    1754               0 :         do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
    1755               0 :       if (NS_FAILED(rv)) {
    1756               0 :         LOG(("WebSocketChannel:: Cannot find compression service\n"));
    1757               0 :         AbortSession(NS_ERROR_UNEXPECTED);
    1758               0 :         return NS_ERROR_UNEXPECTED;
    1759                 :       }
    1760                 : 
    1761               0 :       rv = serv->AsyncConvertData("deflate", "uncompressed", this, nsnull,
    1762               0 :                                   getter_AddRefs(mInflateReader));
    1763                 : 
    1764               0 :       if (NS_FAILED(rv)) {
    1765               0 :         LOG(("WebSocketChannel:: Cannot find inflate listener\n"));
    1766               0 :         AbortSession(NS_ERROR_UNEXPECTED);
    1767               0 :         return NS_ERROR_UNEXPECTED;
    1768                 :       }
    1769                 : 
    1770               0 :       mInflateStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
    1771                 : 
    1772               0 :       if (NS_FAILED(rv)) {
    1773               0 :         LOG(("WebSocketChannel:: Cannot find inflate stream\n"));
    1774               0 :         AbortSession(NS_ERROR_UNEXPECTED);
    1775               0 :         return NS_ERROR_UNEXPECTED;
    1776                 :       }
    1777                 : 
    1778               0 :       mCompressor = new nsWSCompression(this, mSocketOut);
    1779               0 :       if (!mCompressor->Active()) {
    1780               0 :         LOG(("WebSocketChannel:: Cannot init deflate object\n"));
    1781               0 :         delete mCompressor;
    1782               0 :         mCompressor = nsnull;
    1783               0 :         AbortSession(NS_ERROR_UNEXPECTED);
    1784               0 :         return NS_ERROR_UNEXPECTED;
    1785                 :       }
    1786               0 :       mNegotiatedExtensions = extensions;
    1787                 :     }
    1788                 :   }
    1789                 : 
    1790               0 :   return NS_OK;
    1791                 : }
    1792                 : 
    1793                 : nsresult
    1794               0 : WebSocketChannel::SetupRequest()
    1795                 : {
    1796               0 :   LOG(("WebSocketChannel::SetupRequest() %p\n", this));
    1797                 : 
    1798                 :   nsresult rv;
    1799                 : 
    1800               0 :   if (mLoadGroup) {
    1801               0 :     rv = mHttpChannel->SetLoadGroup(mLoadGroup);
    1802               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1803                 :   }
    1804                 : 
    1805               0 :   rv = mHttpChannel->SetLoadFlags(nsIRequest::LOAD_BACKGROUND |
    1806                 :                                   nsIRequest::INHIBIT_CACHING |
    1807               0 :                                   nsIRequest::LOAD_BYPASS_CACHE);
    1808               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1809                 : 
    1810                 :   // draft-ietf-hybi-thewebsocketprotocol-07 illustrates Upgrade: websocket
    1811                 :   // in lower case, so go with that. It is technically case insensitive.
    1812               0 :   rv = mChannel->HTTPUpgrade(NS_LITERAL_CSTRING("websocket"), this);
    1813               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1814                 : 
    1815               0 :   mHttpChannel->SetRequestHeader(
    1816               0 :     NS_LITERAL_CSTRING("Sec-WebSocket-Version"),
    1817               0 :     NS_LITERAL_CSTRING(SEC_WEBSOCKET_VERSION), false);
    1818                 : 
    1819               0 :   if (!mOrigin.IsEmpty())
    1820               0 :     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), mOrigin,
    1821               0 :                                    false);
    1822                 : 
    1823               0 :   if (!mProtocol.IsEmpty())
    1824               0 :     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
    1825               0 :                                    mProtocol, true);
    1826                 : 
    1827               0 :   if (mAllowCompression)
    1828               0 :     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
    1829               0 :                                    NS_LITERAL_CSTRING("deflate-stream"),
    1830               0 :                                    false);
    1831                 : 
    1832                 :   PRUint8      *secKey;
    1833               0 :   nsCAutoString secKeyString;
    1834                 : 
    1835               0 :   rv = mRandomGenerator->GenerateRandomBytes(16, &secKey);
    1836               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1837               0 :   char* b64 = PL_Base64Encode((const char *)secKey, 16, nsnull);
    1838               0 :   NS_Free(secKey);
    1839               0 :   if (!b64)
    1840               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1841               0 :   secKeyString.Assign(b64);
    1842               0 :   PR_Free(b64);
    1843               0 :   mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Key"),
    1844               0 :                                  secKeyString, false);
    1845               0 :   LOG(("WebSocketChannel::SetupRequest: client key %s\n", secKeyString.get()));
    1846                 : 
    1847                 :   // prepare the value we expect to see in
    1848                 :   // the sec-websocket-accept response header
    1849               0 :   secKeyString.AppendLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    1850                 :   nsCOMPtr<nsICryptoHash> hasher =
    1851               0 :     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    1852               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1853               0 :   rv = hasher->Init(nsICryptoHash::SHA1);
    1854               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1855               0 :   rv = hasher->Update((const PRUint8 *) secKeyString.BeginWriting(),
    1856               0 :                       secKeyString.Length());
    1857               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1858               0 :   rv = hasher->Finish(true, mHashedSecret);
    1859               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1860               0 :   LOG(("WebSocketChannel::SetupRequest: expected server key %s\n",
    1861                 :        mHashedSecret.get()));
    1862                 : 
    1863               0 :   return NS_OK;
    1864                 : }
    1865                 : 
    1866                 : nsresult
    1867               0 : WebSocketChannel::ApplyForAdmission()
    1868                 : {
    1869               0 :   LOG(("WebSocketChannel::ApplyForAdmission() %p\n", this));
    1870                 : 
    1871                 :   // Websockets has a policy of 1 session at a time being allowed in the
    1872                 :   // CONNECTING state per server IP address (not hostname)
    1873                 : 
    1874                 :   nsresult rv;
    1875               0 :   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
    1876               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1877                 : 
    1878               0 :   nsCString hostName;
    1879               0 :   rv = mURI->GetHost(hostName);
    1880               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1881               0 :   mAddress = hostName;
    1882                 : 
    1883                 :   // expect the callback in ::OnLookupComplete
    1884               0 :   LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
    1885               0 :   nsCOMPtr<nsIThread> mainThread;
    1886               0 :   NS_GetMainThread(getter_AddRefs(mainThread));
    1887               0 :   dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mDNSRequest));
    1888               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1889                 : 
    1890               0 :   return NS_OK;
    1891                 : }
    1892                 : 
    1893                 : // Called after both OnStartRequest and OnTransportAvailable have
    1894                 : // executed. This essentially ends the handshake and starts the websockets
    1895                 : // protocol state machine.
    1896                 : nsresult
    1897               0 : WebSocketChannel::StartWebsocketData()
    1898                 : {
    1899               0 :   LOG(("WebSocketChannel::StartWebsocketData() %p", this));
    1900                 : 
    1901               0 :   return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
    1902                 : }
    1903                 : 
    1904                 : // nsIDNSListener
    1905                 : 
    1906                 : NS_IMETHODIMP
    1907               0 : WebSocketChannel::OnLookupComplete(nsICancelable *aRequest,
    1908                 :                                      nsIDNSRecord *aRecord,
    1909                 :                                      nsresult aStatus)
    1910                 : {
    1911               0 :   LOG(("WebSocketChannel::OnLookupComplete() %p [%p %p %x]\n",
    1912                 :        this, aRequest, aRecord, aStatus));
    1913                 : 
    1914               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    1915               0 :   NS_ABORT_IF_FALSE(aRequest == mDNSRequest || mStopped,
    1916                 :                     "wrong dns request");
    1917                 : 
    1918               0 :   if (mStopped) {
    1919               0 :     LOG(("WebSocketChannel::OnLookupComplete: Request Already Stopped\n"));
    1920               0 :     return NS_OK;
    1921                 :   }
    1922                 : 
    1923               0 :   mDNSRequest = nsnull;
    1924                 : 
    1925                 :   // These failures are not fatal - we just use the hostname as the key
    1926               0 :   if (NS_FAILED(aStatus)) {
    1927               0 :     LOG(("WebSocketChannel::OnLookupComplete: No DNS Response\n"));
    1928                 :   } else {
    1929               0 :     nsresult rv = aRecord->GetNextAddrAsString(mAddress);
    1930               0 :     if (NS_FAILED(rv))
    1931               0 :       LOG(("WebSocketChannel::OnLookupComplete: Failed GetNextAddr\n"));
    1932                 :   }
    1933                 : 
    1934               0 :   if (sWebSocketAdmissions->ConditionallyConnect(mAddress, this)) {
    1935               0 :     LOG(("WebSocketChannel::OnLookupComplete: Proceeding with Open\n"));
    1936                 :   } else {
    1937               0 :     LOG(("WebSocketChannel::OnLookupComplete: Deferring Open\n"));
    1938                 :   }
    1939                 : 
    1940               0 :   return NS_OK;
    1941                 : }
    1942                 : 
    1943                 : // nsIInterfaceRequestor
    1944                 : 
    1945                 : NS_IMETHODIMP
    1946               0 : WebSocketChannel::GetInterface(const nsIID & iid, void **result NS_OUTPARAM)
    1947                 : {
    1948               0 :   LOG(("WebSocketChannel::GetInterface() %p\n", this));
    1949                 : 
    1950               0 :   if (iid.Equals(NS_GET_IID(nsIChannelEventSink)))
    1951               0 :     return QueryInterface(iid, result);
    1952                 : 
    1953               0 :   if (mCallbacks)
    1954               0 :     return mCallbacks->GetInterface(iid, result);
    1955                 : 
    1956               0 :   return NS_ERROR_FAILURE;
    1957                 : }
    1958                 : 
    1959                 : // nsIChannelEventSink
    1960                 : 
    1961                 : NS_IMETHODIMP
    1962               0 : WebSocketChannel::AsyncOnChannelRedirect(
    1963                 :                     nsIChannel *oldChannel,
    1964                 :                     nsIChannel *newChannel,
    1965                 :                     PRUint32 flags,
    1966                 :                     nsIAsyncVerifyRedirectCallback *callback)
    1967                 : {
    1968               0 :   LOG(("WebSocketChannel::AsyncOnChannelRedirect() %p\n", this));
    1969                 :   nsresult rv;
    1970                 : 
    1971               0 :   nsCOMPtr<nsIURI> newuri;
    1972               0 :   rv = newChannel->GetURI(getter_AddRefs(newuri));
    1973               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1974                 : 
    1975                 :   // newuri is expected to be http or https
    1976               0 :   bool newuriIsHttps = false;
    1977               0 :   rv = newuri->SchemeIs("https", &newuriIsHttps);
    1978               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1979                 : 
    1980               0 :   if (!mAutoFollowRedirects) {
    1981                 :     // Even if redirects configured off, still allow them for HTTP Strict
    1982                 :     // Transport Security (from ws://FOO to https://FOO (mapped to wss://FOO)
    1983                 : 
    1984               0 :     nsCOMPtr<nsIURI> clonedNewURI;
    1985               0 :     rv = newuri->Clone(getter_AddRefs(clonedNewURI));
    1986               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1987                 : 
    1988               0 :     rv = clonedNewURI->SetScheme(NS_LITERAL_CSTRING("ws"));
    1989               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1990                 : 
    1991               0 :     nsCOMPtr<nsIURI> currentURI;
    1992               0 :     rv = GetURI(getter_AddRefs(currentURI));
    1993               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1994                 : 
    1995                 :     // currentURI is expected to be ws or wss
    1996               0 :     bool currentIsHttps = false;
    1997               0 :     rv = currentURI->SchemeIs("wss", &currentIsHttps);
    1998               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1999                 : 
    2000               0 :     bool uriEqual = false;
    2001               0 :     rv = clonedNewURI->Equals(currentURI, &uriEqual);
    2002               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2003                 : 
    2004                 :     // It's only a HSTS redirect if we started with non-secure, are going to
    2005                 :     // secure, and the new URI is otherwise the same as the old one.
    2006               0 :     if (!(!currentIsHttps && newuriIsHttps && uriEqual)) {
    2007               0 :       nsCAutoString newSpec;
    2008               0 :       rv = newuri->GetSpec(newSpec);
    2009               0 :       NS_ENSURE_SUCCESS(rv, rv);
    2010                 : 
    2011               0 :       LOG(("WebSocketChannel: Redirect to %s denied by configuration\n",
    2012                 :            newSpec.get()));
    2013               0 :       return NS_ERROR_FAILURE;
    2014                 :     }
    2015                 :   }
    2016                 : 
    2017               0 :   if (mEncrypted && !newuriIsHttps) {
    2018               0 :     nsCAutoString spec;
    2019               0 :     if (NS_SUCCEEDED(newuri->GetSpec(spec)))
    2020               0 :       LOG(("WebSocketChannel: Redirect to %s violates encryption rule\n",
    2021                 :            spec.get()));
    2022               0 :     return NS_ERROR_FAILURE;
    2023                 :   }
    2024                 : 
    2025               0 :   nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel, &rv);
    2026               0 :   if (NS_FAILED(rv)) {
    2027               0 :     LOG(("WebSocketChannel: Redirect could not QI to HTTP\n"));
    2028               0 :     return rv;
    2029                 :   }
    2030                 : 
    2031                 :   nsCOMPtr<nsIHttpChannelInternal> newUpgradeChannel =
    2032               0 :     do_QueryInterface(newChannel, &rv);
    2033                 : 
    2034               0 :   if (NS_FAILED(rv)) {
    2035               0 :     LOG(("WebSocketChannel: Redirect could not QI to HTTP Upgrade\n"));
    2036               0 :     return rv;
    2037                 :   }
    2038                 : 
    2039                 :   // The redirect is likely OK
    2040                 : 
    2041               0 :   newChannel->SetNotificationCallbacks(this);
    2042                 : 
    2043               0 :   mEncrypted = newuriIsHttps;
    2044               0 :   newuri->Clone(getter_AddRefs(mURI));
    2045               0 :   if (mEncrypted)
    2046               0 :     rv = mURI->SetScheme(NS_LITERAL_CSTRING("wss"));
    2047                 :   else
    2048               0 :     rv = mURI->SetScheme(NS_LITERAL_CSTRING("ws"));
    2049                 : 
    2050               0 :   mHttpChannel = newHttpChannel;
    2051               0 :   mChannel = newUpgradeChannel;
    2052               0 :   rv = SetupRequest();
    2053               0 :   if (NS_FAILED(rv)) {
    2054               0 :     LOG(("WebSocketChannel: Redirect could not SetupRequest()\n"));
    2055               0 :     return rv;
    2056                 :   }
    2057                 : 
    2058                 :   // We cannot just tell the callback OK right now due to the 1 connect at a
    2059                 :   // time policy. First we need to complete the old location and then start the
    2060                 :   // lookup chain for the new location - once that is complete and we have been
    2061                 :   // admitted, OnRedirectVerifyCallback(NS_OK) will be called out of BeginOpen()
    2062                 : 
    2063               0 :   sWebSocketAdmissions->Complete(this);
    2064               0 :   mAddress.Truncate();
    2065               0 :   mRedirectCallback = callback;
    2066                 : 
    2067               0 :   mChannelWasOpened = 0;
    2068                 : 
    2069               0 :   rv = ApplyForAdmission();
    2070               0 :   if (NS_FAILED(rv)) {
    2071               0 :     LOG(("WebSocketChannel: Redirect failed due to DNS failure\n"));
    2072               0 :     mRedirectCallback = nsnull;
    2073               0 :     return rv;
    2074                 :   }
    2075                 : 
    2076               0 :   return NS_OK;
    2077                 : }
    2078                 : 
    2079                 : // nsITimerCallback
    2080                 : 
    2081                 : NS_IMETHODIMP
    2082               0 : WebSocketChannel::Notify(nsITimer *timer)
    2083                 : {
    2084               0 :   LOG(("WebSocketChannel::Notify() %p [%p]\n", this, timer));
    2085                 : 
    2086               0 :   if (timer == mCloseTimer) {
    2087               0 :     NS_ABORT_IF_FALSE(mClientClosed, "Close Timeout without local close");
    2088               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
    2089                 :                       "not socket thread");
    2090                 : 
    2091               0 :     mCloseTimer = nsnull;
    2092               0 :     if (mStopped || mServerClosed)                /* no longer relevant */
    2093               0 :       return NS_OK;
    2094                 : 
    2095               0 :     LOG(("WebSocketChannel:: Expecting Server Close - Timed Out\n"));
    2096               0 :     AbortSession(NS_ERROR_NET_TIMEOUT);
    2097               0 :   } else if (timer == mOpenTimer) {
    2098               0 :     NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest,
    2099                 :                       "Open Timer after open complete");
    2100               0 :     NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2101                 : 
    2102               0 :     mOpenTimer = nsnull;
    2103               0 :     LOG(("WebSocketChannel:: Connection Timed Out\n"));
    2104               0 :     if (mStopped || mServerClosed)                /* no longer relevant */
    2105               0 :       return NS_OK;
    2106                 : 
    2107               0 :     AbortSession(NS_ERROR_NET_TIMEOUT);
    2108               0 :   } else if (timer == mPingTimer) {
    2109               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
    2110                 :                       "not socket thread");
    2111                 : 
    2112               0 :     if (mClientClosed || mServerClosed || mRequestedClose) {
    2113                 :       // no point in worrying about ping now
    2114               0 :       mPingTimer = nsnull;
    2115               0 :       return NS_OK;
    2116                 :     }
    2117                 : 
    2118               0 :     if (!mPingOutstanding) {
    2119               0 :       LOG(("nsWebSocketChannel:: Generating Ping\n"));
    2120               0 :       mPingOutstanding = 1;
    2121               0 :       GeneratePing();
    2122               0 :       mPingTimer->InitWithCallback(this, mPingResponseTimeout,
    2123               0 :                                    nsITimer::TYPE_ONE_SHOT);
    2124                 :     } else {
    2125               0 :       LOG(("nsWebSocketChannel:: Timed out Ping\n"));
    2126               0 :       mPingTimer = nsnull;
    2127               0 :       AbortSession(NS_ERROR_NET_TIMEOUT);
    2128                 :     }
    2129               0 :   } else if (timer == mLingeringCloseTimer) {
    2130               0 :     LOG(("WebSocketChannel:: Lingering Close Timer"));
    2131               0 :     CleanupConnection();
    2132                 :   } else {
    2133               0 :     NS_ABORT_IF_FALSE(0, "Unknown Timer");
    2134                 :   }
    2135                 : 
    2136               0 :   return NS_OK;
    2137                 : }
    2138                 : 
    2139                 : 
    2140                 : NS_IMETHODIMP
    2141               0 : WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
    2142                 : {
    2143               0 :   LOG(("WebSocketChannel::GetSecurityInfo() %p\n", this));
    2144               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2145                 : 
    2146               0 :   if (mTransport) {
    2147               0 :     if (NS_FAILED(mTransport->GetSecurityInfo(aSecurityInfo)))
    2148               0 :       *aSecurityInfo = nsnull;
    2149                 :   }
    2150               0 :   return NS_OK;
    2151                 : }
    2152                 : 
    2153                 : 
    2154                 : NS_IMETHODIMP
    2155               0 : WebSocketChannel::AsyncOpen(nsIURI *aURI,
    2156                 :                             const nsACString &aOrigin,
    2157                 :                             nsIWebSocketListener *aListener,
    2158                 :                             nsISupports *aContext)
    2159                 : {
    2160               0 :   LOG(("WebSocketChannel::AsyncOpen() %p\n", this));
    2161                 : 
    2162               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2163                 : 
    2164               0 :   if (!aURI || !aListener) {
    2165               0 :     LOG(("WebSocketChannel::AsyncOpen() Uri or Listener null"));
    2166               0 :     return NS_ERROR_UNEXPECTED;
    2167                 :   }
    2168                 : 
    2169               0 :   if (mListener)
    2170               0 :     return NS_ERROR_ALREADY_OPENED;
    2171                 : 
    2172                 :   nsresult rv;
    2173                 : 
    2174               0 :   mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
    2175               0 :   if (NS_FAILED(rv)) {
    2176               0 :     NS_WARNING("unable to continue without socket transport service");
    2177               0 :     return rv;
    2178                 :   }
    2179                 : 
    2180                 :   mRandomGenerator =
    2181               0 :     do_GetService("@mozilla.org/security/random-generator;1", &rv);
    2182               0 :   if (NS_FAILED(rv)) {
    2183               0 :     NS_WARNING("unable to continue without random number generator");
    2184               0 :     return rv;
    2185                 :   }
    2186                 : 
    2187               0 :   nsCOMPtr<nsIPrefBranch> prefService;
    2188               0 :   prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
    2189                 : 
    2190               0 :   if (prefService) {
    2191                 :     PRInt32 intpref;
    2192                 :     bool boolpref;
    2193               0 :     rv = prefService->GetIntPref("network.websocket.max-message-size", 
    2194               0 :                                  &intpref);
    2195               0 :     if (NS_SUCCEEDED(rv)) {
    2196               0 :       mMaxMessageSize = clamped(intpref, 1024, PR_INT32_MAX);
    2197                 :     }
    2198               0 :     rv = prefService->GetIntPref("network.websocket.timeout.close", &intpref);
    2199               0 :     if (NS_SUCCEEDED(rv)) {
    2200               0 :       mCloseTimeout = clamped(intpref, 1, 1800) * 1000;
    2201                 :     }
    2202               0 :     rv = prefService->GetIntPref("network.websocket.timeout.open", &intpref);
    2203               0 :     if (NS_SUCCEEDED(rv)) {
    2204               0 :       mOpenTimeout = clamped(intpref, 1, 1800) * 1000;
    2205                 :     }
    2206               0 :     rv = prefService->GetIntPref("network.websocket.timeout.ping.request",
    2207               0 :                                  &intpref);
    2208               0 :     if (NS_SUCCEEDED(rv)) {
    2209               0 :       mPingTimeout = clamped(intpref, 0, 86400) * 1000;
    2210                 :     }
    2211               0 :     rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
    2212               0 :                                  &intpref);
    2213               0 :     if (NS_SUCCEEDED(rv)) {
    2214               0 :       mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
    2215                 :     }
    2216               0 :     rv = prefService->GetBoolPref("network.websocket.extensions.stream-deflate",
    2217               0 :                                   &boolpref);
    2218               0 :     if (NS_SUCCEEDED(rv)) {
    2219               0 :       mAllowCompression = boolpref ? 1 : 0;
    2220                 :     }
    2221               0 :     rv = prefService->GetBoolPref("network.websocket.auto-follow-http-redirects",
    2222               0 :                                   &boolpref);
    2223               0 :     if (NS_SUCCEEDED(rv)) {
    2224               0 :       mAutoFollowRedirects = boolpref ? 1 : 0;
    2225                 :     }
    2226               0 :     rv = prefService->GetIntPref
    2227               0 :       ("network.websocket.max-connections", &intpref);
    2228               0 :     if (NS_SUCCEEDED(rv)) {
    2229               0 :       mMaxConcurrentConnections = clamped(intpref, 1, 0xffff);
    2230                 :     }
    2231                 :   }
    2232                 : 
    2233               0 :   if (sWebSocketAdmissions)
    2234               0 :     LOG(("WebSocketChannel::AsyncOpen %p sessionCount=%d max=%d\n", this,
    2235                 :          sWebSocketAdmissions->SessionCount(), mMaxConcurrentConnections));
    2236                 : 
    2237               0 :   if (sWebSocketAdmissions &&
    2238               0 :       sWebSocketAdmissions->SessionCount() >= mMaxConcurrentConnections)
    2239                 :   {
    2240               0 :     LOG(("WebSocketChannel: max concurrency %d exceeded (%d)",
    2241                 :          mMaxConcurrentConnections,
    2242                 :          sWebSocketAdmissions->SessionCount()));
    2243                 : 
    2244                 :     // WebSocket connections are expected to be long lived, so return
    2245                 :     // an error here instead of queueing
    2246               0 :     return NS_ERROR_SOCKET_CREATE_FAILED;
    2247                 :   }
    2248                 : 
    2249               0 :   if (mPingTimeout) {
    2250               0 :     mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    2251               0 :     if (NS_FAILED(rv)) {
    2252               0 :       NS_WARNING("unable to create ping timer. Carrying on.");
    2253                 :     } else {
    2254               0 :       LOG(("WebSocketChannel will generate ping after %d ms of receive silence\n",
    2255                 :            mPingTimeout));
    2256               0 :       mPingTimer->SetTarget(mSocketThread);
    2257               0 :       mPingTimer->InitWithCallback(this, mPingTimeout, nsITimer::TYPE_ONE_SHOT);
    2258                 :     }
    2259                 :   }
    2260                 : 
    2261               0 :   mOriginalURI = aURI;
    2262               0 :   mURI = mOriginalURI;
    2263               0 :   mListener = aListener;
    2264               0 :   mContext = aContext;
    2265               0 :   mOrigin = aOrigin;
    2266                 : 
    2267               0 :   nsCOMPtr<nsIURI> localURI;
    2268               0 :   nsCOMPtr<nsIChannel> localChannel;
    2269                 : 
    2270               0 :   mURI->Clone(getter_AddRefs(localURI));
    2271               0 :   if (mEncrypted)
    2272               0 :     rv = localURI->SetScheme(NS_LITERAL_CSTRING("https"));
    2273                 :   else
    2274               0 :     rv = localURI->SetScheme(NS_LITERAL_CSTRING("http"));
    2275               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2276                 : 
    2277               0 :   nsCOMPtr<nsIIOService> ioService;
    2278               0 :   ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
    2279               0 :   if (NS_FAILED(rv)) {
    2280               0 :     NS_WARNING("unable to continue without io service");
    2281               0 :     return rv;
    2282                 :   }
    2283                 : 
    2284               0 :   nsCOMPtr<nsIIOService2> io2 = do_QueryInterface(ioService, &rv);
    2285               0 :   if (NS_FAILED(rv)) {
    2286               0 :     NS_WARNING("WebSocketChannel: unable to continue without ioservice2");
    2287               0 :     return rv;
    2288                 :   }
    2289                 : 
    2290               0 :   rv = io2->NewChannelFromURIWithProxyFlags(
    2291                 :               localURI,
    2292                 :               mURI,
    2293                 :               nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY |
    2294                 :               nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
    2295                 :               nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
    2296               0 :               getter_AddRefs(localChannel));
    2297               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2298                 : 
    2299                 :   // Pass most GetInterface() requests through to our instantiator, but handle
    2300                 :   // nsIChannelEventSink in this object in order to deal with redirects
    2301               0 :   localChannel->SetNotificationCallbacks(this);
    2302                 : 
    2303               0 :   mChannel = do_QueryInterface(localChannel, &rv);
    2304               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2305                 : 
    2306               0 :   mHttpChannel = do_QueryInterface(localChannel, &rv);
    2307               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2308                 : 
    2309               0 :   rv = SetupRequest();
    2310               0 :   if (NS_FAILED(rv))
    2311               0 :     return rv;
    2312                 : 
    2313               0 :   return ApplyForAdmission();
    2314                 : }
    2315                 : 
    2316                 : NS_IMETHODIMP
    2317               0 : WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
    2318                 : {
    2319               0 :   LOG(("WebSocketChannel::Close() %p\n", this));
    2320               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2321                 : 
    2322               0 :   if (mRequestedClose) {
    2323               0 :     return NS_OK;
    2324                 :   }
    2325                 : 
    2326               0 :   if (!mTransport) {
    2327               0 :     LOG(("WebSocketChannel::Close() without transport - aborting."));
    2328               0 :     AbortSession(NS_ERROR_NOT_CONNECTED);
    2329               0 :     return NS_ERROR_NOT_CONNECTED;
    2330                 :   }
    2331                 : 
    2332                 :   // The API requires the UTF-8 string to be 123 or less bytes
    2333               0 :   if (reason.Length() > 123)
    2334               0 :     return NS_ERROR_ILLEGAL_VALUE;
    2335                 : 
    2336               0 :   mRequestedClose = 1;
    2337               0 :   mScriptCloseReason = reason;
    2338               0 :   mScriptCloseCode = code;
    2339                 : 
    2340               0 :   return mSocketThread->Dispatch(
    2341               0 :       new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nsnull)),
    2342               0 :                            nsIEventTarget::DISPATCH_NORMAL);
    2343                 : }
    2344                 : 
    2345                 : NS_IMETHODIMP
    2346               0 : WebSocketChannel::SendMsg(const nsACString &aMsg)
    2347                 : {
    2348               0 :   LOG(("WebSocketChannel::SendMsg() %p\n", this));
    2349                 : 
    2350               0 :   return SendMsgCommon(&aMsg, false, aMsg.Length());
    2351                 : }
    2352                 : 
    2353                 : NS_IMETHODIMP
    2354               0 : WebSocketChannel::SendBinaryMsg(const nsACString &aMsg)
    2355                 : {
    2356               0 :   LOG(("WebSocketChannel::SendBinaryMsg() %p len=%d\n", this, aMsg.Length()));
    2357               0 :   return SendMsgCommon(&aMsg, true, aMsg.Length());
    2358                 : }
    2359                 : 
    2360                 : NS_IMETHODIMP
    2361               0 : WebSocketChannel::SendBinaryStream(nsIInputStream *aStream, PRUint32 aLength)
    2362                 : {
    2363               0 :   LOG(("WebSocketChannel::SendBinaryStream() %p\n", this));
    2364                 : 
    2365               0 :   return SendMsgCommon(nsnull, true, aLength, aStream);
    2366                 : }
    2367                 : 
    2368                 : nsresult
    2369               0 : WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary,
    2370                 :                                 PRUint32 aLength, nsIInputStream *aStream)
    2371                 : {
    2372               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2373                 : 
    2374               0 :   if (mRequestedClose) {
    2375               0 :     LOG(("WebSocketChannel:: Error: send when closed\n"));
    2376               0 :     return NS_ERROR_UNEXPECTED;
    2377                 :   }
    2378                 : 
    2379               0 :   if (mStopped) {
    2380               0 :     LOG(("WebSocketChannel:: Error: send when stopped\n"));
    2381               0 :     return NS_ERROR_NOT_CONNECTED;
    2382                 :   }
    2383                 : 
    2384               0 :   NS_ABORT_IF_FALSE(mMaxMessageSize >= 0, "max message size negative");
    2385               0 :   if (aLength > static_cast<PRUint32>(mMaxMessageSize)) {
    2386               0 :     LOG(("WebSocketChannel:: Error: message too big\n"));
    2387               0 :     return NS_ERROR_FILE_TOO_BIG;
    2388                 :   }
    2389                 : 
    2390               0 :   return mSocketThread->Dispatch(
    2391               0 :     aStream ? new OutboundEnqueuer(this, new OutboundMessage(aStream, aLength))
    2392                 :             : new OutboundEnqueuer(this,
    2393                 :                      new OutboundMessage(aIsBinary ? kMsgTypeBinaryString
    2394                 :                                                    : kMsgTypeString,
    2395               0 :                                          new nsCString(*aMsg))),
    2396               0 :     nsIEventTarget::DISPATCH_NORMAL);
    2397                 : }
    2398                 : 
    2399                 : NS_IMETHODIMP
    2400               0 : WebSocketChannel::OnTransportAvailable(nsISocketTransport *aTransport,
    2401                 :                                        nsIAsyncInputStream *aSocketIn,
    2402                 :                                        nsIAsyncOutputStream *aSocketOut)
    2403                 : {
    2404               0 :   LOG(("WebSocketChannel::OnTransportAvailable %p [%p %p %p] rcvdonstart=%d\n",
    2405                 :        this, aTransport, aSocketIn, aSocketOut, mRecvdHttpOnStartRequest));
    2406                 : 
    2407               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2408               0 :   NS_ABORT_IF_FALSE(!mRecvdHttpUpgradeTransport, "OTA duplicated");
    2409               0 :   NS_ABORT_IF_FALSE(aSocketIn, "OTA with invalid socketIn");
    2410                 : 
    2411               0 :   mTransport = aTransport;
    2412               0 :   mSocketIn = aSocketIn;
    2413               0 :   mSocketOut = aSocketOut;
    2414                 : 
    2415                 :   nsresult rv;
    2416               0 :   rv = mTransport->SetEventSink(nsnull, nsnull);
    2417               0 :   if (NS_FAILED(rv)) return rv;
    2418               0 :   rv = mTransport->SetSecurityCallbacks(this);
    2419               0 :   if (NS_FAILED(rv)) return rv;
    2420                 : 
    2421               0 :   mRecvdHttpUpgradeTransport = 1;
    2422               0 :   if (mRecvdHttpOnStartRequest)
    2423               0 :     return StartWebsocketData();
    2424               0 :   return NS_OK;
    2425                 : }
    2426                 : 
    2427                 : // nsIRequestObserver (from nsIStreamListener)
    2428                 : 
    2429                 : NS_IMETHODIMP
    2430               0 : WebSocketChannel::OnStartRequest(nsIRequest *aRequest,
    2431                 :                                  nsISupports *aContext)
    2432                 : {
    2433               0 :   LOG(("WebSocketChannel::OnStartRequest(): %p [%p %p] recvdhttpupgrade=%d\n",
    2434                 :        this, aRequest, aContext, mRecvdHttpUpgradeTransport));
    2435               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2436               0 :   NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest, "OTA duplicated");
    2437                 : 
    2438                 :   // Generating the onStart event will take us out of the
    2439                 :   // CONNECTING state which means we can now open another,
    2440                 :   // perhaps parallel, connection to the same host if one
    2441                 :   // is pending
    2442                 : 
    2443               0 :   if (sWebSocketAdmissions->Complete(this))
    2444               0 :     LOG(("WebSocketChannel::OnStartRequest: Starting Pending Open\n"));
    2445                 :   else
    2446               0 :     LOG(("WebSocketChannel::OnStartRequest: No More Pending Opens\n"));
    2447                 : 
    2448               0 :   if (mOpenTimer) {
    2449               0 :     mOpenTimer->Cancel();
    2450               0 :     mOpenTimer = nsnull;
    2451                 :   }
    2452                 : 
    2453               0 :   if (mStopped) {
    2454               0 :     LOG(("WebSocketChannel::OnStartRequest: Channel Already Done\n"));
    2455               0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    2456               0 :     return NS_ERROR_CONNECTION_REFUSED;
    2457                 :   }
    2458                 : 
    2459                 :   nsresult rv;
    2460                 :   PRUint32 status;
    2461                 :   char *val, *token;
    2462                 : 
    2463               0 :   rv = mHttpChannel->GetResponseStatus(&status);
    2464               0 :   if (NS_FAILED(rv)) {
    2465               0 :     LOG(("WebSocketChannel::OnStartRequest: No HTTP Response\n"));
    2466               0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    2467               0 :     return NS_ERROR_CONNECTION_REFUSED;
    2468                 :   }
    2469                 : 
    2470               0 :   LOG(("WebSocketChannel::OnStartRequest: HTTP status %d\n", status));
    2471               0 :   if (status != 101) {
    2472               0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    2473               0 :     return NS_ERROR_CONNECTION_REFUSED;
    2474                 :   }
    2475                 : 
    2476               0 :   nsCAutoString respUpgrade;
    2477               0 :   rv = mHttpChannel->GetResponseHeader(
    2478               0 :     NS_LITERAL_CSTRING("Upgrade"), respUpgrade);
    2479                 : 
    2480               0 :   if (NS_SUCCEEDED(rv)) {
    2481               0 :     rv = NS_ERROR_ILLEGAL_VALUE;
    2482               0 :     if (!respUpgrade.IsEmpty()) {
    2483               0 :       val = respUpgrade.BeginWriting();
    2484               0 :       while ((token = nsCRT::strtok(val, ", \t", &val))) {
    2485               0 :         if (PL_strcasecmp(token, "Websocket") == 0) {
    2486               0 :           rv = NS_OK;
    2487               0 :           break;
    2488                 :         }
    2489                 :       }
    2490                 :     }
    2491                 :   }
    2492                 : 
    2493               0 :   if (NS_FAILED(rv)) {
    2494               0 :     LOG(("WebSocketChannel::OnStartRequest: "
    2495                 :          "HTTP response header Upgrade: websocket not found\n"));
    2496               0 :     AbortSession(rv);
    2497               0 :     return rv;
    2498                 :   }
    2499                 : 
    2500               0 :   nsCAutoString respConnection;
    2501               0 :   rv = mHttpChannel->GetResponseHeader(
    2502               0 :     NS_LITERAL_CSTRING("Connection"), respConnection);
    2503                 : 
    2504               0 :   if (NS_SUCCEEDED(rv)) {
    2505               0 :     rv = NS_ERROR_ILLEGAL_VALUE;
    2506               0 :     if (!respConnection.IsEmpty()) {
    2507               0 :       val = respConnection.BeginWriting();
    2508               0 :       while ((token = nsCRT::strtok(val, ", \t", &val))) {
    2509               0 :         if (PL_strcasecmp(token, "Upgrade") == 0) {
    2510               0 :           rv = NS_OK;
    2511               0 :           break;
    2512                 :         }
    2513                 :       }
    2514                 :     }
    2515                 :   }
    2516                 : 
    2517               0 :   if (NS_FAILED(rv)) {
    2518               0 :     LOG(("WebSocketChannel::OnStartRequest: "
    2519                 :          "HTTP response header 'Connection: Upgrade' not found\n"));
    2520               0 :     AbortSession(rv);
    2521               0 :     return rv;
    2522                 :   }
    2523                 : 
    2524               0 :   nsCAutoString respAccept;
    2525               0 :   rv = mHttpChannel->GetResponseHeader(
    2526               0 :                        NS_LITERAL_CSTRING("Sec-WebSocket-Accept"),
    2527               0 :                        respAccept);
    2528                 : 
    2529               0 :   if (NS_FAILED(rv) ||
    2530               0 :     respAccept.IsEmpty() || !respAccept.Equals(mHashedSecret)) {
    2531               0 :     LOG(("WebSocketChannel::OnStartRequest: "
    2532                 :          "HTTP response header Sec-WebSocket-Accept check failed\n"));
    2533               0 :     LOG(("WebSocketChannel::OnStartRequest: Expected %s recevied %s\n",
    2534                 :          mHashedSecret.get(), respAccept.get()));
    2535               0 :     AbortSession(NS_ERROR_ILLEGAL_VALUE);
    2536               0 :     return NS_ERROR_ILLEGAL_VALUE;
    2537                 :   }
    2538                 : 
    2539                 :   // If we sent a sub protocol header, verify the response matches
    2540                 :   // If it does not, set mProtocol to "" so the protocol attribute
    2541                 :   // of the WebSocket JS object reflects that
    2542               0 :   if (!mProtocol.IsEmpty()) {
    2543               0 :     nsCAutoString respProtocol;
    2544               0 :     rv = mHttpChannel->GetResponseHeader(
    2545               0 :                          NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), 
    2546               0 :                          respProtocol);
    2547               0 :     if (NS_SUCCEEDED(rv)) {
    2548               0 :       rv = NS_ERROR_ILLEGAL_VALUE;
    2549               0 :       val = mProtocol.BeginWriting();
    2550               0 :       while ((token = nsCRT::strtok(val, ", \t", &val))) {
    2551               0 :         if (PL_strcasecmp(token, respProtocol.get()) == 0) {
    2552               0 :           rv = NS_OK;
    2553               0 :           break;
    2554                 :         }
    2555                 :       }
    2556                 : 
    2557               0 :       if (NS_SUCCEEDED(rv)) {
    2558               0 :         LOG(("WebsocketChannel::OnStartRequest: subprotocol %s confirmed",
    2559                 :              respProtocol.get()));
    2560               0 :         mProtocol = respProtocol;
    2561                 :       } else {
    2562               0 :         LOG(("WebsocketChannel::OnStartRequest: "
    2563                 :              "subprotocol [%s] not found - %s returned",
    2564                 :              mProtocol.get(), respProtocol.get()));
    2565               0 :         mProtocol.Truncate();
    2566                 :       }
    2567                 :     } else {
    2568               0 :       LOG(("WebsocketChannel::OnStartRequest "
    2569                 :                  "subprotocol [%s] not found - none returned",
    2570                 :                  mProtocol.get()));
    2571               0 :       mProtocol.Truncate();
    2572                 :     }
    2573                 :   }
    2574                 : 
    2575               0 :   rv = HandleExtensions();
    2576               0 :   if (NS_FAILED(rv))
    2577               0 :     return rv;
    2578                 : 
    2579               0 :   LOG(("WebSocketChannel::OnStartRequest: Notifying Listener %p\n",
    2580                 :        mListener.get()));
    2581                 : 
    2582               0 :   if (mListener)
    2583               0 :     mListener->OnStart(mContext);
    2584                 : 
    2585               0 :   mRecvdHttpOnStartRequest = 1;
    2586               0 :   if (mRecvdHttpUpgradeTransport)
    2587               0 :     return StartWebsocketData();
    2588                 : 
    2589               0 :   return NS_OK;
    2590                 : }
    2591                 : 
    2592                 : NS_IMETHODIMP
    2593               0 : WebSocketChannel::OnStopRequest(nsIRequest *aRequest,
    2594                 :                                   nsISupports *aContext,
    2595                 :                                   nsresult aStatusCode)
    2596                 : {
    2597               0 :   LOG(("WebSocketChannel::OnStopRequest() %p [%p %p %x]\n",
    2598                 :        this, aRequest, aContext, aStatusCode));
    2599               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
    2600                 : 
    2601                 :   // This is the end of the HTTP upgrade transaction, the
    2602                 :   // upgraded streams live on
    2603                 : 
    2604               0 :   mChannel = nsnull;
    2605               0 :   mHttpChannel = nsnull;
    2606               0 :   mLoadGroup = nsnull;
    2607               0 :   mCallbacks = nsnull;
    2608                 : 
    2609               0 :   return NS_OK;
    2610                 : }
    2611                 : 
    2612                 : // nsIInputStreamCallback
    2613                 : 
    2614                 : NS_IMETHODIMP
    2615               0 : WebSocketChannel::OnInputStreamReady(nsIAsyncInputStream *aStream)
    2616                 : {
    2617               0 :   LOG(("WebSocketChannel::OnInputStreamReady() %p\n", this));
    2618               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
    2619                 : 
    2620               0 :   if (!mSocketIn) // did we we clean up the socket after scheduling InputReady?
    2621               0 :     return NS_OK;
    2622                 :   
    2623               0 :   nsRefPtr<nsIStreamListener>    deleteProtector1(mInflateReader);
    2624               0 :   nsRefPtr<nsIStringInputStream> deleteProtector2(mInflateStream);
    2625                 : 
    2626                 :   // this is after the  http upgrade - so we are speaking websockets
    2627                 :   char  buffer[2048];
    2628                 :   PRUint32 count;
    2629                 :   nsresult rv;
    2630                 : 
    2631               0 :   do {
    2632               0 :     rv = mSocketIn->Read((char *)buffer, 2048, &count);
    2633               0 :     LOG(("WebSocketChannel::OnInputStreamReady: read %u rv %x\n", count, rv));
    2634                 : 
    2635               0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    2636               0 :       mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
    2637               0 :       return NS_OK;
    2638                 :     }
    2639                 : 
    2640               0 :     if (NS_FAILED(rv)) {
    2641               0 :       mTCPClosed = true;
    2642               0 :       AbortSession(rv);
    2643               0 :       return rv;
    2644                 :     }
    2645                 : 
    2646               0 :     if (count == 0) {
    2647               0 :       mTCPClosed = true;
    2648               0 :       AbortSession(NS_BASE_STREAM_CLOSED);
    2649               0 :       return NS_OK;
    2650                 :     }
    2651                 : 
    2652               0 :     if (mStopped) {
    2653               0 :       NS_ABORT_IF_FALSE(mLingeringCloseTimer,
    2654                 :                         "OnInputReady after stop without linger");
    2655               0 :       continue;
    2656                 :     }
    2657                 : 
    2658               0 :     if (mInflateReader) {
    2659               0 :       mInflateStream->ShareData(buffer, count);
    2660               0 :       rv = mInflateReader->OnDataAvailable(nsnull, mSocketIn, mInflateStream, 
    2661               0 :                                            0, count);
    2662                 :     } else {
    2663               0 :       rv = ProcessInput((PRUint8 *)buffer, count);
    2664                 :     }
    2665                 : 
    2666               0 :     if (NS_FAILED(rv)) {
    2667               0 :       AbortSession(rv);
    2668               0 :       return rv;
    2669                 :     }
    2670               0 :   } while (NS_SUCCEEDED(rv) && mSocketIn);
    2671                 : 
    2672               0 :   return NS_OK;
    2673                 : }
    2674                 : 
    2675                 : 
    2676                 : // nsIOutputStreamCallback
    2677                 : 
    2678                 : NS_IMETHODIMP
    2679               0 : WebSocketChannel::OnOutputStreamReady(nsIAsyncOutputStream *aStream)
    2680                 : {
    2681               0 :   LOG(("WebSocketChannel::OnOutputStreamReady() %p\n", this));
    2682               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
    2683                 :   nsresult rv;
    2684                 : 
    2685               0 :   if (!mCurrentOut)
    2686               0 :     PrimeNewOutgoingMessage();
    2687                 : 
    2688               0 :   while (mCurrentOut && mSocketOut) {
    2689                 :     const char *sndBuf;
    2690                 :     PRUint32 toSend;
    2691                 :     PRUint32 amtSent;
    2692                 : 
    2693               0 :     if (mHdrOut) {
    2694               0 :       sndBuf = (const char *)mHdrOut;
    2695               0 :       toSend = mHdrOutToSend;
    2696               0 :       LOG(("WebSocketChannel::OnOutputStreamReady: "
    2697                 :            "Try to send %u of hdr/copybreak\n", toSend));
    2698                 :     } else {
    2699               0 :       sndBuf = (char *) mCurrentOut->BeginReading() + mCurrentOutSent;
    2700               0 :       toSend = mCurrentOut->Length() - mCurrentOutSent;
    2701               0 :       if (toSend > 0) {
    2702               0 :         LOG(("WebSocketChannel::OnOutputStreamReady: "
    2703                 :              "Try to send %u of data\n", toSend));
    2704                 :       }
    2705                 :     }
    2706                 : 
    2707               0 :     if (toSend == 0) {
    2708               0 :       amtSent = 0;
    2709                 :     } else {
    2710               0 :       rv = mSocketOut->Write(sndBuf, toSend, &amtSent);
    2711               0 :       LOG(("WebSocketChannel::OnOutputStreamReady: write %u rv %x\n",
    2712                 :            amtSent, rv));
    2713                 : 
    2714               0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    2715               0 :         mSocketOut->AsyncWait(this, 0, 0, nsnull);
    2716               0 :         return NS_OK;
    2717                 :       }
    2718                 : 
    2719               0 :       if (NS_FAILED(rv)) {
    2720               0 :         AbortSession(rv);
    2721               0 :         return NS_OK;
    2722                 :       }
    2723                 :     }
    2724                 : 
    2725               0 :     if (mHdrOut) {
    2726               0 :       if (amtSent == toSend) {
    2727               0 :         mHdrOut = nsnull;
    2728               0 :         mHdrOutToSend = 0;
    2729                 :       } else {
    2730               0 :         mHdrOut += amtSent;
    2731               0 :         mHdrOutToSend -= amtSent;
    2732                 :       }
    2733                 :     } else {
    2734               0 :       if (amtSent == toSend) {
    2735               0 :         if (!mStopped) {
    2736                 :           NS_DispatchToMainThread(new CallAcknowledge(this,
    2737               0 :                                                       mCurrentOut->Length()));
    2738                 :         }
    2739               0 :         DeleteCurrentOutGoingMessage();
    2740               0 :         PrimeNewOutgoingMessage();
    2741                 :       } else {
    2742               0 :         mCurrentOutSent += amtSent;
    2743                 :       }
    2744                 :     }
    2745                 :   }
    2746                 : 
    2747               0 :   if (mReleaseOnTransmit)
    2748               0 :     ReleaseSession();
    2749               0 :   return NS_OK;
    2750                 : }
    2751                 : 
    2752                 : // nsIStreamListener
    2753                 : 
    2754                 : NS_IMETHODIMP
    2755               0 : WebSocketChannel::OnDataAvailable(nsIRequest *aRequest,
    2756                 :                                     nsISupports *aContext,
    2757                 :                                     nsIInputStream *aInputStream,
    2758                 :                                     PRUint32 aOffset,
    2759                 :                                     PRUint32 aCount)
    2760                 : {
    2761               0 :   LOG(("WebSocketChannel::OnDataAvailable() %p [%p %p %p %u %u]\n",
    2762                 :          this, aRequest, aContext, aInputStream, aOffset, aCount));
    2763                 : 
    2764               0 :   if (aContext == mSocketIn) {
    2765                 :     // This is the deflate decoder
    2766                 : 
    2767               0 :     LOG(("WebSocketChannel::OnDataAvailable: Deflate Data %u\n",
    2768                 :              aCount));
    2769                 : 
    2770                 :     PRUint8  buffer[2048];
    2771                 :     PRUint32 maxRead;
    2772                 :     PRUint32 count;
    2773                 :     nsresult rv;
    2774                 : 
    2775               0 :     while (aCount > 0) {
    2776               0 :       if (mStopped)
    2777               0 :         return NS_BASE_STREAM_CLOSED;
    2778                 : 
    2779               0 :       maxRead = NS_MIN(2048U, aCount);
    2780               0 :       rv = aInputStream->Read((char *)buffer, maxRead, &count);
    2781               0 :       LOG(("WebSocketChannel::OnDataAvailable: InflateRead read %u rv %x\n",
    2782                 :            count, rv));
    2783               0 :       if (NS_FAILED(rv) || count == 0) {
    2784               0 :         AbortSession(rv);
    2785               0 :         break;
    2786                 :       }
    2787                 : 
    2788               0 :       aCount -= count;
    2789               0 :       rv = ProcessInput(buffer, count);
    2790                 :     }
    2791               0 :     return NS_OK;
    2792                 :   }
    2793                 : 
    2794               0 :   if (aContext == mSocketOut) {
    2795                 :     // This is the deflate encoder
    2796                 : 
    2797                 :     PRUint32 maxRead;
    2798                 :     PRUint32 count;
    2799                 :     nsresult rv;
    2800                 : 
    2801               0 :     while (aCount > 0) {
    2802               0 :       if (mStopped)
    2803               0 :         return NS_BASE_STREAM_CLOSED;
    2804                 : 
    2805               0 :       maxRead = NS_MIN(2048U, aCount);
    2806               0 :       EnsureHdrOut(mHdrOutToSend + aCount);
    2807               0 :       rv = aInputStream->Read((char *)mHdrOut + mHdrOutToSend, maxRead, &count);
    2808               0 :       LOG(("WebSocketChannel::OnDataAvailable: DeflateWrite read %u rv %x\n", 
    2809                 :            count, rv));
    2810               0 :       if (NS_FAILED(rv) || count == 0) {
    2811               0 :         AbortSession(rv);
    2812               0 :         break;
    2813                 :       }
    2814                 : 
    2815               0 :       mHdrOutToSend += count;
    2816               0 :       aCount -= count;
    2817                 :     }
    2818               0 :     return NS_OK;
    2819                 :   }
    2820                 : 
    2821                 : 
    2822                 :   // Otherwise, this is the HTTP OnDataAvailable Method, which means
    2823                 :   // this is http data in response to the upgrade request and
    2824                 :   // there should be no http response body if the upgrade succeeded
    2825                 : 
    2826                 :   // This generally should be caught by a non 101 response code in
    2827                 :   // OnStartRequest().. so we can ignore the data here
    2828                 : 
    2829               0 :   LOG(("WebSocketChannel::OnDataAvailable: HTTP data unexpected len>=%u\n",
    2830                 :          aCount));
    2831                 : 
    2832               0 :   return NS_OK;
    2833                 : }
    2834                 : 
    2835                 : } // namespace mozilla::net
    2836                 : } // namespace mozilla

Generated by: LCOV version 1.7