Adding WebSocket per-frame DEFLATE extension
authorbashi@chromium.org <bashi@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Feb 2012 10:03:46 +0000 (10:03 +0000)
committerbashi@chromium.org <bashi@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Feb 2012 10:03:46 +0000 (10:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=77522

Source/JavaScriptCore:

Added USE(ZLIB) flag.

Reviewed by Kent Tamura.

* wtf/Platform.h:

Source/WebCore:

Add WebSocketDeflateFramer class which handles deflate-frame extension.
This class encapsulates WebSocketDeflater and WebSocketInflater classes,
which depend on zlib, so that WebSocketChannel is not necessary to aware
zlib dependency.

Reviewed by Kent Tamura.

Tests: http/tests/websocket/tests/hybi/compressed-control-frame.html
       http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html
       http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html
       http/tests/websocket/tests/hybi/deflate-frame-parameter.html

* CMakeLists.txt: Added WebSocketDeflateFramer.(cpp|h)
* GNUmakefile.list.am: Ditto.
* Target.pri: Ditto.
* WebCore.gypi: Ditto.
* WebCore.vcproj/WebCore.vcproj: Ditto.
* WebCore.xcodeproj/project.pbxproj: Ditto.
* websockets/WebSocket.cpp:
(WebCore::WebSocket::didConnect): Set m_extensions.
* websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::connect): Add deflate-frame extension processor to WebSocketHanshake if deflate can use.
(WebCore::WebSocketChannel::fail): Call m_deflateFramer.didFail().
(WebCore::WebSocketChannel::processFrame): Decompress frames if needed.
(WebCore::WebSocketChannel::sendFrame): Compress frames if possible.
* websockets/WebSocketChannel.h:
* websockets/WebSocketDeflateFramer.cpp: Added.
(WebCore):
(WebSocketExtensionDeflateFrame):
(WebCore::WebSocketExtensionDeflateFrame::create):
(WebCore::WebSocketExtensionDeflateFrame::~WebSocketExtensionDeflateFrame):
(WebCore::WebSocketExtensionDeflateFrame::WebSocketExtensionDeflateFrame):
(WebCore::WebSocketExtensionDeflateFrame::handshakeString):
(WebCore::WebSocketExtensionDeflateFrame::processResponse):
(WebCore::DeflateResultHolder::DeflateResultHolder):
(WebCore::DeflateResultHolder::~DeflateResultHolder):
(WebCore::DeflateResultHolder::fail):
(WebCore::InflateResultHolder::InflateResultHolder):
(WebCore::InflateResultHolder::~InflateResultHolder):
(WebCore::InflateResultHolder::fail):
(WebCore::WebSocketDeflateFramer::WebSocketDeflateFramer):
(WebCore::WebSocketDeflateFramer::createExtensionProcessor):
(WebCore::WebSocketDeflateFramer::canDeflate):
(WebCore::WebSocketDeflateFramer::enableDeflate):
(WebCore::WebSocketDeflateFramer::deflate):
(WebCore::WebSocketDeflateFramer::resetDeflateContext):
(WebCore::WebSocketDeflateFramer::inflate):
(WebCore::WebSocketDeflateFramer::resetInflateContext):
(WebCore::WebSocketDeflateFramer::didFail):
* websockets/WebSocketDeflateFramer.h: Added.
(WebCore):
(DeflateResultHolder):
(WebCore::DeflateResultHolder::succeeded):
(WebCore::DeflateResultHolder::failureReason):
(InflateResultHolder):
(WebCore::InflateResultHolder::succeeded):
(WebCore::InflateResultHolder::failureReason):
(WebSocketDeflateFramer):
(WebCore::WebSocketDeflateFramer::enabled):

LayoutTests:

Added tests for WebSocket deflate-frame extension. Also updated some
tests to follow the change.

Reviewed by Kent Tamura.

* http/tests/websocket/tests/hybi/compressed-control-frame-expected.txt: Added.
* http/tests/websocket/tests/hybi/compressed-control-frame.html: Added.
* http/tests/websocket/tests/hybi/compressed-control-frame_wsh.py: Added.
(web_socket_do_extra_handshake):
(web_socket_transfer_data):
* http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff-expected.txt: Added.
* http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html: Added.
* http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter-expected.txt: Added.
* http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html: Added.
* http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter_wsh.py: Added.
(web_socket_do_extra_handshake):
(web_socket_transfer_data):
* http/tests/websocket/tests/hybi/deflate-frame-parameter-expected.txt: Added.
* http/tests/websocket/tests/hybi/deflate-frame-parameter.html: Added.
* http/tests/websocket/tests/hybi/deflate-frame_wsh.py: Added.
(_get_deflate_frame_extension_processor):
(web_socket_do_extra_handshake):
(web_socket_transfer_data):
* http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header-expected.txt:
* http/tests/websocket/tests/hybi/send-file-blob_wsh.py:
(_retrieve_frame):
(web_socket_transfer_data):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@108468 268f45cc-cd09-0410-ab3c-d52691b4dbfc

