diff options
Diffstat (limited to 'netwerk/protocol/websocket/WebSocketChannel.h')
-rw-r--r-- | netwerk/protocol/websocket/WebSocketChannel.h | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/netwerk/protocol/websocket/WebSocketChannel.h b/netwerk/protocol/websocket/WebSocketChannel.h new file mode 100644 index 0000000000..950f76d0b1 --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketChannel.h @@ -0,0 +1,315 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_WebSocketChannel_h +#define mozilla_net_WebSocketChannel_h + +#include "nsISupports.h" +#include "nsIInterfaceRequestor.h" +#include "nsIStreamListener.h" +#include "nsIAsyncInputStream.h" +#include "nsIAsyncOutputStream.h" +#include "nsITimer.h" +#include "nsIDNSListener.h" +#include "nsINamed.h" +#include "nsIObserver.h" +#include "nsIProtocolProxyCallback.h" +#include "nsIChannelEventSink.h" +#include "nsIHttpChannelInternal.h" +#include "BaseWebSocketChannel.h" + +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsDeque.h" +#include "mozilla/Atomics.h" + +class nsIAsyncVerifyRedirectCallback; +class nsIDashboardEventNotifier; +class nsIEventTarget; +class nsIHttpChannel; +class nsIRandomGenerator; +class nsISocketTransport; +class nsIURI; + +namespace mozilla { +namespace net { + +class OutboundMessage; +class OutboundEnqueuer; +class nsWSAdmissionManager; +class PMCECompression; +class CallOnMessageAvailable; +class CallOnStop; +class CallOnServerClose; +class CallAcknowledge; +class WebSocketEventService; + +[[nodiscard]] extern nsresult CalculateWebSocketHashedSecret( + const nsACString& aKey, nsACString& aHash); +extern void ProcessServerWebSocketExtensions(const nsACString& aExtensions, + nsACString& aNegotiatedExtensions); + +// Used to enforce "1 connecting websocket per host" rule, and reconnect delays +enum wsConnectingState { + NOT_CONNECTING = 0, // Not yet (or no longer) trying to open connection + CONNECTING_QUEUED, // Waiting for other ws to same host to finish opening + CONNECTING_DELAYED, // Delayed by "reconnect after failure" algorithm + CONNECTING_IN_PROGRESS // Started connection: waiting for result +}; + +class WebSocketChannel : public BaseWebSocketChannel, + public nsIHttpUpgradeListener, + public nsIStreamListener, + public nsIInputStreamCallback, + public nsIOutputStreamCallback, + public nsITimerCallback, + public nsIDNSListener, + public nsIObserver, + public nsIProtocolProxyCallback, + public nsIInterfaceRequestor, + public nsIChannelEventSink, + public nsINamed { + friend class WebSocketFrame; + + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIHTTPUPGRADELISTENER + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIINPUTSTREAMCALLBACK + NS_DECL_NSIOUTPUTSTREAMCALLBACK + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSIDNSLISTENER + NS_DECL_NSIPROTOCOLPROXYCALLBACK + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIOBSERVER + NS_DECL_NSINAMED + + // nsIWebSocketChannel methods BaseWebSocketChannel didn't implement for us + // + NS_IMETHOD AsyncOpen(nsIURI* aURI, const nsACString& aOrigin, + uint64_t aWindowID, nsIWebSocketListener* aListener, + nsISupports* aContext) override; + NS_IMETHOD Close(uint16_t aCode, const nsACString& aReason) override; + NS_IMETHOD SendMsg(const nsACString& aMsg) override; + NS_IMETHOD SendBinaryMsg(const nsACString& aMsg) override; + NS_IMETHOD SendBinaryStream(nsIInputStream* aStream, + uint32_t length) override; + NS_IMETHOD GetSecurityInfo(nsISupports** aSecurityInfo) override; + + WebSocketChannel(); + static void Shutdown(); + bool IsOnTargetThread(); + + // Off main thread URI access. + void GetEffectiveURL(nsAString& aEffectiveURL) const override; + bool IsEncrypted() const override; + + const static uint32_t kControlFrameMask = 0x8; + + // First byte of the header + const static uint8_t kFinalFragBit = 0x80; + const static uint8_t kRsvBitsMask = 0x70; + const static uint8_t kRsv1Bit = 0x40; + const static uint8_t kRsv2Bit = 0x20; + const static uint8_t kRsv3Bit = 0x10; + const static uint8_t kOpcodeBitsMask = 0x0F; + + // Second byte of the header + const static uint8_t kMaskBit = 0x80; + const static uint8_t kPayloadLengthBitsMask = 0x7F; + + protected: + virtual ~WebSocketChannel(); + + private: + friend class OutboundEnqueuer; + friend class nsWSAdmissionManager; + friend class FailDelayManager; + friend class CallOnMessageAvailable; + friend class CallOnStop; + friend class CallOnServerClose; + friend class CallAcknowledge; + + // Common send code for binary + text msgs + [[nodiscard]] nsresult SendMsgCommon(const nsACString& aMsg, bool isBinary, + uint32_t length, + nsIInputStream* aStream = nullptr); + + void EnqueueOutgoingMessage(nsDeque<OutboundMessage>& aQueue, + OutboundMessage* aMsg); + + void PrimeNewOutgoingMessage(); + void DeleteCurrentOutGoingMessage(); + void GeneratePong(uint8_t* payload, uint32_t len); + void GeneratePing(); + + [[nodiscard]] nsresult OnNetworkChanged(); + [[nodiscard]] nsresult StartPinging(); + + void BeginOpen(bool aCalledFromAdmissionManager); + void BeginOpenInternal(); + [[nodiscard]] nsresult HandleExtensions(); + [[nodiscard]] nsresult SetupRequest(); + [[nodiscard]] nsresult ApplyForAdmission(); + [[nodiscard]] nsresult DoAdmissionDNS(); + [[nodiscard]] nsresult CallStartWebsocketData(); + [[nodiscard]] nsresult StartWebsocketData(); + uint16_t ResultToCloseCode(nsresult resultCode); + void ReportConnectionTelemetry(nsresult aStatusCode); + + void StopSession(nsresult reason); + void DoStopSession(nsresult reason); + void AbortSession(nsresult reason); + void ReleaseSession(); + void CleanupConnection(); + void IncrementSessionCount(); + void DecrementSessionCount(); + + void EnsureHdrOut(uint32_t size); + + static void ApplyMask(uint32_t mask, uint8_t* data, uint64_t len); + + bool IsPersistentFramePtr(); + [[nodiscard]] nsresult ProcessInput(uint8_t* buffer, uint32_t count); + [[nodiscard]] bool UpdateReadBuffer(uint8_t* buffer, uint32_t count, + uint32_t accumulatedFragments, + uint32_t* available); + + inline void ResetPingTimer() { + mPingOutstanding = 0; + if (mPingTimer) { + if (!mPingInterval) { + // The timer was created by forced ping and regular pinging is disabled, + // so cancel and null out mPingTimer. + mPingTimer->Cancel(); + mPingTimer = nullptr; + } else { + mPingTimer->SetDelay(mPingInterval); + } + } + } + + nsCOMPtr<nsIEventTarget> mSocketThread; + nsCOMPtr<nsIHttpChannelInternal> mChannel; + nsCOMPtr<nsIHttpChannel> mHttpChannel; + nsCOMPtr<nsICancelable> mCancelable; + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; + nsCOMPtr<nsIRandomGenerator> mRandomGenerator; + + nsCString mHashedSecret; + + // Used as key for connection managment: Initially set to hostname from URI, + // then to IP address (unless we're leaving DNS resolution to a proxy server) + nsCString mAddress; + int32_t mPort; // WS server port + + // Used for off main thread access to the URI string. + nsCString mHost; + nsString mEffectiveURL; + + nsCOMPtr<nsISocketTransport> mTransport; + nsCOMPtr<nsIAsyncInputStream> mSocketIn; + nsCOMPtr<nsIAsyncOutputStream> mSocketOut; + + nsCOMPtr<nsITimer> mCloseTimer; + uint32_t mCloseTimeout; /* milliseconds */ + + nsCOMPtr<nsITimer> mOpenTimer; + uint32_t mOpenTimeout; /* milliseconds */ + wsConnectingState mConnecting; /* 0 if not connecting */ + nsCOMPtr<nsITimer> mReconnectDelayTimer; + + nsCOMPtr<nsITimer> mPingTimer; + + nsCOMPtr<nsITimer> mLingeringCloseTimer; + const static int32_t kLingeringCloseTimeout = 1000; + const static int32_t kLingeringCloseThreshold = 50; + + RefPtr<WebSocketEventService> mService; + + int32_t mMaxConcurrentConnections; + + uint64_t mInnerWindowID; + + // following members are accessed only on the main thread + uint32_t mGotUpgradeOK : 1; + uint32_t mRecvdHttpUpgradeTransport : 1; + uint32_t mAutoFollowRedirects : 1; + uint32_t mAllowPMCE : 1; + uint32_t : 0; + + // following members are accessed only on the socket thread + uint32_t mPingOutstanding : 1; + uint32_t mReleaseOnTransmit : 1; + uint32_t : 0; + + Atomic<bool> mDataStarted; + Atomic<bool> mRequestedClose; + Atomic<bool> mClientClosed; + Atomic<bool> mServerClosed; + Atomic<bool> mStopped; + Atomic<bool> mCalledOnStop; + Atomic<bool> mTCPClosed; + Atomic<bool> mOpenedHttpChannel; + Atomic<bool> mIncrementedSessionCount; + Atomic<bool> mDecrementedSessionCount; + + int32_t mMaxMessageSize; + nsresult mStopOnClose; + uint16_t mServerCloseCode; + nsCString mServerCloseReason; + uint16_t mScriptCloseCode; + nsCString mScriptCloseReason; + + // These are for the read buffers + const static uint32_t kIncomingBufferInitialSize = 16 * 1024; + // We're ok with keeping a buffer this size or smaller around for the life of + // the websocket. If a particular message needs bigger than this we'll + // increase the buffer temporarily, then drop back down to this size. + const static uint32_t kIncomingBufferStableSize = 128 * 1024; + + uint8_t* mFramePtr; + uint8_t* mBuffer; + uint8_t mFragmentOpcode; + uint32_t mFragmentAccumulator; + uint32_t mBuffered; + uint32_t mBufferSize; + + // These are for the send buffers + const static int32_t kCopyBreak = 1000; + + OutboundMessage* mCurrentOut; + uint32_t mCurrentOutSent; + nsDeque<OutboundMessage> mOutgoingMessages; + nsDeque<OutboundMessage> mOutgoingPingMessages; + nsDeque<OutboundMessage> mOutgoingPongMessages; + uint32_t mHdrOutToSend; + uint8_t* mHdrOut; + uint8_t mOutHeader[kCopyBreak + 16]; + UniquePtr<PMCECompression> mPMCECompressor; + uint32_t mDynamicOutputSize; + uint8_t* mDynamicOutput; + bool mPrivateBrowsing; + + nsCOMPtr<nsIDashboardEventNotifier> mConnectionLogService; + + mozilla::Mutex mMutex; +}; + +class WebSocketSSLChannel : public WebSocketChannel { + public: + WebSocketSSLChannel() { BaseWebSocketChannel::mEncrypted = true; } + + protected: + virtual ~WebSocketSSLChannel() = default; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_WebSocketChannel_h |