From: bashi@chromium.org Date: Wed, 15 Feb 2012 02:11:46 +0000 (+0000) Subject: [WebSocket] Add extension attribute support X-Git-Tag: 070512121124~12844 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=afb40ae5a3abcd8beab2bf6f9db43af4c9d28832;p=profile%2Fivi%2Fwebkit-efl.git [WebSocket] Add extension attribute support https://bugs.webkit.org/show_bug.cgi?id=78557 Source/WebCore: Implement WebSocket "extensions" attribute that holds a list of extension the server accepted. No change in behavior at this time because we don't send any extension on handshake. Reviewed by Kent Tamura. No new tests. http/tests/websocket/tests/hybi/extensions.html checks the value of this attribute. * websockets/ThreadableWebSocketChannel.h: Add extensions(). (ThreadableWebSocketChannel): * websockets/ThreadableWebSocketChannelClientWrapper.cpp: (WebCore::ThreadableWebSocketChannelClientWrapper::extensions): Added. (WebCore): (WebCore::ThreadableWebSocketChannelClientWrapper::setExtensions): Added. * websockets/ThreadableWebSocketChannelClientWrapper.h: (ThreadableWebSocketChannelClientWrapper): * websockets/WebSocket.cpp: Added m_extensions member variable. (WebCore::WebSocket::WebSocket): (WebCore::WebSocket::extensions): Returns m_extensions. * websockets/WebSocket.h: * websockets/WebSocketChannel.cpp: (WebCore::WebSocketChannel::extensions): Added. (WebCore): * websockets/WebSocketChannel.h: (WebSocketChannel): * websockets/WebSocketExtensionDispatcher.cpp: (WebCore::WebSocketExtensionDispatcher::fail): Added. (WebCore::WebSocketExtensionDispatcher::processHeaderValue): Stores accepted extensions. (WebCore::WebSocketExtensionDispatcher::acceptedExtensions): Added. (WebCore): (WebCore::WebSocketExtensionDispatcher::acceptedExtensions): Added. * websockets/WebSocketExtensionDispatcher.h: (WebSocketExtensionDispatcher): * websockets/WebSocketHandshake.cpp: (WebCore::WebSocketHandshake::acceptedExtensions): Added. (WebCore): * websockets/WebSocketHandshake.h: * websockets/WorkerThreadableWebSocketChannel.cpp: (WebCore::WorkerThreadableWebSocketChannel::extensions): Added. (WebCore): (WebCore::workerContextDidConnect): Calls ThreadableWebSocketChannelClientWrapper::setExtensions(). (WebCore::WorkerThreadableWebSocketChannel::Peer::didConnect): Passes extensions as an argument. * websockets/WorkerThreadableWebSocketChannel.h: (WorkerThreadableWebSocketChannel): Source/WebKit/chromium: Add WebSocketExtensionDispatcher::acceptedExtensions() checks. Reviewed by Kent Tamura. * tests/WebSocketExtensionDispatcherTest.cpp: (WebCore::TEST_F): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@107769 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 4592f46..9975c00 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,53 @@ +2012-02-14 Kenichi Ishibashi + + [WebSocket] Add extension attribute support + https://bugs.webkit.org/show_bug.cgi?id=78557 + + Implement WebSocket "extensions" attribute that holds a list of + extension the server accepted. No change in behavior at this time + because we don't send any extension on handshake. + + Reviewed by Kent Tamura. + + No new tests. http/tests/websocket/tests/hybi/extensions.html checks the value of this attribute. + + * websockets/ThreadableWebSocketChannel.h: Add extensions(). + (ThreadableWebSocketChannel): + * websockets/ThreadableWebSocketChannelClientWrapper.cpp: + (WebCore::ThreadableWebSocketChannelClientWrapper::extensions): Added. + (WebCore): + (WebCore::ThreadableWebSocketChannelClientWrapper::setExtensions): Added. + * websockets/ThreadableWebSocketChannelClientWrapper.h: + (ThreadableWebSocketChannelClientWrapper): + * websockets/WebSocket.cpp: Added m_extensions member variable. + (WebCore::WebSocket::WebSocket): + (WebCore::WebSocket::extensions): Returns m_extensions. + * websockets/WebSocket.h: + * websockets/WebSocketChannel.cpp: + (WebCore::WebSocketChannel::extensions): Added. + (WebCore): + * websockets/WebSocketChannel.h: + (WebSocketChannel): + * websockets/WebSocketExtensionDispatcher.cpp: + (WebCore::WebSocketExtensionDispatcher::fail): Added. + (WebCore::WebSocketExtensionDispatcher::processHeaderValue): Stores accepted extensions. + (WebCore::WebSocketExtensionDispatcher::acceptedExtensions): Added. + (WebCore): + (WebCore::WebSocketExtensionDispatcher::acceptedExtensions): Added. + * websockets/WebSocketExtensionDispatcher.h: + (WebSocketExtensionDispatcher): + * websockets/WebSocketHandshake.cpp: + (WebCore::WebSocketHandshake::acceptedExtensions): Added. + (WebCore): + * websockets/WebSocketHandshake.h: + * websockets/WorkerThreadableWebSocketChannel.cpp: + (WebCore::WorkerThreadableWebSocketChannel::extensions): Added. + (WebCore): + (WebCore::workerContextDidConnect): Calls ThreadableWebSocketChannelClientWrapper::setExtensions(). + (WebCore::WorkerThreadableWebSocketChannel::Peer::didConnect): Passes extensions as an argument. + * websockets/WorkerThreadableWebSocketChannel.h: + (WorkerThreadableWebSocketChannel): + 2012-02-14 Kentaro Hara Rename [JSGenerateToJS] to [JSGenerateToJSObject] diff --git a/Source/WebCore/websockets/ThreadableWebSocketChannel.h b/Source/WebCore/websockets/ThreadableWebSocketChannel.h index e93cf86..a14582a 100644 --- a/Source/WebCore/websockets/ThreadableWebSocketChannel.h +++ b/Source/WebCore/websockets/ThreadableWebSocketChannel.h @@ -53,6 +53,7 @@ public: virtual bool useHixie76Protocol() = 0; virtual void connect(const KURL&, const String& protocol) = 0; virtual String subprotocol() = 0; // Will be available after didConnect() callback is invoked. + virtual String extensions() = 0; // Will be available after didConnect() callback is invoked. virtual bool send(const String& message) = 0; virtual bool send(const ArrayBuffer&) = 0; virtual bool send(const Blob&) = 0; diff --git a/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.cpp b/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.cpp index 3be04a9..0db708f 100644 --- a/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.cpp +++ b/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.cpp @@ -95,6 +95,21 @@ void ThreadableWebSocketChannelClientWrapper::setSubprotocol(const String& subpr memcpy(m_subprotocol.data(), subprotocol.characters(), sizeof(UChar) * length); } +String ThreadableWebSocketChannelClientWrapper::extensions() const +{ + if (m_extensions.isEmpty()) + return String(""); + return String(m_extensions); +} + +void ThreadableWebSocketChannelClientWrapper::setExtensions(const String& extensions) +{ + unsigned length = extensions.length(); + m_extensions.resize(length); + if (length) + memcpy(m_extensions.data(), extensions.characters(), sizeof(UChar) * length); +} + bool ThreadableWebSocketChannelClientWrapper::sendRequestResult() const { return m_sendRequestResult; diff --git a/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h b/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h index ec9397c..e83a80a 100644 --- a/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h +++ b/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h @@ -59,9 +59,11 @@ public: bool useHixie76Protocol() const; void setUseHixie76Protocol(bool); - // Subprotocol is cached too. Will be available when didConnect() callback is invoked. + // Subprotocol and extensions are cached too. Will be available when didConnect() callback is invoked. String subprotocol() const; void setSubprotocol(const String&); + String extensions() const; + void setExtensions(const String&); bool sendRequestResult() const; void setSendRequestResult(bool); @@ -95,7 +97,9 @@ protected: WebSocketChannelClient* m_client; bool m_syncMethodDone; bool m_useHixie76Protocol; - Vector m_subprotocol; // ThreadSafeRefCounted must not have a String member variable. + // ThreadSafeRefCounted must not have String member variables. + Vector m_subprotocol; + Vector m_extensions; bool m_sendRequestResult; unsigned long m_bufferedAmount; bool m_suspended; diff --git a/Source/WebCore/websockets/WebSocket.cpp b/Source/WebCore/websockets/WebSocket.cpp index 3010ea3..36dbb6f 100644 --- a/Source/WebCore/websockets/WebSocket.cpp +++ b/Source/WebCore/websockets/WebSocket.cpp @@ -155,6 +155,7 @@ WebSocket::WebSocket(ScriptExecutionContext* context) , m_binaryType(BinaryTypeBlob) , m_useHixie76Protocol(true) , m_subprotocol("") + , m_extensions("") { } @@ -387,8 +388,7 @@ String WebSocket::extensions() const { if (m_useHixie76Protocol) return String(); - // WebSocket protocol extension is not supported yet. - return ""; + return m_extensions; } String WebSocket::binaryType() const diff --git a/Source/WebCore/websockets/WebSocket.h b/Source/WebCore/websockets/WebSocket.h index cae57d2..1e44245 100644 --- a/Source/WebCore/websockets/WebSocket.h +++ b/Source/WebCore/websockets/WebSocket.h @@ -136,6 +136,7 @@ private: BinaryType m_binaryType; bool m_useHixie76Protocol; String m_subprotocol; + String m_extensions; }; } // namespace WebCore diff --git a/Source/WebCore/websockets/WebSocketChannel.cpp b/Source/WebCore/websockets/WebSocketChannel.cpp index 53436e3..18f4351 100644 --- a/Source/WebCore/websockets/WebSocketChannel.cpp +++ b/Source/WebCore/websockets/WebSocketChannel.cpp @@ -153,6 +153,17 @@ String WebSocketChannel::subprotocol() return serverProtocol; } +String WebSocketChannel::extensions() +{ + LOG(Network, "WebSocketChannel %p extensions", this); + if (!m_handshake || m_handshake->mode() != WebSocketHandshake::Connected) + return ""; + String extensions = m_handshake->acceptedExtensions(); + if (extensions.isNull()) + return ""; + return extensions; +} + bool WebSocketChannel::send(const String& message) { LOG(Network, "WebSocketChannel %p send %s", this, message.utf8().data()); diff --git a/Source/WebCore/websockets/WebSocketChannel.h b/Source/WebCore/websockets/WebSocketChannel.h index 9f9bab8..8bdb47f 100644 --- a/Source/WebCore/websockets/WebSocketChannel.h +++ b/Source/WebCore/websockets/WebSocketChannel.h @@ -68,6 +68,7 @@ public: virtual bool useHixie76Protocol() OVERRIDE; virtual void connect(const KURL&, const String& protocol) OVERRIDE; virtual String subprotocol() OVERRIDE; + virtual String extensions() OVERRIDE; virtual bool send(const String& message) OVERRIDE; virtual bool send(const ArrayBuffer&) OVERRIDE; virtual bool send(const Blob&) OVERRIDE; diff --git a/Source/WebCore/websockets/WebSocketExtensionDispatcher.cpp b/Source/WebCore/websockets/WebSocketExtensionDispatcher.cpp index d85092a..7c592ad 100644 --- a/Source/WebCore/websockets/WebSocketExtensionDispatcher.cpp +++ b/Source/WebCore/websockets/WebSocketExtensionDispatcher.cpp @@ -35,8 +35,8 @@ #include "WebSocketExtensionDispatcher.h" #include +#include #include -#include #include namespace WebCore { @@ -173,6 +173,28 @@ const String WebSocketExtensionDispatcher::createHeaderValue() const return builder.toString(); } +void WebSocketExtensionDispatcher::appendAcceptedExtension(const String& extensionToken, HashMap& extensionParameters) +{ + if (!m_acceptedExtensionsBuilder.isEmpty()) + m_acceptedExtensionsBuilder.append(", "); + m_acceptedExtensionsBuilder.append(extensionToken); + // FIXME: Should use ListHashSet to keep the order of the parameters. + for (HashMap::const_iterator iterator = extensionParameters.begin(); iterator != extensionParameters.end(); ++iterator) { + m_acceptedExtensionsBuilder.append("; "); + m_acceptedExtensionsBuilder.append(iterator->first); + if (!iterator->second.isNull()) { + m_acceptedExtensionsBuilder.append("="); + m_acceptedExtensionsBuilder.append(iterator->second); + } + } +} + +void WebSocketExtensionDispatcher::fail(const String& reason) +{ + m_failureReason = reason; + m_acceptedExtensionsBuilder.clear(); +} + bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue) { if (!headerValue.length()) @@ -180,17 +202,16 @@ bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue) // If we don't send Sec-WebSocket-Extensions header, the server should not return the header. if (!m_processors.size()) { - m_failureReason = "Received unexpected Sec-WebSocket-Extensions header"; + fail("Received unexpected Sec-WebSocket-Extensions header"); return false; } const CString headerValueData = headerValue.utf8(); ExtensionParser parser(headerValueData.data(), headerValueData.data() + headerValueData.length()); - while (!parser.finished()) { // Parse extension-token. if (!parser.consumeToken()) { - m_failureReason = "Sec-WebSocket-Extensions header is invalid"; + fail("Sec-WebSocket-Extensions header is invalid"); return false; } String extensionToken = parser.currentToken(); @@ -199,7 +220,7 @@ bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue) HashMap extensionParameters; while (parser.consumeCharacter(';')) { if (!parser.consumeToken()) { - m_failureReason = "Sec-WebSocket-Extensions header is invalid"; + fail("Sec-WebSocket-Extensions header is invalid"); return false; } @@ -208,14 +229,14 @@ bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue) if (parser.consumeQuotedStringOrToken()) extensionParameters.add(parameterToken, parser.currentToken()); else { - m_failureReason = "Sec-WebSocket-Extensions header is invalid"; + fail("Sec-WebSocket-Extensions header is invalid"); return false; } } else extensionParameters.add(parameterToken, String()); } if (!parser.finished() && !parser.consumeCharacter(',')) { - m_failureReason = "Sec-WebSocket-Extensions header is invalid"; + fail("Sec-WebSocket-Extensions header is invalid"); return false; } @@ -223,21 +244,30 @@ bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue) for (index = 0; index < m_processors.size(); ++index) { WebSocketExtensionProcessor* processor = m_processors[index].get(); if (extensionToken == processor->extensionToken()) { - if (processor->processResponse(extensionParameters)) + if (processor->processResponse(extensionParameters)) { + appendAcceptedExtension(extensionToken, extensionParameters); break; - m_failureReason = processor->failureReason(); + } + fail(processor->failureReason()); return false; } } // There is no extension which can process the response. if (index == m_processors.size()) { - m_failureReason = "Received unexpected extension: " + extensionToken; + fail("Received unexpected extension: " + extensionToken); return false; } } return parser.parsedSuccessfully(); } +String WebSocketExtensionDispatcher::acceptedExtensions() const +{ + if (m_acceptedExtensionsBuilder.isEmpty()) + return String(); + return m_acceptedExtensionsBuilder.toStringPreserveCapacity(); +} + String WebSocketExtensionDispatcher::failureReason() const { return m_failureReason; diff --git a/Source/WebCore/websockets/WebSocketExtensionDispatcher.h b/Source/WebCore/websockets/WebSocketExtensionDispatcher.h index f882bb9..35cd769 100644 --- a/Source/WebCore/websockets/WebSocketExtensionDispatcher.h +++ b/Source/WebCore/websockets/WebSocketExtensionDispatcher.h @@ -37,6 +37,7 @@ #include #include #include +#include #include namespace WebCore { @@ -50,10 +51,15 @@ public: const String createHeaderValue() const; bool processHeaderValue(const String&); + String acceptedExtensions() const; String failureReason() const; private: + void appendAcceptedExtension(const String& extensionToken, HashMap& extensionParameters); + void fail(const String& reason); + Vector > m_processors; + StringBuilder m_acceptedExtensionsBuilder; String m_failureReason; }; diff --git a/Source/WebCore/websockets/WebSocketHandshake.cpp b/Source/WebCore/websockets/WebSocketHandshake.cpp index b261557..8cc509e 100644 --- a/Source/WebCore/websockets/WebSocketHandshake.cpp +++ b/Source/WebCore/websockets/WebSocketHandshake.cpp @@ -476,6 +476,11 @@ String WebSocketHandshake::serverWebSocketAccept() const return m_response.headerFields().get("sec-websocket-accept"); } +String WebSocketHandshake::acceptedExtensions() const +{ + return m_extensionDispatcher.acceptedExtensions(); +} + const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const { return m_response; diff --git a/Source/WebCore/websockets/WebSocketHandshake.h b/Source/WebCore/websockets/WebSocketHandshake.h index 9777fb9..b025f0c 100644 --- a/Source/WebCore/websockets/WebSocketHandshake.h +++ b/Source/WebCore/websockets/WebSocketHandshake.h @@ -84,6 +84,7 @@ public: String serverUpgrade() const; String serverConnection() const; String serverWebSocketAccept() const; // Only for hybi-10 handshake. + String acceptedExtensions() const; const WebSocketHandshakeResponse& serverHandshakeResponse() const; diff --git a/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp index f0e63e1..af03a4e 100644 --- a/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp +++ b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp @@ -82,6 +82,12 @@ String WorkerThreadableWebSocketChannel::subprotocol() return m_workerClientWrapper->subprotocol(); } +String WorkerThreadableWebSocketChannel::extensions() +{ + ASSERT(m_workerClientWrapper); + return m_workerClientWrapper->extensions(); +} + bool WorkerThreadableWebSocketChannel::send(const String& message) { if (!m_bridge) @@ -262,17 +268,18 @@ void WorkerThreadableWebSocketChannel::Peer::resume() m_mainWebSocketChannel->resume(); } -static void workerContextDidConnect(ScriptExecutionContext* context, PassRefPtr workerClientWrapper, const String& subprotocol) +static void workerContextDidConnect(ScriptExecutionContext* context, PassRefPtr workerClientWrapper, const String& subprotocol, const String& extensions) { ASSERT_UNUSED(context, context->isWorkerContext()); workerClientWrapper->setSubprotocol(subprotocol); + workerClientWrapper->setExtensions(extensions); workerClientWrapper->didConnect(); } void WorkerThreadableWebSocketChannel::Peer::didConnect() { ASSERT(isMainThread()); - m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidConnect, m_workerClientWrapper, m_mainWebSocketChannel->subprotocol()), m_taskMode); + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidConnect, m_workerClientWrapper, m_mainWebSocketChannel->subprotocol(), m_mainWebSocketChannel->extensions()), m_taskMode); } static void workerContextDidReceiveMessage(ScriptExecutionContext* context, PassRefPtr workerClientWrapper, const String& message) diff --git a/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h index 2b1fa38..317fce9 100644 --- a/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h +++ b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h @@ -64,6 +64,7 @@ public: virtual bool useHixie76Protocol() OVERRIDE; virtual void connect(const KURL&, const String& protocol) OVERRIDE; virtual String subprotocol() OVERRIDE; + virtual String extensions() OVERRIDE; virtual bool send(const String& message) OVERRIDE; virtual bool send(const ArrayBuffer&) OVERRIDE; virtual bool send(const Blob&) OVERRIDE; diff --git a/Source/WebKit/chromium/ChangeLog b/Source/WebKit/chromium/ChangeLog index d2048eb..dc17ecb 100644 --- a/Source/WebKit/chromium/ChangeLog +++ b/Source/WebKit/chromium/ChangeLog @@ -1,5 +1,17 @@ 2012-02-14 Kenichi Ishibashi + [WebSocket] Add extension attribute support + https://bugs.webkit.org/show_bug.cgi?id=78557 + + Add WebSocketExtensionDispatcher::acceptedExtensions() checks. + + Reviewed by Kent Tamura. + + * tests/WebSocketExtensionDispatcherTest.cpp: + (WebCore::TEST_F): + +2012-02-14 Kenichi Ishibashi + [WebSocket] Add deflater/inflater classes. https://bugs.webkit.org/show_bug.cgi?id=78449 diff --git a/Source/WebKit/chromium/tests/WebSocketExtensionDispatcherTest.cpp b/Source/WebKit/chromium/tests/WebSocketExtensionDispatcherTest.cpp index fa0b7d7..358983b 100644 --- a/Source/WebKit/chromium/tests/WebSocketExtensionDispatcherTest.cpp +++ b/Source/WebKit/chromium/tests/WebSocketExtensionDispatcherTest.cpp @@ -90,6 +90,7 @@ TEST_F(WebSocketExtensionDispatcherTest, TestSingle) EXPECT_TRUE(m_extensions.processHeaderValue("deflate-frame")); EXPECT_EQ(1UL, m_parsedExtensionTokens.size()); EXPECT_EQ("deflate-frame", m_parsedExtensionTokens[0]); + EXPECT_EQ("deflate-frame", m_extensions.acceptedExtensions()); EXPECT_EQ(0, m_parsedParameters[0].size()); } @@ -122,6 +123,8 @@ TEST_F(WebSocketExtensionDispatcherTest, TestMultiple) addMockProcessor("mux"); addMockProcessor("deflate-frame"); EXPECT_TRUE(m_extensions.processHeaderValue("mux ; max-channels =4;flow-control, deflate-frame ")); + EXPECT_TRUE(m_extensions.acceptedExtensions().find("mux") != notFound); + EXPECT_TRUE(m_extensions.acceptedExtensions().find("deflate-frame") != notFound); for (size_t i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) { EXPECT_EQ(expected[i].token, m_parsedExtensionTokens[i]); const HashMap& expectedParameters = expected[i].parameters; @@ -169,6 +172,7 @@ TEST_F(WebSocketExtensionDispatcherTest, TestInvalid) addMockProcessor("x-foo"); addMockProcessor("x-bar"); EXPECT_FALSE(m_extensions.processHeaderValue(inputs[i])); + EXPECT_TRUE(m_extensions.acceptedExtensions().isNull()); } }