28 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame_wsh.py [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter_wsh.py [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-parameter-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-parameter.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/deflate-frame_wsh.py [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header-expected.txt
LayoutTests/http/tests/websocket/tests/hybi/send-file-blob_wsh.py
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/wtf/Platform.h
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Target.pri
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/websockets/WebSocket.cpp
Source/WebCore/websockets/WebSocketChannel.cpp
Source/WebCore/websockets/WebSocketChannel.h
Source/WebCore/websockets/WebSocketDeflateFramer.cpp [new file with mode: 0644]
Source/WebCore/websockets/WebSocketDeflateFramer.h [new file with mode: 0644]

index ec85fd8..2ff21d6 100644 (file)
@@ -1,3 +1,36 @@
+2012-02-22  Kenichi Ishibashi  <bashi@chromium.org>
+
+        Adding WebSocket per-frame DEFLATE extension
+        https://bugs.webkit.org/show_bug.cgi?id=77522
+
+        Added tests for WebSocket deflate-frame extension. Also updated some
+        tests to follow the change.
+
+        Reviewed by Kent Tamura.
+
+        * http/tests/websocket/tests/hybi/compressed-control-frame-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/compressed-control-frame.html: Added.
+        * http/tests/websocket/tests/hybi/compressed-control-frame_wsh.py: Added.
+        (web_socket_do_extra_handshake):
+        (web_socket_transfer_data):
+        * http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html: Added.
+        * http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html: Added.
+        * http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter_wsh.py: Added.
+        (web_socket_do_extra_handshake):
+        (web_socket_transfer_data):
+        * http/tests/websocket/tests/hybi/deflate-frame-parameter-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/deflate-frame-parameter.html: Added.
+        * http/tests/websocket/tests/hybi/deflate-frame_wsh.py: Added.
+        (_get_deflate_frame_extension_processor):
+        (web_socket_do_extra_handshake):
+        (web_socket_transfer_data):
+        * http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header-expected.txt:
+        * http/tests/websocket/tests/hybi/send-file-blob_wsh.py:
+        (_retrieve_frame):
+        (web_socket_transfer_data):
+
 2012-02-22  Yanbin Zhang  <yanbin.zhang@intel.com>
 
         [GTK] fast/mediastream/peerconnection-argument-types.html fails
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame-expected.txt
new file mode 100644 (file)
index 0000000..641ec47
--- /dev/null
@@ -0,0 +1,12 @@
+CONSOLE MESSAGE: Received unexpected compressed frame
+Test whether a compressed control frame is rejected
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+onopen() was called.
+onclose() was called.
+PASS closeEvent.wasClean is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame.html b/LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame.html
new file mode 100644 (file)
index 0000000..b2088c7
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../../js-test-resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script type="text/javascript">
+description("Test whether a compressed control frame is rejected");
+
+window.jsTestIsAsync = true;
+if (window.layoutTestController)
+    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
+
+var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/compressed-control-frame");
+var closeEvent;
+
+ws.onopen = function()
+{
+    debug("onopen() was called.");
+};
+
+ws.onmessage = function(event)
+{
+    var message = event.data;
+    testFailed("onmessage() was called. (message = \"" + message + "\")");
+};
+
+ws.onclose = function(event)
+{
+    debug("onclose() was called.");
+    closeEvent = event;
+    shouldBeFalse("closeEvent.wasClean");
+    finishJSTest();
+};
+
+</script>
+<script src="../../../../js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame_wsh.py b/LayoutTests/http/tests/websocket/tests/hybi/compressed-control-frame_wsh.py
new file mode 100644 (file)
index 0000000..8e12e9c
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright 2012, Google Inc.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from mod_pywebsocket import common
+from mod_pywebsocket import stream
+import zlib
+
+
+def web_socket_do_extra_handshake(request):
+    pass
+
+
+def web_socket_transfer_data(request):
+    compress = zlib.compressobj(
+        zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
+    compressed_message = compress.compress('close message')
+    compressed_message += compress.flush(zlib.Z_SYNC_FLUSH)
+    compressed_message = compressed_message[:-4]
+    header = stream.create_header(
+        opcode=common.OPCODE_CLOSE, payload_length=len(compressed_message),
+        fin=1, rsv1=1, rsv2=0, rsv3=0, mask=False)
+    request.connection.write(header + compressed_message)
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff-expected.txt
new file mode 100644 (file)
index 0000000..562fef3
--- /dev/null
@@ -0,0 +1,20 @@
+Test compression enabled/disabled frame receiving.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+Sending message: "Hello"
+PASS event.data is 'Hello'
+Sending message: "DisableCompression"
+PASS event.data is 'DisableCompression'
+Sending message: "World"
+PASS event.data is 'World'
+Sending message: "EnableCompression"
+PASS event.data is 'EnableCompression'
+Sending message: "Goodbye"
+PASS event.data is 'Goodbye'
+onclose() was called.
+PASS closeEvent.wasClean is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html
new file mode 100644 (file)
index 0000000..532ac57
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../../js-test-resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+description("Test compression enabled/disabled frame receiving.");
+
+window.jsTestIsAsync = true;
+if (window.layoutTestController)
+    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
+
+var closeEvent;
+var ws;
+var messageIndex;
+
+var messages = [
+    "Hello",
+    "DisableCompression", // This disables compression
+    "World",
+    "EnableCompression", // This enables compression
+    "Goodbye"
+];
+
+ws = new WebSocket("ws://localhost:8880/websocket/tests/hybi/deflate-frame");
+
+ws.onopen = function(event)
+{
+    messageIndex = 0;
+    debug("Sending message: \"" + messages[messageIndex] + "\"");
+    ws.send(messages[messageIndex]);
+};
+
+ws.onmessage = function(event)
+{
+    shouldBe("event.data", "'" + messages[messageIndex] + "'");
+    if (messageIndex === messages.length - 1)
+        ws.close();
+    else {
+        messageIndex += 1;
+        debug("Sending message: \"" + messages[messageIndex] + "\"");
+        ws.send(messages[messageIndex]);
+    }
+};
+
+ws.onclose = function(event)
+{
+    debug("onclose() was called.");
+    closeEvent = event;
+    shouldBeTrue("closeEvent.wasClean");
+    finishJSTest();
+};
+
+</script>
+<script src="../../../../js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter-expected.txt
new file mode 100644 (file)
index 0000000..83a24d2
--- /dev/null
@@ -0,0 +1,28 @@
+CONSOLE MESSAGE: Received unexpected deflate-frame parameter
+CONSOLE MESSAGE: Received invalid max_window_bits parameter
+CONSOLE MESSAGE: Received invalid max_window_bits parameter
+CONSOLE MESSAGE: Received invalid no_context_takeover parameter
+CONSOLE MESSAGE: Received unexpected deflate-frame parameter
+Test whether WebSocket rejects invalid deflate-frame parameters.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+Testing parameter: "x-foo"
+onclose() was called.
+PASS closeEvent.wasClean is false
+Testing parameter: "max_window_bits=7"
+onclose() was called.
+PASS closeEvent.wasClean is false
+Testing parameter: "max_window_bits=16"
+onclose() was called.
+PASS closeEvent.wasClean is false
+Testing parameter: "no_context_takeover=foo"
+onclose() was called.
+PASS closeEvent.wasClean is false
+Testing parameter: "max_window_bits=8; no_context_takeover; x-foo"
+onclose() was called.
+PASS closeEvent.wasClean is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html
new file mode 100644 (file)
index 0000000..5a5fbd0
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../../js-test-resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+description("Test whether WebSocket rejects invalid deflate-frame parameters.");
+
+window.jsTestIsAsync = true;
+if (window.layoutTestController)
+    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
+
+var closeEvent;
+
+var testCase = [
+    "x-foo",
+    "max_window_bits=7",
+    "max_window_bits=16",
+    "no_context_takeover=foo",
+    "max_window_bits=8; no_context_takeover; x-foo"
+];
+
+function doTest(index)
+{
+    var parameter = testCase[index];
+    var url = "ws://localhost:8880/websocket/tests/hybi/deflate-frame-invalid-parameter?" + encodeURI(parameter);
+    var ws = new WebSocket(url);
+
+    debug("Testing parameter: \"" + parameter + "\"");
+
+    ws.onmessage = function(event)
+    {
+        var message = event.data;
+        testFailed("onmessage() was called. (message = \"" + message + "\")");
+    };
+
+    ws.onclose = function(event)
+    {
+        debug("onclose() was called.");
+        closeEvent = event;
+        shouldBeFalse("closeEvent.wasClean");
+        if (index === testCase.length - 1)
+            finishJSTest();
+        else
+            doTest(index + 1);
+    };
+}
+
+doTest(0);
+
+</script>
+<script src="../../../../js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter_wsh.py b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter_wsh.py
new file mode 100644 (file)
index 0000000..a8cc25c
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright 2012, Google Inc.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import urllib
+from mod_pywebsocket import handshake
+from mod_pywebsocket.handshake.hybi import compute_accept
+
+
+def web_socket_do_extra_handshake(request):
+    resources = request.ws_resource.split('?', 1)
+    parameters = None
+    if len(resources) == 2:
+        parameters = urllib.unquote(resources[1])
+
+    message = 'HTTP/1.1 101 Switching Protocols\r\n'
+    message += 'Upgrade: websocket\r\n'
+    message += 'Connection: Upgrade\r\n'
+    message += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
+    message += 'Sec-WebSocket-Extensions: x-webkit-deflate-frame'
+    if parameters:
+        message += '; %s\r\n' % parameters
+    else:
+        message += '\r\n'
+    message += '\r\n'
+    request.connection.write(message)
+    raise handshake.AbortedByUserException('Abort the connection') # Prevents pywebsocket from sending its own handshake message.
+
+
+def web_socket_transfer_data(request):
+    pass
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-parameter-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-parameter-expected.txt
new file mode 100644 (file)
index 0000000..7263fe3
--- /dev/null
@@ -0,0 +1,30 @@
+Test WebSocket deflate-frame extension.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+Testing query: "max_window_bits=8"
+PASS ws.extensions.search('x-webkit-deflate-frame') != -1 is true
+PASS ws.extensions.search('max_window_bits=8') != -1 is true
+PASS event.data is firstMessage
+PASS event.data is secondMessage
+onclose() was called.
+PASS closeEvent.wasClean is true
+Testing query: "no_context_takeover"
+PASS ws.extensions.search('x-webkit-deflate-frame') != -1 is true
+PASS ws.extensions.search('no_context_takeover') != -1 is true
+PASS event.data is firstMessage
+PASS event.data is secondMessage
+onclose() was called.
+PASS closeEvent.wasClean is true
+Testing query: "max_window_bits=8&no_context_takeover"
+PASS ws.extensions.search('x-webkit-deflate-frame') != -1 is true
+PASS ws.extensions.search('max_window_bits=8') != -1 is true
+PASS ws.extensions.search('no_context_takeover') != -1 is true
+PASS event.data is firstMessage
+PASS event.data is secondMessage
+onclose() was called.
+PASS closeEvent.wasClean is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-parameter.html b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame-parameter.html
new file mode 100644 (file)
index 0000000..4b77e47
--- /dev/null
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../../js-test-resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+description("Test WebSocket deflate-frame extension.");
+
+window.jsTestIsAsync = true;
+if (window.layoutTestController)
+    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
+
+var closeEvent;
+var ws;
+var messageIndex;
+
+var queries = [
+    "max_window_bits=8",
+    "no_context_takeover",
+    "max_window_bits=8&no_context_takeover"
+];
+
+// The first message consists of a lot of 'b' and a few 'a' at the head and
+// the tail, while the second one consists of 'a'.
+var firstMessage = '';
+var secondMessage = '';
+for (var i = 0; i < 16; ++i) {
+    firstMessage += 'a';
+    secondMessage += 'a';
+}
+for (var i = 0; i < 1024; ++i) {
+    firstMessage += 'b';
+    secondMessage += 'a';
+}
+for (var i = 0; i < 16; ++i) {
+    firstMessage += 'a';
+    secondMessage += 'a';
+}
+
+function doTest(queryIndex)
+{
+    var query = queries[queryIndex];
+    debug("Testing query: \"" + query + "\"");
+
+    var url = "ws://localhost:8880/websocket/tests/hybi/deflate-frame?" + query;
+    ws = new WebSocket(url);
+    messageIndex = 0;
+
+    ws.onopen = function(event)
+    {
+        shouldBeTrue("ws.extensions.search('x-webkit-deflate-frame') != -1");
+        parameters = query.split('&');
+        for (var i = 0; i < parameters.length; ++i)
+            shouldBeTrue("ws.extensions.search('" + parameters[i] + "') != -1");
+        ws.send(firstMessage);
+    };
+
+    ws.onmessage = function(event)
+    {
+        if (messageIndex === 0) {
+            shouldBe("event.data", "firstMessage");
+            messageIndex += 1
+            ws.send(secondMessage);
+        } else {
+            shouldBe("event.data", "secondMessage");
+            ws.close();
+        }
+    };
+
+    ws.onclose = function(event)
+    {
+        debug("onclose() was called.");
+        closeEvent = event;
+        shouldBeTrue("closeEvent.wasClean");
+        if (queryIndex === queries.length - 1)
+            finishJSTest();
+        else
+            doTest(queryIndex + 1);
+    };
+}
+
+doTest(0);
+
+</script>
+<script src="../../../../js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame_wsh.py b/LayoutTests/http/tests/websocket/tests/hybi/deflate-frame_wsh.py
new file mode 100644 (file)
index 0000000..83b7838
--- /dev/null
@@ -0,0 +1,82 @@
+# Copyright 2012, Google Inc.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import urlparse
+from mod_pywebsocket.extensions import DeflateFrameExtensionProcessor
+from mod_pywebsocket.extensions import ExtensionProcessorInterface
+from mod_pywebsocket.common import ExtensionParameter
+
+
+_GOODBYE_MESSAGE = u'Goodbye'
+_ENABLE_MESSAGE = u'EnableCompression'
+_DISABLE_MESSAGE = u'DisableCompression'
+
+
+def _get_deflate_frame_extension_processor(request):
+    for extension_processor in request.ws_extension_processors:
+        if isinstance(extension_processor, DeflateFrameExtensionProcessor):
+            return extension_processor
+    return None
+
+
+def web_socket_do_extra_handshake(request):
+    processor = _get_deflate_frame_extension_processor(request)
+    if not processor:
+        return
+    r = request.ws_resource.split('?', 1)
+    if len(r) == 1:
+        return
+    parameters = urlparse.parse_qs(r[1], keep_blank_values=True)
+    if 'max_window_bits' in parameters:
+        window_bits = int(parameters['max_window_bits'][0])
+        processor.set_response_window_bits(window_bits)
+    if 'no_context_takeover' in parameters:
+        processor.set_response_no_context_takeover(True)
+
+
+def web_socket_transfer_data(request):
+    processor = _get_deflate_frame_extension_processor(request)
+    while True:
+        line = request.ws_stream.receive_message()
+        if line is None:
+            return
+        if isinstance(line, unicode):
+            if processor:
+                if line == _ENABLE_MESSAGE:
+                    processor.enable_outgoing_compression()
+                elif line == _DISABLE_MESSAGE:
+                    processor.disable_outgoing_compression()
+            request.ws_stream.send_message(line, binary=False)
+            if line == _GOODBYE_MESSAGE:
+                return
+        else:
+            request.ws_stream.send_message(line, binary=True)
+
+
+# vi:sts=4 sw=4 et
index adf06b6..cc99af0 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: Received unexpected Sec-WebSocket-Extensions header
+CONSOLE MESSAGE: Received unexpected extension: x-foo
 Test whether WebSocket handshake fails if the server sends Sec-WebSocket-Extensions header.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
index daa4404..792034c 100644 (file)
@@ -2,6 +2,14 @@ from mod_pywebsocket import common
 from mod_pywebsocket import msgutil
 
 
+def _retrieve_frame(stream):
+    # FIXME: Use better API.
+    frame = stream._receive_frame_as_frame_object()
+    for frame_filter in stream._options.incoming_frame_filters:
+        frame_filter.filter(frame)
+    return frame
+
+
 def web_socket_do_extra_handshake(request):
     pass # Always accept.
 
@@ -10,12 +18,11 @@ def web_socket_transfer_data(request):
     expected_messages = ['Hello, world!']
 
     for test_number, expected_message in enumerate(expected_messages):
-        # FIXME: Use better API.
-        opcode, payload, final, unused_reserved1, unused_reserved2, unused_reserved3 = request.ws_stream._receive_frame()
-        if opcode == common.OPCODE_BINARY and payload == expected_message and final:
+        frame = _retrieve_frame(request.ws_stream)
+        if frame.opcode == common.OPCODE_BINARY and frame.payload == expected_message and frame.fin:
             msgutil.send_message(request, 'PASS: Message #%d.' % test_number)
         else:
-            msgutil.send_message(request, 'FAIL: Message #%d: Received unexpected frame: opcode = %r, payload = %r, final = %r' % (test_number, opcode, payload, final))
+            msgutil.send_message(request, 'FAIL: Message #%d: Received unexpected frame: opcode = %r, payload = %r, final = %r' % (test_number, frame.opcode, frame.payload, frame.fin))
 
 
 def all_distinct_bytes():
index d62e95c..8c76aa6 100644 (file)
@@ -1,3 +1,14 @@
+2012-02-22  Kenichi Ishibashi  <bashi@chromium.org>
+
+        Adding WebSocket per-frame DEFLATE extension
+        https://bugs.webkit.org/show_bug.cgi?id=77522
+
+        Added USE(ZLIB) flag.
+
+        Reviewed by Kent Tamura.
+
+        * wtf/Platform.h:
+
 2012-02-22  Hojong Han  <hojong.han@samsung.com>
 
         Short circuit fixed for a 16 bt pattern character and an 8 bit string.
index bf6ad6e..b49890b 100644 (file)
 #define WTF_USE_WTFURL 0
 #endif
 
+#if !PLATFORM(QT) && !PLATFORM(EFL)
+#define WTF_USE_ZLIB 1
+#endif
+
 #endif /* WTF_Platform_h */
index 4f83c63..9c20761 100644 (file)
@@ -2056,6 +2056,7 @@ IF (ENABLE_WEB_SOCKETS)
         websockets/ThreadableWebSocketChannelClientWrapper.cpp
         websockets/WebSocket.cpp
         websockets/WebSocketChannel.cpp
+        websockets/WebSocketDeflateFramer.cpp
         websockets/WebSocketExtensionDispatcher.cpp
         websockets/WebSocketHandshake.cpp
         websockets/WebSocketHandshakeRequest.cpp
index 6f5db5b..7d10fbb 100644 (file)
@@ -1,3 +1,68 @@
+2012-02-22  Kenichi Ishibashi  <bashi@chromium.org>
+
+        Adding WebSocket per-frame DEFLATE extension
+        https://bugs.webkit.org/show_bug.cgi?id=77522
+
+        Add WebSocketDeflateFramer class which handles deflate-frame extension.
+        This class encapsulates WebSocketDeflater and WebSocketInflater classes,
+        which depend on zlib, so that WebSocketChannel is not necessary to aware
+        zlib dependency.
+
+        Reviewed by Kent Tamura.
+
+        Tests: http/tests/websocket/tests/hybi/compressed-control-frame.html
+               http/tests/websocket/tests/hybi/deflate-frame-comp-bit-onoff.html
+               http/tests/websocket/tests/hybi/deflate-frame-invalid-parameter.html
+               http/tests/websocket/tests/hybi/deflate-frame-parameter.html
+
+        * CMakeLists.txt: Added WebSocketDeflateFramer.(cpp|h)
+        * GNUmakefile.list.am: Ditto.
+        * Target.pri: Ditto.
+        * WebCore.gypi: Ditto.
+        * WebCore.vcproj/WebCore.vcproj: Ditto.
+        * WebCore.xcodeproj/project.pbxproj: Ditto.
+        * websockets/WebSocket.cpp:
+        (WebCore::WebSocket::didConnect): Set m_extensions.
+        * websockets/WebSocketChannel.cpp:
+        (WebCore::WebSocketChannel::connect): Add deflate-frame extension processor to WebSocketHanshake if deflate can use.
+        (WebCore::WebSocketChannel::fail): Call m_deflateFramer.didFail().
+        (WebCore::WebSocketChannel::processFrame): Decompress frames if needed.
+        (WebCore::WebSocketChannel::sendFrame): Compress frames if possible.
+        * websockets/WebSocketChannel.h:
+        * websockets/WebSocketDeflateFramer.cpp: Added.
+        (WebCore):
+        (WebSocketExtensionDeflateFrame):
+        (WebCore::WebSocketExtensionDeflateFrame::create):
+        (WebCore::WebSocketExtensionDeflateFrame::~WebSocketExtensionDeflateFrame):
+        (WebCore::WebSocketExtensionDeflateFrame::WebSocketExtensionDeflateFrame):
+        (WebCore::WebSocketExtensionDeflateFrame::handshakeString):
+        (WebCore::WebSocketExtensionDeflateFrame::processResponse):
+        (WebCore::DeflateResultHolder::DeflateResultHolder):
+        (WebCore::DeflateResultHolder::~DeflateResultHolder):
+        (WebCore::DeflateResultHolder::fail):
+        (WebCore::InflateResultHolder::InflateResultHolder):
+        (WebCore::InflateResultHolder::~InflateResultHolder):
+        (WebCore::InflateResultHolder::fail):
+        (WebCore::WebSocketDeflateFramer::WebSocketDeflateFramer):
+        (WebCore::WebSocketDeflateFramer::createExtensionProcessor):
+        (WebCore::WebSocketDeflateFramer::canDeflate):
+        (WebCore::WebSocketDeflateFramer::enableDeflate):
+        (WebCore::WebSocketDeflateFramer::deflate):
+        (WebCore::WebSocketDeflateFramer::resetDeflateContext):
+        (WebCore::WebSocketDeflateFramer::inflate):
+        (WebCore::WebSocketDeflateFramer::resetInflateContext):
+        (WebCore::WebSocketDeflateFramer::didFail):
+        * websockets/WebSocketDeflateFramer.h: Added.
+        (WebCore):
+        (DeflateResultHolder):
+        (WebCore::DeflateResultHolder::succeeded):
+        (WebCore::DeflateResultHolder::failureReason):
+        (InflateResultHolder):
+        (WebCore::InflateResultHolder::succeeded):
+        (WebCore::InflateResultHolder::failureReason):
+        (WebSocketDeflateFramer):
+        (WebCore::WebSocketDeflateFramer::enabled):
+
 2012-02-22  Yuta Kitamura  <yutak@chromium.org>
 
         Unreviewed, rolling out r108453.
index 639f1b4..b335363 100644 (file)
@@ -4339,6 +4339,8 @@ webcore_sources += \
        Source/WebCore/websockets/WebSocketChannel.h \
        Source/WebCore/websockets/WebSocket.cpp \
        Source/WebCore/websockets/WebSocket.h \
+       Source/WebCore/websockets/WebSocketDeflateFramer.cpp \
+       Source/WebCore/websockets/WebSocketDeflateFramer.h \
        Source/WebCore/websockets/WebSocketDeflater.cpp \
        Source/WebCore/websockets/WebSocketDeflater.h \
        Source/WebCore/websockets/WebSocketExtensionDispatcher.cpp \
index b81ec6f..fc1e4a3 100644 (file)
@@ -3666,6 +3666,7 @@ contains(DEFINES, ENABLE_WEB_SOCKETS=1) {
         websockets/WebSocket.h \
         websockets/WebSocketChannel.h \
         websockets/WebSocketChannelClient.h \
+        websockets/WebSocketDeflateFramer.h \
         websockets/WebSocketExtensionDispatcher.h \
         websockets/WebSocketExtensionProcessor.h \
         websockets/WebSocketFrame.h \
@@ -3677,6 +3678,7 @@ contains(DEFINES, ENABLE_WEB_SOCKETS=1) {
     SOURCES += \
         websockets/WebSocket.cpp \
         websockets/WebSocketChannel.cpp \
+        websockets/WebSocketDeflateFramer.cpp \
         websockets/WebSocketExtensionDispatcher.cpp \
         websockets/WebSocketHandshake.cpp \
         websockets/WebSocketHandshakeRequest.cpp \
index 71e9636..7102a40 100644 (file)
             'websockets/WebSocketChannel.cpp',
             'websockets/WebSocketChannel.h',
             'websockets/WebSocketChannelClient.h',
+            'websockets/WebSocketDeflateFramer.cpp',
+            'websockets/WebSocketDeflateFramer.h',
             'websockets/WebSocketDeflater.cpp',
             'websockets/WebSocketDeflater.h',
             'websockets/WebSocketExtensionDispatcher.cpp',
index 061c43f..135de25 100755 (executable)
                                >
                        </File>
                        <File
+                               RelativePath="..\websockets\WebSocketDeflateFramer.cpp"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\websockets\WebSocketDeflateFramer.h"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\websockets\WebSocketDeflater.cpp"
                                >
                        </File>
index 2b7d767..114e2d3 100644 (file)
                4A1E719614E101F900626F9D /* JSHTMLShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A1E719414E101F900626F9D /* JSHTMLShadowElement.h */; };
                4A1E71A514E106AC00626F9D /* JSShadowRoot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A1E71A314E106AC00626F9D /* JSShadowRoot.cpp */; };
                4A1E71A614E106AC00626F9D /* JSShadowRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A1E71A414E106AC00626F9D /* JSShadowRoot.h */; };
+               4A29222B14F468BA0021F77E /* WebSocketDeflateFramer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A29222914F468BA0021F77E /* WebSocketDeflateFramer.cpp */; };
+               4A29222C14F468BA0021F77E /* WebSocketDeflateFramer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A29222A14F468BA0021F77E /* WebSocketDeflateFramer.h */; };
                4A4A234614F1E1440046FBF1 /* WebSocketFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A4A234514F1E1440046FBF1 /* WebSocketFrame.h */; };
                4A6E9FC313C17D1D0046A7F8 /* FontFeatureValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6E9FC113C17D1D0046A7F8 /* FontFeatureValue.cpp */; };
                4A6E9FC413C17D1D0046A7F8 /* FontFeatureValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A6E9FC213C17D1D0046A7F8 /* FontFeatureValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4A1E719414E101F900626F9D /* JSHTMLShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSHTMLShadowElement.h; sourceTree = "<group>"; };
                4A1E71A314E106AC00626F9D /* JSShadowRoot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSShadowRoot.cpp; sourceTree = "<group>"; };
                4A1E71A414E106AC00626F9D /* JSShadowRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSShadowRoot.h; sourceTree = "<group>"; };
+               4A29222914F468BA0021F77E /* WebSocketDeflateFramer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketDeflateFramer.cpp; sourceTree = "<group>"; };
+               4A29222A14F468BA0021F77E /* WebSocketDeflateFramer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketDeflateFramer.h; sourceTree = "<group>"; };
                4A4A234514F1E1440046FBF1 /* WebSocketFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketFrame.h; sourceTree = "<group>"; };
                4A6E9FC113C17D1D0046A7F8 /* FontFeatureValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontFeatureValue.cpp; sourceTree = "<group>"; };
                4A6E9FC213C17D1D0046A7F8 /* FontFeatureValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontFeatureValue.h; sourceTree = "<group>"; };
                                510D4A47103177A20049EA54 /* WebSocketChannel.cpp */,
                                510D4A48103177A20049EA54 /* WebSocketChannel.h */,
                                510D4A49103177A20049EA54 /* WebSocketChannelClient.h */,
