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 73822 5edb33c72ec0c29ba4157ee85480458cfb1aff5e
parent 73821 65617cb216fa8ad8ce0a2e68a26d71c36321c54f
child 73823 75c8a2eb9f87a0a2a4e039f1311de7eba87ca95d
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersbiesi, sicking, bz
bugs674905
milestone8.0a1
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