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 : #ifndef mozilla_net_WebSocketChannel_h
8 : #define mozilla_net_WebSocketChannel_h
9 :
10 : #include "nsISupports.h"
11 : #include "nsIInterfaceRequestor.h"
12 : #include "nsIStreamListener.h"
13 : #include "nsIAsyncInputStream.h"
14 : #include "nsIAsyncOutputStream.h"
15 : #include "nsITimer.h"
16 : #include "nsIDNSListener.h"
17 : #include "nsIObserver.h"
18 : #include "nsIProtocolProxyCallback.h"
19 : #include "nsIChannelEventSink.h"
20 : #include "nsIHttpChannelInternal.h"
21 : #include "nsIStringStream.h"
22 : #include "BaseWebSocketChannel.h"
23 :
24 : #include "nsCOMPtr.h"
25 : #include "nsString.h"
26 : #include "nsDeque.h"
27 : #include "mozilla/Atomics.h"
28 :
29 : class nsIAsyncVerifyRedirectCallback;
30 : class nsIDashboardEventNotifier;
31 : class nsIEventTarget;
32 : class nsIHttpChannel;
33 : class nsIRandomGenerator;
34 : class nsISocketTransport;
35 : class nsIURI;
36 :
37 : namespace mozilla {
38 : namespace net {
39 :
40 : class OutboundMessage;
41 : class OutboundEnqueuer;
42 : class nsWSAdmissionManager;
43 : class PMCECompression;
44 : class CallOnMessageAvailable;
45 : class CallOnStop;
46 : class CallOnServerClose;
47 : class CallAcknowledge;
48 : class WebSocketEventService;
49 :
50 : extern MOZ_MUST_USE nsresult
51 : CalculateWebSocketHashedSecret(const nsACString& aKey, nsACString& aHash);
52 : extern void
53 : ProcessServerWebSocketExtensions(const nsACString& aExtensions,
54 : nsACString& aNegotiatedExtensions);
55 :
56 : // Used to enforce "1 connecting websocket per host" rule, and reconnect delays
57 : enum wsConnectingState {
58 : NOT_CONNECTING = 0, // Not yet (or no longer) trying to open connection
59 : CONNECTING_QUEUED, // Waiting for other ws to same host to finish opening
60 : CONNECTING_DELAYED, // Delayed by "reconnect after failure" algorithm
61 : CONNECTING_IN_PROGRESS // Started connection: waiting for result
62 : };
63 :
64 : class WebSocketChannel : public BaseWebSocketChannel,
65 : public nsIHttpUpgradeListener,
66 : public nsIStreamListener,
67 : public nsIInputStreamCallback,
68 : public nsIOutputStreamCallback,
69 : public nsITimerCallback,
70 : public nsIDNSListener,
71 : public nsIObserver,
72 : public nsIProtocolProxyCallback,
73 : public nsIInterfaceRequestor,
74 : public nsIChannelEventSink
75 : {
76 : friend class WebSocketFrame;
77 :
78 : public:
79 : NS_DECL_THREADSAFE_ISUPPORTS
80 : NS_DECL_NSIHTTPUPGRADELISTENER
81 : NS_DECL_NSIREQUESTOBSERVER
82 : NS_DECL_NSISTREAMLISTENER
83 : NS_DECL_NSIINPUTSTREAMCALLBACK
84 : NS_DECL_NSIOUTPUTSTREAMCALLBACK
85 : NS_DECL_NSITIMERCALLBACK
86 : NS_DECL_NSIDNSLISTENER
87 : NS_DECL_NSIPROTOCOLPROXYCALLBACK
88 : NS_DECL_NSIINTERFACEREQUESTOR
89 : NS_DECL_NSICHANNELEVENTSINK
90 : NS_DECL_NSIOBSERVER
91 :
92 : // nsIWebSocketChannel methods BaseWebSocketChannel didn't implement for us
93 : //
94 : NS_IMETHOD AsyncOpen(nsIURI *aURI,
95 : const nsACString &aOrigin,
96 : uint64_t aWindowID,
97 : nsIWebSocketListener *aListener,
98 : nsISupports *aContext) override;
99 : NS_IMETHOD Close(uint16_t aCode, const nsACString & aReason) override;
100 : NS_IMETHOD SendMsg(const nsACString &aMsg) override;
101 : NS_IMETHOD SendBinaryMsg(const nsACString &aMsg) override;
102 : NS_IMETHOD SendBinaryStream(nsIInputStream *aStream, uint32_t length) override;
103 : NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
104 :
105 : WebSocketChannel();
106 : static void Shutdown();
107 : bool IsOnTargetThread();
108 :
109 : // Off main thread URI access.
110 : void GetEffectiveURL(nsAString& aEffectiveURL) const override;
111 : bool IsEncrypted() const override;
112 :
113 : const static uint32_t kControlFrameMask = 0x8;
114 :
115 : // First byte of the header
116 : const static uint8_t kFinalFragBit = 0x80;
117 : const static uint8_t kRsvBitsMask = 0x70;
118 : const static uint8_t kRsv1Bit = 0x40;
119 : const static uint8_t kRsv2Bit = 0x20;
120 : const static uint8_t kRsv3Bit = 0x10;
121 : const static uint8_t kOpcodeBitsMask = 0x0F;
122 :
123 : // Second byte of the header
124 : const static uint8_t kMaskBit = 0x80;
125 : const static uint8_t kPayloadLengthBitsMask = 0x7F;
126 :
127 : protected:
128 : virtual ~WebSocketChannel();
129 :
130 : private:
131 : friend class OutboundEnqueuer;
132 : friend class nsWSAdmissionManager;
133 : friend class FailDelayManager;
134 : friend class CallOnMessageAvailable;
135 : friend class CallOnStop;
136 : friend class CallOnServerClose;
137 : friend class CallAcknowledge;
138 :
139 : // Common send code for binary + text msgs
140 : MOZ_MUST_USE nsresult SendMsgCommon(const nsACString *aMsg, bool isBinary,
141 : uint32_t length,
142 : nsIInputStream *aStream = nullptr);
143 :
144 : void EnqueueOutgoingMessage(nsDeque &aQueue, OutboundMessage *aMsg);
145 :
146 : void PrimeNewOutgoingMessage();
147 : void DeleteCurrentOutGoingMessage();
148 : void GeneratePong(uint8_t *payload, uint32_t len);
149 : void GeneratePing();
150 :
151 : MOZ_MUST_USE nsresult OnNetworkChanged();
152 : MOZ_MUST_USE nsresult StartPinging();
153 :
154 : void BeginOpen(bool aCalledFromAdmissionManager);
155 : void BeginOpenInternal();
156 : MOZ_MUST_USE nsresult HandleExtensions();
157 : MOZ_MUST_USE nsresult SetupRequest();
158 : MOZ_MUST_USE nsresult ApplyForAdmission();
159 : MOZ_MUST_USE nsresult DoAdmissionDNS();
160 : MOZ_MUST_USE nsresult StartWebsocketData();
161 : uint16_t ResultToCloseCode(nsresult resultCode);
162 : void ReportConnectionTelemetry();
163 :
164 : void StopSession(nsresult reason);
165 : void AbortSession(nsresult reason);
166 : void ReleaseSession();
167 : void CleanupConnection();
168 : void IncrementSessionCount();
169 : void DecrementSessionCount();
170 :
171 : void EnsureHdrOut(uint32_t size);
172 :
173 : static void ApplyMask(uint32_t mask, uint8_t *data, uint64_t len);
174 :
175 : bool IsPersistentFramePtr();
176 : MOZ_MUST_USE nsresult ProcessInput(uint8_t *buffer, uint32_t count);
177 : MOZ_MUST_USE bool UpdateReadBuffer(uint8_t *buffer, uint32_t count,
178 : uint32_t accumulatedFragments,
179 : uint32_t *available);
180 :
181 0 : inline void ResetPingTimer()
182 : {
183 0 : mPingOutstanding = 0;
184 0 : if (mPingTimer) {
185 0 : if (!mPingInterval) {
186 : // The timer was created by forced ping and regular pinging is disabled,
187 : // so cancel and null out mPingTimer.
188 0 : mPingTimer->Cancel();
189 0 : mPingTimer = nullptr;
190 : } else {
191 0 : mPingTimer->SetDelay(mPingInterval);
192 : }
193 : }
194 0 : }
195 :
196 : nsCOMPtr<nsIEventTarget> mSocketThread;
197 : nsCOMPtr<nsIHttpChannelInternal> mChannel;
198 : nsCOMPtr<nsIHttpChannel> mHttpChannel;
199 : nsCOMPtr<nsICancelable> mCancelable;
200 : nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
201 : nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
202 :
203 : nsCString mHashedSecret;
204 :
205 : // Used as key for connection managment: Initially set to hostname from URI,
206 : // then to IP address (unless we're leaving DNS resolution to a proxy server)
207 : nsCString mAddress;
208 : int32_t mPort; // WS server port
209 :
210 : // Used for off main thread access to the URI string.
211 : nsCString mHost;
212 : nsString mEffectiveURL;
213 :
214 : nsCOMPtr<nsISocketTransport> mTransport;
215 : nsCOMPtr<nsIAsyncInputStream> mSocketIn;
216 : nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
217 :
218 : nsCOMPtr<nsITimer> mCloseTimer;
219 : uint32_t mCloseTimeout; /* milliseconds */
220 :
221 : nsCOMPtr<nsITimer> mOpenTimer;
222 : uint32_t mOpenTimeout; /* milliseconds */
223 : wsConnectingState mConnecting; /* 0 if not connecting */
224 : nsCOMPtr<nsITimer> mReconnectDelayTimer;
225 :
226 : nsCOMPtr<nsITimer> mPingTimer;
227 :
228 : nsCOMPtr<nsITimer> mLingeringCloseTimer;
229 : const static int32_t kLingeringCloseTimeout = 1000;
230 : const static int32_t kLingeringCloseThreshold = 50;
231 :
232 : RefPtr<WebSocketEventService> mService;
233 :
234 : int32_t mMaxConcurrentConnections;
235 :
236 : uint64_t mInnerWindowID;
237 :
238 : // following members are accessed only on the main thread
239 : uint32_t mGotUpgradeOK : 1;
240 : uint32_t mRecvdHttpUpgradeTransport : 1;
241 : uint32_t mAutoFollowRedirects : 1;
242 : uint32_t mAllowPMCE : 1;
243 : uint32_t : 0;
244 :
245 : // following members are accessed only on the socket thread
246 : uint32_t mPingOutstanding : 1;
247 : uint32_t mReleaseOnTransmit : 1;
248 : uint32_t : 0;
249 :
250 : Atomic<bool> mDataStarted;
251 : Atomic<bool> mRequestedClose;
252 : Atomic<bool> mClientClosed;
253 : Atomic<bool> mServerClosed;
254 : Atomic<bool> mStopped;
255 : Atomic<bool> mCalledOnStop;
256 : Atomic<bool> mTCPClosed;
257 : Atomic<bool> mOpenedHttpChannel;
258 : Atomic<bool> mIncrementedSessionCount;
259 : Atomic<bool> mDecrementedSessionCount;
260 :
261 : int32_t mMaxMessageSize;
262 : nsresult mStopOnClose;
263 : uint16_t mServerCloseCode;
264 : nsCString mServerCloseReason;
265 : uint16_t mScriptCloseCode;
266 : nsCString mScriptCloseReason;
267 :
268 : // These are for the read buffers
269 : const static uint32_t kIncomingBufferInitialSize = 16 * 1024;
270 : // We're ok with keeping a buffer this size or smaller around for the life of
271 : // the websocket. If a particular message needs bigger than this we'll
272 : // increase the buffer temporarily, then drop back down to this size.
273 : const static uint32_t kIncomingBufferStableSize = 128 * 1024;
274 :
275 : uint8_t *mFramePtr;
276 : uint8_t *mBuffer;
277 : uint8_t mFragmentOpcode;
278 : uint32_t mFragmentAccumulator;
279 : uint32_t mBuffered;
280 : uint32_t mBufferSize;
281 :
282 : // These are for the send buffers
283 : const static int32_t kCopyBreak = 1000;
284 :
285 : OutboundMessage *mCurrentOut;
286 : uint32_t mCurrentOutSent;
287 : nsDeque mOutgoingMessages;
288 : nsDeque mOutgoingPingMessages;
289 : nsDeque mOutgoingPongMessages;
290 : uint32_t mHdrOutToSend;
291 : uint8_t *mHdrOut;
292 : uint8_t mOutHeader[kCopyBreak + 16];
293 : nsAutoPtr<PMCECompression> mPMCECompressor;
294 : uint32_t mDynamicOutputSize;
295 : uint8_t *mDynamicOutput;
296 : bool mPrivateBrowsing;
297 :
298 : nsCOMPtr<nsIDashboardEventNotifier> mConnectionLogService;
299 : };
300 :
301 : class WebSocketSSLChannel : public WebSocketChannel
302 : {
303 : public:
304 0 : WebSocketSSLChannel() { BaseWebSocketChannel::mEncrypted = true; }
305 : protected:
306 0 : virtual ~WebSocketSSLChannel() {}
307 : };
308 :
309 : } // namespace net
310 : } // namespace mozilla
311 :
312 : #endif // mozilla_net_WebSocketChannel_h
|