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
+2012-02-14 Kenichi Ishibashi <bashi@chromium.org>
+
+ [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 <haraken@chromium.org>
Rename [JSGenerateToJS] to [JSGenerateToJSObject]
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;
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;
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);
WebSocketChannelClient* m_client;
bool m_syncMethodDone;
bool m_useHixie76Protocol;
- Vector<UChar> m_subprotocol; // ThreadSafeRefCounted must not have a String member variable.
+ // ThreadSafeRefCounted must not have String member variables.
+ Vector<UChar> m_subprotocol;
+ Vector<UChar> m_extensions;
bool m_sendRequestResult;
unsigned long m_bufferedAmount;
bool m_suspended;
, m_binaryType(BinaryTypeBlob)
, m_useHixie76Protocol(true)
, m_subprotocol("")
+ , m_extensions("")
{
}
{
if (m_useHixie76Protocol)
return String();
- // WebSocket protocol extension is not supported yet.
- return "";
+ return m_extensions;
}
String WebSocket::binaryType() const
BinaryType m_binaryType;
bool m_useHixie76Protocol;
String m_subprotocol;
+ String m_extensions;
};
} // namespace WebCore
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());
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;
#include "WebSocketExtensionDispatcher.h"
#include <wtf/ASCIICType.h>
+#include <wtf/HashMap.h>
#include <wtf/text/CString.h>
-#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringHash.h>
namespace WebCore {
return builder.toString();
}
+void WebSocketExtensionDispatcher::appendAcceptedExtension(const String& extensionToken, HashMap<String, String>& 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<String, String>::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())
// 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();
HashMap<String, String> extensionParameters;
while (parser.consumeCharacter(';')) {
if (!parser.consumeToken()) {
- m_failureReason = "Sec-WebSocket-Extensions header is invalid";
+ fail("Sec-WebSocket-Extensions header is invalid");
return false;
}
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;
}
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;
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
const String createHeaderValue() const;
bool processHeaderValue(const String&);
+ String acceptedExtensions() const;
String failureReason() const;
private:
+ void appendAcceptedExtension(const String& extensionToken, HashMap<String, String>& extensionParameters);
+ void fail(const String& reason);
+
Vector<OwnPtr<WebSocketExtensionProcessor> > m_processors;
+ StringBuilder m_acceptedExtensionsBuilder;
String m_failureReason;
};
return m_response.headerFields().get("sec-websocket-accept");
}
+String WebSocketHandshake::acceptedExtensions() const
+{
+ return m_extensionDispatcher.acceptedExtensions();
+}
+
const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const
{
return m_response;
String serverUpgrade() const;
String serverConnection() const;
String serverWebSocketAccept() const; // Only for hybi-10 handshake.
+ String acceptedExtensions() const;
const WebSocketHandshakeResponse& serverHandshakeResponse() const;
return m_workerClientWrapper->subprotocol();
}
+String WorkerThreadableWebSocketChannel::extensions()
+{
+ ASSERT(m_workerClientWrapper);
+ return m_workerClientWrapper->extensions();
+}
+
bool WorkerThreadableWebSocketChannel::send(const String& message)
{
if (!m_bridge)
m_mainWebSocketChannel->resume();
}
-static void workerContextDidConnect(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, const String& subprotocol)
+static void workerContextDidConnect(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> 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<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, const String& message)
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;
2012-02-14 Kenichi Ishibashi <bashi@chromium.org>
+ [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 <bashi@chromium.org>
+
[WebSocket] Add deflater/inflater classes.
https://bugs.webkit.org/show_bug.cgi?id=78449
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());
}
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<String, String>& expectedParameters = expected[i].parameters;
addMockProcessor("x-foo");
addMockProcessor("x-bar");
EXPECT_FALSE(m_extensions.processHeaderValue(inputs[i]));
+ EXPECT_TRUE(m_extensions.acceptedExtensions().isNull());
}
}