mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 674716 - websockets API close reason codes and messages r=sicking r=biesi sr=bz
--HG-- extra : rebase_source : f7a0eb6b310f29f8e697c26ff5e0ef9d2fde559a
This commit is contained in:
parent
2adea969f2
commit
c9a6067c84
@ -58,7 +58,7 @@ class nsString;
|
||||
* http://dev.w3.org/html5/websockets/
|
||||
*
|
||||
*/
|
||||
[scriptable, uuid(d50eb158-30a1-4692-8664-fdd479fa24b3)]
|
||||
[scriptable, uuid(8fa080c7-934a-4040-90cb-31055798b35d)]
|
||||
interface nsIMozWebSocket : nsISupports
|
||||
{
|
||||
readonly attribute DOMString url;
|
||||
@ -92,7 +92,8 @@ interface nsIMozWebSocket : nsISupports
|
||||
* Closes the Web Socket connection or connection attempt, if any.
|
||||
* If the connection is already closed, it does nothing.
|
||||
*/
|
||||
void close();
|
||||
[optional_argc] void close([optional] in unsigned short code,
|
||||
[optional] in DOMString reason);
|
||||
|
||||
/**
|
||||
* Initialize the object for use from C++ code with the principal, script
|
||||
|
@ -390,7 +390,8 @@ nsWebSocketEstablishedConnection::Close()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mWebSocketChannel->Close();
|
||||
return mWebSocketChannel->Close(mOwner->mClientReasonCode,
|
||||
mOwner->mClientReason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -561,9 +562,15 @@ nsWebSocketEstablishedConnection::OnAcknowledge(nsISupports *aContext,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWebSocketEstablishedConnection::OnServerClose(nsISupports *aContext)
|
||||
nsWebSocketEstablishedConnection::OnServerClose(nsISupports *aContext,
|
||||
PRUint16 aCode,
|
||||
const nsACString &aReason)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
||||
if (mOwner) {
|
||||
mOwner->mServerReasonCode = aCode;
|
||||
CopyUTF8toUTF16(aReason, mOwner->mServerReason);
|
||||
}
|
||||
|
||||
Close(); /* reciprocate! */
|
||||
return NS_OK;
|
||||
@ -611,6 +618,8 @@ nsWebSocketEstablishedConnection::GetInterface(const nsIID &aIID,
|
||||
nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE),
|
||||
mCheckMustKeepAlive(PR_TRUE),
|
||||
mTriggeredCloseEvent(PR_FALSE),
|
||||
mClientReasonCode(0),
|
||||
mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
|
||||
mReadyState(nsIMozWebSocket::CONNECTING),
|
||||
mOutgoingBufferedAmount(0),
|
||||
mScriptLine(0),
|
||||
@ -806,7 +815,7 @@ nsWebSocket::EstablishConnection()
|
||||
rv = conn->Init(this);
|
||||
mConnection = conn;
|
||||
if (NS_FAILED(rv)) {
|
||||
Close();
|
||||
Close(0, EmptyString(), 0);
|
||||
mConnection = nsnull;
|
||||
return rv;
|
||||
}
|
||||
@ -817,14 +826,18 @@ nsWebSocket::EstablishConnection()
|
||||
class nsWSCloseEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean)
|
||||
nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean,
|
||||
PRUint16 aCode, const nsString &aReason)
|
||||
: mWebSocket(aWebSocket),
|
||||
mWasClean(aWasClean)
|
||||
mWasClean(aWasClean),
|
||||
mCode(aCode),
|
||||
mReason(aReason)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean);
|
||||
nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean,
|
||||
mCode, mReason);
|
||||
mWebSocket->UpdateMustKeepAlive();
|
||||
return rv;
|
||||
}
|
||||
@ -832,6 +845,8 @@ public:
|
||||
private:
|
||||
nsRefPtr<nsWebSocket> mWebSocket;
|
||||
PRBool mWasClean;
|
||||
PRUint16 mCode;
|
||||
nsString mReason;
|
||||
};
|
||||
|
||||
nsresult
|
||||
@ -919,7 +934,9 @@ nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean)
|
||||
nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean,
|
||||
PRUint16 aCode,
|
||||
const nsString &aReason)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
||||
nsresult rv;
|
||||
@ -941,7 +958,7 @@ nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean)
|
||||
nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
|
||||
rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
|
||||
PR_FALSE, PR_FALSE,
|
||||
aWasClean);
|
||||
aWasClean, aCode, aReason);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
|
||||
@ -999,7 +1016,10 @@ nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
|
||||
if (mConnection) {
|
||||
// The close event must be dispatched asynchronously.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new nsWSCloseEvent(this, mConnection->ClosedCleanly());
|
||||
new nsWSCloseEvent(this,
|
||||
mConnection->ClosedCleanly(),
|
||||
mServerReasonCode,
|
||||
mServerReason);
|
||||
mOutgoingBufferedAmount += mConnection->GetOutgoingBufferedAmount();
|
||||
mConnection = nsnull; // this is no longer necessary
|
||||
|
||||
@ -1257,6 +1277,26 @@ NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
|
||||
NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
|
||||
NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
|
||||
|
||||
static PRBool
|
||||
ContainsUnpairedSurrogates(const nsAString& aData)
|
||||
{
|
||||
// Check for unpaired surrogates.
|
||||
PRUint32 i, length = aData.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
if (NS_IS_LOW_SURROGATE(aData[i])) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
if (NS_IS_HIGH_SURROGATE(aData[i])) {
|
||||
++i;
|
||||
if (i == length || !NS_IS_LOW_SURROGATE(aData[i])) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWebSocket::Send(const nsAString& aData)
|
||||
{
|
||||
@ -1266,20 +1306,8 @@ nsWebSocket::Send(const nsAString& aData)
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
// Check for unpaired surrogates.
|
||||
PRUint32 i, length = aData.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
if (NS_IS_LOW_SURROGATE(aData[i])) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
if (NS_IS_HIGH_SURROGATE(aData[i])) {
|
||||
if (i + 1 == length || !NS_IS_LOW_SURROGATE(aData[i + 1])) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ContainsUnpairedSurrogates(aData))
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
|
||||
if (mReadyState == nsIMozWebSocket::CLOSING ||
|
||||
mReadyState == nsIMozWebSocket::CLOSED) {
|
||||
@ -1293,9 +1321,34 @@ nsWebSocket::Send(const nsAString& aData)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWebSocket::Close()
|
||||
nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
||||
|
||||
// the reason code is optional, but if provided it must be in a specific range
|
||||
if (argc >= 1) {
|
||||
if (code != 1000 && (code < 3000 || code > 4999))
|
||||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
}
|
||||
|
||||
nsCAutoString utf8Reason;
|
||||
if (argc >= 2) {
|
||||
if (ContainsUnpairedSurrogates(reason))
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
|
||||
CopyUTF16toUTF8(reason, utf8Reason);
|
||||
|
||||
// The API requires the UTF-8 string to be 123 or less bytes
|
||||
if (utf8Reason.Length() > 123)
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
// Format checks for reason and code both passed, they can now be assigned.
|
||||
if (argc >= 1)
|
||||
mClientReasonCode = code;
|
||||
if (argc >= 2)
|
||||
mClientReason = utf8Reason;
|
||||
|
||||
if (mReadyState == nsIMozWebSocket::CLOSING ||
|
||||
mReadyState == nsIMozWebSocket::CLOSED) {
|
||||
return NS_OK;
|
||||
|
@ -109,7 +109,8 @@ protected:
|
||||
|
||||
nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
|
||||
nsresult CreateAndDispatchMessageEvent(const nsACString& aData);
|
||||
nsresult CreateAndDispatchCloseEvent(PRBool aWasClean);
|
||||
nsresult CreateAndDispatchCloseEvent(PRBool aWasClean, PRUint16 aCode,
|
||||
const nsString &aReason);
|
||||
|
||||
// called from mConnection accordingly to the situation
|
||||
void SetReadyState(PRUint16 aNewReadyState);
|
||||
@ -136,6 +137,11 @@ protected:
|
||||
PRPackedBool mCheckMustKeepAlive;
|
||||
PRPackedBool mTriggeredCloseEvent;
|
||||
|
||||
nsCString mClientReason;
|
||||
PRUint16 mClientReasonCode;
|
||||
nsString mServerReason;
|
||||
PRUint16 mServerReasonCode;
|
||||
|
||||
nsCString mAsciiHost; // hostname
|
||||
PRUint32 mPort;
|
||||
nsCString mResource; // [filepath[?query]]
|
||||
|
@ -96,5 +96,25 @@ def web_socket_transfer_data(request):
|
||||
elif request.ws_protocol == "test-20":
|
||||
msgutil.send_message(request, "server data")
|
||||
msgutil.close_connection(request)
|
||||
elif request.ws_protocol == "test-34":
|
||||
request.ws_stream.close_connection(1001, "going away now")
|
||||
elif request.ws_protocol == "test-35a":
|
||||
while not request.client_terminated:
|
||||
msgutil.receive_message(request)
|
||||
global test35code
|
||||
test35code = request.ws_close_code
|
||||
global test35reason
|
||||
test35reason = request.ws_close_reason
|
||||
elif request.ws_protocol == "test-35b":
|
||||
request.ws_stream.close_connection(test35code + 1, test35reason)
|
||||
elif request.ws_protocol == "test-37b":
|
||||
while not request.client_terminated:
|
||||
msgutil.receive_message(request)
|
||||
global test37code
|
||||
test37code = request.ws_close_code
|
||||
global test37reason
|
||||
test37reason = request.ws_close_reason
|
||||
elif request.ws_protocol == "test-37c":
|
||||
request.ws_stream.close_connection(test37code, test37reason)
|
||||
while not request.client_terminated:
|
||||
msgutil.receive_message(request)
|
||||
|
@ -56,10 +56,15 @@
|
||||
* 31. ctor using valid 2 element sub-protocol array with 1 element server
|
||||
* will reject and one server will accept.
|
||||
* 32. ctor using invalid sub-protocol array that contains duplicate items
|
||||
* 33. default close code test
|
||||
* 34. test for receiving custom close code and reason
|
||||
* 35. test for sending custom close code and reason
|
||||
* 36. negative test for sending out of range close code
|
||||
* 37. negative test for too long of a close reason
|
||||
*/
|
||||
|
||||
var first_test = 1;
|
||||
var last_test = 32;
|
||||
var last_test = 37;
|
||||
|
||||
var current_test = first_test;
|
||||
|
||||
@ -884,6 +889,173 @@ function test32()
|
||||
doTest(33);
|
||||
}
|
||||
|
||||
function test33()
|
||||
{
|
||||
var prots=["test33"];
|
||||
|
||||
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
|
||||
ws.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 33 open");
|
||||
ws.close();
|
||||
};
|
||||
|
||||
ws.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 33 close");
|
||||
ok(e.wasClean, "test 33 closed cleanly");
|
||||
ok(e.code == 1000, "test 33 had normal 1000 error code");
|
||||
doTest(34);
|
||||
};
|
||||
}
|
||||
|
||||
function test34()
|
||||
{
|
||||
var prots=["test-34"];
|
||||
|
||||
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
|
||||
ws.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 34 open");
|
||||
ws.close();
|
||||
};
|
||||
|
||||
ws.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 34 close");
|
||||
ok(e.wasClean, "test 34 closed cleanly");
|
||||
ok(e.code == 1001, "test 34 custom server code");
|
||||
ok(e.reason == "going away now", "test 34 custom server reason");
|
||||
doTest(35);
|
||||
};
|
||||
}
|
||||
|
||||
function test35()
|
||||
{
|
||||
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-35a");
|
||||
|
||||
ws.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 35a open");
|
||||
ws.close(3500, "my code");
|
||||
};
|
||||
|
||||
ws.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 35a close");
|
||||
ok(e.wasClean, "test 35a closed cleanly");
|
||||
current_test--; // CreateTestWS for 35a incremented this
|
||||
var wsb = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-35b");
|
||||
|
||||
wsb.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 35b open");
|
||||
wsb.close();
|
||||
};
|
||||
|
||||
wsb.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 35b close");
|
||||
ok(e.wasClean, "test 35b closed cleanly");
|
||||
ok(e.code == 3501, "test 35 custom server code");
|
||||
ok(e.reason == "my code", "test 35 custom server reason");
|
||||
doTest(36);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function test36()
|
||||
{
|
||||
var prots=["test-36"];
|
||||
|
||||
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
|
||||
ws.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 36 open");
|
||||
|
||||
try {
|
||||
ws.close(13200);
|
||||
ok(false, "testing custom close code out of range");
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "testing custom close code out of range");
|
||||
ws.close(3200);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 36 close");
|
||||
ok(e.wasClean, "test 36 closed cleanly");
|
||||
doTest(37);
|
||||
};
|
||||
}
|
||||
|
||||
function test37()
|
||||
{
|
||||
var prots=["test-37"];
|
||||
|
||||
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
|
||||
ws.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 37 open");
|
||||
|
||||
try {
|
||||
ws.close(3100,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
|
||||
ok(false, "testing custom close reason out of range");
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "testing custom close reason out of range");
|
||||
ws.close(3100,"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012");
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 37 close");
|
||||
ok(e.wasClean, "test 37 closed cleanly");
|
||||
|
||||
current_test--; // CreateTestWS for 37 incremented this
|
||||
var wsb = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-37b");
|
||||
|
||||
wsb.onopen = function(e)
|
||||
{
|
||||
// now test that a rejected close code and reason dont persist
|
||||
ok(true, "test 37b open");
|
||||
try {
|
||||
wsb.close(3101,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
|
||||
ok(false, "testing custom close reason out of range 37b");
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "testing custom close reason out of range 37b");
|
||||
wsb.close();
|
||||
}
|
||||
}
|
||||
|
||||
wsb.onclose = function(e)
|
||||
{
|
||||
ok(true, "test 37b close");
|
||||
ok(e.wasClean, "test 37b closed cleanly");
|
||||
|
||||
current_test--; // CreateTestWS for 37 incremented this
|
||||
var wsc = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test-37c");
|
||||
|
||||
wsc.onopen = function(e)
|
||||
{
|
||||
ok(true, "test 37c open");
|
||||
wsc.close();
|
||||
}
|
||||
|
||||
wsc.onclose = function(e)
|
||||
{
|
||||
ok(e.code != 3101, "test 37c custom server code not present");
|
||||
ok(e.reason == "", "test 37c custom server reason not present");
|
||||
doTest(38);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ranAllTests = false;
|
||||
|
||||
function maybeFinished()
|
||||
|
@ -56,16 +56,34 @@ nsDOMCloseEvent::GetWasClean(PRBool *aWasClean)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMCloseEvent::GetCode(PRUint16 *aCode)
|
||||
{
|
||||
*aCode = mReasonCode;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMCloseEvent::GetReason(nsAString & aReason)
|
||||
{
|
||||
aReason = mReason;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMCloseEvent::InitCloseEvent(const nsAString& aType,
|
||||
PRBool aCanBubble,
|
||||
PRBool aCancelable,
|
||||
PRBool aWasClean)
|
||||
PRBool aWasClean,
|
||||
PRUint16 aReasonCode,
|
||||
const nsAString &aReason)
|
||||
{
|
||||
nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mWasClean = aWasClean;
|
||||
mReasonCode = aReasonCode;
|
||||
mReason = aReason;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -53,9 +53,9 @@ class nsDOMCloseEvent : public nsDOMEvent,
|
||||
{
|
||||
public:
|
||||
nsDOMCloseEvent(nsPresContext* aPresContext, nsEvent* aEvent)
|
||||
: nsDOMEvent(aPresContext, aEvent), mWasClean(PR_FALSE)
|
||||
{
|
||||
}
|
||||
: nsDOMEvent(aPresContext, aEvent),
|
||||
mWasClean(PR_FALSE),
|
||||
mReasonCode(1005) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
@ -66,6 +66,8 @@ public:
|
||||
|
||||
private:
|
||||
PRBool mWasClean;
|
||||
PRUint16 mReasonCode;
|
||||
nsString mReason;
|
||||
};
|
||||
|
||||
#endif // nsDOMCloseEvent_h__
|
||||
|
@ -45,13 +45,17 @@
|
||||
* For more information on this interface, please see
|
||||
* http://dev.w3.org/html5/websockets/#closeevent
|
||||
*/
|
||||
[scriptable, uuid(a94d4379-eba2-45f4-be3a-6cc2fa1453a8)]
|
||||
[scriptable, uuid(f83d9d6d-6c0c-418c-b12a-438e76d5866b)]
|
||||
interface nsIDOMCloseEvent : nsIDOMEvent
|
||||
{
|
||||
readonly attribute boolean wasClean;
|
||||
|
||||
readonly attribute unsigned short code;
|
||||
readonly attribute DOMString reason;
|
||||
|
||||
void initCloseEvent(in DOMString aType,
|
||||
in boolean aCanBubble,
|
||||
in boolean aCancelable,
|
||||
in boolean aWasClean);
|
||||
in boolean aCanBubble,
|
||||
in boolean aCancelable,
|
||||
in boolean aWasClean,
|
||||
in unsigned short aReasonCode,
|
||||
in DOMString aReason);
|
||||
};
|
||||
|
@ -55,7 +55,7 @@ async protocol PWebSocket
|
||||
parent:
|
||||
// Forwarded methods corresponding to methods on nsIWebSocketChannel
|
||||
AsyncOpen(URI aURI, nsCString aOrigin, nsCString aProtocol, bool aSecure);
|
||||
Close();
|
||||
Close(PRUint16 code, nsCString reason);
|
||||
SendMsg(nsCString aMsg);
|
||||
SendBinaryMsg(nsCString aMsg);
|
||||
|
||||
@ -68,7 +68,7 @@ child:
|
||||
OnMessageAvailable(nsCString aMsg);
|
||||
OnBinaryMessageAvailable(nsCString aMsg);
|
||||
OnAcknowledge(PRUint32 aSize);
|
||||
OnServerClose();
|
||||
OnServerClose(PRUint16 code, nsCString aReason);
|
||||
|
||||
__delete__();
|
||||
|
||||
|
@ -179,13 +179,17 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
CallOnServerClose(nsIWebSocketListener *aListener,
|
||||
nsISupports *aContext)
|
||||
nsISupports *aContext,
|
||||
PRUint16 aCode,
|
||||
nsCString &aReason)
|
||||
: mListener(aListener),
|
||||
mContext(aContext) {}
|
||||
mContext(aContext),
|
||||
mCode(aCode),
|
||||
mReason(aReason) {}
|
||||
|
||||
NS_SCRIPTABLE NS_IMETHOD Run()
|
||||
{
|
||||
mListener->OnServerClose(mContext);
|
||||
mListener->OnServerClose(mContext, mCode, mReason);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -194,6 +198,8 @@ private:
|
||||
|
||||
nsCOMPtr<nsIWebSocketListener> mListener;
|
||||
nsCOMPtr<nsISupports> mContext;
|
||||
PRUint16 mCode;
|
||||
nsCString mReason;
|
||||
};
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnServerClose, nsIRunnable)
|
||||
|
||||
@ -502,7 +508,8 @@ WebSocketChannel::WebSocketChannel() :
|
||||
mTCPClosed(0),
|
||||
mMaxMessageSize(16000000),
|
||||
mStopOnClose(NS_OK),
|
||||
mCloseCode(kCloseAbnormal),
|
||||
mServerCloseCode(CLOSE_ABNORMAL),
|
||||
mScriptCloseCode(0),
|
||||
mFragmentOpcode(0),
|
||||
mFragmentAccumulator(0),
|
||||
mBuffered(0),
|
||||
@ -869,26 +876,29 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
|
||||
LOG(("WebSocketChannel:: close received\n"));
|
||||
mServerClosed = 1;
|
||||
|
||||
mCloseCode = kCloseNoStatus;
|
||||
mServerCloseCode = CLOSE_NO_STATUS;
|
||||
if (payloadLength >= 2) {
|
||||
memcpy(&mCloseCode, payload, 2);
|
||||
mCloseCode = PR_ntohs(mCloseCode);
|
||||
LOG(("WebSocketChannel:: close recvd code %u\n", mCloseCode));
|
||||
memcpy(&mServerCloseCode, payload, 2);
|
||||
mServerCloseCode = PR_ntohs(mServerCloseCode);
|
||||
LOG(("WebSocketChannel:: close recvd code %u\n", mServerCloseCode));
|
||||
PRUint16 msglen = payloadLength - 2;
|
||||
if (msglen > 0) {
|
||||
nsCString utf8Data((const char *)payload + 2, msglen);
|
||||
mServerCloseReason.SetLength(msglen);
|
||||
memcpy(mServerCloseReason.BeginWriting(),
|
||||
(const char *)payload + 2, msglen);
|
||||
|
||||
// section 8.1 says to replace received non utf-8 sequences
|
||||
// (which are non-conformant to send) with u+fffd,
|
||||
// but secteam feels that silently rewriting messages is
|
||||
// inappropriate - so we will fail the connection instead.
|
||||
if (!IsUTF8(utf8Data)) {
|
||||
if (!IsUTF8(mServerCloseReason)) {
|
||||
LOG(("WebSocketChannel:: close frame invalid utf-8\n"));
|
||||
AbortSession(NS_ERROR_ILLEGAL_VALUE);
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
LOG(("WebSocketChannel:: close msg %s\n", utf8Data.get()));
|
||||
LOG(("WebSocketChannel:: close msg %s\n",
|
||||
mServerCloseReason.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -897,7 +907,9 @@ WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
|
||||
mCloseTimer = nsnull;
|
||||
}
|
||||
if (mListener)
|
||||
NS_DispatchToMainThread(new CallOnServerClose(mListener, mContext));
|
||||
NS_DispatchToMainThread(
|
||||
new CallOnServerClose(mListener, mContext,
|
||||
mServerCloseCode, mServerCloseReason));
|
||||
|
||||
if (mClientClosed)
|
||||
ReleaseSession();
|
||||
@ -1058,16 +1070,16 @@ PRUint16
|
||||
WebSocketChannel::ResultToCloseCode(nsresult resultCode)
|
||||
{
|
||||
if (NS_SUCCEEDED(resultCode))
|
||||
return kCloseNormal;
|
||||
return CLOSE_NORMAL;
|
||||
if (resultCode == NS_ERROR_FILE_TOO_BIG)
|
||||
return kCloseTooLarge;
|
||||
return CLOSE_TOO_LARGE;
|
||||
if (resultCode == NS_BASE_STREAM_CLOSED ||
|
||||
resultCode == NS_ERROR_NET_TIMEOUT ||
|
||||
resultCode == NS_ERROR_CONNECTION_REFUSED) {
|
||||
return kCloseAbnormal;
|
||||
return CLOSE_ABNORMAL;
|
||||
}
|
||||
|
||||
return kCloseProtocolError;
|
||||
return CLOSE_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1107,16 +1119,34 @@ WebSocketChannel::PrimeNewOutgoingMessage()
|
||||
LOG(("WebSocketChannel:: PrimeNewOutgoingMessage() found close request\n"));
|
||||
mClientClosed = 1;
|
||||
mOutHeader[0] = kFinalFragBit | kClose;
|
||||
mOutHeader[1] = 0x02; // payload len = 2
|
||||
mOutHeader[1] = 0x02; // payload len = 2, maybe more for reason
|
||||
mOutHeader[1] |= kMaskBit;
|
||||
|
||||
// payload is offset 6 including 4 for the mask
|
||||
payload = mOutHeader + 6;
|
||||
|
||||
// The close reason code sits in the first 2 bytes of payload
|
||||
*((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
|
||||
|
||||
// length is 8 plus any reason information
|
||||
mHdrOutToSend = 8;
|
||||
|
||||
// The close reason code sits in the first 2 bytes of payload
|
||||
// If the channel user provided a code and reason during Close()
|
||||
// and there isn't an internal error, use that.
|
||||
if (NS_SUCCEEDED(mStopOnClose) && mScriptCloseCode) {
|
||||
*((PRUint16 *)payload) = PR_htons(mScriptCloseCode);
|
||||
if (!mScriptCloseReason.IsEmpty()) {
|
||||
NS_ABORT_IF_FALSE(mScriptCloseReason.Length() <= 123,
|
||||
"Close Reason Too Long");
|
||||
mOutHeader[1] += mScriptCloseReason.Length();
|
||||
mHdrOutToSend += mScriptCloseReason.Length();
|
||||
memcpy (payload + 2,
|
||||
mScriptCloseReason.BeginReading(),
|
||||
mScriptCloseReason.Length());
|
||||
}
|
||||
}
|
||||
else {
|
||||
*((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
|
||||
}
|
||||
|
||||
if (mServerClosed) {
|
||||
/* bidi close complete */
|
||||
mReleaseOnTransmit = 1;
|
||||
@ -1984,7 +2014,7 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebSocketChannel::Close()
|
||||
WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
|
||||
{
|
||||
LOG(("WebSocketChannel::Close() %p\n", this));
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
|
||||
@ -2000,8 +2030,14 @@ WebSocketChannel::Close()
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mRequestedClose = 1;
|
||||
// The API requires the UTF-8 string to be 123 or less bytes
|
||||
if (reason.Length() > 123)
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
|
||||
mRequestedClose = 1;
|
||||
mScriptCloseReason = reason;
|
||||
mScriptCloseCode = code;
|
||||
|
||||
return mSocketThread->Dispatch(new nsPostMessage(this, kFinMessage, -1),
|
||||
nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ public:
|
||||
const nsACString &aOrigin,
|
||||
nsIWebSocketListener *aListener,
|
||||
nsISupports *aContext);
|
||||
NS_IMETHOD Close();
|
||||
NS_IMETHOD Close(PRUint16 aCode, const nsACString & aReason);
|
||||
NS_IMETHOD SendMsg(const nsACString &aMsg);
|
||||
NS_IMETHOD SendBinaryMsg(const nsACString &aMsg);
|
||||
NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
|
||||
@ -123,15 +123,6 @@ public:
|
||||
const static PRUint8 kMaskBit = 0x80;
|
||||
const static PRUint8 kFinalFragBit = 0x80;
|
||||
|
||||
// section 7.4.1 defines these
|
||||
const static PRUint16 kCloseNormal = 1000;
|
||||
const static PRUint16 kCloseGoingAway = 1001;
|
||||
const static PRUint16 kCloseProtocolError = 1002;
|
||||
const static PRUint16 kCloseUnsupported = 1003;
|
||||
const static PRUint16 kCloseTooLarge = 1004;
|
||||
const static PRUint16 kCloseNoStatus = 1005;
|
||||
const static PRUint16 kCloseAbnormal = 1006;
|
||||
|
||||
protected:
|
||||
virtual ~WebSocketChannel();
|
||||
|
||||
@ -252,7 +243,10 @@ private:
|
||||
|
||||
PRInt32 mMaxMessageSize;
|
||||
nsresult mStopOnClose;
|
||||
PRUint16 mCloseCode;
|
||||
PRUint16 mServerCloseCode;
|
||||
nsCString mServerCloseReason;
|
||||
PRUint16 mScriptCloseCode;
|
||||
nsCString mScriptCloseReason;
|
||||
|
||||
// These are for the read buffers
|
||||
PRUint8 *mFramePtr;
|
||||
|
@ -291,36 +291,44 @@ WebSocketChannelChild::OnAcknowledge(const PRUint32& aSize)
|
||||
class ServerCloseEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
ServerCloseEvent(WebSocketChannelChild* aChild)
|
||||
ServerCloseEvent(WebSocketChannelChild* aChild,
|
||||
const PRUint16 aCode,
|
||||
const nsCString &aReason)
|
||||
: mChild(aChild)
|
||||
, mCode(aCode)
|
||||
, mReason(aReason)
|
||||
{}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->OnServerClose();
|
||||
mChild->OnServerClose(mCode, mReason);
|
||||
}
|
||||
private:
|
||||
WebSocketChannelChild* mChild;
|
||||
PRUint16 mCode;
|
||||
nsCString mReason;
|
||||
};
|
||||
|
||||
bool
|
||||
WebSocketChannelChild::RecvOnServerClose()
|
||||
WebSocketChannelChild::RecvOnServerClose(const PRUint16& aCode,
|
||||
const nsCString& aReason)
|
||||
{
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new ServerCloseEvent(this));
|
||||
mEventQ.Enqueue(new ServerCloseEvent(this, aCode, aReason));
|
||||
} else {
|
||||
OnServerClose();
|
||||
OnServerClose(aCode, aReason);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketChannelChild::OnServerClose()
|
||||
WebSocketChannelChild::OnServerClose(const PRUint16& aCode,
|
||||
const nsCString& aReason)
|
||||
{
|
||||
LOG(("WebSocketChannelChild::RecvOnServerClose() %p\n", this));
|
||||
if (mListener) {
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
|
||||
mListener->OnServerClose(mContext);
|
||||
mListener->OnServerClose(mContext, aCode, aReason);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,11 +369,11 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebSocketChannelChild::Close()
|
||||
WebSocketChannelChild::Close(PRUint16 code, const nsACString & reason)
|
||||
{
|
||||
LOG(("WebSocketChannelChild::Close() %p\n", this));
|
||||
|
||||
if (!mIPCOpen || !SendClose())
|
||||
if (!mIPCOpen || !SendClose(code, nsCString(reason)))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ class WebSocketChannelChild : public BaseWebSocketChannel,
|
||||
const nsACString &aOrigin,
|
||||
nsIWebSocketListener *aListener,
|
||||
nsISupports *aContext);
|
||||
NS_SCRIPTABLE NS_IMETHOD Close();
|
||||
NS_SCRIPTABLE NS_IMETHOD Close(PRUint16 code, const nsACString & reason);
|
||||
NS_SCRIPTABLE NS_IMETHOD SendMsg(const nsACString &aMsg);
|
||||
NS_SCRIPTABLE NS_IMETHOD SendBinaryMsg(const nsACString &aMsg);
|
||||
NS_SCRIPTABLE NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
|
||||
@ -78,7 +78,7 @@ class WebSocketChannelChild : public BaseWebSocketChannel,
|
||||
bool RecvOnMessageAvailable(const nsCString& aMsg);
|
||||
bool RecvOnBinaryMessageAvailable(const nsCString& aMsg);
|
||||
bool RecvOnAcknowledge(const PRUint32& aSize);
|
||||
bool RecvOnServerClose();
|
||||
bool RecvOnServerClose(const PRUint16& aCode, const nsCString &aReason);
|
||||
bool RecvAsyncOpenFailed();
|
||||
|
||||
void OnStart(const nsCString& aProtocol);
|
||||
@ -86,7 +86,7 @@ class WebSocketChannelChild : public BaseWebSocketChannel,
|
||||
void OnMessageAvailable(const nsCString& aMsg);
|
||||
void OnBinaryMessageAvailable(const nsCString& aMsg);
|
||||
void OnAcknowledge(const PRUint32& aSize);
|
||||
void OnServerClose();
|
||||
void OnServerClose(const PRUint16& aCode, const nsCString& aReason);
|
||||
void AsyncOpenFailed();
|
||||
|
||||
ChannelEventQueue mEventQ;
|
||||
|
@ -105,11 +105,11 @@ fail:
|
||||
}
|
||||
|
||||
bool
|
||||
WebSocketChannelParent::RecvClose()
|
||||
WebSocketChannelParent::RecvClose(const PRUint16& code, const nsCString& reason)
|
||||
{
|
||||
LOG(("WebSocketChannelParent::RecvClose() %p\n", this));
|
||||
if (mChannel) {
|
||||
nsresult rv = mChannel->Close();
|
||||
nsresult rv = mChannel->Close(code, reason);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
}
|
||||
return true;
|
||||
@ -203,10 +203,11 @@ WebSocketChannelParent::OnAcknowledge(nsISupports *aContext, PRUint32 aSize)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebSocketChannelParent::OnServerClose(nsISupports *aContext)
|
||||
WebSocketChannelParent::OnServerClose(nsISupports *aContext,
|
||||
PRUint16 code, const nsACString & reason)
|
||||
{
|
||||
LOG(("WebSocketChannelParent::OnServerClose() %p\n", this));
|
||||
if (!mIPCOpen || !SendOnServerClose()) {
|
||||
if (!mIPCOpen || !SendOnServerClose(code, nsCString(reason))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -67,7 +67,7 @@ class WebSocketChannelParent : public PWebSocketParent,
|
||||
const nsCString& aOrigin,
|
||||
const nsCString& aProtocol,
|
||||
const bool& aSecure);
|
||||
bool RecvClose();
|
||||
bool RecvClose(const PRUint16 & code, const nsCString & reason);
|
||||
bool RecvSendMsg(const nsCString& aMsg);
|
||||
bool RecvSendBinaryMsg(const nsCString& aMsg);
|
||||
bool RecvDeleteSelf();
|
||||
|
@ -44,7 +44,7 @@ interface nsIWebSocketListener;
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(398a2460-a46d-11e0-8264-0800200c9a66)]
|
||||
[scriptable, uuid(783029cf-75b5-439e-8e47-86b390202b4c)]
|
||||
interface nsIWebSocketChannel : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -106,9 +106,22 @@ interface nsIWebSocketChannel : nsISupports
|
||||
* Close the websocket connection for writing - no more calls to sendMsg
|
||||
* or sendBinaryMsg should be made after calling this. The listener object
|
||||
* may receive more messages if a server close has not yet been received.
|
||||
*
|
||||
* @param aCode the websocket closing handshake close code. Set to 0 if
|
||||
* you are not providing a code.
|
||||
* @param aReason the websocket closing handshake close reason
|
||||
*/
|
||||
void close();
|
||||
void close(in unsigned short aCode, in AUTF8String aReason);
|
||||
|
||||
// section 7.4.1 defines these close codes
|
||||
const unsigned short CLOSE_NORMAL = 1000;
|
||||
const unsigned short CLOSE_GOING_AWAY = 1001;
|
||||
const unsigned short CLOSE_PROTOCOL_ERROR = 1002;
|
||||
const unsigned short CLOSE_UNSUPPORTED = 1003;
|
||||
const unsigned short CLOSE_TOO_LARGE = 1004;
|
||||
const unsigned short CLOSE_NO_STATUS = 1005;
|
||||
const unsigned short CLOSE_ABNORMAL = 1006;
|
||||
|
||||
/**
|
||||
* Use to send text message down the connection to WebSocket peer.
|
||||
*
|
||||
|
@ -43,7 +43,7 @@
|
||||
* nsIWebSocketListener: passed to nsIWebSocketChannel::AsyncOpen. Receives
|
||||
* websocket traffic events as they arrive.
|
||||
*/
|
||||
[scriptable, uuid(b0c27050-31e9-42e5-bc59-499d54b52f99)]
|
||||
[scriptable, uuid(d74c96b2-65b3-4e39-9e39-c577de5d7a73)]
|
||||
interface nsIWebSocketListener : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -101,8 +101,14 @@ interface nsIWebSocketListener : nsISupports
|
||||
* onBinaryMessageAvailable() or onAcknowledge() will be delievered
|
||||
* to the listener after onServerClose(), though outgoing messages can still
|
||||
* be sent through the nsIWebSocketChannel connection.
|
||||
*
|
||||
* @param aContext user defined context
|
||||
* @param aCode the websocket closing handshake close code.
|
||||
* @param aReason the websocket closing handshake close reason
|
||||
|
||||
*/
|
||||
void onServerClose(in nsISupports aContext);
|
||||
void onServerClose(in nsISupports aContext, in unsigned short aCode,
|
||||
in AUTF8String aReason);
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user