Merge mozilla-central to tracemonkey.
[mozilla-central.git] / content / base / src / nsWebSocket.cpp
blobefbfa21486da414fa499ebb641dbad2e3ad4d4d2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Wellington Fernando de Macedo.
20 * Portions created by the Initial Developer are Copyright (C) 2009
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsWebSocket.h"
41 #include "nsIScriptGlobalObject.h"
42 #include "nsIDOMWindow.h"
43 #include "nsIDocument.h"
44 #include "nsXPCOM.h"
45 #include "nsIXPConnect.h"
46 #include "nsContentUtils.h"
47 #include "nsEventDispatcher.h"
48 #include "nsDOMError.h"
49 #include "nsIScriptObjectPrincipal.h"
50 #include "nsIDOMClassInfo.h"
51 #include "nsDOMClassInfo.h"
52 #include "jsapi.h"
53 #include "nsNetUtil.h"
54 #include "nsIStandardURL.h"
55 #include "nsIURL.h"
56 #include "nsIPrivateDOMEvent.h"
57 #include "nsISocketTransportService.h"
58 #include "nsIProtocolProxyCallback.h"
59 #include "nsISocketTransport.h"
60 #include "nsIAsyncInputStream.h"
61 #include "nsIAsyncOutputStream.h"
62 #include "nsICancelable.h"
63 #include "nsIInterfaceRequestor.h"
64 #include "nsISSLSocketControl.h"
65 #include "nsISocketProviderService.h"
66 #include "nsIProtocolProxyService2.h"
67 #include "nsISocketProvider.h"
68 #include "nsDeque.h"
69 #include "nsICookieService.h"
70 #include "nsICharsetConverterManager.h"
71 #include "nsIUnicodeEncoder.h"
72 #include "nsThreadUtils.h"
73 #include "nsIDOMDocumentEvent.h"
74 #include "nsIDOMMessageEvent.h"
75 #include "nsIStandardURL.h"
76 #include "nsIPromptFactory.h"
77 #include "nsIWindowWatcher.h"
78 #include "nsIPrompt.h"
79 #include "nsIStringBundle.h"
80 #include "nsIConsoleService.h"
81 #include "nsITimer.h"
82 #include "nsIDNSListener.h"
83 #include "nsIDNSRecord.h"
84 #include "nsIDNSService.h"
85 #include "nsLayoutStatics.h"
86 #include "nsIHttpAuthenticableChannel.h"
87 #include "nsIHttpChannelAuthProvider.h"
88 #include "mozilla/Mutex.h"
89 #include "nsIDOMCloseEvent.h"
90 #include "nsICryptoHash.h"
91 #include "jsdbgapi.h"
92 #include "nsIJSContextStack.h"
93 #include "nsJSUtils.h"
94 #include "nsIScriptError.h"
96 using namespace mozilla;
98 static nsIThread *gWebSocketThread = nsnull;
100 ////////////////////////////////////////////////////////////////////////////////
101 // nsWebSocketEstablishedConnection
102 ////////////////////////////////////////////////////////////////////////////////
104 #define DEFAULT_BUFFER_SIZE 2048
105 #define UTF_8_REPLACEMENT_CHAR static_cast<PRUnichar>(0xFFFD)
107 #define TIMEOUT_TRY_CONNECT_AGAIN 1000
108 #define TIMEOUT_WAIT_FOR_SERVER_RESPONSE 20000
109 #define TIMEOUT_WAIT_FOR_CLOSING 20000
111 #define ENSURE_TRUE_AND_FAIL_IF_FAILED(x, ret) \
112 PR_BEGIN_MACRO \
113 if (NS_UNLIKELY(!(x))) { \
114 NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \
115 FailConnection(); \
116 return ret; \
118 PR_END_MACRO
120 #define ENSURE_SUCCESS_AND_FAIL_IF_FAILED(res, ret) \
121 PR_BEGIN_MACRO \
122 nsresult __rv = res; \
123 if (NS_FAILED(__rv)) { \
124 NS_ENSURE_SUCCESS_BODY(res, ret) \
125 FailConnection(); \
126 return ret; \
128 PR_END_MACRO
130 #define CHECK_TRUE_AND_FAIL_IF_FAILED(x) \
131 PR_BEGIN_MACRO \
132 if (NS_UNLIKELY(!(x))) { \
133 NS_WARNING("CHECK_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \
134 FailConnection(); \
135 return; \
137 PR_END_MACRO
139 #define CHECK_SUCCESS_AND_FAIL_IF_FAILED(res) \
140 PR_BEGIN_MACRO \
141 nsresult __rv = res; \
142 if (NS_FAILED(__rv)) { \
143 NS_ENSURE_SUCCESS_BODY(res, ret) \
144 FailConnection(); \
145 return; \
147 PR_END_MACRO
149 #define CHECK_SUCCESS_AND_FAIL_IF_FAILED2(res) \
150 PR_BEGIN_MACRO \
151 nsresult __rv = res; \
152 if (NS_FAILED(__rv)) { \
153 NS_ENSURE_SUCCESS_BODY(res, ret) \
154 thisObject->FailConnection(); \
155 return; \
157 PR_END_MACRO
159 #define WARN_IF_FALSE_AND_RETURN(_expr, _msg) \
160 if (!(_expr)) { \
161 NS_WARNING(_msg); \
162 return; \
165 #define DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(_method) \
166 nsresult _method(); \
167 void MainRunnable##_method();
169 #define IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(_method) \
170 nsresult \
171 nsWebSocketEstablishedConnection::_method() \
173 if (!NS_IsMainThread()) { \
174 nsCOMPtr<nsIRunnable> event = \
175 NS_NewRunnableMethod(this, &nsWebSocketEstablishedConnection:: \
176 MainRunnable##_method); \
177 if (!event) { \
178 return NS_ERROR_OUT_OF_MEMORY; \
180 return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); \
182 MainRunnable##_method(); \
183 return NS_OK; \
186 void \
187 nsWebSocketEstablishedConnection::MainRunnable##_method() \
189 if (!mOwner) { \
190 return; \
193 #define IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END \
196 // protocol specific
197 #define IS_HIGH_BIT_OF_FRAME_TYPE_SET(_v) ((_v & 0x80) == 0x80)
198 #define IS_HIGH_BIT_OF_BYTE_SET(_v) ((_v & 0x80) == 0x80)
199 #define START_BYTE_OF_MESSAGE 0x00
200 #define END_BYTE_OF_MESSAGE 0xff
201 #define START_BYTE_OF_CLOSE_FRAME 0xff
202 #define END_BYTE_OF_CLOSE_FRAME 0x00
204 // nsIInterfaceRequestor will be the unambiguous class for this class
205 class nsWebSocketEstablishedConnection: public nsIInterfaceRequestor,
206 public nsIDNSListener,
207 public nsIProtocolProxyCallback,
208 public nsIInputStreamCallback,
209 public nsIOutputStreamCallback,
210 public nsIChannel,
211 public nsIHttpAuthenticableChannel
213 friend class nsWSNetAddressComparator;
214 friend class nsWSAutoClose;
216 public:
217 nsWebSocketEstablishedConnection();
218 virtual ~nsWebSocketEstablishedConnection();
220 NS_DECL_ISUPPORTS
221 NS_DECL_NSIDNSLISTENER
222 NS_DECL_NSIPROTOCOLPROXYCALLBACK
223 NS_DECL_NSIINPUTSTREAMCALLBACK
224 NS_DECL_NSIOUTPUTSTREAMCALLBACK
225 NS_DECL_NSIINTERFACEREQUESTOR
226 NS_DECL_NSIREQUEST
227 NS_DECL_NSICHANNEL
228 NS_DECL_NSIPROXIEDCHANNEL
230 // nsIHttpAuthenticableChannel.
231 // We can't use NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates
232 // cancel() and others.
233 NS_IMETHOD GetRequestMethod(nsACString &method);
234 NS_IMETHOD GetIsSSL(PRBool *aIsSSL);
235 NS_IMETHOD GetProxyMethodIsConnect(PRBool *aProxyMethodIsConnect);
236 NS_IMETHOD GetServerResponseHeader(nsACString & aServerResponseHeader);
237 NS_IMETHOD GetProxyChallenges(nsACString & aChallenges);
238 NS_IMETHOD GetWWWChallenges(nsACString & aChallenges);
239 NS_IMETHOD SetProxyCredentials(const nsACString & aCredentials);
240 NS_IMETHOD SetWWWCredentials(const nsACString & aCredentials);
241 NS_IMETHOD OnAuthAvailable();
242 NS_IMETHOD OnAuthCancelled(PRBool userCancel);
244 nsresult Init(nsWebSocket *aOwner);
245 nsresult Disconnect();
247 // These are called always on the main thread (they dispatch themselves).
248 // ATTENTION, these method when called can release both the WebSocket object
249 // (i.e. mOwner) and its connection (i.e. *this*).
250 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Close)
251 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(FailConnection)
253 PRBool HasOutgoingMessages()
254 { return mOutgoingMessages.GetSize() != 0; }
256 PRBool ClosedCleanly() { return mClosedCleanly; }
258 nsresult PostMessage(const nsString& aMessage);
259 PRUint32 GetOutgoingBufferedAmount() { return mOutgoingBufferedAmount; }
261 // prevent more than one instance from connecting at a time per IP
262 static nsTArray<nsRefPtr<nsWebSocketEstablishedConnection> >* sWSsConnecting;
264 private:
265 enum WSFrameType {
266 eConnectFrame,
267 eUTF8MessageFrame,
268 eCloseFrame
271 struct nsWSFrame {
272 WSFrameType mType;
273 nsAutoPtr<nsCString> mData;
276 // We can only establish one connection at a time per IP address.
277 // TryConnect ensures this by checking sWSsConnecting.
278 // If there is a IP address entry there it tries again after
279 // TIMEOUT_TRY_CONNECT_AGAIN milliseconds.
280 static void TryConnect(nsITimer *aTimer,
281 void *aClosure);
283 // We wait for the initial server response for
284 // TIMEOUT_WAIT_FOR_SERVER_RESPONSE milliseconds
285 static void TimerInitialServerResponseCallback(nsITimer *aTimer,
286 void *aClosure);
288 // We wait TIMEOUT_WAIT_FOR_CLOSING milliseconds to the connection be closed
289 // after setting the readyState to CLOSING.
290 static void TimerForceCloseCallback(nsITimer *aTimer,
291 void *aClosure);
293 // Similar to the Close method, but this one neither sends the close frame
294 // nor expects the connection to be closed (mStatus == CONN_CLOSED) to
295 // disconnect.
296 void ForceClose();
298 nsresult DoConnect();
299 nsresult HandleNewInputString(PRUint32 aStart);
300 nsresult AddAuthorizationHeaders(nsCString& aAuthHeaderStr,
301 PRBool aIsProxyAuth);
302 nsresult AddCookiesToRequest(nsCString& aAuthHeaderStr);
304 // Returns the related number of the generated key.
305 PRUint32 GenerateSecKey(nsCString& aKey);
306 nsresult GenerateRequestKeys(nsCString& aKey1,
307 nsCString& aKey2,
308 nsCString& aKey3);
310 PRBool UsingHttpProxy();
311 nsresult Reset();
312 void RemoveFromLoadGroup();
313 nsresult ProcessHeaders();
314 nsresult PostData(nsCString *aBuffer,
315 WSFrameType aWSFrameType);
316 nsresult PrintErrorOnConsole(const char *aBundleURI,
317 const PRUnichar *aError,
318 const PRUnichar **aFormatStrings,
319 PRUint32 aFormatStringsLen);
321 // auth specific methods
322 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(ProcessAuthentication)
324 // these are called always on the main thread (they dispatch themselves)
325 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(AddWSConnecting)
326 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(RemoveWSConnecting)
327 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(HandleSetCookieHeader)
328 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(DoInitialRequest)
329 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Connected)
330 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(FrameError)
331 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(DispatchNewMessage)
332 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Retry)
333 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(ResolveNextProxyAndConnect)
334 DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(UpdateMustKeepAlive)
336 // called to cause the underlying socket to start speaking SSL
337 nsresult ProxyStartSSL();
339 // shared by both threads (the main and the socket ones)
340 Mutex mLockDisconnect;
341 Mutex mLockOutgoingMessages;
342 Mutex mLockReceivedMessages;
343 nsCOMPtr<nsISocketTransport> mSocketTransport;
344 nsCOMPtr<nsIAsyncInputStream> mSocketInput;
345 nsCOMPtr<nsIAsyncOutputStream> mSocketOutput;
346 nsCOMPtr<nsIProxyInfo> mProxyInfo;
347 nsDeque mOutgoingMessages; // has nsWSFrame* which need to be sent
348 PRUint32 mBytesAlreadySentOfFirstOutString;
349 PRUint32 mOutgoingBufferedAmount; // not really necessary, but it is
350 // here for fast access.
351 nsDeque mReceivedMessages; // has nsCString* messages that need
352 // to be dispatched as message events
353 nsCString mExpectedMD5Challenge;
354 nsWebSocket* mOwner; // WEAK
356 // used in mHeaders
357 enum {
358 kUpgradePos = 0,
359 kConnectionPos,
360 kSecWebSocketOriginPos,
361 kSecWebSocketLocationPos,
362 kSecWebSocketProtocolPos,
363 kSetCookiePos,
364 kProxyAuthenticatePos,
365 kServerPos, // for digest auth
366 kHeadersLen
369 // used only by the socket thread
370 nsCString mBuffer;
371 PRUint32 mBytesInBuffer; // it is needed because mBuffer.SetLength() does
372 // change also the buffer's Capacity.
373 nsCString mHeaders[kHeadersLen];
374 PRUint32 mLengthToDiscard;
375 PRPackedBool mReadingProxyConnectResponse;
377 // WebSockets should resolve proxy in this order:
378 // (1) socks, (2) https, (3) http
379 enum ProxyConfig {
380 eNotResolvingProxy,
381 eResolvingSOCKSProxy,
382 eResolvingHTTPSProxy,
383 eResolvingHTTPProxy,
384 eResolvingProxyFailed
386 ProxyConfig mCurrentProxyConfig;
388 nsresult mProxyFailureReason;
389 nsCOMPtr<nsICancelable> mProxyResolveCancelable;
390 nsCOMPtr<nsICancelable> mDNSRequest;
391 PRNetAddr mPRNetAddr;
393 nsCOMPtr<nsITimer> mTryConnectTimer;
394 nsCOMPtr<nsITimer> mInitialServerResponseTimer;
395 nsCOMPtr<nsITimer> mCloseFrameServerResponseTimer;
397 // for nsIRequest implementation
398 nsCString mRequestName;
399 nsresult mFailureStatus;
401 // auth specific data
402 nsCString mProxyCredentials;
403 nsCString mCredentials;
404 nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;
405 PRPackedBool mAuthenticating;
407 PRPackedBool mPostedCloseFrame;
408 PRPackedBool mSentCloseFrame;
409 PRPackedBool mClosedCleanly;
412 * A simple state machine used to manage the flow status of the input/output
413 * streams of the connection.
415 * CONN_NOT_CONNECTED (initial state) ->
416 * CONN_CONNECTING |
417 * CONN_CLOSED
419 * CONN_CONNECTING ->
420 * CONN_CONNECTING_TO_HTTP_PROXY |
421 * CONN_SENDING_INITIAL_REQUEST |
422 * CONN_CLOSED
424 * CONN_RETRYING_TO_AUTHENTICATE ->
425 * CONN_CONNECTING_TO_HTTP_PROXY |
426 * CONN_SENDING_INITIAL_REQUEST |
427 * CONN_CLOSED
429 * CONN_CONNECTING_TO_HTTP_PROXY ->
430 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
431 * CONN_CLOSED
433 * CONN_SENDING_INITIAL_REQUEST ->
434 * CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST |
435 * CONN_CLOSED
437 * CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST ->
438 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
439 * CONN_CLOSED
441 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME ->
442 * CONN_READING_RESPONSE_HEADER_NAME |
443 * CONN_WAITING_LF_CHAR_TO_CONNECTING |
444 * CONN_CLOSED
446 * CONN_READING_RESPONSE_HEADER_NAME ->
447 * CONN_READING_RESPONSE_HEADER_VALUE |
448 * CONN_CLOSED
450 * CONN_READING_RESPONSE_HEADER_VALUE ->
451 * CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER |
452 * CONN_CLOSED
454 * CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER ->
455 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
456 * CONN_CLOSED
458 * CONN_WAITING_LF_CHAR_TO_CONNECTING ->
459 * CONN_SENDING_INITIAL_REQUEST |
460 * CONN_READING_CHALLENGE_RESPONSE |
461 * CONN_RETRYING_TO_AUTHENTICATE |
462 * CONN_CLOSED
464 * CONN_READING_CHALLENGE_RESPONSE ->
465 * CONN_CONNECTED_AND_READY |
466 * CONN_CLOSED
468 * CONN_CONNECTED_AND_READY ->
469 * CONN_HIGH_BIT_OF_FRAME_TYPE_SET |
470 * CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET |
471 * CONN_CLOSED
473 * CONN_HIGH_BIT_OF_FRAME_TYPE_SET ->
474 * CONN_READING_AND_DISCARDING_LENGTH_BYTES |
475 * CONN_SENDING_ACK_CLOSE_FRAME |
476 * CONN_CLOSED
478 * CONN_READING_AND_DISCARDING_LENGTH_BYTES ->
479 * CONN_CONNECTED_AND_READY |
480 * CONN_CLOSED
482 * CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET ->
483 * CONN_CONNECTED_AND_READY |
484 * CONN_CLOSED
486 * CONN_SENDING_ACK_CLOSE_FRAME ->
487 * CONN_CLOSED
489 * CONN_CLOSED (final state)
493 enum ConnectionStatus {
494 CONN_NOT_CONNECTED,
495 CONN_CONNECTING,
496 // connecting to the http proxy
497 CONN_RETRYING_TO_AUTHENTICATE,
498 CONN_CONNECTING_TO_HTTP_PROXY,
499 // doing the websocket handshake
500 CONN_SENDING_INITIAL_REQUEST,
501 CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST,
502 CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME,
503 CONN_READING_RESPONSE_HEADER_NAME,
504 CONN_READING_RESPONSE_HEADER_VALUE,
505 CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER,
506 CONN_WAITING_LF_CHAR_TO_CONNECTING,
507 CONN_READING_CHALLENGE_RESPONSE,
508 // the websocket connection is established
509 CONN_CONNECTED_AND_READY,
510 CONN_HIGH_BIT_OF_FRAME_TYPE_SET,
511 CONN_READING_AND_DISCARDING_LENGTH_BYTES,
512 CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET,
513 // the websocket server requested to close the connection and because of
514 // this the close frame in acknowledgement is expected to be sent
515 CONN_SENDING_ACK_CLOSE_FRAME,
516 // the websocket connection is closed (or it is going to)
517 CONN_CLOSED
519 // Shared by both threads (the main and the socket ones).
520 // The initial states (CONN_NOT_CONNECTED, CONN_CONNECTING,
521 // CONN_CONNECTING_TO_HTTP_PROXY and CONN_SENDING_INITIAL_REQUEST) are set by
522 // the main thread. The other ones are set by the socket thread.
523 ConnectionStatus mStatus;
526 nsTArray<nsRefPtr<nsWebSocketEstablishedConnection> >*
527 nsWebSocketEstablishedConnection::sWSsConnecting = nsnull;
529 //------------------------------------------------------------------------------
530 // Helper classes
531 //------------------------------------------------------------------------------
533 class nsWSNetAddressComparator
535 public:
536 // when comparing, if the connection is under a proxy it'll use its hostname,
537 // otherwise it'll use its mPRNetAddr.
538 PRBool Equals(nsWebSocketEstablishedConnection* a,
539 nsWebSocketEstablishedConnection* b) const;
540 PRBool LessThan(nsWebSocketEstablishedConnection* a,
541 nsWebSocketEstablishedConnection* b) const;
544 class nsWSAutoClose
546 public:
547 nsWSAutoClose(nsWebSocketEstablishedConnection* conn) : mConnection(conn)
550 ~nsWSAutoClose() { mConnection->Close(); }
552 private:
553 nsWebSocketEstablishedConnection* mConnection;
556 PRBool
557 nsWSNetAddressComparator::Equals(nsWebSocketEstablishedConnection* a,
558 nsWebSocketEstablishedConnection* b) const
560 NS_ASSERTION(a->mOwner && b->mOwner, "Unexpected disconnected connection");
562 if ((a->mProxyInfo && !b->mProxyInfo) ||
563 (!a->mProxyInfo && b->mProxyInfo)) {
564 return PR_FALSE;
567 if (a->mProxyInfo) {
568 return a->mOwner->mAsciiHost.Equals(b->mOwner->mAsciiHost);
571 if (a->mPRNetAddr.raw.family != b->mPRNetAddr.raw.family) {
572 return PR_FALSE;
575 if (a->mPRNetAddr.raw.family == PR_AF_INET) {
576 return a->mPRNetAddr.inet.ip == b->mPRNetAddr.inet.ip;
579 NS_ASSERTION(a->mPRNetAddr.raw.family == PR_AF_INET6,
580 "Invalid net raw family");
582 return a->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] ==
583 b->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] &&
584 a->mPRNetAddr.ipv6.ip.pr_s6_addr64[1] ==
585 b->mPRNetAddr.ipv6.ip.pr_s6_addr64[1];
588 PRBool
589 nsWSNetAddressComparator::LessThan(nsWebSocketEstablishedConnection* a,
590 nsWebSocketEstablishedConnection* b) const
592 NS_ASSERTION(a->mOwner && b->mOwner, "Unexpected disconnected connection");
594 if (a->mProxyInfo && !b->mProxyInfo) {
595 return PR_FALSE;
598 if (!a->mProxyInfo && b->mProxyInfo) {
599 return PR_TRUE;
602 if (a->mProxyInfo) {
603 return (a->mOwner->mAsciiHost < b->mOwner->mAsciiHost);
606 if (a->mPRNetAddr.raw.family == PR_AF_INET &&
607 b->mPRNetAddr.raw.family == PR_AF_INET6) {
608 return PR_TRUE;
611 if (a->mPRNetAddr.raw.family == PR_AF_INET6 &&
612 b->mPRNetAddr.raw.family == PR_AF_INET) {
613 return PR_FALSE;
616 if (a->mPRNetAddr.raw.family == PR_AF_INET &&
617 b->mPRNetAddr.raw.family == PR_AF_INET) {
618 return a->mPRNetAddr.inet.ip < b->mPRNetAddr.inet.ip;
621 NS_ASSERTION(a->mPRNetAddr.raw.family == PR_AF_INET6 &&
622 b->mPRNetAddr.raw.family == PR_AF_INET6,
623 "Invalid net raw family");
625 return a->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] <
626 b->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] ||
627 (a->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] ==
628 b->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] &&
629 a->mPRNetAddr.ipv6.ip.pr_s6_addr64[1] <
630 b->mPRNetAddr.ipv6.ip.pr_s6_addr64[1]);
633 //-----------------------------------------------------------------------------
634 // nsWebSocketEstablishedConnection::nsISupports
635 //-----------------------------------------------------------------------------
637 NS_IMPL_THREADSAFE_ISUPPORTS9(nsWebSocketEstablishedConnection,
638 nsIInterfaceRequestor,
639 nsIDNSListener,
640 nsIProtocolProxyCallback,
641 nsIInputStreamCallback,
642 nsIOutputStreamCallback,
643 nsIRequest,
644 nsIChannel,
645 nsIProxiedChannel,
646 nsIHttpAuthenticableChannel)
648 //-----------------------------------------------------------------------------
649 // nsWebSocketEstablishedConnection methods:
650 //-----------------------------------------------------------------------------
652 nsWebSocketEstablishedConnection::nsWebSocketEstablishedConnection() :
653 mLockDisconnect("WebSocket's disconnect lock"),
654 mLockOutgoingMessages("WebSocket's outgoing messages lock"),
655 mLockReceivedMessages("WebSocket's received messages lock"),
656 mBytesAlreadySentOfFirstOutString(0),
657 mOutgoingBufferedAmount(0),
658 mOwner(nsnull),
659 mBytesInBuffer(0),
660 mLengthToDiscard(0),
661 mReadingProxyConnectResponse(PR_FALSE),
662 mCurrentProxyConfig(eNotResolvingProxy),
663 mProxyFailureReason(NS_OK),
664 mFailureStatus(NS_OK),
665 mAuthenticating(PR_FALSE),
666 mPostedCloseFrame(PR_FALSE),
667 mSentCloseFrame(PR_FALSE),
668 mClosedCleanly(PR_FALSE),
669 mStatus(CONN_NOT_CONNECTED)
671 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
672 nsLayoutStatics::AddRef();
675 nsWebSocketEstablishedConnection::~nsWebSocketEstablishedConnection()
677 NS_ASSERTION(!mOwner, "Disconnect wasn't called!");
680 nsresult
681 nsWebSocketEstablishedConnection::PostData(nsCString *aBuffer,
682 WSFrameType aWSFrameType)
684 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
686 nsAutoPtr<nsCString> data(aBuffer);
688 if (mStatus == CONN_CLOSED) {
689 NS_ASSERTION(mOwner, "Posting data after disconnecting the websocket!");
690 // the tcp connection has been closed, but the main thread hasn't received
691 // the event for disconnecting the object yet.
692 return NS_BASE_STREAM_CLOSED;
695 MutexAutoLock lockOut(mLockOutgoingMessages);
697 nsAutoPtr<nsWSFrame> frame(new nsWSFrame());
698 NS_ENSURE_TRUE(frame.get(), NS_ERROR_OUT_OF_MEMORY);
699 frame->mType = aWSFrameType;
700 frame->mData = data.forget();
702 nsresult rv;
703 PRInt32 sizeBefore = mOutgoingMessages.GetSize();
704 mOutgoingMessages.Push(frame.forget());
705 NS_ENSURE_TRUE(mOutgoingMessages.GetSize() == sizeBefore + 1,
706 NS_ERROR_OUT_OF_MEMORY);
707 if (aWSFrameType == eUTF8MessageFrame) {
708 // without the START_BYTE_OF_MESSAGE and END_BYTE_OF_MESSAGE bytes
709 mOutgoingBufferedAmount += aBuffer->Length() - 2;
710 } else if (aWSFrameType == eCloseFrame) {
711 mPostedCloseFrame = PR_TRUE;
714 if (sizeBefore == 0) {
715 mBytesAlreadySentOfFirstOutString = 0;
716 rv = mSocketOutput->AsyncWait(this, 0, 0, gWebSocketThread);
717 NS_ENSURE_SUCCESS(rv, rv);
720 UpdateMustKeepAlive();
722 return NS_OK;
725 nsresult
726 nsWebSocketEstablishedConnection::PostMessage(const nsString& aMessage)
728 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
730 if (!mOwner) {
731 return NS_OK;
734 // only send messages when connected
735 NS_ENSURE_STATE(mStatus >= CONN_CONNECTED_AND_READY);
737 nsresult rv;
739 nsCOMPtr<nsICharsetConverterManager> ccm =
740 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
741 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
743 nsCOMPtr<nsIUnicodeEncoder> converter;
744 rv = ccm->GetUnicodeEncoder("UTF-8", getter_AddRefs(converter));
745 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
747 rv = converter->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
748 nsnull, UTF_8_REPLACEMENT_CHAR);
749 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
751 PRInt32 inLen = aMessage.Length();
752 PRInt32 maxLen;
753 rv = converter->GetMaxLength(aMessage.BeginReading(), inLen, &maxLen);
754 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
755 maxLen += 2; // 2 bytes for START_BYTE_OF_MESSAGE and END_BYTE_OF_MESSAGE
757 nsAutoPtr<nsCString> buf(new nsCString());
758 ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.get(), NS_ERROR_OUT_OF_MEMORY);
760 buf->SetLength(maxLen);
761 ENSURE_TRUE_AND_FAIL_IF_FAILED(buf->Length() == static_cast<PRUint32>(maxLen),
762 NS_ERROR_OUT_OF_MEMORY);
764 char* start = buf->BeginWriting();
765 *start = static_cast<char>(START_BYTE_OF_MESSAGE);
766 ++start;
768 PRInt32 outLen = maxLen;
769 rv = converter->Convert(aMessage.BeginReading(), &inLen, start, &outLen);
770 if (NS_SUCCEEDED(rv)) {
771 PRInt32 outLen2 = maxLen - outLen;
772 rv = converter->Finish(start + outLen, &outLen2);
773 outLen += outLen2;
775 if (NS_FAILED(rv) || rv == NS_ERROR_UENC_NOMAPPING) {
776 // Yes, NS_ERROR_UENC_NOMAPPING is a success code
777 return NS_ERROR_DOM_SYNTAX_ERR;
780 char* end = buf->BeginWriting() + outLen + 1;
781 *end = static_cast<char>(END_BYTE_OF_MESSAGE);
783 outLen += 2;
785 buf->SetLength(outLen);
786 ENSURE_TRUE_AND_FAIL_IF_FAILED(buf->Length() == static_cast<PRUint32>(outLen),
787 NS_ERROR_UNEXPECTED);
789 rv = PostData(buf.forget(), eUTF8MessageFrame);
790 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
792 return NS_OK;
795 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(DoInitialRequest)
797 nsresult rv;
798 nsString strRequestTmp;
800 nsAutoPtr<nsCString> buf(new nsCString());
801 CHECK_TRUE_AND_FAIL_IF_FAILED(buf.get());
803 // GET resource HTTP/1.1
804 buf->AppendLiteral("GET ");
805 buf->Append(mOwner->mResource);
806 buf->AppendLiteral(" HTTP/1.1\r\n");
808 nsCAutoString key_1, key_2, key_3;
809 rv = GenerateRequestKeys(key_1, key_2, key_3);
810 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
812 // the headers should be sent in a random order
814 enum eRequestHeader { upgradeHeader = 0, connectionHeader, hostHeader,
815 originHeader, secWebSocketProtocolHeader,
816 authorizationHeaders, cookieHeaders,
817 secWebSocketKey1Header, secWebSocketKey2Header,
818 numberRequestHeaders };
819 nsAutoTArray<PRUint32, numberRequestHeaders> headersToSend;
820 for (PRUint32 i = 0; i < numberRequestHeaders; ++i) {
821 headersToSend.AppendElement(i);
824 while (!headersToSend.IsEmpty()) {
825 PRUint8 headerPosToSendNow = rand() % headersToSend.Length();
826 eRequestHeader headerToSendNow =
827 static_cast<eRequestHeader>(headersToSend[headerPosToSendNow]);
829 switch (headerToSendNow)
831 case upgradeHeader:
833 buf->AppendLiteral("Upgrade: WebSocket\r\n");
835 break;
837 case connectionHeader:
839 buf->AppendLiteral("Connection: Upgrade\r\n");
841 break;
843 case hostHeader:
845 buf->AppendLiteral("Host: ");
846 buf->Append(mOwner->mAsciiHost);
847 buf->AppendLiteral(":");
848 buf->AppendInt(mOwner->mPort);
849 buf->AppendLiteral("\r\n");
851 break;
853 case originHeader:
855 buf->AppendLiteral("Origin: ");
856 buf->Append(mOwner->mOrigin);
857 buf->AppendLiteral("\r\n");
859 break;
861 case secWebSocketProtocolHeader:
863 if (!mOwner->mProtocol.IsEmpty()) {
864 buf->AppendLiteral("Sec-WebSocket-Protocol: ");
865 buf->Append(mOwner->mProtocol);
866 buf->AppendLiteral("\r\n");
869 break;
871 case authorizationHeaders:
873 rv = AddAuthorizationHeaders(*buf, PR_FALSE);
874 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
876 break;
878 case cookieHeaders:
880 rv = AddCookiesToRequest(*buf);
881 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
883 break;
885 case secWebSocketKey1Header:
887 buf->AppendLiteral("Sec-WebSocket-Key1: ");
888 buf->Append(key_1);
889 buf->AppendLiteral("\r\n");
891 break;
893 case secWebSocketKey2Header:
895 buf->AppendLiteral("Sec-WebSocket-Key2: ");
896 buf->Append(key_2);
897 buf->AppendLiteral("\r\n");
899 break;
902 headersToSend.RemoveElementAt(headerPosToSendNow);
905 buf->AppendLiteral("\r\n");
906 buf->Append(key_3);
908 mStatus = CONN_SENDING_INITIAL_REQUEST;
910 rv = PostData(buf.forget(), eConnectFrame);
911 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
913 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
915 static
916 nsresult
917 GetHttpResponseCode(const nsCString& aLine, PRUint32 aLen,
918 PRUint32 *aStatusCode, PRUint32 *aLineLen)
920 // make sure we have HTTP at the beginning
921 if (aLen < 4) {
922 return NS_ERROR_IN_PROGRESS;
924 if (!StringBeginsWith(aLine, NS_LITERAL_CSTRING("HTTP"))) {
925 return NS_ERROR_UNEXPECTED;
928 // get the response code
929 PRUint32 responseCode = 0;
930 PRUint8 responseCodeReadingState = 0; // 0:not reading, 1:reading,
931 // 2:done reading
932 char last2Chrs[2] = {'\0', '\0'};
933 PRUint32 i = 4; // just after the HTTP
934 for (; i < aLen; ++i) {
935 if (responseCodeReadingState == 0 && aLine[i] == ' ') {
936 responseCodeReadingState = 1;
937 } else if (responseCodeReadingState == 1) {
938 if (aLine[i] == ' ') {
939 responseCodeReadingState = 2;
940 } else if (aLine[i] >= '0' && aLine[i] <= '9') {
941 responseCode = 10 * responseCode + (aLine[i] - '0');
942 if (responseCode > 999) { // the response code must be three digits long
943 return NS_ERROR_UNEXPECTED;
945 } else {
946 return NS_ERROR_UNEXPECTED;
950 last2Chrs[0] = last2Chrs[1];
951 last2Chrs[1] = aLine[i];
952 if (last2Chrs[0] == '\r' && last2Chrs[1] == '\n') { // CR LF
953 *aStatusCode = responseCode;
954 *aLineLen = i + 1;
955 return NS_OK;
959 return NS_ERROR_IN_PROGRESS;
962 nsresult
963 nsWebSocketEstablishedConnection::HandleNewInputString(PRUint32 aStart)
965 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
967 if (mBytesInBuffer == 0 || aStart == mBytesInBuffer) {
968 return NS_OK;
971 NS_ENSURE_STATE(aStart < mBytesInBuffer);
973 nsresult rv;
975 switch (mStatus)
977 case CONN_CONNECTING_TO_HTTP_PROXY:
979 PRUint32 statusCode, lengthStr;
981 rv = GetHttpResponseCode(mBuffer, mBytesInBuffer, &statusCode,
982 &lengthStr);
983 if (rv != NS_ERROR_IN_PROGRESS) {
984 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
986 if (statusCode == 200) {
987 mReadingProxyConnectResponse = PR_TRUE;
988 mAuthenticating = PR_FALSE;
989 } else if (statusCode == 407) {
990 mReadingProxyConnectResponse = PR_TRUE;
991 mAuthenticating = PR_TRUE;
992 } else {
993 return NS_ERROR_UNEXPECTED;
996 mStatus = CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME;
997 mBuffer.Cut(0, lengthStr);
998 mBytesInBuffer -= lengthStr;
1000 return HandleNewInputString(0);
1003 break;
1005 case CONN_SENDING_ACK_CLOSE_FRAME:
1006 case CONN_CLOSED:
1008 mBytesInBuffer = 0;
1010 break;
1012 case CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST:
1014 PRUint32 statusCode, lengthStr;
1016 rv = GetHttpResponseCode(mBuffer, mBytesInBuffer, &statusCode,
1017 &lengthStr);
1018 if (rv != NS_ERROR_IN_PROGRESS) {
1019 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
1021 if (statusCode != 101) {
1022 return NS_ERROR_UNEXPECTED;
1025 mReadingProxyConnectResponse = PR_FALSE;
1026 mAuthenticating = PR_FALSE;
1028 mStatus = CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME;
1029 mBuffer.Cut(0, lengthStr);
1030 mBytesInBuffer -= lengthStr;
1032 // we have just received the server response. We must cancel this timer
1033 // or it will fail the connection.
1034 mInitialServerResponseTimer->Cancel();
1036 return HandleNewInputString(0);
1039 break;
1041 case CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME:
1043 if (mBuffer[aStart] == '\r') {
1044 mStatus = CONN_WAITING_LF_CHAR_TO_CONNECTING;
1045 return HandleNewInputString(aStart + 1);
1048 NS_ENSURE_STATE(mBuffer[aStart] != '\n');
1050 mStatus = CONN_READING_RESPONSE_HEADER_NAME;
1051 return HandleNewInputString(aStart);
1053 break;
1055 case CONN_READING_RESPONSE_HEADER_NAME:
1057 PRUint32 i;
1058 for (i = aStart; i < mBytesInBuffer; ++i) {
1059 NS_ENSURE_STATE(mBuffer[i] != '\r' && mBuffer[i] != '\n');
1061 if (mBuffer[i] == ':') {
1062 mStatus = CONN_READING_RESPONSE_HEADER_VALUE;
1063 return HandleNewInputString(i + 1);
1067 break;
1069 case CONN_READING_RESPONSE_HEADER_VALUE:
1071 PRUint32 i;
1072 for (i = aStart; i < mBytesInBuffer; ++i) {
1073 if (mBuffer[i] == '\r') {
1074 mStatus = CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER;
1075 return HandleNewInputString(i + 1);
1078 NS_ENSURE_STATE(mBuffer[i] != '\n');
1081 break;
1083 case CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER:
1085 NS_ENSURE_STATE(mBuffer[aStart] == '\n');
1087 PRUint32 posColon = mBuffer.FindChar(':');
1088 PRUint32 posCR = mBuffer.FindChar('\r');
1090 const nsCSubstring& headerName = Substring(mBuffer, 0, posColon);
1092 nsCString headerValue;
1093 if (mBuffer[posColon + 1] == 0x20 && posColon + 2 != posCR) {
1094 headerValue = Substring(mBuffer, posColon + 2, posCR - posColon - 2);
1095 } else if (posColon + 1 != posCR) {
1096 headerValue = Substring(mBuffer, posColon + 1, posCR - posColon - 1);
1097 } else {
1098 ; // No header value
1101 NS_ENSURE_STATE(!headerName.IsEmpty());
1103 PRInt32 headerPos = -1;
1104 if (mReadingProxyConnectResponse) {
1105 if (headerName.LowerCaseEqualsLiteral("proxy-authenticate")) {
1106 headerPos = kProxyAuthenticatePos;
1108 } else {
1109 if (headerName.LowerCaseEqualsLiteral("upgrade")) {
1110 headerPos = kUpgradePos;
1111 } else if (headerName.LowerCaseEqualsLiteral("connection")) {
1112 headerPos = kConnectionPos;
1113 } else if (headerName.LowerCaseEqualsLiteral("sec-websocket-origin")) {
1114 headerPos = kSecWebSocketOriginPos;
1115 } else if (headerName.LowerCaseEqualsLiteral("sec-websocket-location")) {
1116 headerPos = kSecWebSocketLocationPos;
1117 } else if (headerName.LowerCaseEqualsLiteral("sec-websocket-protocol")) {
1118 headerPos = kSecWebSocketProtocolPos;
1119 } else if (headerName.LowerCaseEqualsLiteral("set-cookie")) {
1120 headerPos = kSetCookiePos;
1123 if (headerPos == -1 && headerName.LowerCaseEqualsLiteral("server")) {
1124 headerPos = kServerPos;
1127 if (headerPos != -1) {
1128 NS_ENSURE_STATE(mHeaders[headerPos].IsEmpty());
1129 mHeaders[headerPos] = headerValue;
1132 mStatus = CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME;
1133 mBuffer.Cut(0, aStart + 1);
1134 mBytesInBuffer -= aStart + 1;
1136 return HandleNewInputString(0);
1138 break;
1140 case CONN_WAITING_LF_CHAR_TO_CONNECTING:
1142 NS_ENSURE_STATE(mBuffer[aStart] == '\n');
1144 rv = ProcessHeaders();
1145 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
1147 if (mAuthenticating) {
1148 Reset();
1149 mStatus = CONN_RETRYING_TO_AUTHENTICATE;
1150 return ProcessAuthentication();
1153 if (mReadingProxyConnectResponse) {
1154 if (mOwner->mSecure) {
1155 rv = ProxyStartSSL();
1156 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
1159 mBytesInBuffer = 0;
1160 return DoInitialRequest();
1163 mStatus = CONN_READING_CHALLENGE_RESPONSE;
1165 mBuffer.Cut(0, aStart + 1);
1166 mBytesInBuffer -= aStart + 1;
1167 return HandleNewInputString(0);
1170 case CONN_READING_CHALLENGE_RESPONSE:
1172 NS_ENSURE_STATE(aStart == 0);
1174 if (mBytesInBuffer < 16) {
1175 return NS_OK;
1178 const nsCSubstring& receivedMD5Challenge = Substring(mBuffer, 0, 16);
1179 if (!mExpectedMD5Challenge.Equals(receivedMD5Challenge)) {
1180 return NS_ERROR_UNEXPECTED;
1183 mStatus = CONN_CONNECTED_AND_READY;
1184 rv = Connected();
1185 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
1187 mBuffer.Cut(0, aStart + 16);
1188 mBytesInBuffer -= aStart + 16;
1189 return HandleNewInputString(0);
1192 case CONN_CONNECTED_AND_READY:
1194 NS_ENSURE_STATE(aStart == 0);
1195 PRUint8 frameType = mBuffer[0];
1197 if (IS_HIGH_BIT_OF_FRAME_TYPE_SET(frameType)) {
1198 mStatus = CONN_HIGH_BIT_OF_FRAME_TYPE_SET;
1199 mLengthToDiscard = 0;
1200 } else {
1201 mStatus = CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET;
1204 return HandleNewInputString(1);
1206 break;
1208 case CONN_HIGH_BIT_OF_FRAME_TYPE_SET:
1210 PRUint32 i;
1211 PRUint8 frameType = mBuffer[0];
1212 for (i = aStart; i < mBytesInBuffer; ++i) {
1213 PRUint8 b, bv;
1214 b = mBuffer[i];
1215 bv = (b & 0x7f);
1217 // prevent overflow
1218 NS_ENSURE_STATE(mLengthToDiscard <= ((PR_UINT32_MAX - bv) / 128));
1220 mLengthToDiscard = mLengthToDiscard * 128 + bv;
1222 if (!IS_HIGH_BIT_OF_BYTE_SET(b)) {
1223 // check if it is the close frame
1224 if (mLengthToDiscard == 0 && frameType == START_BYTE_OF_CLOSE_FRAME) {
1225 mBytesInBuffer = 0;
1226 if (mSentCloseFrame) {
1227 mClosedCleanly = PR_TRUE;
1228 mStatus = CONN_CLOSED;
1229 } else {
1230 mStatus = CONN_SENDING_ACK_CLOSE_FRAME;
1232 return Close();
1234 FrameError();
1235 mStatus = CONN_READING_AND_DISCARDING_LENGTH_BYTES;
1236 return HandleNewInputString(i + 1);
1239 mBytesInBuffer = 0;
1241 break;
1243 case CONN_READING_AND_DISCARDING_LENGTH_BYTES:
1245 if (mBytesInBuffer - aStart >= mLengthToDiscard) {
1246 mBuffer.Cut(0, aStart + mLengthToDiscard);
1247 mBytesInBuffer -= aStart + mLengthToDiscard;
1249 mStatus = CONN_CONNECTED_AND_READY;
1250 return HandleNewInputString(0);
1253 mLengthToDiscard -= mBytesInBuffer - aStart;
1254 mBytesInBuffer = 0;
1256 break;
1258 case CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET:
1260 PRUint32 i;
1261 for (i = aStart; i < mBytesInBuffer; ++i) {
1262 PRUint8 b;
1263 b = mBuffer[i];
1264 if (b == END_BYTE_OF_MESSAGE) {
1265 PRUint8 frameType = mBuffer[0];
1266 if (frameType == START_BYTE_OF_MESSAGE) {
1267 // get the message, without the START_BYTE_OF_MESSAGE and
1268 // END_BYTE_OF_MESSAGE bytes
1269 nsAutoPtr<nsCString> dataMessage(new nsCString());
1270 NS_ENSURE_TRUE(dataMessage.get(), NS_ERROR_OUT_OF_MEMORY);
1271 dataMessage->Assign(Substring(mBuffer, 1, i - 1));
1273 // push the new message onto our stack
1275 MutexAutoLock lockIn(mLockReceivedMessages);
1277 PRInt32 sizeBefore = mReceivedMessages.GetSize();
1278 mReceivedMessages.Push(dataMessage.forget());
1279 NS_ENSURE_TRUE(mReceivedMessages.GetSize() == sizeBefore + 1,
1280 NS_ERROR_OUT_OF_MEMORY);
1283 // and dispatch it
1284 rv = DispatchNewMessage();
1285 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
1286 } else {
1287 FrameError();
1290 mBuffer.Cut(0, i + 1);
1291 mBytesInBuffer -= i + 1;
1293 mStatus = CONN_CONNECTED_AND_READY;
1294 return HandleNewInputString(0);
1298 break;
1300 default:
1301 NS_ASSERTION(PR_FALSE, "Invalid state.");
1304 return NS_OK;
1307 nsresult
1308 nsWebSocketEstablishedConnection::AddAuthorizationHeaders(nsCString& aStr,
1309 PRBool aIsProxyAuth)
1311 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1313 mAuthProvider->AddAuthorizationHeaders();
1315 if (UsingHttpProxy() && !mProxyCredentials.IsEmpty()) {
1316 aStr.AppendLiteral("Proxy-Authorization: ");
1317 aStr.Append(mProxyCredentials);
1318 aStr.AppendLiteral("\r\n");
1321 if (!aIsProxyAuth && !mCredentials.IsEmpty()) {
1322 aStr.AppendLiteral("Authorization: ");
1323 aStr.Append(mCredentials);
1324 aStr.AppendLiteral("\r\n");
1326 return NS_OK;
1329 nsresult
1330 nsWebSocketEstablishedConnection::AddCookiesToRequest(nsCString& aStr)
1332 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1334 // get the content of the cookie request header
1335 nsCOMPtr<nsICookieService> cookieService =
1336 do_GetService(NS_COOKIESERVICE_CONTRACTID);
1337 nsCOMPtr<nsIDocument> doc =
1338 nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
1340 if (!cookieService || !doc) {
1341 return NS_OK;
1344 nsCOMPtr<nsIURI> documentURI = doc->GetDocumentURI();
1345 if (!documentURI) {
1346 return NS_OK;
1349 nsXPIDLCString cookieValue;
1350 cookieService->GetCookieStringFromHttp(documentURI,
1351 documentURI,
1352 nsnull,
1353 getter_Copies(cookieValue));
1354 if (!cookieValue.IsEmpty()) {
1355 aStr.AppendLiteral("Cookie: ");
1356 aStr.Append(cookieValue);
1357 aStr.AppendLiteral("\r\n");
1360 return NS_OK;
1363 PRUint32
1364 nsWebSocketEstablishedConnection::GenerateSecKey(nsCString& aKey)
1366 PRUint32 i;
1368 PRUint32 spaces = rand() % 12 + 1;
1369 PRUint32 max = PR_UINT32_MAX / spaces;
1370 PRUint32 number = rand() % max;
1371 PRUint32 product = number * spaces;
1373 nsCAutoString key;
1375 key.AppendInt(product);
1377 // Insert between one and twelve random characters from the ranges
1378 // U+0021 to U+002F and U+003A to U+007E into the key at random
1379 // positions.
1380 PRUint32 numberOfCharsToInsert = rand() % 12 + 1;
1381 for (i = 0; i < numberOfCharsToInsert; ++i) {
1382 PRUint32 posToInsert = rand() % key.Length();
1383 char charToInsert =
1384 rand() % 2 == 0 ?
1385 static_cast<char>(0x21 + (rand() % (0x2F - 0x21 + 1))) :
1386 static_cast<char>(0x3A + (rand() % (0x7E - 0x3A + 1)));
1388 key.Insert(charToInsert, posToInsert);
1391 // Insert /spaces/ U+0020 SPACE characters into the key at random
1392 // positions other than the start or end of the string.
1393 for (i = 0; i < spaces; ++i) {
1394 PRUint32 posToInsert = rand() % (key.Length() - 1) + 1;
1395 key.Insert(static_cast<char>(0x20), posToInsert);
1398 aKey = key;
1399 return number;
1402 nsresult
1403 nsWebSocketEstablishedConnection::GenerateRequestKeys(nsCString& aKey1,
1404 nsCString& aKey2,
1405 nsCString& aKey3)
1407 nsresult rv;
1408 PRUint32 i;
1410 nsCAutoString key_1;
1411 PRUint32 number_1;
1413 nsCAutoString key_2;
1414 PRUint32 number_2;
1416 // generate the sec-keys headers values
1417 number_1 = GenerateSecKey(key_1);
1418 number_2 = GenerateSecKey(key_2);
1420 // key3 must be a string consisting of eight random bytes
1421 nsCAutoString key_3;
1422 for (i = 0; i < 8; ++i) {
1423 // get a byte between 1 and 255. 0x00 was discarted to prevent possible
1424 // issues in ws servers.
1425 key_3 += static_cast<char>(rand() % 0xff + 1);
1428 // since we have the keys, we calculate the server md5 challenge response,
1429 // which is the md5 string of the concatenation of /number_1/, expressed as
1430 // a big-endian 32 bit integer, /number_2/, expressed as a big-endian
1431 // 32 bit integer, and the eight bytes of /key_3/
1433 nsCOMPtr<nsICryptoHash> md5CryptoHash =
1434 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1435 NS_ENSURE_SUCCESS(rv, rv);
1437 rv = md5CryptoHash->Init(nsICryptoHash::MD5);
1438 NS_ENSURE_SUCCESS(rv, rv);
1440 PRUint8 data[16];
1441 for (i = 1; i <= 4; ++i) {
1442 data[i - 1] = static_cast<PRUint8>(number_1 >> (32 - i * 8));
1444 for (i = 1; i <= 4; ++i) {
1445 data[i + 3] = static_cast<PRUint8>(number_2 >> (32 - i * 8));
1447 for (i = 0; i < 8; ++i) {
1448 data[i + 8] = static_cast<PRUint8>(key_3[i]);
1451 rv = md5CryptoHash->Update(data, 16);
1452 NS_ENSURE_SUCCESS(rv, rv);
1454 rv = md5CryptoHash->Finish(PR_FALSE, mExpectedMD5Challenge);
1455 NS_ENSURE_SUCCESS(rv, rv);
1457 aKey1 = key_1;
1458 aKey2 = key_2;
1459 aKey3 = key_3;
1461 return NS_OK;
1464 PRBool
1465 nsWebSocketEstablishedConnection::UsingHttpProxy()
1467 if (!mProxyInfo) {
1468 return PR_FALSE;
1471 nsCAutoString proxyType;
1472 mProxyInfo->GetType(proxyType);
1473 return proxyType.EqualsLiteral("http");
1476 // it is called by both main and socket threads under the mLockDisconnect
1477 nsresult
1478 nsWebSocketEstablishedConnection::Reset()
1480 RemoveWSConnecting();
1482 mStatus = CONN_NOT_CONNECTED;
1484 if (mSocketTransport) {
1485 mSocketTransport->Close(NS_OK);
1486 mSocketTransport = nsnull;
1488 mSocketInput = nsnull;
1489 mSocketOutput = nsnull;
1491 while (mOutgoingMessages.GetSize() != 0) {
1492 delete static_cast<nsWSFrame*>(mOutgoingMessages.PopFront());
1495 while (mReceivedMessages.GetSize() != 0) {
1496 delete static_cast<nsCString*>(mReceivedMessages.PopFront());
1499 mBytesAlreadySentOfFirstOutString = 0;
1500 mBytesInBuffer = 0;
1502 return NS_OK;
1505 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Connected)
1507 RemoveWSConnecting();
1509 if (mAuthProvider) {
1510 mAuthProvider->Disconnect(NS_ERROR_ABORT);
1511 mAuthProvider = nsnull;
1514 mOwner->SetReadyState(nsIWebSocket::OPEN);
1516 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1518 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FrameError)
1520 nsresult rv =
1521 mOwner->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
1522 if (NS_FAILED(rv)) {
1523 NS_WARNING("Failed to dispatch the error event");
1526 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1528 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(DispatchNewMessage)
1530 nsresult rv;
1532 while (PR_TRUE) {
1533 nsAutoPtr<nsCString> data;
1536 MutexAutoLock lockIn(mLockReceivedMessages);
1538 if (mReceivedMessages.GetSize() == 0) {
1539 return;
1542 data = static_cast<nsCString*>(mReceivedMessages.PopFront());
1545 rv = mOwner->CreateAndDispatchMessageEvent(data);
1546 if (NS_FAILED(rv)) {
1547 NS_WARNING("Failed to dispatch the message event");
1551 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1553 nsresult
1554 nsWebSocketEstablishedConnection::ProxyStartSSL()
1556 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
1558 nsCOMPtr<nsISupports> securityInfo;
1559 nsresult rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
1560 NS_ENSURE_SUCCESS(rv, rv);
1562 nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
1563 NS_ENSURE_SUCCESS(rv, rv);
1565 return ssl->ProxyStartSSL();
1568 nsresult
1569 nsWebSocketEstablishedConnection::Init(nsWebSocket *aOwner)
1571 // test if it has been alredy initialized
1572 NS_ASSERTION(!mOwner, "WebSocket's connection is already initialized");
1574 nsresult rv;
1576 mOwner = aOwner;
1578 if (mOwner->mSecure) {
1579 // HACK: make sure PSM gets initialized on the main thread.
1580 nsCOMPtr<nsISocketProviderService> spserv =
1581 do_GetService(NS_SOCKETPROVIDERSERVICE_CONTRACTID);
1582 NS_ENSURE_STATE(spserv);
1584 nsCOMPtr<nsISocketProvider> provider;
1585 rv = spserv->GetSocketProvider("ssl", getter_AddRefs(provider));
1586 NS_ENSURE_SUCCESS(rv, rv);
1589 mTryConnectTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1590 NS_ENSURE_SUCCESS(rv, rv);
1592 mInitialServerResponseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1593 NS_ENSURE_SUCCESS(rv, rv);
1595 // add ourselves to the document's load group
1596 nsCOMPtr<nsILoadGroup> loadGroup;
1597 rv = GetLoadGroup(getter_AddRefs(loadGroup));
1598 NS_ENSURE_SUCCESS(rv, rv);
1599 if (loadGroup) {
1600 rv = loadGroup->AddRequest(this, nsnull);
1601 NS_ENSURE_SUCCESS(rv, rv);
1604 mAuthProvider =
1605 do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
1606 &rv);
1607 NS_ENSURE_SUCCESS(rv, rv);
1609 rv = mAuthProvider->Init(this);
1610 NS_ENSURE_SUCCESS(rv, rv);
1612 CopyUTF16toUTF8(mOwner->mOriginalURL, mRequestName);
1614 if (!sWSsConnecting) {
1615 sWSsConnecting =
1616 new nsTArray<nsRefPtr<nsWebSocketEstablishedConnection> >();
1617 ENSURE_TRUE_AND_FAIL_IF_FAILED(sWSsConnecting, NS_ERROR_OUT_OF_MEMORY);
1620 if (!gWebSocketThread) {
1621 rv = NS_NewThread(&gWebSocketThread);
1622 NS_ENSURE_SUCCESS(rv, rv);
1625 rv = ResolveNextProxyAndConnect();
1626 NS_ENSURE_SUCCESS(rv, rv);
1628 return NS_OK;
1631 nsresult
1632 nsWebSocketEstablishedConnection::DoConnect()
1634 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1636 nsresult rv;
1638 rv = AddWSConnecting();
1639 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1641 mStatus = CONN_CONNECTING;
1643 nsCOMPtr<nsISocketTransportService> sts =
1644 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1645 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1647 // configure the socket type based on the connection type requested.
1648 const char* types[1];
1649 nsAdoptingCString value =
1650 nsContentUtils::GetCharPref("network.http.default-socket-type");
1652 if (mOwner->mSecure) {
1653 types[0] = "ssl";
1654 } else {
1655 if (value.IsEmpty()) {
1656 types[0] = nsnull;
1657 } else {
1658 types[0] = value.get();
1662 nsCOMPtr<nsISocketTransport> strans;
1663 PRUint32 typeCount = (types[0] != nsnull ? 1 : 0);
1665 rv = sts->CreateTransport(types, typeCount, mOwner->mAsciiHost, mOwner->mPort,
1666 mProxyInfo, getter_AddRefs(strans));
1667 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1669 rv = strans->SetSecurityCallbacks(this);
1670 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1672 nsCOMPtr<nsIOutputStream> outStream;
1673 rv = strans->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
1674 getter_AddRefs(outStream));
1675 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1677 nsCOMPtr<nsIInputStream> inStream;
1678 rv = strans->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
1679 getter_AddRefs(inStream));
1680 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1682 mSocketTransport = strans;
1683 mSocketInput = do_QueryInterface(inStream);
1684 mSocketOutput = do_QueryInterface(outStream);
1685 mProxyResolveCancelable = nsnull;
1687 if (!UsingHttpProxy()) {
1688 rv = DoInitialRequest();
1689 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1690 return NS_OK;
1693 nsAutoPtr<nsCString> buf(new nsCString());
1694 ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.get(), NS_ERROR_OUT_OF_MEMORY);
1696 nsString strRequestTmp;
1698 // CONNECT host:port HTTP/1.1
1699 buf->AppendLiteral("CONNECT ");
1700 buf->Append(mOwner->mAsciiHost);
1701 buf->AppendLiteral(":");
1702 buf->AppendInt(mOwner->mPort);
1703 buf->AppendLiteral(" HTTP/1.1\r\n");
1705 // Host
1706 // all HTTP/1.1 requests must include a Host header (even though it
1707 // may seem redundant in this case; see bug 82388).
1708 buf->AppendLiteral("Host: ");
1709 buf->Append(mOwner->mAsciiHost);
1710 buf->AppendLiteral(":");
1711 buf->AppendInt(mOwner->mPort);
1712 buf->AppendLiteral("\r\n");
1714 // Proxy Authorization
1715 rv = AddAuthorizationHeaders(*buf, PR_TRUE);
1716 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1718 buf->AppendLiteral("\r\n");
1720 mStatus = CONN_CONNECTING_TO_HTTP_PROXY;
1722 rv = PostData(buf.forget(), eConnectFrame);
1723 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
1725 return NS_OK;
1728 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(AddWSConnecting)
1730 #ifdef DEBUG
1731 PRUint32 index =
1732 sWSsConnecting->BinaryIndexOf(this, nsWSNetAddressComparator());
1733 NS_ASSERTION(index == nsTArray<PRNetAddr>::NoIndex,
1734 "The ws connection shouldn't be already added in the "
1735 "serialization list.");
1736 #endif
1738 PRBool inserted =
1739 !!(sWSsConnecting->InsertElementSorted(this, nsWSNetAddressComparator()));
1740 NS_ASSERTION(inserted, "Couldn't insert the ws connection into the "
1741 "serialization list.");
1743 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1745 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(RemoveWSConnecting)
1747 if (mStatus == CONN_NOT_CONNECTED) {
1748 return;
1750 PRUint32 index =
1751 sWSsConnecting->BinaryIndexOf(this, nsWSNetAddressComparator());
1752 if (index != nsTArray<PRNetAddr>::NoIndex) {
1753 sWSsConnecting->RemoveElementAt(index);
1756 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1758 // static
1759 void
1760 nsWebSocketEstablishedConnection::TryConnect(nsITimer* aTimer,
1761 void* aClosure)
1763 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1765 nsresult rv;
1766 nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
1767 static_cast<nsWebSocketEstablishedConnection*>(aClosure);
1769 if (!thisObject->mOwner) { // we have been disconnected
1770 return;
1773 PRUint32 index = sWSsConnecting->BinaryIndexOf(thisObject,
1774 nsWSNetAddressComparator());
1775 if (index != nsTArray<PRNetAddr>::NoIndex) {
1776 // try again after TIMEOUT_TRY_CONNECT_AGAIN second
1777 rv = thisObject->mTryConnectTimer->
1778 InitWithFuncCallback(TryConnect, thisObject,
1779 TIMEOUT_TRY_CONNECT_AGAIN, nsITimer::TYPE_ONE_SHOT);
1780 CHECK_SUCCESS_AND_FAIL_IF_FAILED2(rv);
1781 } else {
1782 rv = thisObject->DoConnect();
1783 CHECK_SUCCESS_AND_FAIL_IF_FAILED2(rv);
1787 // static
1788 void
1789 nsWebSocketEstablishedConnection::
1790 TimerInitialServerResponseCallback(nsITimer* aTimer,
1791 void* aClosure)
1793 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1795 nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
1796 static_cast<nsWebSocketEstablishedConnection*>(aClosure);
1798 if (!thisObject->mOwner) { // we have been disconnected
1799 return;
1802 thisObject->FailConnection();
1805 // static
1806 void
1807 nsWebSocketEstablishedConnection::TimerForceCloseCallback(nsITimer* aTimer,
1808 void* aClosure)
1810 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1812 nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
1813 static_cast<nsWebSocketEstablishedConnection*>(aClosure);
1815 if (!thisObject->mOwner) { // we have been disconnected
1816 return;
1819 thisObject->ForceClose();
1822 nsresult
1823 nsWebSocketEstablishedConnection::ProcessHeaders()
1825 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
1827 nsresult rv;
1829 if (mAuthenticating) {
1830 if (mHeaders[kProxyAuthenticatePos].IsEmpty())
1831 return NS_ERROR_UNEXPECTED;
1832 return NS_OK;
1835 if (mReadingProxyConnectResponse) {
1836 return NS_OK;
1839 // test the upgrade header
1841 if (!mHeaders[kUpgradePos].EqualsLiteral("WebSocket")) {
1842 return NS_ERROR_UNEXPECTED;
1845 // test the connection header
1847 if (!mHeaders[kConnectionPos].LowerCaseEqualsLiteral("upgrade")) {
1848 return NS_ERROR_UNEXPECTED;
1851 // test the sec-websocket-origin header
1853 nsCString responseOriginHeader = mHeaders[kSecWebSocketOriginPos];
1854 ToLowerCase(responseOriginHeader);
1856 if (!responseOriginHeader.Equals(mOwner->mOrigin)) {
1857 return NS_ERROR_UNEXPECTED;
1860 // test the sec-websocket-location header
1862 nsCString validWebSocketLocation1, validWebSocketLocation2;
1863 validWebSocketLocation1.Append(mOwner->mSecure ? "wss://" : "ws://");
1864 validWebSocketLocation1.Append(mOwner->mAsciiHost);
1865 validWebSocketLocation1.Append(":");
1866 validWebSocketLocation1.AppendInt(mOwner->mPort);
1867 validWebSocketLocation1.Append(mOwner->mResource);
1869 if ((mOwner->mSecure && mOwner->mPort != DEFAULT_WSS_SCHEME_PORT) ||
1870 (!mOwner->mSecure && mOwner->mPort != DEFAULT_WS_SCHEME_PORT)) {
1871 validWebSocketLocation2 = validWebSocketLocation1;
1872 } else {
1873 validWebSocketLocation2.Append(mOwner->mSecure ? "wss://" : "ws://");
1874 validWebSocketLocation2.Append(mOwner->mAsciiHost);
1875 validWebSocketLocation2.Append(mOwner->mResource);
1878 if (!mHeaders[kSecWebSocketLocationPos].Equals(validWebSocketLocation1) &&
1879 !mHeaders[kSecWebSocketLocationPos].Equals(validWebSocketLocation2)) {
1880 return NS_ERROR_UNEXPECTED;
1883 // handle the sec-websocket-protocol header
1884 if (!mOwner->mProtocol.IsEmpty() &&
1885 !mHeaders[kSecWebSocketProtocolPos].
1886 Equals(mOwner->mProtocol)) {
1887 return NS_ERROR_UNEXPECTED;
1890 // handle the set-cookie header
1892 if (!mHeaders[kSetCookiePos].IsEmpty()) {
1893 rv = HandleSetCookieHeader();
1894 NS_ENSURE_SUCCESS(rv, rv);
1897 return NS_OK;
1900 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(HandleSetCookieHeader)
1902 nsresult rv;
1904 nsCOMPtr<nsICookieService> cookieService =
1905 do_GetService(NS_COOKIESERVICE_CONTRACTID);
1906 nsCOMPtr<nsIDocument> doc =
1907 nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
1909 if (!cookieService || !doc) {
1910 return;
1913 nsCOMPtr<nsIURI> documentURI = doc->GetDocumentURI();
1914 if (!documentURI) {
1915 return;
1918 nsCOMPtr<nsIPromptFactory> wwatch =
1919 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
1920 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
1922 nsCOMPtr<nsIPrompt> prompt;
1923 nsCOMPtr<nsPIDOMWindow> outerWindow = doc->GetWindow();
1924 rv = wwatch->GetPrompt(outerWindow, NS_GET_IID(nsIPrompt),
1925 getter_AddRefs(prompt));
1926 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
1928 rv = cookieService->SetCookieStringFromHttp(documentURI,
1929 documentURI,
1930 prompt,
1931 mHeaders[kSetCookiePos].get(),
1932 nsnull,
1933 nsnull);
1934 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
1936 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1938 nsresult
1939 nsWebSocketEstablishedConnection::PrintErrorOnConsole(const char *aBundleURI,
1940 const PRUnichar *aError,
1941 const PRUnichar **aFormatStrings,
1942 PRUint32 aFormatStringsLen)
1944 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1946 nsresult rv;
1948 nsCOMPtr<nsIStringBundleService> bundleService =
1949 do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
1950 NS_ENSURE_SUCCESS(rv, rv);
1952 nsCOMPtr<nsIStringBundle> strBundle;
1953 rv = bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
1954 NS_ENSURE_SUCCESS(rv, rv);
1956 nsCOMPtr<nsIConsoleService> console(
1957 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
1958 NS_ENSURE_SUCCESS(rv, rv);
1960 nsCOMPtr<nsIScriptError2> errorObject(
1961 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
1962 NS_ENSURE_SUCCESS(rv, rv);
1964 // Localize the error message
1965 nsXPIDLString message;
1966 if (aFormatStrings) {
1967 rv = strBundle->FormatStringFromName(aError, aFormatStrings,
1968 aFormatStringsLen,
1969 getter_Copies(message));
1970 } else {
1971 rv = strBundle->GetStringFromName(aError, getter_Copies(message));
1973 NS_ENSURE_SUCCESS(rv, rv);
1975 errorObject->InitWithWindowID
1976 (message.get(),
1977 NS_ConvertUTF8toUTF16(mOwner->GetScriptFile()).get(),
1978 nsnull,
1979 mOwner->GetScriptLine(), 0, nsIScriptError::errorFlag,
1980 "Web Socket", mOwner->WindowID()
1983 // print the error message directly to the JS console
1984 nsCOMPtr<nsIScriptError> logError(do_QueryInterface(errorObject));
1985 rv = console->LogMessage(logError);
1986 NS_ENSURE_SUCCESS(rv, rv);
1988 return NS_OK;
1991 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Close)
1993 nsresult rv;
1995 // Disconnect() can release this object, so we keep a
1996 // reference until the end of the method
1997 nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
1999 if (mOwner->mReadyState == nsIWebSocket::CONNECTING) {
2000 mOwner->SetReadyState(nsIWebSocket::CLOSING);
2001 mOwner->SetReadyState(nsIWebSocket::CLOSED);
2002 Disconnect();
2003 return;
2006 mOwner->SetReadyState(nsIWebSocket::CLOSING);
2007 if (!mCloseFrameServerResponseTimer) {
2008 mCloseFrameServerResponseTimer =
2009 do_CreateInstance("@mozilla.org/timer;1", &rv);
2011 if (NS_FAILED(rv)) {
2012 NS_WARNING("Failed to create mCloseFrameServerResponseTimer.");
2013 } else {
2014 rv = mCloseFrameServerResponseTimer->
2015 InitWithFuncCallback(TimerForceCloseCallback, this,
2016 TIMEOUT_WAIT_FOR_CLOSING, nsITimer::TYPE_ONE_SHOT);
2017 if (NS_FAILED(rv)) {
2018 NS_WARNING("Failed to start the ForceClose timeout.");
2023 if (mStatus == CONN_CLOSED) {
2024 mOwner->SetReadyState(nsIWebSocket::CLOSED);
2025 Disconnect();
2026 } else if (!mPostedCloseFrame) {
2027 nsAutoPtr<nsCString> closeFrame(new nsCString());
2028 if (!closeFrame.get()) {
2029 return;
2032 closeFrame->SetLength(2);
2033 if (closeFrame->Length() != 2) {
2034 return;
2037 closeFrame->SetCharAt(START_BYTE_OF_CLOSE_FRAME, 0);
2038 closeFrame->SetCharAt(END_BYTE_OF_CLOSE_FRAME, 1);
2040 rv = PostData(closeFrame.forget(), eCloseFrame);
2041 if (NS_FAILED(rv)) {
2042 NS_WARNING("Failed to post the close frame");
2043 return;
2045 } else {
2046 // Probably failed to send the close frame. Just disconnect.
2047 Disconnect();
2050 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2052 void
2053 nsWebSocketEstablishedConnection::ForceClose()
2055 // Disconnect() can release this object, so we keep a
2056 // reference until the end of the method
2057 nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
2059 mOwner->SetReadyState(nsIWebSocket::CLOSING);
2060 mOwner->SetReadyState(nsIWebSocket::CLOSED);
2061 Disconnect();
2064 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FailConnection)
2066 nsresult rv;
2067 nsWSAutoClose autoClose(this);
2069 if (mFailureStatus == NS_OK) {
2070 mFailureStatus = NS_ERROR_UNEXPECTED;
2073 nsCAutoString targetSpec;
2074 rv = mOwner->mURI->GetSpec(targetSpec);
2075 WARN_IF_FALSE_AND_RETURN(NS_SUCCEEDED(rv), "Failed to get targetSpec");
2077 NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
2078 const PRUnichar *formatStrings[] = { specUTF16.get() };
2080 if (mStatus < CONN_CONNECTED_AND_READY) {
2081 if (mCurrentProxyConfig == eResolvingProxyFailed) {
2082 PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
2083 NS_LITERAL_STRING("proxyConnectFailure").get(),
2084 nsnull, 0);
2086 PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
2087 NS_LITERAL_STRING("connectionFailure").get(),
2088 formatStrings, NS_ARRAY_LENGTH(formatStrings));
2089 } else {
2090 PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
2091 NS_LITERAL_STRING("netInterrupt").get(),
2092 formatStrings, NS_ARRAY_LENGTH(formatStrings));
2095 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2097 nsresult
2098 nsWebSocketEstablishedConnection::Disconnect()
2100 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2103 if (!mOwner) {
2104 return NS_OK;
2107 MutexAutoLock lockDisconnect(mLockDisconnect);
2109 // If mOwner is deleted when calling mOwner->DontKeepAliveAnyMore()
2110 // then this method can be called again, and we will get a deadlock.
2111 nsRefPtr<nsWebSocket> kungfuDeathGrip = mOwner;
2113 mOwner->DontKeepAliveAnyMore();
2115 RemoveWSConnecting();
2117 mStatus = CONN_CLOSED;
2118 mOwner = nsnull;
2120 if (mAuthProvider) {
2121 mAuthProvider->Disconnect(NS_ERROR_ABORT);
2122 mAuthProvider = nsnull;
2125 if (mTryConnectTimer) {
2126 mTryConnectTimer->Cancel();
2127 mTryConnectTimer = nsnull;
2130 if (mInitialServerResponseTimer) {
2131 mInitialServerResponseTimer->Cancel();
2132 mInitialServerResponseTimer = nsnull;
2135 if (mCloseFrameServerResponseTimer) {
2136 mCloseFrameServerResponseTimer->Cancel();
2137 mCloseFrameServerResponseTimer = nsnull;
2140 if (mProxyResolveCancelable) {
2141 mProxyResolveCancelable->Cancel(NS_ERROR_ABORT);
2142 mProxyResolveCancelable = nsnull;
2145 if (mDNSRequest) {
2146 mDNSRequest->Cancel(NS_ERROR_ABORT);
2147 mDNSRequest = nsnull;
2150 if (mSocketInput) {
2151 mSocketInput->Close();
2152 mSocketInput = nsnull;
2154 if (mSocketOutput) {
2155 mSocketOutput->Close();
2156 mSocketOutput = nsnull;
2158 if (mSocketTransport) {
2159 mSocketTransport->Close(NS_OK);
2160 mSocketTransport = nsnull;
2162 mProxyInfo = nsnull;
2164 while (mOutgoingMessages.GetSize() != 0) {
2165 delete static_cast<nsWSFrame*>(mOutgoingMessages.PopFront());
2168 while (mReceivedMessages.GetSize() != 0) {
2169 delete static_cast<nsCString*>(mReceivedMessages.PopFront());
2172 // Remove ourselves from the document's load group. nsIRequest expects
2173 // this be done asynchronously.
2174 nsCOMPtr<nsIRunnable> event =
2175 NS_NewRunnableMethod(this, &nsWebSocketEstablishedConnection::
2176 RemoveFromLoadGroup);
2177 if (event) {
2178 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
2181 nsLayoutStatics::Release();
2184 return NS_OK;
2187 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Retry)
2189 nsresult rv;
2191 for (PRUint32 i = 0; i < kHeadersLen; ++i) {
2192 mHeaders[i].Truncate();
2195 rv = OnProxyAvailable(nsnull, mOwner->mURI, mProxyInfo, NS_OK);
2196 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
2198 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2200 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(ResolveNextProxyAndConnect)
2202 nsresult rv;
2204 if (mCurrentProxyConfig == eResolvingProxyFailed) {
2205 return;
2208 nsCOMPtr<nsIProtocolProxyService2> proxyService =
2209 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
2210 if (NS_FAILED(rv)) {
2211 NS_WARNING("Failed getting proxyService");
2212 mCurrentProxyConfig = eResolvingProxyFailed;
2213 mFailureStatus = NS_ERROR_UNKNOWN_PROXY_HOST;
2214 FailConnection();
2215 return;
2218 if (mProxyInfo) {
2219 // If there was already a proxy info it means we tried to connect to its
2220 // proxy, but we couldn't. We have to remove the connection from the
2221 // serialization list, reset it, and try to get a failover proxy.
2224 MutexAutoLock lockDisconnect(mLockDisconnect);
2225 rv = Reset();
2226 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
2229 nsCOMPtr<nsIProxyInfo> pi;
2230 rv = proxyService->GetFailoverForProxy(mProxyInfo, mOwner->mURI,
2231 mProxyFailureReason,
2232 getter_AddRefs(pi));
2233 if (NS_FAILED(rv)) {
2234 mProxyInfo = nsnull;
2235 ResolveNextProxyAndConnect();
2236 return;
2239 OnProxyAvailable(nsnull, mOwner->mURI, pi, NS_OK);
2240 return;
2243 // if (!mProxyInfo)
2245 PRUint32 flags = nsIProtocolProxyService::RESOLVE_IGNORE_URI_SCHEME;
2247 if (mCurrentProxyConfig == eNotResolvingProxy) {
2248 mCurrentProxyConfig = eResolvingSOCKSProxy;
2249 flags |= nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY;
2250 } else if (mCurrentProxyConfig == eResolvingSOCKSProxy) {
2251 flags |= nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY;
2252 mCurrentProxyConfig = eResolvingHTTPSProxy;
2253 } else if (mCurrentProxyConfig == eResolvingHTTPSProxy) {
2254 mCurrentProxyConfig = eResolvingHTTPProxy;
2255 } else if (mCurrentProxyConfig == eResolvingHTTPProxy) {
2256 mCurrentProxyConfig = eResolvingProxyFailed;
2257 mFailureStatus = NS_ERROR_UNKNOWN_PROXY_HOST;
2258 FailConnection();
2259 return;
2262 rv = proxyService->AsyncResolve(mOwner->mURI,
2263 flags, this,
2264 getter_AddRefs(mProxyResolveCancelable));
2265 if (NS_FAILED(rv)) {
2266 ResolveNextProxyAndConnect();
2267 return;
2270 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2272 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(UpdateMustKeepAlive)
2274 mOwner->UpdateMustKeepAlive();
2276 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2278 void
2279 nsWebSocketEstablishedConnection::RemoveFromLoadGroup()
2281 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2282 nsCOMPtr<nsILoadGroup> loadGroup;
2283 GetLoadGroup(getter_AddRefs(loadGroup));
2284 if (loadGroup) {
2285 loadGroup->RemoveRequest(this, nsnull, NS_OK);
2289 //-----------------------------------------------------------------------------
2290 // Authentication
2291 //-----------------------------------------------------------------------------
2293 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(ProcessAuthentication)
2295 nsresult rv = mAuthProvider->ProcessAuthentication(407, PR_FALSE);
2297 if (rv == NS_ERROR_IN_PROGRESS) {
2298 return;
2301 if (NS_FAILED(rv)) {
2302 NS_WARNING("ProcessAuthentication failed");
2303 FailConnection();
2304 return;
2307 Retry();
2309 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2311 //-----------------------------------------------------------------------------
2312 //-----------------------------------------------------------------------------
2314 #define NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) \
2315 NS_IMETHODIMP \
2316 nsWebSocketEstablishedConnection::_func
2318 #define NOT_IMPLEMENTED_IF_FUNC_END(_func) \
2320 return NS_ERROR_NOT_IMPLEMENTED; \
2323 #define NOT_IMPLEMENTED_IF_FUNC_0(_func) \
2324 NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) () \
2325 NOT_IMPLEMENTED_IF_FUNC_END(_func)
2327 #define NOT_IMPLEMENTED_IF_FUNC_1(_func, _arg) \
2328 NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) (_arg) \
2329 NOT_IMPLEMENTED_IF_FUNC_END(_func)
2331 #define NOT_IMPLEMENTED_IF_FUNC_2(_func, _arg1, arg2) \
2332 NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) (_arg1, arg2) \
2333 NOT_IMPLEMENTED_IF_FUNC_END(_func)
2335 //-----------------------------------------------------------------------------
2336 // nsWebSocketEstablishedConnection::nsIRequest
2337 //-----------------------------------------------------------------------------
2339 NS_IMETHODIMP
2340 nsWebSocketEstablishedConnection::GetName(nsACString &aName)
2342 aName = mRequestName;
2343 return NS_OK;
2346 NS_IMETHODIMP
2347 nsWebSocketEstablishedConnection::IsPending(PRBool *aValue)
2349 *aValue = !!(mOwner);
2350 return NS_OK;
2353 NS_IMETHODIMP
2354 nsWebSocketEstablishedConnection::GetStatus(nsresult *aStatus)
2356 *aStatus = mFailureStatus;
2357 return NS_OK;
2360 NS_IMETHODIMP
2361 nsWebSocketEstablishedConnection::Cancel(nsresult aStatus)
2363 if (!mOwner) {
2364 return NS_OK;
2367 mFailureStatus = aStatus;
2369 return Close();
2372 NOT_IMPLEMENTED_IF_FUNC_0(Suspend)
2373 NOT_IMPLEMENTED_IF_FUNC_0(Resume)
2375 NS_IMETHODIMP
2376 nsWebSocketEstablishedConnection::GetLoadGroup(nsILoadGroup **aLoadGroup)
2378 *aLoadGroup = nsnull;
2379 if (!mOwner)
2380 return NS_OK;
2382 nsCOMPtr<nsIDocument> doc =
2383 nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
2385 if (doc) {
2386 *aLoadGroup = doc->GetDocumentLoadGroup().get(); // already_AddRefed
2389 return NS_OK;
2392 NS_IMETHODIMP
2393 nsWebSocketEstablishedConnection::SetLoadGroup(nsILoadGroup *aLoadGroup)
2395 return NS_ERROR_UNEXPECTED;
2398 NS_IMETHODIMP
2399 nsWebSocketEstablishedConnection::GetLoadFlags(nsLoadFlags *aLoadFlags)
2401 *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
2402 return NS_OK;
2405 NS_IMETHODIMP
2406 nsWebSocketEstablishedConnection::SetLoadFlags(nsLoadFlags aLoadFlags)
2408 // we won't change the load flags at all.
2409 return NS_OK;
2412 //-----------------------------------------------------------------------------
2413 // nsWebSocketEstablishedConnection::nsIChannel
2414 //-----------------------------------------------------------------------------
2416 NOT_IMPLEMENTED_IF_FUNC_1(GetOriginalURI, nsIURI **originalURI)
2417 NOT_IMPLEMENTED_IF_FUNC_1(SetOriginalURI, nsIURI *originalURI)
2418 NOT_IMPLEMENTED_IF_FUNC_1(GetOwner, nsISupports **owner)
2419 NOT_IMPLEMENTED_IF_FUNC_1(SetOwner, nsISupports *owner)
2420 NOT_IMPLEMENTED_IF_FUNC_1(SetNotificationCallbacks,
2421 nsIInterfaceRequestor *callbacks)
2422 NOT_IMPLEMENTED_IF_FUNC_1(GetSecurityInfo, nsISupports **securityInfo)
2423 NOT_IMPLEMENTED_IF_FUNC_1(GetContentType, nsACString &value)
2424 NOT_IMPLEMENTED_IF_FUNC_1(SetContentType, const nsACString &value)
2425 NOT_IMPLEMENTED_IF_FUNC_1(GetContentCharset, nsACString &value)
2426 NOT_IMPLEMENTED_IF_FUNC_1(SetContentCharset, const nsACString &value)
2427 NOT_IMPLEMENTED_IF_FUNC_1(GetContentLength, PRInt32 *value)
2428 NOT_IMPLEMENTED_IF_FUNC_1(SetContentLength, PRInt32 value)
2429 NOT_IMPLEMENTED_IF_FUNC_1(Open, nsIInputStream **_retval)
2430 NOT_IMPLEMENTED_IF_FUNC_2(AsyncOpen, nsIStreamListener *listener,
2431 nsISupports *context)
2433 //-----------------------------------------------------------------------------
2434 // nsWebSocketEstablishedConnection::nsIHttpAuthenticableChannel
2435 //-----------------------------------------------------------------------------
2437 NS_IMETHODIMP
2438 nsWebSocketEstablishedConnection::GetProxyInfo(nsIProxyInfo **result)
2440 NS_IF_ADDREF(*result = mProxyInfo);
2441 return NS_OK;
2444 NS_IMETHODIMP
2445 nsWebSocketEstablishedConnection::GetIsSSL(PRBool *aIsSSL)
2447 *aIsSSL = mOwner->mSecure;
2448 return NS_OK;
2451 NS_IMETHODIMP
2452 nsWebSocketEstablishedConnection::GetProxyMethodIsConnect(PRBool *aProxyMethodIsConnect)
2454 *aProxyMethodIsConnect = UsingHttpProxy();
2455 return NS_OK;
2458 NS_IMETHODIMP
2459 nsWebSocketEstablishedConnection::GetURI(nsIURI **aURI)
2461 NS_IF_ADDREF(*aURI = mOwner->mURI);
2462 return NS_OK;
2465 NS_IMETHODIMP
2466 nsWebSocketEstablishedConnection::GetNotificationCallbacks(nsIInterfaceRequestor **callbacks)
2468 NS_ADDREF(*callbacks = this);
2469 return NS_OK;
2472 NS_IMETHODIMP
2473 nsWebSocketEstablishedConnection::GetRequestMethod(nsACString &method)
2475 if (mAuthenticating) {
2476 method.AssignLiteral("CONNECT");
2477 } else {
2478 method.AssignLiteral("GET");
2480 return NS_OK;
2483 NS_IMETHODIMP
2484 nsWebSocketEstablishedConnection::GetServerResponseHeader(nsACString &value)
2486 if (mHeaders[kServerPos].IsEmpty()) {
2487 return NS_ERROR_NOT_AVAILABLE;
2490 value.Assign(mHeaders[kServerPos]);
2491 return NS_OK;
2494 NS_IMETHODIMP
2495 nsWebSocketEstablishedConnection::GetProxyChallenges(nsACString &value)
2497 if (mHeaders[kProxyAuthenticatePos].IsEmpty()) {
2498 return NS_ERROR_NOT_AVAILABLE;
2500 value.Assign(mHeaders[kProxyAuthenticatePos]);
2501 return NS_OK;
2504 NS_IMETHODIMP
2505 nsWebSocketEstablishedConnection::GetWWWChallenges(nsACString &value)
2507 return NS_ERROR_NOT_AVAILABLE;
2510 NS_IMETHODIMP
2511 nsWebSocketEstablishedConnection::SetProxyCredentials(const nsACString &value)
2513 mProxyCredentials.Assign(value);
2514 return NS_OK;
2517 NS_IMETHODIMP
2518 nsWebSocketEstablishedConnection::SetWWWCredentials(const nsACString &value)
2520 mCredentials.Assign(value);
2521 return NS_OK;
2524 NS_IMETHODIMP
2525 nsWebSocketEstablishedConnection::OnAuthAvailable()
2527 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2529 if (!mOwner) {
2530 return NS_OK;
2533 return Retry();
2536 NS_IMETHODIMP
2537 nsWebSocketEstablishedConnection::OnAuthCancelled(PRBool userCancel)
2539 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2541 if (!mOwner) {
2542 return NS_OK;
2545 if (!userCancel) {
2546 return FailConnection();
2549 return Close();
2552 //-----------------------------------------------------------------------------
2553 // nsWebSocketEstablishedConnection::nsIDNSListener
2554 //-----------------------------------------------------------------------------
2556 NS_IMETHODIMP
2557 nsWebSocketEstablishedConnection::OnLookupComplete(nsICancelable *aRequest,
2558 nsIDNSRecord *aRec,
2559 nsresult aStatus)
2561 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2563 if (!mOwner) {
2564 return NS_ERROR_ABORT;
2567 mDNSRequest = nsnull;
2568 mFailureStatus = aStatus;
2569 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(aStatus, aStatus);
2571 nsresult rv;
2573 rv = aRec->GetNextAddr(mOwner->mPort, &mPRNetAddr);
2574 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2576 TryConnect(nsnull, this);
2578 return NS_OK;
2581 //-----------------------------------------------------------------------------
2582 // nsWebSocketEstablishedConnection::nsIProtocolProxyCallback
2583 //-----------------------------------------------------------------------------
2585 NS_IMETHODIMP
2586 nsWebSocketEstablishedConnection::OnProxyAvailable(nsICancelable *aRequest,
2587 nsIURI *aUri,
2588 nsIProxyInfo *aProxyInfo,
2589 nsresult aStatus)
2591 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2593 nsresult rv;
2595 if (!mOwner) {
2596 return NS_ERROR_ABORT;
2599 if (NS_FAILED(aStatus)) {
2600 return ResolveNextProxyAndConnect();
2603 mProxyInfo = aProxyInfo;
2605 if (mProxyInfo) {
2606 TryConnect(nsnull, this);
2607 } else {
2608 // we need the server IP address because it must connect only one instance
2609 // per IP address at a time.
2611 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
2612 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2614 nsCOMPtr<nsIThread> thread = do_GetMainThread();
2615 rv = dns->AsyncResolve(mOwner->mAsciiHost,
2616 0, this, thread, getter_AddRefs(mDNSRequest));
2617 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2620 return NS_OK;
2623 //-----------------------------------------------------------------------------
2624 // nsWebSocketEstablishedConnection::nsIInputStreamCallback methods:
2625 //-----------------------------------------------------------------------------
2627 nsresult
2628 nsWebSocketEstablishedConnection::OnInputStreamReady(nsIAsyncInputStream *aStream)
2630 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
2632 nsresult rv;
2635 MutexAutoLock lockDisconnect(mLockDisconnect);
2637 if (!mOwner) {
2638 return NS_ERROR_ABORT;
2641 NS_ASSERTION(aStream == mSocketInput, "unexpected stream");
2643 while (PR_TRUE) {
2644 if (mBuffer.Length() - mBytesInBuffer < DEFAULT_BUFFER_SIZE) {
2645 PRUint32 newLen = mBuffer.Length() + DEFAULT_BUFFER_SIZE;
2646 mBuffer.SetLength(newLen);
2647 ENSURE_TRUE_AND_FAIL_IF_FAILED(mBuffer.Length() == newLen,
2648 NS_ERROR_OUT_OF_MEMORY);
2651 PRUint32 read;
2652 rv = aStream->Read(mBuffer.BeginWriting() + mBytesInBuffer,
2653 DEFAULT_BUFFER_SIZE, &read);
2654 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2655 break;
2657 mFailureStatus = rv;
2658 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2660 // check if the stream has been closed
2661 if (read == 0) {
2662 // If we are asking for credentials then the old connection has been
2663 // closed. In this case we have to reset the WebSocket, not Close it.
2664 if (mStatus != CONN_RETRYING_TO_AUTHENTICATE) {
2665 mStatus = CONN_CLOSED;
2666 mFailureStatus = NS_BASE_STREAM_CLOSED;
2667 if (mStatus < CONN_CONNECTED_AND_READY) {
2668 FailConnection();
2669 } else {
2670 Close();
2673 return NS_BASE_STREAM_CLOSED;
2676 PRUint32 start = mBytesInBuffer;
2677 mBytesInBuffer += read;
2678 rv = HandleNewInputString(start);
2679 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2682 rv = mSocketInput->AsyncWait(this, 0, 0, gWebSocketThread);
2683 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2686 return NS_OK;
2689 //-----------------------------------------------------------------------------
2690 // nsWebSocketEstablishedConnection::nsIOutputStreamCallback methods:
2691 //-----------------------------------------------------------------------------
2693 nsresult
2694 nsWebSocketEstablishedConnection::OnOutputStreamReady(nsIAsyncOutputStream *aStream)
2696 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
2698 nsresult rv;
2701 MutexAutoLock lockDisconnect(mLockDisconnect);
2703 if (!mOwner) {
2704 return NS_ERROR_ABORT;
2707 NS_ASSERTION(aStream == mSocketOutput, "unexpected stream");
2710 MutexAutoLock lockOut(mLockOutgoingMessages);
2712 while (PR_TRUE) {
2713 if (mOutgoingMessages.GetSize() == 0) {
2714 break;
2717 // send what we can of the 1st string
2719 nsWSFrame *frameToSend =
2720 static_cast<nsWSFrame*>(mOutgoingMessages.PeekFront());
2721 nsCString *strToSend = frameToSend->mData;
2722 PRUint32 sizeToSend =
2723 strToSend->Length() - mBytesAlreadySentOfFirstOutString;
2724 PRBool currentStrHasStartFrameByte =
2725 (mBytesAlreadySentOfFirstOutString == 0);
2727 if (sizeToSend != 0) {
2728 PRUint32 written;
2729 rv = aStream->Write(strToSend->get() + mBytesAlreadySentOfFirstOutString,
2730 sizeToSend, &written);
2731 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2732 break;
2735 // on proxy errors when connecting, try to failover
2736 if ((mStatus == CONN_CONNECTING_TO_HTTP_PROXY ||
2737 (mStatus == CONN_SENDING_INITIAL_REQUEST && mProxyInfo)) &&
2738 (rv == NS_ERROR_PROXY_CONNECTION_REFUSED ||
2739 rv == NS_ERROR_UNKNOWN_PROXY_HOST ||
2740 rv == NS_ERROR_NET_TIMEOUT ||
2741 rv == NS_ERROR_NET_RESET)) {
2742 mProxyFailureReason = rv;
2743 return ResolveNextProxyAndConnect();
2746 mFailureStatus = rv;
2747 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2749 if (written == 0) {
2750 mStatus = CONN_CLOSED;
2751 mFailureStatus = NS_BASE_STREAM_CLOSED;
2752 if (mStatus < CONN_CONNECTED_AND_READY) {
2753 FailConnection();
2754 } else {
2755 Close();
2757 return NS_BASE_STREAM_CLOSED;
2760 if (frameToSend->mType == eUTF8MessageFrame) {
2761 PRBool currentStrHasEndFrameByte =
2762 (mBytesAlreadySentOfFirstOutString + written ==
2763 strToSend->Length());
2765 // START_BYTE_OF_MESSAGE and END_BYTE_OF_MESSAGE bytes don't count
2766 if (currentStrHasStartFrameByte) {
2767 if (currentStrHasEndFrameByte) {
2768 mOutgoingBufferedAmount -= written - 2;
2769 } else {
2770 mOutgoingBufferedAmount -= written - 1;
2772 } else {
2773 if (currentStrHasEndFrameByte) {
2774 mOutgoingBufferedAmount -= written - 1;
2775 } else {
2776 mOutgoingBufferedAmount -= written;
2781 mBytesAlreadySentOfFirstOutString += written;
2784 sizeToSend = strToSend->Length() - mBytesAlreadySentOfFirstOutString;
2785 if (sizeToSend != 0) { // if different, we try sending what remain after
2786 break;
2789 // ok, send the next string
2790 if (frameToSend->mType == eCloseFrame) {
2791 mSentCloseFrame = PR_TRUE;
2793 mOutgoingMessages.PopFront();
2794 delete frameToSend;
2795 mBytesAlreadySentOfFirstOutString = 0;
2796 UpdateMustKeepAlive();
2799 if (mOutgoingMessages.GetSize() != 0) {
2800 rv = mSocketOutput->AsyncWait(this, 0, 0, gWebSocketThread);
2801 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2802 } else {
2803 if (mStatus == CONN_SENDING_ACK_CLOSE_FRAME && mSentCloseFrame) {
2804 mClosedCleanly = PR_TRUE;
2805 mStatus = CONN_CLOSED;
2806 return Close();
2809 if (mStatus == CONN_SENDING_INITIAL_REQUEST ||
2810 mStatus == CONN_CONNECTING_TO_HTTP_PROXY) {
2811 if (mStatus == CONN_SENDING_INITIAL_REQUEST) {
2812 mStatus = CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST;
2814 rv = mInitialServerResponseTimer->
2815 InitWithFuncCallback(TimerInitialServerResponseCallback, this,
2816 TIMEOUT_WAIT_FOR_SERVER_RESPONSE,
2817 nsITimer::TYPE_ONE_SHOT);
2818 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2820 rv = mSocketInput->AsyncWait(this, 0, 0, gWebSocketThread);
2821 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
2827 return NS_OK;
2830 //-----------------------------------------------------------------------------
2831 // nsWebSocketEstablishedConnection::nsIInterfaceRequestor
2832 //-----------------------------------------------------------------------------
2834 NS_IMETHODIMP
2835 nsWebSocketEstablishedConnection::GetInterface(const nsIID &aIID,
2836 void **aResult)
2838 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2840 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
2841 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
2842 nsresult rv;
2844 nsCOMPtr<nsIDocument> doc =
2845 nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
2847 if (!doc) {
2848 return NS_ERROR_NOT_AVAILABLE;
2851 nsCOMPtr<nsIPromptFactory> wwatch =
2852 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
2853 NS_ENSURE_SUCCESS(rv, rv);
2855 nsCOMPtr<nsPIDOMWindow> outerWindow = doc->GetWindow();
2856 return wwatch->GetPrompt(outerWindow, aIID, aResult);
2859 return NS_ERROR_UNEXPECTED;
2862 ////////////////////////////////////////////////////////////////////////////////
2863 // nsWebSocket
2864 ////////////////////////////////////////////////////////////////////////////////
2866 nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE),
2867 mCheckMustKeepAlive(PR_TRUE),
2868 mTriggeredCloseEvent(PR_FALSE),
2869 mReadyState(nsIWebSocket::CONNECTING),
2870 mOutgoingBufferedAmount(0),
2871 mScriptLine(0),
2872 mWindowID(0)
2876 nsWebSocket::~nsWebSocket()
2878 if (mConnection) {
2879 mConnection->Disconnect();
2880 mConnection = nsnull;
2882 if (mListenerManager) {
2883 mListenerManager->Disconnect();
2884 mListenerManager = nsnull;
2888 NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket)
2890 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsWebSocket,
2891 nsDOMEventTargetWrapperCache)
2892 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnOpenListener)
2893 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnMessageListener)
2894 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCloseListener)
2895 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
2896 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
2897 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mURI)
2898 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mConnection");
2899 cb.NoteXPCOMChild(static_cast<nsIInterfaceRequestor*>(tmp->mConnection));
2900 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2902 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsWebSocket,
2903 nsDOMEventTargetWrapperCache)
2904 if (tmp->mConnection) {
2905 tmp->mConnection->Disconnect();
2906 tmp->mConnection = nsnull;
2908 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnOpenListener)
2909 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnMessageListener)
2910 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCloseListener)
2911 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
2912 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal)
2913 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mURI)
2914 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2916 DOMCI_DATA(WebSocket, nsWebSocket)
2918 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsWebSocket)
2919 NS_INTERFACE_MAP_ENTRY(nsIWebSocket)
2920 NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
2921 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket)
2922 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache)
2924 NS_IMPL_ADDREF_INHERITED(nsWebSocket, nsDOMEventTargetWrapperCache)
2925 NS_IMPL_RELEASE_INHERITED(nsWebSocket, nsDOMEventTargetWrapperCache)
2927 //-----------------------------------------------------------------------------
2928 // nsWebSocket::nsIJSNativeInitializer methods:
2929 //-----------------------------------------------------------------------------
2932 * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
2933 * It is used for constructing our nsWebSocket from JavaScript. It expects a URL
2934 * string parameter and an optional protocol parameter. It also initializes the
2935 * principal, the script context and the window owner.
2937 NS_IMETHODIMP
2938 nsWebSocket::Initialize(nsISupports* aOwner,
2939 JSContext* aContext,
2940 JSObject* aObject,
2941 PRUint32 aArgc,
2942 jsval* aArgv)
2944 nsAutoString urlParam, protocolParam;
2946 if (!PrefEnabled()) {
2947 return NS_ERROR_DOM_SECURITY_ERR;
2950 if (aArgc != 1 && aArgc != 2) {
2951 return NS_ERROR_DOM_SYNTAX_ERR;
2954 JSAutoRequest ar(aContext);
2956 JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
2957 if (!jsstr) {
2958 return NS_ERROR_DOM_SYNTAX_ERR;
2961 size_t length;
2962 const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
2963 if (!chars) {
2964 return NS_ERROR_OUT_OF_MEMORY;
2967 urlParam.Assign(chars, length);
2969 if (aArgc == 2) {
2970 jsstr = JS_ValueToString(aContext, aArgv[1]);
2971 if (!jsstr) {
2972 return NS_ERROR_DOM_SYNTAX_ERR;
2975 chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
2976 if (!chars) {
2977 return NS_ERROR_OUT_OF_MEMORY;
2980 protocolParam.Assign(chars, length);
2981 if (protocolParam.IsEmpty()) {
2982 return NS_ERROR_DOM_SYNTAX_ERR;
2986 nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
2987 NS_ENSURE_STATE(ownerWindow);
2989 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
2990 NS_ENSURE_STATE(sgo);
2991 nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
2992 NS_ENSURE_STATE(scriptContext);
2994 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aOwner));
2995 NS_ENSURE_STATE(scriptPrincipal);
2996 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
2997 NS_ENSURE_STATE(principal);
2999 return Init(principal, scriptContext, ownerWindow, urlParam, protocolParam);
3002 //-----------------------------------------------------------------------------
3003 // nsWebSocket methods:
3004 //-----------------------------------------------------------------------------
3006 nsresult
3007 nsWebSocket::EstablishConnection()
3009 NS_ASSERTION(!mConnection, "mConnection should be null");
3011 nsresult rv;
3013 nsRefPtr<nsWebSocketEstablishedConnection> conn =
3014 new nsWebSocketEstablishedConnection();
3015 NS_ENSURE_TRUE(conn, NS_ERROR_OUT_OF_MEMORY);
3017 rv = conn->Init(this);
3018 NS_ENSURE_SUCCESS(rv, rv);
3020 mConnection = conn;
3022 return NS_OK;
3025 class nsWSCloseEvent : public nsRunnable
3027 public:
3028 nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean)
3029 : mWebSocket(aWebSocket),
3030 mWasClean(aWasClean)
3033 NS_IMETHOD Run()
3035 nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean);
3036 mWebSocket->UpdateMustKeepAlive();
3037 return rv;
3040 private:
3041 nsRefPtr<nsWebSocket> mWebSocket;
3042 PRBool mWasClean;
3045 nsresult
3046 nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
3048 nsresult rv;
3050 rv = CheckInnerWindowCorrectness();
3051 if (NS_FAILED(rv)) {
3052 return NS_OK;
3055 nsCOMPtr<nsIDOMEvent> event;
3056 rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
3057 NS_ENSURE_SUCCESS(rv, rv);
3059 // it doesn't bubble, and it isn't cancelable
3060 rv = event->InitEvent(aName, PR_FALSE, PR_FALSE);
3061 NS_ENSURE_SUCCESS(rv, rv);
3063 nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
3064 rv = privateEvent->SetTrusted(PR_TRUE);
3065 NS_ENSURE_SUCCESS(rv, rv);
3067 return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
3070 nsresult
3071 nsWebSocket::CreateAndDispatchMessageEvent(nsCString *aData)
3073 nsresult rv;
3075 rv = CheckInnerWindowCorrectness();
3076 if (NS_FAILED(rv)) {
3077 return NS_OK;
3080 // create an event that uses the MessageEvent interface,
3081 // which does not bubble, is not cancelable, and has no default action
3083 nsCOMPtr<nsIDOMEvent> event;
3084 rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nsnull, nsnull);
3085 NS_ENSURE_SUCCESS(rv, rv);
3087 nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
3088 rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
3089 PR_FALSE, PR_FALSE,
3090 NS_ConvertUTF8toUTF16(*aData),
3091 NS_ConvertUTF8toUTF16(mOrigin),
3092 EmptyString(), nsnull);
3093 NS_ENSURE_SUCCESS(rv, rv);
3095 nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
3096 rv = privateEvent->SetTrusted(PR_TRUE);
3097 NS_ENSURE_SUCCESS(rv, rv);
3099 return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
3102 nsresult
3103 nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean)
3105 nsresult rv;
3107 mTriggeredCloseEvent = PR_TRUE;
3109 rv = CheckInnerWindowCorrectness();
3110 if (NS_FAILED(rv)) {
3111 return NS_OK;
3114 // create an event that uses the CloseEvent interface,
3115 // which does not bubble, is not cancelable, and has no default action
3117 nsCOMPtr<nsIDOMEvent> event;
3118 rv = NS_NewDOMCloseEvent(getter_AddRefs(event), nsnull, nsnull);
3119 NS_ENSURE_SUCCESS(rv, rv);
3121 nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
3122 rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
3123 PR_FALSE, PR_FALSE,
3124 aWasClean);
3125 NS_ENSURE_SUCCESS(rv, rv);
3127 nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
3128 rv = privateEvent->SetTrusted(PR_TRUE);
3129 NS_ENSURE_SUCCESS(rv, rv);
3131 return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
3134 PRBool
3135 nsWebSocket::PrefEnabled()
3137 return nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE) &&
3138 nsContentUtils::GetBoolPref("network.websocket.override-security-block",
3139 PR_FALSE);
3142 void
3143 nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
3145 nsresult rv;
3147 if (mReadyState == aNewReadyState) {
3148 return;
3151 NS_ASSERTION((aNewReadyState == nsIWebSocket::OPEN) ||
3152 (aNewReadyState == nsIWebSocket::CLOSING) ||
3153 (aNewReadyState == nsIWebSocket::CLOSED),
3154 "unexpected readyState");
3156 if (aNewReadyState == nsIWebSocket::OPEN) {
3157 NS_ASSERTION(mReadyState == nsIWebSocket::CONNECTING,
3158 "unexpected readyState transition");
3159 mReadyState = aNewReadyState;
3161 rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
3162 if (NS_FAILED(rv)) {
3163 NS_WARNING("Failed to dispatch the open event");
3165 UpdateMustKeepAlive();
3166 return;
3169 if (aNewReadyState == nsIWebSocket::CLOSING) {
3170 NS_ASSERTION((mReadyState == nsIWebSocket::CONNECTING) ||
3171 (mReadyState == nsIWebSocket::OPEN),
3172 "unexpected readyState transition");
3173 mReadyState = aNewReadyState;
3174 return;
3177 if (aNewReadyState == nsIWebSocket::CLOSED) {
3178 NS_ASSERTION(mReadyState == nsIWebSocket::CLOSING,
3179 "unexpected readyState transition");
3180 mReadyState = aNewReadyState;
3182 // The close event must be dispatched asynchronously.
3183 nsCOMPtr<nsIRunnable> event =
3184 new nsWSCloseEvent(this, mConnection->ClosedCleanly());
3186 mOutgoingBufferedAmount += mConnection->GetOutgoingBufferedAmount();
3187 mConnection = nsnull; // this is no longer necessary
3189 rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
3190 if (NS_FAILED(rv)) {
3191 NS_WARNING("Failed to dispatch the close event");
3192 mTriggeredCloseEvent = PR_TRUE;
3193 UpdateMustKeepAlive();
3198 nsresult
3199 nsWebSocket::ParseURL(const nsString& aURL)
3201 nsresult rv;
3203 NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
3205 nsCOMPtr<nsIURI> uri;
3206 rv = NS_NewURI(getter_AddRefs(uri), aURL);
3207 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3209 nsCOMPtr<nsIURL> parsedURL(do_QueryInterface(uri, &rv));
3210 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3212 nsCAutoString fragment;
3213 rv = parsedURL->GetRef(fragment);
3214 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && fragment.IsEmpty(),
3215 NS_ERROR_DOM_SYNTAX_ERR);
3217 nsCAutoString scheme;
3218 rv = parsedURL->GetScheme(scheme);
3219 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !scheme.IsEmpty(),
3220 NS_ERROR_DOM_SYNTAX_ERR);
3222 nsCAutoString host;
3223 rv = parsedURL->GetAsciiHost(host);
3224 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !host.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
3226 PRInt32 port;
3227 rv = parsedURL->GetPort(&port);
3228 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3230 rv = NS_CheckPortSafety(port, scheme.get());
3231 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3233 nsCAutoString filePath;
3234 rv = parsedURL->GetFilePath(filePath);
3235 if (filePath.IsEmpty()) {
3236 filePath.AssignLiteral("/");
3238 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3240 nsCAutoString query;
3241 rv = parsedURL->GetQuery(query);
3242 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3244 nsXPIDLCString origin;
3245 rv = mPrincipal->GetOrigin(getter_Copies(origin));
3246 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
3248 if (scheme.LowerCaseEqualsLiteral("ws")) {
3249 mSecure = PR_FALSE;
3250 mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
3251 } else if (scheme.LowerCaseEqualsLiteral("wss")) {
3252 mSecure = PR_TRUE;
3253 mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
3254 } else {
3255 return NS_ERROR_DOM_SYNTAX_ERR;
3258 mOrigin = origin;
3259 ToLowerCase(mOrigin);
3261 mAsciiHost = host;
3262 ToLowerCase(mAsciiHost);
3264 mResource = filePath;
3265 if (!query.IsEmpty()) {
3266 mResource.AppendLiteral("?");
3267 mResource.Append(query);
3269 PRUint32 length = mResource.Length();
3270 PRUint32 i;
3271 for (i = 0; i < length; ++i) {
3272 if (mResource[i] < static_cast<PRUnichar>(0x0021) ||
3273 mResource[i] > static_cast<PRUnichar>(0x007E)) {
3274 return NS_ERROR_DOM_SYNTAX_ERR;
3278 mOriginalURL = aURL;
3280 mURI = parsedURL;
3282 return NS_OK;
3285 nsresult
3286 nsWebSocket::SetProtocol(const nsString& aProtocol)
3288 if (aProtocol.IsEmpty()) {
3289 return NS_ERROR_DOM_SYNTAX_ERR;
3292 PRUint32 length = aProtocol.Length();
3293 PRUint32 i;
3294 for (i = 0; i < length; ++i) {
3295 if (aProtocol[i] < static_cast<PRUnichar>(0x0020) ||
3296 aProtocol[i] > static_cast<PRUnichar>(0x007E)) {
3297 return NS_ERROR_DOM_SYNTAX_ERR;
3301 CopyUTF16toUTF8(aProtocol, mProtocol);
3302 return NS_OK;
3305 //-----------------------------------------------------------------------------
3306 // Methods that keep alive the WebSocket object when:
3307 // 1. the object has registered event listeners that can be triggered
3308 // ("strong event listeners");
3309 // 2. there are outgoing not sent messages.
3310 //-----------------------------------------------------------------------------
3312 void
3313 nsWebSocket::UpdateMustKeepAlive()
3315 if (!mCheckMustKeepAlive) {
3316 return;
3319 PRBool shouldKeepAlive = PR_FALSE;
3321 if (mListenerManager) {
3322 switch (mReadyState)
3324 case nsIWebSocket::CONNECTING:
3326 if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("open")) ||
3327 mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) ||
3328 mListenerManager->HasListenersFor(NS_LITERAL_STRING("close"))) {
3329 shouldKeepAlive = PR_TRUE;
3332 break;
3334 case nsIWebSocket::OPEN:
3335 case nsIWebSocket::CLOSING:
3337 if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) ||
3338 mListenerManager->HasListenersFor(NS_LITERAL_STRING("close")) ||
3339 mConnection->HasOutgoingMessages()) {
3340 shouldKeepAlive = PR_TRUE;
3343 break;
3345 case nsIWebSocket::CLOSED:
3347 shouldKeepAlive =
3348 (!mTriggeredCloseEvent &&
3349 mListenerManager->HasListenersFor(NS_LITERAL_STRING("close")));
3354 if (mKeepingAlive && !shouldKeepAlive) {
3355 mKeepingAlive = PR_FALSE;
3356 static_cast<nsPIDOMEventTarget*>(this)->Release();
3357 } else if (!mKeepingAlive && shouldKeepAlive) {
3358 mKeepingAlive = PR_TRUE;
3359 static_cast<nsPIDOMEventTarget*>(this)->AddRef();
3363 void
3364 nsWebSocket::DontKeepAliveAnyMore()
3366 if (mKeepingAlive) {
3367 mKeepingAlive = PR_FALSE;
3368 static_cast<nsPIDOMEventTarget*>(this)->Release();
3370 mCheckMustKeepAlive = PR_FALSE;
3373 NS_IMETHODIMP
3374 nsWebSocket::AddEventListener(const nsAString& aType,
3375 nsIDOMEventListener* aListener,
3376 PRBool aUseCapture)
3378 nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType,
3379 aListener,
3380 aUseCapture);
3381 if (NS_SUCCEEDED(rv)) {
3382 UpdateMustKeepAlive();
3384 return rv;
3387 NS_IMETHODIMP
3388 nsWebSocket::RemoveEventListener(const nsAString& aType,
3389 nsIDOMEventListener* aListener,
3390 PRBool aUseCapture)
3392 nsresult rv = nsDOMEventTargetHelper::RemoveEventListener(aType,
3393 aListener,
3394 aUseCapture);
3395 if (NS_SUCCEEDED(rv)) {
3396 UpdateMustKeepAlive();
3398 return rv;
3401 NS_IMETHODIMP
3402 nsWebSocket::AddEventListener(const nsAString& aType,
3403 nsIDOMEventListener *aListener,
3404 PRBool aUseCapture,
3405 PRBool aWantsUntrusted,
3406 PRUint8 optional_argc)
3408 nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType,
3409 aListener,
3410 aUseCapture,
3411 aWantsUntrusted,
3412 optional_argc);
3413 if (NS_SUCCEEDED(rv)) {
3414 UpdateMustKeepAlive();
3416 return rv;
3419 //-----------------------------------------------------------------------------
3420 // nsWebSocket::nsIWebSocket methods:
3421 //-----------------------------------------------------------------------------
3423 NS_IMETHODIMP
3424 nsWebSocket::GetUrl(nsAString& aURL)
3426 aURL = mOriginalURL;
3427 return NS_OK;
3430 NS_IMETHODIMP
3431 nsWebSocket::GetReadyState(PRUint16 *aReadyState)
3433 *aReadyState = mReadyState;
3434 return NS_OK;
3437 NS_IMETHODIMP
3438 nsWebSocket::GetBufferedAmount(PRUint32 *aBufferedAmount)
3440 if (!mConnection) {
3441 *aBufferedAmount = mOutgoingBufferedAmount;
3442 } else {
3443 *aBufferedAmount = mConnection->GetOutgoingBufferedAmount();
3445 return NS_OK;
3448 #define NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(_eventlistenername, _eventlistener) \
3449 NS_IMETHODIMP \
3450 nsWebSocket::GetOn##_eventlistenername(nsIDOMEventListener * *aEventListener)\
3452 return GetInnerEventListener(_eventlistener, aEventListener); \
3455 NS_IMETHODIMP \
3456 nsWebSocket::SetOn##_eventlistenername(nsIDOMEventListener * aEventListener) \
3458 return RemoveAddEventListener(NS_LITERAL_STRING(#_eventlistenername), \
3459 _eventlistener, aEventListener); \
3462 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
3463 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
3464 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
3465 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
3467 NS_IMETHODIMP
3468 nsWebSocket::Send(const nsAString& aData, PRBool *aRet)
3470 *aRet = PR_FALSE;
3472 if (mReadyState == nsIWebSocket::CONNECTING) {
3473 return NS_ERROR_DOM_INVALID_STATE_ERR;
3476 // We need to check if there isn't unpaired surrogates.
3477 PRUint32 i, length = aData.Length();
3478 for (i = 0; i < length; ++i) {
3479 if (NS_IS_LOW_SURROGATE(aData[i])) {
3480 return NS_ERROR_DOM_SYNTAX_ERR;
3482 if (NS_IS_HIGH_SURROGATE(aData[i])) {
3483 if (i + 1 == length || !NS_IS_LOW_SURROGATE(aData[i + 1])) {
3484 return NS_ERROR_DOM_SYNTAX_ERR;
3486 ++i;
3487 continue;
3491 if (mReadyState == nsIWebSocket::CLOSING ||
3492 mReadyState == nsIWebSocket::CLOSED) {
3493 mOutgoingBufferedAmount += NS_ConvertUTF16toUTF8(aData).Length();
3494 return NS_OK;
3497 nsresult rv = mConnection->PostMessage(PromiseFlatString(aData));
3498 *aRet = NS_SUCCEEDED(rv);
3500 return NS_OK;
3503 NS_IMETHODIMP
3504 nsWebSocket::Close()
3506 if (mReadyState == nsIWebSocket::CLOSING ||
3507 mReadyState == nsIWebSocket::CLOSED) {
3508 return NS_OK;
3511 if (mReadyState == nsIWebSocket::CONNECTING) {
3512 // FailConnection() can release the object, so we keep a reference
3513 // before calling it
3514 nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
3516 mConnection->FailConnection();
3517 return NS_OK;
3520 // mReadyState == nsIWebSocket::OPEN
3521 mConnection->Close();
3523 return NS_OK;
3527 * This Init method should only be called by C++ consumers.
3529 NS_IMETHODIMP
3530 nsWebSocket::Init(nsIPrincipal* aPrincipal,
3531 nsIScriptContext* aScriptContext,
3532 nsPIDOMWindow* aOwnerWindow,
3533 const nsAString& aURL,
3534 const nsAString& aProtocol)
3536 nsresult rv;
3538 NS_ENSURE_ARG(aPrincipal);
3540 if (!PrefEnabled()) {
3541 return NS_ERROR_DOM_SECURITY_ERR;
3544 mPrincipal = aPrincipal;
3545 mScriptContext = aScriptContext;
3546 if (aOwnerWindow) {
3547 mOwner = aOwnerWindow->IsOuterWindow() ?
3548 aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow;
3550 else {
3551 mOwner = nsnull;
3554 nsCOMPtr<nsIJSContextStack> stack =
3555 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
3556 JSContext* cx = nsnull;
3557 if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
3558 JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
3559 if (fp) {
3560 JSScript *script = JS_GetFrameScript(cx, fp);
3561 if (script) {
3562 mScriptFile = JS_GetScriptFilename(cx, script);
3565 jsbytecode *pc = JS_GetFramePC(cx, fp);
3566 if (script && pc) {
3567 mScriptLine = JS_PCToLineNumber(cx, script, pc);
3571 mWindowID = nsJSUtils::GetCurrentlyRunningCodeWindowID(cx);
3574 // parses the url
3575 rv = ParseURL(PromiseFlatString(aURL));
3576 NS_ENSURE_SUCCESS(rv, rv);
3578 // sets the protocol
3579 if (!aProtocol.IsEmpty()) {
3580 rv = SetProtocol(PromiseFlatString(aProtocol));
3581 NS_ENSURE_SUCCESS(rv, rv);
3584 // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
3585 // url parameter, so we don't care about the EstablishConnection result.
3586 EstablishConnection();
3588 return NS_OK;
3591 // static
3592 void
3593 nsWebSocket::ReleaseGlobals()
3595 if (nsWebSocketEstablishedConnection::sWSsConnecting) {
3596 nsWebSocketEstablishedConnection::sWSsConnecting->Clear();
3597 delete nsWebSocketEstablishedConnection::sWSsConnecting;
3598 nsWebSocketEstablishedConnection::sWSsConnecting = nsnull;
3600 if (gWebSocketThread) {
3601 gWebSocketThread->Shutdown();
3602 NS_RELEASE(gWebSocketThread);
3606 ////////////////////////////////////////////////////////////////////////////////
3607 // nsWSProtocolHandler
3608 ////////////////////////////////////////////////////////////////////////////////
3610 NS_IMPL_ISUPPORTS2(nsWSProtocolHandler,
3611 nsIProtocolHandler, nsIProxiedProtocolHandler)
3613 NS_IMETHODIMP
3614 nsWSProtocolHandler::GetScheme(nsACString& aScheme)
3616 aScheme.AssignLiteral("ws");
3617 return NS_OK;
3620 NS_IMETHODIMP
3621 nsWSProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
3623 *aDefaultPort = DEFAULT_WS_SCHEME_PORT;
3624 return NS_OK;
3627 NS_IMETHODIMP
3628 nsWSProtocolHandler::GetProtocolFlags(PRUint32 *aProtocolFlags)
3630 *aProtocolFlags = URI_STD | URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA |
3631 ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_DANGEROUS_TO_LOAD;
3632 return NS_OK;
3635 NS_IMETHODIMP
3636 nsWSProtocolHandler::NewURI(const nsACString& aSpec,
3637 const char *aCharset,
3638 nsIURI *aBaseURI,
3639 nsIURI **aURI)
3641 nsresult rv;
3642 nsCOMPtr<nsIStandardURL> url(do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv));
3643 NS_ENSURE_SUCCESS(rv, rv);
3645 PRInt32 defaultPort;
3646 GetDefaultPort(&defaultPort);
3648 rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
3649 defaultPort, aSpec, aCharset, aBaseURI);
3650 NS_ENSURE_SUCCESS(rv, rv);
3652 rv = CallQueryInterface(url, aURI);
3653 NS_ENSURE_SUCCESS(rv, rv);
3655 return NS_OK;
3658 NS_IMETHODIMP
3659 nsWSProtocolHandler::NewChannel(nsIURI *aURI,
3660 nsIChannel **aChannel)
3662 return NS_ERROR_NOT_AVAILABLE;
3665 NS_IMETHODIMP
3666 nsWSProtocolHandler::NewProxiedChannel(nsIURI *aURI,
3667 nsIProxyInfo* aProxyInfo,
3668 nsIChannel **aChannel)
3670 return NS_ERROR_NOT_AVAILABLE;
3673 NS_IMETHODIMP
3674 nsWSProtocolHandler::AllowPort(PRInt32 aPort,
3675 const char *aScheme,
3676 PRBool *aAllowPort)
3678 PRInt32 defaultPort;
3679 GetDefaultPort(&defaultPort);
3681 *aAllowPort = (aPort == defaultPort);
3682 return NS_OK;
3685 ////////////////////////////////////////////////////////////////////////////////
3686 // nsWSSProtocolHandler
3687 ////////////////////////////////////////////////////////////////////////////////
3689 NS_IMETHODIMP
3690 nsWSSProtocolHandler::GetScheme(nsACString& aScheme)
3692 aScheme.AssignLiteral("wss");
3693 return NS_OK;
3696 NS_IMETHODIMP
3697 nsWSSProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
3699 *aDefaultPort = DEFAULT_WSS_SCHEME_PORT;
3700 return NS_OK;