+                               4A29222914F468BA0021F77E /* WebSocketDeflateFramer.cpp */,
+                               4A29222A14F468BA0021F77E /* WebSocketDeflateFramer.h */,
                                4AE02ABB14E8A9D200BC3BA7 /* WebSocketDeflater.cpp */,
                                4AE02ABC14E8A9D200BC3BA7 /* WebSocketDeflater.h */,
                                4A957F0314E241100049DBFB /* WebSocketExtensionDispatcher.cpp */,
                                518A34C21026C831001B6896 /* WebSocket.h in Headers */,
                                510D4A4F103177A20049EA54 /* WebSocketChannel.h in Headers */,
                                510D4A50103177A20049EA54 /* WebSocketChannelClient.h in Headers */,
+                               4A29222C14F468BA0021F77E /* WebSocketDeflateFramer.h in Headers */,
                                4AE02ABE14E8A9D200BC3BA7 /* WebSocketDeflater.h in Headers */,
                                4A957F0714E241300049DBFB /* WebSocketExtensionDispatcher.h in Headers */,
                                4ADE25FA14E3BB4C004C2213 /* WebSocketExtensionProcessor.h in Headers */,
                                1CAF34820A6C405200ABE06E /* WebScriptObject.mm in Sources */,
                                518A34C11026C831001B6896 /* WebSocket.cpp in Sources */,
                                510D4A4E103177A20049EA54 /* WebSocketChannel.cpp in Sources */,
