LCOV - code coverage report
Current view: top level - netwerk/protocol/websocket - WebSocketChannel.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 1958 0.1 %
Date: 2017-07-14 16:53:18 Functions: 0 145 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "WebSocketFrame.h"
       8             : #include "WebSocketLog.h"
       9             : #include "WebSocketChannel.h"
      10             : 
      11             : #include "mozilla/Atomics.h"
      12             : #include "mozilla/Attributes.h"
      13             : #include "mozilla/EndianUtils.h"
      14             : #include "mozilla/MathAlgorithms.h"
      15             : #include "mozilla/net/WebSocketEventService.h"
      16             : 
      17             : #include "nsIURI.h"
      18             : #include "nsIChannel.h"
      19             : #include "nsICryptoHash.h"
      20             : #include "nsIRunnable.h"
      21             : #include "nsIPrefBranch.h"
      22             : #include "nsIPrefService.h"
      23             : #include "nsICancelable.h"
      24             : #include "nsIClassOfService.h"
      25             : #include "nsIDNSRecord.h"
      26             : #include "nsIDNSService.h"
      27             : #include "nsIStreamConverterService.h"
      28             : #include "nsIIOService2.h"
      29             : #include "nsIProtocolProxyService.h"
      30             : #include "nsIProxyInfo.h"
      31             : #include "nsIProxiedChannel.h"
      32             : #include "nsIAsyncVerifyRedirectCallback.h"
      33             : #include "nsIDashboardEventNotifier.h"
      34             : #include "nsIEventTarget.h"
      35             : #include "nsIHttpChannel.h"
      36             : #include "nsILoadGroup.h"
      37             : #include "nsIProtocolHandler.h"
      38             : #include "nsIRandomGenerator.h"
      39             : #include "nsISocketTransport.h"
      40             : #include "nsThreadUtils.h"
      41             : #include "nsINetworkLinkService.h"
      42             : #include "nsIObserverService.h"
      43             : #include "nsITransportProvider.h"
      44             : #include "nsCharSeparatedTokenizer.h"
      45             : 
      46             : #include "nsAutoPtr.h"
      47             : #include "nsNetCID.h"
      48             : #include "nsServiceManagerUtils.h"
      49             : #include "nsCRT.h"
      50             : #include "nsThreadUtils.h"
      51             : #include "nsError.h"
      52             : #include "nsStringStream.h"
      53             : #include "nsAlgorithm.h"
      54             : #include "nsProxyRelease.h"
      55             : #include "nsNetUtil.h"
      56             : #include "nsINode.h"
      57             : #include "mozilla/StaticMutex.h"
      58             : #include "mozilla/Telemetry.h"
      59             : #include "mozilla/TimeStamp.h"
      60             : #include "nsSocketTransportService2.h"
      61             : 
      62             : #include "plbase64.h"
      63             : #include "prmem.h"
      64             : #include "prnetdb.h"
      65             : #include "zlib.h"
      66             : #include <algorithm>
      67             : 
      68             : // rather than slurp up all of nsIWebSocket.idl, which lives outside necko, just
      69             : // dupe one constant we need from it
      70             : #define CLOSE_GOING_AWAY 1001
      71             : 
      72             : using namespace mozilla;
      73             : using namespace mozilla::net;
      74             : 
      75             : namespace mozilla {
      76             : namespace net {
      77             : 
      78           0 : NS_IMPL_ISUPPORTS(WebSocketChannel,
      79             :                   nsIWebSocketChannel,
      80             :                   nsIHttpUpgradeListener,
      81             :                   nsIRequestObserver,
      82             :                   nsIStreamListener,
      83             :                   nsIProtocolHandler,
      84             :                   nsIInputStreamCallback,
      85             :                   nsIOutputStreamCallback,
      86             :                   nsITimerCallback,
      87             :                   nsIDNSListener,
      88             :                   nsIProtocolProxyCallback,
      89             :                   nsIInterfaceRequestor,
      90             :                   nsIChannelEventSink,
      91             :                   nsIThreadRetargetableRequest,
      92             :                   nsIObserver)
      93             : 
      94             : // We implement RFC 6455, which uses Sec-WebSocket-Version: 13 on the wire.
      95             : #define SEC_WEBSOCKET_VERSION "13"
      96             : 
      97             : /*
      98             :  * About SSL unsigned certificates
      99             :  *
     100             :  * wss will not work to a host using an unsigned certificate unless there
     101             :  * is already an exception (i.e. it cannot popup a dialog asking for
     102             :  * a security exception). This is similar to how an inlined img will
     103             :  * fail without a dialog if fails for the same reason. This should not
     104             :  * be a problem in practice as it is expected the websocket javascript
     105             :  * is served from the same host as the websocket server (or of course,
     106             :  * a valid cert could just be provided).
     107             :  *
     108             :  */
     109             : 
     110             : // some helper classes
     111             : 
     112             : //-----------------------------------------------------------------------------
     113             : // FailDelayManager
     114             : //
     115             : // Stores entries (searchable by {host, port}) of connections that have recently
     116             : // failed, so we can do delay of reconnects per RFC 6455 Section 7.2.3
     117             : //-----------------------------------------------------------------------------
     118             : 
     119             : 
     120             : // Initial reconnect delay is randomly chosen between 200-400 ms.
     121             : // This is a gentler backoff than the 0-5 seconds the spec offhandedly suggests.
     122             : const uint32_t kWSReconnectInitialBaseDelay     = 200;
     123             : const uint32_t kWSReconnectInitialRandomDelay   = 200;
     124             : 
     125             : // Base lifetime (in ms) of a FailDelay: kept longer if more failures occur
     126             : const uint32_t kWSReconnectBaseLifeTime         = 60 * 1000;
     127             : // Maximum reconnect delay (in ms)
     128             : const uint32_t kWSReconnectMaxDelay             = 60 * 1000;
     129             : 
     130             : // hold record of failed connections, and calculates needed delay for reconnects
     131             : // to same host/port.
     132           0 : class FailDelay
     133             : {
     134             : public:
     135           0 :   FailDelay(nsCString address, int32_t port)
     136           0 :     : mAddress(address), mPort(port)
     137             :   {
     138           0 :     mLastFailure = TimeStamp::Now();
     139           0 :     mNextDelay = kWSReconnectInitialBaseDelay +
     140           0 :                  (rand() % kWSReconnectInitialRandomDelay);
     141           0 :   }
     142             : 
     143             :   // Called to update settings when connection fails again.
     144           0 :   void FailedAgain()
     145             :   {
     146           0 :     mLastFailure = TimeStamp::Now();
     147             :     // We use a truncated exponential backoff as suggested by RFC 6455,
     148             :     // but multiply by 1.5 instead of 2 to be more gradual.
     149           0 :     mNextDelay = static_cast<uint32_t>(
     150           0 :       std::min<double>(kWSReconnectMaxDelay, mNextDelay * 1.5));
     151           0 :     LOG(("WebSocket: FailedAgain: host=%s, port=%d: incremented delay to %" PRIu32,
     152             :          mAddress.get(), mPort, mNextDelay));
     153           0 :   }
     154             : 
     155             :   // returns 0 if there is no need to delay (i.e. delay interval is over)
     156           0 :   uint32_t RemainingDelay(TimeStamp rightNow)
     157             :   {
     158           0 :     TimeDuration dur = rightNow - mLastFailure;
     159           0 :     uint32_t sinceFail = (uint32_t) dur.ToMilliseconds();
     160           0 :     if (sinceFail > mNextDelay)
     161           0 :       return 0;
     162             : 
     163           0 :     return mNextDelay - sinceFail;
     164             :   }
     165             : 
     166           0 :   bool IsExpired(TimeStamp rightNow)
     167             :   {
     168           0 :     return (mLastFailure +
     169           0 :             TimeDuration::FromMilliseconds(kWSReconnectBaseLifeTime + mNextDelay))
     170           0 :             <= rightNow;
     171             :   }
     172             : 
     173             :   nsCString  mAddress;     // IP address (or hostname if using proxy)
     174             :   int32_t    mPort;
     175             : 
     176             : private:
     177             :   TimeStamp  mLastFailure; // Time of last failed attempt
     178             :   // mLastFailure + mNextDelay is the soonest we'll allow a reconnect
     179             :   uint32_t   mNextDelay;   // milliseconds
     180             : };
     181             : 
     182             : class FailDelayManager
     183             : {
     184             : public:
     185           0 :   FailDelayManager()
     186           0 :   {
     187           0 :     MOZ_COUNT_CTOR(FailDelayManager);
     188             : 
     189           0 :     mDelaysDisabled = false;
     190             : 
     191             :     nsCOMPtr<nsIPrefBranch> prefService =
     192           0 :       do_GetService(NS_PREFSERVICE_CONTRACTID);
     193           0 :     if (!prefService) {
     194           0 :       return;
     195             :     }
     196           0 :     bool boolpref = true;
     197             :     nsresult rv;
     198           0 :     rv = prefService->GetBoolPref("network.websocket.delay-failed-reconnects",
     199           0 :                                   &boolpref);
     200           0 :     if (NS_SUCCEEDED(rv) && !boolpref) {
     201           0 :       mDelaysDisabled = true;
     202             :     }
     203             :   }
     204             : 
     205           0 :   ~FailDelayManager()
     206           0 :   {
     207           0 :     MOZ_COUNT_DTOR(FailDelayManager);
     208           0 :     for (uint32_t i = 0; i < mEntries.Length(); i++) {
     209           0 :       delete mEntries[i];
     210             :     }
     211           0 :   }
     212             : 
     213           0 :   void Add(nsCString &address, int32_t port)
     214             :   {
     215           0 :     if (mDelaysDisabled)
     216           0 :       return;
     217             : 
     218           0 :     FailDelay *record = new FailDelay(address, port);
     219           0 :     mEntries.AppendElement(record);
     220             :   }
     221             : 
     222             :   // Element returned may not be valid after next main thread event: don't keep
     223             :   // pointer to it around
     224           0 :   FailDelay* Lookup(nsCString &address, int32_t port,
     225             :                     uint32_t *outIndex = nullptr)
     226             :   {
     227           0 :     if (mDelaysDisabled)
     228           0 :       return nullptr;
     229             : 
     230           0 :     FailDelay *result = nullptr;
     231           0 :     TimeStamp rightNow = TimeStamp::Now();
     232             : 
     233             :     // We also remove expired entries during search: iterate from end to make
     234             :     // indexing simpler
     235           0 :     for (int32_t i = mEntries.Length() - 1; i >= 0; --i) {
     236           0 :       FailDelay *fail = mEntries[i];
     237           0 :       if (fail->mAddress.Equals(address) && fail->mPort == port) {
     238           0 :         if (outIndex)
     239           0 :           *outIndex = i;
     240           0 :         result = fail;
     241             :         // break here: removing more entries would mess up *outIndex.
     242             :         // Any remaining expired entries will be deleted next time Lookup
     243             :         // finds nothing, which is the most common case anyway.
     244           0 :         break;
     245           0 :       } else if (fail->IsExpired(rightNow)) {
     246           0 :         mEntries.RemoveElementAt(i);
     247           0 :         delete fail;
     248             :       }
     249             :     }
     250           0 :     return result;
     251             :   }
     252             : 
     253             :   // returns true if channel connects immediately, or false if it's delayed
     254           0 :   void DelayOrBegin(WebSocketChannel *ws)
     255             :   {
     256           0 :     if (!mDelaysDisabled) {
     257           0 :       uint32_t failIndex = 0;
     258           0 :       FailDelay *fail = Lookup(ws->mAddress, ws->mPort, &failIndex);
     259             : 
     260           0 :       if (fail) {
     261           0 :         TimeStamp rightNow = TimeStamp::Now();
     262             : 
     263           0 :         uint32_t remainingDelay = fail->RemainingDelay(rightNow);
     264           0 :         if (remainingDelay) {
     265             :           // reconnecting within delay interval: delay by remaining time
     266             :           nsresult rv;
     267             :           ws->mReconnectDelayTimer =
     268           0 :             do_CreateInstance("@mozilla.org/timer;1", &rv);
     269           0 :           if (NS_SUCCEEDED(rv)) {
     270           0 :             rv = ws->mReconnectDelayTimer->InitWithCallback(
     271           0 :                           ws, remainingDelay, nsITimer::TYPE_ONE_SHOT);
     272           0 :             if (NS_SUCCEEDED(rv)) {
     273           0 :               LOG(("WebSocket: delaying websocket [this=%p] by %lu ms, changing"
     274             :                    " state to CONNECTING_DELAYED", ws,
     275             :                    (unsigned long)remainingDelay));
     276           0 :               ws->mConnecting = CONNECTING_DELAYED;
     277           0 :               return;
     278             :             }
     279             :           }
     280             :           // if timer fails (which is very unlikely), drop down to BeginOpen call
     281           0 :         } else if (fail->IsExpired(rightNow)) {
     282           0 :           mEntries.RemoveElementAt(failIndex);
     283           0 :           delete fail;
     284             :         }
     285             :       }
     286             :     }
     287             : 
     288             :     // Delays disabled, or no previous failure, or we're reconnecting after scheduled
     289             :     // delay interval has passed: connect.
     290           0 :     ws->BeginOpen(true);
     291             :   }
     292             : 
     293             :   // Remove() also deletes all expired entries as it iterates: better for
     294             :   // battery life than using a periodic timer.
     295           0 :   void Remove(nsCString &address, int32_t port)
     296             :   {
     297           0 :     TimeStamp rightNow = TimeStamp::Now();
     298             : 
     299             :     // iterate from end, to make deletion indexing easier
     300           0 :     for (int32_t i = mEntries.Length() - 1; i >= 0; --i) {
     301           0 :       FailDelay *entry = mEntries[i];
     302           0 :       if ((entry->mAddress.Equals(address) && entry->mPort == port) ||
     303           0 :           entry->IsExpired(rightNow)) {
     304           0 :         mEntries.RemoveElementAt(i);
     305           0 :         delete entry;
     306             :       }
     307             :     }
     308           0 :   }
     309             : 
     310             : private:
     311             :   nsTArray<FailDelay *> mEntries;
     312             :   bool                  mDelaysDisabled;
     313             : };
     314             : 
     315             : //-----------------------------------------------------------------------------
     316             : // nsWSAdmissionManager
     317             : //
     318             : // 1) Ensures that only one websocket at a time is CONNECTING to a given IP
     319             : //    address (or hostname, if using proxy), per RFC 6455 Section 4.1.
     320             : // 2) Delays reconnects to IP/host after connection failure, per Section 7.2.3
     321             : //-----------------------------------------------------------------------------
     322             : 
     323             : class nsWSAdmissionManager
     324             : {
     325             : public:
     326           0 :   static void Init()
     327             :   {
     328           0 :     StaticMutexAutoLock lock(sLock);
     329           0 :     if (!sManager) {
     330           0 :       sManager = new nsWSAdmissionManager();
     331             :     }
     332           0 :   }
     333             : 
     334           0 :   static void Shutdown()
     335             :   {
     336           0 :     StaticMutexAutoLock lock(sLock);
     337           0 :     delete sManager;
     338           0 :     sManager = nullptr;
     339           0 :   }
     340             : 
     341             :   // Determine if we will open connection immediately (returns true), or
     342             :   // delay/queue the connection (returns false)
     343           0 :   static void ConditionallyConnect(WebSocketChannel *ws)
     344             :   {
     345           0 :     LOG(("Websocket: ConditionallyConnect: [this=%p]", ws));
     346           0 :     MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     347           0 :     MOZ_ASSERT(ws->mConnecting == NOT_CONNECTING, "opening state");
     348             : 
     349           0 :     StaticMutexAutoLock lock(sLock);
     350           0 :     if (!sManager) {
     351           0 :       return;
     352             :     }
     353             : 
     354             :     // If there is already another WS channel connecting to this IP address,
     355             :     // defer BeginOpen and mark as waiting in queue.
     356           0 :     bool found = (sManager->IndexOf(ws->mAddress) >= 0);
     357             : 
     358             :     // Always add ourselves to queue, even if we'll connect immediately
     359           0 :     nsOpenConn *newdata = new nsOpenConn(ws->mAddress, ws);
     360           0 :     LOG(("Websocket: adding conn %p to the queue", newdata));
     361           0 :     sManager->mQueue.AppendElement(newdata);
     362             : 
     363           0 :     if (found) {
     364           0 :       LOG(("Websocket: some other channel is connecting, changing state to "
     365             :            "CONNECTING_QUEUED"));
     366           0 :       ws->mConnecting = CONNECTING_QUEUED;
     367             :     } else {
     368           0 :       sManager->mFailures.DelayOrBegin(ws);
     369             :     }
     370             :   }
     371             : 
     372           0 :   static void OnConnected(WebSocketChannel *aChannel)
     373             :   {
     374           0 :     LOG(("Websocket: OnConnected: [this=%p]", aChannel));
     375             : 
     376           0 :     MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     377           0 :     MOZ_ASSERT(aChannel->mConnecting == CONNECTING_IN_PROGRESS,
     378             :                "Channel completed connect, but not connecting?");
     379             : 
     380           0 :     StaticMutexAutoLock lock(sLock);
     381           0 :     if (!sManager) {
     382           0 :       return;
     383             :     }
     384             : 
     385           0 :     LOG(("Websocket: changing state to NOT_CONNECTING"));
     386           0 :     aChannel->mConnecting = NOT_CONNECTING;
     387             : 
     388             :     // Remove from queue
     389           0 :     sManager->RemoveFromQueue(aChannel);
     390             : 
     391             :     // Connection succeeded, so stop keeping track of any previous failures
     392           0 :     sManager->mFailures.Remove(aChannel->mAddress, aChannel->mPort);
     393             : 
     394             :     // Check for queued connections to same host.
     395             :     // Note: still need to check for failures, since next websocket with same
     396             :     // host may have different port
     397           0 :     sManager->ConnectNext(aChannel->mAddress);
     398             :   }
     399             : 
     400             :   // Called every time a websocket channel ends its session (including going away
     401             :   // w/o ever successfully creating a connection)
     402           0 :   static void OnStopSession(WebSocketChannel *aChannel, nsresult aReason)
     403             :   {
     404           0 :     LOG(("Websocket: OnStopSession: [this=%p, reason=0x%08" PRIx32 "]", aChannel,
     405             :          static_cast<uint32_t>(aReason)));
     406             : 
     407           0 :     StaticMutexAutoLock lock(sLock);
     408           0 :     if (!sManager) {
     409           0 :       return;
     410             :     }
     411             : 
     412           0 :     if (NS_FAILED(aReason)) {
     413             :       // Have we seen this failure before?
     414           0 :       FailDelay *knownFailure = sManager->mFailures.Lookup(aChannel->mAddress,
     415           0 :                                                            aChannel->mPort);
     416           0 :       if (knownFailure) {
     417           0 :         if (aReason == NS_ERROR_NOT_CONNECTED) {
     418             :           // Don't count close() before connection as a network error
     419           0 :           LOG(("Websocket close() before connection to %s, %d completed"
     420             :                " [this=%p]", aChannel->mAddress.get(), (int)aChannel->mPort,
     421             :                aChannel));
     422             :         } else {
     423             :           // repeated failure to connect: increase delay for next connection
     424           0 :           knownFailure->FailedAgain();
     425             :         }
     426             :       } else {
     427             :         // new connection failure: record it.
     428           0 :         LOG(("WebSocket: connection to %s, %d failed: [this=%p]",
     429             :               aChannel->mAddress.get(), (int)aChannel->mPort, aChannel));
     430           0 :         sManager->mFailures.Add(aChannel->mAddress, aChannel->mPort);
     431             :       }
     432             :     }
     433             : 
     434           0 :     if (aChannel->mConnecting) {
     435           0 :       MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     436             : 
     437             :       // Only way a connecting channel may get here w/o failing is if it was
     438             :       // closed with GOING_AWAY (1001) because of navigation, tab close, etc.
     439           0 :       MOZ_ASSERT(NS_FAILED(aReason) ||
     440             :                  aChannel->mScriptCloseCode == CLOSE_GOING_AWAY,
     441             :                  "websocket closed while connecting w/o failing?");
     442             : 
     443           0 :       sManager->RemoveFromQueue(aChannel);
     444             : 
     445           0 :       bool wasNotQueued = (aChannel->mConnecting != CONNECTING_QUEUED);
     446           0 :       LOG(("Websocket: changing state to NOT_CONNECTING"));
     447           0 :       aChannel->mConnecting = NOT_CONNECTING;
     448           0 :       if (wasNotQueued) {
     449           0 :         sManager->ConnectNext(aChannel->mAddress);
     450             :       }
     451             :     }
     452             :   }
     453             : 
     454           0 :   static void IncrementSessionCount()
     455             :   {
     456           0 :     StaticMutexAutoLock lock(sLock);
     457           0 :     if (!sManager) {
     458           0 :       return;
     459             :     }
     460           0 :     sManager->mSessionCount++;
     461             :   }
     462             : 
     463           0 :   static void DecrementSessionCount()
     464             :   {
     465           0 :     StaticMutexAutoLock lock(sLock);
     466           0 :     if (!sManager) {
     467           0 :       return;
     468             :     }
     469           0 :     sManager->mSessionCount--;
     470             :   }
     471             : 
     472           0 :   static void GetSessionCount(int32_t &aSessionCount)
     473             :   {
     474           0 :     StaticMutexAutoLock lock(sLock);
     475           0 :     if (!sManager) {
     476           0 :       return;
     477             :     }
     478           0 :     aSessionCount = sManager->mSessionCount;
     479             :   }
     480             : 
     481             : private:
     482           0 :   nsWSAdmissionManager() : mSessionCount(0)
     483             :   {
     484           0 :     MOZ_COUNT_CTOR(nsWSAdmissionManager);
     485           0 :   }
     486             : 
     487           0 :   ~nsWSAdmissionManager()
     488           0 :   {
     489           0 :     MOZ_COUNT_DTOR(nsWSAdmissionManager);
     490           0 :     for (uint32_t i = 0; i < mQueue.Length(); i++)
     491           0 :       delete mQueue[i];
     492           0 :   }
     493             : 
     494             :   class nsOpenConn
     495             :   {
     496             :   public:
     497           0 :     nsOpenConn(nsCString &addr, WebSocketChannel *channel)
     498           0 :       : mAddress(addr), mChannel(channel) { MOZ_COUNT_CTOR(nsOpenConn); }
     499           0 :     ~nsOpenConn() { MOZ_COUNT_DTOR(nsOpenConn); }
     500             : 
     501             :     nsCString mAddress;
     502             :     WebSocketChannel *mChannel;
     503             :   };
     504             : 
     505           0 :   void ConnectNext(nsCString &hostName)
     506             :   {
     507           0 :     MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     508             : 
     509           0 :     int32_t index = IndexOf(hostName);
     510           0 :     if (index >= 0) {
     511           0 :       WebSocketChannel *chan = mQueue[index]->mChannel;
     512             : 
     513           0 :       MOZ_ASSERT(chan->mConnecting == CONNECTING_QUEUED,
     514             :                  "transaction not queued but in queue");
     515           0 :       LOG(("WebSocket: ConnectNext: found channel [this=%p] in queue", chan));
     516             : 
     517           0 :       mFailures.DelayOrBegin(chan);
     518             :     }
     519           0 :   }
     520             : 
     521           0 :   void RemoveFromQueue(WebSocketChannel *aChannel)
     522             :   {
     523           0 :     LOG(("Websocket: RemoveFromQueue: [this=%p]", aChannel));
     524           0 :     int32_t index = IndexOf(aChannel);
     525           0 :     MOZ_ASSERT(index >= 0, "connection to remove not in queue");
     526           0 :     if (index >= 0) {
     527           0 :       nsOpenConn *olddata = mQueue[index];
     528           0 :       mQueue.RemoveElementAt(index);
     529           0 :       LOG(("Websocket: removing conn %p from the queue", olddata));
     530           0 :       delete olddata;
     531             :     }
     532           0 :   }
     533             : 
     534           0 :   int32_t IndexOf(nsCString &aStr)
     535             :   {
     536           0 :     for (uint32_t i = 0; i < mQueue.Length(); i++)
     537           0 :       if (aStr == (mQueue[i])->mAddress)
     538           0 :         return i;
     539           0 :     return -1;
     540             :   }
     541             : 
     542           0 :   int32_t IndexOf(WebSocketChannel *aChannel)
     543             :   {
     544           0 :     for (uint32_t i = 0; i < mQueue.Length(); i++)
     545           0 :       if (aChannel == (mQueue[i])->mChannel)
     546           0 :         return i;
     547           0 :     return -1;
     548             :   }
     549             : 
     550             :   // SessionCount might be decremented from the main or the socket
     551             :   // thread, so manage it with atomic counters
     552             :   Atomic<int32_t>               mSessionCount;
     553             : 
     554             :   // Queue for websockets that have not completed connecting yet.
     555             :   // The first nsOpenConn with a given address will be either be
     556             :   // CONNECTING_IN_PROGRESS or CONNECTING_DELAYED.  Later ones with the same
     557             :   // hostname must be CONNECTING_QUEUED.
     558             :   //
     559             :   // We could hash hostnames instead of using a single big vector here, but the
     560             :   // dataset is expected to be small.
     561             :   nsTArray<nsOpenConn *> mQueue;
     562             : 
     563             :   FailDelayManager       mFailures;
     564             : 
     565             :   static nsWSAdmissionManager *sManager;
     566             :   static StaticMutex           sLock;
     567             : };
     568             : 
     569             : nsWSAdmissionManager *nsWSAdmissionManager::sManager;
     570           3 : StaticMutex           nsWSAdmissionManager::sLock;
     571             : 
     572             : //-----------------------------------------------------------------------------
     573             : // CallOnMessageAvailable
     574             : //-----------------------------------------------------------------------------
     575             : 
     576             : class CallOnMessageAvailable final : public nsIRunnable
     577             : {
     578             : public:
     579             :   NS_DECL_THREADSAFE_ISUPPORTS
     580             : 
     581           0 :   CallOnMessageAvailable(WebSocketChannel* aChannel,
     582             :                          nsACString& aData,
     583             :                          int32_t aLen)
     584           0 :     : mChannel(aChannel),
     585             :       mListenerMT(aChannel->mListenerMT),
     586             :       mData(aData),
     587           0 :       mLen(aLen) {}
     588             : 
     589           0 :   NS_IMETHOD Run() override
     590             :   {
     591           0 :     MOZ_ASSERT(mChannel->IsOnTargetThread());
     592             : 
     593           0 :     if (mListenerMT) {
     594             :       nsresult rv;
     595           0 :       if (mLen < 0) {
     596           0 :         rv = mListenerMT->mListener->OnMessageAvailable(mListenerMT->mContext,
     597           0 :                                                         mData);
     598             :       } else {
     599           0 :         rv = mListenerMT->mListener->OnBinaryMessageAvailable(mListenerMT->mContext,
     600           0 :                                                               mData);
     601             :       }
     602           0 :       if (NS_FAILED(rv)) {
     603           0 :         LOG(("OnMessageAvailable or OnBinaryMessageAvailable "
     604             :              "failed with 0x%08" PRIx32, static_cast<uint32_t>(rv)));
     605             :       }
     606             :     }
     607             : 
     608           0 :     return NS_OK;
     609             :   }
     610             : 
     611             : private:
     612           0 :   ~CallOnMessageAvailable() {}
     613             : 
     614             :   RefPtr<WebSocketChannel> mChannel;
     615             :   RefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
     616             :   nsCString mData;
     617             :   int32_t mLen;
     618             : };
     619           0 : NS_IMPL_ISUPPORTS(CallOnMessageAvailable, nsIRunnable)
     620             : 
     621             : //-----------------------------------------------------------------------------
     622             : // CallOnStop
     623             : //-----------------------------------------------------------------------------
     624             : 
     625             : class CallOnStop final : public nsIRunnable
     626             : {
     627             : public:
     628             :   NS_DECL_THREADSAFE_ISUPPORTS
     629             : 
     630           0 :   CallOnStop(WebSocketChannel* aChannel,
     631             :              nsresult aReason)
     632           0 :     : mChannel(aChannel),
     633           0 :       mListenerMT(mChannel->mListenerMT),
     634           0 :       mReason(aReason)
     635           0 :   {}
     636             : 
     637           0 :   NS_IMETHOD Run() override
     638             :   {
     639           0 :     MOZ_ASSERT(mChannel->IsOnTargetThread());
     640             : 
     641           0 :     if (mListenerMT) {
     642           0 :       nsresult rv = mListenerMT->mListener->OnStop(mListenerMT->mContext, mReason);
     643           0 :       if (NS_FAILED(rv)) {
     644           0 :         LOG(("WebSocketChannel::CallOnStop "
     645             :              "OnStop failed (%08" PRIx32 ")\n", static_cast<uint32_t>(rv)));
     646             :       }
     647           0 :       mChannel->mListenerMT = nullptr;
     648             :     }
     649             : 
     650           0 :     return NS_OK;
     651             :   }
     652             : 
     653             : private:
     654           0 :   ~CallOnStop() {}
     655             : 
     656             :   RefPtr<WebSocketChannel> mChannel;
     657             :   RefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
     658             :   nsresult mReason;
     659             : };
     660           0 : NS_IMPL_ISUPPORTS(CallOnStop, nsIRunnable)
     661             : 
     662             : //-----------------------------------------------------------------------------
     663             : // CallOnServerClose
     664             : //-----------------------------------------------------------------------------
     665             : 
     666             : class CallOnServerClose final : public nsIRunnable
     667             : {
     668             : public:
     669             :   NS_DECL_THREADSAFE_ISUPPORTS
     670             : 
     671           0 :   CallOnServerClose(WebSocketChannel* aChannel,
     672             :                     uint16_t aCode,
     673             :                     nsACString& aReason)
     674           0 :     : mChannel(aChannel),
     675           0 :       mListenerMT(mChannel->mListenerMT),
     676             :       mCode(aCode),
     677           0 :       mReason(aReason) {}
     678             : 
     679           0 :   NS_IMETHOD Run() override
     680             :   {
     681           0 :     MOZ_ASSERT(mChannel->IsOnTargetThread());
     682             : 
     683           0 :     if (mListenerMT) {
     684             :       nsresult rv =
     685           0 :         mListenerMT->mListener->OnServerClose(mListenerMT->mContext, mCode,
     686           0 :                                               mReason);
     687           0 :       if (NS_FAILED(rv)) {
     688           0 :         LOG(("WebSocketChannel::CallOnServerClose "
     689             :              "OnServerClose failed (%08" PRIx32 ")\n", static_cast<uint32_t>(rv)));
     690             :       }
     691             :     }
     692           0 :     return NS_OK;
     693             :   }
     694             : 
     695             : private:
     696           0 :   ~CallOnServerClose() {}
     697             : 
     698             :   RefPtr<WebSocketChannel> mChannel;
     699             :   RefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
     700             :   uint16_t mCode;
     701             :   nsCString mReason;
     702             : };
     703           0 : NS_IMPL_ISUPPORTS(CallOnServerClose, nsIRunnable)
     704             : 
     705             : //-----------------------------------------------------------------------------
     706             : // CallAcknowledge
     707             : //-----------------------------------------------------------------------------
     708             : 
     709             : class CallAcknowledge final : public CancelableRunnable
     710             : {
     711             : public:
     712           0 :   CallAcknowledge(WebSocketChannel* aChannel, uint32_t aSize)
     713           0 :     : CancelableRunnable("net::CallAcknowledge")
     714             :     , mChannel(aChannel)
     715           0 :     , mListenerMT(mChannel->mListenerMT)
     716           0 :     , mSize(aSize)
     717             :   {
     718           0 :   }
     719             : 
     720           0 :   NS_IMETHOD Run() override
     721             :   {
     722           0 :     MOZ_ASSERT(mChannel->IsOnTargetThread());
     723             : 
     724           0 :     LOG(("WebSocketChannel::CallAcknowledge: Size %u\n", mSize));
     725           0 :     if (mListenerMT) {
     726           0 :       nsresult rv = mListenerMT->mListener->OnAcknowledge(mListenerMT->mContext, mSize);
     727           0 :       if (NS_FAILED(rv)) {
     728           0 :         LOG(("WebSocketChannel::CallAcknowledge: Acknowledge failed (%08" PRIx32 ")\n",
     729             :              static_cast<uint32_t>(rv)));
     730             :       }
     731             :     }
     732           0 :     return NS_OK;
     733             :   }
     734             : 
     735             : private:
     736           0 :   ~CallAcknowledge() {}
     737             : 
     738             :   RefPtr<WebSocketChannel> mChannel;
     739             :   RefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
     740             :   uint32_t mSize;
     741             : };
     742             : 
     743             : //-----------------------------------------------------------------------------
     744             : // CallOnTransportAvailable
     745             : //-----------------------------------------------------------------------------
     746             : 
     747             : class CallOnTransportAvailable final : public nsIRunnable
     748             : {
     749             : public:
     750             :   NS_DECL_THREADSAFE_ISUPPORTS
     751             : 
     752           0 :   CallOnTransportAvailable(WebSocketChannel *aChannel,
     753             :                            nsISocketTransport *aTransport,
     754             :                            nsIAsyncInputStream *aSocketIn,
     755             :                            nsIAsyncOutputStream *aSocketOut)
     756           0 :     : mChannel(aChannel),
     757             :       mTransport(aTransport),
     758             :       mSocketIn(aSocketIn),
     759           0 :       mSocketOut(aSocketOut) {}
     760             : 
     761           0 :   NS_IMETHOD Run() override
     762             :   {
     763           0 :     LOG(("WebSocketChannel::CallOnTransportAvailable %p\n", this));
     764           0 :     return mChannel->OnTransportAvailable(mTransport, mSocketIn, mSocketOut);
     765             :   }
     766             : 
     767             : private:
     768           0 :   ~CallOnTransportAvailable() {}
     769             : 
     770             :   RefPtr<WebSocketChannel>     mChannel;
     771             :   nsCOMPtr<nsISocketTransport>   mTransport;
     772             :   nsCOMPtr<nsIAsyncInputStream>  mSocketIn;
     773             :   nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
     774             : };
     775           0 : NS_IMPL_ISUPPORTS(CallOnTransportAvailable, nsIRunnable)
     776             : 
     777             : //-----------------------------------------------------------------------------
     778             : // PMCECompression
     779             : //-----------------------------------------------------------------------------
     780             : 
     781             : class PMCECompression
     782             : {
     783             : public:
     784           0 :   PMCECompression(bool aNoContextTakeover,
     785             :                   int32_t aLocalMaxWindowBits,
     786             :                   int32_t aRemoteMaxWindowBits)
     787           0 :     : mActive(false)
     788             :     , mNoContextTakeover(aNoContextTakeover)
     789             :     , mResetDeflater(false)
     790           0 :     , mMessageDeflated(false)
     791             :   {
     792           0 :     MOZ_COUNT_CTOR(PMCECompression);
     793             : 
     794           0 :     mDeflater.zalloc = mInflater.zalloc = Z_NULL;
     795           0 :     mDeflater.zfree  = mInflater.zfree  = Z_NULL;
     796           0 :     mDeflater.opaque = mInflater.opaque = Z_NULL;
     797             : 
     798           0 :     if (deflateInit2(&mDeflater, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
     799             :                      -aLocalMaxWindowBits, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
     800           0 :       if (inflateInit2(&mInflater, -aRemoteMaxWindowBits) == Z_OK) {
     801           0 :         mActive = true;
     802             :       } else {
     803           0 :         deflateEnd(&mDeflater);
     804             :       }
     805             :     }
     806           0 :   }
     807             : 
     808           0 :   ~PMCECompression()
     809           0 :   {
     810           0 :     MOZ_COUNT_DTOR(PMCECompression);
     811             : 
     812           0 :     if (mActive) {
     813           0 :       inflateEnd(&mInflater);
     814           0 :       deflateEnd(&mDeflater);
     815             :     }
     816           0 :   }
     817             : 
     818           0 :   bool Active()
     819             :   {
     820           0 :     return mActive;
     821             :   }
     822             : 
     823           0 :   void SetMessageDeflated()
     824             :   {
     825           0 :     MOZ_ASSERT(!mMessageDeflated);
     826           0 :     mMessageDeflated = true;
     827           0 :   }
     828           0 :   bool IsMessageDeflated()
     829             :   {
     830           0 :     return mMessageDeflated;
     831             :   }
     832             : 
     833           0 :   bool UsingContextTakeover()
     834             :   {
     835           0 :     return !mNoContextTakeover;
     836             :   }
     837             : 
     838           0 :   nsresult Deflate(uint8_t *data, uint32_t dataLen, nsACString &_retval)
     839             :   {
     840           0 :     if (mResetDeflater || mNoContextTakeover) {
     841           0 :       if (deflateReset(&mDeflater) != Z_OK) {
     842           0 :         return NS_ERROR_UNEXPECTED;
     843             :       }
     844           0 :       mResetDeflater = false;
     845             :     }
     846             : 
     847           0 :     mDeflater.avail_out = kBufferLen;
     848           0 :     mDeflater.next_out = mBuffer;
     849           0 :     mDeflater.avail_in = dataLen;
     850           0 :     mDeflater.next_in = data;
     851             : 
     852             :     while (true) {
     853           0 :       int zerr = deflate(&mDeflater, Z_SYNC_FLUSH);
     854             : 
     855           0 :       if (zerr != Z_OK) {
     856           0 :         mResetDeflater = true;
     857           0 :         return NS_ERROR_UNEXPECTED;
     858             :       }
     859             : 
     860           0 :       uint32_t deflated = kBufferLen - mDeflater.avail_out;
     861           0 :       if (deflated > 0) {
     862           0 :         _retval.Append(reinterpret_cast<char *>(mBuffer), deflated);
     863             :       }
     864             : 
     865           0 :       mDeflater.avail_out = kBufferLen;
     866           0 :       mDeflater.next_out = mBuffer;
     867             : 
     868           0 :       if (mDeflater.avail_in > 0) {
     869           0 :         continue; // There is still some data to deflate
     870             :       }
     871             : 
     872           0 :       if (deflated == kBufferLen) {
     873           0 :         continue; // There was not enough space in the buffer
     874             :       }
     875             : 
     876           0 :       break;
     877           0 :     }
     878             : 
     879           0 :     if (_retval.Length() < 4) {
     880           0 :       MOZ_ASSERT(false, "Expected trailing not found in deflated data!");
     881             :       mResetDeflater = true;
     882             :       return NS_ERROR_UNEXPECTED;
     883             :     }
     884             : 
     885           0 :     _retval.Truncate(_retval.Length() - 4);
     886             : 
     887           0 :     return NS_OK;
     888             :   }
     889             : 
     890           0 :   nsresult Inflate(uint8_t *data, uint32_t dataLen, nsACString &_retval)
     891             :   {
     892           0 :     mMessageDeflated = false;
     893             : 
     894           0 :     Bytef trailingData[] = { 0x00, 0x00, 0xFF, 0xFF };
     895           0 :     bool trailingDataUsed = false;
     896             : 
     897           0 :     mInflater.avail_out = kBufferLen;
     898           0 :     mInflater.next_out = mBuffer;
     899           0 :     mInflater.avail_in = dataLen;
     900           0 :     mInflater.next_in = data;
     901             : 
     902             :     while (true) {
     903           0 :       int zerr = inflate(&mInflater, Z_NO_FLUSH);
     904             : 
     905           0 :       if (zerr == Z_STREAM_END) {
     906           0 :         Bytef *saveNextIn = mInflater.next_in;
     907           0 :         uint32_t saveAvailIn = mInflater.avail_in;
     908           0 :         Bytef *saveNextOut = mInflater.next_out;
     909           0 :         uint32_t saveAvailOut = mInflater.avail_out;
     910             : 
     911           0 :         inflateReset(&mInflater);
     912             : 
     913           0 :         mInflater.next_in = saveNextIn;
     914           0 :         mInflater.avail_in = saveAvailIn;
     915           0 :         mInflater.next_out = saveNextOut;
     916           0 :         mInflater.avail_out = saveAvailOut;
     917           0 :       } else if (zerr != Z_OK && zerr != Z_BUF_ERROR) {
     918           0 :         return NS_ERROR_INVALID_CONTENT_ENCODING;
     919             :       }
     920             : 
     921           0 :       uint32_t inflated = kBufferLen - mInflater.avail_out;
     922           0 :       if (inflated > 0) {
     923           0 :         _retval.Append(reinterpret_cast<char *>(mBuffer), inflated);
     924             :       }
     925             : 
     926           0 :       mInflater.avail_out = kBufferLen;
     927           0 :       mInflater.next_out = mBuffer;
     928             : 
     929           0 :       if (mInflater.avail_in > 0) {
     930           0 :         continue; // There is still some data to inflate
     931             :       }
     932             : 
     933           0 :       if (inflated == kBufferLen) {
     934           0 :         continue; // There was not enough space in the buffer
     935             :       }
     936             : 
     937           0 :       if (!trailingDataUsed) {
     938           0 :         trailingDataUsed = true;
     939           0 :         mInflater.avail_in = sizeof(trailingData);
     940           0 :         mInflater.next_in = trailingData;
     941           0 :         continue;
     942             :       }
     943             : 
     944           0 :       return NS_OK;
     945           0 :     }
     946             :   }
     947             : 
     948             : private:
     949             :   bool                  mActive;
     950             :   bool                  mNoContextTakeover;
     951             :   bool                  mResetDeflater;
     952             :   bool                  mMessageDeflated;
     953             :   z_stream              mDeflater;
     954             :   z_stream              mInflater;
     955             :   const static uint32_t kBufferLen = 4096;
     956             :   uint8_t               mBuffer[kBufferLen];
     957             : };
     958             : 
     959             : //-----------------------------------------------------------------------------
     960             : // OutboundMessage
     961             : //-----------------------------------------------------------------------------
     962             : 
     963             : enum WsMsgType {
     964             :   kMsgTypeString = 0,
     965             :   kMsgTypeBinaryString,
     966             :   kMsgTypeStream,
     967             :   kMsgTypePing,
     968             :   kMsgTypePong,
     969             :   kMsgTypeFin
     970             : };
     971             : 
     972             : static const char* msgNames[] = {
     973             :   "text",
     974             :   "binaryString",
     975             :   "binaryStream",
     976             :   "ping",
     977             :   "pong",
     978             :   "close"
     979             : };
     980             : 
     981             : class OutboundMessage
     982             : {
     983             : public:
     984           0 :   OutboundMessage(WsMsgType type, nsCString *str)
     985           0 :     : mMsgType(type), mDeflated(false), mOrigLength(0)
     986             :   {
     987           0 :     MOZ_COUNT_CTOR(OutboundMessage);
     988           0 :     mMsg.pString.mValue = str;
     989           0 :     mMsg.pString.mOrigValue = nullptr;
     990           0 :     mLength = str ? str->Length() : 0;
     991           0 :   }
     992             : 
     993           0 :   OutboundMessage(nsIInputStream *stream, uint32_t length)
     994           0 :     : mMsgType(kMsgTypeStream), mLength(length), mDeflated(false)
     995           0 :     , mOrigLength(0)
     996             :   {
     997           0 :     MOZ_COUNT_CTOR(OutboundMessage);
     998           0 :     mMsg.pStream = stream;
     999           0 :     mMsg.pStream->AddRef();
    1000           0 :   }
    1001             : 
    1002           0 :  ~OutboundMessage() {
    1003           0 :     MOZ_COUNT_DTOR(OutboundMessage);
    1004           0 :     switch (mMsgType) {
    1005             :       case kMsgTypeString:
    1006             :       case kMsgTypeBinaryString:
    1007             :       case kMsgTypePing:
    1008             :       case kMsgTypePong:
    1009           0 :         delete mMsg.pString.mValue;
    1010           0 :         if (mMsg.pString.mOrigValue)
    1011           0 :           delete mMsg.pString.mOrigValue;
    1012           0 :         break;
    1013             :       case kMsgTypeStream:
    1014             :         // for now this only gets hit if msg deleted w/o being sent
    1015           0 :         if (mMsg.pStream) {
    1016           0 :           mMsg.pStream->Close();
    1017           0 :           mMsg.pStream->Release();
    1018             :         }
    1019           0 :         break;
    1020             :       case kMsgTypeFin:
    1021           0 :         break;    // do-nothing: avoid compiler warning
    1022             :     }
    1023           0 :   }
    1024             : 
    1025           0 :   WsMsgType GetMsgType() const { return mMsgType; }
    1026           0 :   int32_t Length() const { return mLength; }
    1027           0 :   int32_t OrigLength() const { return mDeflated ? mOrigLength : mLength; }
    1028             : 
    1029           0 :   uint8_t* BeginWriting() {
    1030           0 :     MOZ_ASSERT(mMsgType != kMsgTypeStream,
    1031             :                "Stream should have been converted to string by now");
    1032           0 :     return (uint8_t *)(mMsg.pString.mValue ? mMsg.pString.mValue->BeginWriting() : nullptr);
    1033             :   }
    1034             : 
    1035           0 :   uint8_t* BeginReading() {
    1036           0 :     MOZ_ASSERT(mMsgType != kMsgTypeStream,
    1037             :                "Stream should have been converted to string by now");
    1038           0 :     return (uint8_t *)(mMsg.pString.mValue ? mMsg.pString.mValue->BeginReading() : nullptr);
    1039             :   }
    1040             : 
    1041           0 :   uint8_t* BeginOrigReading() {
    1042           0 :     MOZ_ASSERT(mMsgType != kMsgTypeStream,
    1043             :                "Stream should have been converted to string by now");
    1044           0 :     if (!mDeflated)
    1045           0 :       return BeginReading();
    1046           0 :     return (uint8_t *)(mMsg.pString.mOrigValue ? mMsg.pString.mOrigValue->BeginReading() : nullptr);
    1047             :   }
    1048             : 
    1049           0 :   nsresult ConvertStreamToString()
    1050             :   {
    1051           0 :     MOZ_ASSERT(mMsgType == kMsgTypeStream, "Not a stream!");
    1052             : 
    1053             : #ifdef DEBUG
    1054             :     // Make sure we got correct length from Blob
    1055             :     uint64_t bytes;
    1056           0 :     mMsg.pStream->Available(&bytes);
    1057           0 :     NS_ASSERTION(bytes == mLength, "Stream length != blob length!");
    1058             : #endif
    1059             : 
    1060           0 :     nsAutoPtr<nsCString> temp(new nsCString());
    1061           0 :     nsresult rv = NS_ReadInputStreamToString(mMsg.pStream, *temp, mLength);
    1062             : 
    1063           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1064             : 
    1065           0 :     mMsg.pStream->Close();
    1066           0 :     mMsg.pStream->Release();
    1067           0 :     mMsg.pString.mValue = temp.forget();
    1068           0 :     mMsg.pString.mOrigValue = nullptr;
    1069           0 :     mMsgType = kMsgTypeBinaryString;
    1070             : 
    1071           0 :     return NS_OK;
    1072             :   }
    1073             : 
    1074           0 :   bool DeflatePayload(PMCECompression *aCompressor)
    1075             :   {
    1076           0 :     MOZ_ASSERT(mMsgType != kMsgTypeStream,
    1077             :                "Stream should have been converted to string by now");
    1078           0 :     MOZ_ASSERT(!mDeflated);
    1079             : 
    1080             :     nsresult rv;
    1081             : 
    1082           0 :     if (mLength == 0) {
    1083             :       // Empty message
    1084           0 :       return false;
    1085             :     }
    1086             : 
    1087           0 :     nsAutoPtr<nsCString> temp(new nsCString());
    1088           0 :     rv = aCompressor->Deflate(BeginReading(), mLength, *temp);
    1089           0 :     if (NS_FAILED(rv)) {
    1090           0 :       LOG(("WebSocketChannel::OutboundMessage: Deflating payload failed "
    1091             :            "[rv=0x%08" PRIx32 "]\n", static_cast<uint32_t>(rv)));
    1092           0 :       return false;
    1093             :     }
    1094             : 
    1095           0 :     if (!aCompressor->UsingContextTakeover() && temp->Length() > mLength) {
    1096             :       // When "<local>_no_context_takeover" was negotiated, do not send deflated
    1097             :       // payload if it's larger that the original one. OTOH, it makes sense
    1098             :       // to send the larger deflated payload when the sliding window is not
    1099             :       // reset between messages because if we would skip some deflated block
    1100             :       // we would need to empty the sliding window which could affect the
    1101             :       // compression of the subsequent messages.
    1102           0 :       LOG(("WebSocketChannel::OutboundMessage: Not deflating message since the "
    1103             :            "deflated payload is larger than the original one [deflated=%d, "
    1104             :            "original=%d]", temp->Length(), mLength));
    1105           0 :       return false;
    1106             :     }
    1107             : 
    1108           0 :     mOrigLength = mLength;
    1109           0 :     mDeflated = true;
    1110           0 :     mLength = temp->Length();
    1111           0 :     mMsg.pString.mOrigValue = mMsg.pString.mValue;
    1112           0 :     mMsg.pString.mValue = temp.forget();
    1113           0 :     return true;
    1114             :   }
    1115             : 
    1116             : private:
    1117             :   union {
    1118             :     struct {
    1119             :       nsCString *mValue;
    1120             :       nsCString *mOrigValue;
    1121             :     } pString;
    1122             :     nsIInputStream *pStream;
    1123             :   }                           mMsg;
    1124             :   WsMsgType                   mMsgType;
    1125             :   uint32_t                    mLength;
    1126             :   bool                        mDeflated;
    1127             :   uint32_t                    mOrigLength;
    1128             : };
    1129             : 
    1130             : //-----------------------------------------------------------------------------
    1131             : // OutboundEnqueuer
    1132             : //-----------------------------------------------------------------------------
    1133             : 
    1134             : class OutboundEnqueuer final : public nsIRunnable
    1135             : {
    1136             : public:
    1137             :   NS_DECL_THREADSAFE_ISUPPORTS
    1138             : 
    1139           0 :   OutboundEnqueuer(WebSocketChannel *aChannel, OutboundMessage *aMsg)
    1140           0 :     : mChannel(aChannel), mMessage(aMsg) {}
    1141             : 
    1142           0 :   NS_IMETHOD Run() override
    1143             :   {
    1144           0 :     mChannel->EnqueueOutgoingMessage(mChannel->mOutgoingMessages, mMessage);
    1145           0 :     return NS_OK;
    1146             :   }
    1147             : 
    1148             : private:
    1149           0 :   ~OutboundEnqueuer() {}
    1150             : 
    1151             :   RefPtr<WebSocketChannel>  mChannel;
    1152             :   OutboundMessage            *mMessage;
    1153             : };
    1154           0 : NS_IMPL_ISUPPORTS(OutboundEnqueuer, nsIRunnable)
    1155             : 
    1156             : 
    1157             : //-----------------------------------------------------------------------------
    1158             : // WebSocketChannel
    1159             : //-----------------------------------------------------------------------------
    1160             : 
    1161           0 : WebSocketChannel::WebSocketChannel() :
    1162             :   mPort(0),
    1163             :   mCloseTimeout(20000),
    1164             :   mOpenTimeout(20000),
    1165             :   mConnecting(NOT_CONNECTING),
    1166             :   mMaxConcurrentConnections(200),
    1167             :   mGotUpgradeOK(0),
    1168             :   mRecvdHttpUpgradeTransport(0),
    1169             :   mAutoFollowRedirects(0),
    1170             :   mAllowPMCE(1),
    1171             :   mPingOutstanding(0),
    1172             :   mReleaseOnTransmit(0),
    1173             :   mDataStarted(0),
    1174             :   mRequestedClose(0),
    1175             :   mClientClosed(0),
    1176             :   mServerClosed(0),
    1177             :   mStopped(0),
    1178             :   mCalledOnStop(0),
    1179             :   mTCPClosed(0),
    1180             :   mOpenedHttpChannel(0),
    1181             :   mIncrementedSessionCount(0),
    1182             :   mDecrementedSessionCount(0),
    1183             :   mMaxMessageSize(INT32_MAX),
    1184             :   mStopOnClose(NS_OK),
    1185             :   mServerCloseCode(CLOSE_ABNORMAL),
    1186             :   mScriptCloseCode(0),
    1187             :   mFragmentOpcode(nsIWebSocketFrame::OPCODE_CONTINUATION),
    1188             :   mFragmentAccumulator(0),
    1189             :   mBuffered(0),
    1190             :   mBufferSize(kIncomingBufferInitialSize),
    1191             :   mCurrentOut(nullptr),
    1192             :   mCurrentOutSent(0),
    1193             :   mDynamicOutputSize(0),
    1194             :   mDynamicOutput(nullptr),
    1195             :   mPrivateBrowsing(false),
    1196           0 :   mConnectionLogService(nullptr)
    1197             : {
    1198           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    1199             : 
    1200           0 :   LOG(("WebSocketChannel::WebSocketChannel() %p\n", this));
    1201             : 
    1202           0 :   nsWSAdmissionManager::Init();
    1203             : 
    1204           0 :   mFramePtr = mBuffer = static_cast<uint8_t *>(moz_xmalloc(mBufferSize));
    1205             : 
    1206             :   nsresult rv;
    1207           0 :   mConnectionLogService = do_GetService("@mozilla.org/network/dashboard;1",&rv);
    1208           0 :   if (NS_FAILED(rv))
    1209           0 :     LOG(("Failed to initiate dashboard service."));
    1210             : 
    1211           0 :   mService = WebSocketEventService::GetOrCreate();
    1212           0 : }
    1213             : 
    1214           0 : WebSocketChannel::~WebSocketChannel()
    1215             : {
    1216           0 :   LOG(("WebSocketChannel::~WebSocketChannel() %p\n", this));
    1217             : 
    1218           0 :   if (mWasOpened) {
    1219           0 :     MOZ_ASSERT(mCalledOnStop, "WebSocket was opened but OnStop was not called");
    1220           0 :     MOZ_ASSERT(mStopped, "WebSocket was opened but never stopped");
    1221             :   }
    1222           0 :   MOZ_ASSERT(!mCancelable, "DNS/Proxy Request still alive at destruction");
    1223           0 :   MOZ_ASSERT(!mConnecting, "Should not be connecting in destructor");
    1224             : 
    1225           0 :   free(mBuffer);
    1226           0 :   free(mDynamicOutput);
    1227           0 :   delete mCurrentOut;
    1228             : 
    1229           0 :   while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
    1230           0 :     delete mCurrentOut;
    1231           0 :   while ((mCurrentOut = (OutboundMessage *) mOutgoingPongMessages.PopFront()))
    1232           0 :     delete mCurrentOut;
    1233           0 :   while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront()))
    1234           0 :     delete mCurrentOut;
    1235             : 
    1236           0 :   NS_ReleaseOnMainThread("WebSocketChannel::mURI", mURI.forget());
    1237           0 :   NS_ReleaseOnMainThread("WebSocketChannel::mOriginalURI", mOriginalURI.forget());
    1238             : 
    1239           0 :   mListenerMT = nullptr;
    1240             : 
    1241           0 :   NS_ReleaseOnMainThread("WebSocketChannel::mLoadGroup", mLoadGroup.forget());
    1242           0 :   NS_ReleaseOnMainThread("WebSocketChannel::mLoadInfo", mLoadInfo.forget());
    1243           0 :   NS_ReleaseOnMainThread("WebSocketChannel::mService", mService.forget());
    1244           0 : }
    1245             : 
    1246             : NS_IMETHODIMP
    1247           0 : WebSocketChannel::Observe(nsISupports *subject,
    1248             :                           const char *topic,
    1249             :                           const char16_t *data)
    1250             : {
    1251           0 :   LOG(("WebSocketChannel::Observe [topic=\"%s\"]\n", topic));
    1252             : 
    1253           0 :   if (strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0) {
    1254           0 :     nsCString converted = NS_ConvertUTF16toUTF8(data);
    1255           0 :     const char *state = converted.get();
    1256             : 
    1257           0 :     if (strcmp(state, NS_NETWORK_LINK_DATA_CHANGED) == 0) {
    1258           0 :       LOG(("WebSocket: received network CHANGED event"));
    1259             : 
    1260           0 :       if (!mSocketThread) {
    1261             :         // there has not been an asyncopen yet on the object and then we need
    1262             :         // no ping.
    1263           0 :         LOG(("WebSocket: early object, no ping needed"));
    1264             :       } else {
    1265             :         // Next we check mDataStarted, which we need to do on mTargetThread.
    1266           0 :         if (!IsOnTargetThread()) {
    1267           0 :           mTargetThread->Dispatch(
    1268           0 :             NewRunnableMethod("net::WebSocketChannel::OnNetworkChanged",
    1269             :                               this,
    1270             :                               &WebSocketChannel::OnNetworkChanged),
    1271           0 :             NS_DISPATCH_NORMAL);
    1272             :         } else {
    1273           0 :           nsresult rv = OnNetworkChanged();
    1274           0 :           if (NS_FAILED(rv)) {
    1275           0 :             LOG(("WebSocket: OnNetworkChanged failed (%08" PRIx32 ")",
    1276             :                  static_cast<uint32_t>(rv)));
    1277             :           }
    1278             :         }
    1279             :       }
    1280             :     }
    1281             :   }
    1282             : 
    1283           0 :   return NS_OK;
    1284             : }
    1285             : 
    1286             : nsresult
    1287           0 : WebSocketChannel::OnNetworkChanged()
    1288             : {
    1289           0 :   if (IsOnTargetThread()) {
    1290           0 :     LOG(("WebSocketChannel::OnNetworkChanged() - on target thread %p", this));
    1291             : 
    1292           0 :     if (!mDataStarted) {
    1293           0 :       LOG(("WebSocket: data not started yet, no ping needed"));
    1294           0 :       return NS_OK;
    1295             :     }
    1296             : 
    1297           0 :     return mSocketThread->Dispatch(
    1298           0 :       NewRunnableMethod("net::WebSocketChannel::OnNetworkChanged",
    1299             :                         this,
    1300             :                         &WebSocketChannel::OnNetworkChanged),
    1301           0 :       NS_DISPATCH_NORMAL);
    1302             :   }
    1303             : 
    1304           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1305             : 
    1306           0 :   LOG(("WebSocketChannel::OnNetworkChanged() - on socket thread %p", this));
    1307             : 
    1308           0 :   if (mPingOutstanding) {
    1309             :     // If there's an outstanding ping that's expected to get a pong back
    1310             :     // we let that do its thing.
    1311           0 :     LOG(("WebSocket: pong already pending"));
    1312           0 :     return NS_OK;
    1313             :   }
    1314             : 
    1315           0 :   if (mPingForced) {
    1316             :     // avoid more than one
    1317           0 :     LOG(("WebSocket: forced ping timer already fired"));
    1318           0 :     return NS_OK;
    1319             :   }
    1320             : 
    1321           0 :   LOG(("nsWebSocketChannel:: Generating Ping as network changed\n"));
    1322             : 
    1323           0 :   if (!mPingTimer) {
    1324             :     // The ping timer is only conditionally running already. If it wasn't
    1325             :     // already created do it here.
    1326             :     nsresult rv;
    1327           0 :     mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1328           0 :     if (NS_FAILED(rv)) {
    1329           0 :       LOG(("WebSocket: unable to create ping timer!"));
    1330           0 :       NS_WARNING("unable to create ping timer!");
    1331           0 :       return rv;
    1332             :     }
    1333             :   }
    1334             :   // Trigger the ping timeout asap to fire off a new ping. Wait just
    1335             :   // a little bit to better avoid multi-triggers.
    1336           0 :   mPingForced = 1;
    1337           0 :   mPingTimer->InitWithCallback(this, 200, nsITimer::TYPE_ONE_SHOT);
    1338             : 
    1339           0 :   return NS_OK;
    1340             : }
    1341             : 
    1342             : void
    1343           0 : WebSocketChannel::Shutdown()
    1344             : {
    1345           0 :   nsWSAdmissionManager::Shutdown();
    1346           0 : }
    1347             : 
    1348             : bool
    1349           0 : WebSocketChannel::IsOnTargetThread()
    1350             : {
    1351           0 :   MOZ_ASSERT(mTargetThread);
    1352           0 :   bool isOnTargetThread = false;
    1353           0 :   nsresult rv = mTargetThread->IsOnCurrentThread(&isOnTargetThread);
    1354           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    1355           0 :   return NS_FAILED(rv) ? false : isOnTargetThread;
    1356             : }
    1357             : 
    1358             : void
    1359           0 : WebSocketChannel::GetEffectiveURL(nsAString& aEffectiveURL) const
    1360             : {
    1361           0 :   aEffectiveURL = mEffectiveURL;
    1362           0 : }
    1363             : 
    1364             : bool
    1365           0 : WebSocketChannel::IsEncrypted() const
    1366             : {
    1367           0 :   return mEncrypted;
    1368             : }
    1369             : 
    1370             : void
    1371           0 : WebSocketChannel::BeginOpen(bool aCalledFromAdmissionManager)
    1372             : {
    1373           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    1374             : 
    1375           0 :   LOG(("WebSocketChannel::BeginOpen() %p\n", this));
    1376             : 
    1377             :   // Important that we set CONNECTING_IN_PROGRESS before any call to
    1378             :   // AbortSession here: ensures that any remaining queued connection(s) are
    1379             :   // scheduled in OnStopSession
    1380           0 :   LOG(("Websocket: changing state to CONNECTING_IN_PROGRESS"));
    1381           0 :   mConnecting = CONNECTING_IN_PROGRESS;
    1382             : 
    1383           0 :   if (aCalledFromAdmissionManager) {
    1384             :     // When called from nsWSAdmissionManager post an event to avoid potential
    1385             :     // re-entering of nsWSAdmissionManager and its lock.
    1386           0 :     NS_DispatchToMainThread(
    1387           0 :       NewRunnableMethod("net::WebSocketChannel::BeginOpenInternal",
    1388             :                         this,
    1389             :                         &WebSocketChannel::BeginOpenInternal),
    1390           0 :       NS_DISPATCH_NORMAL);
    1391             :   } else {
    1392           0 :     BeginOpenInternal();
    1393             :   }
    1394           0 : }
    1395             : 
    1396             : void
    1397           0 : WebSocketChannel::BeginOpenInternal()
    1398             : {
    1399           0 :   LOG(("WebSocketChannel::BeginOpenInternal() %p\n", this));
    1400             : 
    1401             :   nsresult rv;
    1402             : 
    1403           0 :   if (mRedirectCallback) {
    1404           0 :     LOG(("WebSocketChannel::BeginOpenInternal: Resuming Redirect\n"));
    1405           0 :     rv = mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
    1406           0 :     mRedirectCallback = nullptr;
    1407           0 :     return;
    1408             :   }
    1409             : 
    1410           0 :   nsCOMPtr<nsIChannel> localChannel = do_QueryInterface(mChannel, &rv);
    1411           0 :   if (NS_FAILED(rv)) {
    1412           0 :     LOG(("WebSocketChannel::BeginOpenInternal: cannot async open\n"));
    1413           0 :     AbortSession(NS_ERROR_UNEXPECTED);
    1414           0 :     return;
    1415             :   }
    1416             : 
    1417           0 :   rv = NS_MaybeOpenChannelUsingAsyncOpen2(localChannel, this);
    1418             : 
    1419           0 :   if (NS_FAILED(rv)) {
    1420           0 :     LOG(("WebSocketChannel::BeginOpenInternal: cannot async open\n"));
    1421           0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    1422           0 :     return;
    1423             :   }
    1424           0 :   mOpenedHttpChannel = 1;
    1425             : 
    1426           0 :   mOpenTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1427           0 :   if (NS_FAILED(rv)) {
    1428           0 :     LOG(("WebSocketChannel::BeginOpenInternal: cannot create open timer\n"));
    1429           0 :     AbortSession(NS_ERROR_UNEXPECTED);
    1430           0 :     return;
    1431             :   }
    1432             : 
    1433           0 :   rv = mOpenTimer->InitWithCallback(this, mOpenTimeout,
    1434           0 :                                     nsITimer::TYPE_ONE_SHOT);
    1435           0 :   if (NS_FAILED(rv)) {
    1436           0 :     LOG(("WebSocketChannel::BeginOpenInternal: cannot initialize open "
    1437             :          "timer\n"));
    1438           0 :     AbortSession(NS_ERROR_UNEXPECTED);
    1439           0 :     return;
    1440             :   }
    1441             : }
    1442             : 
    1443             : bool
    1444           0 : WebSocketChannel::IsPersistentFramePtr()
    1445             : {
    1446           0 :   return (mFramePtr >= mBuffer && mFramePtr < mBuffer + mBufferSize);
    1447             : }
    1448             : 
    1449             : // Extends the internal buffer by count and returns the total
    1450             : // amount of data available for read
    1451             : //
    1452             : // Accumulated fragment size is passed in instead of using the member
    1453             : // variable beacuse when transitioning from the stack to the persistent
    1454             : // read buffer we want to explicitly include them in the buffer instead
    1455             : // of as already existing data.
    1456             : bool
    1457           0 : WebSocketChannel::UpdateReadBuffer(uint8_t *buffer, uint32_t count,
    1458             :                                    uint32_t accumulatedFragments,
    1459             :                                    uint32_t *available)
    1460             : {
    1461           0 :   LOG(("WebSocketChannel::UpdateReadBuffer() %p [%p %u]\n",
    1462             :          this, buffer, count));
    1463             : 
    1464           0 :   if (!mBuffered)
    1465           0 :     mFramePtr = mBuffer;
    1466             : 
    1467           0 :   MOZ_ASSERT(IsPersistentFramePtr(), "update read buffer bad mFramePtr");
    1468           0 :   MOZ_ASSERT(mFramePtr - accumulatedFragments >= mBuffer,
    1469             :              "reserved FramePtr bad");
    1470             : 
    1471           0 :   if (mBuffered + count <= mBufferSize) {
    1472             :     // append to existing buffer
    1473           0 :     LOG(("WebSocketChannel: update read buffer absorbed %u\n", count));
    1474           0 :   } else if (mBuffered + count -
    1475           0 :              (mFramePtr - accumulatedFragments - mBuffer) <= mBufferSize) {
    1476             :     // make room in existing buffer by shifting unused data to start
    1477           0 :     mBuffered -= (mFramePtr - mBuffer - accumulatedFragments);
    1478           0 :     LOG(("WebSocketChannel: update read buffer shifted %u\n", mBuffered));
    1479           0 :     ::memmove(mBuffer, mFramePtr - accumulatedFragments, mBuffered);
    1480           0 :     mFramePtr = mBuffer + accumulatedFragments;
    1481             :   } else {
    1482             :     // existing buffer is not sufficient, extend it
    1483           0 :     mBufferSize += count + 8192 + mBufferSize/3;
    1484           0 :     LOG(("WebSocketChannel: update read buffer extended to %u\n", mBufferSize));
    1485           0 :     uint8_t *old = mBuffer;
    1486           0 :     mBuffer = (uint8_t *)realloc(mBuffer, mBufferSize);
    1487           0 :     if (!mBuffer) {
    1488           0 :       mBuffer = old;
    1489           0 :       return false;
    1490             :     }
    1491           0 :     mFramePtr = mBuffer + (mFramePtr - old);
    1492             :   }
    1493             : 
    1494           0 :   ::memcpy(mBuffer + mBuffered, buffer, count);
    1495           0 :   mBuffered += count;
    1496             : 
    1497           0 :   if (available)
    1498           0 :     *available = mBuffered - (mFramePtr - mBuffer);
    1499             : 
    1500           0 :   return true;
    1501             : }
    1502             : 
    1503             : nsresult
    1504           0 : WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count)
    1505             : {
    1506           0 :   LOG(("WebSocketChannel::ProcessInput %p [%d %d]\n", this, count, mBuffered));
    1507           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1508             : 
    1509             :   nsresult rv;
    1510             : 
    1511             :   // The purpose of ping/pong is to actively probe the peer so that an
    1512             :   // unreachable peer is not mistaken for a period of idleness. This
    1513             :   // implementation accepts any application level read activity as a sign of
    1514             :   // life, it does not necessarily have to be a pong.
    1515           0 :   ResetPingTimer();
    1516             : 
    1517             :   uint32_t avail;
    1518             : 
    1519           0 :   if (!mBuffered) {
    1520             :     // Most of the time we can process right off the stack buffer without
    1521             :     // having to accumulate anything
    1522           0 :     mFramePtr = buffer;
    1523           0 :     avail = count;
    1524             :   } else {
    1525           0 :     if (!UpdateReadBuffer(buffer, count, mFragmentAccumulator, &avail)) {
    1526           0 :       return NS_ERROR_FILE_TOO_BIG;
    1527             :     }
    1528             :   }
    1529             : 
    1530             :   uint8_t *payload;
    1531           0 :   uint32_t totalAvail = avail;
    1532             : 
    1533           0 :   while (avail >= 2) {
    1534           0 :     int64_t payloadLength64 = mFramePtr[1] & kPayloadLengthBitsMask;
    1535           0 :     uint8_t finBit  = mFramePtr[0] & kFinalFragBit;
    1536           0 :     uint8_t rsvBits = mFramePtr[0] & kRsvBitsMask;
    1537           0 :     uint8_t rsvBit1 = mFramePtr[0] & kRsv1Bit;
    1538           0 :     uint8_t rsvBit2 = mFramePtr[0] & kRsv2Bit;
    1539           0 :     uint8_t rsvBit3 = mFramePtr[0] & kRsv3Bit;
    1540           0 :     uint8_t opcode  = mFramePtr[0] & kOpcodeBitsMask;
    1541           0 :     uint8_t maskBit = mFramePtr[1] & kMaskBit;
    1542           0 :     uint32_t mask = 0;
    1543             : 
    1544           0 :     uint32_t framingLength = 2;
    1545           0 :     if (maskBit)
    1546           0 :       framingLength += 4;
    1547             : 
    1548           0 :     if (payloadLength64 < 126) {
    1549           0 :       if (avail < framingLength)
    1550           0 :         break;
    1551           0 :     } else if (payloadLength64 == 126) {
    1552             :       // 16 bit length field
    1553           0 :       framingLength += 2;
    1554           0 :       if (avail < framingLength)
    1555           0 :         break;
    1556             : 
    1557           0 :       payloadLength64 = mFramePtr[2] << 8 | mFramePtr[3];
    1558             : 
    1559           0 :       if(payloadLength64 < 126){
    1560             :         // Section 5.2 says that the minimal number of bytes MUST
    1561             :         // be used to encode the length in all cases
    1562           0 :         LOG(("WebSocketChannel:: non-minimal-encoded payload length"));
    1563           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1564             :       }
    1565             : 
    1566             :     } else {
    1567             :       // 64 bit length
    1568           0 :       framingLength += 8;
    1569           0 :       if (avail < framingLength)
    1570           0 :         break;
    1571             : 
    1572           0 :       if (mFramePtr[2] & 0x80) {
    1573             :         // Section 4.2 says that the most significant bit MUST be
    1574             :         // 0. (i.e. this is really a 63 bit value)
    1575           0 :         LOG(("WebSocketChannel:: high bit of 64 bit length set"));
    1576           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1577             :       }
    1578             : 
    1579             :       // copy this in case it is unaligned
    1580           0 :       payloadLength64 = NetworkEndian::readInt64(mFramePtr + 2);
    1581             : 
    1582           0 :       if(payloadLength64 <= 0xffff){
    1583             :         // Section 5.2 says that the minimal number of bytes MUST
    1584             :         // be used to encode the length in all cases
    1585           0 :         LOG(("WebSocketChannel:: non-minimal-encoded payload length"));
    1586           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1587             :       }
    1588             : 
    1589             :     }
    1590             : 
    1591           0 :     payload = mFramePtr + framingLength;
    1592           0 :     avail -= framingLength;
    1593             : 
    1594           0 :     LOG(("WebSocketChannel::ProcessInput: payload %" PRId64 " avail %" PRIu32 "\n",
    1595             :          payloadLength64, avail));
    1596             : 
    1597           0 :     CheckedInt<int64_t> payloadLengthChecked(payloadLength64);
    1598           0 :     payloadLengthChecked += mFragmentAccumulator;
    1599           0 :     if (!payloadLengthChecked.isValid() || payloadLengthChecked.value() >
    1600           0 :         mMaxMessageSize) {
    1601           0 :       return NS_ERROR_FILE_TOO_BIG;
    1602             :     }
    1603             : 
    1604           0 :     uint32_t payloadLength = static_cast<uint32_t>(payloadLength64);
    1605             : 
    1606           0 :     if (avail < payloadLength)
    1607           0 :       break;
    1608             : 
    1609           0 :     LOG(("WebSocketChannel::ProcessInput: Frame accumulated - opcode %d\n",
    1610             :          opcode));
    1611             : 
    1612           0 :     if (!maskBit && mIsServerSide) {
    1613           0 :       LOG(("WebSocketChannel::ProcessInput: unmasked frame received "
    1614             :            "from client\n"));
    1615           0 :       return NS_ERROR_ILLEGAL_VALUE;
    1616             :     }
    1617             : 
    1618           0 :     if (maskBit) {
    1619           0 :       if (!mIsServerSide) {
    1620             :         // The server should not be allowed to send masked frames to clients.
    1621             :         // But we've been allowing it for some time, so this should be
    1622             :         // deprecated with care.
    1623           0 :         LOG(("WebSocketChannel:: Client RECEIVING masked frame."));
    1624             :       }
    1625             : 
    1626           0 :       mask = NetworkEndian::readUint32(payload - 4);
    1627             :     }
    1628             : 
    1629           0 :     if (mask) {
    1630           0 :       ApplyMask(mask, payload, payloadLength);
    1631           0 :     } else if (mIsServerSide) {
    1632           0 :       LOG(("WebSocketChannel::ProcessInput: masked frame with mask 0 received"
    1633             :            "from client\n"));
    1634           0 :       return NS_ERROR_ILLEGAL_VALUE;
    1635             :     }
    1636             : 
    1637             : 
    1638             :     // Control codes are required to have the fin bit set
    1639           0 :     if (!finBit && (opcode & kControlFrameMask)) {
    1640           0 :       LOG(("WebSocketChannel:: fragmented control frame code %d\n", opcode));
    1641           0 :       return NS_ERROR_ILLEGAL_VALUE;
    1642             :     }
    1643             : 
    1644           0 :     if (rsvBits) {
    1645             :       // PMCE sets RSV1 bit in the first fragment when the non-control frame
    1646             :       // is deflated
    1647           0 :       if (mPMCECompressor && rsvBits == kRsv1Bit && mFragmentAccumulator == 0 &&
    1648           0 :           !(opcode & kControlFrameMask)) {
    1649           0 :         mPMCECompressor->SetMessageDeflated();
    1650           0 :         LOG(("WebSocketChannel::ProcessInput: received deflated frame\n"));
    1651             :       } else {
    1652           0 :         LOG(("WebSocketChannel::ProcessInput: unexpected reserved bits %x\n",
    1653             :              rsvBits));
    1654           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1655             :       }
    1656             :     }
    1657             : 
    1658           0 :     if (!finBit || opcode == nsIWebSocketFrame::OPCODE_CONTINUATION) {
    1659             :       // This is part of a fragment response
    1660             : 
    1661             :       // Only the first frame has a non zero op code: Make sure we don't see a
    1662             :       // first frame while some old fragments are open
    1663           0 :       if ((mFragmentAccumulator != 0) &&
    1664             :           (opcode != nsIWebSocketFrame::OPCODE_CONTINUATION)) {
    1665           0 :         LOG(("WebSocketChannel:: nested fragments\n"));
    1666           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1667             :       }
    1668             : 
    1669           0 :       LOG(("WebSocketChannel:: Accumulating Fragment %" PRIu32 "\n", payloadLength));
    1670             : 
    1671           0 :       if (opcode == nsIWebSocketFrame::OPCODE_CONTINUATION) {
    1672             : 
    1673             :         // Make sure this continuation fragment isn't the first fragment
    1674           0 :         if (mFragmentOpcode == nsIWebSocketFrame::OPCODE_CONTINUATION) {
    1675           0 :           LOG(("WebSocketHeandler:: continuation code in first fragment\n"));
    1676           0 :           return NS_ERROR_ILLEGAL_VALUE;
    1677             :         }
    1678             : 
    1679             :         // For frag > 1 move the data body back on top of the headers
    1680             :         // so we have contiguous stream of data
    1681           0 :         MOZ_ASSERT(mFramePtr + framingLength == payload,
    1682             :                    "payload offset from frameptr wrong");
    1683           0 :         ::memmove(mFramePtr, payload, avail);
    1684           0 :         payload = mFramePtr;
    1685           0 :         if (mBuffered)
    1686           0 :           mBuffered -= framingLength;
    1687             :       } else {
    1688           0 :         mFragmentOpcode = opcode;
    1689             :       }
    1690             : 
    1691           0 :       if (finBit) {
    1692           0 :         LOG(("WebSocketChannel:: Finalizing Fragment\n"));
    1693           0 :         payload -= mFragmentAccumulator;
    1694           0 :         payloadLength += mFragmentAccumulator;
    1695           0 :         avail += mFragmentAccumulator;
    1696           0 :         mFragmentAccumulator = 0;
    1697           0 :         opcode = mFragmentOpcode;
    1698             :         // reset to detect if next message illegally starts with continuation
    1699           0 :         mFragmentOpcode = nsIWebSocketFrame::OPCODE_CONTINUATION;
    1700             :       } else {
    1701           0 :         opcode = nsIWebSocketFrame::OPCODE_CONTINUATION;
    1702           0 :         mFragmentAccumulator += payloadLength;
    1703             :       }
    1704           0 :     } else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) {
    1705             :       // This frame is not part of a fragment sequence but we
    1706             :       // have an open fragment.. it must be a control code or else
    1707             :       // we have a problem
    1708           0 :       LOG(("WebSocketChannel:: illegal fragment sequence\n"));
    1709           0 :       return NS_ERROR_ILLEGAL_VALUE;
    1710             :     }
    1711             : 
    1712           0 :     if (mServerClosed) {
    1713           0 :       LOG(("WebSocketChannel:: ignoring read frame code %d after close\n",
    1714             :                  opcode));
    1715             :       // nop
    1716           0 :     } else if (mStopped) {
    1717           0 :       LOG(("WebSocketChannel:: ignoring read frame code %d after completion\n",
    1718             :            opcode));
    1719           0 :     } else if (opcode == nsIWebSocketFrame::OPCODE_TEXT) {
    1720           0 :       bool isDeflated = mPMCECompressor && mPMCECompressor->IsMessageDeflated();
    1721           0 :       LOG(("WebSocketChannel:: %stext frame received\n",
    1722             :            isDeflated ? "deflated " : ""));
    1723             : 
    1724           0 :       if (mListenerMT) {
    1725           0 :         nsCString utf8Data;
    1726             : 
    1727           0 :         if (isDeflated) {
    1728           0 :           rv = mPMCECompressor->Inflate(payload, payloadLength, utf8Data);
    1729           0 :           if (NS_FAILED(rv)) {
    1730           0 :             return rv;
    1731             :           }
    1732           0 :           LOG(("WebSocketChannel:: message successfully inflated "
    1733             :                "[origLength=%d, newLength=%d]\n", payloadLength,
    1734             :                utf8Data.Length()));
    1735             :         } else {
    1736           0 :           if (!utf8Data.Assign((const char *)payload, payloadLength,
    1737             :                                mozilla::fallible)) {
    1738           0 :             return NS_ERROR_OUT_OF_MEMORY;
    1739             :           }
    1740             :         }
    1741             : 
    1742             :         // Section 8.1 says to fail connection if invalid utf-8 in text message
    1743           0 :         if (!IsUTF8(utf8Data, false)) {
    1744           0 :           LOG(("WebSocketChannel:: text frame invalid utf-8\n"));
    1745           0 :           return NS_ERROR_CANNOT_CONVERT_DATA;
    1746             :         }
    1747             : 
    1748             :         RefPtr<WebSocketFrame> frame =
    1749           0 :           mService->CreateFrameIfNeeded(finBit, rsvBit1, rsvBit2, rsvBit3,
    1750           0 :                                         opcode, maskBit, mask, utf8Data);
    1751             : 
    1752           0 :         if (frame) {
    1753           0 :           mService->FrameReceived(mSerial, mInnerWindowID, frame.forget());
    1754             :         }
    1755             : 
    1756           0 :         mTargetThread->Dispatch(new CallOnMessageAvailable(this, utf8Data, -1),
    1757           0 :                                 NS_DISPATCH_NORMAL);
    1758           0 :         if (mConnectionLogService && !mPrivateBrowsing) {
    1759           0 :           mConnectionLogService->NewMsgReceived(mHost, mSerial, count);
    1760           0 :           LOG(("Added new msg received for %s", mHost.get()));
    1761             :         }
    1762             :       }
    1763           0 :     } else if (opcode & kControlFrameMask) {
    1764             :       // control frames
    1765           0 :       if (payloadLength > 125) {
    1766           0 :         LOG(("WebSocketChannel:: bad control frame code %d length %d\n",
    1767             :              opcode, payloadLength));
    1768           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1769             :       }
    1770             : 
    1771             :       RefPtr<WebSocketFrame> frame =
    1772           0 :         mService->CreateFrameIfNeeded(finBit, rsvBit1, rsvBit2, rsvBit3,
    1773             :                                       opcode, maskBit, mask, payload,
    1774           0 :                                       payloadLength);
    1775             : 
    1776           0 :       if (opcode == nsIWebSocketFrame::OPCODE_CLOSE) {
    1777           0 :         LOG(("WebSocketChannel:: close received\n"));
    1778           0 :         mServerClosed = 1;
    1779             : 
    1780           0 :         mServerCloseCode = CLOSE_NO_STATUS;
    1781           0 :         if (payloadLength >= 2) {
    1782           0 :           mServerCloseCode = NetworkEndian::readUint16(payload);
    1783           0 :           LOG(("WebSocketChannel:: close recvd code %u\n", mServerCloseCode));
    1784           0 :           uint16_t msglen = static_cast<uint16_t>(payloadLength - 2);
    1785           0 :           if (msglen > 0) {
    1786           0 :             mServerCloseReason.SetLength(msglen);
    1787           0 :             memcpy(mServerCloseReason.BeginWriting(),
    1788           0 :                    (const char *)payload + 2, msglen);
    1789             : 
    1790             :             // section 8.1 says to replace received non utf-8 sequences
    1791             :             // (which are non-conformant to send) with u+fffd,
    1792             :             // but secteam feels that silently rewriting messages is
    1793             :             // inappropriate - so we will fail the connection instead.
    1794           0 :             if (!IsUTF8(mServerCloseReason, false)) {
    1795           0 :               LOG(("WebSocketChannel:: close frame invalid utf-8\n"));
    1796           0 :               return NS_ERROR_CANNOT_CONVERT_DATA;
    1797             :             }
    1798             : 
    1799           0 :             LOG(("WebSocketChannel:: close msg %s\n",
    1800             :                  mServerCloseReason.get()));
    1801             :           }
    1802             :         }
    1803             : 
    1804           0 :         if (mCloseTimer) {
    1805           0 :           mCloseTimer->Cancel();
    1806           0 :           mCloseTimer = nullptr;
    1807             :         }
    1808             : 
    1809           0 :         if (frame) {
    1810             :           // We send the frame immediately becuase we want to have it dispatched
    1811             :           // before the CallOnServerClose.
    1812           0 :           mService->FrameReceived(mSerial, mInnerWindowID, frame.forget());
    1813           0 :           frame = nullptr;
    1814             :         }
    1815             : 
    1816           0 :         if (mListenerMT) {
    1817           0 :           mTargetThread->Dispatch(new CallOnServerClose(this, mServerCloseCode,
    1818           0 :                                                         mServerCloseReason),
    1819           0 :                                   NS_DISPATCH_NORMAL);
    1820             :         }
    1821             : 
    1822           0 :         if (mClientClosed)
    1823           0 :           ReleaseSession();
    1824           0 :       } else if (opcode == nsIWebSocketFrame::OPCODE_PING) {
    1825           0 :         LOG(("WebSocketChannel:: ping received\n"));
    1826           0 :         GeneratePong(payload, payloadLength);
    1827           0 :       } else if (opcode == nsIWebSocketFrame::OPCODE_PONG) {
    1828             :         // opcode OPCODE_PONG: the mere act of receiving the packet is all we
    1829             :         // need to do for the pong to trigger the activity timers
    1830           0 :         LOG(("WebSocketChannel:: pong received\n"));
    1831             :       } else {
    1832             :         /* unknown control frame opcode */
    1833           0 :         LOG(("WebSocketChannel:: unknown control op code %d\n", opcode));
    1834           0 :         return NS_ERROR_ILLEGAL_VALUE;
    1835             :       }
    1836             : 
    1837           0 :       if (mFragmentAccumulator) {
    1838             :         // Remove the control frame from the stream so we have a contiguous
    1839             :         // data buffer of reassembled fragments
    1840           0 :         LOG(("WebSocketChannel:: Removing Control From Read buffer\n"));
    1841           0 :         MOZ_ASSERT(mFramePtr + framingLength == payload,
    1842             :                    "payload offset from frameptr wrong");
    1843           0 :         ::memmove(mFramePtr, payload + payloadLength, avail - payloadLength);
    1844           0 :         payload = mFramePtr;
    1845           0 :         avail -= payloadLength;
    1846           0 :         if (mBuffered)
    1847           0 :           mBuffered -= framingLength + payloadLength;
    1848           0 :         payloadLength = 0;
    1849             :       }
    1850             : 
    1851           0 :       if (frame) {
    1852           0 :         mService->FrameReceived(mSerial, mInnerWindowID, frame.forget());
    1853             :       }
    1854           0 :     } else if (opcode == nsIWebSocketFrame::OPCODE_BINARY) {
    1855           0 :       bool isDeflated = mPMCECompressor && mPMCECompressor->IsMessageDeflated();
    1856           0 :       LOG(("WebSocketChannel:: %sbinary frame received\n",
    1857             :            isDeflated ? "deflated " : ""));
    1858             : 
    1859           0 :       if (mListenerMT) {
    1860           0 :         nsCString binaryData;
    1861             : 
    1862           0 :         if (isDeflated) {
    1863           0 :           rv = mPMCECompressor->Inflate(payload, payloadLength, binaryData);
    1864           0 :           if (NS_FAILED(rv)) {
    1865           0 :             return rv;
    1866             :           }
    1867           0 :           LOG(("WebSocketChannel:: message successfully inflated "
    1868             :                "[origLength=%d, newLength=%d]\n", payloadLength,
    1869             :                binaryData.Length()));
    1870             :         } else {
    1871           0 :           if (!binaryData.Assign((const char *)payload, payloadLength,
    1872             :                                  mozilla::fallible)) {
    1873           0 :             return NS_ERROR_OUT_OF_MEMORY;
    1874             :           }
    1875             :         }
    1876             : 
    1877             :         RefPtr<WebSocketFrame> frame =
    1878           0 :           mService->CreateFrameIfNeeded(finBit, rsvBit1, rsvBit2, rsvBit3,
    1879           0 :                                         opcode, maskBit, mask, binaryData);
    1880           0 :         if (frame) {
    1881           0 :           mService->FrameReceived(mSerial, mInnerWindowID, frame.forget());
    1882             :         }
    1883             : 
    1884           0 :         mTargetThread->Dispatch(
    1885           0 :           new CallOnMessageAvailable(this, binaryData, binaryData.Length()),
    1886           0 :           NS_DISPATCH_NORMAL);
    1887             :         // To add the header to 'Networking Dashboard' log
    1888           0 :         if (mConnectionLogService && !mPrivateBrowsing) {
    1889           0 :           mConnectionLogService->NewMsgReceived(mHost, mSerial, count);
    1890           0 :           LOG(("Added new received msg for %s", mHost.get()));
    1891             :         }
    1892             :       }
    1893           0 :     } else if (opcode != nsIWebSocketFrame::OPCODE_CONTINUATION) {
    1894             :       /* unknown opcode */
    1895           0 :       LOG(("WebSocketChannel:: unknown op code %d\n", opcode));
    1896           0 :       return NS_ERROR_ILLEGAL_VALUE;
    1897             :     }
    1898             : 
    1899           0 :     mFramePtr = payload + payloadLength;
    1900           0 :     avail -= payloadLength;
    1901           0 :     totalAvail = avail;
    1902             :   }
    1903             : 
    1904             :   // Adjust the stateful buffer. If we were operating off the stack and
    1905             :   // now have a partial message then transition to the buffer, or if
    1906             :   // we were working off the buffer but no longer have any active state
    1907             :   // then transition to the stack
    1908           0 :   if (!IsPersistentFramePtr()) {
    1909           0 :     mBuffered = 0;
    1910             : 
    1911           0 :     if (mFragmentAccumulator) {
    1912           0 :       LOG(("WebSocketChannel:: Setup Buffer due to fragment"));
    1913             : 
    1914           0 :       if (!UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
    1915           0 :                             totalAvail + mFragmentAccumulator, 0, nullptr)) {
    1916           0 :         return NS_ERROR_FILE_TOO_BIG;
    1917             :       }
    1918             : 
    1919             :       // UpdateReadBuffer will reset the frameptr to the beginning
    1920             :       // of new saved state, so we need to skip past processed framgents
    1921           0 :       mFramePtr += mFragmentAccumulator;
    1922           0 :     } else if (totalAvail) {
    1923           0 :       LOG(("WebSocketChannel:: Setup Buffer due to partial frame"));
    1924           0 :       if (!UpdateReadBuffer(mFramePtr, totalAvail, 0, nullptr)) {
    1925           0 :         return NS_ERROR_FILE_TOO_BIG;
    1926             :       }
    1927             :     }
    1928           0 :   } else if (!mFragmentAccumulator && !totalAvail) {
    1929             :     // If we were working off a saved buffer state and there is no partial
    1930             :     // frame or fragment in process, then revert to stack behavior
    1931           0 :     LOG(("WebSocketChannel:: Internal buffering not needed anymore"));
    1932           0 :     mBuffered = 0;
    1933             : 
    1934             :     // release memory if we've been processing a large message
    1935           0 :     if (mBufferSize > kIncomingBufferStableSize) {
    1936           0 :       mBufferSize = kIncomingBufferStableSize;
    1937           0 :       free(mBuffer);
    1938           0 :       mBuffer = (uint8_t *)moz_xmalloc(mBufferSize);
    1939             :     }
    1940             :   }
    1941           0 :   return NS_OK;
    1942             : }
    1943             : 
    1944             : /* static */ void
    1945           0 : WebSocketChannel::ApplyMask(uint32_t mask, uint8_t *data, uint64_t len)
    1946             : {
    1947           0 :   if (!data || len == 0)
    1948           0 :     return;
    1949             : 
    1950             :   // Optimally we want to apply the mask 32 bits at a time,
    1951             :   // but the buffer might not be alligned. So we first deal with
    1952             :   // 0 to 3 bytes of preamble individually
    1953             : 
    1954           0 :   while (len && (reinterpret_cast<uintptr_t>(data) & 3)) {
    1955           0 :     *data ^= mask >> 24;
    1956           0 :     mask = RotateLeft(mask, 8);
    1957           0 :     data++;
    1958           0 :     len--;
    1959             :   }
    1960             : 
    1961             :   // perform mask on full words of data
    1962             : 
    1963           0 :   uint32_t *iData = (uint32_t *) data;
    1964           0 :   uint32_t *end = iData + (len / 4);
    1965           0 :   NetworkEndian::writeUint32(&mask, mask);
    1966           0 :   for (; iData < end; iData++)
    1967           0 :     *iData ^= mask;
    1968           0 :   mask = NetworkEndian::readUint32(&mask);
    1969           0 :   data = (uint8_t *)iData;
    1970           0 :   len  = len % 4;
    1971             : 
    1972             :   // There maybe up to 3 trailing bytes that need to be dealt with
    1973             :   // individually
    1974             : 
    1975           0 :   while (len) {
    1976           0 :     *data ^= mask >> 24;
    1977           0 :     mask = RotateLeft(mask, 8);
    1978           0 :     data++;
    1979           0 :     len--;
    1980             :   }
    1981             : }
    1982             : 
    1983             : void
    1984           0 : WebSocketChannel::GeneratePing()
    1985             : {
    1986           0 :   nsCString *buf = new nsCString();
    1987           0 :   buf->AssignLiteral("PING");
    1988           0 :   EnqueueOutgoingMessage(mOutgoingPingMessages,
    1989           0 :                          new OutboundMessage(kMsgTypePing, buf));
    1990           0 : }
    1991             : 
    1992             : void
    1993           0 : WebSocketChannel::GeneratePong(uint8_t *payload, uint32_t len)
    1994             : {
    1995           0 :   nsCString *buf = new nsCString();
    1996           0 :   buf->SetLength(len);
    1997           0 :   if (buf->Length() < len) {
    1998           0 :     LOG(("WebSocketChannel::GeneratePong Allocation Failure\n"));
    1999           0 :     delete buf;
    2000           0 :     return;
    2001             :   }
    2002             : 
    2003           0 :   memcpy(buf->BeginWriting(), payload, len);
    2004           0 :   EnqueueOutgoingMessage(mOutgoingPongMessages,
    2005           0 :                          new OutboundMessage(kMsgTypePong, buf));
    2006             : }
    2007             : 
    2008             : void
    2009           0 : WebSocketChannel::EnqueueOutgoingMessage(nsDeque &aQueue,
    2010             :                                          OutboundMessage *aMsg)
    2011             : {
    2012           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2013             : 
    2014           0 :   LOG(("WebSocketChannel::EnqueueOutgoingMessage %p "
    2015             :        "queueing msg %p [type=%s len=%d]\n",
    2016             :        this, aMsg, msgNames[aMsg->GetMsgType()], aMsg->Length()));
    2017             : 
    2018           0 :   aQueue.Push(aMsg);
    2019           0 :   OnOutputStreamReady(mSocketOut);
    2020           0 : }
    2021             : 
    2022             : 
    2023             : uint16_t
    2024           0 : WebSocketChannel::ResultToCloseCode(nsresult resultCode)
    2025             : {
    2026           0 :   if (NS_SUCCEEDED(resultCode))
    2027           0 :     return CLOSE_NORMAL;
    2028             : 
    2029           0 :   switch (resultCode) {
    2030             :     case NS_ERROR_FILE_TOO_BIG:
    2031             :     case NS_ERROR_OUT_OF_MEMORY:
    2032           0 :       return CLOSE_TOO_LARGE;
    2033             :     case NS_ERROR_CANNOT_CONVERT_DATA:
    2034           0 :       return CLOSE_INVALID_PAYLOAD;
    2035             :     case NS_ERROR_UNEXPECTED:
    2036           0 :       return CLOSE_INTERNAL_ERROR;
    2037             :     default:
    2038           0 :       return CLOSE_PROTOCOL_ERROR;
    2039             :   }
    2040             : }
    2041             : 
    2042             : void
    2043           0 : WebSocketChannel::PrimeNewOutgoingMessage()
    2044             : {
    2045           0 :   LOG(("WebSocketChannel::PrimeNewOutgoingMessage() %p\n", this));
    2046           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2047           0 :   MOZ_ASSERT(!mCurrentOut, "Current message in progress");
    2048             : 
    2049           0 :   nsresult rv = NS_OK;
    2050             : 
    2051           0 :   mCurrentOut = (OutboundMessage *)mOutgoingPongMessages.PopFront();
    2052           0 :   if (mCurrentOut) {
    2053           0 :     MOZ_ASSERT(mCurrentOut->GetMsgType() == kMsgTypePong,
    2054             :                "Not pong message!");
    2055             :   } else {
    2056           0 :     mCurrentOut = (OutboundMessage *)mOutgoingPingMessages.PopFront();
    2057           0 :     if (mCurrentOut)
    2058           0 :       MOZ_ASSERT(mCurrentOut->GetMsgType() == kMsgTypePing,
    2059             :                  "Not ping message!");
    2060             :     else
    2061           0 :       mCurrentOut = (OutboundMessage *)mOutgoingMessages.PopFront();
    2062             :   }
    2063             : 
    2064           0 :   if (!mCurrentOut)
    2065           0 :     return;
    2066             : 
    2067           0 :   WsMsgType msgType = mCurrentOut->GetMsgType();
    2068             : 
    2069           0 :   LOG(("WebSocketChannel::PrimeNewOutgoingMessage "
    2070             :        "%p found queued msg %p [type=%s len=%d]\n",
    2071             :        this, mCurrentOut, msgNames[msgType], mCurrentOut->Length()));
    2072             : 
    2073           0 :   mCurrentOutSent = 0;
    2074           0 :   mHdrOut = mOutHeader;
    2075             : 
    2076           0 :   uint8_t maskBit = mIsServerSide ? 0 : kMaskBit;
    2077           0 :   uint8_t maskSize = mIsServerSide ? 0 : 4;
    2078             : 
    2079           0 :   uint8_t *payload = nullptr;
    2080             : 
    2081           0 :   if (msgType == kMsgTypeFin) {
    2082             :     // This is a demand to create a close message
    2083           0 :     if (mClientClosed) {
    2084           0 :       DeleteCurrentOutGoingMessage();
    2085           0 :       PrimeNewOutgoingMessage();
    2086           0 :       return;
    2087             :     }
    2088             : 
    2089           0 :     mClientClosed = 1;
    2090           0 :     mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_CLOSE;
    2091           0 :     mOutHeader[1] = maskBit;
    2092             : 
    2093             :     // payload is offset 2 plus size of the mask
    2094           0 :     payload = mOutHeader + 2 + maskSize;
    2095             : 
    2096             :     // The close reason code sits in the first 2 bytes of payload
    2097             :     // If the channel user provided a code and reason during Close()
    2098             :     // and there isn't an internal error, use that.
    2099           0 :     if (NS_SUCCEEDED(mStopOnClose)) {
    2100           0 :       if (mScriptCloseCode) {
    2101           0 :         NetworkEndian::writeUint16(payload, mScriptCloseCode);
    2102           0 :         mOutHeader[1] += 2;
    2103           0 :         mHdrOutToSend = 4 + maskSize;
    2104           0 :         if (!mScriptCloseReason.IsEmpty()) {
    2105           0 :           MOZ_ASSERT(mScriptCloseReason.Length() <= 123,
    2106             :                      "Close Reason Too Long");
    2107           0 :           mOutHeader[1] += mScriptCloseReason.Length();
    2108           0 :           mHdrOutToSend += mScriptCloseReason.Length();
    2109           0 :           memcpy (payload + 2,
    2110           0 :                   mScriptCloseReason.BeginReading(),
    2111           0 :                   mScriptCloseReason.Length());
    2112             :         }
    2113             :       } else {
    2114             :         // No close code/reason, so payload length = 0.  We must still send mask
    2115             :         // even though it's not used.  Keep payload offset so we write mask
    2116             :         // below.
    2117           0 :         mHdrOutToSend = 2 + maskSize;
    2118             :       }
    2119             :     } else {
    2120           0 :       NetworkEndian::writeUint16(payload, ResultToCloseCode(mStopOnClose));
    2121           0 :       mOutHeader[1] += 2;
    2122           0 :       mHdrOutToSend = 4 + maskSize;
    2123             :     }
    2124             : 
    2125           0 :     if (mServerClosed) {
    2126             :       /* bidi close complete */
    2127           0 :       mReleaseOnTransmit = 1;
    2128           0 :     } else if (NS_FAILED(mStopOnClose)) {
    2129             :       /* result of abort session - give up */
    2130           0 :       StopSession(mStopOnClose);
    2131             :     } else {
    2132             :       /* wait for reciprocal close from server */
    2133           0 :       mCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    2134           0 :       if (NS_SUCCEEDED(rv)) {
    2135           0 :         mCloseTimer->InitWithCallback(this, mCloseTimeout,
    2136           0 :                                       nsITimer::TYPE_ONE_SHOT);
    2137             :       } else {
    2138           0 :         StopSession(rv);
    2139             :       }
    2140             :     }
    2141             :   } else {
    2142           0 :     switch (msgType) {
    2143             :     case kMsgTypePong:
    2144           0 :       mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_PONG;
    2145           0 :       break;
    2146             :     case kMsgTypePing:
    2147           0 :       mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_PING;
    2148           0 :       break;
    2149             :     case kMsgTypeString:
    2150           0 :       mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_TEXT;
    2151           0 :       break;
    2152             :     case kMsgTypeStream:
    2153             :       // HACK ALERT:  read in entire stream into string.
    2154             :       // Will block socket transport thread if file is blocking.
    2155             :       // TODO: bug 704447:  don't block socket thread!
    2156           0 :       rv = mCurrentOut->ConvertStreamToString();
    2157           0 :       if (NS_FAILED(rv)) {
    2158           0 :         AbortSession(NS_ERROR_FILE_TOO_BIG);
    2159           0 :         return;
    2160             :       }
    2161             :       // Now we're a binary string
    2162           0 :       msgType = kMsgTypeBinaryString;
    2163             : 
    2164             :       // no break: fall down into binary string case
    2165             :       MOZ_FALLTHROUGH;
    2166             : 
    2167             :     case kMsgTypeBinaryString:
    2168           0 :       mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_BINARY;
    2169           0 :       break;
    2170             :     case kMsgTypeFin:
    2171           0 :       MOZ_ASSERT(false, "unreachable");  // avoid compiler warning
    2172             :       break;
    2173             :     }
    2174             : 
    2175             :     // deflate the payload if PMCE is negotiated
    2176           0 :     if (mPMCECompressor &&
    2177           0 :         (msgType == kMsgTypeString || msgType == kMsgTypeBinaryString)) {
    2178           0 :       if (mCurrentOut->DeflatePayload(mPMCECompressor)) {
    2179             :         // The payload was deflated successfully, set RSV1 bit
    2180           0 :         mOutHeader[0] |= kRsv1Bit;
    2181             : 
    2182           0 :         LOG(("WebSocketChannel::PrimeNewOutgoingMessage %p current msg %p was "
    2183             :              "deflated [origLength=%d, newLength=%d].\n", this, mCurrentOut,
    2184             :              mCurrentOut->OrigLength(), mCurrentOut->Length()));
    2185             :       }
    2186             :     }
    2187             : 
    2188           0 :     if (mCurrentOut->Length() < 126) {
    2189           0 :       mOutHeader[1] = mCurrentOut->Length() | maskBit;
    2190           0 :       mHdrOutToSend = 2 + maskSize;
    2191           0 :     } else if (mCurrentOut->Length() <= 0xffff) {
    2192           0 :       mOutHeader[1] = 126 | maskBit;
    2193           0 :       NetworkEndian::writeUint16(mOutHeader + sizeof(uint16_t),
    2194           0 :                                  mCurrentOut->Length());
    2195           0 :       mHdrOutToSend = 4 + maskSize;
    2196             :     } else {
    2197           0 :       mOutHeader[1] = 127 | maskBit;
    2198           0 :       NetworkEndian::writeUint64(mOutHeader + 2, mCurrentOut->Length());
    2199           0 :       mHdrOutToSend = 10 + maskSize;
    2200             :     }
    2201           0 :     payload = mOutHeader + mHdrOutToSend;
    2202             :   }
    2203             : 
    2204           0 :   MOZ_ASSERT(payload, "payload offset not found");
    2205             : 
    2206           0 :   uint32_t mask = 0;
    2207           0 :   if (!mIsServerSide) {
    2208             :     // Perform the sending mask. Never use a zero mask
    2209           0 :     do {
    2210             :       uint8_t *buffer;
    2211             :       static_assert(4 == sizeof(mask), "Size of the mask should be equal to 4");
    2212           0 :       nsresult rv = mRandomGenerator->GenerateRandomBytes(sizeof(mask),
    2213           0 :                                                           &buffer);
    2214           0 :       if (NS_FAILED(rv)) {
    2215           0 :         LOG(("WebSocketChannel::PrimeNewOutgoingMessage(): "
    2216             :              "GenerateRandomBytes failure %" PRIx32 "\n", static_cast<uint32_t>(rv)));
    2217           0 :         StopSession(rv);
    2218           0 :         return;
    2219             :       }
    2220           0 :       memcpy(&mask, buffer, sizeof(mask));
    2221           0 :       free(buffer);
    2222           0 :     } while (!mask);
    2223           0 :     NetworkEndian::writeUint32(payload - sizeof(uint32_t), mask);
    2224             :   }
    2225             : 
    2226           0 :   LOG(("WebSocketChannel::PrimeNewOutgoingMessage() using mask %08x\n", mask));
    2227             : 
    2228             :   // We don't mask the framing, but occasionally we stick a little payload
    2229             :   // data in the buffer used for the framing. Close frames are the current
    2230             :   // example. This data needs to be masked, but it is never more than a
    2231             :   // handful of bytes and might rotate the mask, so we can just do it locally.
    2232             :   // For real data frames we ship the bulk of the payload off to ApplyMask()
    2233             : 
    2234             :    RefPtr<WebSocketFrame> frame =
    2235           0 :      mService->CreateFrameIfNeeded(
    2236           0 :                            mOutHeader[0] & WebSocketChannel::kFinalFragBit,
    2237           0 :                            mOutHeader[0] & WebSocketChannel::kRsv1Bit,
    2238           0 :                            mOutHeader[0] & WebSocketChannel::kRsv2Bit,
    2239           0 :                            mOutHeader[0] & WebSocketChannel::kRsv3Bit,
    2240           0 :                            mOutHeader[0] & WebSocketChannel::kOpcodeBitsMask,
    2241           0 :                            mOutHeader[1] & WebSocketChannel::kMaskBit,
    2242             :                            mask,
    2243           0 :                            payload, mHdrOutToSend - (payload - mOutHeader),
    2244           0 :                            mCurrentOut->BeginOrigReading(),
    2245           0 :                            mCurrentOut->OrigLength());
    2246             : 
    2247           0 :   if (frame) {
    2248           0 :     mService->FrameSent(mSerial, mInnerWindowID, frame.forget());
    2249             :   }
    2250             : 
    2251           0 :   if (mask) {
    2252           0 :     while (payload < (mOutHeader + mHdrOutToSend)) {
    2253           0 :       *payload ^= mask >> 24;
    2254           0 :       mask = RotateLeft(mask, 8);
    2255           0 :       payload++;
    2256             :     }
    2257             : 
    2258             :     // Mask the real message payloads
    2259           0 :     ApplyMask(mask, mCurrentOut->BeginWriting(), mCurrentOut->Length());
    2260             :   }
    2261             : 
    2262           0 :   int32_t len = mCurrentOut->Length();
    2263             : 
    2264             :   // for small frames, copy it all together for a contiguous write
    2265           0 :   if (len && len <= kCopyBreak) {
    2266           0 :     memcpy(mOutHeader + mHdrOutToSend, mCurrentOut->BeginWriting(), len);
    2267           0 :     mHdrOutToSend += len;
    2268           0 :     mCurrentOutSent = len;
    2269             :   }
    2270             : 
    2271             :   // Transmitting begins - mHdrOutToSend bytes from mOutHeader and
    2272             :   // mCurrentOut->Length() bytes from mCurrentOut. The latter may be
    2273             :   // coaleseced into the former for small messages or as the result of the
    2274             :   // compression process.
    2275             : }
    2276             : 
    2277             : void
    2278           0 : WebSocketChannel::DeleteCurrentOutGoingMessage()
    2279             : {
    2280           0 :   delete mCurrentOut;
    2281           0 :   mCurrentOut = nullptr;
    2282           0 :   mCurrentOutSent = 0;
    2283           0 : }
    2284             : 
    2285             : void
    2286           0 : WebSocketChannel::EnsureHdrOut(uint32_t size)
    2287             : {
    2288           0 :   LOG(("WebSocketChannel::EnsureHdrOut() %p [%d]\n", this, size));
    2289             : 
    2290           0 :   if (mDynamicOutputSize < size) {
    2291           0 :     mDynamicOutputSize = size;
    2292           0 :     mDynamicOutput =
    2293           0 :       (uint8_t *) moz_xrealloc(mDynamicOutput, mDynamicOutputSize);
    2294             :   }
    2295             : 
    2296           0 :   mHdrOut = mDynamicOutput;
    2297           0 : }
    2298             : 
    2299             : namespace {
    2300             : 
    2301           0 : class RemoveObserverRunnable : public Runnable
    2302             : {
    2303             :   RefPtr<WebSocketChannel> mChannel;
    2304             : 
    2305             : public:
    2306           0 :   explicit RemoveObserverRunnable(WebSocketChannel* aChannel)
    2307           0 :     : Runnable("net::RemoveObserverRunnable")
    2308           0 :     , mChannel(aChannel)
    2309           0 :   {}
    2310             : 
    2311           0 :   NS_IMETHOD Run() override
    2312             :   {
    2313             :     nsCOMPtr<nsIObserverService> observerService =
    2314           0 :       mozilla::services::GetObserverService();
    2315           0 :     if (!observerService) {
    2316           0 :       NS_WARNING("failed to get observer service");
    2317           0 :       return NS_OK;
    2318             :     }
    2319             : 
    2320           0 :     observerService->RemoveObserver(mChannel, NS_NETWORK_LINK_TOPIC);
    2321           0 :     return NS_OK;
    2322             :   }
    2323             : };
    2324             : 
    2325             : } // namespace
    2326             : 
    2327             : void
    2328           0 : WebSocketChannel::CleanupConnection()
    2329             : {
    2330           0 :   LOG(("WebSocketChannel::CleanupConnection() %p", this));
    2331             : 
    2332           0 :   if (mLingeringCloseTimer) {
    2333           0 :     mLingeringCloseTimer->Cancel();
    2334           0 :     mLingeringCloseTimer = nullptr;
    2335             :   }
    2336             : 
    2337           0 :   if (mSocketIn) {
    2338           0 :     mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
    2339           0 :     mSocketIn = nullptr;
    2340             :   }
    2341             : 
    2342           0 :   if (mSocketOut) {
    2343           0 :     mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
    2344           0 :     mSocketOut = nullptr;
    2345             :   }
    2346             : 
    2347           0 :   if (mTransport) {
    2348           0 :     mTransport->SetSecurityCallbacks(nullptr);
    2349           0 :     mTransport->SetEventSink(nullptr, nullptr);
    2350           0 :     mTransport->Close(NS_BASE_STREAM_CLOSED);
    2351           0 :     mTransport = nullptr;
    2352             :   }
    2353             : 
    2354           0 :   if (mConnectionLogService && !mPrivateBrowsing) {
    2355           0 :     mConnectionLogService->RemoveHost(mHost, mSerial);
    2356             :   }
    2357             : 
    2358             :   // This method can run in any thread, but the observer has to be removed on
    2359             :   // the main-thread.
    2360           0 :   NS_DispatchToMainThread(new RemoveObserverRunnable(this));
    2361             : 
    2362           0 :   DecrementSessionCount();
    2363           0 : }
    2364             : 
    2365             : void
    2366           0 : WebSocketChannel::StopSession(nsresult reason)
    2367             : {
    2368           0 :   LOG(("WebSocketChannel::StopSession() %p [%" PRIx32 "]\n",
    2369             :        this, static_cast<uint32_t>(reason)));
    2370             : 
    2371             :   // normally this should be called on socket thread, but it is ok to call it
    2372             :   // from OnStartRequest before the socket thread machine has gotten underway
    2373             : 
    2374           0 :   mStopped = 1;
    2375             : 
    2376           0 :   if (!mOpenedHttpChannel) {
    2377             :     // The HTTP channel information will never be used in this case
    2378           0 :     NS_ReleaseOnMainThread("WebSocketChannel::mChannel", mChannel.forget());
    2379           0 :     NS_ReleaseOnMainThread("WebSocketChannel::mHttpChannel", mHttpChannel.forget());
    2380           0 :     NS_ReleaseOnMainThread("WebSocketChannel::mLoadGroup", mLoadGroup.forget());
    2381           0 :     NS_ReleaseOnMainThread("WebSocketChannel::mCallbacks", mCallbacks.forget());
    2382             :   }
    2383             : 
    2384           0 :   if (mCloseTimer) {
    2385           0 :     mCloseTimer->Cancel();
    2386           0 :     mCloseTimer = nullptr;
    2387             :   }
    2388             : 
    2389           0 :   if (mOpenTimer) {
    2390           0 :     mOpenTimer->Cancel();
    2391           0 :     mOpenTimer = nullptr;
    2392             :   }
    2393             : 
    2394           0 :   if (mReconnectDelayTimer) {
    2395           0 :     mReconnectDelayTimer->Cancel();
    2396           0 :     mReconnectDelayTimer = nullptr;
    2397             :   }
    2398             : 
    2399           0 :   if (mPingTimer) {
    2400           0 :     mPingTimer->Cancel();
    2401           0 :     mPingTimer = nullptr;
    2402             :   }
    2403             : 
    2404           0 :   if (mSocketIn && !mTCPClosed) {
    2405             :     // Drain, within reason, this socket. if we leave any data
    2406             :     // unconsumed (including the tcp fin) a RST will be generated
    2407             :     // The right thing to do here is shutdown(SHUT_WR) and then wait
    2408             :     // a little while to see if any data comes in.. but there is no
    2409             :     // reason to delay things for that when the websocket handshake
    2410             :     // is supposed to guarantee a quiet connection except for that fin.
    2411             : 
    2412             :     char     buffer[512];
    2413           0 :     uint32_t count = 0;
    2414           0 :     uint32_t total = 0;
    2415             :     nsresult rv;
    2416           0 :     do {
    2417           0 :       total += count;
    2418           0 :       rv = mSocketIn->Read(buffer, 512, &count);
    2419           0 :       if (rv != NS_BASE_STREAM_WOULD_BLOCK &&
    2420           0 :         (NS_FAILED(rv) || count == 0))
    2421           0 :         mTCPClosed = true;
    2422           0 :     } while (NS_SUCCEEDED(rv) && count > 0 && total < 32000);
    2423             :   }
    2424             : 
    2425           0 :   int32_t sessionCount = kLingeringCloseThreshold;
    2426           0 :   nsWSAdmissionManager::GetSessionCount(sessionCount);
    2427             : 
    2428           0 :   if (!mTCPClosed && mTransport && sessionCount < kLingeringCloseThreshold) {
    2429             : 
    2430             :     // 7.1.1 says that the client SHOULD wait for the server to close the TCP
    2431             :     // connection. This is so we can reuse port numbers before 2 MSL expires,
    2432             :     // which is not really as much of a concern for us as the amount of state
    2433             :     // that might be accrued by keeping this channel object around waiting for
    2434             :     // the server. We handle the SHOULD by waiting a short time in the common
    2435             :     // case, but not waiting in the case of high concurrency.
    2436             :     //
    2437             :     // Normally this will be taken care of in AbortSession() after mTCPClosed
    2438             :     // is set when the server close arrives without waiting for the timeout to
    2439             :     // expire.
    2440             : 
    2441           0 :     LOG(("WebSocketChannel::StopSession: Wait for Server TCP close"));
    2442             : 
    2443             :     nsresult rv;
    2444           0 :     mLingeringCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    2445           0 :     if (NS_SUCCEEDED(rv))
    2446           0 :       mLingeringCloseTimer->InitWithCallback(this, kLingeringCloseTimeout,
    2447           0 :                                              nsITimer::TYPE_ONE_SHOT);
    2448             :     else
    2449           0 :       CleanupConnection();
    2450             :   } else {
    2451           0 :     CleanupConnection();
    2452             :   }
    2453             : 
    2454           0 :   if (mCancelable) {
    2455           0 :     mCancelable->Cancel(NS_ERROR_UNEXPECTED);
    2456           0 :     mCancelable = nullptr;
    2457             :   }
    2458             : 
    2459           0 :   mPMCECompressor = nullptr;
    2460             : 
    2461           0 :   if (!mCalledOnStop) {
    2462           0 :     mCalledOnStop = 1;
    2463             : 
    2464           0 :     nsWSAdmissionManager::OnStopSession(this, reason);
    2465             : 
    2466           0 :     RefPtr<CallOnStop> runnable = new CallOnStop(this, reason);
    2467           0 :     mTargetThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
    2468             :   }
    2469           0 : }
    2470             : 
    2471             : void
    2472           0 : WebSocketChannel::AbortSession(nsresult reason)
    2473             : {
    2474           0 :   LOG(("WebSocketChannel::AbortSession() %p [reason %" PRIx32 "] stopped = %d\n",
    2475             :        this, static_cast<uint32_t>(reason), !!mStopped));
    2476             : 
    2477             :   // normally this should be called on socket thread, but it is ok to call it
    2478             :   // from the main thread before StartWebsocketData() has completed
    2479             : 
    2480             :   // When we are failing we need to close the TCP connection immediately
    2481             :   // as per 7.1.1
    2482           0 :   mTCPClosed = true;
    2483             : 
    2484           0 :   if (mLingeringCloseTimer) {
    2485           0 :     MOZ_ASSERT(mStopped, "Lingering without Stop");
    2486           0 :     LOG(("WebSocketChannel:: Cleanup connection based on TCP Close"));
    2487           0 :     CleanupConnection();
    2488           0 :     return;
    2489             :   }
    2490             : 
    2491           0 :   if (mStopped)
    2492           0 :     return;
    2493           0 :   mStopped = 1;
    2494             : 
    2495           0 :   if (mTransport && reason != NS_BASE_STREAM_CLOSED && !mRequestedClose &&
    2496           0 :       !mClientClosed && !mServerClosed && mConnecting == NOT_CONNECTING) {
    2497           0 :     mRequestedClose = 1;
    2498           0 :     mStopOnClose = reason;
    2499           0 :     mSocketThread->Dispatch(
    2500           0 :       new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nullptr)),
    2501           0 :                            nsIEventTarget::DISPATCH_NORMAL);
    2502             :   } else {
    2503           0 :     StopSession(reason);
    2504             :   }
    2505             : }
    2506             : 
    2507             : // ReleaseSession is called on orderly shutdown
    2508             : void
    2509           0 : WebSocketChannel::ReleaseSession()
    2510             : {
    2511           0 :   LOG(("WebSocketChannel::ReleaseSession() %p stopped = %d\n",
    2512             :        this, !!mStopped));
    2513           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2514             : 
    2515           0 :   if (mStopped)
    2516           0 :     return;
    2517           0 :   StopSession(NS_OK);
    2518             : }
    2519             : 
    2520             : void
    2521           0 : WebSocketChannel::IncrementSessionCount()
    2522             : {
    2523           0 :   if (!mIncrementedSessionCount) {
    2524           0 :     nsWSAdmissionManager::IncrementSessionCount();
    2525           0 :     mIncrementedSessionCount = 1;
    2526             :   }
    2527           0 : }
    2528             : 
    2529             : void
    2530           0 : WebSocketChannel::DecrementSessionCount()
    2531             : {
    2532             :   // Make sure we decrement session count only once, and only if we incremented it.
    2533             :   // This code is thread-safe: sWebSocketAdmissions->DecrementSessionCount is
    2534             :   // atomic, and mIncrementedSessionCount/mDecrementedSessionCount are set at
    2535             :   // times when they'll never be a race condition for checking/setting them.
    2536           0 :   if (mIncrementedSessionCount && !mDecrementedSessionCount) {
    2537           0 :     nsWSAdmissionManager::DecrementSessionCount();
    2538           0 :     mDecrementedSessionCount = 1;
    2539             :   }
    2540           0 : }
    2541             : 
    2542             : namespace {
    2543             : enum ExtensionParseMode { eParseServerSide, eParseClientSide };
    2544             : }
    2545             : 
    2546             : static nsresult
    2547           0 : ParseWebSocketExtension(const nsACString& aExtension,
    2548             :                         ExtensionParseMode aMode,
    2549             :                         bool& aClientNoContextTakeover,
    2550             :                         bool& aServerNoContextTakeover,
    2551             :                         int32_t& aClientMaxWindowBits,
    2552             :                         int32_t& aServerMaxWindowBits)
    2553             : {
    2554           0 :   nsCCharSeparatedTokenizer tokens(aExtension, ';');
    2555             : 
    2556           0 :   if (!tokens.hasMoreTokens() ||
    2557           0 :       !tokens.nextToken().Equals(NS_LITERAL_CSTRING("permessage-deflate"))) {
    2558           0 :     LOG(("WebSocketChannel::ParseWebSocketExtension: "
    2559             :          "HTTP Sec-WebSocket-Extensions negotiated unknown value %s\n",
    2560             :          PromiseFlatCString(aExtension).get()));
    2561           0 :     return NS_ERROR_ILLEGAL_VALUE;
    2562             :   }
    2563             : 
    2564           0 :   aClientNoContextTakeover = aServerNoContextTakeover = false;
    2565           0 :   aClientMaxWindowBits = aServerMaxWindowBits = -1;
    2566             : 
    2567           0 :   while (tokens.hasMoreTokens()) {
    2568           0 :     auto token = tokens.nextToken();
    2569             : 
    2570             :     int32_t nameEnd, valueStart;
    2571           0 :     int32_t delimPos = token.FindChar('=');
    2572           0 :     if (delimPos == kNotFound) {
    2573           0 :       nameEnd = token.Length();
    2574           0 :       valueStart = token.Length();
    2575             :     } else {
    2576           0 :       nameEnd = delimPos;
    2577           0 :       valueStart = delimPos + 1;
    2578             :     }
    2579             : 
    2580           0 :     auto paramName = Substring(token, 0, nameEnd);
    2581           0 :     auto paramValue = Substring(token, valueStart);
    2582             : 
    2583           0 :     if (paramName.EqualsLiteral("client_no_context_takeover")) {
    2584           0 :       if (!paramValue.IsEmpty()) {
    2585           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: parameter "
    2586             :              "client_no_context_takeover must not have value, found %s\n",
    2587             :              PromiseFlatCString(paramValue).get()));
    2588           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2589             :       }
    2590           0 :       if (aClientNoContextTakeover) {
    2591           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: found multiple "
    2592             :              "parameters client_no_context_takeover\n"));
    2593           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2594             :       }
    2595           0 :       aClientNoContextTakeover = true;
    2596           0 :     } else if (paramName.EqualsLiteral("server_no_context_takeover")) {
    2597           0 :       if (!paramValue.IsEmpty()) {
    2598           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: parameter "
    2599             :              "server_no_context_takeover must not have value, found %s\n",
    2600             :              PromiseFlatCString(paramValue).get()));
    2601           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2602             :       }
    2603           0 :       if (aServerNoContextTakeover) {
    2604           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: found multiple "
    2605             :              "parameters server_no_context_takeover\n"));
    2606           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2607             :       }
    2608           0 :       aServerNoContextTakeover = true;
    2609           0 :     } else if (paramName.EqualsLiteral("client_max_window_bits")) {
    2610           0 :       if (aClientMaxWindowBits != -1) {
    2611           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: found multiple "
    2612             :              "parameters client_max_window_bits\n"));
    2613           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2614             :       }
    2615             : 
    2616           0 :       if (aMode == eParseServerSide && paramValue.IsEmpty()) {
    2617             :         // Use -2 to indicate that "client_max_window_bits" has been parsed,
    2618             :         // but had no value.
    2619           0 :         aClientMaxWindowBits = -2;
    2620             :       }
    2621             :       else {
    2622             :         nsresult errcode;
    2623           0 :         aClientMaxWindowBits =
    2624           0 :           PromiseFlatCString(paramValue).ToInteger(&errcode);
    2625           0 :         if (NS_FAILED(errcode) || aClientMaxWindowBits < 8 ||
    2626           0 :             aClientMaxWindowBits > 15) {
    2627           0 :           LOG(("WebSocketChannel::ParseWebSocketExtension: found invalid "
    2628             :                "parameter client_max_window_bits %s\n",
    2629             :                PromiseFlatCString(paramValue).get()));
    2630           0 :           return NS_ERROR_ILLEGAL_VALUE;
    2631             :         }
    2632             :       }
    2633           0 :     } else if (paramName.EqualsLiteral("server_max_window_bits")) {
    2634           0 :       if (aServerMaxWindowBits != -1) {
    2635           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: found multiple "
    2636             :              "parameters server_max_window_bits\n"));
    2637           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2638             :       }
    2639             : 
    2640             :       nsresult errcode;
    2641           0 :       aServerMaxWindowBits =
    2642           0 :         PromiseFlatCString(paramValue).ToInteger(&errcode);
    2643           0 :       if (NS_FAILED(errcode) || aServerMaxWindowBits < 8 ||
    2644           0 :           aServerMaxWindowBits > 15) {
    2645           0 :         LOG(("WebSocketChannel::ParseWebSocketExtension: found invalid "
    2646             :              "parameter server_max_window_bits %s\n",
    2647             :              PromiseFlatCString(paramValue).get()));
    2648           0 :         return NS_ERROR_ILLEGAL_VALUE;
    2649             :       }
    2650             :     } else {
    2651           0 :       LOG(("WebSocketChannel::ParseWebSocketExtension: found unknown "
    2652             :            "parameter %s\n", PromiseFlatCString(paramName).get()));
    2653           0 :       return NS_ERROR_ILLEGAL_VALUE;
    2654             :     }
    2655             :   }
    2656             : 
    2657           0 :   if (aClientMaxWindowBits == -2) {
    2658           0 :     aClientMaxWindowBits = -1;
    2659             :   }
    2660             : 
    2661           0 :   return NS_OK;
    2662             : }
    2663             : 
    2664             : nsresult
    2665           0 : WebSocketChannel::HandleExtensions()
    2666             : {
    2667           0 :   LOG(("WebSocketChannel::HandleExtensions() %p\n", this));
    2668             : 
    2669             :   nsresult rv;
    2670           0 :   nsAutoCString extensions;
    2671             : 
    2672           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    2673             : 
    2674           0 :   rv = mHttpChannel->GetResponseHeader(
    2675           0 :     NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions);
    2676           0 :   extensions.CompressWhitespace();
    2677           0 :   if (extensions.IsEmpty()) {
    2678           0 :     return NS_OK;
    2679             :   }
    2680             : 
    2681           0 :   LOG(("WebSocketChannel::HandleExtensions: received "
    2682             :        "Sec-WebSocket-Extensions header: %s\n", extensions.get()));
    2683             : 
    2684             :   bool clientNoContextTakeover;
    2685             :   bool serverNoContextTakeover;
    2686             :   int32_t clientMaxWindowBits;
    2687             :   int32_t serverMaxWindowBits;
    2688             : 
    2689             :   rv = ParseWebSocketExtension(extensions,
    2690             :                                eParseClientSide,
    2691             :                                clientNoContextTakeover,
    2692             :                                serverNoContextTakeover,
    2693             :                                clientMaxWindowBits,
    2694           0 :                                serverMaxWindowBits);
    2695           0 :   if (NS_FAILED(rv)) {
    2696           0 :     AbortSession(rv);
    2697           0 :     return rv;
    2698             :   }
    2699             : 
    2700           0 :   if (!mAllowPMCE) {
    2701           0 :     LOG(("WebSocketChannel::HandleExtensions: "
    2702             :          "Recvd permessage-deflate which wasn't offered\n"));
    2703           0 :     AbortSession(NS_ERROR_ILLEGAL_VALUE);
    2704           0 :     return NS_ERROR_ILLEGAL_VALUE;
    2705             :   }
    2706             : 
    2707           0 :   if (clientMaxWindowBits == -1) {
    2708           0 :     clientMaxWindowBits = 15;
    2709             :   }
    2710           0 :   if (serverMaxWindowBits == -1) {
    2711           0 :     serverMaxWindowBits = 15;
    2712             :   }
    2713             : 
    2714             :   mPMCECompressor = new PMCECompression(clientNoContextTakeover,
    2715             :                                         clientMaxWindowBits,
    2716           0 :                                         serverMaxWindowBits);
    2717           0 :   if (mPMCECompressor->Active()) {
    2718           0 :     LOG(("WebSocketChannel::HandleExtensions: PMCE negotiated, %susing "
    2719             :          "context takeover, clientMaxWindowBits=%d, "
    2720             :          "serverMaxWindowBits=%d\n",
    2721             :          clientNoContextTakeover ? "NOT " : "", clientMaxWindowBits,
    2722             :          serverMaxWindowBits));
    2723             : 
    2724           0 :     mNegotiatedExtensions = "permessage-deflate";
    2725             :   } else {
    2726           0 :     LOG(("WebSocketChannel::HandleExtensions: Cannot init PMCE "
    2727             :          "compression object\n"));
    2728           0 :     mPMCECompressor = nullptr;
    2729           0 :     AbortSession(NS_ERROR_UNEXPECTED);
    2730           0 :     return NS_ERROR_UNEXPECTED;
    2731             :   }
    2732             : 
    2733           0 :   return NS_OK;
    2734             : }
    2735             : 
    2736             : void
    2737           0 : ProcessServerWebSocketExtensions(const nsACString& aExtensions,
    2738             :                                  nsACString& aNegotiatedExtensions)
    2739             : {
    2740           0 :   aNegotiatedExtensions.Truncate();
    2741             : 
    2742             :   nsCOMPtr<nsIPrefBranch> prefService =
    2743           0 :     do_GetService(NS_PREFSERVICE_CONTRACTID);
    2744           0 :   if (prefService) {
    2745             :     bool boolpref;
    2746           0 :     nsresult rv = prefService->
    2747           0 :       GetBoolPref("network.websocket.extensions.permessage-deflate", &boolpref);
    2748           0 :     if (NS_SUCCEEDED(rv) && !boolpref) {
    2749           0 :       return;
    2750             :     }
    2751             :   }
    2752             : 
    2753           0 :   nsCCharSeparatedTokenizer extList(aExtensions, ',');
    2754           0 :   while (extList.hasMoreTokens()) {
    2755             :     bool clientNoContextTakeover;
    2756             :     bool serverNoContextTakeover;
    2757             :     int32_t clientMaxWindowBits;
    2758             :     int32_t serverMaxWindowBits;
    2759             : 
    2760           0 :     nsresult rv = ParseWebSocketExtension(extList.nextToken(),
    2761             :                                           eParseServerSide,
    2762             :                                           clientNoContextTakeover,
    2763             :                                           serverNoContextTakeover,
    2764             :                                           clientMaxWindowBits,
    2765           0 :                                           serverMaxWindowBits);
    2766           0 :     if (NS_FAILED(rv)) {
    2767             :       // Ignore extensions that we can't parse
    2768           0 :       continue;
    2769             :     }
    2770             : 
    2771           0 :     aNegotiatedExtensions.AssignLiteral("permessage-deflate");
    2772           0 :     if (clientNoContextTakeover) {
    2773           0 :       aNegotiatedExtensions.AppendLiteral(";client_no_context_takeover");
    2774             :     }
    2775           0 :     if (serverNoContextTakeover) {
    2776           0 :       aNegotiatedExtensions.AppendLiteral(";server_no_context_takeover");
    2777             :     }
    2778           0 :     if (clientMaxWindowBits != -1) {
    2779           0 :       aNegotiatedExtensions.AppendLiteral(";client_max_window_bits=");
    2780           0 :       aNegotiatedExtensions.AppendInt(clientMaxWindowBits);
    2781             :     }
    2782           0 :     if (serverMaxWindowBits != -1) {
    2783           0 :       aNegotiatedExtensions.AppendLiteral(";server_max_window_bits=");
    2784           0 :       aNegotiatedExtensions.AppendInt(serverMaxWindowBits);
    2785             :     }
    2786             : 
    2787           0 :     return;
    2788             :   }
    2789             : }
    2790             : 
    2791             : nsresult
    2792           0 : CalculateWebSocketHashedSecret(const nsACString& aKey, nsACString& aHash)
    2793             : {
    2794             :   nsresult rv;
    2795             :   nsCString key =
    2796           0 :     aKey + NS_LITERAL_CSTRING("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    2797             :   nsCOMPtr<nsICryptoHash> hasher =
    2798           0 :     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    2799           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2800           0 :   rv = hasher->Init(nsICryptoHash::SHA1);
    2801           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2802           0 :   rv = hasher->Update((const uint8_t *)key.BeginWriting(), key.Length());
    2803           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2804           0 :   return hasher->Finish(true, aHash);
    2805             : }
    2806             : 
    2807             : nsresult
    2808           0 : WebSocketChannel::SetupRequest()
    2809             : {
    2810           0 :   LOG(("WebSocketChannel::SetupRequest() %p\n", this));
    2811             : 
    2812             :   nsresult rv;
    2813             : 
    2814           0 :   if (mLoadGroup) {
    2815           0 :     rv = mHttpChannel->SetLoadGroup(mLoadGroup);
    2816           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2817             :   }
    2818             : 
    2819           0 :   rv = mHttpChannel->SetLoadFlags(nsIRequest::LOAD_BACKGROUND |
    2820             :                                   nsIRequest::INHIBIT_CACHING |
    2821             :                                   nsIRequest::LOAD_BYPASS_CACHE |
    2822           0 :                                   nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
    2823           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2824             : 
    2825             :   // we never let websockets be blocked by head CSS/JS loads to avoid
    2826             :   // potential deadlock where server generation of CSS/JS requires
    2827             :   // an XHR signal.
    2828           0 :   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
    2829           0 :   if (cos) {
    2830           0 :     cos->AddClassFlags(nsIClassOfService::Unblocked);
    2831             :   }
    2832             : 
    2833             :   // draft-ietf-hybi-thewebsocketprotocol-07 illustrates Upgrade: websocket
    2834             :   // in lower case, so go with that. It is technically case insensitive.
    2835           0 :   rv = mChannel->HTTPUpgrade(NS_LITERAL_CSTRING("websocket"), this);
    2836           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2837             : 
    2838           0 :   rv = mHttpChannel->SetRequestHeader(
    2839           0 :     NS_LITERAL_CSTRING("Sec-WebSocket-Version"),
    2840           0 :     NS_LITERAL_CSTRING(SEC_WEBSOCKET_VERSION), false);
    2841           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2842             : 
    2843           0 :   if (!mOrigin.IsEmpty()) {
    2844           0 :     rv = mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), mOrigin,
    2845           0 :                                         false);
    2846           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2847             :   }
    2848             : 
    2849           0 :   if (!mProtocol.IsEmpty()) {
    2850           0 :     rv = mHttpChannel->SetRequestHeader(
    2851           0 :       NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), mProtocol, true);
    2852           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2853             :   }
    2854             : 
    2855           0 :   if (mAllowPMCE) {
    2856           0 :     rv = mHttpChannel->SetRequestHeader(
    2857           0 :       NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
    2858           0 :       NS_LITERAL_CSTRING("permessage-deflate"), false);
    2859           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2860             :   }
    2861             : 
    2862             :   uint8_t      *secKey;
    2863           0 :   nsAutoCString secKeyString;
    2864             : 
    2865           0 :   rv = mRandomGenerator->GenerateRandomBytes(16, &secKey);
    2866           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2867           0 :   char* b64 = PL_Base64Encode((const char *)secKey, 16, nullptr);
    2868           0 :   free(secKey);
    2869           0 :   if (!b64)
    2870           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2871           0 :   secKeyString.Assign(b64);
    2872           0 :   PR_Free(b64); // PL_Base64Encode() uses PR_Malloc.
    2873           0 :   rv = mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Key"),
    2874           0 :                                       secKeyString, false);
    2875           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2876           0 :   LOG(("WebSocketChannel::SetupRequest: client key %s\n", secKeyString.get()));
    2877             : 
    2878             :   // prepare the value we expect to see in
    2879             :   // the sec-websocket-accept response header
    2880           0 :   rv = CalculateWebSocketHashedSecret(secKeyString, mHashedSecret);
    2881           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2882           0 :   LOG(("WebSocketChannel::SetupRequest: expected server key %s\n",
    2883             :        mHashedSecret.get()));
    2884             : 
    2885           0 :   return NS_OK;
    2886             : }
    2887             : 
    2888             : nsresult
    2889           0 : WebSocketChannel::DoAdmissionDNS()
    2890             : {
    2891             :   nsresult rv;
    2892             : 
    2893           0 :   nsCString hostName;
    2894           0 :   rv = mURI->GetHost(hostName);
    2895           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2896           0 :   mAddress = hostName;
    2897           0 :   rv = mURI->GetPort(&mPort);
    2898           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2899           0 :   if (mPort == -1)
    2900           0 :     mPort = (mEncrypted ? kDefaultWSSPort : kDefaultWSPort);
    2901           0 :   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
    2902           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2903           0 :   nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
    2904           0 :   MOZ_ASSERT(!mCancelable);
    2905           0 :   return dns->AsyncResolveNative(hostName, 0, this,
    2906           0 :                                  main, mLoadInfo->GetOriginAttributes(),
    2907           0 :                                  getter_AddRefs(mCancelable));
    2908             : }
    2909             : 
    2910             : nsresult
    2911           0 : WebSocketChannel::ApplyForAdmission()
    2912             : {
    2913           0 :   LOG(("WebSocketChannel::ApplyForAdmission() %p\n", this));
    2914             : 
    2915             :   // Websockets has a policy of 1 session at a time being allowed in the
    2916             :   // CONNECTING state per server IP address (not hostname)
    2917             : 
    2918             :   // Check to see if a proxy is being used before making DNS call
    2919             :   nsCOMPtr<nsIProtocolProxyService> pps =
    2920           0 :     do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
    2921             : 
    2922           0 :   if (!pps) {
    2923             :     // go straight to DNS
    2924             :     // expect the callback in ::OnLookupComplete
    2925           0 :     LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
    2926           0 :     return DoAdmissionDNS();
    2927             :   }
    2928             : 
    2929           0 :   MOZ_ASSERT(!mCancelable);
    2930             : 
    2931             :   nsresult rv;
    2932           0 :   rv = pps->AsyncResolve(mHttpChannel,
    2933             :                          nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
    2934             :                          nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
    2935           0 :                          this, nullptr, getter_AddRefs(mCancelable));
    2936           0 :   NS_ASSERTION(NS_FAILED(rv) || mCancelable,
    2937             :                "nsIProtocolProxyService::AsyncResolve succeeded but didn't "
    2938             :                "return a cancelable object!");
    2939           0 :   return rv;
    2940             : }
    2941             : 
    2942             : // Called after both OnStartRequest and OnTransportAvailable have
    2943             : // executed. This essentially ends the handshake and starts the websockets
    2944             : // protocol state machine.
    2945             : nsresult
    2946           0 : WebSocketChannel::StartWebsocketData()
    2947             : {
    2948             :   nsresult rv;
    2949             : 
    2950           0 :   if (!IsOnTargetThread()) {
    2951           0 :     return mTargetThread->Dispatch(
    2952           0 :       NewRunnableMethod("net::WebSocketChannel::StartWebsocketData",
    2953             :                         this,
    2954             :                         &WebSocketChannel::StartWebsocketData),
    2955           0 :       NS_DISPATCH_NORMAL);
    2956             :   }
    2957             : 
    2958           0 :   LOG(("WebSocketChannel::StartWebsocketData() %p", this));
    2959           0 :   MOZ_ASSERT(!mDataStarted, "StartWebsocketData twice");
    2960           0 :   mDataStarted = 1;
    2961             : 
    2962           0 :   rv = mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
    2963           0 :   if (NS_FAILED(rv)) {
    2964           0 :     LOG(("WebSocketChannel::StartWebsocketData mSocketIn->AsyncWait() failed "
    2965             :          "with error 0x%08" PRIx32, static_cast<uint32_t>(rv)));
    2966           0 :     return mSocketThread->Dispatch(
    2967           0 :       NewRunnableMethod<nsresult>("net::WebSocketChannel::AbortSession",
    2968             :                                   this,
    2969             :                                   &WebSocketChannel::AbortSession,
    2970             :                                   rv),
    2971           0 :       NS_DISPATCH_NORMAL);
    2972             :   }
    2973             : 
    2974           0 :   if (mPingInterval) {
    2975           0 :     rv = mSocketThread->Dispatch(
    2976           0 :       NewRunnableMethod("net::WebSocketChannel::StartPinging",
    2977             :                         this,
    2978             :                         &WebSocketChannel::StartPinging),
    2979           0 :       NS_DISPATCH_NORMAL);
    2980           0 :     if (NS_FAILED(rv)) {
    2981           0 :       LOG(("WebSocketChannel::StartWebsocketData Could not start pinging, "
    2982             :            "rv=0x%08" PRIx32, static_cast<uint32_t>(rv)));
    2983           0 :       return rv;
    2984             :     }
    2985             :   }
    2986             : 
    2987           0 :   LOG(("WebSocketChannel::StartWebsocketData Notifying Listener %p",
    2988             :        mListenerMT ? mListenerMT->mListener.get() : nullptr));
    2989             : 
    2990           0 :   if (mListenerMT) {
    2991           0 :     rv = mListenerMT->mListener->OnStart(mListenerMT->mContext);
    2992           0 :     if (NS_FAILED(rv)) {
    2993           0 :       LOG(("WebSocketChannel::StartWebsocketData "
    2994             :            "mListenerMT->mListener->OnStart() failed with error 0x%08" PRIx32,
    2995             :            static_cast<uint32_t>(rv)));
    2996             :     }
    2997             :   }
    2998             : 
    2999           0 :   return NS_OK;
    3000             : }
    3001             : 
    3002             : nsresult
    3003           0 : WebSocketChannel::StartPinging()
    3004             : {
    3005           0 :   LOG(("WebSocketChannel::StartPinging() %p", this));
    3006           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3007           0 :   MOZ_ASSERT(mPingInterval);
    3008           0 :   MOZ_ASSERT(!mPingTimer);
    3009             : 
    3010             :   nsresult rv;
    3011           0 :   mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    3012           0 :   if (NS_FAILED(rv)) {
    3013           0 :     NS_WARNING("unable to create ping timer. Carrying on.");
    3014             :   } else {
    3015           0 :     LOG(("WebSocketChannel will generate ping after %d ms of receive silence\n",
    3016             :          mPingInterval));
    3017           0 :     mPingTimer->InitWithCallback(this, mPingInterval, nsITimer::TYPE_ONE_SHOT);
    3018             :   }
    3019             : 
    3020           0 :   return NS_OK;
    3021             : }
    3022             : 
    3023             : 
    3024             : void
    3025           0 : WebSocketChannel::ReportConnectionTelemetry()
    3026             : {
    3027             :   // 3 bits are used. high bit is for wss, middle bit for failed,
    3028             :   // and low bit for proxy..
    3029             :   // 0 - 7 : ws-ok-plain, ws-ok-proxy, ws-failed-plain, ws-failed-proxy,
    3030             :   //         wss-ok-plain, wss-ok-proxy, wss-failed-plain, wss-failed-proxy
    3031             : 
    3032           0 :   bool didProxy = false;
    3033             : 
    3034           0 :   nsCOMPtr<nsIProxyInfo> pi;
    3035           0 :   nsCOMPtr<nsIProxiedChannel> pc = do_QueryInterface(mChannel);
    3036           0 :   if (pc)
    3037           0 :     pc->GetProxyInfo(getter_AddRefs(pi));
    3038           0 :   if (pi) {
    3039           0 :     nsAutoCString proxyType;
    3040           0 :     pi->GetType(proxyType);
    3041           0 :     if (!proxyType.IsEmpty() &&
    3042           0 :         !proxyType.EqualsLiteral("direct"))
    3043           0 :       didProxy = true;
    3044             :   }
    3045             : 
    3046           0 :   uint8_t value = (mEncrypted ? (1 << 2) : 0) |
    3047           0 :     (!mGotUpgradeOK ? (1 << 1) : 0) |
    3048           0 :     (didProxy ? (1 << 0) : 0);
    3049             : 
    3050           0 :   LOG(("WebSocketChannel::ReportConnectionTelemetry() %p %d", this, value));
    3051           0 :   Telemetry::Accumulate(Telemetry::WEBSOCKETS_HANDSHAKE_TYPE, value);
    3052           0 : }
    3053             : 
    3054             : // nsIDNSListener
    3055             : 
    3056             : NS_IMETHODIMP
    3057           0 : WebSocketChannel::OnLookupComplete(nsICancelable *aRequest,
    3058             :                                    nsIDNSRecord *aRecord,
    3059             :                                    nsresult aStatus)
    3060             : {
    3061           0 :   LOG(("WebSocketChannel::OnLookupComplete() %p [%p %p %" PRIx32 "]\n",
    3062             :        this, aRequest, aRecord, static_cast<uint32_t>(aStatus)));
    3063             : 
    3064           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3065             : 
    3066           0 :   if (mStopped) {
    3067           0 :     LOG(("WebSocketChannel::OnLookupComplete: Request Already Stopped\n"));
    3068           0 :     mCancelable = nullptr;
    3069           0 :     return NS_OK;
    3070             :   }
    3071             : 
    3072           0 :   mCancelable = nullptr;
    3073             : 
    3074             :   // These failures are not fatal - we just use the hostname as the key
    3075           0 :   if (NS_FAILED(aStatus)) {
    3076           0 :     LOG(("WebSocketChannel::OnLookupComplete: No DNS Response\n"));
    3077             : 
    3078             :     // set host in case we got here without calling DoAdmissionDNS()
    3079           0 :     mURI->GetHost(mAddress);
    3080             :   } else {
    3081           0 :     nsresult rv = aRecord->GetNextAddrAsString(mAddress);
    3082           0 :     if (NS_FAILED(rv))
    3083           0 :       LOG(("WebSocketChannel::OnLookupComplete: Failed GetNextAddr\n"));
    3084             :   }
    3085             : 
    3086           0 :   LOG(("WebSocket OnLookupComplete: Proceeding to ConditionallyConnect\n"));
    3087           0 :   nsWSAdmissionManager::ConditionallyConnect(this);
    3088             : 
    3089           0 :   return NS_OK;
    3090             : }
    3091             : 
    3092             : // nsIProtocolProxyCallback
    3093             : NS_IMETHODIMP
    3094           0 : WebSocketChannel::OnProxyAvailable(nsICancelable *aRequest, nsIChannel *aChannel,
    3095             :                                    nsIProxyInfo *pi, nsresult status)
    3096             : {
    3097           0 :   if (mStopped) {
    3098           0 :     LOG(("WebSocketChannel::OnProxyAvailable: [%p] Request Already Stopped\n", this));
    3099           0 :     mCancelable = nullptr;
    3100           0 :     return NS_OK;
    3101             :   }
    3102             : 
    3103           0 :   MOZ_ASSERT(!mCancelable || (aRequest == mCancelable));
    3104           0 :   mCancelable = nullptr;
    3105             : 
    3106           0 :   nsAutoCString type;
    3107           0 :   if (NS_SUCCEEDED(status) && pi &&
    3108           0 :       NS_SUCCEEDED(pi->GetType(type)) &&
    3109           0 :       !type.EqualsLiteral("direct")) {
    3110           0 :     LOG(("WebSocket OnProxyAvailable [%p] Proxy found skip DNS lookup\n", this));
    3111             :     // call DNS callback directly without DNS resolver
    3112           0 :     OnLookupComplete(nullptr, nullptr, NS_ERROR_FAILURE);
    3113             :   } else {
    3114           0 :     LOG(("WebSocketChannel::OnProxyAvailable[%p] checking DNS resolution\n", this));
    3115           0 :     nsresult rv = DoAdmissionDNS();
    3116           0 :     if (NS_FAILED(rv)) {
    3117           0 :       LOG(("WebSocket OnProxyAvailable [%p] DNS lookup failed\n", this));
    3118             :       // call DNS callback directly without DNS resolver
    3119           0 :       OnLookupComplete(nullptr, nullptr, NS_ERROR_FAILURE);
    3120             :     }
    3121             :   }
    3122             : 
    3123           0 :   return NS_OK;
    3124             : }
    3125             : 
    3126             : // nsIInterfaceRequestor
    3127             : 
    3128             : NS_IMETHODIMP
    3129           0 : WebSocketChannel::GetInterface(const nsIID & iid, void **result)
    3130             : {
    3131           0 :   LOG(("WebSocketChannel::GetInterface() %p\n", this));
    3132             : 
    3133           0 :   if (iid.Equals(NS_GET_IID(nsIChannelEventSink)))
    3134           0 :     return QueryInterface(iid, result);
    3135             : 
    3136           0 :   if (mCallbacks)
    3137           0 :     return mCallbacks->GetInterface(iid, result);
    3138             : 
    3139           0 :   return NS_ERROR_FAILURE;
    3140             : }
    3141             : 
    3142             : // nsIChannelEventSink
    3143             : 
    3144             : NS_IMETHODIMP
    3145           0 : WebSocketChannel::AsyncOnChannelRedirect(
    3146             :                     nsIChannel *oldChannel,
    3147             :                     nsIChannel *newChannel,
    3148             :                     uint32_t flags,
    3149             :                     nsIAsyncVerifyRedirectCallback *callback)
    3150             : {
    3151           0 :   LOG(("WebSocketChannel::AsyncOnChannelRedirect() %p\n", this));
    3152             : 
    3153           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3154             : 
    3155             :   nsresult rv;
    3156             : 
    3157           0 :   nsCOMPtr<nsIURI> newuri;
    3158           0 :   rv = newChannel->GetURI(getter_AddRefs(newuri));
    3159           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3160             : 
    3161             :   // newuri is expected to be http or https
    3162           0 :   bool newuriIsHttps = false;
    3163           0 :   rv = newuri->SchemeIs("https", &newuriIsHttps);
    3164           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3165             : 
    3166           0 :   if (!mAutoFollowRedirects) {
    3167             :     // Even if redirects configured off, still allow them for HTTP Strict
    3168             :     // Transport Security (from ws://FOO to https://FOO (mapped to wss://FOO)
    3169             : 
    3170           0 :     if (!(flags & (nsIChannelEventSink::REDIRECT_INTERNAL |
    3171             :                    nsIChannelEventSink::REDIRECT_STS_UPGRADE))) {
    3172           0 :       nsAutoCString newSpec;
    3173           0 :       rv = newuri->GetSpec(newSpec);
    3174           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3175             : 
    3176           0 :       LOG(("WebSocketChannel: Redirect to %s denied by configuration\n",
    3177             :            newSpec.get()));
    3178           0 :       return NS_ERROR_FAILURE;
    3179             :     }
    3180             :   }
    3181             : 
    3182           0 :   if (mEncrypted && !newuriIsHttps) {
    3183           0 :     nsAutoCString spec;
    3184           0 :     if (NS_SUCCEEDED(newuri->GetSpec(spec)))
    3185           0 :       LOG(("WebSocketChannel: Redirect to %s violates encryption rule\n",
    3186             :            spec.get()));
    3187           0 :     return NS_ERROR_FAILURE;
    3188             :   }
    3189             : 
    3190           0 :   nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel, &rv);
    3191           0 :   if (NS_FAILED(rv)) {
    3192           0 :     LOG(("WebSocketChannel: Redirect could not QI to HTTP\n"));
    3193           0 :     return rv;
    3194             :   }
    3195             : 
    3196             :   nsCOMPtr<nsIHttpChannelInternal> newUpgradeChannel =
    3197           0 :     do_QueryInterface(newChannel, &rv);
    3198             : 
    3199           0 :   if (NS_FAILED(rv)) {
    3200           0 :     LOG(("WebSocketChannel: Redirect could not QI to HTTP Upgrade\n"));
    3201           0 :     return rv;
    3202             :   }
    3203             : 
    3204             :   // The redirect is likely OK
    3205             : 
    3206           0 :   newChannel->SetNotificationCallbacks(this);
    3207             : 
    3208           0 :   mEncrypted = newuriIsHttps;
    3209           0 :   newuri->Clone(getter_AddRefs(mURI));
    3210           0 :   if (mEncrypted)
    3211           0 :     rv = mURI->SetScheme(NS_LITERAL_CSTRING("wss"));
    3212             :   else
    3213           0 :     rv = mURI->SetScheme(NS_LITERAL_CSTRING("ws"));
    3214             : 
    3215           0 :   mHttpChannel = newHttpChannel;
    3216           0 :   mChannel = newUpgradeChannel;
    3217           0 :   rv = SetupRequest();
    3218           0 :   if (NS_FAILED(rv)) {
    3219           0 :     LOG(("WebSocketChannel: Redirect could not SetupRequest()\n"));
    3220           0 :     return rv;
    3221             :   }
    3222             : 
    3223             :   // Redirected-to URI may need to be delayed by 1-connecting-per-host and
    3224             :   // delay-after-fail algorithms.  So hold off calling OnRedirectVerifyCallback
    3225             :   // until BeginOpen, when we know it's OK to proceed with new channel.
    3226           0 :   mRedirectCallback = callback;
    3227             : 
    3228             :   // Mark old channel as successfully connected so we'll clear any FailDelay
    3229             :   // associated with the old URI.  Note: no need to also call OnStopSession:
    3230             :   // it's a no-op for successful, already-connected channels.
    3231           0 :   nsWSAdmissionManager::OnConnected(this);
    3232             : 
    3233             :   // ApplyForAdmission as if we were starting from fresh...
    3234           0 :   mAddress.Truncate();
    3235           0 :   mOpenedHttpChannel = 0;
    3236           0 :   rv = ApplyForAdmission();
    3237           0 :   if (NS_FAILED(rv)) {
    3238           0 :     LOG(("WebSocketChannel: Redirect failed due to DNS failure\n"));
    3239           0 :     mRedirectCallback = nullptr;
    3240           0 :     return rv;
    3241             :   }
    3242             : 
    3243           0 :   return NS_OK;
    3244             : }
    3245             : 
    3246             : // nsITimerCallback
    3247             : 
    3248             : NS_IMETHODIMP
    3249           0 : WebSocketChannel::Notify(nsITimer *timer)
    3250             : {
    3251           0 :   LOG(("WebSocketChannel::Notify() %p [%p]\n", this, timer));
    3252             : 
    3253           0 :   if (timer == mCloseTimer) {
    3254           0 :     MOZ_ASSERT(mClientClosed, "Close Timeout without local close");
    3255           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3256             : 
    3257           0 :     mCloseTimer = nullptr;
    3258           0 :     if (mStopped || mServerClosed)                /* no longer relevant */
    3259           0 :       return NS_OK;
    3260             : 
    3261           0 :     LOG(("WebSocketChannel:: Expecting Server Close - Timed Out\n"));
    3262           0 :     AbortSession(NS_ERROR_NET_TIMEOUT);
    3263           0 :   } else if (timer == mOpenTimer) {
    3264           0 :     MOZ_ASSERT(!mGotUpgradeOK,
    3265             :                "Open Timer after open complete");
    3266           0 :     MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3267             : 
    3268           0 :     mOpenTimer = nullptr;
    3269           0 :     LOG(("WebSocketChannel:: Connection Timed Out\n"));
    3270           0 :     if (mStopped || mServerClosed)                /* no longer relevant */
    3271           0 :       return NS_OK;
    3272             : 
    3273           0 :     AbortSession(NS_ERROR_NET_TIMEOUT);
    3274           0 :   } else if (timer == mReconnectDelayTimer) {
    3275           0 :     MOZ_ASSERT(mConnecting == CONNECTING_DELAYED,
    3276             :                "woke up from delay w/o being delayed?");
    3277           0 :     MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3278             : 
    3279           0 :     mReconnectDelayTimer = nullptr;
    3280           0 :     LOG(("WebSocketChannel: connecting [this=%p] after reconnect delay", this));
    3281           0 :     BeginOpen(false);
    3282           0 :   } else if (timer == mPingTimer) {
    3283           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3284             : 
    3285           0 :     if (mClientClosed || mServerClosed || mRequestedClose) {
    3286             :       // no point in worrying about ping now
    3287           0 :       mPingTimer = nullptr;
    3288           0 :       return NS_OK;
    3289             :     }
    3290             : 
    3291           0 :     if (!mPingOutstanding) {
    3292             :       // Ping interval must be non-null or PING was forced by OnNetworkChanged()
    3293           0 :       MOZ_ASSERT(mPingInterval || mPingForced);
    3294           0 :       LOG(("nsWebSocketChannel:: Generating Ping\n"));
    3295           0 :       mPingOutstanding = 1;
    3296           0 :       mPingForced = 0;
    3297           0 :       mPingTimer->InitWithCallback(this, mPingResponseTimeout,
    3298           0 :                                    nsITimer::TYPE_ONE_SHOT);
    3299           0 :       GeneratePing();
    3300             :     } else {
    3301           0 :       LOG(("nsWebSocketChannel:: Timed out Ping\n"));
    3302           0 :       mPingTimer = nullptr;
    3303           0 :       AbortSession(NS_ERROR_NET_TIMEOUT);
    3304             :     }
    3305           0 :   } else if (timer == mLingeringCloseTimer) {
    3306           0 :     LOG(("WebSocketChannel:: Lingering Close Timer"));
    3307           0 :     CleanupConnection();
    3308             :   } else {
    3309           0 :     MOZ_ASSERT(0, "Unknown Timer");
    3310             :   }
    3311             : 
    3312           0 :   return NS_OK;
    3313             : }
    3314             : 
    3315             : // nsIWebSocketChannel
    3316             : 
    3317             : NS_IMETHODIMP
    3318           0 : WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
    3319             : {
    3320           0 :   LOG(("WebSocketChannel::GetSecurityInfo() %p\n", this));
    3321           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3322             : 
    3323           0 :   if (mTransport) {
    3324           0 :     if (NS_FAILED(mTransport->GetSecurityInfo(aSecurityInfo)))
    3325           0 :       *aSecurityInfo = nullptr;
    3326             :   }
    3327           0 :   return NS_OK;
    3328             : }
    3329             : 
    3330             : 
    3331             : NS_IMETHODIMP
    3332           0 : WebSocketChannel::AsyncOpen(nsIURI *aURI,
    3333             :                             const nsACString &aOrigin,
    3334             :                             uint64_t aInnerWindowID,
    3335             :                             nsIWebSocketListener *aListener,
    3336             :                             nsISupports *aContext)
    3337             : {
    3338           0 :   LOG(("WebSocketChannel::AsyncOpen() %p\n", this));
    3339             : 
    3340           0 :   if (!NS_IsMainThread()) {
    3341           0 :     MOZ_ASSERT(false, "not main thread");
    3342             :     LOG(("WebSocketChannel::AsyncOpen() called off the main thread"));
    3343             :     return NS_ERROR_UNEXPECTED;
    3344             :   }
    3345             : 
    3346           0 :   if ((!aURI && !mIsServerSide) || !aListener) {
    3347           0 :     LOG(("WebSocketChannel::AsyncOpen() Uri or Listener null"));
    3348           0 :     return NS_ERROR_UNEXPECTED;
    3349             :   }
    3350             : 
    3351           0 :   if (mListenerMT || mWasOpened)
    3352           0 :     return NS_ERROR_ALREADY_OPENED;
    3353             : 
    3354             :   nsresult rv;
    3355             : 
    3356             :   // Ensure target thread is set.
    3357           0 :   if (!mTargetThread) {
    3358           0 :     mTargetThread = GetMainThreadEventTarget();
    3359             :   }
    3360             : 
    3361           0 :   mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
    3362           0 :   if (NS_FAILED(rv)) {
    3363           0 :     NS_WARNING("unable to continue without socket transport service");
    3364           0 :     return rv;
    3365             :   }
    3366             : 
    3367           0 :   nsCOMPtr<nsIPrefBranch> prefService;
    3368           0 :   prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
    3369             : 
    3370           0 :   if (prefService) {
    3371             :     int32_t intpref;
    3372             :     bool boolpref;
    3373           0 :     rv = prefService->GetIntPref("network.websocket.max-message-size",
    3374           0 :                                  &intpref);
    3375           0 :     if (NS_SUCCEEDED(rv)) {
    3376           0 :       mMaxMessageSize = clamped(intpref, 1024, INT32_MAX);
    3377             :     }
    3378           0 :     rv = prefService->GetIntPref("network.websocket.timeout.close", &intpref);
    3379           0 :     if (NS_SUCCEEDED(rv)) {
    3380           0 :       mCloseTimeout = clamped(intpref, 1, 1800) * 1000;
    3381             :     }
    3382           0 :     rv = prefService->GetIntPref("network.websocket.timeout.open", &intpref);
    3383           0 :     if (NS_SUCCEEDED(rv)) {
    3384           0 :       mOpenTimeout = clamped(intpref, 1, 1800) * 1000;
    3385             :     }
    3386           0 :     rv = prefService->GetIntPref("network.websocket.timeout.ping.request",
    3387           0 :                                  &intpref);
    3388           0 :     if (NS_SUCCEEDED(rv) && !mClientSetPingInterval) {
    3389           0 :       mPingInterval = clamped(intpref, 0, 86400) * 1000;
    3390             :     }
    3391           0 :     rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
    3392           0 :                                  &intpref);
    3393           0 :     if (NS_SUCCEEDED(rv) && !mClientSetPingTimeout) {
    3394           0 :       mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
    3395             :     }
    3396           0 :     rv = prefService->GetBoolPref("network.websocket.extensions.permessage-deflate",
    3397           0 :                                   &boolpref);
    3398           0 :     if (NS_SUCCEEDED(rv)) {
    3399           0 :       mAllowPMCE = boolpref ? 1 : 0;
    3400             :     }
    3401           0 :     rv = prefService->GetBoolPref("network.websocket.auto-follow-http-redirects",
    3402           0 :                                   &boolpref);
    3403           0 :     if (NS_SUCCEEDED(rv)) {
    3404           0 :       mAutoFollowRedirects = boolpref ? 1 : 0;
    3405             :     }
    3406           0 :     rv = prefService->GetIntPref
    3407           0 :       ("network.websocket.max-connections", &intpref);
    3408           0 :     if (NS_SUCCEEDED(rv)) {
    3409           0 :       mMaxConcurrentConnections = clamped(intpref, 1, 0xffff);
    3410             :     }
    3411             :   }
    3412             : 
    3413           0 :   int32_t sessionCount = -1;
    3414           0 :   nsWSAdmissionManager::GetSessionCount(sessionCount);
    3415           0 :   if (sessionCount >= 0) {
    3416           0 :     LOG(("WebSocketChannel::AsyncOpen %p sessionCount=%d max=%d\n", this,
    3417             :          sessionCount, mMaxConcurrentConnections));
    3418             :   }
    3419             : 
    3420           0 :   if (sessionCount >= mMaxConcurrentConnections) {
    3421           0 :     LOG(("WebSocketChannel: max concurrency %d exceeded (%d)",
    3422             :          mMaxConcurrentConnections,
    3423             :          sessionCount));
    3424             : 
    3425             :     // WebSocket connections are expected to be long lived, so return
    3426             :     // an error here instead of queueing
    3427           0 :     return NS_ERROR_SOCKET_CREATE_FAILED;
    3428             :   }
    3429             : 
    3430           0 :   mInnerWindowID = aInnerWindowID;
    3431           0 :   mOriginalURI = aURI;
    3432           0 :   mURI = mOriginalURI;
    3433           0 :   mOrigin = aOrigin;
    3434             : 
    3435           0 :   if (mIsServerSide) {
    3436             :     //IncrementSessionCount();
    3437           0 :     mWasOpened = 1;
    3438           0 :     mListenerMT = new ListenerAndContextContainer(aListener, aContext);
    3439           0 :     rv = mServerTransportProvider->SetListener(this);
    3440           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3441           0 :     mServerTransportProvider = nullptr;
    3442             : 
    3443           0 :     return NS_OK;
    3444             :   }
    3445             : 
    3446           0 :   mURI->GetHostPort(mHost);
    3447             : 
    3448             :   mRandomGenerator =
    3449           0 :     do_GetService("@mozilla.org/security/random-generator;1", &rv);
    3450           0 :   if (NS_FAILED(rv)) {
    3451           0 :     NS_WARNING("unable to continue without random number generator");
    3452           0 :     return rv;
    3453             :   }
    3454             : 
    3455           0 :   nsCOMPtr<nsIURI> localURI;
    3456           0 :   nsCOMPtr<nsIChannel> localChannel;
    3457             : 
    3458           0 :   mURI->Clone(getter_AddRefs(localURI));
    3459           0 :   if (mEncrypted)
    3460           0 :     rv = localURI->SetScheme(NS_LITERAL_CSTRING("https"));
    3461             :   else
    3462           0 :     rv = localURI->SetScheme(NS_LITERAL_CSTRING("http"));
    3463           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3464             : 
    3465           0 :   nsCOMPtr<nsIIOService> ioService;
    3466           0 :   ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
    3467           0 :   if (NS_FAILED(rv)) {
    3468           0 :     NS_WARNING("unable to continue without io service");
    3469           0 :     return rv;
    3470             :   }
    3471             : 
    3472           0 :   nsCOMPtr<nsIIOService2> io2 = do_QueryInterface(ioService, &rv);
    3473           0 :   if (NS_FAILED(rv)) {
    3474           0 :     NS_WARNING("WebSocketChannel: unable to continue without ioservice2");
    3475           0 :     return rv;
    3476             :   }
    3477             : 
    3478             :   // Ideally we'd call newChannelFromURIWithLoadInfo here, but that doesn't
    3479             :   // allow setting proxy uri/flags
    3480           0 :   rv = io2->NewChannelFromURIWithProxyFlags2(
    3481             :               localURI,
    3482             :               mURI,
    3483             :               nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
    3484             :               nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
    3485           0 :               mLoadInfo->LoadingNode() ?
    3486           0 :                 mLoadInfo->LoadingNode()->AsDOMNode() : nullptr,
    3487           0 :               mLoadInfo->LoadingPrincipal(),
    3488           0 :               mLoadInfo->TriggeringPrincipal(),
    3489             :               mLoadInfo->GetSecurityFlags(),
    3490           0 :               mLoadInfo->InternalContentPolicyType(),
    3491           0 :               getter_AddRefs(localChannel));
    3492           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3493             : 
    3494             :   // Please note that we still call SetLoadInfo on the channel because
    3495             :   // we want the same instance of the loadInfo to be set on the channel.
    3496           0 :   rv = localChannel->SetLoadInfo(mLoadInfo);
    3497           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3498             : 
    3499             :   // Pass most GetInterface() requests through to our instantiator, but handle
    3500             :   // nsIChannelEventSink in this object in order to deal with redirects
    3501           0 :   localChannel->SetNotificationCallbacks(this);
    3502             : 
    3503             :   class MOZ_STACK_CLASS CleanUpOnFailure
    3504             :   {
    3505             :   public:
    3506           0 :     explicit CleanUpOnFailure(WebSocketChannel* aWebSocketChannel)
    3507           0 :       : mWebSocketChannel(aWebSocketChannel)
    3508           0 :     {}
    3509             : 
    3510           0 :     ~CleanUpOnFailure()
    3511           0 :     {
    3512           0 :       if (!mWebSocketChannel->mWasOpened) {
    3513           0 :         mWebSocketChannel->mChannel = nullptr;
    3514           0 :         mWebSocketChannel->mHttpChannel = nullptr;
    3515             :       }
    3516           0 :     }
    3517             : 
    3518             :     WebSocketChannel *mWebSocketChannel;
    3519             :   };
    3520             : 
    3521           0 :   CleanUpOnFailure cuof(this);
    3522             : 
    3523           0 :   mChannel = do_QueryInterface(localChannel, &rv);
    3524           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3525             : 
    3526           0 :   mHttpChannel = do_QueryInterface(localChannel, &rv);
    3527           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3528             : 
    3529           0 :   rv = SetupRequest();
    3530           0 :   if (NS_FAILED(rv))
    3531           0 :     return rv;
    3532             : 
    3533           0 :   mPrivateBrowsing = NS_UsePrivateBrowsing(localChannel);
    3534             : 
    3535           0 :   if (mConnectionLogService && !mPrivateBrowsing) {
    3536           0 :     mConnectionLogService->AddHost(mHost, mSerial,
    3537           0 :                                    BaseWebSocketChannel::mEncrypted);
    3538             :   }
    3539             : 
    3540           0 :   rv = ApplyForAdmission();
    3541           0 :   if (NS_FAILED(rv))
    3542           0 :     return rv;
    3543             : 
    3544             :   // Register for prefs change notifications
    3545             :   nsCOMPtr<nsIObserverService> observerService =
    3546           0 :     mozilla::services::GetObserverService();
    3547           0 :   if (!observerService) {
    3548           0 :     NS_WARNING("failed to get observer service");
    3549           0 :     return NS_ERROR_FAILURE;
    3550             :   }
    3551             : 
    3552           0 :   rv = observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
    3553           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3554           0 :     return rv;
    3555             :   }
    3556             : 
    3557             :   // Only set these if the open was successful:
    3558             :   //
    3559           0 :   mWasOpened = 1;
    3560           0 :   mListenerMT = new ListenerAndContextContainer(aListener, aContext);
    3561           0 :   IncrementSessionCount();
    3562             : 
    3563           0 :   return rv;
    3564             : }
    3565             : 
    3566             : NS_IMETHODIMP
    3567           0 : WebSocketChannel::Close(uint16_t code, const nsACString & reason)
    3568             : {
    3569           0 :   LOG(("WebSocketChannel::Close() %p\n", this));
    3570           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3571             : 
    3572           0 :   if (mRequestedClose) {
    3573           0 :     return NS_OK;
    3574             :   }
    3575             : 
    3576             :   // The API requires the UTF-8 string to be 123 or less bytes
    3577           0 :   if (reason.Length() > 123)
    3578           0 :     return NS_ERROR_ILLEGAL_VALUE;
    3579             : 
    3580           0 :   mRequestedClose = 1;
    3581           0 :   mScriptCloseReason = reason;
    3582           0 :   mScriptCloseCode = code;
    3583             : 
    3584           0 :   if (!mTransport || mConnecting != NOT_CONNECTING) {
    3585             :     nsresult rv;
    3586           0 :     if (code == CLOSE_GOING_AWAY) {
    3587             :       // Not an error: for example, tab has closed or navigated away
    3588           0 :       LOG(("WebSocketChannel::Close() GOING_AWAY without transport."));
    3589           0 :       rv = NS_OK;
    3590             :     } else {
    3591           0 :       LOG(("WebSocketChannel::Close() without transport - error."));
    3592           0 :       rv = NS_ERROR_NOT_CONNECTED;
    3593             :     }
    3594           0 :     StopSession(rv);
    3595           0 :     return rv;
    3596             :   }
    3597             : 
    3598           0 :   return mSocketThread->Dispatch(
    3599           0 :       new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nullptr)),
    3600           0 :                            nsIEventTarget::DISPATCH_NORMAL);
    3601             : }
    3602             : 
    3603             : NS_IMETHODIMP
    3604           0 : WebSocketChannel::SendMsg(const nsACString &aMsg)
    3605             : {
    3606           0 :   LOG(("WebSocketChannel::SendMsg() %p\n", this));
    3607             : 
    3608           0 :   return SendMsgCommon(&aMsg, false, aMsg.Length());
    3609             : }
    3610             : 
    3611             : NS_IMETHODIMP
    3612           0 : WebSocketChannel::SendBinaryMsg(const nsACString &aMsg)
    3613             : {
    3614           0 :   LOG(("WebSocketChannel::SendBinaryMsg() %p len=%d\n", this, aMsg.Length()));
    3615           0 :   return SendMsgCommon(&aMsg, true, aMsg.Length());
    3616             : }
    3617             : 
    3618             : NS_IMETHODIMP
    3619           0 : WebSocketChannel::SendBinaryStream(nsIInputStream *aStream, uint32_t aLength)
    3620             : {
    3621           0 :   LOG(("WebSocketChannel::SendBinaryStream() %p\n", this));
    3622             : 
    3623           0 :   return SendMsgCommon(nullptr, true, aLength, aStream);
    3624             : }
    3625             : 
    3626             : nsresult
    3627           0 : WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary,
    3628             :                                 uint32_t aLength, nsIInputStream *aStream)
    3629             : {
    3630           0 :   MOZ_ASSERT(IsOnTargetThread(), "not target thread");
    3631             : 
    3632           0 :   if (!mDataStarted) {
    3633           0 :     LOG(("WebSocketChannel:: Error: data not started yet\n"));
    3634           0 :     return NS_ERROR_UNEXPECTED;
    3635             :   }
    3636             : 
    3637           0 :   if (mRequestedClose) {
    3638           0 :     LOG(("WebSocketChannel:: Error: send when closed\n"));
    3639           0 :     return NS_ERROR_UNEXPECTED;
    3640             :   }
    3641             : 
    3642           0 :   if (mStopped) {
    3643           0 :     LOG(("WebSocketChannel:: Error: send when stopped\n"));
    3644           0 :     return NS_ERROR_NOT_CONNECTED;
    3645             :   }
    3646             : 
    3647           0 :   MOZ_ASSERT(mMaxMessageSize >= 0, "max message size negative");
    3648           0 :   if (aLength > static_cast<uint32_t>(mMaxMessageSize)) {
    3649           0 :     LOG(("WebSocketChannel:: Error: message too big\n"));
    3650           0 :     return NS_ERROR_FILE_TOO_BIG;
    3651             :   }
    3652             : 
    3653           0 :   if (mConnectionLogService && !mPrivateBrowsing) {
    3654           0 :     mConnectionLogService->NewMsgSent(mHost, mSerial, aLength);
    3655           0 :     LOG(("Added new msg sent for %s", mHost.get()));
    3656             :   }
    3657             : 
    3658           0 :   return mSocketThread->Dispatch(
    3659           0 :     aStream ? new OutboundEnqueuer(this, new OutboundMessage(aStream, aLength))
    3660             :             : new OutboundEnqueuer(this,
    3661           0 :                      new OutboundMessage(aIsBinary ? kMsgTypeBinaryString
    3662             :                                                    : kMsgTypeString,
    3663           0 :                                          new nsCString(*aMsg))),
    3664           0 :     nsIEventTarget::DISPATCH_NORMAL);
    3665             : }
    3666             : 
    3667             : // nsIHttpUpgradeListener
    3668             : 
    3669             : NS_IMETHODIMP
    3670           0 : WebSocketChannel::OnTransportAvailable(nsISocketTransport *aTransport,
    3671             :                                        nsIAsyncInputStream *aSocketIn,
    3672             :                                        nsIAsyncOutputStream *aSocketOut)
    3673             : {
    3674           0 :   if (!NS_IsMainThread()) {
    3675             :     return NS_DispatchToMainThread(new CallOnTransportAvailable(this,
    3676             :                                                                 aTransport,
    3677             :                                                                 aSocketIn,
    3678           0 :                                                                 aSocketOut));
    3679             :   }
    3680             : 
    3681           0 :   LOG(("WebSocketChannel::OnTransportAvailable %p [%p %p %p] rcvdonstart=%d\n",
    3682             :        this, aTransport, aSocketIn, aSocketOut, mGotUpgradeOK));
    3683             : 
    3684           0 :   if (mStopped) {
    3685           0 :     LOG(("WebSocketChannel::OnTransportAvailable: Already stopped"));
    3686           0 :     return NS_OK;
    3687             :   }
    3688             : 
    3689           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3690           0 :   MOZ_ASSERT(!mRecvdHttpUpgradeTransport, "OTA duplicated");
    3691           0 :   MOZ_ASSERT(aSocketIn, "OTA with invalid socketIn");
    3692             : 
    3693           0 :   mTransport = aTransport;
    3694           0 :   mSocketIn = aSocketIn;
    3695           0 :   mSocketOut = aSocketOut;
    3696             : 
    3697             :   nsresult rv;
    3698           0 :   rv = mTransport->SetEventSink(nullptr, nullptr);
    3699           0 :   if (NS_FAILED(rv)) return rv;
    3700           0 :   rv = mTransport->SetSecurityCallbacks(this);
    3701           0 :   if (NS_FAILED(rv)) return rv;
    3702             : 
    3703           0 :   mRecvdHttpUpgradeTransport = 1;
    3704           0 :   if (mGotUpgradeOK) {
    3705             :     // We're now done CONNECTING, which means we can now open another,
    3706             :     // perhaps parallel, connection to the same host if one
    3707             :     // is pending
    3708           0 :     nsWSAdmissionManager::OnConnected(this);
    3709             : 
    3710           0 :     return StartWebsocketData();
    3711             :   }
    3712             : 
    3713           0 :   if (mIsServerSide) {
    3714           0 :     if (!mNegotiatedExtensions.IsEmpty()) {
    3715             :       bool clientNoContextTakeover;
    3716             :       bool serverNoContextTakeover;
    3717             :       int32_t clientMaxWindowBits;
    3718             :       int32_t serverMaxWindowBits;
    3719             : 
    3720           0 :       rv = ParseWebSocketExtension(mNegotiatedExtensions,
    3721             :                                    eParseServerSide,
    3722             :                                    clientNoContextTakeover,
    3723             :                                    serverNoContextTakeover,
    3724             :                                    clientMaxWindowBits,
    3725           0 :                                    serverMaxWindowBits);
    3726           0 :       MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "illegal value provided by server");
    3727             : 
    3728           0 :       if (clientMaxWindowBits == -1) {
    3729           0 :         clientMaxWindowBits = 15;
    3730             :       }
    3731           0 :       if (serverMaxWindowBits == -1) {
    3732           0 :         serverMaxWindowBits = 15;
    3733             :       }
    3734             : 
    3735             :       mPMCECompressor = new PMCECompression(serverNoContextTakeover,
    3736             :                                             serverMaxWindowBits,
    3737           0 :                                             clientMaxWindowBits);
    3738           0 :       if (mPMCECompressor->Active()) {
    3739           0 :         LOG(("WebSocketChannel::OnTransportAvailable: PMCE negotiated, %susing "
    3740             :              "context takeover, serverMaxWindowBits=%d, "
    3741             :              "clientMaxWindowBits=%d\n",
    3742             :              serverNoContextTakeover ? "NOT " : "", serverMaxWindowBits,
    3743             :              clientMaxWindowBits));
    3744             : 
    3745           0 :         mNegotiatedExtensions = "permessage-deflate";
    3746             :       } else {
    3747           0 :         LOG(("WebSocketChannel::OnTransportAvailable: Cannot init PMCE "
    3748             :              "compression object\n"));
    3749           0 :         mPMCECompressor = nullptr;
    3750           0 :         AbortSession(NS_ERROR_UNEXPECTED);
    3751           0 :         return NS_ERROR_UNEXPECTED;
    3752             :       }
    3753             :     }
    3754             : 
    3755           0 :     return StartWebsocketData();
    3756             :   }
    3757             : 
    3758           0 :   return NS_OK;
    3759             : }
    3760             : 
    3761             : // nsIRequestObserver (from nsIStreamListener)
    3762             : 
    3763             : NS_IMETHODIMP
    3764           0 : WebSocketChannel::OnStartRequest(nsIRequest *aRequest,
    3765             :                                  nsISupports *aContext)
    3766             : {
    3767           0 :   LOG(("WebSocketChannel::OnStartRequest(): %p [%p %p] recvdhttpupgrade=%d\n",
    3768             :        this, aRequest, mHttpChannel.get(), mRecvdHttpUpgradeTransport));
    3769           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3770           0 :   MOZ_ASSERT(!mGotUpgradeOK, "OTA duplicated");
    3771             : 
    3772           0 :   if (mOpenTimer) {
    3773           0 :     mOpenTimer->Cancel();
    3774           0 :     mOpenTimer = nullptr;
    3775             :   }
    3776             : 
    3777           0 :   if (mStopped) {
    3778           0 :     LOG(("WebSocketChannel::OnStartRequest: Channel Already Done\n"));
    3779           0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    3780           0 :     return NS_ERROR_CONNECTION_REFUSED;
    3781             :   }
    3782             : 
    3783             :   nsresult rv;
    3784             :   uint32_t status;
    3785             :   char *val, *token;
    3786             : 
    3787           0 :   rv = mHttpChannel->GetResponseStatus(&status);
    3788           0 :   if (NS_FAILED(rv)) {
    3789           0 :     LOG(("WebSocketChannel::OnStartRequest: No HTTP Response\n"));
    3790           0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    3791           0 :     return NS_ERROR_CONNECTION_REFUSED;
    3792             :   }
    3793             : 
    3794           0 :   LOG(("WebSocketChannel::OnStartRequest: HTTP status %d\n", status));
    3795           0 :   if (status != 101) {
    3796           0 :     AbortSession(NS_ERROR_CONNECTION_REFUSED);
    3797           0 :     return NS_ERROR_CONNECTION_REFUSED;
    3798             :   }
    3799             : 
    3800           0 :   nsAutoCString respUpgrade;
    3801           0 :   rv = mHttpChannel->GetResponseHeader(
    3802           0 :     NS_LITERAL_CSTRING("Upgrade"), respUpgrade);
    3803             : 
    3804           0 :   if (NS_SUCCEEDED(rv)) {
    3805           0 :     rv = NS_ERROR_ILLEGAL_VALUE;
    3806           0 :     if (!respUpgrade.IsEmpty()) {
    3807           0 :       val = respUpgrade.BeginWriting();
    3808           0 :       while ((token = nsCRT::strtok(val, ", \t", &val))) {
    3809           0 :         if (PL_strcasecmp(token, "Websocket") == 0) {
    3810           0 :           rv = NS_OK;
    3811           0 :           break;
    3812             :         }
    3813             :       }
    3814             :     }
    3815             :   }
    3816             : 
    3817           0 :   if (NS_FAILED(rv)) {
    3818           0 :     LOG(("WebSocketChannel::OnStartRequest: "
    3819             :          "HTTP response header Upgrade: websocket not found\n"));
    3820           0 :     AbortSession(NS_ERROR_ILLEGAL_VALUE);
    3821           0 :     return rv;
    3822             :   }
    3823             : 
    3824           0 :   nsAutoCString respConnection;
    3825           0 :   rv = mHttpChannel->GetResponseHeader(
    3826           0 :     NS_LITERAL_CSTRING("Connection"), respConnection);
    3827             : 
    3828           0 :   if (NS_SUCCEEDED(rv)) {
    3829           0 :     rv = NS_ERROR_ILLEGAL_VALUE;
    3830           0 :     if (!respConnection.IsEmpty()) {
    3831           0 :       val = respConnection.BeginWriting();
    3832           0 :       while ((token = nsCRT::strtok(val, ", \t", &val))) {
    3833           0 :         if (PL_strcasecmp(token, "Upgrade") == 0) {
    3834           0 :           rv = NS_OK;
    3835           0 :           break;
    3836             :         }
    3837             :       }
    3838             :     }
    3839             :   }
    3840             : 
    3841           0 :   if (NS_FAILED(rv)) {
    3842           0 :     LOG(("WebSocketChannel::OnStartRequest: "
    3843             :          "HTTP response header 'Connection: Upgrade' not found\n"));
    3844           0 :     AbortSession(NS_ERROR_ILLEGAL_VALUE);
    3845           0 :     return rv;
    3846             :   }
    3847             : 
    3848           0 :   nsAutoCString respAccept;
    3849           0 :   rv = mHttpChannel->GetResponseHeader(
    3850           0 :                        NS_LITERAL_CSTRING("Sec-WebSocket-Accept"),
    3851           0 :                        respAccept);
    3852             : 
    3853           0 :   if (NS_FAILED(rv) ||
    3854           0 :     respAccept.IsEmpty() || !respAccept.Equals(mHashedSecret)) {
    3855           0 :     LOG(("WebSocketChannel::OnStartRequest: "
    3856             :          "HTTP response header Sec-WebSocket-Accept check failed\n"));
    3857           0 :     LOG(("WebSocketChannel::OnStartRequest: Expected %s received %s\n",
    3858             :          mHashedSecret.get(), respAccept.get()));
    3859           0 :     AbortSession(NS_ERROR_ILLEGAL_VALUE);
    3860           0 :     return NS_ERROR_ILLEGAL_VALUE;
    3861             :   }
    3862             : 
    3863             :   // If we sent a sub protocol header, verify the response matches.
    3864             :   // If response contains protocol that was not in request, fail.
    3865             :   // If response contained no protocol header, set to "" so the protocol
    3866             :   // attribute of the WebSocket JS object reflects that
    3867           0 :   if (!mProtocol.IsEmpty()) {
    3868           0 :     nsAutoCString respProtocol;
    3869           0 :     rv = mHttpChannel->GetResponseHeader(
    3870           0 :                          NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
    3871           0 :                          respProtocol);
    3872           0 :     if (NS_SUCCEEDED(rv)) {
    3873           0 :       rv = NS_ERROR_ILLEGAL_VALUE;
    3874           0 :       val = mProtocol.BeginWriting();
    3875           0 :       while ((token = nsCRT::strtok(val, ", \t", &val))) {
    3876           0 :         if (PL_strcmp(token, respProtocol.get()) == 0) {
    3877           0 :           rv = NS_OK;
    3878           0 :           break;
    3879             :         }
    3880             :       }
    3881             : 
    3882           0 :       if (NS_SUCCEEDED(rv)) {
    3883           0 :         LOG(("WebsocketChannel::OnStartRequest: subprotocol %s confirmed",
    3884             :              respProtocol.get()));
    3885           0 :         mProtocol = respProtocol;
    3886             :       } else {
    3887           0 :         LOG(("WebsocketChannel::OnStartRequest: "
    3888             :              "Server replied with non-matching subprotocol [%s]: aborting",
    3889             :              respProtocol.get()));
    3890           0 :         mProtocol.Truncate();
    3891           0 :         AbortSession(NS_ERROR_ILLEGAL_VALUE);
    3892           0 :         return NS_ERROR_ILLEGAL_VALUE;
    3893             :       }
    3894             :     } else {
    3895           0 :       LOG(("WebsocketChannel::OnStartRequest "
    3896             :                  "subprotocol [%s] not found - none returned",
    3897             :                  mProtocol.get()));
    3898           0 :       mProtocol.Truncate();
    3899             :     }
    3900             :   }
    3901             : 
    3902           0 :   rv = HandleExtensions();
    3903           0 :   if (NS_FAILED(rv))
    3904           0 :     return rv;
    3905             : 
    3906             :   // Update mEffectiveURL for off main thread URI access.
    3907           0 :   nsCOMPtr<nsIURI> uri = mURI ? mURI : mOriginalURI;
    3908           0 :   nsAutoCString spec;
    3909           0 :   rv = uri->GetSpec(spec);
    3910           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    3911           0 :   CopyUTF8toUTF16(spec, mEffectiveURL);
    3912             : 
    3913           0 :   mGotUpgradeOK = 1;
    3914           0 :   if (mRecvdHttpUpgradeTransport) {
    3915             :     // We're now done CONNECTING, which means we can now open another,
    3916             :     // perhaps parallel, connection to the same host if one
    3917             :     // is pending
    3918           0 :     nsWSAdmissionManager::OnConnected(this);
    3919             : 
    3920           0 :     return StartWebsocketData();
    3921             :   }
    3922             : 
    3923           0 :   return NS_OK;
    3924             : }
    3925             : 
    3926             : NS_IMETHODIMP
    3927           0 : WebSocketChannel::OnStopRequest(nsIRequest *aRequest,
    3928             :                                 nsISupports *aContext,
    3929             :                                 nsresult aStatusCode)
    3930             : {
    3931           0 :   LOG(("WebSocketChannel::OnStopRequest() %p [%p %p %" PRIx32 "]\n",
    3932             :        this, aRequest, mHttpChannel.get(), static_cast<uint32_t>(aStatusCode)));
    3933           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
    3934             : 
    3935           0 :   ReportConnectionTelemetry();
    3936             : 
    3937             :   // This is the end of the HTTP upgrade transaction, the
    3938             :   // upgraded streams live on
    3939             : 
    3940           0 :   mChannel = nullptr;
    3941           0 :   mHttpChannel = nullptr;
    3942           0 :   mLoadGroup = nullptr;
    3943           0 :   mCallbacks = nullptr;
    3944             : 
    3945           0 :   return NS_OK;
    3946             : }
    3947             : 
    3948             : // nsIInputStreamCallback
    3949             : 
    3950             : NS_IMETHODIMP
    3951           0 : WebSocketChannel::OnInputStreamReady(nsIAsyncInputStream *aStream)
    3952             : {
    3953           0 :   LOG(("WebSocketChannel::OnInputStreamReady() %p\n", this));
    3954           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3955             : 
    3956           0 :   if (!mSocketIn) // did we we clean up the socket after scheduling InputReady?
    3957           0 :     return NS_OK;
    3958             : 
    3959             :   // this is after the http upgrade - so we are speaking websockets
    3960             :   char  buffer[2048];
    3961             :   uint32_t count;
    3962             :   nsresult rv;
    3963             : 
    3964           0 :   do {
    3965           0 :     rv = mSocketIn->Read((char *)buffer, 2048, &count);
    3966           0 :     LOG(("WebSocketChannel::OnInputStreamReady: read %u rv %" PRIx32 "\n",
    3967             :          count, static_cast<uint32_t>(rv)));
    3968             : 
    3969           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    3970           0 :       mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
    3971           0 :       return NS_OK;
    3972             :     }
    3973             : 
    3974           0 :     if (NS_FAILED(rv)) {
    3975           0 :       mTCPClosed = true;
    3976           0 :       AbortSession(rv);
    3977           0 :       return rv;
    3978             :     }
    3979             : 
    3980           0 :     if (count == 0) {
    3981           0 :       mTCPClosed = true;
    3982           0 :       AbortSession(NS_BASE_STREAM_CLOSED);
    3983           0 :       return NS_OK;
    3984             :     }
    3985             : 
    3986           0 :     if (mStopped) {
    3987           0 :       continue;
    3988             :     }
    3989             : 
    3990           0 :     rv = ProcessInput((uint8_t *)buffer, count);
    3991           0 :     if (NS_FAILED(rv)) {
    3992           0 :       AbortSession(rv);
    3993           0 :       return rv;
    3994             :     }
    3995           0 :   } while (NS_SUCCEEDED(rv) && mSocketIn);
    3996             : 
    3997           0 :   return NS_OK;
    3998             : }
    3999             : 
    4000             : 
    4001             : // nsIOutputStreamCallback
    4002             : 
    4003             : NS_IMETHODIMP
    4004           0 : WebSocketChannel::OnOutputStreamReady(nsIAsyncOutputStream *aStream)
    4005             : {
    4006           0 :   LOG(("WebSocketChannel::OnOutputStreamReady() %p\n", this));
    4007           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4008             :   nsresult rv;
    4009             : 
    4010           0 :   if (!mCurrentOut)
    4011           0 :     PrimeNewOutgoingMessage();
    4012             : 
    4013           0 :   while (mCurrentOut && mSocketOut) {
    4014             :     const char *sndBuf;
    4015             :     uint32_t toSend;
    4016             :     uint32_t amtSent;
    4017             : 
    4018           0 :     if (mHdrOut) {
    4019           0 :       sndBuf = (const char *)mHdrOut;
    4020           0 :       toSend = mHdrOutToSend;
    4021           0 :       LOG(("WebSocketChannel::OnOutputStreamReady: "
    4022             :            "Try to send %u of hdr/copybreak\n", toSend));
    4023             :     } else {
    4024           0 :       sndBuf = (char *) mCurrentOut->BeginReading() + mCurrentOutSent;
    4025           0 :       toSend = mCurrentOut->Length() - mCurrentOutSent;
    4026           0 :       if (toSend > 0) {
    4027           0 :         LOG(("WebSocketChannel::OnOutputStreamReady: "
    4028             :              "Try to send %u of data\n", toSend));
    4029             :       }
    4030             :     }
    4031             : 
    4032           0 :     if (toSend == 0) {
    4033           0 :       amtSent = 0;
    4034             :     } else {
    4035           0 :       rv = mSocketOut->Write(sndBuf, toSend, &amtSent);
    4036           0 :       LOG(("WebSocketChannel::OnOutputStreamReady: write %u rv %" PRIx32 "\n",
    4037             :            amtSent, static_cast<uint32_t>(rv)));
    4038             : 
    4039           0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    4040           0 :         mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
    4041           0 :         return NS_OK;
    4042             :       }
    4043             : 
    4044           0 :       if (NS_FAILED(rv)) {
    4045           0 :         AbortSession(rv);
    4046           0 :         return NS_OK;
    4047             :       }
    4048             :     }
    4049             : 
    4050           0 :     if (mHdrOut) {
    4051           0 :       if (amtSent == toSend) {
    4052           0 :         mHdrOut = nullptr;
    4053           0 :         mHdrOutToSend = 0;
    4054             :       } else {
    4055           0 :         mHdrOut += amtSent;
    4056           0 :         mHdrOutToSend -= amtSent;
    4057           0 :         mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
    4058             :       }
    4059             :     } else {
    4060           0 :       if (amtSent == toSend) {
    4061           0 :         if (!mStopped) {
    4062           0 :           mTargetThread->Dispatch(
    4063           0 :             new CallAcknowledge(this, mCurrentOut->OrigLength()),
    4064           0 :             NS_DISPATCH_NORMAL);
    4065             :         }
    4066           0 :         DeleteCurrentOutGoingMessage();
    4067           0 :         PrimeNewOutgoingMessage();
    4068             :       } else {
    4069           0 :         mCurrentOutSent += amtSent;
    4070           0 :         mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
    4071             :       }
    4072             :     }
    4073             :   }
    4074             : 
    4075           0 :   if (mReleaseOnTransmit)
    4076           0 :     ReleaseSession();
    4077           0 :   return NS_OK;
    4078             : }
    4079             : 
    4080             : // nsIStreamListener
    4081             : 
    4082             : NS_IMETHODIMP
    4083           0 : WebSocketChannel::OnDataAvailable(nsIRequest *aRequest,
    4084             :                                     nsISupports *aContext,
    4085             :                                     nsIInputStream *aInputStream,
    4086             :                                     uint64_t aOffset,
    4087             :                                     uint32_t aCount)
    4088             : {
    4089           0 :   LOG(("WebSocketChannel::OnDataAvailable() %p [%p %p %p %" PRIu64 " %u]\n",
    4090             :          this, aRequest, mHttpChannel.get(), aInputStream, aOffset, aCount));
    4091             : 
    4092             :   // This is the HTTP OnDataAvailable Method, which means this is http data in
    4093             :   // response to the upgrade request and there should be no http response body
    4094             :   // if the upgrade succeeded. This generally should be caught by a non 101
    4095             :   // response code in OnStartRequest().. so we can ignore the data here
    4096             : 
    4097           0 :   LOG(("WebSocketChannel::OnDataAvailable: HTTP data unexpected len>=%u\n",
    4098             :          aCount));
    4099             : 
    4100           0 :   return NS_OK;
    4101             : }
    4102             : 
    4103             : } // namespace net
    4104             : } // namespace mozilla
    4105             : 
    4106             : #undef CLOSE_GOING_AWAY

Generated by: LCOV version 1.13