michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_net_WebSocketChannel_h michael@0: #define mozilla_net_WebSocketChannel_h michael@0: michael@0: #include "nsISupports.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsIAsyncInputStream.h" michael@0: #include "nsIAsyncOutputStream.h" michael@0: #include "nsITimer.h" michael@0: #include "nsIDNSListener.h" michael@0: #include "nsIProtocolProxyCallback.h" michael@0: #include "nsIChannelEventSink.h" michael@0: #include "nsIHttpChannelInternal.h" michael@0: #include "BaseWebSocketChannel.h" michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include "nsINetworkManager.h" michael@0: #include "nsProxyRelease.h" michael@0: #endif michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsDeque.h" michael@0: michael@0: class nsIAsyncVerifyRedirectCallback; michael@0: class nsIDashboardEventNotifier; michael@0: class nsIEventTarget; michael@0: class nsIHttpChannel; michael@0: class nsIRandomGenerator; michael@0: class nsISocketTransport; michael@0: class nsIURI; michael@0: michael@0: namespace mozilla { namespace net { michael@0: michael@0: class OutboundMessage; michael@0: class OutboundEnqueuer; michael@0: class nsWSAdmissionManager; michael@0: class nsWSCompression; michael@0: class CallOnMessageAvailable; michael@0: class CallOnStop; michael@0: class CallOnServerClose; michael@0: class CallAcknowledge; michael@0: michael@0: // Used to enforce "1 connecting websocket per host" rule, and reconnect delays michael@0: enum wsConnectingState { michael@0: NOT_CONNECTING = 0, // Not yet (or no longer) trying to open connection michael@0: CONNECTING_QUEUED, // Waiting for other ws to same host to finish opening michael@0: CONNECTING_DELAYED, // Delayed by "reconnect after failure" algorithm michael@0: CONNECTING_IN_PROGRESS // Started connection: waiting for result michael@0: }; michael@0: michael@0: class WebSocketChannel : public BaseWebSocketChannel, michael@0: public nsIHttpUpgradeListener, michael@0: public nsIStreamListener, michael@0: public nsIInputStreamCallback, michael@0: public nsIOutputStreamCallback, michael@0: public nsITimerCallback, michael@0: public nsIDNSListener, michael@0: public nsIProtocolProxyCallback, michael@0: public nsIInterfaceRequestor, michael@0: public nsIChannelEventSink michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIHTTPUPGRADELISTENER michael@0: NS_DECL_NSIREQUESTOBSERVER michael@0: NS_DECL_NSISTREAMLISTENER michael@0: NS_DECL_NSIINPUTSTREAMCALLBACK michael@0: NS_DECL_NSIOUTPUTSTREAMCALLBACK michael@0: NS_DECL_NSITIMERCALLBACK michael@0: NS_DECL_NSIDNSLISTENER michael@0: NS_DECL_NSIPROTOCOLPROXYCALLBACK michael@0: NS_DECL_NSIINTERFACEREQUESTOR michael@0: NS_DECL_NSICHANNELEVENTSINK michael@0: michael@0: // nsIWebSocketChannel methods BaseWebSocketChannel didn't implement for us michael@0: // michael@0: NS_IMETHOD AsyncOpen(nsIURI *aURI, michael@0: const nsACString &aOrigin, michael@0: nsIWebSocketListener *aListener, michael@0: nsISupports *aContext); michael@0: NS_IMETHOD Close(uint16_t aCode, const nsACString & aReason); michael@0: NS_IMETHOD SendMsg(const nsACString &aMsg); michael@0: NS_IMETHOD SendBinaryMsg(const nsACString &aMsg); michael@0: NS_IMETHOD SendBinaryStream(nsIInputStream *aStream, uint32_t length); michael@0: NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo); michael@0: michael@0: WebSocketChannel(); michael@0: static void Shutdown(); michael@0: michael@0: enum { michael@0: // Non Control Frames michael@0: kContinuation = 0x0, michael@0: kText = 0x1, michael@0: kBinary = 0x2, michael@0: michael@0: // Control Frames michael@0: kClose = 0x8, michael@0: kPing = 0x9, michael@0: kPong = 0xA michael@0: }; michael@0: michael@0: const static uint32_t kControlFrameMask = 0x8; michael@0: const static uint8_t kMaskBit = 0x80; michael@0: const static uint8_t kFinalFragBit = 0x80; michael@0: michael@0: protected: michael@0: virtual ~WebSocketChannel(); michael@0: michael@0: private: michael@0: friend class OutboundEnqueuer; michael@0: friend class nsWSAdmissionManager; michael@0: friend class FailDelayManager; michael@0: friend class CallOnMessageAvailable; michael@0: friend class CallOnStop; michael@0: friend class CallOnServerClose; michael@0: friend class CallAcknowledge; michael@0: michael@0: // Common send code for binary + text msgs michael@0: nsresult SendMsgCommon(const nsACString *aMsg, bool isBinary, michael@0: uint32_t length, nsIInputStream *aStream = nullptr); michael@0: michael@0: void EnqueueOutgoingMessage(nsDeque &aQueue, OutboundMessage *aMsg); michael@0: michael@0: void PrimeNewOutgoingMessage(); michael@0: void DeleteCurrentOutGoingMessage(); michael@0: void GeneratePong(uint8_t *payload, uint32_t len); michael@0: void GeneratePing(); michael@0: michael@0: void BeginOpen(); michael@0: nsresult HandleExtensions(); michael@0: nsresult SetupRequest(); michael@0: nsresult ApplyForAdmission(); michael@0: nsresult DoAdmissionDNS(); michael@0: nsresult StartWebsocketData(); michael@0: uint16_t ResultToCloseCode(nsresult resultCode); michael@0: void ReportConnectionTelemetry(); michael@0: michael@0: void StopSession(nsresult reason); michael@0: void AbortSession(nsresult reason); michael@0: void ReleaseSession(); michael@0: void CleanupConnection(); michael@0: void IncrementSessionCount(); michael@0: void DecrementSessionCount(); michael@0: michael@0: void EnsureHdrOut(uint32_t size); michael@0: void ApplyMask(uint32_t mask, uint8_t *data, uint64_t len); michael@0: michael@0: bool IsPersistentFramePtr(); michael@0: nsresult ProcessInput(uint8_t *buffer, uint32_t count); michael@0: bool UpdateReadBuffer(uint8_t *buffer, uint32_t count, michael@0: uint32_t accumulatedFragments, michael@0: uint32_t *available); michael@0: michael@0: inline void ResetPingTimer() michael@0: { michael@0: if (mPingTimer) { michael@0: mPingOutstanding = 0; michael@0: mPingTimer->SetDelay(mPingInterval); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr mSocketThread; michael@0: nsCOMPtr mChannel; michael@0: nsCOMPtr mHttpChannel; michael@0: nsCOMPtr mCancelable; michael@0: nsCOMPtr mRedirectCallback; michael@0: nsCOMPtr mRandomGenerator; michael@0: michael@0: nsCString mHashedSecret; michael@0: michael@0: // Used as key for connection managment: Initially set to hostname from URI, michael@0: // then to IP address (unless we're leaving DNS resolution to a proxy server) michael@0: nsCString mAddress; michael@0: int32_t mPort; // WS server port michael@0: michael@0: // Used for off main thread access to the URI string. michael@0: nsCString mHost; michael@0: michael@0: nsCOMPtr mTransport; michael@0: nsCOMPtr mSocketIn; michael@0: nsCOMPtr mSocketOut; michael@0: michael@0: nsCOMPtr mCloseTimer; michael@0: uint32_t mCloseTimeout; /* milliseconds */ michael@0: michael@0: nsCOMPtr mOpenTimer; michael@0: uint32_t mOpenTimeout; /* milliseconds */ michael@0: wsConnectingState mConnecting; /* 0 if not connecting */ michael@0: nsCOMPtr mReconnectDelayTimer; michael@0: michael@0: nsCOMPtr mPingTimer; michael@0: michael@0: nsCOMPtr mLingeringCloseTimer; michael@0: const static int32_t kLingeringCloseTimeout = 1000; michael@0: const static int32_t kLingeringCloseThreshold = 50; michael@0: michael@0: int32_t mMaxConcurrentConnections; michael@0: michael@0: uint32_t mGotUpgradeOK : 1; michael@0: uint32_t mRecvdHttpUpgradeTransport : 1; michael@0: uint32_t mRequestedClose : 1; michael@0: uint32_t mClientClosed : 1; michael@0: uint32_t mServerClosed : 1; michael@0: uint32_t mStopped : 1; michael@0: uint32_t mCalledOnStop : 1; michael@0: uint32_t mPingOutstanding : 1; michael@0: uint32_t mAllowCompression : 1; michael@0: uint32_t mAutoFollowRedirects : 1; michael@0: uint32_t mReleaseOnTransmit : 1; michael@0: uint32_t mTCPClosed : 1; michael@0: uint32_t mOpenedHttpChannel : 1; michael@0: uint32_t mDataStarted : 1; michael@0: uint32_t mIncrementedSessionCount : 1; michael@0: uint32_t mDecrementedSessionCount : 1; michael@0: michael@0: int32_t mMaxMessageSize; michael@0: nsresult mStopOnClose; michael@0: uint16_t mServerCloseCode; michael@0: nsCString mServerCloseReason; michael@0: uint16_t mScriptCloseCode; michael@0: nsCString mScriptCloseReason; michael@0: michael@0: // These are for the read buffers michael@0: const static uint32_t kIncomingBufferInitialSize = 16 * 1024; michael@0: // We're ok with keeping a buffer this size or smaller around for the life of michael@0: // the websocket. If a particular message needs bigger than this we'll michael@0: // increase the buffer temporarily, then drop back down to this size. michael@0: const static uint32_t kIncomingBufferStableSize = 128 * 1024; michael@0: michael@0: uint8_t *mFramePtr; michael@0: uint8_t *mBuffer; michael@0: uint8_t mFragmentOpcode; michael@0: uint32_t mFragmentAccumulator; michael@0: uint32_t mBuffered; michael@0: uint32_t mBufferSize; michael@0: nsCOMPtr mInflateReader; michael@0: nsCOMPtr mInflateStream; michael@0: michael@0: // These are for the send buffers michael@0: const static int32_t kCopyBreak = 1000; michael@0: michael@0: OutboundMessage *mCurrentOut; michael@0: uint32_t mCurrentOutSent; michael@0: nsDeque mOutgoingMessages; michael@0: nsDeque mOutgoingPingMessages; michael@0: nsDeque mOutgoingPongMessages; michael@0: uint32_t mHdrOutToSend; michael@0: uint8_t *mHdrOut; michael@0: uint8_t mOutHeader[kCopyBreak + 16]; michael@0: nsWSCompression *mCompressor; michael@0: uint32_t mDynamicOutputSize; michael@0: uint8_t *mDynamicOutput; michael@0: bool mPrivateBrowsing; michael@0: michael@0: nsCOMPtr mConnectionLogService; michael@0: uint32_t mSerial; michael@0: static uint32_t sSerialSeed; michael@0: michael@0: // These members are used for network per-app metering (bug 855949) michael@0: // Currently, they are only available on gonk. michael@0: uint64_t mCountRecv; michael@0: uint64_t mCountSent; michael@0: uint32_t mAppId; michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsMainThreadPtrHandle mActiveNetwork; michael@0: #endif michael@0: nsresult SaveNetworkStats(bool); michael@0: void CountRecvBytes(uint64_t recvBytes) michael@0: { michael@0: mCountRecv += recvBytes; michael@0: SaveNetworkStats(false); michael@0: } michael@0: void CountSentBytes(uint64_t sentBytes) michael@0: { michael@0: mCountSent += sentBytes; michael@0: SaveNetworkStats(false); michael@0: } michael@0: }; michael@0: michael@0: class WebSocketSSLChannel : public WebSocketChannel michael@0: { michael@0: public: michael@0: WebSocketSSLChannel() { BaseWebSocketChannel::mEncrypted = true; } michael@0: protected: michael@0: virtual ~WebSocketSSLChannel() {} michael@0: }; michael@0: michael@0: }} // namespace mozilla::net michael@0: michael@0: #endif // mozilla_net_WebSocketChannel_h