bug 674905 - implement ws extensions attribute r=biesi r=sicking sr=bz
--- a/content/base/public/nsIMozWebSocket.idl
+++ b/content/base/public/nsIMozWebSocket.idl
@@ -53,20 +53,21 @@ class nsString;
/**
* The nsIMozWebSocket interface enables Web applications to maintain
* bidirectional communications with server-side processes as described in:
*
* http://dev.w3.org/html5/websockets/
*
*/
-[scriptable, uuid(8fa080c7-934a-4040-90cb-31055798b35d)]
+[scriptable, uuid(5b124f54-7d46-4bc0-8507-e58ed22c19b9)]
interface nsIMozWebSocket : nsISupports
{
readonly attribute DOMString url;
+ readonly attribute DOMString extensions;
readonly attribute DOMString protocol;
//ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
const unsigned short CLOSED = 3;
readonly attribute unsigned short readyState;
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -508,16 +508,18 @@ nsWebSocketEstablishedConnection::OnStar
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
if (!mOwner)
return NS_OK;
if (!mOwner->mRequestedProtocolList.IsEmpty())
mWebSocketChannel->GetProtocol(mOwner->mEstablishedProtocol);
+ mWebSocketChannel->GetExtensions(mOwner->mEstablishedExtensions);
+
mStatus = CONN_CONNECTED_AND_READY;
mOwner->SetReadyState(nsIMozWebSocket::OPEN);
return NS_OK;
}
NS_IMETHODIMP
nsWebSocketEstablishedConnection::OnStop(nsISupports *aContext,
nsresult aStatusCode)
@@ -1229,16 +1231,23 @@ nsWebSocket::AddEventListener(const nsAS
NS_IMETHODIMP
nsWebSocket::GetUrl(nsAString& aURL)
{
aURL = mOriginalURL;
return NS_OK;
}
NS_IMETHODIMP
+nsWebSocket::GetExtensions(nsAString& aExtensions)
+{
+ CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsWebSocket::GetProtocol(nsAString& aProtocol)
{
CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
return NS_OK;
}
NS_IMETHODIMP
nsWebSocket::GetReadyState(PRUint16 *aReadyState)
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -145,16 +145,17 @@ protected:
nsCString mAsciiHost; // hostname
PRUint32 mPort;
nsCString mResource; // [filepath[?query]]
nsString mUTF16Origin;
nsCOMPtr<nsIURI> mURI;
nsCString mRequestedProtocolList;
nsCString mEstablishedProtocol;
+ nsCString mEstablishedExtensions;
PRUint16 mReadyState;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsRefPtr<nsWebSocketEstablishedConnection> mConnection;
PRUint32 mOutgoingBufferedAmount; // actually, we get this value from
// mConnection when we are connected,
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -55,17 +55,17 @@
* 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
- * 38. reserved for extension test
+ * 38. ensure extensions attribute is defined
* 39. a basic wss:// connectivity test
* 40. negative test for wss:// with no cert
*/
var first_test = 1;
var last_test = 40;
var current_test = first_test;
@@ -1056,19 +1056,32 @@ function test37()
doTest(38);
}
}
}
}
function test38()
{
- ok(true, "test 38 reserved for extension test.");
- current_test++;
- doTest(39);
+ var prots=["test-38"];
+
+ var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
+ ws.onopen = function(e)
+ {
+ ok(true, "test 38 open");
+ ok(ws.extensions != undefined, "extensions attribute defined");
+ ok(ws.extensions == "deflate-stream", "extensions attribute deflate-stream");
+ ws.close();
+ };
+
+ ws.onclose = function(e)
+ {
+ ok(true, "test 38 close");
+ doTest(39);
+ };
}
function test39()
{
var prots=["test-39"];
var ws = CreateTestWS("wss://example.com/tests/content/base/test/file_websocket", prots);
status_test39 = "started";
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
@@ -120,16 +120,24 @@ NS_IMETHODIMP
BaseWebSocketChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
{
LOG(("BaseWebSocketChannel::SetLoadGroup() %p\n", this));
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
+BaseWebSocketChannel::GetExtensions(nsACString &aExtensions)
+{
+ LOG(("BaseWebSocketChannel::GetExtensions() %p\n", this));
+ aExtensions = mNegotiatedExtensions;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
BaseWebSocketChannel::GetProtocol(nsACString &aProtocol)
{
LOG(("BaseWebSocketChannel::GetProtocol() %p\n", this));
aProtocol = mProtocol;
return NS_OK;
}
NS_IMETHODIMP
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.h
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.h
@@ -67,29 +67,31 @@ class BaseWebSocketChannel : public nsIW
// Partial implementation of nsIWebSocketChannel
//
NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI);
NS_IMETHOD GetURI(nsIURI **aURI);
NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor **aNotificationCallbacks);
NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aNotificationCallbacks);
NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup);
NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup);
+ NS_IMETHOD GetExtensions(nsACString &aExtensions);
NS_IMETHOD GetProtocol(nsACString &aProtocol);
NS_IMETHOD SetProtocol(const nsACString &aProtocol);
protected:
nsCOMPtr<nsIURI> mOriginalURI;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIWebSocketListener> mListener;
nsCOMPtr<nsISupports> mContext;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCString mProtocol;
nsCString mOrigin;
PRBool mEncrypted;
+ nsCString mNegotiatedExtensions;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_BaseWebSocketChannel_h
--- a/netwerk/protocol/websocket/PWebSocket.ipdl
+++ b/netwerk/protocol/websocket/PWebSocket.ipdl
@@ -58,17 +58,17 @@ parent:
Close(PRUint16 code, nsCString reason);
SendMsg(nsCString aMsg);
SendBinaryMsg(nsCString aMsg);
DeleteSelf();
child:
// Forwarded notifications corresponding to the nsIWebSocketListener interface
- OnStart(nsCString aProtocol);
+ OnStart(nsCString aProtocol, nsCString aExtensions);
OnStop(nsresult aStatusCode);
OnMessageAvailable(nsCString aMsg);
OnBinaryMessageAvailable(nsCString aMsg);
OnAcknowledge(PRUint32 aSize);
OnServerClose(PRUint16 code, nsCString aReason);
__delete__();
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1511,16 +1511,17 @@ WebSocketChannel::HandleExtensions()
mCompressor = new nsWSCompression(this, mSocketOut);
if (!mCompressor->Active()) {
LOG(("WebSocketChannel:: Cannot init deflate object\n"));
delete mCompressor;
mCompressor = nsnull;
AbortSession(NS_ERROR_UNEXPECTED);
return NS_ERROR_UNEXPECTED;
}
+ mNegotiatedExtensions = extensions;
}
}
return NS_OK;
}
nsresult
WebSocketChannel::SetupRequest()
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -102,46 +102,53 @@ WebSocketChannelChild::ReleaseIPDLRefere
mIPCOpen = false;
Release();
}
class StartEvent : public ChannelEvent
{
public:
StartEvent(WebSocketChannelChild* aChild,
- const nsCString& aProtocol)
+ const nsCString& aProtocol,
+ const nsCString& aExtensions)
: mChild(aChild)
, mProtocol(aProtocol)
+ , mExtensions(aExtensions)
{}
void Run()
{
- mChild->OnStart(mProtocol);
+ mChild->OnStart(mProtocol, mExtensions);
}
private:
WebSocketChannelChild* mChild;
nsCString mProtocol;
+ nsCString mExtensions;
};
bool
-WebSocketChannelChild::RecvOnStart(const nsCString& aProtocol)
+WebSocketChannelChild::RecvOnStart(const nsCString& aProtocol,
+ const nsCString& aExtensions)
{
if (mEventQ.ShouldEnqueue()) {
- mEventQ.Enqueue(new StartEvent(this, aProtocol));
+ mEventQ.Enqueue(new StartEvent(this, aProtocol, aExtensions));
} else {
- OnStart(aProtocol);
+ OnStart(aProtocol, aExtensions);
}
return true;
}
void
-WebSocketChannelChild::OnStart(const nsCString& aProtocol)
+WebSocketChannelChild::OnStart(const nsCString& aProtocol,
+ const nsCString& aExtensions)
{
LOG(("WebSocketChannelChild::RecvOnStart() %p\n", this));
SetProtocol(aProtocol);
+ mNegotiatedExtensions = aExtensions;
+
if (mListener) {
AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
mListener->OnStart(mContext);
}
}
class StopEvent : public ChannelEvent
{
--- a/netwerk/protocol/websocket/WebSocketChannelChild.h
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.h
@@ -68,25 +68,25 @@ class WebSocketChannelChild : public Bas
NS_SCRIPTABLE NS_IMETHOD SendMsg(const nsACString &aMsg);
NS_SCRIPTABLE NS_IMETHOD SendBinaryMsg(const nsACString &aMsg);
NS_SCRIPTABLE NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
void AddIPDLReference();
void ReleaseIPDLReference();
private:
- bool RecvOnStart(const nsCString& aProtocol);
+ bool RecvOnStart(const nsCString& aProtocol, const nsCString& aExtensions);
bool RecvOnStop(const nsresult& aStatusCode);
bool RecvOnMessageAvailable(const nsCString& aMsg);
bool RecvOnBinaryMessageAvailable(const nsCString& aMsg);
bool RecvOnAcknowledge(const PRUint32& aSize);
bool RecvOnServerClose(const PRUint16& aCode, const nsCString &aReason);
bool RecvAsyncOpenFailed();
- void OnStart(const nsCString& aProtocol);
+ void OnStart(const nsCString& aProtocol, const nsCString& aExtensions);
void OnStop(const nsresult& aStatusCode);
void OnMessageAvailable(const nsCString& aMsg);
void OnBinaryMessageAvailable(const nsCString& aMsg);
void OnAcknowledge(const PRUint32& aSize);
void OnServerClose(const PRUint16& aCode, const nsCString& aReason);
void AsyncOpenFailed();
ChannelEventQueue mEventQ;
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -147,21 +147,22 @@ WebSocketChannelParent::GetInterface(con
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
WebSocketChannelParent::OnStart(nsISupports *aContext)
{
LOG(("WebSocketChannelParent::OnStart() %p\n", this));
- nsCAutoString protocol;
+ nsCAutoString protocol, extensions;
if (mChannel) {
mChannel->GetProtocol(protocol);
+ mChannel->GetExtensions(extensions);
}
- if (!mIPCOpen || !SendOnStart(protocol)) {
+ if (!mIPCOpen || !SendOnStart(protocol, extensions)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
WebSocketChannelParent::OnStop(nsISupports *aContext, nsresult aStatusCode)
{
--- a/netwerk/protocol/websocket/nsIWebSocketChannel.idl
+++ b/netwerk/protocol/websocket/nsIWebSocketChannel.idl
@@ -39,17 +39,17 @@
interface nsIURI;
interface nsIInterfaceRequestor;
interface nsILoadGroup;
interface nsIWebSocketListener;
#include "nsISupports.idl"
-[scriptable, uuid(783029cf-75b5-439e-8e47-86b390202b4c)]
+[scriptable, uuid(e8ae0371-c28f-4d61-b257-514e014a4686)]
interface nsIWebSocketChannel : nsISupports
{
/**
* The original URI used to construct the protocol connection. This is used
* in the case of a redirect or URI "resolution" (e.g. resolving a
* resource: URI to a file: URI) so that the original pre-redirect
* URI can still be obtained. This is never null.
*/
@@ -77,16 +77,21 @@ interface nsIWebSocketChannel : nsISuppo
attribute nsILoadGroup loadGroup;
/**
* Sec-Websocket-Protocol value
*/
attribute ACString protocol;
/**
+ * Sec-Websocket-Extensions response header value
+ */
+ readonly attribute ACString extensions;
+
+ /**
* Asynchronously open the websocket connection. Received messages are fed
* to the socket listener as they arrive. The socket listener's methods
* are called on the thread that calls asyncOpen and are not called until
* after asyncOpen returns. If asyncOpen returns successfully, the
* protocol implementation promises to call at least onStart and onStop of
* the listener.
*
* NOTE: Implementations should throw NS_ERROR_ALREADY_OPENED if the