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
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.
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"
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"
53 #include "nsNetUtil.h"
54 #include "nsIStandardURL.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"
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"
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"
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) \
113 if (NS_UNLIKELY(!(x))) { \
114 NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \
120 #define ENSURE_SUCCESS_AND_FAIL_IF_FAILED(res, ret) \
122 nsresult __rv = res; \
123 if (NS_FAILED(__rv)) { \
124 NS_ENSURE_SUCCESS_BODY(res, ret) \
130 #define CHECK_TRUE_AND_FAIL_IF_FAILED(x) \
132 if (NS_UNLIKELY(!(x))) { \
133 NS_WARNING("CHECK_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \
139 #define CHECK_SUCCESS_AND_FAIL_IF_FAILED(res) \
141 nsresult __rv = res; \
142 if (NS_FAILED(__rv)) { \
143 NS_ENSURE_SUCCESS_BODY(res, ret) \
149 #define CHECK_SUCCESS_AND_FAIL_IF_FAILED2(res) \
151 nsresult __rv = res; \
152 if (NS_FAILED(__rv)) { \
153 NS_ENSURE_SUCCESS_BODY(res, ret) \
154 thisObject->FailConnection(); \
159 #define WARN_IF_FALSE_AND_RETURN(_expr, _msg) \
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) \
171 nsWebSocketEstablishedConnection::_method() \
173 if (!NS_IsMainThread()) { \
174 nsCOMPtr<nsIRunnable> event = \
175 NS_NewRunnableMethod(this, &nsWebSocketEstablishedConnection:: \
176 MainRunnable##_method); \
178 return NS_ERROR_OUT_OF_MEMORY; \
180 return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); \
182 MainRunnable##_method(); \
187 nsWebSocketEstablishedConnection::MainRunnable##_method() \
193 #define IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END \
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
,
211 public nsIHttpAuthenticableChannel
213 friend class nsWSNetAddressComparator
;
214 friend class nsWSAutoClose
;
217 nsWebSocketEstablishedConnection();
218 virtual ~nsWebSocketEstablishedConnection();
221 NS_DECL_NSIDNSLISTENER
222 NS_DECL_NSIPROTOCOLPROXYCALLBACK
223 NS_DECL_NSIINPUTSTREAMCALLBACK
224 NS_DECL_NSIOUTPUTSTREAMCALLBACK
225 NS_DECL_NSIINTERFACEREQUESTOR
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
;
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
,
283 // We wait for the initial server response for
284 // TIMEOUT_WAIT_FOR_SERVER_RESPONSE milliseconds
285 static void TimerInitialServerResponseCallback(nsITimer
*aTimer
,
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
,
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
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
,
310 PRBool
UsingHttpProxy();
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
360 kSecWebSocketOriginPos
,
361 kSecWebSocketLocationPos
,
362 kSecWebSocketProtocolPos
,
364 kProxyAuthenticatePos
,
365 kServerPos
, // for digest auth
369 // used only by the socket thread
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
381 eResolvingSOCKSProxy
,
382 eResolvingHTTPSProxy
,
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) ->
420 * CONN_CONNECTING_TO_HTTP_PROXY |
421 * CONN_SENDING_INITIAL_REQUEST |
424 * CONN_RETRYING_TO_AUTHENTICATE ->
425 * CONN_CONNECTING_TO_HTTP_PROXY |
426 * CONN_SENDING_INITIAL_REQUEST |
429 * CONN_CONNECTING_TO_HTTP_PROXY ->
430 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
433 * CONN_SENDING_INITIAL_REQUEST ->
434 * CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST |
437 * CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST ->
438 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
441 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME ->
442 * CONN_READING_RESPONSE_HEADER_NAME |
443 * CONN_WAITING_LF_CHAR_TO_CONNECTING |
446 * CONN_READING_RESPONSE_HEADER_NAME ->
447 * CONN_READING_RESPONSE_HEADER_VALUE |
450 * CONN_READING_RESPONSE_HEADER_VALUE ->
451 * CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER |
454 * CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER ->
455 * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
458 * CONN_WAITING_LF_CHAR_TO_CONNECTING ->
459 * CONN_SENDING_INITIAL_REQUEST |
460 * CONN_READING_CHALLENGE_RESPONSE |
461 * CONN_RETRYING_TO_AUTHENTICATE |
464 * CONN_READING_CHALLENGE_RESPONSE ->
465 * CONN_CONNECTED_AND_READY |
468 * CONN_CONNECTED_AND_READY ->
469 * CONN_HIGH_BIT_OF_FRAME_TYPE_SET |
470 * CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET |
473 * CONN_HIGH_BIT_OF_FRAME_TYPE_SET ->
474 * CONN_READING_AND_DISCARDING_LENGTH_BYTES |
475 * CONN_SENDING_ACK_CLOSE_FRAME |
478 * CONN_READING_AND_DISCARDING_LENGTH_BYTES ->
479 * CONN_CONNECTED_AND_READY |
482 * CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET ->
483 * CONN_CONNECTED_AND_READY |
486 * CONN_SENDING_ACK_CLOSE_FRAME ->
489 * CONN_CLOSED (final state)
493 enum ConnectionStatus
{
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)
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 //------------------------------------------------------------------------------
531 //------------------------------------------------------------------------------
533 class nsWSNetAddressComparator
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;
547 nsWSAutoClose(nsWebSocketEstablishedConnection
* conn
) : mConnection(conn
)
550 ~nsWSAutoClose() { mConnection
->Close(); }
553 nsWebSocketEstablishedConnection
* mConnection
;
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
)) {
568 return a
->mOwner
->mAsciiHost
.Equals(b
->mOwner
->mAsciiHost
);
571 if (a
->mPRNetAddr
.raw
.family
!= b
->mPRNetAddr
.raw
.family
) {
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];
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
) {
598 if (!a
->mProxyInfo
&& b
->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
) {
611 if (a
->mPRNetAddr
.raw
.family
== PR_AF_INET6
&&
612 b
->mPRNetAddr
.raw
.family
== PR_AF_INET
) {
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
,
640 nsIProtocolProxyCallback
,
641 nsIInputStreamCallback
,
642 nsIOutputStreamCallback
,
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),
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!");
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();
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();
726 nsWebSocketEstablishedConnection::PostMessage(const nsString
& aMessage
)
728 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
734 // only send messages when connected
735 NS_ENSURE_STATE(mStatus
>= CONN_CONNECTED_AND_READY
);
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();
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
);
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
);
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
);
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
);
795 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(DoInitialRequest
)
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
)
833 buf
->AppendLiteral("Upgrade: WebSocket\r\n");
837 case connectionHeader
:
839 buf
->AppendLiteral("Connection: Upgrade\r\n");
845 buf
->AppendLiteral("Host: ");
846 buf
->Append(mOwner
->mAsciiHost
);
847 buf
->AppendLiteral(":");
848 buf
->AppendInt(mOwner
->mPort
);
849 buf
->AppendLiteral("\r\n");
855 buf
->AppendLiteral("Origin: ");
856 buf
->Append(mOwner
->mOrigin
);
857 buf
->AppendLiteral("\r\n");
861 case secWebSocketProtocolHeader
:
863 if (!mOwner
->mProtocol
.IsEmpty()) {
864 buf
->AppendLiteral("Sec-WebSocket-Protocol: ");
865 buf
->Append(mOwner
->mProtocol
);
866 buf
->AppendLiteral("\r\n");
871 case authorizationHeaders
:
873 rv
= AddAuthorizationHeaders(*buf
, PR_FALSE
);
874 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv
);
880 rv
= AddCookiesToRequest(*buf
);
881 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv
);
885 case secWebSocketKey1Header
:
887 buf
->AppendLiteral("Sec-WebSocket-Key1: ");
889 buf
->AppendLiteral("\r\n");
893 case secWebSocketKey2Header
:
895 buf
->AppendLiteral("Sec-WebSocket-Key2: ");
897 buf
->AppendLiteral("\r\n");
902 headersToSend
.RemoveElementAt(headerPosToSendNow
);
905 buf
->AppendLiteral("\r\n");
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
917 GetHttpResponseCode(const nsCString
& aLine
, PRUint32 aLen
,
918 PRUint32
*aStatusCode
, PRUint32
*aLineLen
)
920 // make sure we have HTTP at the beginning
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,
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
;
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
;
959 return NS_ERROR_IN_PROGRESS
;
963 nsWebSocketEstablishedConnection::HandleNewInputString(PRUint32 aStart
)
965 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
967 if (mBytesInBuffer
== 0 || aStart
== mBytesInBuffer
) {
971 NS_ENSURE_STATE(aStart
< mBytesInBuffer
);
977 case CONN_CONNECTING_TO_HTTP_PROXY
:
979 PRUint32 statusCode
, lengthStr
;
981 rv
= GetHttpResponseCode(mBuffer
, mBytesInBuffer
, &statusCode
,
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
;
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);
1005 case CONN_SENDING_ACK_CLOSE_FRAME
:
1012 case CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST
:
1014 PRUint32 statusCode
, lengthStr
;
1016 rv
= GetHttpResponseCode(mBuffer
, mBytesInBuffer
, &statusCode
,
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);
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
);
1055 case CONN_READING_RESPONSE_HEADER_NAME
:
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);
1069 case CONN_READING_RESPONSE_HEADER_VALUE
:
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');
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);
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
;
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);
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
) {
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
);
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) {
1178 const nsCSubstring
& receivedMD5Challenge
= Substring(mBuffer
, 0, 16);
1179 if (!mExpectedMD5Challenge
.Equals(receivedMD5Challenge
)) {
1180 return NS_ERROR_UNEXPECTED
;
1183 mStatus
= CONN_CONNECTED_AND_READY
;
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;
1201 mStatus
= CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET
;
1204 return HandleNewInputString(1);
1208 case CONN_HIGH_BIT_OF_FRAME_TYPE_SET
:
1211 PRUint8 frameType
= mBuffer
[0];
1212 for (i
= aStart
; i
< mBytesInBuffer
; ++i
) {
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
) {
1226 if (mSentCloseFrame
) {
1227 mClosedCleanly
= PR_TRUE
;
1228 mStatus
= CONN_CLOSED
;
1230 mStatus
= CONN_SENDING_ACK_CLOSE_FRAME
;
1235 mStatus
= CONN_READING_AND_DISCARDING_LENGTH_BYTES
;
1236 return HandleNewInputString(i
+ 1);
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
;
1258 case CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET
:
1261 for (i
= aStart
; i
< mBytesInBuffer
; ++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
);
1284 rv
= DispatchNewMessage();
1285 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
1290 mBuffer
.Cut(0, i
+ 1);
1291 mBytesInBuffer
-= i
+ 1;
1293 mStatus
= CONN_CONNECTED_AND_READY
;
1294 return HandleNewInputString(0);
1301 NS_ASSERTION(PR_FALSE
, "Invalid state.");
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");
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
) {
1344 nsCOMPtr
<nsIURI
> documentURI
= doc
->GetDocumentURI();
1349 nsXPIDLCString cookieValue
;
1350 cookieService
->GetCookieStringFromHttp(documentURI
,
1353 getter_Copies(cookieValue
));
1354 if (!cookieValue
.IsEmpty()) {
1355 aStr
.AppendLiteral("Cookie: ");
1356 aStr
.Append(cookieValue
);
1357 aStr
.AppendLiteral("\r\n");
1364 nsWebSocketEstablishedConnection::GenerateSecKey(nsCString
& aKey
)
1368 PRUint32 spaces
= rand() % 12 + 1;
1369 PRUint32 max
= PR_UINT32_MAX
/ spaces
;
1370 PRUint32 number
= rand() % max
;
1371 PRUint32 product
= number
* spaces
;
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
1380 PRUint32 numberOfCharsToInsert
= rand() % 12 + 1;
1381 for (i
= 0; i
< numberOfCharsToInsert
; ++i
) {
1382 PRUint32 posToInsert
= rand() % key
.Length();
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
);
1403 nsWebSocketEstablishedConnection::GenerateRequestKeys(nsCString
& aKey1
,
1410 nsCAutoString key_1
;
1413 nsCAutoString key_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
);
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
);
1465 nsWebSocketEstablishedConnection::UsingHttpProxy()
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
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;
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
)
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
)
1533 nsAutoPtr
<nsCString
> data
;
1536 MutexAutoLock
lockIn(mLockReceivedMessages
);
1538 if (mReceivedMessages
.GetSize() == 0) {
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
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();
1569 nsWebSocketEstablishedConnection::Init(nsWebSocket
*aOwner
)
1571 // test if it has been alredy initialized
1572 NS_ASSERTION(!mOwner
, "WebSocket's connection is already initialized");
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
);
1600 rv
= loadGroup
->AddRequest(this, nsnull
);
1601 NS_ENSURE_SUCCESS(rv
, rv
);
1605 do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
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
) {
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
);
1632 nsWebSocketEstablishedConnection::DoConnect()
1634 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
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
) {
1655 if (value
.IsEmpty()) {
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
);
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");
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
);
1728 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(AddWSConnecting
)
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.");
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
) {
1751 sWSsConnecting
->BinaryIndexOf(this, nsWSNetAddressComparator());
1752 if (index
!= nsTArray
<PRNetAddr
>::NoIndex
) {
1753 sWSsConnecting
->RemoveElementAt(index
);
1756 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
1760 nsWebSocketEstablishedConnection::TryConnect(nsITimer
* aTimer
,
1763 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
1766 nsRefPtr
<nsWebSocketEstablishedConnection
> thisObject
=
1767 static_cast<nsWebSocketEstablishedConnection
*>(aClosure
);
1769 if (!thisObject
->mOwner
) { // we have been disconnected
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
);
1782 rv
= thisObject
->DoConnect();
1783 CHECK_SUCCESS_AND_FAIL_IF_FAILED2(rv
);
1789 nsWebSocketEstablishedConnection::
1790 TimerInitialServerResponseCallback(nsITimer
* aTimer
,
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
1802 thisObject
->FailConnection();
1807 nsWebSocketEstablishedConnection::TimerForceCloseCallback(nsITimer
* aTimer
,
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
1819 thisObject
->ForceClose();
1823 nsWebSocketEstablishedConnection::ProcessHeaders()
1825 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
1829 if (mAuthenticating
) {
1830 if (mHeaders
[kProxyAuthenticatePos
].IsEmpty())
1831 return NS_ERROR_UNEXPECTED
;
1835 if (mReadingProxyConnectResponse
) {
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
;
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
);
1900 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(HandleSetCookieHeader
)
1904 nsCOMPtr
<nsICookieService
> cookieService
=
1905 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
1906 nsCOMPtr
<nsIDocument
> doc
=
1907 nsContentUtils::GetDocumentFromScriptContext(mOwner
->mScriptContext
);
1909 if (!cookieService
|| !doc
) {
1913 nsCOMPtr
<nsIURI
> documentURI
= doc
->GetDocumentURI();
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
,
1931 mHeaders
[kSetCookiePos
].get(),
1934 CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv
);
1936 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
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");
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
,
1969 getter_Copies(message
));
1971 rv
= strBundle
->GetStringFromName(aError
, getter_Copies(message
));
1973 NS_ENSURE_SUCCESS(rv
, rv
);
1975 errorObject
->InitWithWindowID
1977 NS_ConvertUTF8toUTF16(mOwner
->GetScriptFile()).get(),
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
);
1991 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Close
)
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
);
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.");
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
);
2026 } else if (!mPostedCloseFrame
) {
2027 nsAutoPtr
<nsCString
> closeFrame(new nsCString());
2028 if (!closeFrame
.get()) {
2032 closeFrame
->SetLength(2);
2033 if (closeFrame
->Length() != 2) {
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");
2046 // Probably failed to send the close frame. Just disconnect.
2050 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
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
);
2064 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FailConnection
)
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(),
2086 PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
2087 NS_LITERAL_STRING("connectionFailure").get(),
2088 formatStrings
, NS_ARRAY_LENGTH(formatStrings
));
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
2098 nsWebSocketEstablishedConnection::Disconnect()
2100 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
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
;
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
;
2146 mDNSRequest
->Cancel(NS_ERROR_ABORT
);
2147 mDNSRequest
= nsnull
;
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
);
2178 NS_DispatchToMainThread(event
, NS_DISPATCH_NORMAL
);
2181 nsLayoutStatics::Release();
2187 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Retry
)
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
)
2204 if (mCurrentProxyConfig
== eResolvingProxyFailed
) {
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
;
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
);
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();
2239 OnProxyAvailable(nsnull
, mOwner
->mURI
, pi
, NS_OK
);
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
;
2262 rv
= proxyService
->AsyncResolve(mOwner
->mURI
,
2264 getter_AddRefs(mProxyResolveCancelable
));
2265 if (NS_FAILED(rv
)) {
2266 ResolveNextProxyAndConnect();
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
2279 nsWebSocketEstablishedConnection::RemoveFromLoadGroup()
2281 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2282 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2283 GetLoadGroup(getter_AddRefs(loadGroup
));
2285 loadGroup
->RemoveRequest(this, nsnull
, NS_OK
);
2289 //-----------------------------------------------------------------------------
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
) {
2301 if (NS_FAILED(rv
)) {
2302 NS_WARNING("ProcessAuthentication failed");
2309 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
2311 //-----------------------------------------------------------------------------
2312 //-----------------------------------------------------------------------------
2314 #define NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) \
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 //-----------------------------------------------------------------------------
2340 nsWebSocketEstablishedConnection::GetName(nsACString
&aName
)
2342 aName
= mRequestName
;
2347 nsWebSocketEstablishedConnection::IsPending(PRBool
*aValue
)
2349 *aValue
= !!(mOwner
);
2354 nsWebSocketEstablishedConnection::GetStatus(nsresult
*aStatus
)
2356 *aStatus
= mFailureStatus
;
2361 nsWebSocketEstablishedConnection::Cancel(nsresult aStatus
)
2367 mFailureStatus
= aStatus
;
2372 NOT_IMPLEMENTED_IF_FUNC_0(Suspend
)
2373 NOT_IMPLEMENTED_IF_FUNC_0(Resume
)
2376 nsWebSocketEstablishedConnection::GetLoadGroup(nsILoadGroup
**aLoadGroup
)
2378 *aLoadGroup
= nsnull
;
2382 nsCOMPtr
<nsIDocument
> doc
=
2383 nsContentUtils::GetDocumentFromScriptContext(mOwner
->mScriptContext
);
2386 *aLoadGroup
= doc
->GetDocumentLoadGroup().get(); // already_AddRefed
2393 nsWebSocketEstablishedConnection::SetLoadGroup(nsILoadGroup
*aLoadGroup
)
2395 return NS_ERROR_UNEXPECTED
;
2399 nsWebSocketEstablishedConnection::GetLoadFlags(nsLoadFlags
*aLoadFlags
)
2401 *aLoadFlags
= nsIRequest::LOAD_BACKGROUND
;
2406 nsWebSocketEstablishedConnection::SetLoadFlags(nsLoadFlags aLoadFlags
)
2408 // we won't change the load flags at all.
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 //-----------------------------------------------------------------------------
2438 nsWebSocketEstablishedConnection::GetProxyInfo(nsIProxyInfo
**result
)
2440 NS_IF_ADDREF(*result
= mProxyInfo
);
2445 nsWebSocketEstablishedConnection::GetIsSSL(PRBool
*aIsSSL
)
2447 *aIsSSL
= mOwner
->mSecure
;
2452 nsWebSocketEstablishedConnection::GetProxyMethodIsConnect(PRBool
*aProxyMethodIsConnect
)
2454 *aProxyMethodIsConnect
= UsingHttpProxy();
2459 nsWebSocketEstablishedConnection::GetURI(nsIURI
**aURI
)
2461 NS_IF_ADDREF(*aURI
= mOwner
->mURI
);
2466 nsWebSocketEstablishedConnection::GetNotificationCallbacks(nsIInterfaceRequestor
**callbacks
)
2468 NS_ADDREF(*callbacks
= this);
2473 nsWebSocketEstablishedConnection::GetRequestMethod(nsACString
&method
)
2475 if (mAuthenticating
) {
2476 method
.AssignLiteral("CONNECT");
2478 method
.AssignLiteral("GET");
2484 nsWebSocketEstablishedConnection::GetServerResponseHeader(nsACString
&value
)
2486 if (mHeaders
[kServerPos
].IsEmpty()) {
2487 return NS_ERROR_NOT_AVAILABLE
;
2490 value
.Assign(mHeaders
[kServerPos
]);
2495 nsWebSocketEstablishedConnection::GetProxyChallenges(nsACString
&value
)
2497 if (mHeaders
[kProxyAuthenticatePos
].IsEmpty()) {
2498 return NS_ERROR_NOT_AVAILABLE
;
2500 value
.Assign(mHeaders
[kProxyAuthenticatePos
]);
2505 nsWebSocketEstablishedConnection::GetWWWChallenges(nsACString
&value
)
2507 return NS_ERROR_NOT_AVAILABLE
;
2511 nsWebSocketEstablishedConnection::SetProxyCredentials(const nsACString
&value
)
2513 mProxyCredentials
.Assign(value
);
2518 nsWebSocketEstablishedConnection::SetWWWCredentials(const nsACString
&value
)
2520 mCredentials
.Assign(value
);
2525 nsWebSocketEstablishedConnection::OnAuthAvailable()
2527 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2537 nsWebSocketEstablishedConnection::OnAuthCancelled(PRBool userCancel
)
2539 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2546 return FailConnection();
2552 //-----------------------------------------------------------------------------
2553 // nsWebSocketEstablishedConnection::nsIDNSListener
2554 //-----------------------------------------------------------------------------
2557 nsWebSocketEstablishedConnection::OnLookupComplete(nsICancelable
*aRequest
,
2561 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2564 return NS_ERROR_ABORT
;
2567 mDNSRequest
= nsnull
;
2568 mFailureStatus
= aStatus
;
2569 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(aStatus
, aStatus
);
2573 rv
= aRec
->GetNextAddr(mOwner
->mPort
, &mPRNetAddr
);
2574 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv
, rv
);
2576 TryConnect(nsnull
, this);
2581 //-----------------------------------------------------------------------------
2582 // nsWebSocketEstablishedConnection::nsIProtocolProxyCallback
2583 //-----------------------------------------------------------------------------
2586 nsWebSocketEstablishedConnection::OnProxyAvailable(nsICancelable
*aRequest
,
2588 nsIProxyInfo
*aProxyInfo
,
2591 NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
2596 return NS_ERROR_ABORT
;
2599 if (NS_FAILED(aStatus
)) {
2600 return ResolveNextProxyAndConnect();
2603 mProxyInfo
= aProxyInfo
;
2606 TryConnect(nsnull
, this);
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
);
2623 //-----------------------------------------------------------------------------
2624 // nsWebSocketEstablishedConnection::nsIInputStreamCallback methods:
2625 //-----------------------------------------------------------------------------
2628 nsWebSocketEstablishedConnection::OnInputStreamReady(nsIAsyncInputStream
*aStream
)
2630 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
2635 MutexAutoLock
lockDisconnect(mLockDisconnect
);
2638 return NS_ERROR_ABORT
;
2641 NS_ASSERTION(aStream
== mSocketInput
, "unexpected stream");
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
);
2652 rv
= aStream
->Read(mBuffer
.BeginWriting() + mBytesInBuffer
,
2653 DEFAULT_BUFFER_SIZE
, &read
);
2654 if (rv
== NS_BASE_STREAM_WOULD_BLOCK
) {
2657 mFailureStatus
= rv
;
2658 ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv
, rv
);
2660 // check if the stream has been closed
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
) {
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
);
2689 //-----------------------------------------------------------------------------
2690 // nsWebSocketEstablishedConnection::nsIOutputStreamCallback methods:
2691 //-----------------------------------------------------------------------------
2694 nsWebSocketEstablishedConnection::OnOutputStreamReady(nsIAsyncOutputStream
*aStream
)
2696 NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
2701 MutexAutoLock
lockDisconnect(mLockDisconnect
);
2704 return NS_ERROR_ABORT
;
2707 NS_ASSERTION(aStream
== mSocketOutput
, "unexpected stream");
2710 MutexAutoLock
lockOut(mLockOutgoingMessages
);
2713 if (mOutgoingMessages
.GetSize() == 0) {
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) {
2729 rv
= aStream
->Write(strToSend
->get() + mBytesAlreadySentOfFirstOutString
,
2730 sizeToSend
, &written
);
2731 if (rv
== NS_BASE_STREAM_WOULD_BLOCK
) {
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
);
2750 mStatus
= CONN_CLOSED
;
2751 mFailureStatus
= NS_BASE_STREAM_CLOSED
;
2752 if (mStatus
< CONN_CONNECTED_AND_READY
) {
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;
2770 mOutgoingBufferedAmount
-= written
- 1;
2773 if (currentStrHasEndFrameByte
) {
2774 mOutgoingBufferedAmount
-= written
- 1;
2776 mOutgoingBufferedAmount
-= written
;
2781 mBytesAlreadySentOfFirstOutString
+= written
;
2784 sizeToSend
= strToSend
->Length() - mBytesAlreadySentOfFirstOutString
;
2785 if (sizeToSend
!= 0) { // if different, we try sending what remain after
2789 // ok, send the next string
2790 if (frameToSend
->mType
== eCloseFrame
) {
2791 mSentCloseFrame
= PR_TRUE
;
2793 mOutgoingMessages
.PopFront();
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
);
2803 if (mStatus
== CONN_SENDING_ACK_CLOSE_FRAME
&& mSentCloseFrame
) {
2804 mClosedCleanly
= PR_TRUE
;
2805 mStatus
= CONN_CLOSED
;
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
);
2830 //-----------------------------------------------------------------------------
2831 // nsWebSocketEstablishedConnection::nsIInterfaceRequestor
2832 //-----------------------------------------------------------------------------
2835 nsWebSocketEstablishedConnection::GetInterface(const nsIID
&aIID
,
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
))) {
2844 nsCOMPtr
<nsIDocument
> doc
=
2845 nsContentUtils::GetDocumentFromScriptContext(mOwner
->mScriptContext
);
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 ////////////////////////////////////////////////////////////////////////////////
2864 ////////////////////////////////////////////////////////////////////////////////
2866 nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE
),
2867 mCheckMustKeepAlive(PR_TRUE
),
2868 mTriggeredCloseEvent(PR_FALSE
),
2869 mReadyState(nsIWebSocket::CONNECTING
),
2870 mOutgoingBufferedAmount(0),
2876 nsWebSocket::~nsWebSocket()
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.
2938 nsWebSocket::Initialize(nsISupports
* aOwner
,
2939 JSContext
* aContext
,
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]);
2958 return NS_ERROR_DOM_SYNTAX_ERR
;
2962 const jschar
*chars
= JS_GetStringCharsAndLength(aContext
, jsstr
, &length
);
2964 return NS_ERROR_OUT_OF_MEMORY
;
2967 urlParam
.Assign(chars
, length
);
2970 jsstr
= JS_ValueToString(aContext
, aArgv
[1]);
2972 return NS_ERROR_DOM_SYNTAX_ERR
;
2975 chars
= JS_GetStringCharsAndLength(aContext
, jsstr
, &length
);
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 //-----------------------------------------------------------------------------
3007 nsWebSocket::EstablishConnection()
3009 NS_ASSERTION(!mConnection
, "mConnection should be null");
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
);
3025 class nsWSCloseEvent
: public nsRunnable
3028 nsWSCloseEvent(nsWebSocket
*aWebSocket
, PRBool aWasClean
)
3029 : mWebSocket(aWebSocket
),
3030 mWasClean(aWasClean
)
3035 nsresult rv
= mWebSocket
->CreateAndDispatchCloseEvent(mWasClean
);
3036 mWebSocket
->UpdateMustKeepAlive();
3041 nsRefPtr
<nsWebSocket
> mWebSocket
;
3046 nsWebSocket::CreateAndDispatchSimpleEvent(const nsString
& aName
)
3050 rv
= CheckInnerWindowCorrectness();
3051 if (NS_FAILED(rv
)) {
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
);
3071 nsWebSocket::CreateAndDispatchMessageEvent(nsCString
*aData
)
3075 rv
= CheckInnerWindowCorrectness();
3076 if (NS_FAILED(rv
)) {
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"),
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
);
3103 nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean
)
3107 mTriggeredCloseEvent
= PR_TRUE
;
3109 rv
= CheckInnerWindowCorrectness();
3110 if (NS_FAILED(rv
)) {
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"),
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
);
3135 nsWebSocket::PrefEnabled()
3137 return nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE
) &&
3138 nsContentUtils::GetBoolPref("network.websocket.override-security-block",
3143 nsWebSocket::SetReadyState(PRUint16 aNewReadyState
)
3147 if (mReadyState
== aNewReadyState
) {
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();
3169 if (aNewReadyState
== nsIWebSocket::CLOSING
) {
3170 NS_ASSERTION((mReadyState
== nsIWebSocket::CONNECTING
) ||
3171 (mReadyState
== nsIWebSocket::OPEN
),
3172 "unexpected readyState transition");
3173 mReadyState
= aNewReadyState
;
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();
3199 nsWebSocket::ParseURL(const nsString
& aURL
)
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
);
3223 rv
= parsedURL
->GetAsciiHost(host
);
3224 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && !host
.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR
);
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")) {
3250 mPort
= (port
== -1) ? DEFAULT_WS_SCHEME_PORT
: port
;
3251 } else if (scheme
.LowerCaseEqualsLiteral("wss")) {
3253 mPort
= (port
== -1) ? DEFAULT_WSS_SCHEME_PORT
: port
;
3255 return NS_ERROR_DOM_SYNTAX_ERR
;
3259 ToLowerCase(mOrigin
);
3262 ToLowerCase(mAsciiHost
);
3264 mResource
= filePath
;
3265 if (!query
.IsEmpty()) {
3266 mResource
.AppendLiteral("?");
3267 mResource
.Append(query
);
3269 PRUint32 length
= mResource
.Length();
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
;
3286 nsWebSocket::SetProtocol(const nsString
& aProtocol
)
3288 if (aProtocol
.IsEmpty()) {
3289 return NS_ERROR_DOM_SYNTAX_ERR
;
3292 PRUint32 length
= aProtocol
.Length();
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
);
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 //-----------------------------------------------------------------------------
3313 nsWebSocket::UpdateMustKeepAlive()
3315 if (!mCheckMustKeepAlive
) {
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
;
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
;
3345 case nsIWebSocket::CLOSED
:
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();
3364 nsWebSocket::DontKeepAliveAnyMore()
3366 if (mKeepingAlive
) {
3367 mKeepingAlive
= PR_FALSE
;
3368 static_cast<nsPIDOMEventTarget
*>(this)->Release();
3370 mCheckMustKeepAlive
= PR_FALSE
;
3374 nsWebSocket::AddEventListener(const nsAString
& aType
,
3375 nsIDOMEventListener
* aListener
,
3378 nsresult rv
= nsDOMEventTargetHelper::AddEventListener(aType
,
3381 if (NS_SUCCEEDED(rv
)) {
3382 UpdateMustKeepAlive();
3388 nsWebSocket::RemoveEventListener(const nsAString
& aType
,
3389 nsIDOMEventListener
* aListener
,
3392 nsresult rv
= nsDOMEventTargetHelper::RemoveEventListener(aType
,
3395 if (NS_SUCCEEDED(rv
)) {
3396 UpdateMustKeepAlive();
3402 nsWebSocket::AddEventListener(const nsAString
& aType
,
3403 nsIDOMEventListener
*aListener
,
3405 PRBool aWantsUntrusted
,
3406 PRUint8 optional_argc
)
3408 nsresult rv
= nsDOMEventTargetHelper::AddEventListener(aType
,
3413 if (NS_SUCCEEDED(rv
)) {
3414 UpdateMustKeepAlive();
3419 //-----------------------------------------------------------------------------
3420 // nsWebSocket::nsIWebSocket methods:
3421 //-----------------------------------------------------------------------------
3424 nsWebSocket::GetUrl(nsAString
& aURL
)
3426 aURL
= mOriginalURL
;
3431 nsWebSocket::GetReadyState(PRUint16
*aReadyState
)
3433 *aReadyState
= mReadyState
;
3438 nsWebSocket::GetBufferedAmount(PRUint32
*aBufferedAmount
)
3441 *aBufferedAmount
= mOutgoingBufferedAmount
;
3443 *aBufferedAmount
= mConnection
->GetOutgoingBufferedAmount();
3448 #define NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(_eventlistenername, _eventlistener) \
3450 nsWebSocket::GetOn##_eventlistenername(nsIDOMEventListener * *aEventListener)\
3452 return GetInnerEventListener(_eventlistener, aEventListener); \
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
)
3468 nsWebSocket::Send(const nsAString
& aData
, PRBool
*aRet
)
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
;
3491 if (mReadyState
== nsIWebSocket::CLOSING
||
3492 mReadyState
== nsIWebSocket::CLOSED
) {
3493 mOutgoingBufferedAmount
+= NS_ConvertUTF16toUTF8(aData
).Length();
3497 nsresult rv
= mConnection
->PostMessage(PromiseFlatString(aData
));
3498 *aRet
= NS_SUCCEEDED(rv
);
3504 nsWebSocket::Close()
3506 if (mReadyState
== nsIWebSocket::CLOSING
||
3507 mReadyState
== nsIWebSocket::CLOSED
) {
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();
3520 // mReadyState == nsIWebSocket::OPEN
3521 mConnection
->Close();
3527 * This Init method should only be called by C++ consumers.
3530 nsWebSocket::Init(nsIPrincipal
* aPrincipal
,
3531 nsIScriptContext
* aScriptContext
,
3532 nsPIDOMWindow
* aOwnerWindow
,
3533 const nsAString
& aURL
,
3534 const nsAString
& aProtocol
)
3538 NS_ENSURE_ARG(aPrincipal
);
3540 if (!PrefEnabled()) {
3541 return NS_ERROR_DOM_SECURITY_ERR
;
3544 mPrincipal
= aPrincipal
;
3545 mScriptContext
= aScriptContext
;
3547 mOwner
= aOwnerWindow
->IsOuterWindow() ?
3548 aOwnerWindow
->GetCurrentInnerWindow() : aOwnerWindow
;
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
);
3560 JSScript
*script
= JS_GetFrameScript(cx
, fp
);
3562 mScriptFile
= JS_GetScriptFilename(cx
, script
);
3565 jsbytecode
*pc
= JS_GetFramePC(cx
, fp
);
3567 mScriptLine
= JS_PCToLineNumber(cx
, script
, pc
);
3571 mWindowID
= nsJSUtils::GetCurrentlyRunningCodeWindowID(cx
);
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();
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
)
3614 nsWSProtocolHandler::GetScheme(nsACString
& aScheme
)
3616 aScheme
.AssignLiteral("ws");
3621 nsWSProtocolHandler::GetDefaultPort(PRInt32
*aDefaultPort
)
3623 *aDefaultPort
= DEFAULT_WS_SCHEME_PORT
;
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
;
3636 nsWSProtocolHandler::NewURI(const nsACString
& aSpec
,
3637 const char *aCharset
,
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
);
3659 nsWSProtocolHandler::NewChannel(nsIURI
*aURI
,
3660 nsIChannel
**aChannel
)
3662 return NS_ERROR_NOT_AVAILABLE
;
3666 nsWSProtocolHandler::NewProxiedChannel(nsIURI
*aURI
,
3667 nsIProxyInfo
* aProxyInfo
,
3668 nsIChannel
**aChannel
)
3670 return NS_ERROR_NOT_AVAILABLE
;
3674 nsWSProtocolHandler::AllowPort(PRInt32 aPort
,
3675 const char *aScheme
,
3678 PRInt32 defaultPort
;
3679 GetDefaultPort(&defaultPort
);
3681 *aAllowPort
= (aPort
== defaultPort
);
3685 ////////////////////////////////////////////////////////////////////////////////
3686 // nsWSSProtocolHandler
3687 ////////////////////////////////////////////////////////////////////////////////
3690 nsWSSProtocolHandler::GetScheme(nsACString
& aScheme
)
3692 aScheme
.AssignLiteral("wss");
3697 nsWSSProtocolHandler::GetDefaultPort(PRInt32
*aDefaultPort
)
3699 *aDefaultPort
= DEFAULT_WSS_SCHEME_PORT
;