bug 674905 - implement ws extensions attribute r=biesi r=sicking sr=bz
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 03 Aug 2011 23:46:13 -0400
changeset 74592 5edb33c72ec0c29ba4157ee85480458cfb1aff5e
parent 74591 65617cb216fa8ad8ce0a2e68a26d71c36321c54f
child 74593 75c8a2eb9f87a0a2a4e039f1311de7eba87ca95d
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, sicking, bz
bugs674905
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 674905 - implement ws extensions attribute r=biesi r=sicking sr=bz
content/base/public/nsIMozWebSocket.idl
content/base/src/nsWebSocket.cpp
content/base/src/nsWebSocket.h
content/base/test/test_websocket.html
netwerk/protocol/websocket/BaseWebSocketChannel.cpp
netwerk/protocol/websocket/BaseWebSocketChannel.h
netwerk/protocol/websocket/PWebSocket.ipdl
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/protocol/websocket/WebSocketChannelChild.cpp
netwerk/protocol/websocket/WebSocketChannelChild.h
netwerk/protocol/websocket/WebSocketChannelParent.cpp
netwerk/protocol/websocket/nsIWebSocketChannel.idl
--- 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