+                               4A29222B14F468BA0021F77E /* WebSocketDeflateFramer.cpp in Sources */,
                                4AE02ABD14E8A9D200BC3BA7 /* WebSocketDeflater.cpp in Sources */,
                                4A957F0614E2412A0049DBFB /* WebSocketExtensionDispatcher.cpp in Sources */,
                                51ABAE441043AB4A008C5260 /* WebSocketHandshake.cpp in Sources */,
index 36dbb6f..7dd854e 100644 (file)
@@ -478,6 +478,7 @@ void WebSocket::didConnect()
     ASSERT(scriptExecutionContext());
     m_state = OPEN;
     m_subprotocol = m_channel->subprotocol();
+    m_extensions = m_channel->extensions();
     dispatchEvent(Event::create(eventNames().openEvent, false, false));
 }
 
index ec0706d..c40b098 100644 (file)
@@ -127,6 +127,8 @@ void WebSocketChannel::connect(const KURL& url, const String& protocol)
     ASSERT(!m_suspended);
     m_handshake = adoptPtr(new WebSocketHandshake(url, protocol, m_document, m_useHixie76Protocol));
     m_handshake->reset();
+    if (!m_useHixie76Protocol && m_deflateFramer.canDeflate())
+        m_handshake->addExtensionProcessor(m_deflateFramer.createExtensionProcessor());
     if (m_identifier)
         InspectorInstrumentation::didCreateWebSocket(m_document, m_identifier, url, m_document->url());
     ref();
@@ -228,6 +230,7 @@ void WebSocketChannel::fail(const String& reason)
         m_shouldDiscardReceivedData = true;
         if (m_buffer)
             skipBuffer(m_bufferSize); // Save memory.
+        m_deflateFramer.didFail();
         m_hasContinuousFrame = false;
         m_continuousFrameData.clear();
     }
@@ -618,6 +621,12 @@ bool WebSocketChannel::processFrame()
     ASSERT(m_buffer < frameEnd);
     ASSERT(frameEnd <= m_buffer + m_bufferSize);
 
+    InflateResultHolder inflateResult = m_deflateFramer.inflate(frame);
+    if (!inflateResult.succeeded()) {
+        fail(inflateResult.failureReason());
+        return false;
+    }
+
     // Validate the frame data.
     if (WebSocketFrame::isReservedOpCode(frame.opCode)) {
         fail("Unrecognized frame opcode: " + String::number(frame.opCode));
@@ -1011,6 +1020,13 @@ bool WebSocketChannel::sendFrame(WebSocketFrame::OpCode opCode, const char* data
 
     ASSERT(!(opCode & ~opCodeMask)); // Checks whether "opCode" fits in the range of opCodes.
     WebSocketFrame frame(opCode, true, false, true, data, dataLength);
+
+    DeflateResultHolder deflateResult = m_deflateFramer.deflate(frame);
+    if (!deflateResult.succeeded()) {
+        fail(deflateResult.failureReason());
+        return false;
+    }
+
     Vector<char> frameData;
     makeFrameData(frame, frameData);
 
index 4bb28c9..6d1febb 100644 (file)
@@ -37,6 +37,7 @@
 #include "SocketStreamHandleClient.h"
 #include "ThreadableWebSocketChannel.h"
 #include "Timer.h"
+#include "WebSocketDeflateFramer.h"
 #include "WebSocketFrame.h"
 #include "WebSocketHandshake.h"
 #include <wtf/Deque.h>
@@ -231,6 +232,8 @@ private:
     OwnPtr<FileReaderLoader> m_blobLoader;
     BlobLoaderStatus m_blobLoaderStatus;
 #endif
+
+    WebSocketDeflateFramer m_deflateFramer;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/websockets/WebSocketDeflateFramer.cpp b/Source/WebCore/websockets/WebSocketDeflateFramer.cpp
new file mode 100644 (file)
index 0000000..0c9d353
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketDeflateFramer.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class WebSocketExtensionDeflateFrame : public WebSocketExtensionProcessor {
+public:
+    static PassOwnPtr<WebSocketExtensionDeflateFrame> create(WebSocketDeflateFramer* framer)
+    {
+        return adoptPtr(new WebSocketExtensionDeflateFrame(framer));
+    }
+    virtual ~WebSocketExtensionDeflateFrame() { }
+
+    virtual String handshakeString() OVERRIDE;
+    virtual bool processResponse(const HashMap<String, String>&) OVERRIDE;
+    virtual String failureReason() OVERRIDE { return m_failureReason; }
+
+private:
+    WebSocketExtensionDeflateFrame(WebSocketDeflateFramer*);
+
+    WebSocketDeflateFramer* m_framer;
+    bool m_responseProcessed;
+    String m_failureReason;
+};
+
+// FXIME: Remove vendor prefix after the specification matured.
+WebSocketExtensionDeflateFrame::WebSocketExtensionDeflateFrame(WebSocketDeflateFramer* framer)
+    : WebSocketExtensionProcessor("x-webkit-deflate-frame")
+    , m_framer(framer)
+    , m_responseProcessed(false)
+{
+    ASSERT(m_framer);
+}
+
+String WebSocketExtensionDeflateFrame::handshakeString()
+{
+    return extensionToken(); // No parameter
+}
+
+bool WebSocketExtensionDeflateFrame::processResponse(const HashMap<String, String>& serverParameters)
+{
+#if USE(ZLIB)
+    if (m_responseProcessed) {
+        m_failureReason = "Received duplicate deflate-frame response";
+        return false;
+    }
+    m_responseProcessed = true;
+
+    int expectedNumParameters = 0;
+    int windowBits = 15;
+    HashMap<String, String>::const_iterator parameter = serverParameters.find("max_window_bits");
+    if (parameter != serverParameters.end()) {
+        windowBits = parameter->second.toInt();
+        if (windowBits < 8 || windowBits > 15) {
+            m_failureReason = "Received invalid max_window_bits parameter";
+            return false;
+        }
+        expectedNumParameters++;
+    }
+
+    WebSocketDeflater::ContextTakeOverMode mode = WebSocketDeflater::TakeOverContext;
+    parameter = serverParameters.find("no_context_takeover");
+    if (parameter != serverParameters.end()) {
+        if (!parameter->second.isNull()) {
+            m_failureReason = "Received invalid no_context_takeover parameter";
+            return false;
+        }
+        mode = WebSocketDeflater::DoNotTakeOverContext;
+        expectedNumParameters++;
+    }
+
+    if (expectedNumParameters != serverParameters.size()) {
+        m_failureReason = "Received unexpected deflate-frame parameter";
+        return false;
+    }
+
+    m_framer->enableDeflate(windowBits, mode);
+    return true;
+#else
+    ASSERT_NOT_REACHED();
+    return false;
+#endif
+}
+
+DeflateResultHolder::DeflateResultHolder(WebSocketDeflateFramer* framer)
+    : m_framer(framer)
+    , m_succeeded(true)
+{
+    ASSERT(m_framer);
+}
+
+DeflateResultHolder::~DeflateResultHolder()
+{
+    m_framer->resetDeflateContext();
+}
+
+void DeflateResultHolder::fail(const String& failureReason)
+{
+    m_succeeded = false;
+    m_failureReason = failureReason;
+}
+
+InflateResultHolder::InflateResultHolder(WebSocketDeflateFramer* framer)
+    : m_framer(framer)
+    , m_succeeded(true)
+{
+    ASSERT(m_framer);
+}
+
+InflateResultHolder::~InflateResultHolder()
+{
+    m_framer->resetInflateContext();
+}
+
+void InflateResultHolder::fail(const String& failureReason)
+{
+    m_succeeded = false;
+    m_failureReason = failureReason;
+}
+
+WebSocketDeflateFramer::WebSocketDeflateFramer()
+    : m_enabled(false)
+{
+}
+
+PassOwnPtr<WebSocketExtensionProcessor> WebSocketDeflateFramer::createExtensionProcessor()
+{
+    return WebSocketExtensionDeflateFrame::create(this);
+}
+
+bool WebSocketDeflateFramer::canDeflate() const
+{
+#if USE(ZLIB)
+    return true;
+#else
+    return false;
+#endif
+}
+
+#if USE(ZLIB)
+void WebSocketDeflateFramer::enableDeflate(int windowBits, WebSocketDeflater::ContextTakeOverMode mode)
+{
+    m_deflater = WebSocketDeflater::create(windowBits, mode);
+    m_inflater = WebSocketInflater::create();
+    if (!m_deflater || !m_inflater) {
+        m_deflater.clear();
+        m_inflater.clear();
+        return;
+    }
+    if (!m_deflater->initialize() || !m_inflater->initialize()) {
+        m_deflater.clear();
+        m_inflater.clear();
+        return;
+    }
+    m_enabled = true;
+}
+#endif
+
+DeflateResultHolder WebSocketDeflateFramer::deflate(WebSocketFrame& frame)
+{
+#if USE(ZLIB)
+    DeflateResultHolder result(this);
+    if (!enabled() || !WebSocketFrame::isNonControlOpCode(frame.opCode) || !frame.payloadLength)
+        return result;
+    if (!m_deflater->addBytes(frame.payload, frame.payloadLength) || !m_deflater->finish()) {
+        result.fail("Failed to compress frame");
+        return result;
+    }
+    frame.compress = true;
+    frame.payload = m_deflater->data();
+    frame.payloadLength = m_deflater->size();
+    return result;
+#else
+    return DeflateResultHolder(this);
+#endif
+}
+
+void WebSocketDeflateFramer::resetDeflateContext()
+{
+#if USE(ZLIB)
+    if (m_deflater)
+        m_deflater->reset();
+#endif
+}
+
+InflateResultHolder WebSocketDeflateFramer::inflate(WebSocketFrame& frame)
+{
+#if USE(ZLIB)
+    InflateResultHolder result(this);
+    if (!frame.compress)
+        return result;
+    if (!enabled() || !WebSocketFrame::isNonControlOpCode(frame.opCode)) {
+        result.fail("Received unexpected compressed frame");
+        return result;
+    }
+    if (!m_inflater->addBytes(frame.payload, frame.payloadLength) || !m_inflater->finish()) {
+        result.fail("Failed to decompress frame");
+        return result;
+    }
+    frame.compress = false;
+    frame.payload = m_inflater->data();
+    frame.payloadLength = m_inflater->size();
+    return result;
+#else
+    return InflateResultHolder(this);
+#endif
+}
+
+void WebSocketDeflateFramer::resetInflateContext()
+{
+#if USE(ZLIB)
+    if (m_inflater)
+        m_inflater->reset();
+#endif
+}
+
+void WebSocketDeflateFramer::didFail()
+{
+    resetDeflateContext();
+    resetInflateContext();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/WebSocketDeflateFramer.h b/Source/WebCore/websockets/WebSocketDeflateFramer.h
new file mode 100644 (file)
index 0000000..8e8ba83
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketDeflateFramer_h
+#define WebSocketDeflateFramer_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#if USE(ZLIB)
+#include "WebSocketDeflater.h"
+#endif
+#include "WebSocketExtensionProcessor.h"
+#include "WebSocketFrame.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class WebSocketDeflateFramer;
+
+class DeflateResultHolder {
+public:
+    explicit DeflateResultHolder(WebSocketDeflateFramer*);
+    ~DeflateResultHolder();
+
+    bool succeeded() const { return m_succeeded; }
+    String failureReason() const { return m_failureReason; }
+
+    void fail(const String& failureReason);
+
+private:
+    WebSocketDeflateFramer* m_framer;
+    bool m_succeeded;
+    String m_failureReason;
+};
+
+class InflateResultHolder {
+public:
+    explicit InflateResultHolder(WebSocketDeflateFramer*);
+    ~InflateResultHolder();
+
+    bool succeeded() const { return m_succeeded; }
+    String failureReason() const { return m_failureReason; }
+
+    void fail(const String& failureReason);
+
+private:
+    WebSocketDeflateFramer* m_framer;
+    bool m_succeeded;
+    String m_failureReason;
+};
+
+class WebSocketDeflateFramer {
+public:
+    WebSocketDeflateFramer();
+
+    PassOwnPtr<WebSocketExtensionProcessor> createExtensionProcessor();
+
+    bool canDeflate() const;
+    bool enabled() const { return m_enabled; }
+
+    DeflateResultHolder deflate(WebSocketFrame&);
+    void resetDeflateContext();
+    InflateResultHolder inflate(WebSocketFrame&);
+    void resetInflateContext();
+
+    void didFail();
+
+#if USE(ZLIB)
+    void enableDeflate(int windowBits, WebSocketDeflater::ContextTakeOverMode);
+#endif
+
+private:
+    bool m_enabled;
+#if USE(ZLIB)
+    OwnPtr<WebSocketDeflater> m_deflater;
+    OwnPtr<WebSocketInflater> m_inflater;
+#endif
+};
+
+}
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketDeflateFramer_h