summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/pywebsocket
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/tools/pywebsocket')
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/COPYING28
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/MANIFEST.in6
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/README17
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/abort_handshake_wsh.py43
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/abort_wsh.py43
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/arraybuffer_benchmark.html134
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/bench_wsh.py60
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.html203
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.js309
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/benchmark_helper_wsh.py85
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/close_wsh.py69
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/console.html317
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/cookie_wsh.py32
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/example/echo_client.py1128
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/echo_noext_wsh.py61
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/echo_wsh.py54
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/example/eventsource.cgi54
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.html74
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/handler_map.txt11
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/hsts_wsh.py40
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/internal_error_wsh.py42
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/origin_check_wsh.py44
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/pywebsocket.conf42
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/example/special_headers.cgi28
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/util.js177
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/util_main.js63
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/util_worker.js19
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.html222
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.js389
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/example/xhr_event_logger.html110
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/__init__.py224
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_base.py181
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hixie75.py229
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hybi.py887
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/common.py303
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/dispatch.py393
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/extensions.py885
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/fast_masking.i98
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/__init__.py110
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/_base.py182
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi.py420
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi00.py293
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/headerparserhandler.py254
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/http_header_util.py263
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/memorizingfile.py99
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/msgutil.py219
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/mux.py1889
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/standalone.py1193
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/stream.py57
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/util.py416
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/xhr_benchmark_handler.py109
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/setup.py74
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/__init__.py0
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/cert/cacert.pem17
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/cert/cert.pem61
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/cert/client_cert.p12bin2582 -> 0 bytes
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/cert/key.pem15
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/client_for_testing.py1100
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/endtoend_with_external_server.py67
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/mock.py221
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/mux_client_for_testing.py690
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/run_all.py89
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/set_sys_path.py45
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_dispatch.py288
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_endtoend.py753
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_extensions.py360
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_handshake.py188
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi.py534
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi00.py516
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_http_header_util.py90
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_memorizingfile.py104
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_mock.py145
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_msgutil.py1356
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/test_mux.py2089
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_stream.py77
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_stream_hixie75.py59
-rwxr-xr-xtesting/web-platform/tests/tools/pywebsocket/src/test/test_util.py200
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/README1
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/abort_by_user_wsh.py42
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/blank_wsh.py31
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/origin_check_wsh.py42
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/exception_in_transfer_wsh.py44
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/no_wsh_at_the_end.py45
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/non_callable_wsh.py39
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/plain_wsh.py40
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py45
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py45
-rw-r--r--testing/web-platform/tests/tools/pywebsocket/src/test/testdata/hello.pl32
88 files changed, 0 insertions, 22152 deletions
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/COPYING b/testing/web-platform/tests/tools/pywebsocket/src/COPYING
deleted file mode 100644
index 989d02e4c..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/COPYING
+++ /dev/null
@@ -1,28 +0,0 @@
-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.
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/MANIFEST.in b/testing/web-platform/tests/tools/pywebsocket/src/MANIFEST.in
deleted file mode 100644
index 19256882c..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/MANIFEST.in
+++ /dev/null
@@ -1,6 +0,0 @@
-include COPYING
-include MANIFEST.in
-include README
-recursive-include example *.py
-recursive-include mod_pywebsocket *.py
-recursive-include test *.py
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/README b/testing/web-platform/tests/tools/pywebsocket/src/README
deleted file mode 100644
index c8c758f5e..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/README
+++ /dev/null
@@ -1,17 +0,0 @@
-INSTALL
-
-To install this package to the system, run this:
-$ python setup.py build
-$ sudo python setup.py install
-
-To install this package as a normal user, run this instead:
-$ python setup.py build
-$ python setup.py install --user
-
-LAUNCH
-
-To use pywebsocket as Apache module, run this to read the document:
-$ pydoc mod_pywebsocket
-
-To use pywebsocket as standalone server, run this to read the document:
-$ pydoc mod_pywebsocket.standalone
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/abort_handshake_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/abort_handshake_wsh.py
deleted file mode 100644
index 008023a1f..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/abort_handshake_wsh.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# 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 handshake
-
-
-def web_socket_do_extra_handshake(request):
- raise handshake.AbortedByUserException(
- "Aborted in web_socket_do_extra_handshake")
-
-
-def web_socket_transfer_data(request):
- pass
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/abort_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/abort_wsh.py
deleted file mode 100644
index 2bbf005f6..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/abort_wsh.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# 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 handshake
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- raise handshake.AbortedByUserException(
- "Aborted in web_socket_transfer_data")
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/arraybuffer_benchmark.html b/testing/web-platform/tests/tools/pywebsocket/src/example/arraybuffer_benchmark.html
deleted file mode 100644
index 869cd7e1e..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/arraybuffer_benchmark.html
+++ /dev/null
@@ -1,134 +0,0 @@
-<!--
-Copyright 2013, 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.
--->
-
-<html>
-<head>
-<title>ArrayBuffer benchmark</title>
-<script src="util.js"></script>
-<script>
-var PRINT_SIZE = true;
-
-// Initial size of arrays.
-var START_SIZE = 10 * 1024;
-// Stops benchmark when the size of an array exceeds this threshold.
-var STOP_THRESHOLD = 100000 * 1024;
-// If the size of each array is small, write/read the array multiple times
-// until the sum of sizes reaches this threshold.
-var MIN_TOTAL = 100000 * 1024;
-var MULTIPLIERS = [5, 2];
-
-// Repeat benchmark for several times to measure performance of optimized
-// (such as JIT) run.
-var REPEAT_FOR_WARMUP = 3;
-
-function writeBenchmark(size, minTotal) {
- var totalSize = 0;
- while (totalSize < minTotal) {
- var arrayBuffer = new ArrayBuffer(size);
-
- // Write 'a's.
- fillArrayBuffer(arrayBuffer, 0x61);
-
- totalSize += size;
- }
- return totalSize;
-}
-
-function readBenchmark(size, minTotal) {
- var totalSize = 0;
- while (totalSize < minTotal) {
- var arrayBuffer = new ArrayBuffer(size);
-
- if (!verifyArrayBuffer(arrayBuffer, 0x00)) {
- queueLog('Verification failed');
- return -1;
- }
-
- totalSize += size;
- }
- return totalSize;
-}
-
-function runBenchmark(benchmarkFunction,
- size,
- stopThreshold,
- minTotal,
- multipliers,
- multiplierIndex) {
- while (size <= stopThreshold) {
- var maxSpeed = 0;
-
- for (var i = 0; i < REPEAT_FOR_WARMUP; ++i) {
- var startTimeInMs = getTimeStamp();
-
- var totalSize = benchmarkFunction(size, minTotal);
-
- maxSpeed = Math.max(maxSpeed,
- calculateSpeedInKB(totalSize, startTimeInMs));
- }
- queueLog(formatResultInKiB(size, maxSpeed, PRINT_SIZE));
-
- size *= multipliers[multiplierIndex];
- multiplierIndex = (multiplierIndex + 1) % multipliers.length;
- }
-}
-
-function runBenchmarks() {
- queueLog('Message size in KiB, Speed in kB/s');
-
- queueLog('Write benchmark');
- runBenchmark(
- writeBenchmark, START_SIZE, STOP_THRESHOLD, MIN_TOTAL, MULTIPLIERS, 0);
- queueLog('Finished');
-
- queueLog('Read benchmark');
- runBenchmark(
- readBenchmark, START_SIZE, STOP_THRESHOLD, MIN_TOTAL, MULTIPLIERS, 0);
- addToLog('Finished');
-}
-
-function init() {
- logBox = document.getElementById('log');
-
- queueLog(window.navigator.userAgent.toLowerCase());
-
- addToLog('Started...');
-
- setTimeout(runBenchmarks, 0);
-}
-
-</script>
-</head>
-<body onload="init()">
-<textarea
- id="log" rows="50" style="width: 100%" readonly></textarea>
-</body>
-</html>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/bench_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/bench_wsh.py
deleted file mode 100644
index 5067ca7d8..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/bench_wsh.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""A simple load tester for WebSocket clients.
-
-A client program sends a message formatted as "<time> <count> <message>" to
-this handler. This handler starts sending total <count> WebSocket messages
-containing <message> every <time> seconds. <time> can be a floating point
-value. <count> must be an integer value.
-"""
-
-
-import time
-
-
-def web_socket_do_extra_handshake(request):
- pass # Always accept.
-
-
-def web_socket_transfer_data(request):
- line = request.ws_stream.receive_message()
- parts = line.split(' ')
- if len(parts) != 3:
- raise ValueError('Bad parameter format')
- wait = float(parts[0])
- count = int(parts[1])
- message = parts[2]
- for i in xrange(count):
- request.ws_stream.send_message(message)
- time.sleep(wait)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.html b/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.html
deleted file mode 100644
index 3a218173a..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.html
+++ /dev/null
@@ -1,203 +0,0 @@
-<!--
-Copyright 2013, 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.
--->
-
-<html>
-<head>
-<title>WebSocket benchmark</title>
-<script src="util_main.js"></script>
-<script src="util.js"></script>
-<script src="benchmark.js"></script>
-<script>
-var addressBox = null;
-
-function getConfig() {
- return {
- prefixUrl: addressBox.value,
- printSize: getBoolFromCheckBox('printsize'),
- numSockets: getIntFromInput('numsockets'),
- // Initial size of messages.
- numIterations: getIntFromInput('numiterations'),
- numWarmUpIterations: getIntFromInput('numwarmupiterations'),
- startSize: getIntFromInput('startsize'),
- // Stops benchmark when the size of message exceeds this threshold.
- stopThreshold: getIntFromInput('stopthreshold'),
- // If the size of each message is small, send/receive multiple messages
- // until the sum of sizes reaches this threshold.
- minTotal: getIntFromInput('mintotal'),
- multipliers: getIntArrayFromInput('multipliers'),
- verifyData: getBoolFromCheckBox('verifydata')
- };
-}
-
-var worker = new Worker('benchmark.js');
-worker.onmessage = onMessage;
-
-function onSendBenchmark() {
- var config = getConfig();
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'sendBenchmark', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- sendBenchmark(config);
- }
-}
-
-function onReceiveBenchmark() {
- var config = getConfig();
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'receiveBenchmark', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- receiveBenchmark(config);
- }
-}
-
-function onBatchBenchmark() {
- var config = getConfig();
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'batchBenchmark', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- batchBenchmark(config);
- }
-}
-
-function onStop() {
- var config = getConfig();
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'stop', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- stop(config);
- }
-}
-function init() {
- addressBox = document.getElementById('address');
- logBox = document.getElementById('log');
-
- summaryBox = document.getElementById('summary');
-
- var scheme = window.location.protocol == 'https:' ? 'wss://' : 'ws://';
- var defaultAddress = scheme + window.location.host + '/benchmark_helper';
-
- addressBox.value = defaultAddress;
-
- addToLog(window.navigator.userAgent.toLowerCase());
- addToSummary(window.navigator.userAgent.toLowerCase());
-
- if (!('WebSocket' in window)) {
- addToLog('WebSocket is not available');
- }
-}
-</script>
-</head>
-<body onload="init()">
-
-<div id="benchmark_div">
- url <input type="text" id="address" size="40">
- <input type="button" value="send" onclick="onSendBenchmark()">
- <input type="button" value="receive" onclick="onReceiveBenchmark()">
- <input type="button" value="batch" onclick="onBatchBenchmark()">
- <input type="button" value="stop" onclick="onStop()">
-
- <br/>
-
- <input type="checkbox" id="printsize" checked>
- <label for="printsize">Print size and time per message</label>
- <input type="checkbox" id="verifydata" checked>
- <label for="verifydata">Verify data</label>
- <input type="checkbox" id="worker">
- <label for="worker">Run on worker</label>
-
- <br/>
-
- Parameters:
-
- <br/>
-
- <table>
- <tr>
- <td>Num sockets</td>
- <td><input type="text" id="numsockets" value="1"></td>
- </tr>
- <tr>
- <td>Number of iterations</td>
- <td><input type="text" id="numiterations" value="1"></td>
- </tr>
- <tr>
- <td>Number of warm-up iterations</td>
- <td><input type="text" id="numwarmupiterations" value="0"></td>
- </tr>
- <tr>
- <td>Start size</td>
- <td><input type="text" id="startsize" value="10240"></td>
- </tr>
- <tr>
- <td>Stop threshold</td>
- <td><input type="text" id="stopthreshold" value="102400000"></td>
- </tr>
- <tr>
- <td>Minimum total</td>
- <td><input type="text" id="mintotal" value="102400000"></td>
- </tr>
- <tr>
- <td>Multipliers</td>
- <td><input type="text" id="multipliers" value="5, 2"></td>
- </tr>
- </table>
-</div>
-
-<div id="log_div">
- <textarea
- id="log" rows="20" style="width: 100%" readonly></textarea>
-</div>
-<div id="summary_div">
- Summary
- <textarea
- id="summary" rows="20" style="width: 100%" readonly></textarea>
-</div>
-
-Note: Effect of RTT is not eliminated.
-
-</body>
-</html>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.js b/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.js
deleted file mode 100644
index d347ae9e1..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark.js
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the COPYING file or at
-// https://developers.google.com/open-source/licenses/bsd
-
-if (typeof importScripts !== "undefined") {
- // Running on a worker
- importScripts('util.js', 'util_worker.js');
-}
-
-// Namespace for holding globals.
-var benchmark = {startTimeInMs: 0};
-
-var sockets = [];
-var numEstablishedSockets = 0;
-
-var timerID = null;
-
-function destroySocket(socket) {
- socket.onopen = null;
- socket.onmessage = null;
- socket.onerror = null;
- socket.onclose = null;
- socket.close();
-}
-
-function destroyAllSockets() {
- for (var i = 0; i < sockets.length; ++i) {
- destroySocket(sockets[i]);
- }
- sockets = [];
-}
-
-function sendBenchmarkStep(size, config) {
- timerID = null;
-
- var totalSize = 0;
- var totalReplied = 0;
-
- var onMessageHandler = function(event) {
- if (!verifyAcknowledgement(config, event.data, size)) {
- destroyAllSockets();
- return;
- }
-
- totalReplied += size;
-
- if (totalReplied < totalSize) {
- return;
- }
-
- calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize);
-
- runNextTask(config);
- };
-
- for (var i = 0; i < sockets.length; ++i) {
- var socket = sockets[i];
- socket.onmessage = onMessageHandler;
- }
-
- var dataArray = [];
-
- while (totalSize < config.minTotal) {
- var buffer = new ArrayBuffer(size);
-
- fillArrayBuffer(buffer, 0x61);
-
- dataArray.push(buffer);
- totalSize += size;
- }
-
- benchmark.startTimeInMs = getTimeStamp();
-
- totalSize = 0;
-
- var socketIndex = 0;
- var dataIndex = 0;
- while (totalSize < config.minTotal) {
- var command = ['send'];
- command.push(config.verifyData ? '1' : '0');
- sockets[socketIndex].send(command.join(' '));
- sockets[socketIndex].send(dataArray[dataIndex]);
- socketIndex = (socketIndex + 1) % sockets.length;
-
- totalSize += size;
- ++dataIndex;
- }
-}
-
-function receiveBenchmarkStep(size, config) {
- timerID = null;
-
- var totalSize = 0;
- var totalReplied = 0;
-
- var onMessageHandler = function(event) {
- var bytesReceived = event.data.byteLength;
- if (bytesReceived != size) {
- config.addToLog('Expected ' + size + 'B but received ' +
- bytesReceived + 'B');
- destroyAllSockets();
- return;
- }
-
- if (config.verifyData && !verifyArrayBuffer(event.data, 0x61)) {
- config.addToLog('Response verification failed');
- destroyAllSockets();
- return;
- }
-
- totalReplied += bytesReceived;
-
- if (totalReplied < totalSize) {
- return;
- }
-
- calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize);
-
- runNextTask(config);
- };
-
- for (var i = 0; i < sockets.length; ++i) {
- var socket = sockets[i];
- socket.binaryType = 'arraybuffer';
- socket.onmessage = onMessageHandler;
- }
-
- benchmark.startTimeInMs = getTimeStamp();
-
- var socketIndex = 0;
- while (totalSize < config.minTotal) {
- sockets[socketIndex].send('receive ' + size);
- socketIndex = (socketIndex + 1) % sockets.length;
-
- totalSize += size;
- }
-}
-
-function createSocket(config) {
- // TODO(tyoshino): Add TCP warm up.
- var url = config.prefixUrl;
-
- config.addToLog('Connect ' + url);
-
- var socket = new WebSocket(url);
- socket.onmessage = function(event) {
- config.addToLog('Unexpected message received. Aborting.');
- };
- socket.onerror = function() {
- config.addToLog('Error');
- };
- socket.onclose = function(event) {
- config.addToLog('Closed');
- };
- return socket;
-}
-
-var tasks = [];
-
-function startBenchmark(config) {
- clearTimeout(timerID);
- destroyAllSockets();
-
- numEstablishedSockets = 0;
-
- for (var i = 0; i < config.numSockets; ++i) {
- var socket = createSocket(config);
- socket.onopen = function() {
- config.addToLog('Opened');
-
- ++numEstablishedSockets;
-
- if (numEstablishedSockets == sockets.length) {
- runNextTask(config);
- }
- };
- sockets.push(socket);
- }
-}
-
-function runNextTask(config) {
- var task = tasks.shift();
- if (task == undefined) {
- config.addToLog('Finished');
- destroyAllSockets();
- return;
- }
- timerID = setTimeout(task, 0);
-}
-
-function buildLegendString(config) {
- var legend = ''
- if (config.printSize)
- legend = 'Message size in KiB, Time/message in ms, ';
- legend += 'Speed in kB/s';
- return legend;
-}
-
-function getConfigString(config) {
- return '(WebSocket' +
- ', ' + (typeof importScripts !== "undefined" ? 'Worker' : 'Main') +
- ', numSockets=' + config.numSockets +
- ', numIterations=' + config.numIterations +
- ', verifyData=' + config.verifyData +
- ', minTotal=' + config.minTotal +
- ', numWarmUpIterations=' + config.numWarmUpIterations +
- ')';
-}
-
-function addTasks(config, stepFunc) {
- for (var i = 0;
- i < config.numWarmUpIterations + config.numIterations; ++i) {
- // Ignore the first |config.numWarmUpIterations| iterations.
- if (i == config.numWarmUpIterations)
- addResultClearingTask(config);
-
- var multiplierIndex = 0;
- for (var size = config.startSize;
- size <= config.stopThreshold;
- ++multiplierIndex) {
- var task = stepFunc.bind(
- null,
- size,
- config);
- tasks.push(task);
- size *= config.multipliers[
- multiplierIndex % config.multipliers.length];
- }
- }
-}
-
-function addResultReportingTask(config, title) {
- tasks.push(function(){
- timerID = null;
- config.addToSummary(title);
- reportAverageData(config);
- clearAverageData();
- runNextTask(config);
- });
-}
-
-function addResultClearingTask(config) {
- tasks.push(function(){
- timerID = null;
- clearAverageData();
- runNextTask(config);
- });
-}
-
-function sendBenchmark(config) {
- config.addToLog('Send benchmark');
- config.addToLog(buildLegendString(config));
-
- tasks = [];
- clearAverageData();
- addTasks(config, sendBenchmarkStep);
- addResultReportingTask(config, 'Send Benchmark ' + getConfigString(config));
- startBenchmark(config);
-}
-
-function receiveBenchmark(config) {
- config.addToLog('Receive benchmark');
- config.addToLog(buildLegendString(config));
-
- tasks = [];
- clearAverageData();
- addTasks(config, receiveBenchmarkStep);
- addResultReportingTask(config,
- 'Receive Benchmark ' + getConfigString(config));
- startBenchmark(config);
-}
-
-function batchBenchmark(config) {
- config.addToLog('Batch benchmark');
- config.addToLog(buildLegendString(config));
-
- tasks = [];
- clearAverageData();
- addTasks(config, sendBenchmarkStep);
- addResultReportingTask(config, 'Send Benchmark ' + getConfigString(config));
- addTasks(config, receiveBenchmarkStep);
- addResultReportingTask(config, 'Receive Benchmark ' +
- getConfigString(config));
- startBenchmark(config);
-}
-
-function stop(config) {
- clearTimeout(timerID);
- timerID = null;
- config.addToLog('Stopped');
- destroyAllSockets();
-}
-
-onmessage = function (message) {
- var config = message.data.config;
- config.addToLog = workerAddToLog;
- config.addToSummary = workerAddToSummary;
- config.measureValue = workerMeasureValue;
- if (message.data.type === 'sendBenchmark')
- sendBenchmark(config);
- else if (message.data.type === 'receiveBenchmark')
- receiveBenchmark(config);
- else if (message.data.type === 'batchBenchmark')
- batchBenchmark(config);
- else if (message.data.type === 'stop')
- stop(config);
-};
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark_helper_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark_helper_wsh.py
deleted file mode 100644
index 44ad0bfee..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/benchmark_helper_wsh.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright 2013, 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.
-
-
-"""Handler for benchmark.html."""
-
-
-def web_socket_do_extra_handshake(request):
- # Turn off compression.
- request.ws_extension_processors = []
-
-
-def web_socket_transfer_data(request):
- data = ''
-
- while True:
- command = request.ws_stream.receive_message()
- if command is None:
- return
-
- if not isinstance(command, unicode):
- raise ValueError('Invalid command data:' + command)
- commands = command.split(' ')
- if len(commands) == 0:
- raise ValueError('Invalid command data: ' + command)
-
- if commands[0] == 'receive':
- if len(commands) != 2:
- raise ValueError(
- 'Illegal number of arguments for send command' +
- command)
- size = int(commands[1])
-
- # Reuse data if possible.
- if len(data) != size:
- data = 'a' * size
- request.ws_stream.send_message(data, binary=True)
- elif commands[0] == 'send':
- if len(commands) != 2:
- raise ValueError(
- 'Illegal number of arguments for receive command' +
- command)
- verify_data = commands[1] == '1'
-
- data = request.ws_stream.receive_message()
- if data is None:
- raise ValueError('Payload not received')
- size = len(data)
-
- if verify_data:
- if data != 'a' * size:
- raise ValueError('Payload verification failed')
-
- request.ws_stream.send_message(str(size))
- else:
- raise ValueError('Invalid command: ' + commands[0])
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/close_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/close_wsh.py
deleted file mode 100644
index 26b083840..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/close_wsh.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# 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 struct
-
-from mod_pywebsocket import common
-from mod_pywebsocket import stream
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- while True:
- line = request.ws_stream.receive_message()
- if line is None:
- return
- code, reason = line.split(' ', 1)
- if code is None or reason is None:
- return
- request.ws_stream.close_connection(int(code), reason)
- # close_connection() initiates closing handshake. It validates code
- # and reason. If you want to send a broken close frame for a test,
- # following code will be useful.
- # > data = struct.pack('!H', int(code)) + reason.encode('UTF-8')
- # > request.connection.write(stream.create_close_frame(data))
- # > # Suppress to re-respond client responding close frame.
- # > raise Exception("customized server initiated closing handshake")
-
-
-def web_socket_passive_closing_handshake(request):
- # Simply echo a close status code
- code, reason = request.ws_close_code, request.ws_close_reason
-
- # pywebsocket sets pseudo code for receiving an empty body close frame.
- if code == common.STATUS_NO_STATUS_RECEIVED:
- code = None
- reason = ''
- return code, reason
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/console.html b/testing/web-platform/tests/tools/pywebsocket/src/example/console.html
deleted file mode 100644
index ccd6d8f80..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/console.html
+++ /dev/null
@@ -1,317 +0,0 @@
-<!--
-Copyright 2011, 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.
--->
-
-<!--
-A simple console for testing WebSocket server.
-
-Type an address into the top text input and click connect to establish
-WebSocket. Then, type some message into the bottom text input and click send
-to send the message. Received/sent messages and connection state will be shown
-on the middle textarea.
--->
-
-<html>
-<head>
-<title>WebSocket console</title>
-<script>
-var socket = null;
-
-var showTimeStamp = false;
-
-var addressBox = null;
-var protocolsBox = null;
-var logBox = null;
-var messageBox = null;
-var fileBox = null;
-var codeBox = null;
-var reasonBox = null;
-
-function getTimeStamp() {
- return new Date().getTime();
-}
-
-function addToLog(log) {
- if (showTimeStamp) {
- logBox.value += '[' + getTimeStamp() + '] ';
- }
- logBox.value += log + '\n'
- // Large enough to keep showing the latest message.
- logBox.scrollTop = 1000000;
-}
-
-function setbinarytype(binaryType) {
- if (!socket) {
- addToLog('Not connected');
- return;
- }
-
- socket.binaryType = binaryType;
- addToLog('Set binaryType to ' + binaryType);
-}
-
-function send() {
- if (!socket) {
- addToLog('Not connected');
- return;
- }
-
- socket.send(messageBox.value);
- addToLog('> ' + messageBox.value);
- messageBox.value = '';
-}
-
-function sendfile() {
- if (!socket) {
- addToLog('Not connected');
- return;
- }
-
- var files = fileBox.files;
-
- if (files.length == 0) {
- addToLog('File not selected');
- return;
- }
-
- socket.send(files[0]);
- addToLog('> Send ' + files[0].name);
-}
-
-function parseProtocols(protocolsText) {
- var protocols = protocolsText.split(',');
- for (var i = 0; i < protocols.length; ++i) {
- protocols[i] = protocols[i].trim();
- }
-
- if (protocols.length == 0) {
- // Don't pass.
- protocols = null;
- } else if (protocols.length == 1) {
- if (protocols[0].length == 0) {
- // Don't pass.
- protocols = null;
- } else {
- // Pass as a string.
- protocols = protocols[0];
- }
- }
-
- return protocols;
-}
-
-function connect() {
- var url = addressBox.value;
- var protocols = parseProtocols(protocolsBox.value);
-
- if ('WebSocket' in window) {
- if (protocols) {
- socket = new WebSocket(url, protocols);
- } else {
- socket = new WebSocket(url);
- }
- } else {
- return;
- }
-
- socket.onopen = function () {
- var extraInfo = [];
- if (('protocol' in socket) && socket.protocol) {
- extraInfo.push('protocol = ' + socket.protocol);
- }
- if (('extensions' in socket) && socket.extensions) {
- extraInfo.push('extensions = ' + socket.extensions);
- }
-
- var logMessage = 'Opened';
- if (extraInfo.length > 0) {
- logMessage += ' (' + extraInfo.join(', ') + ')';
- }
- addToLog(logMessage);
- };
- socket.onmessage = function (event) {
- if (('ArrayBuffer' in window) && (event.data instanceof ArrayBuffer)) {
- addToLog('< Received an ArrayBuffer of ' + event.data.byteLength +
- ' bytes')
- } else if (('Blob' in window) && (event.data instanceof Blob)) {
- addToLog('< Received a Blob of ' + event.data.size + ' bytes')
- } else {
- addToLog('< ' + event.data);
- }
- };
- socket.onerror = function () {
- addToLog('Error');
- };
- socket.onclose = function (event) {
- var logMessage = 'Closed (';
- if ((arguments.length == 1) && ('CloseEvent' in window) &&
- (event instanceof CloseEvent)) {
- logMessage += 'wasClean = ' + event.wasClean;
- // code and reason are present only for
- // draft-ietf-hybi-thewebsocketprotocol-06 and later
- if ('code' in event) {
- logMessage += ', code = ' + event.code;
- }
- if ('reason' in event) {
- logMessage += ', reason = ' + event.reason;
- }
- } else {
- logMessage += 'CloseEvent is not available';
- }
- addToLog(logMessage + ')');
- };
-
- if (protocols) {
- addToLog('Connect ' + url + ' (protocols = ' + protocols + ')');
- } else {
- addToLog('Connect ' + url);
- }
-}
-
-function closeSocket() {
- if (!socket) {
- addToLog('Not connected');
- return;
- }
-
- if (codeBox.value || reasonBox.value) {
- socket.close(codeBox.value, reasonBox.value);
- } else {
- socket.close();
- }
-}
-
-function printState() {
- if (!socket) {
- addToLog('Not connected');
- return;
- }
-
- addToLog(
- 'url = ' + socket.url +
- ', readyState = ' + socket.readyState +
- ', bufferedAmount = ' + socket.bufferedAmount);
-}
-
-function init() {
- var scheme = window.location.protocol == 'https:' ? 'wss://' : 'ws://';
- var defaultAddress = scheme + window.location.host + '/echo';
-
- addressBox = document.getElementById('address');
- protocolsBox = document.getElementById('protocols');
- logBox = document.getElementById('log');
- messageBox = document.getElementById('message');
- fileBox = document.getElementById('file');
- codeBox = document.getElementById('code');
- reasonBox = document.getElementById('reason');
-
- addressBox.value = defaultAddress;
-
- if (!('WebSocket' in window)) {
- addToLog('WebSocket is not available');
- }
-}
-</script>
-<style type="text/css">
-form {
- margin: 0px;
-}
-
-#connect_div, #log_div, #send_div, #sendfile_div, #close_div, #printstate_div {
- padding: 5px;
- margin: 5px;
- border-width: 0px 0px 0px 10px;
- border-style: solid;
- border-color: silver;
-}
-</style>
-</head>
-<body onload="init()">
-
-<div>
-
-<div id="connect_div">
- <form action="#" onsubmit="connect(); return false;">
- url <input type="text" id="address" size="40">
- <input type="submit" value="connect">
- <br/>
- protocols <input type="text" id="protocols" size="20">
- </form>
-</div>
-
-<div id="log_div">
- <textarea id="log" rows="10" cols="40" readonly></textarea>
- <br/>
- <input type="checkbox"
- name="showtimestamp"
- value="showtimestamp"
- onclick="showTimeStamp = this.checked">Show time stamp
-</div>
-
-<div id="send_div">
- <form action="#" onsubmit="send(); return false;">
- data <input type="text" id="message" size="40">
- <input type="submit" value="send">
- </form>
-</div>
-
-<div id="sendfile_div">
- <form action="#" onsubmit="sendfile(); return false;">
- <input type="file" id="file" size="40">
- <input type="submit" value="send file">
- </form>
-
- Set binaryType
- <input type="radio"
- name="binarytype"
- value="blob"
- onclick="setbinarytype('blob')" checked>blob
- <input type="radio"
- name="binarytype"
- value="arraybuffer"
- onclick="setbinarytype('arraybuffer')">arraybuffer
-</div>
-
-<div id="close_div">
- <form action="#" onsubmit="closeSocket(); return false;">
- code <input type="text" id="code" size="10">
- reason <input type="text" id="reason" size="20">
- <input type="submit" value="close">
- </form>
-</div>
-
-<div id="printstate_div">
- <input type="button" value="print state" onclick="printState();">
-</div>
-
-</div>
-
-</body>
-</html>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/cookie_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/cookie_wsh.py
deleted file mode 100644
index 8b327152e..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/cookie_wsh.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 Google Inc. All rights reserved.
-#
-# Use of this source code is governed by a BSD-style
-# license that can be found in the COPYING file or at
-# https://developers.google.com/open-source/licenses/bsd
-
-
-import urlparse
-
-
-def _add_set_cookie(request, value):
- request.extra_headers.append(('Set-Cookie', value))
-
-
-def web_socket_do_extra_handshake(request):
- components = urlparse.urlparse(request.uri)
- command = components[4]
-
- ONE_DAY_LIFE = 'Max-Age=86400'
-
- if command == 'set':
- _add_set_cookie(request, '; '.join(['foo=bar', ONE_DAY_LIFE]))
- elif command == 'set_httponly':
- _add_set_cookie(request,
- '; '.join(['httpOnlyFoo=bar', ONE_DAY_LIFE, 'httpOnly']))
- elif command == 'clear':
- _add_set_cookie(request, 'foo=0; Max-Age=0')
- _add_set_cookie(request, 'httpOnlyFoo=0; Max-Age=0')
-
-
-def web_socket_transfer_data(request):
- pass
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/echo_client.py b/testing/web-platform/tests/tools/pywebsocket/src/example/echo_client.py
deleted file mode 100755
index 943ce64e8..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/echo_client.py
+++ /dev/null
@@ -1,1128 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Simple WebSocket client named echo_client just because of historical reason.
-
-mod_pywebsocket directory must be in PYTHONPATH.
-
-Example Usage:
-
-# server setup
- % cd $pywebsocket
- % PYTHONPATH=$cwd/src python ./mod_pywebsocket/standalone.py -p 8880 \
- -d $cwd/src/example
-
-# run client
- % PYTHONPATH=$cwd/src python ./src/example/echo_client.py -p 8880 \
- -s localhost \
- -o http://localhost -r /echo -m test
-
-or
-
-# run echo client to test IETF HyBi 00 protocol
- run with --protocol-version=hybi00
-"""
-
-
-import base64
-import codecs
-import logging
-from optparse import OptionParser
-import os
-import random
-import re
-import socket
-import struct
-import sys
-
-from mod_pywebsocket import common
-from mod_pywebsocket.extensions import DeflateFrameExtensionProcessor
-from mod_pywebsocket.extensions import PerMessageDeflateExtensionProcessor
-from mod_pywebsocket.extensions import _PerMessageDeflateFramer
-from mod_pywebsocket.extensions import _parse_window_bits
-from mod_pywebsocket.stream import Stream
-from mod_pywebsocket.stream import StreamHixie75
-from mod_pywebsocket.stream import StreamOptions
-from mod_pywebsocket import util
-
-
-_TIMEOUT_SEC = 10
-_UNDEFINED_PORT = -1
-
-_UPGRADE_HEADER = 'Upgrade: websocket\r\n'
-_UPGRADE_HEADER_HIXIE75 = 'Upgrade: WebSocket\r\n'
-_CONNECTION_HEADER = 'Connection: Upgrade\r\n'
-
-# Special message that tells the echo server to start closing handshake
-_GOODBYE_MESSAGE = 'Goodbye'
-
-_PROTOCOL_VERSION_HYBI13 = 'hybi13'
-_PROTOCOL_VERSION_HYBI08 = 'hybi08'
-_PROTOCOL_VERSION_HYBI00 = 'hybi00'
-_PROTOCOL_VERSION_HIXIE75 = 'hixie75'
-
-# Constants for the --tls_module flag.
-_TLS_BY_STANDARD_MODULE = 'ssl'
-_TLS_BY_PYOPENSSL = 'pyopenssl'
-
-# Values used by the --tls-version flag.
-_TLS_VERSION_SSL23 = 'ssl23'
-_TLS_VERSION_SSL3 = 'ssl3'
-_TLS_VERSION_TLS1 = 'tls1'
-
-
-class ClientHandshakeError(Exception):
- pass
-
-
-def _build_method_line(resource):
- return 'GET %s HTTP/1.1\r\n' % resource
-
-
-def _origin_header(header, origin):
- # 4.1 13. concatenation of the string "Origin:", a U+0020 SPACE character,
- # and the /origin/ value, converted to ASCII lowercase, to /fields/.
- return '%s: %s\r\n' % (header, origin.lower())
-
-
-def _format_host_header(host, port, secure):
- # 4.1 9. Let /hostport/ be an empty string.
- # 4.1 10. Append the /host/ value, converted to ASCII lowercase, to
- # /hostport/
- hostport = host.lower()
- # 4.1 11. If /secure/ is false, and /port/ is not 80, or if /secure/
- # is true, and /port/ is not 443, then append a U+003A COLON character
- # (:) followed by the value of /port/, expressed as a base-ten integer,
- # to /hostport/
- if ((not secure and port != common.DEFAULT_WEB_SOCKET_PORT) or
- (secure and port != common.DEFAULT_WEB_SOCKET_SECURE_PORT)):
- hostport += ':' + str(port)
- # 4.1 12. concatenation of the string "Host:", a U+0020 SPACE
- # character, and /hostport/, to /fields/.
- return '%s: %s\r\n' % (common.HOST_HEADER, hostport)
-
-
-def _receive_bytes(socket, length):
- bytes = []
- remaining = length
- while remaining > 0:
- received_bytes = socket.recv(remaining)
- if not received_bytes:
- raise IOError(
- 'Connection closed before receiving requested length '
- '(requested %d bytes but received only %d bytes)' %
- (length, length - remaining))
- bytes.append(received_bytes)
- remaining -= len(received_bytes)
- return ''.join(bytes)
-
-
-def _get_mandatory_header(fields, name):
- """Gets the value of the header specified by name from fields.
-
- This function expects that there's only one header with the specified name
- in fields. Otherwise, raises an ClientHandshakeError.
- """
-
- values = fields.get(name.lower())
- if values is None or len(values) == 0:
- raise ClientHandshakeError(
- '%s header not found: %r' % (name, values))
- if len(values) > 1:
- raise ClientHandshakeError(
- 'Multiple %s headers found: %r' % (name, values))
- return values[0]
-
-
-def _validate_mandatory_header(fields, name,
- expected_value, case_sensitive=False):
- """Gets and validates the value of the header specified by name from
- fields.
-
- If expected_value is specified, compares expected value and actual value
- and raises an ClientHandshakeError on failure. You can specify case
- sensitiveness in this comparison by case_sensitive parameter. This function
- expects that there's only one header with the specified name in fields.
- Otherwise, raises an ClientHandshakeError.
- """
-
- value = _get_mandatory_header(fields, name)
-
- if ((case_sensitive and value != expected_value) or
- (not case_sensitive and value.lower() != expected_value.lower())):
- raise ClientHandshakeError(
- 'Illegal value for header %s: %r (expected) vs %r (actual)' %
- (name, expected_value, value))
-
-
-class _TLSSocket(object):
- """Wrapper for a TLS connection."""
-
- def __init__(self,
- raw_socket, tls_module, tls_version, disable_tls_compression):
- self._logger = util.get_class_logger(self)
-
- if tls_module == _TLS_BY_STANDARD_MODULE:
- if tls_version == _TLS_VERSION_SSL23:
- version = ssl.PROTOCOL_SSLv23
- elif tls_version == _TLS_VERSION_SSL3:
- version = ssl.PROTOCOL_SSLv3
- elif tls_version == _TLS_VERSION_TLS1:
- version = ssl.PROTOCOL_TLSv1
- else:
- raise ValueError(
- 'Invalid --tls-version flag: %r' % tls_version)
-
- if disable_tls_compression:
- raise ValueError(
- '--disable-tls-compression is not available for ssl '
- 'module')
-
- self._tls_socket = ssl.wrap_socket(raw_socket, ssl_version=version)
-
- # Print cipher in use. Handshake is done on wrap_socket call.
- self._logger.info("Cipher: %s", self._tls_socket.cipher())
- elif tls_module == _TLS_BY_PYOPENSSL:
- if tls_version == _TLS_VERSION_SSL23:
- version = OpenSSL.SSL.SSLv23_METHOD
- elif tls_version == _TLS_VERSION_SSL3:
- version = OpenSSL.SSL.SSLv3_METHOD
- elif tls_version == _TLS_VERSION_TLS1:
- version = OpenSSL.SSL.TLSv1_METHOD
- else:
- raise ValueError(
- 'Invalid --tls-version flag: %r' % tls_version)
-
- context = OpenSSL.SSL.Context(version)
-
- if disable_tls_compression:
- # OP_NO_COMPRESSION is not defined in OpenSSL module.
- context.set_options(0x00020000)
-
- self._tls_socket = OpenSSL.SSL.Connection(context, raw_socket)
- # Client mode.
- self._tls_socket.set_connect_state()
- self._tls_socket.setblocking(True)
-
- # Do handshake now (not necessary).
- self._tls_socket.do_handshake()
- else:
- raise ValueError('No TLS support module is available')
-
- def send(self, data):
- return self._tls_socket.write(data)
-
- def sendall(self, data):
- return self._tls_socket.sendall(data)
-
- def recv(self, size=-1):
- return self._tls_socket.read(size)
-
- def close(self):
- return self._tls_socket.close()
-
- def getpeername(self):
- return self._tls_socket.getpeername()
-
-
-class ClientHandshakeBase(object):
- """A base class for WebSocket opening handshake processors for each
- protocol version.
- """
-
- def __init__(self):
- self._logger = util.get_class_logger(self)
-
- def _read_fields(self):
- # 4.1 32. let /fields/ be a list of name-value pairs, initially empty.
- fields = {}
- while True: # "Field"
- # 4.1 33. let /name/ and /value/ be empty byte arrays
- name = ''
- value = ''
- # 4.1 34. read /name/
- name = self._read_name()
- if name is None:
- break
- # 4.1 35. read spaces
- # TODO(tyoshino): Skip only one space as described in the spec.
- ch = self._skip_spaces()
- # 4.1 36. read /value/
- value = self._read_value(ch)
- # 4.1 37. read a byte from the server
- ch = _receive_bytes(self._socket, 1)
- if ch != '\n': # 0x0A
- raise ClientHandshakeError(
- 'Expected LF but found %r while reading value %r for '
- 'header %r' % (ch, value, name))
- self._logger.debug('Received %r header', name)
- # 4.1 38. append an entry to the /fields/ list that has the name
- # given by the string obtained by interpreting the /name/ byte
- # array as a UTF-8 stream and the value given by the string
- # obtained by interpreting the /value/ byte array as a UTF-8 byte
- # stream.
- fields.setdefault(name, []).append(value)
- # 4.1 39. return to the "Field" step above
- return fields
-
- def _read_name(self):
- # 4.1 33. let /name/ be empty byte arrays
- name = ''
- while True:
- # 4.1 34. read a byte from the server
- ch = _receive_bytes(self._socket, 1)
- if ch == '\r': # 0x0D
- return None
- elif ch == '\n': # 0x0A
- raise ClientHandshakeError(
- 'Unexpected LF when reading header name %r' % name)
- elif ch == ':': # 0x3A
- return name
- elif ch >= 'A' and ch <= 'Z': # Range 0x31 to 0x5A
- ch = chr(ord(ch) + 0x20)
- name += ch
- else:
- name += ch
-
- def _skip_spaces(self):
- # 4.1 35. read a byte from the server
- while True:
- ch = _receive_bytes(self._socket, 1)
- if ch == ' ': # 0x20
- continue
- return ch
-
- def _read_value(self, ch):
- # 4.1 33. let /value/ be empty byte arrays
- value = ''
- # 4.1 36. read a byte from server.
- while True:
- if ch == '\r': # 0x0D
- return value
- elif ch == '\n': # 0x0A
- raise ClientHandshakeError(
- 'Unexpected LF when reading header value %r' % value)
- else:
- value += ch
- ch = _receive_bytes(self._socket, 1)
-
-
-def _get_permessage_deflate_framer(extension_response):
- """Validate the response and return a framer object using the parameters in
- the response. This method doesn't accept the server_.* parameters.
- """
-
- client_max_window_bits = None
- client_no_context_takeover = None
-
- client_max_window_bits_name = (
- PerMessageDeflateExtensionProcessor.
- _CLIENT_MAX_WINDOW_BITS_PARAM)
- client_no_context_takeover_name = (
- PerMessageDeflateExtensionProcessor.
- _CLIENT_NO_CONTEXT_TAKEOVER_PARAM)
-
- # We didn't send any server_.* parameter.
- # Handle those parameters as invalid if found in the response.
-
- for param_name, param_value in extension_response.get_parameters():
- if param_name == client_max_window_bits_name:
- if client_max_window_bits is not None:
- raise ClientHandshakeError(
- 'Multiple %s found' % client_max_window_bits_name)
-
- parsed_value = _parse_window_bits(param_value)
- if parsed_value is None:
- raise ClientHandshakeError(
- 'Bad %s: %r' %
- (client_max_window_bits_name, param_value))
- client_max_window_bits = parsed_value
- elif param_name == client_no_context_takeover_name:
- if client_no_context_takeover is not None:
- raise ClientHandshakeError(
- 'Multiple %s found' % client_no_context_takeover_name)
-
- if param_value is not None:
- raise ClientHandshakeError(
- 'Bad %s: Has value %r' %
- (client_no_context_takeover_name, param_value))
- client_no_context_takeover = True
-
- if client_no_context_takeover is None:
- client_no_context_takeover = False
-
- return _PerMessageDeflateFramer(client_max_window_bits,
- client_no_context_takeover)
-
-
-class ClientHandshakeProcessor(ClientHandshakeBase):
- """WebSocket opening handshake processor for
- draft-ietf-hybi-thewebsocketprotocol-06 and later.
- """
-
- def __init__(self, socket, options):
- super(ClientHandshakeProcessor, self).__init__()
-
- self._socket = socket
- self._options = options
-
- self._logger = util.get_class_logger(self)
-
- def handshake(self):
- """Performs opening handshake on the specified socket.
-
- Raises:
- ClientHandshakeError: handshake failed.
- """
-
- request_line = _build_method_line(self._options.resource)
- self._logger.debug('Client\'s opening handshake Request-Line: %r',
- request_line)
- self._socket.sendall(request_line)
-
- fields = []
- fields.append(_format_host_header(
- self._options.server_host,
- self._options.server_port,
- self._options.use_tls))
- fields.append(_UPGRADE_HEADER)
- fields.append(_CONNECTION_HEADER)
- if self._options.origin is not None:
- if self._options.protocol_version == _PROTOCOL_VERSION_HYBI08:
- fields.append(_origin_header(
- common.SEC_WEBSOCKET_ORIGIN_HEADER,
- self._options.origin))
- else:
- fields.append(_origin_header(common.ORIGIN_HEADER,
- self._options.origin))
-
- original_key = os.urandom(16)
- self._key = base64.b64encode(original_key)
- self._logger.debug(
- '%s: %r (%s)',
- common.SEC_WEBSOCKET_KEY_HEADER,
- self._key,
- util.hexify(original_key))
- fields.append(
- '%s: %s\r\n' % (common.SEC_WEBSOCKET_KEY_HEADER, self._key))
-
- if self._options.version_header > 0:
- fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER,
- self._options.version_header))
- elif self._options.protocol_version == _PROTOCOL_VERSION_HYBI08:
- fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER,
- common.VERSION_HYBI08))
- else:
- fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER,
- common.VERSION_HYBI_LATEST))
-
- extensions_to_request = []
-
- if self._options.deflate_frame:
- extensions_to_request.append(
- common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION))
-
- if self._options.use_permessage_deflate:
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- # Accept the client_max_window_bits extension parameter by default.
- extension.add_parameter(
- PerMessageDeflateExtensionProcessor.
- _CLIENT_MAX_WINDOW_BITS_PARAM,
- None)
- extensions_to_request.append(extension)
-
- if len(extensions_to_request) != 0:
- fields.append(
- '%s: %s\r\n' %
- (common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
- common.format_extensions(extensions_to_request)))
-
- for field in fields:
- self._socket.sendall(field)
-
- self._socket.sendall('\r\n')
-
- self._logger.debug('Sent client\'s opening handshake headers: %r',
- fields)
- self._logger.debug('Start reading Status-Line')
-
- status_line = ''
- while True:
- ch = _receive_bytes(self._socket, 1)
- status_line += ch
- if ch == '\n':
- break
-
- m = re.match('HTTP/\\d+\.\\d+ (\\d\\d\\d) .*\r\n', status_line)
- if m is None:
- raise ClientHandshakeError(
- 'Wrong status line format: %r' % status_line)
- status_code = m.group(1)
- if status_code != '101':
- self._logger.debug('Unexpected status code %s with following '
- 'headers: %r', status_code, self._read_fields())
- raise ClientHandshakeError(
- 'Expected HTTP status code 101 but found %r' % status_code)
-
- self._logger.debug('Received valid Status-Line')
- self._logger.debug('Start reading headers until we see an empty line')
-
- fields = self._read_fields()
-
- ch = _receive_bytes(self._socket, 1)
- if ch != '\n': # 0x0A
- raise ClientHandshakeError(
- 'Expected LF but found %r while reading value %r for header '
- 'name %r' % (ch, value, name))
-
- self._logger.debug('Received an empty line')
- self._logger.debug('Server\'s opening handshake headers: %r', fields)
-
- _validate_mandatory_header(
- fields,
- common.UPGRADE_HEADER,
- common.WEBSOCKET_UPGRADE_TYPE,
- False)
-
- _validate_mandatory_header(
- fields,
- common.CONNECTION_HEADER,
- common.UPGRADE_CONNECTION_TYPE,
- False)
-
- accept = _get_mandatory_header(
- fields, common.SEC_WEBSOCKET_ACCEPT_HEADER)
-
- # Validate
- try:
- binary_accept = base64.b64decode(accept)
- except TypeError, e:
- raise HandshakeError(
- 'Illegal value for header %s: %r' %
- (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept))
-
- if len(binary_accept) != 20:
- raise ClientHandshakeError(
- 'Decoded value of %s is not 20-byte long' %
- common.SEC_WEBSOCKET_ACCEPT_HEADER)
-
- self._logger.debug(
- 'Response for challenge : %r (%s)',
- accept, util.hexify(binary_accept))
-
- binary_expected_accept = util.sha1_hash(
- self._key + common.WEBSOCKET_ACCEPT_UUID).digest()
- expected_accept = base64.b64encode(binary_expected_accept)
-
- self._logger.debug(
- 'Expected response for challenge: %r (%s)',
- expected_accept, util.hexify(binary_expected_accept))
-
- if accept != expected_accept:
- raise ClientHandshakeError(
- 'Invalid %s header: %r (expected: %s)' %
- (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept, expected_accept))
-
- deflate_frame_accepted = False
- permessage_deflate_accepted = False
-
- extensions_header = fields.get(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER.lower())
- accepted_extensions = []
- if extensions_header is not None and len(extensions_header) != 0:
- accepted_extensions = common.parse_extensions(extensions_header[0])
-
- # TODO(bashi): Support the new style perframe compression extension.
- for extension in accepted_extensions:
- extension_name = extension.name()
- if (extension_name == common.DEFLATE_FRAME_EXTENSION and
- self._options.deflate_frame):
- deflate_frame_accepted = True
- processor = DeflateFrameExtensionProcessor(extension)
- unused_extension_response = processor.get_extension_response()
- self._options.deflate_frame = processor
- continue
- elif (extension_name == common.PERMESSAGE_DEFLATE_EXTENSION and
- self._options.use_permessage_deflate):
- permessage_deflate_accepted = True
-
- framer = _get_permessage_deflate_framer(extension)
- framer.set_compress_outgoing_enabled(True)
- self._options.use_permessage_deflate = framer
- continue
-
- raise ClientHandshakeError(
- 'Unexpected extension %r' % extension_name)
-
- if (self._options.deflate_frame and not deflate_frame_accepted):
- raise ClientHandshakeError(
- 'Requested %s, but the server rejected it' %
- common.DEFLATE_FRAME_EXTENSION)
-
- if (self._options.use_permessage_deflate and
- not permessage_deflate_accepted):
- raise ClientHandshakeError(
- 'Requested %s, but the server rejected it' %
- common.PERMESSAGE_DEFLATE_EXTENSION)
-
- # TODO(tyoshino): Handle Sec-WebSocket-Protocol
- # TODO(tyoshino): Handle Cookie, etc.
-
-
-class ClientHandshakeProcessorHybi00(ClientHandshakeBase):
- """WebSocket opening handshake processor for
- draft-ietf-hybi-thewebsocketprotocol-00 (equivalent to
- draft-hixie-thewebsocketprotocol-76).
- """
-
- def __init__(self, socket, options):
- super(ClientHandshakeProcessorHybi00, self).__init__()
-
- self._socket = socket
- self._options = options
-
- self._logger = util.get_class_logger(self)
-
- if (self._options.deflate_frame or
- self._options.use_permessage_deflate):
- logging.critical('HyBi 00 doesn\'t support extensions.')
- sys.exit(1)
-
- def handshake(self):
- """Performs opening handshake on the specified socket.
-
- Raises:
- ClientHandshakeError: handshake failed.
- """
-
- # 4.1 5. send request line.
- self._socket.sendall(_build_method_line(self._options.resource))
- # 4.1 6. Let /fields/ be an empty list of strings.
- fields = []
- # 4.1 7. Add the string "Upgrade: WebSocket" to /fields/.
- fields.append(_UPGRADE_HEADER_HIXIE75)
- # 4.1 8. Add the string "Connection: Upgrade" to /fields/.
- fields.append(_CONNECTION_HEADER)
- # 4.1 9-12. Add Host: field to /fields/.
- fields.append(_format_host_header(
- self._options.server_host,
- self._options.server_port,
- self._options.use_tls))
- # 4.1 13. Add Origin: field to /fields/.
- if not self._options.origin:
- raise ClientHandshakeError(
- 'Specify the origin of the connection by --origin flag')
- fields.append(_origin_header(common.ORIGIN_HEADER,
- self._options.origin))
- # TODO: 4.1 14 Add Sec-WebSocket-Protocol: field to /fields/.
- # TODO: 4.1 15 Add cookie headers to /fields/.
-
- # 4.1 16-23. Add Sec-WebSocket-Key<n> to /fields/.
- self._number1, key1 = self._generate_sec_websocket_key()
- self._logger.debug('Number1: %d', self._number1)
- fields.append('%s: %s\r\n' % (common.SEC_WEBSOCKET_KEY1_HEADER, key1))
- self._number2, key2 = self._generate_sec_websocket_key()
- self._logger.debug('Number2: %d', self._number2)
- fields.append('%s: %s\r\n' % (common.SEC_WEBSOCKET_KEY2_HEADER, key2))
-
- fields.append('%s: 0\r\n' % common.SEC_WEBSOCKET_DRAFT_HEADER)
-
- # 4.1 24. For each string in /fields/, in a random order: send the
- # string, encoded as UTF-8, followed by a UTF-8 encoded U+000D CARRIAGE
- # RETURN U+000A LINE FEED character pair (CRLF).
- random.shuffle(fields)
- for field in fields:
- self._socket.sendall(field)
- # 4.1 25. send a UTF-8-encoded U+000D CARRIAGE RETURN U+000A LINE FEED
- # character pair (CRLF).
- self._socket.sendall('\r\n')
- # 4.1 26. let /key3/ be a string consisting of eight random bytes (or
- # equivalently, a random 64 bit integer encoded in a big-endian order).
- self._key3 = self._generate_key3()
- # 4.1 27. send /key3/ to the server.
- self._socket.sendall(self._key3)
- self._logger.debug(
- 'Key3: %r (%s)', self._key3, util.hexify(self._key3))
-
- self._logger.info('Sent handshake')
-
- # 4.1 28. Read bytes from the server until either the connection
- # closes, or a 0x0A byte is read. let /field/ be these bytes, including
- # the 0x0A bytes.
- field = ''
- while True:
- ch = _receive_bytes(self._socket, 1)
- field += ch
- if ch == '\n':
- break
- # if /field/ is not at least seven bytes long, or if the last
- # two bytes aren't 0x0D and 0x0A respectively, or if it does not
- # contain at least two 0x20 bytes, then fail the WebSocket connection
- # and abort these steps.
- if len(field) < 7 or not field.endswith('\r\n'):
- raise ClientHandshakeError('Wrong status line: %r' % field)
- m = re.match('[^ ]* ([^ ]*) .*', field)
- if m is None:
- raise ClientHandshakeError(
- 'No HTTP status code found in status line: %r' % field)
- # 4.1 29. let /code/ be the substring of /field/ that starts from the
- # byte after the first 0x20 byte, and ends with the byte before the
- # second 0x20 byte.
- code = m.group(1)
- # 4.1 30. if /code/ is not three bytes long, or if any of the bytes in
- # /code/ are not in the range 0x30 to 0x90, then fail the WebSocket
- # connection and abort these steps.
- if not re.match('[0-9][0-9][0-9]', code):
- raise ClientHandshakeError(
- 'HTTP status code %r is not three digit in status line: %r' %
- (code, field))
- # 4.1 31. if /code/, interpreted as UTF-8, is "101", then move to the
- # next step.
- if code != '101':
- raise ClientHandshakeError(
- 'Expected HTTP status code 101 but found %r in status line: '
- '%r' % (code, field))
- # 4.1 32-39. read fields into /fields/
- fields = self._read_fields()
- # 4.1 40. _Fields processing_
- # read a byte from server
- ch = _receive_bytes(self._socket, 1)
- if ch != '\n': # 0x0A
- raise ClientHandshakeError('Expected LF but found %r' % ch)
- # 4.1 41. check /fields/
- # TODO(ukai): protocol
- # if the entry's name is "upgrade"
- # if the value is not exactly equal to the string "WebSocket",
- # then fail the WebSocket connection and abort these steps.
- _validate_mandatory_header(
- fields,
- common.UPGRADE_HEADER,
- common.WEBSOCKET_UPGRADE_TYPE_HIXIE75,
- True)
- # if the entry's name is "connection"
- # if the value, converted to ASCII lowercase, is not exactly equal
- # to the string "upgrade", then fail the WebSocket connection and
- # abort these steps.
- _validate_mandatory_header(
- fields,
- common.CONNECTION_HEADER,
- common.UPGRADE_CONNECTION_TYPE,
- False)
-
- origin = _get_mandatory_header(
- fields, common.SEC_WEBSOCKET_ORIGIN_HEADER)
-
- location = _get_mandatory_header(
- fields, common.SEC_WEBSOCKET_LOCATION_HEADER)
-
- # TODO(ukai): check origin, location, cookie, ..
-
- # 4.1 42. let /challenge/ be the concatenation of /number_1/,
- # expressed as a big endian 32 bit integer, /number_2/, expressed
- # as big endian 32 bit integer, and the eight bytes of /key_3/ in the
- # order they were sent on the wire.
- challenge = struct.pack('!I', self._number1)
- challenge += struct.pack('!I', self._number2)
- challenge += self._key3
-
- self._logger.debug(
- 'Challenge: %r (%s)', challenge, util.hexify(challenge))
-
- # 4.1 43. let /expected/ be the MD5 fingerprint of /challenge/ as a
- # big-endian 128 bit string.
- expected = util.md5_hash(challenge).digest()
- self._logger.debug(
- 'Expected challenge response: %r (%s)',
- expected, util.hexify(expected))
-
- # 4.1 44. read sixteen bytes from the server.
- # let /reply/ be those bytes.
- reply = _receive_bytes(self._socket, 16)
- self._logger.debug(
- 'Actual challenge response: %r (%s)', reply, util.hexify(reply))
-
- # 4.1 45. if /reply/ does not exactly equal /expected/, then fail
- # the WebSocket connection and abort these steps.
- if expected != reply:
- raise ClientHandshakeError(
- 'Bad challenge response: %r (expected) != %r (actual)' %
- (expected, reply))
- # 4.1 46. The *WebSocket connection is established*.
-
- def _generate_sec_websocket_key(self):
- # 4.1 16. let /spaces_n/ be a random integer from 1 to 12 inclusive.
- spaces = random.randint(1, 12)
- # 4.1 17. let /max_n/ be the largest integer not greater than
- # 4,294,967,295 divided by /spaces_n/.
- maxnum = 4294967295 / spaces
- # 4.1 18. let /number_n/ be a random integer from 0 to /max_n/
- # inclusive.
- number = random.randint(0, maxnum)
- # 4.1 19. let /product_n/ be the result of multiplying /number_n/ and
- # /spaces_n/ together.
- product = number * spaces
- # 4.1 20. let /key_n/ be a string consisting of /product_n/, expressed
- # in base ten using the numerals in the range U+0030 DIGIT ZERO (0) to
- # U+0039 DIGIT NINE (9).
- key = str(product)
- # 4.1 21. insert between one and twelve random characters from the
- # range U+0021 to U+002F and U+003A to U+007E into /key_n/ at random
- # positions.
- available_chars = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1)
- n = random.randint(1, 12)
- for _ in xrange(n):
- ch = random.choice(available_chars)
- pos = random.randint(0, len(key))
- key = key[0:pos] + chr(ch) + key[pos:]
- # 4.1 22. insert /spaces_n/ U+0020 SPACE characters into /key_n/ at
- # random positions other than start or end of the string.
- for _ in xrange(spaces):
- pos = random.randint(1, len(key) - 1)
- key = key[0:pos] + ' ' + key[pos:]
- return number, key
-
- def _generate_key3(self):
- # 4.1 26. let /key3/ be a string consisting of eight random bytes (or
- # equivalently, a random 64 bit integer encoded in a big-endian order).
- return ''.join([chr(random.randint(0, 255)) for _ in xrange(8)])
-
-
-class ClientConnection(object):
- """A wrapper for socket object to provide the mp_conn interface.
- mod_pywebsocket library is designed to be working on Apache mod_python's
- mp_conn object.
- """
-
- def __init__(self, socket):
- self._socket = socket
-
- def write(self, data):
- self._socket.sendall(data)
-
- def read(self, n):
- return self._socket.recv(n)
-
- def get_remote_addr(self):
- return self._socket.getpeername()
- remote_addr = property(get_remote_addr)
-
-
-class ClientRequest(object):
- """A wrapper class just to make it able to pass a socket object to
- functions that expect a mp_request object.
- """
-
- def __init__(self, socket):
- self._logger = util.get_class_logger(self)
-
- self._socket = socket
- self.connection = ClientConnection(socket)
-
-
-def _import_ssl():
- global ssl
- try:
- import ssl
- return True
- except ImportError:
- return False
-
-
-def _import_pyopenssl():
- global OpenSSL
- try:
- import OpenSSL.SSL
- return True
- except ImportError:
- return False
-
-
-class EchoClient(object):
- """WebSocket echo client."""
-
- def __init__(self, options):
- self._options = options
- self._socket = None
-
- self._logger = util.get_class_logger(self)
-
- def run(self):
- """Run the client.
-
- Shake hands and then repeat sending message and receiving its echo.
- """
-
- self._socket = socket.socket()
- self._socket.settimeout(self._options.socket_timeout)
- try:
- self._socket.connect((self._options.server_host,
- self._options.server_port))
- if self._options.use_tls:
- self._socket = _TLSSocket(
- self._socket,
- self._options.tls_module,
- self._options.tls_version,
- self._options.disable_tls_compression)
-
- version = self._options.protocol_version
-
- if (version == _PROTOCOL_VERSION_HYBI08 or
- version == _PROTOCOL_VERSION_HYBI13):
- self._handshake = ClientHandshakeProcessor(
- self._socket, self._options)
- elif version == _PROTOCOL_VERSION_HYBI00:
- self._handshake = ClientHandshakeProcessorHybi00(
- self._socket, self._options)
- else:
- raise ValueError(
- 'Invalid --protocol-version flag: %r' % version)
-
- self._handshake.handshake()
-
- self._logger.info('Connection established')
-
- request = ClientRequest(self._socket)
-
- version_map = {
- _PROTOCOL_VERSION_HYBI08: common.VERSION_HYBI08,
- _PROTOCOL_VERSION_HYBI13: common.VERSION_HYBI13,
- _PROTOCOL_VERSION_HYBI00: common.VERSION_HYBI00}
- request.ws_version = version_map[version]
-
- if (version == _PROTOCOL_VERSION_HYBI08 or
- version == _PROTOCOL_VERSION_HYBI13):
- stream_option = StreamOptions()
- stream_option.mask_send = True
- stream_option.unmask_receive = False
-
- if self._options.deflate_frame is not False:
- processor = self._options.deflate_frame
- processor.setup_stream_options(stream_option)
-
- if self._options.use_permessage_deflate is not False:
- framer = self._options.use_permessage_deflate
- framer.setup_stream_options(stream_option)
-
- self._stream = Stream(request, stream_option)
- elif version == _PROTOCOL_VERSION_HYBI00:
- self._stream = StreamHixie75(request, True)
-
- for line in self._options.message.split(','):
- self._stream.send_message(line)
- if self._options.verbose:
- print 'Send: %s' % line
- try:
- received = self._stream.receive_message()
-
- if self._options.verbose:
- print 'Recv: %s' % received
- except Exception, e:
- if self._options.verbose:
- print 'Error: %s' % e
- raise
-
- self._do_closing_handshake()
- finally:
- self._socket.close()
-
- def _do_closing_handshake(self):
- """Perform closing handshake using the specified closing frame."""
-
- if self._options.message.split(',')[-1] == _GOODBYE_MESSAGE:
- # requested server initiated closing handshake, so
- # expecting closing handshake message from server.
- self._logger.info('Wait for server-initiated closing handshake')
- message = self._stream.receive_message()
- if message is None:
- print 'Recv close'
- print 'Send ack'
- self._logger.info(
- 'Received closing handshake and sent ack')
- return
- print 'Send close'
- self._stream.close_connection()
- self._logger.info('Sent closing handshake')
- print 'Recv ack'
- self._logger.info('Received ack')
-
-
-def main():
- sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
-
- parser = OptionParser()
- # We accept --command_line_flag style flags which is the same as Google
- # gflags in addition to common --command-line-flag style flags.
- parser.add_option('-s', '--server-host', '--server_host',
- dest='server_host', type='string',
- default='localhost', help='server host')
- parser.add_option('-p', '--server-port', '--server_port',
- dest='server_port', type='int',
- default=_UNDEFINED_PORT, help='server port')
- parser.add_option('-o', '--origin', dest='origin', type='string',
- default=None, help='origin')
- parser.add_option('-r', '--resource', dest='resource', type='string',
- default='/echo', help='resource path')
- parser.add_option('-m', '--message', dest='message', type='string',
- help=('comma-separated messages to send. '
- '%s will force close the connection from server.' %
- _GOODBYE_MESSAGE))
- parser.add_option('-q', '--quiet', dest='verbose', action='store_false',
- default=True, help='suppress messages')
- parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
- default=False, help='use TLS (wss://). By default, '
- 'it looks for ssl and pyOpenSSL module and uses found '
- 'one. Use --tls-module option to specify which module '
- 'to use')
- parser.add_option('--tls-module', '--tls_module', dest='tls_module',
- type='choice',
- choices=[_TLS_BY_STANDARD_MODULE, _TLS_BY_PYOPENSSL],
- help='Use ssl module if "%s" is specified. '
- 'Use pyOpenSSL module if "%s" is specified' %
- (_TLS_BY_STANDARD_MODULE, _TLS_BY_PYOPENSSL))
- parser.add_option('--tls-version', '--tls_version',
- dest='tls_version',
- type='string', default=_TLS_VERSION_SSL23,
- help='TLS/SSL version to use. One of \'' +
- _TLS_VERSION_SSL23 + '\' (SSL version 2 or 3), \'' +
- _TLS_VERSION_SSL3 + '\' (SSL version 3), \'' +
- _TLS_VERSION_TLS1 + '\' (TLS version 1)')
- parser.add_option('--disable-tls-compression', '--disable_tls_compression',
- dest='disable_tls_compression',
- action='store_true', default=False,
- help='Disable TLS compression. Available only when '
- 'pyOpenSSL module is used.')
- parser.add_option('-k', '--socket-timeout', '--socket_timeout',
- dest='socket_timeout', type='int', default=_TIMEOUT_SEC,
- help='Timeout(sec) for sockets')
- parser.add_option('--draft75', dest='draft75',
- action='store_true', default=False,
- help='Obsolete option. Don\'t use this.')
- parser.add_option('--protocol-version', '--protocol_version',
- dest='protocol_version',
- type='string', default=_PROTOCOL_VERSION_HYBI13,
- help='WebSocket protocol version to use. One of \'' +
- _PROTOCOL_VERSION_HYBI13 + '\', \'' +
- _PROTOCOL_VERSION_HYBI08 + '\', \'' +
- _PROTOCOL_VERSION_HYBI00 + '\'')
- parser.add_option('--version-header', '--version_header',
- dest='version_header',
- type='int', default=-1,
- help='Specify Sec-WebSocket-Version header value')
- parser.add_option('--deflate-frame', '--deflate_frame',
- dest='deflate_frame',
- action='store_true', default=False,
- help='Use the deflate-frame extension.')
- parser.add_option('--use-permessage-deflate', '--use_permessage_deflate',
- dest='use_permessage_deflate',
- action='store_true', default=False,
- help='Use the permessage-deflate extension.')
- parser.add_option('--log-level', '--log_level', type='choice',
- dest='log_level', default='warn',
- choices=['debug', 'info', 'warn', 'error', 'critical'],
- help='Log level.')
-
- (options, unused_args) = parser.parse_args()
-
- logging.basicConfig(level=logging.getLevelName(options.log_level.upper()))
-
- if options.draft75:
- logging.critical('--draft75 option is obsolete.')
- sys.exit(1)
-
- if options.protocol_version == _PROTOCOL_VERSION_HIXIE75:
- logging.critical(
- 'Value %s is obsolete for --protocol_version options' %
- _PROTOCOL_VERSION_HIXIE75)
- sys.exit(1)
-
- if options.use_tls:
- if options.tls_module is None:
- if _import_ssl():
- options.tls_module = _TLS_BY_STANDARD_MODULE
- logging.debug('Using ssl module')
- elif _import_pyopenssl():
- options.tls_module = _TLS_BY_PYOPENSSL
- logging.debug('Using pyOpenSSL module')
- else:
- logging.critical(
- 'TLS support requires ssl or pyOpenSSL module.')
- sys.exit(1)
- elif options.tls_module == _TLS_BY_STANDARD_MODULE:
- if not _import_ssl():
- logging.critical('ssl module is not available')
- sys.exit(1)
- elif options.tls_module == _TLS_BY_PYOPENSSL:
- if not _import_pyopenssl():
- logging.critical('pyOpenSSL module is not available')
- sys.exit(1)
- else:
- logging.critical('Invalid --tls-module option: %r',
- options.tls_module)
- sys.exit(1)
-
- if (options.disable_tls_compression and
- options.tls_module != _TLS_BY_PYOPENSSL):
- logging.critical('You can disable TLS compression only when '
- 'pyOpenSSL module is used.')
- sys.exit(1)
- else:
- if options.tls_module is not None:
- logging.critical('Use --tls-module option only together with '
- '--use-tls option.')
- sys.exit(1)
-
- if options.disable_tls_compression:
- logging.critical('Use --disable-tls-compression only together '
- 'with --use-tls option.')
- sys.exit(1)
-
- # Default port number depends on whether TLS is used.
- if options.server_port == _UNDEFINED_PORT:
- if options.use_tls:
- options.server_port = common.DEFAULT_WEB_SOCKET_SECURE_PORT
- else:
- options.server_port = common.DEFAULT_WEB_SOCKET_PORT
-
- # optparse doesn't seem to handle non-ascii default values.
- # Set default message here.
- if not options.message:
- options.message = u'Hello,\u65e5\u672c' # "Japan" in Japanese
-
- EchoClient(options).run()
-
-
-if __name__ == '__main__':
- main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/echo_noext_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/echo_noext_wsh.py
deleted file mode 100644
index 1df515122..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/echo_noext_wsh.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2013, 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.
-
-
-_GOODBYE_MESSAGE = u'Goodbye'
-
-
-def web_socket_do_extra_handshake(request):
- """Received Sec-WebSocket-Extensions header value is parsed into
- request.ws_requested_extensions. pywebsocket creates extension
- processors using it before do_extra_handshake call and never looks at it
- after the call.
-
- To reject requested extensions, clear the processor list.
- """
-
- request.ws_extension_processors = []
-
-
-def web_socket_transfer_data(request):
- """Echo. Same as echo_wsh.py."""
-
- while True:
- line = request.ws_stream.receive_message()
- if line is None:
- return
- if isinstance(line, unicode):
- 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
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/echo_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/echo_wsh.py
deleted file mode 100644
index 38646c32c..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/echo_wsh.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2011, 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.
-
-
-_GOODBYE_MESSAGE = u'Goodbye'
-
-
-def web_socket_do_extra_handshake(request):
- # This example handler accepts any request. See origin_check_wsh.py for how
- # to reject access from untrusted scripts based on origin value.
-
- pass # Always accept.
-
-
-def web_socket_transfer_data(request):
- while True:
- line = request.ws_stream.receive_message()
- if line is None:
- return
- if isinstance(line, unicode):
- 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
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.cgi b/testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.cgi
deleted file mode 100755
index adddf237c..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.cgi
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/python
-
-# Copyright 2013, 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.
-
-
-"""This CGI script generates text/event-stream type data stream for testing
-the Server-Sent Events.
-
-It will only work correctly with HTTP servers that do not buffer the output of
-CGI scripts.
-"""
-
-
-import sys
-import time
-
-sys.stdout.write('Content-type: text/event-stream\r\n\r\n')
-
-id = 0
-
-while True:
- sys.stdout.write('data: Hello\r\nid: %d\r\n\r\n' % id)
- sys.stdout.flush()
-
- id = id + 1
-
- time.sleep(1)
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.html b/testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.html
deleted file mode 100644
index 1598a8807..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/eventsource.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<!--
-Copyright 2013, 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.
--->
-
-<!--
-Simple example of the Server-Sent Events
-http://dev.w3.org/html5/eventsource/
-
-For comparison with the WebSocket Protocol & API.
-
-Run the pywebsocket with the --cgi_path parameter.
--->
-
-<html>
-<head>
-<title>Server-Sent Events Example</title>
-<script>
-var eventSource = null;
-
-function addToLog(data) {
- logBox.value += data + '\n';
- logBox.scrollTop = 1000000;
-}
-
-function init() {
- logBox = document.getElementById('log');
-
- eventSource = new EventSource('/eventsource.cgi');
- eventSource.onopen = function() {
- addToLog('onopen (readyState = ' + eventSource.readyState + ')');
- }
- eventSource.onmessage = function(event) {
- addToLog(event.data);
- }
- eventSource.onerror = function(event) {
- addToLog('onerror (readyState = ' + eventSource.readyState + ')');
- }
-}
-</script>
-</head>
-<body onload="init()">
-<textarea id="log" rows="10" cols="40" readonly></textarea>
-<p style="font-size: small">
- Make sure that pywebsocket is run with --cgi_path parameter.
-</p>
-</body>
-</html>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/handler_map.txt b/testing/web-platform/tests/tools/pywebsocket/src/example/handler_map.txt
deleted file mode 100644
index 21c4c09aa..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/handler_map.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# websocket handler map file, used by standalone.py -m option.
-# A line starting with '#' is a comment line.
-# Each line consists of 'alias_resource_path' and 'existing_resource_path'
-# separated by spaces.
-# Aliasing is processed from the top to the bottom of the line, and
-# 'existing_resource_path' must exist before it is aliased.
-# For example,
-# / /echo
-# means that a request to '/' will be handled by handlers for '/echo'.
-/ /echo
-
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/hsts_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/hsts_wsh.py
deleted file mode 100644
index e86194692..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/hsts_wsh.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2013, 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.
-
-
-def web_socket_do_extra_handshake(request):
- request.extra_headers.append(
- ('Strict-Transport-Security', 'max-age=86400'))
-
-
-def web_socket_transfer_data(request):
- request.ws_stream.send_message('Hello', binary=False)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/internal_error_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/internal_error_wsh.py
deleted file mode 100644
index fe581b54a..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/internal_error_wsh.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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 msgutil
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- raise msgutil.BadOperationException('Intentional')
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/origin_check_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/example/origin_check_wsh.py
deleted file mode 100644
index e05767ab9..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/origin_check_wsh.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2011, 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.
-
-# This example is derived from test/testdata/handlers/origin_check_wsh.py.
-
-
-def web_socket_do_extra_handshake(request):
- if request.ws_origin == 'http://example.com':
- return
- raise ValueError('Unacceptable origin: %r' % request.ws_origin)
-
-
-def web_socket_transfer_data(request):
- request.connection.write('origin_check_wsh.py is called for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/pywebsocket.conf b/testing/web-platform/tests/tools/pywebsocket/src/example/pywebsocket.conf
deleted file mode 100644
index 335d130a5..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/pywebsocket.conf
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2011, 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.
-
-#
-# Sample configuration file for apache2
-#
-LogLevel debug
-<IfModule python_module>
- PythonPath "sys.path+['/mod_pywebsocket']"
- PythonOption mod_pywebsocket.handler_root /var/www
- PythonOption mod_pywebsocket.handler_scan /var/www/ws
- #PythonOption mod_pywebsocket.allow_draft75 On
- <Location /ws>
- PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
- </Location>
-</IfModule>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/special_headers.cgi b/testing/web-platform/tests/tools/pywebsocket/src/example/special_headers.cgi
deleted file mode 100755
index ea5080f1f..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/special_headers.cgi
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/python
-
-# Copyright 2014 Google Inc. All rights reserved.
-#
-# Use of this source code is governed by a BSD-style
-# license that can be found in the COPYING file or at
-# https://developers.google.com/open-source/licenses/bsd
-
-"""CGI script sample for testing effect of HTTP headers on the origin page.
-
-Note that CGI scripts don't work on the standalone pywebsocket running in TLS
-mode.
-"""
-
-
-print """Content-type: text/html
-Content-Security-Policy: connect-src self
-
-<html>
-<head>
-<title></title>
-</head>
-<body>
-<script>
-var socket = new WebSocket("ws://example.com");
-</script>
-</body>
-</html>"""
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/util.js b/testing/web-platform/tests/tools/pywebsocket/src/example/util.js
deleted file mode 100644
index a1cad4975..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/util.js
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2013, 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.
-
-
-// Utilities for example applications (for both main and worker thread).
-
-var results = {};
-
-function getTimeStamp() {
- return Date.now();
-}
-
-function formatResultInKiB(size, timePerMessageInMs, stddevTimePerMessageInMs,
- speed, printSize) {
- if (printSize) {
- return (size / 1024) +
- '\t' + timePerMessageInMs.toFixed(3) +
- (stddevTimePerMessageInMs == -1 ?
- '' :
- '\t' + stddevTimePerMessageInMs.toFixed(3)) +
- '\t' + speed.toFixed(3);
- } else {
- return speed.toString();
- }
-}
-
-function clearAverageData() {
- results = {};
-}
-
-function reportAverageData(config) {
- config.addToSummary(
- 'Size[KiB]\tAverage time[ms]\tStddev time[ms]\tSpeed[KB/s]');
- for (var size in results) {
- var averageTimePerMessageInMs = results[size].sum_t / results[size].n;
- var speed = calculateSpeedInKB(size, averageTimePerMessageInMs);
- // Calculate sample standard deviation
- var stddevTimePerMessageInMs = Math.sqrt(
- (results[size].sum_t2 / results[size].n -
- averageTimePerMessageInMs * averageTimePerMessageInMs) *
- results[size].n /
- (results[size].n - 1));
- config.addToSummary(formatResultInKiB(
- size, averageTimePerMessageInMs, stddevTimePerMessageInMs, speed,
- true));
- }
-}
-
-function calculateSpeedInKB(size, timeSpentInMs) {
- return Math.round(size / timeSpentInMs * 1000) / 1000;
-}
-
-function calculateAndLogResult(config, size, startTimeInMs, totalSize) {
- var timeSpentInMs = getTimeStamp() - startTimeInMs;
- var speed = calculateSpeedInKB(totalSize, timeSpentInMs);
- var timePerMessageInMs = timeSpentInMs / (totalSize / size);
- if (!results[size]) {
- results[size] = {n: 0, sum_t: 0, sum_t2: 0};
- }
- config.measureValue(timePerMessageInMs);
- results[size].n ++;
- results[size].sum_t += timePerMessageInMs;
- results[size].sum_t2 += timePerMessageInMs * timePerMessageInMs;
- config.addToLog(formatResultInKiB(size, timePerMessageInMs, -1, speed,
- config.printSize));
-}
-
-function fillArrayBuffer(buffer, c) {
- var i;
-
- var u32Content = c * 0x01010101;
-
- var u32Blocks = Math.floor(buffer.byteLength / 4);
- var u32View = new Uint32Array(buffer, 0, u32Blocks);
- // length attribute is slow on Chrome. Don't use it for loop condition.
- for (i = 0; i < u32Blocks; ++i) {
- u32View[i] = u32Content;
- }
-
- // Fraction
- var u8Blocks = buffer.byteLength - u32Blocks * 4;
- var u8View = new Uint8Array(buffer, u32Blocks * 4, u8Blocks);
- for (i = 0; i < u8Blocks; ++i) {
- u8View[i] = c;
- }
-}
-
-function verifyArrayBuffer(buffer, expectedChar) {
- var i;
-
- var expectedU32Value = expectedChar * 0x01010101;
-
- var u32Blocks = Math.floor(buffer.byteLength / 4);
- var u32View = new Uint32Array(buffer, 0, u32Blocks);
- for (i = 0; i < u32Blocks; ++i) {
- if (u32View[i] != expectedU32Value) {
- return false;
- }
- }
-
- var u8Blocks = buffer.byteLength - u32Blocks * 4;
- var u8View = new Uint8Array(buffer, u32Blocks * 4, u8Blocks);
- for (i = 0; i < u8Blocks; ++i) {
- if (u8View[i] != expectedChar) {
- return false;
- }
- }
-
- return true;
-}
-
-function verifyBlob(config, blob, expectedChar, doneCallback) {
- var reader = new FileReader(blob);
- reader.onerror = function() {
- config.addToLog('FileReader Error: ' + reader.error.message);
- doneCallback(blob.size, false);
- }
- reader.onloadend = function() {
- var result = verifyArrayBuffer(reader.result, expectedChar);
- doneCallback(blob.size, result);
- }
- reader.readAsArrayBuffer(blob);
-}
-
-function verifyAcknowledgement(config, message, size) {
- if (typeof message != 'string') {
- config.addToLog('Invalid ack type: ' + typeof message);
- return false;
- }
- var parsedAck = parseInt(message);
- if (isNaN(parsedAck)) {
- config.addToLog('Invalid ack value: ' + message);
- return false;
- }
- if (parsedAck != size) {
- config.addToLog(
- 'Expected ack for ' + size + 'B but received one for ' + parsedAck +
- 'B');
- return false;
- }
-
- return true;
-}
-
-function cloneConfig(obj) {
- var newObj = {};
- for (key in obj) {
- newObj[key] = obj[key];
- }
- return newObj;
-}
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/util_main.js b/testing/web-platform/tests/tools/pywebsocket/src/example/util_main.js
deleted file mode 100644
index b03d1c2bd..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/util_main.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the COPYING file or at
-// https://developers.google.com/open-source/licenses/bsd
-
-// Utilities for example applications (for the main thread only).
-
-var logBox = null;
-var queuedLog = '';
-
-var summaryBox = null;
-
-function queueLog(log) {
- queuedLog += log + '\n';
-}
-
-function addToLog(log) {
- logBox.value += queuedLog;
- queuedLog = '';
- logBox.value += log + '\n';
- logBox.scrollTop = 1000000;
-}
-
-function addToSummary(log) {
- summaryBox.value += log + '\n';
- summaryBox.scrollTop = 1000000;
-}
-
-// value: execution time in milliseconds.
-// config.measureValue is intended to be used in Performance Tests.
-// Do nothing here in non-PerformanceTest.
-function measureValue(value) {
-}
-
-function getIntFromInput(id) {
- return parseInt(document.getElementById(id).value);
-}
-
-function getStringFromRadioBox(name) {
- var list = document.getElementById('benchmark_form')[name];
- for (var i = 0; i < list.length; ++i)
- if (list.item(i).checked)
- return list.item(i).value;
- return undefined;
-}
-function getBoolFromCheckBox(id) {
- return document.getElementById(id).checked;
-}
-
-function getIntArrayFromInput(id) {
- var strArray = document.getElementById(id).value.split(',');
- return strArray.map(function(str) { return parseInt(str, 10); });
-}
-
-function onMessage(message) {
- if (message.data.type === 'addToLog')
- addToLog(message.data.data);
- else if (message.data.type === 'addToSummary')
- addToSummary(message.data.data);
- else if (message.data.type === 'measureValue')
- measureValue(message.data.data);
-}
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/util_worker.js b/testing/web-platform/tests/tools/pywebsocket/src/example/util_worker.js
deleted file mode 100644
index b64f7829d..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/util_worker.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the COPYING file or at
-// https://developers.google.com/open-source/licenses/bsd
-
-// Utilities for example applications (for the worker threads only).
-
-function workerAddToLog(text) {
- postMessage({type: 'addToLog', data: text});
-}
-
-function workerAddToSummary(text) {
- postMessage({type: 'addToSummary', data: text});
-}
-
-function workerMeasureValue(value) {
- postMessage({type: 'measureValue', data: value});
-}
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.html b/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.html
deleted file mode 100644
index 186229775..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.html
+++ /dev/null
@@ -1,222 +0,0 @@
-<!--
-Copyright 2013, 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.
--->
-
-<html>
-<head>
-<title>XMLHttpRequest benchmark</title>
-<script src="util_main.js"></script>
-<script src="util.js"></script>
-<script src="xhr_benchmark.js"></script>
-<script>
-var addressBox = null;
-
-function getConfig() {
- return {
- prefixUrl: addressBox.value,
- printSize: getBoolFromCheckBox('printsize'),
- numXHRs: getIntFromInput('numXHRs'),
- async: getBoolFromCheckBox('async'),
- // Initial size of messages.
- numIterations: getIntFromInput('numiterations'),
- numWarmUpIterations: getIntFromInput('numwarmupiterations'),
- startSize: getIntFromInput('startsize'),
- // Stops benchmark when the size of message exceeds this threshold.
- stopThreshold: getIntFromInput('stopthreshold'),
- // If the size of each message is small, send/receive multiple messages
- // until the sum of sizes reaches this threshold.
- // minTotal: getIntFromInput('mintotal'),
- // minTotal is not yet implemented on XHR benchmark
- multipliers: getIntArrayFromInput('multipliers'),
- verifyData: getBoolFromCheckBox('verifydata')
- };
-}
-
-var worker = new Worker('xhr_benchmark.js');
-worker.onmessage = onMessage;
-
-function onSendBenchmark() {
- var config = getConfig();
- config.dataType = getStringFromRadioBox('datatyperadio');
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'sendBenchmark', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- sendBenchmark(config);
- }
-}
-
-function onReceiveBenchmark() {
- var config = getConfig();
- config.dataType = getStringFromRadioBox('datatyperadio');
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'receiveBenchmark', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- receiveBenchmark(config);
- }
-}
-
-function onBatchBenchmark() {
- var config = getConfig();
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'batchBenchmark', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- batchBenchmark(config);
- }
-}
-
-function onStop() {
- var config = getConfig();
-
- if (getBoolFromCheckBox('worker')) {
- worker.postMessage({type: 'stop', config: config});
- } else {
- config.addToLog = addToLog;
- config.addToSummary = addToSummary;
- config.measureValue = measureValue;
- stop(config);
- }
-}
-
-function init() {
- addressBox = document.getElementById('address');
- logBox = document.getElementById('log');
-
- summaryBox = document.getElementById('summary');
-
- // Special address of pywebsocket for XHR benchmark.
- addressBox.value = '/073be001e10950692ccbf3a2ad21c245';
-
- addToLog(window.navigator.userAgent.toLowerCase());
- addToSummary(window.navigator.userAgent.toLowerCase());
-}
-</script>
-</head>
-<body onload="init()">
-
-<form id="benchmark_form">
- url prefix <input type="text" id="address" size="40">
- <input type="button" value="send" onclick="onSendBenchmark()">
- <input type="button" value="receive" onclick="onReceiveBenchmark()">
- <input type="button" value="batch" onclick="onBatchBenchmark()">
- <input type="button" value="stop" onclick="onStop()">
-
- <br/>
-
- <input type="checkbox" id="printsize" checked>
- <label for="printsize">Print size and time per message</label>
- <input type="checkbox" id="verifydata" checked>
- <label for="verifydata">Verify data</label>
- <input type="checkbox" id="worker">
- <label for="worker">Run on worker</label>
- <input type="checkbox" id="async" checked>
- <label for="async">Async</label><br>
- (Receive &amp;&amp; Non-Worker &amp;&amp; Sync is not supported by spec)
-
- <br/>
-
- Parameters:
-
- <br/>
-
- <table>
- <tr>
- <td>Num XHRs</td>
- <td><input type="text" id="numXHRs" value="1"></td>
- </tr>
- <tr>
- <td>Number of iterations</td>
- <td><input type="text" id="numiterations" value="1"></td>
- </tr>
- <tr>
- <td>Number of warm-up iterations</td>
- <td><input type="text" id="numwarmupiterations" value="0"></td>
- </tr>
- <tr>
- <td>Start size</td>
- <td><input type="text" id="startsize" value="10240"></td>
- </tr>
- <tr>
- <td>Stop threshold</td>
- <td><input type="text" id="stopthreshold" value="102400000"></td>
- </tr>
- <tr>
- <td>Minimum total</td>
- <td><input type="text" id="mintotal" value="102400000"></td>
- </tr>
- <tr>
- <td>Multipliers</td>
- <td><input type="text" id="multipliers" value="5, 2"></td>
- </tr>
- </table>
-
- Set data type
- <input type="radio"
- name="datatyperadio"
- id="datatyperadiotext"
- value="text"
- checked><label for="datatyperadiotext">text</label>
- <input type="radio"
- name="datatyperadio"
- id="datatyperadioblob"
- value="blob"
- ><label for="datatyperadioblob">blob</label>
- <input type="radio"
- name="datatyperadio"
- id="datatyperadioarraybuffer"
- value="arraybuffer"
- ><label for="datatyperadioarraybuffer">arraybuffer</label>
-</form>
-
-<div id="log_div">
- <textarea
- id="log" rows="20" style="width: 100%" readonly></textarea>
-</div>
-<div id="summary_div">
- Summary
- <textarea
- id="summary" rows="20" style="width: 100%" readonly></textarea>
-</div>
-
-Note: Effect of RTT and time spent for ArrayBuffer creation in receive benchmarks are not eliminated.
-
-</body>
-</html>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.js b/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.js
deleted file mode 100644
index 233c7cb38..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_benchmark.js
+++ /dev/null
@@ -1,389 +0,0 @@
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the COPYING file or at
-// https://developers.google.com/open-source/licenses/bsd
-
-var isWorker = typeof importScripts !== "undefined";
-
-if (isWorker) {
- // Running on a worker
- importScripts('util.js', 'util_worker.js');
-}
-
-// Namespace for holding globals.
-var benchmark = {};
-benchmark.startTimeInMs = 0;
-
-var xhrs = [];
-
-var timerID = null;
-
-function destroyAllXHRs() {
- for (var i = 0; i < xhrs.length; ++i) {
- xhrs[i].onreadystatechange = null;
- // Abort XHRs if they are not yet DONE state.
- // Calling abort() here (i.e. in onreadystatechange handler)
- // causes "NetworkError" messages in DevTools in sync mode,
- // even if it is after transition to DONE state.
- if (xhrs[i].readyState != XMLHttpRequest.DONE)
- xhrs[i].abort();
- }
- xhrs = [];
- // gc() might be needed for Chrome/Blob
-}
-
-function repeatString(str, count) {
- var data = '';
- var expChunk = str;
- var remain = count;
- while (true) {
- if (remain % 2) {
- data += expChunk;
- remain = (remain - 1) / 2;
- } else {
- remain /= 2;
- }
-
- if (remain == 0)
- break;
-
- expChunk = expChunk + expChunk;
- }
- return data;
-}
-
-function sendBenchmarkStep(size, config) {
- timerID = null;
-
- benchmark.startTimeInMs = null;
- var totalSize = 0;
- var totalReplied = 0;
-
- var onReadyStateChangeHandler = function () {
- if (this.readyState != this.DONE) {
- return;
- }
-
- if (this.status != 200) {
- config.addToLog('Failed (status=' + this.status + ')');
- destroyAllXHRs();
- return;
- }
-
- if (config.verifyData &&
- !verifyAcknowledgement(config, this.response, size)) {
- destroyAllXHRs();
- return;
- }
-
- totalReplied += size;
-
- if (totalReplied < totalSize) {
- return;
- }
-
- if (benchmark.startTimeInMs == null) {
- config.addToLog('startTimeInMs not set');
- destroyAllXHRs();
- return;
- }
-
- calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize);
-
- destroyAllXHRs();
-
- runNextTask(config);
- };
-
- for (var i = 0; i < config.numXHRs; ++i) {
- var xhr = new XMLHttpRequest();
- xhr.onreadystatechange = onReadyStateChangeHandler;
- xhrs.push(xhr);
- }
-
- var dataArray = [];
-
- for (var i = 0; i < xhrs.length; ++i) {
- var data = null;
- if (config.dataType == 'arraybuffer' ||
- config.dataType == 'blob') {
- data = new ArrayBuffer(size);
-
- fillArrayBuffer(data, 0x61);
-
- if (config.dataType == 'blob') {
- data = new Blob([data]);
- }
- } else {
- data = repeatString('a', size);
- }
-
- dataArray.push(data);
- }
-
-
- benchmark.startTimeInMs = getTimeStamp();
- totalSize = size * xhrs.length;
-
- for (var i = 0; i < xhrs.length; ++i) {
- var data = dataArray[i];
- var xhr = xhrs[i];
- xhr.open('POST', config.prefixUrl + '_send', config.async);
- xhr.send(data);
- }
-}
-
-function receiveBenchmarkStep(size, config) {
- timerID = null;
-
- benchmark.startTimeInMs = null;
- var totalSize = 0;
- var totalReplied = 0;
-
- var checkResultAndContinue = function (bytesReceived, verificationResult) {
- if (!verificationResult) {
- config.addToLog('Response verification failed');
- destroyAllXHRs();
- return;
- }
-
- totalReplied += bytesReceived;
-
- if (totalReplied < totalSize) {
- return;
- }
-
- if (benchmark.startTimeInMs == null) {
- config.addToLog('startTimeInMs not set');
- destroyAllXHRs();
- return;
- }
-
- calculateAndLogResult(config, size, benchmark.startTimeInMs, totalSize);
-
- destroyAllXHRs();
-
- runNextTask(config);
- }
-
- var onReadyStateChangeHandler = function () {
- if (this.readyState != this.DONE) {
- return;
- }
-
- if (this.status != 200) {
- config.addToLog('Failed (status=' + this.status + ')');
- destroyAllXHRs();
- return;
- }
-
- var bytesReceived = -1;
- if (this.responseType == 'arraybuffer') {
- bytesReceived = this.response.byteLength;
- } else if (this.responseType == 'blob') {
- bytesReceived = this.response.size;
- } else {
- bytesReceived = this.response.length;
- }
- if (bytesReceived != size) {
- config.addToLog('Expected ' + size +
- 'B but received ' + bytesReceived + 'B');
- destroyAllXHRs();
- return;
- }
-
- if (this.responseType == 'arraybuffer') {
- checkResultAndContinue(bytesReceived,
- !config.verifyData || verifyArrayBuffer(this.response, 0x61));
- } else if (this.responseType == 'blob') {
- if (config.verifyData)
- verifyBlob(config, this.response, 0x61, checkResultAndContinue);
- else
- checkResultAndContinue(bytesReceived, true);
- } else {
- checkResultAndContinue(
- bytesReceived,
- !config.verifyData ||
- this.response == repeatString('a', this.response.length));
- }
- };
-
- for (var i = 0; i < config.numXHRs; ++i) {
- var xhr = new XMLHttpRequest();
- xhr.onreadystatechange = onReadyStateChangeHandler;
- xhrs.push(xhr);
- }
-
- benchmark.startTimeInMs = getTimeStamp();
- totalSize = size * xhrs.length;
-
- for (var i = 0; i < xhrs.length; ++i) {
- var xhr = xhrs[i];
- xhr.open('POST', config.prefixUrl + '_receive', config.async);
- xhr.responseType = config.dataType;
- xhr.send(size + ' none');
- }
-}
-
-
-function getConfigString(config) {
- return '(' + config.dataType +
- ', verifyData=' + config.verifyData +
- ', ' + (isWorker ? 'Worker' : 'Main') +
- ', ' + (config.async ? 'Async' : 'Sync') +
- ', numXHRs=' + config.numXHRs +
- ', numIterations=' + config.numIterations +
- ', numWarmUpIterations=' + config.numWarmUpIterations +
- ')';
-}
-
-function startBenchmark(config) {
- clearTimeout(timerID);
- destroyAllXHRs();
-
- runNextTask(config);
-}
-
-// TODO(hiroshige): the following code is the same as benchmark.html
-// and some of them should be merged into e.g. util.js
-
-var tasks = [];
-
-function runNextTask(config) {
- var task = tasks.shift();
- if (task == undefined) {
- config.addToLog('Finished');
- destroyAllXHRs();
- return;
- }
- timerID = setTimeout(task, 0);
-}
-
-function buildLegendString(config) {
- var legend = ''
- if (config.printSize)
- legend = 'Message size in KiB, Time/message in ms, ';
- legend += 'Speed in kB/s';
- return legend;
-}
-
-function addTasks(config, stepFunc) {
- for (var i = 0;
- i < config.numWarmUpIterations + config.numIterations; ++i) {
- // Ignore the first |config.numWarmUpIterations| iterations.
- if (i == config.numWarmUpIterations)
- addResultClearingTask(config);
-
- var multiplierIndex = 0;
- for (var size = config.startSize;
- size <= config.stopThreshold;
- ++multiplierIndex) {
- var task = stepFunc.bind(
- null,
- size,
- config);
- tasks.push(task);
- size *= config.multipliers[
- multiplierIndex % config.multipliers.length];
- }
- }
-}
-
-function addResultReportingTask(config, title) {
- tasks.push(function(){
- timerID = null;
- config.addToSummary(title);
- reportAverageData(config);
- clearAverageData();
- runNextTask(config);
- });
-}
-
-function addResultClearingTask(config) {
- tasks.push(function(){
- timerID = null;
- clearAverageData();
- runNextTask(config);
- });
-}
-
-// --------------------------------
-
-function sendBenchmark(config) {
- config.addToLog('Send benchmark');
- config.addToLog(buildLegendString(config));
-
- tasks = [];
- clearAverageData();
- addTasks(config, sendBenchmarkStep);
- addResultReportingTask(config, 'Send Benchmark ' + getConfigString(config));
- startBenchmark(config);
-}
-
-function receiveBenchmark(config) {
- config.addToLog('Receive benchmark');
- config.addToLog(buildLegendString(config));
-
- tasks = [];
- clearAverageData();
- addTasks(config, receiveBenchmarkStep);
- addResultReportingTask(config,
- 'Receive Benchmark ' + getConfigString(config));
- startBenchmark(config);
-}
-
-function batchBenchmark(originalConfig) {
- originalConfig.addToLog('Batch benchmark');
-
- tasks = [];
- clearAverageData();
-
- var dataTypes = ['text', 'blob', 'arraybuffer'];
- var stepFuncs = [sendBenchmarkStep, receiveBenchmarkStep];
- var names = ['Send', 'Receive'];
- var async = [true, false];
- for (var i = 0; i < stepFuncs.length; ++i) {
- for (var j = 0; j < dataTypes.length; ++j) {
- for (var k = 0; k < async.length; ++k) {
- var config = cloneConfig(originalConfig);
- config.dataType = dataTypes[j];
- config.async = async[k];
-
- // Receive && Non-Worker && Sync is not supported by the spec
- if (stepFuncs[i] === receiveBenchmarkStep && !isWorker &&
- !config.async)
- continue;
-
- addTasks(config, stepFuncs[i]);
- addResultReportingTask(config,
- names[i] + ' benchmark ' + getConfigString(config));
- }
- }
- }
-
- startBenchmark(config);
-}
-
-
-function stop(config) {
- destroyAllXHRs();
- clearTimeout(timerID);
- timerID = null;
- config.addToLog('Stopped');
-}
-
-onmessage = function (message) {
- var config = message.data.config;
- config.addToLog = workerAddToLog;
- config.addToSummary = workerAddToSummary;
- config.measureValue = workerMeasureValue;
- if (message.data.type === 'sendBenchmark')
- sendBenchmark(config);
- else if (message.data.type === 'receiveBenchmark')
- receiveBenchmark(config);
- else if (message.data.type === 'batchBenchmark')
- batchBenchmark(config);
- else if (message.data.type === 'stop')
- stop(config);
-};
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_event_logger.html b/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_event_logger.html
deleted file mode 100644
index 6983553b8..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/example/xhr_event_logger.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!--
-Copyright 2014 Google Inc. All rights reserved.
-
-Use of this source code is governed by a BSD-style
-license that can be found in the COPYING file or at
-https://developers.google.com/open-source/licenses/bsd
--->
-
-<html>
-<head>
-<title>XHR event logger</title>
-<script src="util_main.js"></script>
-<script>
-var events = [];
-
-function run() {
- events = [];
-
- function pushToLog(type) {
- if (events.length != 0 && type === events[events.length - 1].type) {
- events[events.length - 1].count += 1;
- } else {
- events.push({type: type, count: 1});
- }
- }
-
- var xhr = new XMLHttpRequest();
-
- function getProgressEventDump(e) {
- return '(' + e.lengthComputable + ', ' + e.loaded + ', ' + e.total + ')';
- }
-
- var dumpProgressEvent = getBoolFromCheckBox('dumpprogressevent');
-
- function log(e) {
- var type = e.type;
- if (type === 'readystatechange') {
- type += e.target.readyState;
- }
- if (dumpProgressEvent && (e instanceof ProgressEvent)) {
- type += getProgressEventDump(e);
- }
- pushToLog(type);
- };
-
- function logUpload(e) {
- var type = e.type;
- if (dumpProgressEvent && (e instanceof ProgressEvent)) {
- type += getProgressEventDump(e);
- }
- pushToLog('upload' + type);
- }
-
- if (getBoolFromCheckBox('upload')) {
- var upload = xhr.upload;
- upload.onloadstart = logUpload;
- upload.onprogress = logUpload;
- upload.onabort = logUpload;
- upload.onerror = logUpload;
- upload.onload = logUpload;
- upload.ontimeout = logUpload;
- upload.onloadend = logUpload;
- }
-
- xhr.onreadystatechange = log;
- xhr.onloadstart = log;
- xhr.onprogress = log;
- xhr.onabort = log;
- xhr.onerror = log;
- xhr.onload = log;
- xhr.ontimeout = log;
- xhr.onloadend = log;
-
- xhr.open('POST', '/073be001e10950692ccbf3a2ad21c245_receive',
- getBoolFromCheckBox('async'));
- var size = getIntFromInput('size');
- var chunkedMode = 'none';
- if (getBoolFromCheckBox('chunkedresponse')) {
- chunkedMode = 'chunked';
- }
- xhr.send(size + ' ' + chunkedMode);
-}
-
-function print() {
- var result = '';
- for (var i = 0; i < events.length; ++i) {
- var event = events[i];
- result += event.type + ' * ' + event.count + '\n';
- }
- document.getElementById('log').value = result;
-}
-</script>
-
-<body>
- <textarea id="log" rows="10" cols="40" readonly></textarea>
- <br/>
- Size: <input type="text" id="size" value="65536"><br/>
- <input type="checkbox" id="chunkedresponse">
- <label for="chunkedresponse">Use Chunked T-E for response</label><br/>
- <input type="checkbox" id="upload">
- <label for="upload">Upload progress</label><br/>
- <input type="checkbox" id="dumpprogressevent">
- <label for="dumpprogressevent">
- Dump lengthComputable/loaded/total</label><br/>
- <input type="checkbox" id="async" checked>
- <label for="async">Async</label><br/>
- <input type="button" onclick="run()" value="Run XHR">
- <input type="button" onclick="print()" value="Print log">
-</body>
-</html>
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/__init__.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/__init__.py
deleted file mode 100644
index 70933a220..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/__init__.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""WebSocket extension for Apache HTTP Server.
-
-mod_pywebsocket is a WebSocket extension for Apache HTTP Server
-intended for testing or experimental purposes. mod_python is required.
-
-
-Installation
-============
-
-0. Prepare an Apache HTTP Server for which mod_python is enabled.
-
-1. Specify the following Apache HTTP Server directives to suit your
- configuration.
-
- If mod_pywebsocket is not in the Python path, specify the following.
- <websock_lib> is the directory where mod_pywebsocket is installed.
-
- PythonPath "sys.path+['<websock_lib>']"
-
- Always specify the following. <websock_handlers> is the directory where
- user-written WebSocket handlers are placed.
-
- PythonOption mod_pywebsocket.handler_root <websock_handlers>
- PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
-
- To limit the search for WebSocket handlers to a directory <scan_dir>
- under <websock_handlers>, configure as follows:
-
- PythonOption mod_pywebsocket.handler_scan <scan_dir>
-
- <scan_dir> is useful in saving scan time when <websock_handlers>
- contains many non-WebSocket handler files.
-
- If you want to allow handlers whose canonical path is not under the root
- directory (i.e. symbolic link is in root directory but its target is not),
- configure as follows:
-
- PythonOption mod_pywebsocket.allow_handlers_outside_root_dir On
-
- Example snippet of httpd.conf:
- (mod_pywebsocket is in /websock_lib, WebSocket handlers are in
- /websock_handlers, port is 80 for ws, 443 for wss.)
-
- <IfModule python_module>
- PythonPath "sys.path+['/websock_lib']"
- PythonOption mod_pywebsocket.handler_root /websock_handlers
- PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
- </IfModule>
-
-2. Tune Apache parameters for serving WebSocket. We'd like to note that at
- least TimeOut directive from core features and RequestReadTimeout
- directive from mod_reqtimeout should be modified not to kill connections
- in only a few seconds of idle time.
-
-3. Verify installation. You can use example/console.html to poke the server.
-
-
-Writing WebSocket handlers
-==========================
-
-When a WebSocket request comes in, the resource name
-specified in the handshake is considered as if it is a file path under
-<websock_handlers> and the handler defined in
-<websock_handlers>/<resource_name>_wsh.py is invoked.
-
-For example, if the resource name is /example/chat, the handler defined in
-<websock_handlers>/example/chat_wsh.py is invoked.
-
-A WebSocket handler is composed of the following three functions:
-
- web_socket_do_extra_handshake(request)
- web_socket_transfer_data(request)
- web_socket_passive_closing_handshake(request)
-
-where:
- request: mod_python request.
-
-web_socket_do_extra_handshake is called during the handshake after the
-headers are successfully parsed and WebSocket properties (ws_location,
-ws_origin, and ws_resource) are added to request. A handler
-can reject the request by raising an exception.
-
-A request object has the following properties that you can use during the
-extra handshake (web_socket_do_extra_handshake):
-- ws_resource
-- ws_origin
-- ws_version
-- ws_location (HyBi 00 only)
-- ws_extensions (HyBi 06 and later)
-- ws_deflate (HyBi 06 and later)
-- ws_protocol
-- ws_requested_protocols (HyBi 06 and later)
-
-The last two are a bit tricky. See the next subsection.
-
-
-Subprotocol Negotiation
------------------------
-
-For HyBi 06 and later, ws_protocol is always set to None when
-web_socket_do_extra_handshake is called. If ws_requested_protocols is not
-None, you must choose one subprotocol from this list and set it to
-ws_protocol.
-
-For HyBi 00, when web_socket_do_extra_handshake is called,
-ws_protocol is set to the value given by the client in
-Sec-WebSocket-Protocol header or None if
-such header was not found in the opening handshake request. Finish extra
-handshake with ws_protocol untouched to accept the request subprotocol.
-Then, Sec-WebSocket-Protocol header will be sent to
-the client in response with the same value as requested. Raise an exception
-in web_socket_do_extra_handshake to reject the requested subprotocol.
-
-
-Data Transfer
--------------
-
-web_socket_transfer_data is called after the handshake completed
-successfully. A handler can receive/send messages from/to the client
-using request. mod_pywebsocket.msgutil module provides utilities
-for data transfer.
-
-You can receive a message by the following statement.
-
- message = request.ws_stream.receive_message()
-
-This call blocks until any complete text frame arrives, and the payload data
-of the incoming frame will be stored into message. When you're using IETF
-HyBi 00 or later protocol, receive_message() will return None on receiving
-client-initiated closing handshake. When any error occurs, receive_message()
-will raise some exception.
-
-You can send a message by the following statement.
-
- request.ws_stream.send_message(message)
-
-
-Closing Connection
-------------------
-
-Executing the following statement or just return-ing from
-web_socket_transfer_data cause connection close.
-
- request.ws_stream.close_connection()
-
-close_connection will wait
-for closing handshake acknowledgement coming from the client. When it
-couldn't receive a valid acknowledgement, raises an exception.
-
-web_socket_passive_closing_handshake is called after the server receives
-incoming closing frame from the client peer immediately. You can specify
-code and reason by return values. They are sent as a outgoing closing frame
-from the server. A request object has the following properties that you can
-use in web_socket_passive_closing_handshake.
-- ws_close_code
-- ws_close_reason
-
-
-Threading
----------
-
-A WebSocket handler must be thread-safe if the server (Apache or
-standalone.py) is configured to use threads.
-
-
-Configuring WebSocket Extension Processors
-------------------------------------------
-
-See extensions.py for supported WebSocket extensions. Note that they are
-unstable and their APIs are subject to change substantially.
-
-A request object has these extension processing related attributes.
-
-- ws_requested_extensions:
-
- A list of common.ExtensionParameter instances representing extension
- parameters received from the client in the client's opening handshake.
- You shouldn't modify it manually.
-
-- ws_extensions:
-
- A list of common.ExtensionParameter instances representing extension
- parameters to send back to the client in the server's opening handshake.
- You shouldn't touch it directly. Instead, call methods on extension
- processors.
-
-- ws_extension_processors:
-
- A list of loaded extension processors. Find the processor for the
- extension you want to configure from it, and call its methods.
-"""
-
-
-# vi:sts=4 sw=4 et tw=72
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_base.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_base.py
deleted file mode 100644
index 8235666bb..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_base.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""Base stream class.
-"""
-
-
-# Note: request.connection.write/read are used in this module, even though
-# mod_python document says that they should be used only in connection
-# handlers. Unfortunately, we have no other options. For example,
-# request.write/read are not suitable because they don't allow direct raw bytes
-# writing/reading.
-
-
-import socket
-
-from mod_pywebsocket import util
-
-
-# Exceptions
-
-
-class ConnectionTerminatedException(Exception):
- """This exception will be raised when a connection is terminated
- unexpectedly.
- """
-
- pass
-
-
-class InvalidFrameException(ConnectionTerminatedException):
- """This exception will be raised when we received an invalid frame we
- cannot parse.
- """
-
- pass
-
-
-class BadOperationException(Exception):
- """This exception will be raised when send_message() is called on
- server-terminated connection or receive_message() is called on
- client-terminated connection.
- """
-
- pass
-
-
-class UnsupportedFrameException(Exception):
- """This exception will be raised when we receive a frame with flag, opcode
- we cannot handle. Handlers can just catch and ignore this exception and
- call receive_message() again to continue processing the next frame.
- """
-
- pass
-
-
-class InvalidUTF8Exception(Exception):
- """This exception will be raised when we receive a text frame which
- contains invalid UTF-8 strings.
- """
-
- pass
-
-
-class StreamBase(object):
- """Base stream class."""
-
- def __init__(self, request):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request = request
-
- def _read(self, length):
- """Reads length bytes from connection. In case we catch any exception,
- prepends remote address to the exception message and raise again.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- try:
- read_bytes = self._request.connection.read(length)
- if not read_bytes:
- raise ConnectionTerminatedException(
- 'Receiving %d byte failed. Peer (%r) closed connection' %
- (length, (self._request.connection.remote_addr,)))
- return read_bytes
- except socket.error, e:
- # Catch a socket.error. Because it's not a child class of the
- # IOError prior to Python 2.6, we cannot omit this except clause.
- # Use %s rather than %r for the exception to use human friendly
- # format.
- raise ConnectionTerminatedException(
- 'Receiving %d byte failed. socket.error (%s) occurred' %
- (length, e))
- except IOError, e:
- # Also catch an IOError because mod_python throws it.
- raise ConnectionTerminatedException(
- 'Receiving %d byte failed. IOError (%s) occurred' %
- (length, e))
-
- def _write(self, bytes_to_write):
- """Writes given bytes to connection. In case we catch any exception,
- prepends remote address to the exception message and raise again.
- """
-
- try:
- self._request.connection.write(bytes_to_write)
- except Exception, e:
- util.prepend_message_to_exception(
- 'Failed to send message to %r: ' %
- (self._request.connection.remote_addr,),
- e)
- raise
-
- def receive_bytes(self, length):
- """Receives multiple bytes. Retries read when we couldn't receive the
- specified amount.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- read_bytes = []
- while length > 0:
- new_read_bytes = self._read(length)
- read_bytes.append(new_read_bytes)
- length -= len(new_read_bytes)
- return ''.join(read_bytes)
-
- def _read_until(self, delim_char):
- """Reads bytes until we encounter delim_char. The result will not
- contain delim_char.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- read_bytes = []
- while True:
- ch = self._read(1)
- if ch == delim_char:
- break
- read_bytes.append(ch)
- return ''.join(read_bytes)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hixie75.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hixie75.py
deleted file mode 100644
index 94cf5b31b..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hixie75.py
+++ /dev/null
@@ -1,229 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""This file provides a class for parsing/building frames of the WebSocket
-protocol version HyBi 00 and Hixie 75.
-
-Specification:
-- HyBi 00 http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
-- Hixie 75 http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
-"""
-
-
-from mod_pywebsocket import common
-from mod_pywebsocket._stream_base import BadOperationException
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import InvalidFrameException
-from mod_pywebsocket._stream_base import StreamBase
-from mod_pywebsocket._stream_base import UnsupportedFrameException
-from mod_pywebsocket import util
-
-
-class StreamHixie75(StreamBase):
- """A class for parsing/building frames of the WebSocket protocol version
- HyBi 00 and Hixie 75.
- """
-
- def __init__(self, request, enable_closing_handshake=False):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- enable_closing_handshake: to let StreamHixie75 perform closing
- handshake as specified in HyBi 00, set
- this option to True.
- """
-
- StreamBase.__init__(self, request)
-
- self._logger = util.get_class_logger(self)
-
- self._enable_closing_handshake = enable_closing_handshake
-
- self._request.client_terminated = False
- self._request.server_terminated = False
-
- def send_message(self, message, end=True, binary=False):
- """Send message.
-
- Args:
- message: unicode string to send.
- binary: not used in hixie75.
-
- Raises:
- BadOperationException: when called on a server-terminated
- connection.
- """
-
- if not end:
- raise BadOperationException(
- 'StreamHixie75 doesn\'t support send_message with end=False')
-
- if binary:
- raise BadOperationException(
- 'StreamHixie75 doesn\'t support send_message with binary=True')
-
- if self._request.server_terminated:
- raise BadOperationException(
- 'Requested send_message after sending out a closing handshake')
-
- self._write(''.join(['\x00', message.encode('utf-8'), '\xff']))
-
- def _read_payload_length_hixie75(self):
- """Reads a length header in a Hixie75 version frame with length.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- length = 0
- while True:
- b_str = self._read(1)
- b = ord(b_str)
- length = length * 128 + (b & 0x7f)
- if (b & 0x80) == 0:
- break
- return length
-
- def receive_message(self):
- """Receive a WebSocket frame and return its payload an unicode string.
-
- Returns:
- payload unicode string in a WebSocket frame.
-
- Raises:
- ConnectionTerminatedException: when read returns empty
- string.
- BadOperationException: when called on a client-terminated
- connection.
- """
-
- if self._request.client_terminated:
- raise BadOperationException(
- 'Requested receive_message after receiving a closing '
- 'handshake')
-
- while True:
- # Read 1 byte.
- # mp_conn.read will block if no bytes are available.
- # Timeout is controlled by TimeOut directive of Apache.
- frame_type_str = self.receive_bytes(1)
- frame_type = ord(frame_type_str)
- if (frame_type & 0x80) == 0x80:
- # The payload length is specified in the frame.
- # Read and discard.
- length = self._read_payload_length_hixie75()
- if length > 0:
- _ = self.receive_bytes(length)
- # 5.3 3. 12. if /type/ is 0xFF and /length/ is 0, then set the
- # /client terminated/ flag and abort these steps.
- if not self._enable_closing_handshake:
- continue
-
- if frame_type == 0xFF and length == 0:
- self._request.client_terminated = True
-
- if self._request.server_terminated:
- self._logger.debug(
- 'Received ack for server-initiated closing '
- 'handshake')
- return None
-
- self._logger.debug(
- 'Received client-initiated closing handshake')
-
- self._send_closing_handshake()
- self._logger.debug(
- 'Sent ack for client-initiated closing handshake')
- return None
- else:
- # The payload is delimited with \xff.
- bytes = self._read_until('\xff')
- # The WebSocket protocol section 4.4 specifies that invalid
- # characters must be replaced with U+fffd REPLACEMENT
- # CHARACTER.
- message = bytes.decode('utf-8', 'replace')
- if frame_type == 0x00:
- return message
- # Discard data of other types.
-
- def _send_closing_handshake(self):
- if not self._enable_closing_handshake:
- raise BadOperationException(
- 'Closing handshake is not supported in Hixie 75 protocol')
-
- self._request.server_terminated = True
-
- # 5.3 the server may decide to terminate the WebSocket connection by
- # running through the following steps:
- # 1. send a 0xFF byte and a 0x00 byte to the client to indicate the
- # start of the closing handshake.
- self._write('\xff\x00')
-
- def close_connection(self, unused_code='', unused_reason=''):
- """Closes a WebSocket connection.
-
- Raises:
- ConnectionTerminatedException: when closing handshake was
- not successfull.
- """
-
- if self._request.server_terminated:
- self._logger.debug(
- 'Requested close_connection but server is already terminated')
- return
-
- if not self._enable_closing_handshake:
- self._request.server_terminated = True
- self._logger.debug('Connection closed')
- return
-
- self._send_closing_handshake()
- self._logger.debug('Sent server-initiated closing handshake')
-
- # TODO(ukai): 2. wait until the /client terminated/ flag has been set,
- # or until a server-defined timeout expires.
- #
- # For now, we expect receiving closing handshake right after sending
- # out closing handshake, and if we couldn't receive non-handshake
- # frame, we take it as ConnectionTerminatedException.
- message = self.receive_message()
- if message is not None:
- raise ConnectionTerminatedException(
- 'Didn\'t receive valid ack for closing handshake')
- # TODO: 3. close the WebSocket connection.
- # note: mod_python Connection (mp_conn) doesn't have close method.
-
- def send_ping(self, body):
- raise BadOperationException(
- 'StreamHixie75 doesn\'t support send_ping')
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hybi.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hybi.py
deleted file mode 100644
index a8a49e3c3..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/_stream_hybi.py
+++ /dev/null
@@ -1,887 +0,0 @@
-# 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.
-
-
-"""This file provides classes and helper functions for parsing/building frames
-of the WebSocket protocol (RFC 6455).
-
-Specification:
-http://tools.ietf.org/html/rfc6455
-"""
-
-
-from collections import deque
-import logging
-import os
-import struct
-import time
-
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-from mod_pywebsocket._stream_base import BadOperationException
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import InvalidFrameException
-from mod_pywebsocket._stream_base import InvalidUTF8Exception
-from mod_pywebsocket._stream_base import StreamBase
-from mod_pywebsocket._stream_base import UnsupportedFrameException
-
-
-_NOOP_MASKER = util.NoopMasker()
-
-
-class Frame(object):
-
- def __init__(self, fin=1, rsv1=0, rsv2=0, rsv3=0,
- opcode=None, payload=''):
- self.fin = fin
- self.rsv1 = rsv1
- self.rsv2 = rsv2
- self.rsv3 = rsv3
- self.opcode = opcode
- self.payload = payload
-
-
-# Helper functions made public to be used for writing unittests for WebSocket
-# clients.
-
-
-def create_length_header(length, mask):
- """Creates a length header.
-
- Args:
- length: Frame length. Must be less than 2^63.
- mask: Mask bit. Must be boolean.
-
- Raises:
- ValueError: when bad data is given.
- """
-
- if mask:
- mask_bit = 1 << 7
- else:
- mask_bit = 0
-
- if length < 0:
- raise ValueError('length must be non negative integer')
- elif length <= 125:
- return chr(mask_bit | length)
- elif length < (1 << 16):
- return chr(mask_bit | 126) + struct.pack('!H', length)
- elif length < (1 << 63):
- return chr(mask_bit | 127) + struct.pack('!Q', length)
- else:
- raise ValueError('Payload is too big for one frame')
-
-
-def create_header(opcode, payload_length, fin, rsv1, rsv2, rsv3, mask):
- """Creates a frame header.
-
- Raises:
- Exception: when bad data is given.
- """
-
- if opcode < 0 or 0xf < opcode:
- raise ValueError('Opcode out of range')
-
- if payload_length < 0 or (1 << 63) <= payload_length:
- raise ValueError('payload_length out of range')
-
- if (fin | rsv1 | rsv2 | rsv3) & ~1:
- raise ValueError('FIN bit and Reserved bit parameter must be 0 or 1')
-
- header = ''
-
- first_byte = ((fin << 7)
- | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4)
- | opcode)
- header += chr(first_byte)
- header += create_length_header(payload_length, mask)
-
- return header
-
-
-def _build_frame(header, body, mask):
- if not mask:
- return header + body
-
- masking_nonce = os.urandom(4)
- masker = util.RepeatedXorMasker(masking_nonce)
-
- return header + masking_nonce + masker.mask(body)
-
-
-def _filter_and_format_frame_object(frame, mask, frame_filters):
- for frame_filter in frame_filters:
- frame_filter.filter(frame)
-
- header = create_header(
- frame.opcode, len(frame.payload), frame.fin,
- frame.rsv1, frame.rsv2, frame.rsv3, mask)
- return _build_frame(header, frame.payload, mask)
-
-
-def create_binary_frame(
- message, opcode=common.OPCODE_BINARY, fin=1, mask=False, frame_filters=[]):
- """Creates a simple binary frame with no extension, reserved bit."""
-
- frame = Frame(fin=fin, opcode=opcode, payload=message)
- return _filter_and_format_frame_object(frame, mask, frame_filters)
-
-
-def create_text_frame(
- message, opcode=common.OPCODE_TEXT, fin=1, mask=False, frame_filters=[]):
- """Creates a simple text frame with no extension, reserved bit."""
-
- encoded_message = message.encode('utf-8')
- return create_binary_frame(encoded_message, opcode, fin, mask,
- frame_filters)
-
-
-def parse_frame(receive_bytes, logger=None,
- ws_version=common.VERSION_HYBI_LATEST,
- unmask_receive=True):
- """Parses a frame. Returns a tuple containing each header field and
- payload.
-
- Args:
- receive_bytes: a function that reads frame data from a stream or
- something similar. The function takes length of the bytes to be
- read. The function must raise ConnectionTerminatedException if
- there is not enough data to be read.
- logger: a logging object.
- ws_version: the version of WebSocket protocol.
- unmask_receive: unmask received frames. When received unmasked
- frame, raises InvalidFrameException.
-
- Raises:
- ConnectionTerminatedException: when receive_bytes raises it.
- InvalidFrameException: when the frame contains invalid data.
- """
-
- if not logger:
- logger = logging.getLogger()
-
- logger.log(common.LOGLEVEL_FINE, 'Receive the first 2 octets of a frame')
-
- received = receive_bytes(2)
-
- first_byte = ord(received[0])
- fin = (first_byte >> 7) & 1
- rsv1 = (first_byte >> 6) & 1
- rsv2 = (first_byte >> 5) & 1
- rsv3 = (first_byte >> 4) & 1
- opcode = first_byte & 0xf
-
- second_byte = ord(received[1])
- mask = (second_byte >> 7) & 1
- payload_length = second_byte & 0x7f
-
- logger.log(common.LOGLEVEL_FINE,
- 'FIN=%s, RSV1=%s, RSV2=%s, RSV3=%s, opcode=%s, '
- 'Mask=%s, Payload_length=%s',
- fin, rsv1, rsv2, rsv3, opcode, mask, payload_length)
-
- if (mask == 1) != unmask_receive:
- raise InvalidFrameException(
- 'Mask bit on the received frame did\'nt match masking '
- 'configuration for received frames')
-
- # The HyBi and later specs disallow putting a value in 0x0-0xFFFF
- # into the 8-octet extended payload length field (or 0x0-0xFD in
- # 2-octet field).
- valid_length_encoding = True
- length_encoding_bytes = 1
- if payload_length == 127:
- logger.log(common.LOGLEVEL_FINE,
- 'Receive 8-octet extended payload length')
-
- extended_payload_length = receive_bytes(8)
- payload_length = struct.unpack(
- '!Q', extended_payload_length)[0]
- if payload_length > 0x7FFFFFFFFFFFFFFF:
- raise InvalidFrameException(
- 'Extended payload length >= 2^63')
- if ws_version >= 13 and payload_length < 0x10000:
- valid_length_encoding = False
- length_encoding_bytes = 8
-
- logger.log(common.LOGLEVEL_FINE,
- 'Decoded_payload_length=%s', payload_length)
- elif payload_length == 126:
- logger.log(common.LOGLEVEL_FINE,
- 'Receive 2-octet extended payload length')
-
- extended_payload_length = receive_bytes(2)
- payload_length = struct.unpack(
- '!H', extended_payload_length)[0]
- if ws_version >= 13 and payload_length < 126:
- valid_length_encoding = False
- length_encoding_bytes = 2
-
- logger.log(common.LOGLEVEL_FINE,
- 'Decoded_payload_length=%s', payload_length)
-
- if not valid_length_encoding:
- logger.warning(
- 'Payload length is not encoded using the minimal number of '
- 'bytes (%d is encoded using %d bytes)',
- payload_length,
- length_encoding_bytes)
-
- if mask == 1:
- logger.log(common.LOGLEVEL_FINE, 'Receive mask')
-
- masking_nonce = receive_bytes(4)
- masker = util.RepeatedXorMasker(masking_nonce)
-
- logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce)
- else:
- masker = _NOOP_MASKER
-
- logger.log(common.LOGLEVEL_FINE, 'Receive payload data')
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- receive_start = time.time()
-
- raw_payload_bytes = receive_bytes(payload_length)
-
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- logger.log(
- common.LOGLEVEL_FINE,
- 'Done receiving payload data at %s MB/s',
- payload_length / (time.time() - receive_start) / 1000 / 1000)
- logger.log(common.LOGLEVEL_FINE, 'Unmask payload data')
-
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- unmask_start = time.time()
-
- unmasked_bytes = masker.mask(raw_payload_bytes)
-
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- logger.log(
- common.LOGLEVEL_FINE,
- 'Done unmasking payload data at %s MB/s',
- payload_length / (time.time() - unmask_start) / 1000 / 1000)
-
- return opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3
-
-
-class FragmentedFrameBuilder(object):
- """A stateful class to send a message as fragments."""
-
- def __init__(self, mask, frame_filters=[], encode_utf8=True):
- """Constructs an instance."""
-
- self._mask = mask
- self._frame_filters = frame_filters
- # This is for skipping UTF-8 encoding when building text type frames
- # from compressed data.
- self._encode_utf8 = encode_utf8
-
- self._started = False
-
- # Hold opcode of the first frame in messages to verify types of other
- # frames in the message are all the same.
- self._opcode = common.OPCODE_TEXT
-
- def build(self, payload_data, end, binary):
- if binary:
- frame_type = common.OPCODE_BINARY
- else:
- frame_type = common.OPCODE_TEXT
- if self._started:
- if self._opcode != frame_type:
- raise ValueError('Message types are different in frames for '
- 'the same message')
- opcode = common.OPCODE_CONTINUATION
- else:
- opcode = frame_type
- self._opcode = frame_type
-
- if end:
- self._started = False
- fin = 1
- else:
- self._started = True
- fin = 0
-
- if binary or not self._encode_utf8:
- return create_binary_frame(
- payload_data, opcode, fin, self._mask, self._frame_filters)
- else:
- return create_text_frame(
- payload_data, opcode, fin, self._mask, self._frame_filters)
-
-
-def _create_control_frame(opcode, body, mask, frame_filters):
- frame = Frame(opcode=opcode, payload=body)
-
- for frame_filter in frame_filters:
- frame_filter.filter(frame)
-
- if len(frame.payload) > 125:
- raise BadOperationException(
- 'Payload data size of control frames must be 125 bytes or less')
-
- header = create_header(
- frame.opcode, len(frame.payload), frame.fin,
- frame.rsv1, frame.rsv2, frame.rsv3, mask)
- return _build_frame(header, frame.payload, mask)
-
-
-def create_ping_frame(body, mask=False, frame_filters=[]):
- return _create_control_frame(common.OPCODE_PING, body, mask, frame_filters)
-
-
-def create_pong_frame(body, mask=False, frame_filters=[]):
- return _create_control_frame(common.OPCODE_PONG, body, mask, frame_filters)
-
-
-def create_close_frame(body, mask=False, frame_filters=[]):
- return _create_control_frame(
- common.OPCODE_CLOSE, body, mask, frame_filters)
-
-
-def create_closing_handshake_body(code, reason):
- body = ''
- if code is not None:
- if (code > common.STATUS_USER_PRIVATE_MAX or
- code < common.STATUS_NORMAL_CLOSURE):
- raise BadOperationException('Status code is out of range')
- if (code == common.STATUS_NO_STATUS_RECEIVED or
- code == common.STATUS_ABNORMAL_CLOSURE or
- code == common.STATUS_TLS_HANDSHAKE):
- raise BadOperationException('Status code is reserved pseudo '
- 'code')
- encoded_reason = reason.encode('utf-8')
- body = struct.pack('!H', code) + encoded_reason
- return body
-
-
-class StreamOptions(object):
- """Holds option values to configure Stream objects."""
-
- def __init__(self):
- """Constructs StreamOptions."""
-
- # Filters applied to frames.
- self.outgoing_frame_filters = []
- self.incoming_frame_filters = []
-
- # Filters applied to messages. Control frames are not affected by them.
- self.outgoing_message_filters = []
- self.incoming_message_filters = []
-
- self.encode_text_message_to_utf8 = True
- self.mask_send = False
- self.unmask_receive = True
-
-
-class Stream(StreamBase):
- """A class for parsing/building frames of the WebSocket protocol
- (RFC 6455).
- """
-
- def __init__(self, request, options):
- """Constructs an instance.
-
- Args:
- request: mod_python request.
- """
-
- StreamBase.__init__(self, request)
-
- self._logger = util.get_class_logger(self)
-
- self._options = options
-
- self._request.client_terminated = False
- self._request.server_terminated = False
-
- # Holds body of received fragments.
- self._received_fragments = []
- # Holds the opcode of the first fragment.
- self._original_opcode = None
-
- self._writer = FragmentedFrameBuilder(
- self._options.mask_send, self._options.outgoing_frame_filters,
- self._options.encode_text_message_to_utf8)
-
- self._ping_queue = deque()
-
- def _receive_frame(self):
- """Receives a frame and return data in the frame as a tuple containing
- each header field and payload separately.
-
- Raises:
- ConnectionTerminatedException: when read returns empty
- string.
- InvalidFrameException: when the frame contains invalid data.
- """
-
- def _receive_bytes(length):
- return self.receive_bytes(length)
-
- return parse_frame(receive_bytes=_receive_bytes,
- logger=self._logger,
- ws_version=self._request.ws_version,
- unmask_receive=self._options.unmask_receive)
-
- def _receive_frame_as_frame_object(self):
- opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
-
- return Frame(fin=fin, rsv1=rsv1, rsv2=rsv2, rsv3=rsv3,
- opcode=opcode, payload=unmasked_bytes)
-
- def receive_filtered_frame(self):
- """Receives a frame and applies frame filters and message filters.
- The frame to be received must satisfy following conditions:
- - The frame is not fragmented.
- - The opcode of the frame is TEXT or BINARY.
-
- DO NOT USE this method except for testing purpose.
- """
-
- frame = self._receive_frame_as_frame_object()
- if not frame.fin:
- raise InvalidFrameException(
- 'Segmented frames must not be received via '
- 'receive_filtered_frame()')
- if (frame.opcode != common.OPCODE_TEXT and
- frame.opcode != common.OPCODE_BINARY):
- raise InvalidFrameException(
- 'Control frames must not be received via '
- 'receive_filtered_frame()')
-
- for frame_filter in self._options.incoming_frame_filters:
- frame_filter.filter(frame)
- for message_filter in self._options.incoming_message_filters:
- frame.payload = message_filter.filter(frame.payload)
- return frame
-
- def send_message(self, message, end=True, binary=False):
- """Send message.
-
- Args:
- message: text in unicode or binary in str to send.
- binary: send message as binary frame.
-
- Raises:
- BadOperationException: when called on a server-terminated
- connection or called with inconsistent message type or
- binary parameter.
- """
-
- if self._request.server_terminated:
- raise BadOperationException(
- 'Requested send_message after sending out a closing handshake')
-
- if binary and isinstance(message, unicode):
- raise BadOperationException(
- 'Message for binary frame must be instance of str')
-
- for message_filter in self._options.outgoing_message_filters:
- message = message_filter.filter(message, end, binary)
-
- try:
- # Set this to any positive integer to limit maximum size of data in
- # payload data of each frame.
- MAX_PAYLOAD_DATA_SIZE = -1
-
- if MAX_PAYLOAD_DATA_SIZE <= 0:
- self._write(self._writer.build(message, end, binary))
- return
-
- bytes_written = 0
- while True:
- end_for_this_frame = end
- bytes_to_write = len(message) - bytes_written
- if (MAX_PAYLOAD_DATA_SIZE > 0 and
- bytes_to_write > MAX_PAYLOAD_DATA_SIZE):
- end_for_this_frame = False
- bytes_to_write = MAX_PAYLOAD_DATA_SIZE
-
- frame = self._writer.build(
- message[bytes_written:bytes_written + bytes_to_write],
- end_for_this_frame,
- binary)
- self._write(frame)
-
- bytes_written += bytes_to_write
-
- # This if must be placed here (the end of while block) so that
- # at least one frame is sent.
- if len(message) <= bytes_written:
- break
- except ValueError, e:
- raise BadOperationException(e)
-
- def _get_message_from_frame(self, frame):
- """Gets a message from frame. If the message is composed of fragmented
- frames and the frame is not the last fragmented frame, this method
- returns None. The whole message will be returned when the last
- fragmented frame is passed to this method.
-
- Raises:
- InvalidFrameException: when the frame doesn't match defragmentation
- context, or the frame contains invalid data.
- """
-
- if frame.opcode == common.OPCODE_CONTINUATION:
- if not self._received_fragments:
- if frame.fin:
- raise InvalidFrameException(
- 'Received a termination frame but fragmentation '
- 'not started')
- else:
- raise InvalidFrameException(
- 'Received an intermediate frame but '
- 'fragmentation not started')
-
- if frame.fin:
- # End of fragmentation frame
- self._received_fragments.append(frame.payload)
- message = ''.join(self._received_fragments)
- self._received_fragments = []
- return message
- else:
- # Intermediate frame
- self._received_fragments.append(frame.payload)
- return None
- else:
- if self._received_fragments:
- if frame.fin:
- raise InvalidFrameException(
- 'Received an unfragmented frame without '
- 'terminating existing fragmentation')
- else:
- raise InvalidFrameException(
- 'New fragmentation started without terminating '
- 'existing fragmentation')
-
- if frame.fin:
- # Unfragmented frame
-
- self._original_opcode = frame.opcode
- return frame.payload
- else:
- # Start of fragmentation frame
-
- if common.is_control_opcode(frame.opcode):
- raise InvalidFrameException(
- 'Control frames must not be fragmented')
-
- self._original_opcode = frame.opcode
- self._received_fragments.append(frame.payload)
- return None
-
- def _process_close_message(self, message):
- """Processes close message.
-
- Args:
- message: close message.
-
- Raises:
- InvalidFrameException: when the message is invalid.
- """
-
- self._request.client_terminated = True
-
- # Status code is optional. We can have status reason only if we
- # have status code. Status reason can be empty string. So,
- # allowed cases are
- # - no application data: no code no reason
- # - 2 octet of application data: has code but no reason
- # - 3 or more octet of application data: both code and reason
- if len(message) == 0:
- self._logger.debug('Received close frame (empty body)')
- self._request.ws_close_code = (
- common.STATUS_NO_STATUS_RECEIVED)
- elif len(message) == 1:
- raise InvalidFrameException(
- 'If a close frame has status code, the length of '
- 'status code must be 2 octet')
- elif len(message) >= 2:
- self._request.ws_close_code = struct.unpack(
- '!H', message[0:2])[0]
- self._request.ws_close_reason = message[2:].decode(
- 'utf-8', 'replace')
- self._logger.debug(
- 'Received close frame (code=%d, reason=%r)',
- self._request.ws_close_code,
- self._request.ws_close_reason)
-
- # As we've received a close frame, no more data is coming over the
- # socket. We can now safely close the socket without worrying about
- # RST sending.
-
- if self._request.server_terminated:
- self._logger.debug(
- 'Received ack for server-initiated closing handshake')
- return
-
- self._logger.debug(
- 'Received client-initiated closing handshake')
-
- code = common.STATUS_NORMAL_CLOSURE
- reason = ''
- if hasattr(self._request, '_dispatcher'):
- dispatcher = self._request._dispatcher
- code, reason = dispatcher.passive_closing_handshake(
- self._request)
- if code is None and reason is not None and len(reason) > 0:
- self._logger.warning(
- 'Handler specified reason despite code being None')
- reason = ''
- if reason is None:
- reason = ''
- self._send_closing_handshake(code, reason)
- self._logger.debug(
- 'Acknowledged closing handshake initiated by the peer '
- '(code=%r, reason=%r)', code, reason)
-
- def _process_ping_message(self, message):
- """Processes ping message.
-
- Args:
- message: ping message.
- """
-
- try:
- handler = self._request.on_ping_handler
- if handler:
- handler(self._request, message)
- return
- except AttributeError, e:
- pass
- self._send_pong(message)
-
- def _process_pong_message(self, message):
- """Processes pong message.
-
- Args:
- message: pong message.
- """
-
- # TODO(tyoshino): Add ping timeout handling.
-
- inflight_pings = deque()
-
- while True:
- try:
- expected_body = self._ping_queue.popleft()
- if expected_body == message:
- # inflight_pings contains pings ignored by the
- # other peer. Just forget them.
- self._logger.debug(
- 'Ping %r is acked (%d pings were ignored)',
- expected_body, len(inflight_pings))
- break
- else:
- inflight_pings.append(expected_body)
- except IndexError, e:
- # The received pong was unsolicited pong. Keep the
- # ping queue as is.
- self._ping_queue = inflight_pings
- self._logger.debug('Received a unsolicited pong')
- break
-
- try:
- handler = self._request.on_pong_handler
- if handler:
- handler(self._request, message)
- except AttributeError, e:
- pass
-
- def receive_message(self):
- """Receive a WebSocket frame and return its payload as a text in
- unicode or a binary in str.
-
- Returns:
- payload data of the frame
- - as unicode instance if received text frame
- - as str instance if received binary frame
- or None iff received closing handshake.
- Raises:
- BadOperationException: when called on a client-terminated
- connection.
- ConnectionTerminatedException: when read returns empty
- string.
- InvalidFrameException: when the frame contains invalid
- data.
- UnsupportedFrameException: when the received frame has
- flags, opcode we cannot handle. You can ignore this
- exception and continue receiving the next frame.
- """
-
- if self._request.client_terminated:
- raise BadOperationException(
- 'Requested receive_message after receiving a closing '
- 'handshake')
-
- while True:
- # mp_conn.read will block if no bytes are available.
- # Timeout is controlled by TimeOut directive of Apache.
-
- frame = self._receive_frame_as_frame_object()
-
- # Check the constraint on the payload size for control frames
- # before extension processes the frame.
- # See also http://tools.ietf.org/html/rfc6455#section-5.5
- if (common.is_control_opcode(frame.opcode) and
- len(frame.payload) > 125):
- raise InvalidFrameException(
- 'Payload data size of control frames must be 125 bytes or '
- 'less')
-
- for frame_filter in self._options.incoming_frame_filters:
- frame_filter.filter(frame)
-
- if frame.rsv1 or frame.rsv2 or frame.rsv3:
- raise UnsupportedFrameException(
- 'Unsupported flag is set (rsv = %d%d%d)' %
- (frame.rsv1, frame.rsv2, frame.rsv3))
-
- message = self._get_message_from_frame(frame)
- if message is None:
- continue
-
- for message_filter in self._options.incoming_message_filters:
- message = message_filter.filter(message)
-
- if self._original_opcode == common.OPCODE_TEXT:
- # The WebSocket protocol section 4.4 specifies that invalid
- # characters must be replaced with U+fffd REPLACEMENT
- # CHARACTER.
- try:
- return message.decode('utf-8')
- except UnicodeDecodeError, e:
- raise InvalidUTF8Exception(e)
- elif self._original_opcode == common.OPCODE_BINARY:
- return message
- elif self._original_opcode == common.OPCODE_CLOSE:
- self._process_close_message(message)
- return None
- elif self._original_opcode == common.OPCODE_PING:
- self._process_ping_message(message)
- elif self._original_opcode == common.OPCODE_PONG:
- self._process_pong_message(message)
- else:
- raise UnsupportedFrameException(
- 'Opcode %d is not supported' % self._original_opcode)
-
- def _send_closing_handshake(self, code, reason):
- body = create_closing_handshake_body(code, reason)
- frame = create_close_frame(
- body, mask=self._options.mask_send,
- frame_filters=self._options.outgoing_frame_filters)
-
- self._request.server_terminated = True
-
- self._write(frame)
-
- def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason='',
- wait_response=True):
- """Closes a WebSocket connection.
-
- Args:
- code: Status code for close frame. If code is None, a close
- frame with empty body will be sent.
- reason: string representing close reason.
- wait_response: True when caller want to wait the response.
- Raises:
- BadOperationException: when reason is specified with code None
- or reason is not an instance of both str and unicode.
- """
-
- if self._request.server_terminated:
- self._logger.debug(
- 'Requested close_connection but server is already terminated')
- return
-
- if code is None:
- if reason is not None and len(reason) > 0:
- raise BadOperationException(
- 'close reason must not be specified if code is None')
- reason = ''
- else:
- if not isinstance(reason, str) and not isinstance(reason, unicode):
- raise BadOperationException(
- 'close reason must be an instance of str or unicode')
-
- self._send_closing_handshake(code, reason)
- self._logger.debug(
- 'Initiated closing handshake (code=%r, reason=%r)',
- code, reason)
-
- if (code == common.STATUS_GOING_AWAY or
- code == common.STATUS_PROTOCOL_ERROR) or not wait_response:
- # It doesn't make sense to wait for a close frame if the reason is
- # protocol error or that the server is going away. For some of
- # other reasons, it might not make sense to wait for a close frame,
- # but it's not clear, yet.
- return
-
- # TODO(ukai): 2. wait until the /client terminated/ flag has been set,
- # or until a server-defined timeout expires.
- #
- # For now, we expect receiving closing handshake right after sending
- # out closing handshake.
- message = self.receive_message()
- if message is not None:
- raise ConnectionTerminatedException(
- 'Didn\'t receive valid ack for closing handshake')
- # TODO: 3. close the WebSocket connection.
- # note: mod_python Connection (mp_conn) doesn't have close method.
-
- def send_ping(self, body=''):
- frame = create_ping_frame(
- body,
- self._options.mask_send,
- self._options.outgoing_frame_filters)
- self._write(frame)
-
- self._ping_queue.append(body)
-
- def _send_pong(self, body):
- frame = create_pong_frame(
- body,
- self._options.mask_send,
- self._options.outgoing_frame_filters)
- self._write(frame)
-
- def get_last_received_opcode(self):
- """Returns the opcode of the WebSocket message which the last received
- frame belongs to. The return value is valid iff immediately after
- receive_message call.
- """
-
- return self._original_opcode
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/common.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/common.py
deleted file mode 100644
index 2fc2ead64..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/common.py
+++ /dev/null
@@ -1,303 +0,0 @@
-# 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.
-
-
-"""This file must not depend on any module specific to the WebSocket protocol.
-"""
-
-
-from mod_pywebsocket import http_header_util
-
-
-# Additional log level definitions.
-LOGLEVEL_FINE = 9
-
-# Constants indicating WebSocket protocol version.
-VERSION_HIXIE75 = -1
-VERSION_HYBI00 = 0
-VERSION_HYBI01 = 1
-VERSION_HYBI02 = 2
-VERSION_HYBI03 = 2
-VERSION_HYBI04 = 4
-VERSION_HYBI05 = 5
-VERSION_HYBI06 = 6
-VERSION_HYBI07 = 7
-VERSION_HYBI08 = 8
-VERSION_HYBI09 = 8
-VERSION_HYBI10 = 8
-VERSION_HYBI11 = 8
-VERSION_HYBI12 = 8
-VERSION_HYBI13 = 13
-VERSION_HYBI14 = 13
-VERSION_HYBI15 = 13
-VERSION_HYBI16 = 13
-VERSION_HYBI17 = 13
-
-# Constants indicating WebSocket protocol latest version.
-VERSION_HYBI_LATEST = VERSION_HYBI13
-
-# Port numbers
-DEFAULT_WEB_SOCKET_PORT = 80
-DEFAULT_WEB_SOCKET_SECURE_PORT = 443
-
-# Schemes
-WEB_SOCKET_SCHEME = 'ws'
-WEB_SOCKET_SECURE_SCHEME = 'wss'
-
-# Frame opcodes defined in the spec.
-OPCODE_CONTINUATION = 0x0
-OPCODE_TEXT = 0x1
-OPCODE_BINARY = 0x2
-OPCODE_CLOSE = 0x8
-OPCODE_PING = 0x9
-OPCODE_PONG = 0xa
-
-# UUIDs used by HyBi 04 and later opening handshake and frame masking.
-WEBSOCKET_ACCEPT_UUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
-
-# Opening handshake header names and expected values.
-UPGRADE_HEADER = 'Upgrade'
-WEBSOCKET_UPGRADE_TYPE = 'websocket'
-WEBSOCKET_UPGRADE_TYPE_HIXIE75 = 'WebSocket'
-CONNECTION_HEADER = 'Connection'
-UPGRADE_CONNECTION_TYPE = 'Upgrade'
-HOST_HEADER = 'Host'
-ORIGIN_HEADER = 'Origin'
-SEC_WEBSOCKET_ORIGIN_HEADER = 'Sec-WebSocket-Origin'
-SEC_WEBSOCKET_KEY_HEADER = 'Sec-WebSocket-Key'
-SEC_WEBSOCKET_ACCEPT_HEADER = 'Sec-WebSocket-Accept'
-SEC_WEBSOCKET_VERSION_HEADER = 'Sec-WebSocket-Version'
-SEC_WEBSOCKET_PROTOCOL_HEADER = 'Sec-WebSocket-Protocol'
-SEC_WEBSOCKET_EXTENSIONS_HEADER = 'Sec-WebSocket-Extensions'
-SEC_WEBSOCKET_DRAFT_HEADER = 'Sec-WebSocket-Draft'
-SEC_WEBSOCKET_KEY1_HEADER = 'Sec-WebSocket-Key1'
-SEC_WEBSOCKET_KEY2_HEADER = 'Sec-WebSocket-Key2'
-SEC_WEBSOCKET_LOCATION_HEADER = 'Sec-WebSocket-Location'
-
-# Extensions
-DEFLATE_FRAME_EXTENSION = 'deflate-frame'
-PERMESSAGE_COMPRESSION_EXTENSION = 'permessage-compress'
-PERMESSAGE_DEFLATE_EXTENSION = 'permessage-deflate'
-X_WEBKIT_DEFLATE_FRAME_EXTENSION = 'x-webkit-deflate-frame'
-X_WEBKIT_PERMESSAGE_COMPRESSION_EXTENSION = 'x-webkit-permessage-compress'
-MUX_EXTENSION = 'mux_DO_NOT_USE'
-
-# Status codes
-# Code STATUS_NO_STATUS_RECEIVED, STATUS_ABNORMAL_CLOSURE, and
-# STATUS_TLS_HANDSHAKE are pseudo codes to indicate specific error cases.
-# Could not be used for codes in actual closing frames.
-# Application level errors must use codes in the range
-# STATUS_USER_REGISTERED_BASE to STATUS_USER_PRIVATE_MAX. The codes in the
-# range STATUS_USER_REGISTERED_BASE to STATUS_USER_REGISTERED_MAX are managed
-# by IANA. Usually application must define user protocol level errors in the
-# range STATUS_USER_PRIVATE_BASE to STATUS_USER_PRIVATE_MAX.
-STATUS_NORMAL_CLOSURE = 1000
-STATUS_GOING_AWAY = 1001
-STATUS_PROTOCOL_ERROR = 1002
-STATUS_UNSUPPORTED_DATA = 1003
-STATUS_NO_STATUS_RECEIVED = 1005
-STATUS_ABNORMAL_CLOSURE = 1006
-STATUS_INVALID_FRAME_PAYLOAD_DATA = 1007
-STATUS_POLICY_VIOLATION = 1008
-STATUS_MESSAGE_TOO_BIG = 1009
-STATUS_MANDATORY_EXTENSION = 1010
-STATUS_INTERNAL_ENDPOINT_ERROR = 1011
-STATUS_TLS_HANDSHAKE = 1015
-STATUS_USER_REGISTERED_BASE = 3000
-STATUS_USER_REGISTERED_MAX = 3999
-STATUS_USER_PRIVATE_BASE = 4000
-STATUS_USER_PRIVATE_MAX = 4999
-# Following definitions are aliases to keep compatibility. Applications must
-# not use these obsoleted definitions anymore.
-STATUS_NORMAL = STATUS_NORMAL_CLOSURE
-STATUS_UNSUPPORTED = STATUS_UNSUPPORTED_DATA
-STATUS_CODE_NOT_AVAILABLE = STATUS_NO_STATUS_RECEIVED
-STATUS_ABNORMAL_CLOSE = STATUS_ABNORMAL_CLOSURE
-STATUS_INVALID_FRAME_PAYLOAD = STATUS_INVALID_FRAME_PAYLOAD_DATA
-STATUS_MANDATORY_EXT = STATUS_MANDATORY_EXTENSION
-
-# HTTP status codes
-HTTP_STATUS_BAD_REQUEST = 400
-HTTP_STATUS_FORBIDDEN = 403
-HTTP_STATUS_NOT_FOUND = 404
-
-
-def is_control_opcode(opcode):
- return (opcode >> 3) == 1
-
-
-class ExtensionParameter(object):
- """Holds information about an extension which is exchanged on extension
- negotiation in opening handshake.
- """
-
- def __init__(self, name):
- self._name = name
- # TODO(tyoshino): Change the data structure to more efficient one such
- # as dict when the spec changes to say like
- # - Parameter names must be unique
- # - The order of parameters is not significant
- self._parameters = []
-
- def name(self):
- return self._name
-
- def add_parameter(self, name, value):
- self._parameters.append((name, value))
-
- def get_parameters(self):
- return self._parameters
-
- def get_parameter_names(self):
- return [name for name, unused_value in self._parameters]
-
- def has_parameter(self, name):
- for param_name, param_value in self._parameters:
- if param_name == name:
- return True
- return False
-
- def get_parameter_value(self, name):
- for param_name, param_value in self._parameters:
- if param_name == name:
- return param_value
-
-
-class ExtensionParsingException(Exception):
- def __init__(self, name):
- super(ExtensionParsingException, self).__init__(name)
-
-
-def _parse_extension_param(state, definition):
- param_name = http_header_util.consume_token(state)
-
- if param_name is None:
- raise ExtensionParsingException('No valid parameter name found')
-
- http_header_util.consume_lwses(state)
-
- if not http_header_util.consume_string(state, '='):
- definition.add_parameter(param_name, None)
- return
-
- http_header_util.consume_lwses(state)
-
- # TODO(tyoshino): Add code to validate that parsed param_value is token
- param_value = http_header_util.consume_token_or_quoted_string(state)
- if param_value is None:
- raise ExtensionParsingException(
- 'No valid parameter value found on the right-hand side of '
- 'parameter %r' % param_name)
-
- definition.add_parameter(param_name, param_value)
-
-
-def _parse_extension(state):
- extension_token = http_header_util.consume_token(state)
- if extension_token is None:
- return None
-
- extension = ExtensionParameter(extension_token)
-
- while True:
- http_header_util.consume_lwses(state)
-
- if not http_header_util.consume_string(state, ';'):
- break
-
- http_header_util.consume_lwses(state)
-
- try:
- _parse_extension_param(state, extension)
- except ExtensionParsingException, e:
- raise ExtensionParsingException(
- 'Failed to parse parameter for %r (%r)' %
- (extension_token, e))
-
- return extension
-
-
-def parse_extensions(data):
- """Parses Sec-WebSocket-Extensions header value returns a list of
- ExtensionParameter objects.
-
- Leading LWSes must be trimmed.
- """
-
- state = http_header_util.ParsingState(data)
-
- extension_list = []
- while True:
- extension = _parse_extension(state)
- if extension is not None:
- extension_list.append(extension)
-
- http_header_util.consume_lwses(state)
-
- if http_header_util.peek(state) is None:
- break
-
- if not http_header_util.consume_string(state, ','):
- raise ExtensionParsingException(
- 'Failed to parse Sec-WebSocket-Extensions header: '
- 'Expected a comma but found %r' %
- http_header_util.peek(state))
-
- http_header_util.consume_lwses(state)
-
- if len(extension_list) == 0:
- raise ExtensionParsingException(
- 'No valid extension entry found')
-
- return extension_list
-
-
-def format_extension(extension):
- """Formats an ExtensionParameter object."""
-
- formatted_params = [extension.name()]
- for param_name, param_value in extension.get_parameters():
- if param_value is None:
- formatted_params.append(param_name)
- else:
- quoted_value = http_header_util.quote_if_necessary(param_value)
- formatted_params.append('%s=%s' % (param_name, quoted_value))
- return '; '.join(formatted_params)
-
-
-def format_extensions(extension_list):
- """Formats a list of ExtensionParameter objects."""
-
- formatted_extension_list = []
- for extension in extension_list:
- formatted_extension_list.append(format_extension(extension))
- return ', '.join(formatted_extension_list)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/dispatch.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/dispatch.py
deleted file mode 100644
index 96c91e0c9..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/dispatch.py
+++ /dev/null
@@ -1,393 +0,0 @@
-# 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.
-
-
-"""Dispatch WebSocket request.
-"""
-
-
-import logging
-import os
-import re
-
-from mod_pywebsocket import common
-from mod_pywebsocket import handshake
-from mod_pywebsocket import msgutil
-from mod_pywebsocket import mux
-from mod_pywebsocket import stream
-from mod_pywebsocket import util
-
-
-_SOURCE_PATH_PATTERN = re.compile(r'(?i)_wsh\.py$')
-_SOURCE_SUFFIX = '_wsh.py'
-_DO_EXTRA_HANDSHAKE_HANDLER_NAME = 'web_socket_do_extra_handshake'
-_TRANSFER_DATA_HANDLER_NAME = 'web_socket_transfer_data'
-_PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME = (
- 'web_socket_passive_closing_handshake')
-
-
-class DispatchException(Exception):
- """Exception in dispatching WebSocket request."""
-
- def __init__(self, name, status=common.HTTP_STATUS_NOT_FOUND):
- super(DispatchException, self).__init__(name)
- self.status = status
-
-
-def _default_passive_closing_handshake_handler(request):
- """Default web_socket_passive_closing_handshake handler."""
-
- return common.STATUS_NORMAL_CLOSURE, ''
-
-
-def _normalize_path(path):
- """Normalize path.
-
- Args:
- path: the path to normalize.
-
- Path is converted to the absolute path.
- The input path can use either '\\' or '/' as the separator.
- The normalized path always uses '/' regardless of the platform.
- """
-
- path = path.replace('\\', os.path.sep)
- path = os.path.realpath(path)
- path = path.replace('\\', '/')
- return path
-
-
-def _create_path_to_resource_converter(base_dir):
- """Returns a function that converts the path of a WebSocket handler source
- file to a resource string by removing the path to the base directory from
- its head, removing _SOURCE_SUFFIX from its tail, and replacing path
- separators in it with '/'.
-
- Args:
- base_dir: the path to the base directory.
- """
-
- base_dir = _normalize_path(base_dir)
-
- base_len = len(base_dir)
- suffix_len = len(_SOURCE_SUFFIX)
-
- def converter(path):
- if not path.endswith(_SOURCE_SUFFIX):
- return None
- # _normalize_path must not be used because resolving symlink breaks
- # following path check.
- path = path.replace('\\', '/')
- if not path.startswith(base_dir):
- return None
- return path[base_len:-suffix_len]
-
- return converter
-
-
-def _enumerate_handler_file_paths(directory):
- """Returns a generator that enumerates WebSocket Handler source file names
- in the given directory.
- """
-
- for root, unused_dirs, files in os.walk(directory):
- for base in files:
- path = os.path.join(root, base)
- if _SOURCE_PATH_PATTERN.search(path):
- yield path
-
-
-class _HandlerSuite(object):
- """A handler suite holder class."""
-
- def __init__(self, do_extra_handshake, transfer_data,
- passive_closing_handshake):
- self.do_extra_handshake = do_extra_handshake
- self.transfer_data = transfer_data
- self.passive_closing_handshake = passive_closing_handshake
-
-
-def _source_handler_file(handler_definition):
- """Source a handler definition string.
-
- Args:
- handler_definition: a string containing Python statements that define
- handler functions.
- """
-
- global_dic = {}
- try:
- exec handler_definition in global_dic
- except Exception:
- raise DispatchException('Error in sourcing handler:' +
- util.get_stack_trace())
- passive_closing_handshake_handler = None
- try:
- passive_closing_handshake_handler = _extract_handler(
- global_dic, _PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME)
- except Exception:
- passive_closing_handshake_handler = (
- _default_passive_closing_handshake_handler)
- return _HandlerSuite(
- _extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME),
- _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME),
- passive_closing_handshake_handler)
-
-
-def _extract_handler(dic, name):
- """Extracts a callable with the specified name from the given dictionary
- dic.
- """
-
- if name not in dic:
- raise DispatchException('%s is not defined.' % name)
- handler = dic[name]
- if not callable(handler):
- raise DispatchException('%s is not callable.' % name)
- return handler
-
-
-class Dispatcher(object):
- """Dispatches WebSocket requests.
-
- This class maintains a map from resource name to handlers.
- """
-
- def __init__(
- self, root_dir, scan_dir=None,
- allow_handlers_outside_root_dir=True):
- """Construct an instance.
-
- Args:
- root_dir: The directory where handler definition files are
- placed.
- scan_dir: The directory where handler definition files are
- searched. scan_dir must be a directory under root_dir,
- including root_dir itself. If scan_dir is None,
- root_dir is used as scan_dir. scan_dir can be useful
- in saving scan time when root_dir contains many
- subdirectories.
- allow_handlers_outside_root_dir: Scans handler files even if their
- canonical path is not under root_dir.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._handler_suite_map = {}
- self._source_warnings = []
- if scan_dir is None:
- scan_dir = root_dir
- if not os.path.realpath(scan_dir).startswith(
- os.path.realpath(root_dir)):
- raise DispatchException('scan_dir:%s must be a directory under '
- 'root_dir:%s.' % (scan_dir, root_dir))
- self._source_handler_files_in_dir(
- root_dir, scan_dir, allow_handlers_outside_root_dir)
-
- def add_resource_path_alias(self,
- alias_resource_path, existing_resource_path):
- """Add resource path alias.
-
- Once added, request to alias_resource_path would be handled by
- handler registered for existing_resource_path.
-
- Args:
- alias_resource_path: alias resource path
- existing_resource_path: existing resource path
- """
- try:
- handler_suite = self._handler_suite_map[existing_resource_path]
- self._handler_suite_map[alias_resource_path] = handler_suite
- except KeyError:
- raise DispatchException('No handler for: %r' %
- existing_resource_path)
-
- def source_warnings(self):
- """Return warnings in sourcing handlers."""
-
- return self._source_warnings
-
- def do_extra_handshake(self, request):
- """Do extra checking in WebSocket handshake.
-
- Select a handler based on request.uri and call its
- web_socket_do_extra_handshake function.
-
- Args:
- request: mod_python request.
-
- Raises:
- DispatchException: when handler was not found
- AbortedByUserException: when user handler abort connection
- HandshakeException: when opening handshake failed
- """
-
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- raise DispatchException('No handler for: %r' % request.ws_resource)
- do_extra_handshake_ = handler_suite.do_extra_handshake
- try:
- do_extra_handshake_(request)
- except handshake.AbortedByUserException, e:
- # Re-raise to tell the caller of this function to finish this
- # connection without sending any error.
- self._logger.debug('%s', util.get_stack_trace())
- raise
- except Exception, e:
- util.prepend_message_to_exception(
- '%s raised exception for %s: ' % (
- _DO_EXTRA_HANDSHAKE_HANDLER_NAME,
- request.ws_resource),
- e)
- raise handshake.HandshakeException(e, common.HTTP_STATUS_FORBIDDEN)
-
- def transfer_data(self, request):
- """Let a handler transfer_data with a WebSocket client.
-
- Select a handler based on request.ws_resource and call its
- web_socket_transfer_data function.
-
- Args:
- request: mod_python request.
-
- Raises:
- DispatchException: when handler was not found
- AbortedByUserException: when user handler abort connection
- """
-
- # TODO(tyoshino): Terminate underlying TCP connection if possible.
- try:
- if mux.use_mux(request):
- mux.start(request, self)
- else:
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- raise DispatchException('No handler for: %r' %
- request.ws_resource)
- transfer_data_ = handler_suite.transfer_data
- transfer_data_(request)
-
- if not request.server_terminated:
- request.ws_stream.close_connection()
- # Catch non-critical exceptions the handler didn't handle.
- except handshake.AbortedByUserException, e:
- self._logger.debug('%s', util.get_stack_trace())
- raise
- except msgutil.BadOperationException, e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(
- common.STATUS_INTERNAL_ENDPOINT_ERROR)
- except msgutil.InvalidFrameException, e:
- # InvalidFrameException must be caught before
- # ConnectionTerminatedException that catches InvalidFrameException.
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_PROTOCOL_ERROR)
- except msgutil.UnsupportedFrameException, e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_UNSUPPORTED_DATA)
- except stream.InvalidUTF8Exception, e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(
- common.STATUS_INVALID_FRAME_PAYLOAD_DATA)
- except msgutil.ConnectionTerminatedException, e:
- self._logger.debug('%s', e)
- except Exception, e:
- # Any other exceptions are forwarded to the caller of this
- # function.
- util.prepend_message_to_exception(
- '%s raised exception for %s: ' % (
- _TRANSFER_DATA_HANDLER_NAME, request.ws_resource),
- e)
- raise
-
- def passive_closing_handshake(self, request):
- """Prepare code and reason for responding client initiated closing
- handshake.
- """
-
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- return _default_passive_closing_handshake_handler(request)
- return handler_suite.passive_closing_handshake(request)
-
- def get_handler_suite(self, resource):
- """Retrieves two handlers (one for extra handshake processing, and one
- for data transfer) for the given request as a HandlerSuite object.
- """
-
- fragment = None
- if '#' in resource:
- resource, fragment = resource.split('#', 1)
- if '?' in resource:
- resource = resource.split('?', 1)[0]
- handler_suite = self._handler_suite_map.get(resource)
- if handler_suite and fragment:
- raise DispatchException('Fragment identifiers MUST NOT be used on '
- 'WebSocket URIs',
- common.HTTP_STATUS_BAD_REQUEST)
- return handler_suite
-
- def _source_handler_files_in_dir(
- self, root_dir, scan_dir, allow_handlers_outside_root_dir):
- """Source all the handler source files in the scan_dir directory.
-
- The resource path is determined relative to root_dir.
- """
-
- # We build a map from resource to handler code assuming that there's
- # only one path from root_dir to scan_dir and it can be obtained by
- # comparing realpath of them.
-
- # Here we cannot use abspath. See
- # https://bugs.webkit.org/show_bug.cgi?id=31603
-
- convert = _create_path_to_resource_converter(root_dir)
- scan_realpath = os.path.realpath(scan_dir)
- root_realpath = os.path.realpath(root_dir)
- for path in _enumerate_handler_file_paths(scan_realpath):
- if (not allow_handlers_outside_root_dir and
- (not os.path.realpath(path).startswith(root_realpath))):
- self._logger.debug(
- 'Canonical path of %s is not under root directory' %
- path)
- continue
- try:
- handler_suite = _source_handler_file(open(path).read())
- except DispatchException, e:
- self._source_warnings.append('%s: %s' % (path, e))
- continue
- resource = convert(path)
- if resource is None:
- self._logger.debug(
- 'Path to resource conversion on %s failed' % path)
- else:
- self._handler_suite_map[convert(path)] = handler_suite
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/extensions.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/extensions.py
deleted file mode 100644
index 49a9fdcf9..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/extensions.py
+++ /dev/null
@@ -1,885 +0,0 @@
-# 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 util
-from mod_pywebsocket.http_header_util import quote_if_necessary
-
-
-# The list of available server side extension processor classes.
-_available_processors = {}
-_compression_extension_names = []
-
-
-class ExtensionProcessorInterface(object):
-
- def __init__(self, request):
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._active = True
-
- def request(self):
- return self._request
-
- def name(self):
- return None
-
- def check_consistency_with_other_processors(self, processors):
- pass
-
- def set_active(self, active):
- self._active = active
-
- def is_active(self):
- return self._active
-
- def _get_extension_response_internal(self):
- return None
-
- def get_extension_response(self):
- if not self._active:
- self._logger.debug('Extension %s is deactivated', self.name())
- return None
-
- response = self._get_extension_response_internal()
- if response is None:
- self._active = False
- return response
-
- def _setup_stream_options_internal(self, stream_options):
- pass
-
- def setup_stream_options(self, stream_options):
- if self._active:
- self._setup_stream_options_internal(stream_options)
-
-
-def _log_outgoing_compression_ratio(
- logger, original_bytes, filtered_bytes, average_ratio):
- # Print inf when ratio is not available.
- ratio = float('inf')
- if original_bytes != 0:
- ratio = float(filtered_bytes) / original_bytes
-
- logger.debug('Outgoing compression ratio: %f (average: %f)' %
- (ratio, average_ratio))
-
-
-def _log_incoming_compression_ratio(
- logger, received_bytes, filtered_bytes, average_ratio):
- # Print inf when ratio is not available.
- ratio = float('inf')
- if filtered_bytes != 0:
- ratio = float(received_bytes) / filtered_bytes
-
- logger.debug('Incoming compression ratio: %f (average: %f)' %
- (ratio, average_ratio))
-
-
-def _parse_window_bits(bits):
- """Return parsed integer value iff the given string conforms to the
- grammar of the window bits extension parameters.
- """
-
- if bits is None:
- raise ValueError('Value is required')
-
- # For non integer values such as "10.0", ValueError will be raised.
- int_bits = int(bits)
-
- # First condition is to drop leading zero case e.g. "08".
- if bits != str(int_bits) or int_bits < 8 or int_bits > 15:
- raise ValueError('Invalid value: %r' % bits)
-
- return int_bits
-
-
-class _AverageRatioCalculator(object):
- """Stores total bytes of original and result data, and calculates average
- result / original ratio.
- """
-
- def __init__(self):
- self._total_original_bytes = 0
- self._total_result_bytes = 0
-
- def add_original_bytes(self, value):
- self._total_original_bytes += value
-
- def add_result_bytes(self, value):
- self._total_result_bytes += value
-
- def get_average_ratio(self):
- if self._total_original_bytes != 0:
- return (float(self._total_result_bytes) /
- self._total_original_bytes)
- else:
- return float('inf')
-
-
-class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
- """deflate-frame extension processor.
-
- Specification:
- http://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate
- """
-
- _WINDOW_BITS_PARAM = 'max_window_bits'
- _NO_CONTEXT_TAKEOVER_PARAM = 'no_context_takeover'
-
- def __init__(self, request):
- ExtensionProcessorInterface.__init__(self, request)
- self._logger = util.get_class_logger(self)
-
- self._response_window_bits = None
- self._response_no_context_takeover = False
- self._bfinal = False
-
- # Calculates
- # (Total outgoing bytes supplied to this filter) /
- # (Total bytes sent to the network after applying this filter)
- self._outgoing_average_ratio_calculator = _AverageRatioCalculator()
-
- # Calculates
- # (Total bytes received from the network) /
- # (Total incoming bytes obtained after applying this filter)
- self._incoming_average_ratio_calculator = _AverageRatioCalculator()
-
- def name(self):
- return common.DEFLATE_FRAME_EXTENSION
-
- def _get_extension_response_internal(self):
- # Any unknown parameter will be just ignored.
-
- window_bits = None
- if self._request.has_parameter(self._WINDOW_BITS_PARAM):
- window_bits = self._request.get_parameter_value(
- self._WINDOW_BITS_PARAM)
- try:
- window_bits = _parse_window_bits(window_bits)
- except ValueError, e:
- return None
-
- no_context_takeover = self._request.has_parameter(
- self._NO_CONTEXT_TAKEOVER_PARAM)
- if (no_context_takeover and
- self._request.get_parameter_value(
- self._NO_CONTEXT_TAKEOVER_PARAM) is not None):
- return None
-
- self._rfc1979_deflater = util._RFC1979Deflater(
- window_bits, no_context_takeover)
-
- self._rfc1979_inflater = util._RFC1979Inflater()
-
- self._compress_outgoing = True
-
- response = common.ExtensionParameter(self._request.name())
-
- if self._response_window_bits is not None:
- response.add_parameter(
- self._WINDOW_BITS_PARAM, str(self._response_window_bits))
- if self._response_no_context_takeover:
- response.add_parameter(
- self._NO_CONTEXT_TAKEOVER_PARAM, None)
-
- self._logger.debug(
- 'Enable %s extension ('
- 'request: window_bits=%s; no_context_takeover=%r, '
- 'response: window_wbits=%s; no_context_takeover=%r)' %
- (self._request.name(),
- window_bits,
- no_context_takeover,
- self._response_window_bits,
- self._response_no_context_takeover))
-
- return response
-
- def _setup_stream_options_internal(self, stream_options):
-
- class _OutgoingFilter(object):
-
- def __init__(self, parent):
- self._parent = parent
-
- def filter(self, frame):
- self._parent._outgoing_filter(frame)
-
- class _IncomingFilter(object):
-
- def __init__(self, parent):
- self._parent = parent
-
- def filter(self, frame):
- self._parent._incoming_filter(frame)
-
- stream_options.outgoing_frame_filters.append(
- _OutgoingFilter(self))
- stream_options.incoming_frame_filters.insert(
- 0, _IncomingFilter(self))
-
- def set_response_window_bits(self, value):
- self._response_window_bits = value
-
- def set_response_no_context_takeover(self, value):
- self._response_no_context_takeover = value
-
- def set_bfinal(self, value):
- self._bfinal = value
-
- def enable_outgoing_compression(self):
- self._compress_outgoing = True
-
- def disable_outgoing_compression(self):
- self._compress_outgoing = False
-
- def _outgoing_filter(self, frame):
- """Transform outgoing frames. This method is called only by
- an _OutgoingFilter instance.
- """
-
- original_payload_size = len(frame.payload)
- self._outgoing_average_ratio_calculator.add_original_bytes(
- original_payload_size)
-
- if (not self._compress_outgoing or
- common.is_control_opcode(frame.opcode)):
- self._outgoing_average_ratio_calculator.add_result_bytes(
- original_payload_size)
- return
-
- frame.payload = self._rfc1979_deflater.filter(
- frame.payload, bfinal=self._bfinal)
- frame.rsv1 = 1
-
- filtered_payload_size = len(frame.payload)
- self._outgoing_average_ratio_calculator.add_result_bytes(
- filtered_payload_size)
-
- _log_outgoing_compression_ratio(
- self._logger,
- original_payload_size,
- filtered_payload_size,
- self._outgoing_average_ratio_calculator.get_average_ratio())
-
- def _incoming_filter(self, frame):
- """Transform incoming frames. This method is called only by
- an _IncomingFilter instance.
- """
-
- received_payload_size = len(frame.payload)
- self._incoming_average_ratio_calculator.add_result_bytes(
- received_payload_size)
-
- if frame.rsv1 != 1 or common.is_control_opcode(frame.opcode):
- self._incoming_average_ratio_calculator.add_original_bytes(
- received_payload_size)
- return
-
- frame.payload = self._rfc1979_inflater.filter(frame.payload)
- frame.rsv1 = 0
-
- filtered_payload_size = len(frame.payload)
- self._incoming_average_ratio_calculator.add_original_bytes(
- filtered_payload_size)
-
- _log_incoming_compression_ratio(
- self._logger,
- received_payload_size,
- filtered_payload_size,
- self._incoming_average_ratio_calculator.get_average_ratio())
-
-
-_available_processors[common.DEFLATE_FRAME_EXTENSION] = (
- DeflateFrameExtensionProcessor)
-_compression_extension_names.append(common.DEFLATE_FRAME_EXTENSION)
-
-_available_processors[common.X_WEBKIT_DEFLATE_FRAME_EXTENSION] = (
- DeflateFrameExtensionProcessor)
-_compression_extension_names.append(common.X_WEBKIT_DEFLATE_FRAME_EXTENSION)
-
-
-def _parse_compression_method(data):
- """Parses the value of "method" extension parameter."""
-
- return common.parse_extensions(data)
-
-
-def _create_accepted_method_desc(method_name, method_params):
- """Creates accepted-method-desc from given method name and parameters"""
-
- extension = common.ExtensionParameter(method_name)
- for name, value in method_params:
- extension.add_parameter(name, value)
- return common.format_extension(extension)
-
-
-class CompressionExtensionProcessorBase(ExtensionProcessorInterface):
- """Base class for perframe-compress and permessage-compress extension."""
-
- _METHOD_PARAM = 'method'
-
- def __init__(self, request):
- ExtensionProcessorInterface.__init__(self, request)
- self._logger = util.get_class_logger(self)
- self._compression_method_name = None
- self._compression_processor = None
- self._compression_processor_hook = None
-
- def name(self):
- return ''
-
- def _lookup_compression_processor(self, method_desc):
- return None
-
- def _get_compression_processor_response(self):
- """Looks up the compression processor based on the self._request and
- returns the compression processor's response.
- """
-
- method_list = self._request.get_parameter_value(self._METHOD_PARAM)
- if method_list is None:
- return None
- methods = _parse_compression_method(method_list)
- if methods is None:
- return None
- comression_processor = None
- # The current implementation tries only the first method that matches
- # supported algorithm. Following methods aren't tried even if the
- # first one is rejected.
- # TODO(bashi): Need to clarify this behavior.
- for method_desc in methods:
- compression_processor = self._lookup_compression_processor(
- method_desc)
- if compression_processor is not None:
- self._compression_method_name = method_desc.name()
- break
- if compression_processor is None:
- return None
-
- if self._compression_processor_hook:
- self._compression_processor_hook(compression_processor)
-
- processor_response = compression_processor.get_extension_response()
- if processor_response is None:
- return None
- self._compression_processor = compression_processor
- return processor_response
-
- def _get_extension_response_internal(self):
- processor_response = self._get_compression_processor_response()
- if processor_response is None:
- return None
-
- response = common.ExtensionParameter(self._request.name())
- accepted_method_desc = _create_accepted_method_desc(
- self._compression_method_name,
- processor_response.get_parameters())
- response.add_parameter(self._METHOD_PARAM, accepted_method_desc)
- self._logger.debug(
- 'Enable %s extension (method: %s)' %
- (self._request.name(), self._compression_method_name))
- return response
-
- def _setup_stream_options_internal(self, stream_options):
- if self._compression_processor is None:
- return
- self._compression_processor.setup_stream_options(stream_options)
-
- def set_compression_processor_hook(self, hook):
- self._compression_processor_hook = hook
-
- def get_compression_processor(self):
- return self._compression_processor
-
-
-class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface):
- """permessage-deflate extension processor. It's also used for
- permessage-compress extension when the deflate method is chosen.
-
- Specification:
- http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-08
- """
-
- _SERVER_MAX_WINDOW_BITS_PARAM = 'server_max_window_bits'
- _SERVER_NO_CONTEXT_TAKEOVER_PARAM = 'server_no_context_takeover'
- _CLIENT_MAX_WINDOW_BITS_PARAM = 'client_max_window_bits'
- _CLIENT_NO_CONTEXT_TAKEOVER_PARAM = 'client_no_context_takeover'
-
- def __init__(self, request, draft08=True):
- """Construct PerMessageDeflateExtensionProcessor
-
- Args:
- draft08: Follow the constraints on the parameters that were not
- specified for permessage-compress but are specified for
- permessage-deflate as on
- draft-ietf-hybi-permessage-compression-08.
- """
-
- ExtensionProcessorInterface.__init__(self, request)
- self._logger = util.get_class_logger(self)
-
- self._preferred_client_max_window_bits = None
- self._client_no_context_takeover = False
-
- self._draft08 = draft08
-
- def name(self):
- return 'deflate'
-
- def _get_extension_response_internal(self):
- if self._draft08:
- for name in self._request.get_parameter_names():
- if name not in [self._SERVER_MAX_WINDOW_BITS_PARAM,
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
- self._CLIENT_MAX_WINDOW_BITS_PARAM]:
- self._logger.debug('Unknown parameter: %r', name)
- return None
- else:
- # Any unknown parameter will be just ignored.
- pass
-
- server_max_window_bits = None
- if self._request.has_parameter(self._SERVER_MAX_WINDOW_BITS_PARAM):
- server_max_window_bits = self._request.get_parameter_value(
- self._SERVER_MAX_WINDOW_BITS_PARAM)
- try:
- server_max_window_bits = _parse_window_bits(
- server_max_window_bits)
- except ValueError, e:
- self._logger.debug('Bad %s parameter: %r',
- self._SERVER_MAX_WINDOW_BITS_PARAM,
- e)
- return None
-
- server_no_context_takeover = self._request.has_parameter(
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM)
- if (server_no_context_takeover and
- self._request.get_parameter_value(
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM) is not None):
- self._logger.debug('%s parameter must not have a value: %r',
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
- server_no_context_takeover)
- return None
-
- # client_max_window_bits from a client indicates whether the client can
- # accept client_max_window_bits from a server or not.
- client_client_max_window_bits = self._request.has_parameter(
- self._CLIENT_MAX_WINDOW_BITS_PARAM)
- if (self._draft08 and
- client_client_max_window_bits and
- self._request.get_parameter_value(
- self._CLIENT_MAX_WINDOW_BITS_PARAM) is not None):
- self._logger.debug('%s parameter must not have a value in a '
- 'client\'s opening handshake: %r',
- self._CLIENT_MAX_WINDOW_BITS_PARAM,
- client_client_max_window_bits)
- return None
-
- self._rfc1979_deflater = util._RFC1979Deflater(
- server_max_window_bits, server_no_context_takeover)
-
- # Note that we prepare for incoming messages compressed with window
- # bits upto 15 regardless of the client_max_window_bits value to be
- # sent to the client.
- self._rfc1979_inflater = util._RFC1979Inflater()
-
- self._framer = _PerMessageDeflateFramer(
- server_max_window_bits, server_no_context_takeover)
- self._framer.set_bfinal(False)
- self._framer.set_compress_outgoing_enabled(True)
-
- response = common.ExtensionParameter(self._request.name())
-
- if server_max_window_bits is not None:
- response.add_parameter(
- self._SERVER_MAX_WINDOW_BITS_PARAM,
- str(server_max_window_bits))
-
- if server_no_context_takeover:
- response.add_parameter(
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM, None)
-
- if self._preferred_client_max_window_bits is not None:
- if self._draft08 and not client_client_max_window_bits:
- self._logger.debug('Processor is configured to use %s but '
- 'the client cannot accept it',
- self._CLIENT_MAX_WINDOW_BITS_PARAM)
- return None
- response.add_parameter(
- self._CLIENT_MAX_WINDOW_BITS_PARAM,
- str(self._preferred_client_max_window_bits))
-
- if self._client_no_context_takeover:
- response.add_parameter(
- self._CLIENT_NO_CONTEXT_TAKEOVER_PARAM, None)
-
- self._logger.debug(
- 'Enable %s extension ('
- 'request: server_max_window_bits=%s; '
- 'server_no_context_takeover=%r, '
- 'response: client_max_window_bits=%s; '
- 'client_no_context_takeover=%r)' %
- (self._request.name(),
- server_max_window_bits,
- server_no_context_takeover,
- self._preferred_client_max_window_bits,
- self._client_no_context_takeover))
-
- return response
-
- def _setup_stream_options_internal(self, stream_options):
- self._framer.setup_stream_options(stream_options)
-
- def set_client_max_window_bits(self, value):
- """If this option is specified, this class adds the
- client_max_window_bits extension parameter to the handshake response,
- but doesn't reduce the LZ77 sliding window size of its inflater.
- I.e., you can use this for testing client implementation but cannot
- reduce memory usage of this class.
-
- If this method has been called with True and an offer without the
- client_max_window_bits extension parameter is received,
- - (When processing the permessage-deflate extension) this processor
- declines the request.
- - (When processing the permessage-compress extension) this processor
- accepts the request.
- """
-
- self._preferred_client_max_window_bits = value
-
- def set_client_no_context_takeover(self, value):
- """If this option is specified, this class adds the
- client_no_context_takeover extension parameter to the handshake
- response, but doesn't reset inflater for each message. I.e., you can
- use this for testing client implementation but cannot reduce memory
- usage of this class.
- """
-
- self._client_no_context_takeover = value
-
- def set_bfinal(self, value):
- self._framer.set_bfinal(value)
-
- def enable_outgoing_compression(self):
- self._framer.set_compress_outgoing_enabled(True)
-
- def disable_outgoing_compression(self):
- self._framer.set_compress_outgoing_enabled(False)
-
-
-class _PerMessageDeflateFramer(object):
- """A framer for extensions with per-message DEFLATE feature."""
-
- def __init__(self, deflate_max_window_bits, deflate_no_context_takeover):
- self._logger = util.get_class_logger(self)
-
- self._rfc1979_deflater = util._RFC1979Deflater(
- deflate_max_window_bits, deflate_no_context_takeover)
-
- self._rfc1979_inflater = util._RFC1979Inflater()
-
- self._bfinal = False
-
- self._compress_outgoing_enabled = False
-
- # True if a message is fragmented and compression is ongoing.
- self._compress_ongoing = False
-
- # Calculates
- # (Total outgoing bytes supplied to this filter) /
- # (Total bytes sent to the network after applying this filter)
- self._outgoing_average_ratio_calculator = _AverageRatioCalculator()
-
- # Calculates
- # (Total bytes received from the network) /
- # (Total incoming bytes obtained after applying this filter)
- self._incoming_average_ratio_calculator = _AverageRatioCalculator()
-
- def set_bfinal(self, value):
- self._bfinal = value
-
- def set_compress_outgoing_enabled(self, value):
- self._compress_outgoing_enabled = value
-
- def _process_incoming_message(self, message, decompress):
- if not decompress:
- return message
-
- received_payload_size = len(message)
- self._incoming_average_ratio_calculator.add_result_bytes(
- received_payload_size)
-
- message = self._rfc1979_inflater.filter(message)
-
- filtered_payload_size = len(message)
- self._incoming_average_ratio_calculator.add_original_bytes(
- filtered_payload_size)
-
- _log_incoming_compression_ratio(
- self._logger,
- received_payload_size,
- filtered_payload_size,
- self._incoming_average_ratio_calculator.get_average_ratio())
-
- return message
-
- def _process_outgoing_message(self, message, end, binary):
- if not binary:
- message = message.encode('utf-8')
-
- if not self._compress_outgoing_enabled:
- return message
-
- original_payload_size = len(message)
- self._outgoing_average_ratio_calculator.add_original_bytes(
- original_payload_size)
-
- message = self._rfc1979_deflater.filter(
- message, end=end, bfinal=self._bfinal)
-
- filtered_payload_size = len(message)
- self._outgoing_average_ratio_calculator.add_result_bytes(
- filtered_payload_size)
-
- _log_outgoing_compression_ratio(
- self._logger,
- original_payload_size,
- filtered_payload_size,
- self._outgoing_average_ratio_calculator.get_average_ratio())
-
- if not self._compress_ongoing:
- self._outgoing_frame_filter.set_compression_bit()
- self._compress_ongoing = not end
- return message
-
- def _process_incoming_frame(self, frame):
- if frame.rsv1 == 1 and not common.is_control_opcode(frame.opcode):
- self._incoming_message_filter.decompress_next_message()
- frame.rsv1 = 0
-
- def _process_outgoing_frame(self, frame, compression_bit):
- if (not compression_bit or
- common.is_control_opcode(frame.opcode)):
- return
-
- frame.rsv1 = 1
-
- def setup_stream_options(self, stream_options):
- """Creates filters and sets them to the StreamOptions."""
-
- class _OutgoingMessageFilter(object):
-
- def __init__(self, parent):
- self._parent = parent
-
- def filter(self, message, end=True, binary=False):
- return self._parent._process_outgoing_message(
- message, end, binary)
-
- class _IncomingMessageFilter(object):
-
- def __init__(self, parent):
- self._parent = parent
- self._decompress_next_message = False
-
- def decompress_next_message(self):
- self._decompress_next_message = True
-
- def filter(self, message):
- message = self._parent._process_incoming_message(
- message, self._decompress_next_message)
- self._decompress_next_message = False
- return message
-
- self._outgoing_message_filter = _OutgoingMessageFilter(self)
- self._incoming_message_filter = _IncomingMessageFilter(self)
- stream_options.outgoing_message_filters.append(
- self._outgoing_message_filter)
- stream_options.incoming_message_filters.append(
- self._incoming_message_filter)
-
- class _OutgoingFrameFilter(object):
-
- def __init__(self, parent):
- self._parent = parent
- self._set_compression_bit = False
-
- def set_compression_bit(self):
- self._set_compression_bit = True
-
- def filter(self, frame):
- self._parent._process_outgoing_frame(
- frame, self._set_compression_bit)
- self._set_compression_bit = False
-
- class _IncomingFrameFilter(object):
-
- def __init__(self, parent):
- self._parent = parent
-
- def filter(self, frame):
- self._parent._process_incoming_frame(frame)
-
- self._outgoing_frame_filter = _OutgoingFrameFilter(self)
- self._incoming_frame_filter = _IncomingFrameFilter(self)
- stream_options.outgoing_frame_filters.append(
- self._outgoing_frame_filter)
- stream_options.incoming_frame_filters.append(
- self._incoming_frame_filter)
-
- stream_options.encode_text_message_to_utf8 = False
-
-
-_available_processors[common.PERMESSAGE_DEFLATE_EXTENSION] = (
- PerMessageDeflateExtensionProcessor)
-# TODO(tyoshino): Reorganize class names.
-_compression_extension_names.append('deflate')
-
-
-class PerMessageCompressExtensionProcessor(
- CompressionExtensionProcessorBase):
- """permessage-compress extension processor.
-
- Specification:
- http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression
- """
-
- _DEFLATE_METHOD = 'deflate'
-
- def __init__(self, request):
- CompressionExtensionProcessorBase.__init__(self, request)
-
- def name(self):
- return common.PERMESSAGE_COMPRESSION_EXTENSION
-
- def _lookup_compression_processor(self, method_desc):
- if method_desc.name() == self._DEFLATE_METHOD:
- return PerMessageDeflateExtensionProcessor(method_desc, False)
- return None
-
-
-_available_processors[common.PERMESSAGE_COMPRESSION_EXTENSION] = (
- PerMessageCompressExtensionProcessor)
-_compression_extension_names.append(common.PERMESSAGE_COMPRESSION_EXTENSION)
-
-
-class MuxExtensionProcessor(ExtensionProcessorInterface):
- """WebSocket multiplexing extension processor."""
-
- _QUOTA_PARAM = 'quota'
-
- def __init__(self, request):
- ExtensionProcessorInterface.__init__(self, request)
- self._quota = 0
- self._extensions = []
-
- def name(self):
- return common.MUX_EXTENSION
-
- def check_consistency_with_other_processors(self, processors):
- before_mux = True
- for processor in processors:
- name = processor.name()
- if name == self.name():
- before_mux = False
- continue
- if not processor.is_active():
- continue
- if before_mux:
- # Mux extension cannot be used after extensions
- # that depend on frame boundary, extension data field, or any
- # reserved bits which are attributed to each frame.
- if (name == common.DEFLATE_FRAME_EXTENSION or
- name == common.X_WEBKIT_DEFLATE_FRAME_EXTENSION):
- self.set_active(False)
- return
- else:
- # Mux extension should not be applied before any history-based
- # compression extension.
- if (name == common.DEFLATE_FRAME_EXTENSION or
- name == common.X_WEBKIT_DEFLATE_FRAME_EXTENSION or
- name == common.PERMESSAGE_COMPRESSION_EXTENSION or
- name == common.X_WEBKIT_PERMESSAGE_COMPRESSION_EXTENSION):
- self.set_active(False)
- return
-
- def _get_extension_response_internal(self):
- self._active = False
- quota = self._request.get_parameter_value(self._QUOTA_PARAM)
- if quota is not None:
- try:
- quota = int(quota)
- except ValueError, e:
- return None
- if quota < 0 or quota >= 2 ** 32:
- return None
- self._quota = quota
-
- self._active = True
- return common.ExtensionParameter(common.MUX_EXTENSION)
-
- def _setup_stream_options_internal(self, stream_options):
- pass
-
- def set_quota(self, quota):
- self._quota = quota
-
- def quota(self):
- return self._quota
-
- def set_extensions(self, extensions):
- self._extensions = extensions
-
- def extensions(self):
- return self._extensions
-
-
-_available_processors[common.MUX_EXTENSION] = MuxExtensionProcessor
-
-
-def get_extension_processor(extension_request):
- """Given an ExtensionParameter representing an extension offer received
- from a client, configures and returns an instance of the corresponding
- extension processor class.
- """
-
- processor_class = _available_processors.get(extension_request.name())
- if processor_class is None:
- return None
- return processor_class(extension_request)
-
-
-def is_compression_extension(extension_name):
- return extension_name in _compression_extension_names
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/fast_masking.i b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/fast_masking.i
deleted file mode 100644
index ddaad27f5..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/fast_masking.i
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2013, 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.
-
-
-%module fast_masking
-
-%include "cstring.i"
-
-%{
-#include <cstring>
-
-#ifdef __SSE2__
-#include <emmintrin.h>
-#endif
-%}
-
-%apply (char *STRING, int LENGTH) {
- (const char* payload, int payload_length),
- (const char* masking_key, int masking_key_length) };
-%cstring_output_allocate_size(
- char** result, int* result_length, delete [] *$1);
-
-%inline %{
-
-void mask(
- const char* payload, int payload_length,
- const char* masking_key, int masking_key_length,
- int masking_key_index,
- char** result, int* result_length) {
- *result = new char[payload_length];
- *result_length = payload_length;
- memcpy(*result, payload, payload_length);
-
- char* cursor = *result;
- char* cursor_end = *result + *result_length;
-
-#ifdef __SSE2__
- while ((cursor < cursor_end) &&
- (reinterpret_cast<size_t>(cursor) & 0xf)) {
- *cursor ^= masking_key[masking_key_index];
- ++cursor;
- masking_key_index = (masking_key_index + 1) % masking_key_length;
- }
- if (cursor == cursor_end) {
- return;
- }
-
- const int kBlockSize = 16;
- __m128i masking_key_block;
- for (int i = 0; i < kBlockSize; ++i) {
- *(reinterpret_cast<char*>(&masking_key_block) + i) =
- masking_key[masking_key_index];
- masking_key_index = (masking_key_index + 1) % masking_key_length;
- }
-
- while (cursor + kBlockSize <= cursor_end) {
- __m128i payload_block =
- _mm_load_si128(reinterpret_cast<__m128i*>(cursor));
- _mm_stream_si128(reinterpret_cast<__m128i*>(cursor),
- _mm_xor_si128(payload_block, masking_key_block));
- cursor += kBlockSize;
- }
-#endif
-
- while (cursor < cursor_end) {
- *cursor ^= masking_key[masking_key_index];
- ++cursor;
- masking_key_index = (masking_key_index + 1) % masking_key_length;
- }
-}
-
-%}
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/__init__.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/__init__.py
deleted file mode 100644
index 194f6b395..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/__init__.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""WebSocket opening handshake processor. This class try to apply available
-opening handshake processors for each protocol version until a connection is
-successfully established.
-"""
-
-
-import logging
-
-from mod_pywebsocket import common
-from mod_pywebsocket.handshake import hybi00
-from mod_pywebsocket.handshake import hybi
-# Export AbortedByUserException, HandshakeException, and VersionException
-# symbol from this module.
-from mod_pywebsocket.handshake._base import AbortedByUserException
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import VersionException
-
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
- """Performs WebSocket handshake.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
- allowDraft75: obsolete argument. ignored.
- strict: obsolete argument. ignored.
-
- Handshaker will add attributes such as ws_resource in performing
- handshake.
- """
-
- _LOGGER.debug('Client\'s opening handshake resource: %r', request.uri)
- # To print mimetools.Message as escaped one-line string, we converts
- # headers_in to dict object. Without conversion, if we use %r, it just
- # prints the type and address, and if we use %s, it prints the original
- # header string as multiple lines.
- #
- # Both mimetools.Message and MpTable_Type of mod_python can be
- # converted to dict.
- #
- # mimetools.Message.__str__ returns the original header string.
- # dict(mimetools.Message object) returns the map from header names to
- # header values. While MpTable_Type doesn't have such __str__ but just
- # __repr__ which formats itself as well as dictionary object.
- _LOGGER.debug(
- 'Client\'s opening handshake headers: %r', dict(request.headers_in))
-
- handshakers = []
- handshakers.append(
- ('RFC 6455', hybi.Handshaker(request, dispatcher)))
- handshakers.append(
- ('HyBi 00', hybi00.Handshaker(request, dispatcher)))
-
- for name, handshaker in handshakers:
- _LOGGER.debug('Trying protocol version %s', name)
- try:
- handshaker.do_handshake()
- _LOGGER.info('Established (%s protocol)', name)
- return
- except HandshakeException, e:
- _LOGGER.debug(
- 'Failed to complete opening handshake as %s protocol: %r',
- name, e)
- if e.status:
- raise e
- except AbortedByUserException, e:
- raise
- except VersionException, e:
- raise
-
- # TODO(toyoshim): Add a test to cover the case all handshakers fail.
- raise HandshakeException(
- 'Failed to complete opening handshake for all available protocols',
- status=common.HTTP_STATUS_BAD_REQUEST)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/_base.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/_base.py
deleted file mode 100644
index c993a584b..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/_base.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# 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.
-
-
-"""Common functions and exceptions used by WebSocket opening handshake
-processors.
-"""
-
-
-from mod_pywebsocket import common
-from mod_pywebsocket import http_header_util
-
-
-class AbortedByUserException(Exception):
- """Exception for aborting a connection intentionally.
-
- If this exception is raised in do_extra_handshake handler, the connection
- will be abandoned. No other WebSocket or HTTP(S) handler will be invoked.
-
- If this exception is raised in transfer_data_handler, the connection will
- be closed without closing handshake. No other WebSocket or HTTP(S) handler
- will be invoked.
- """
-
- pass
-
-
-class HandshakeException(Exception):
- """This exception will be raised when an error occurred while processing
- WebSocket initial handshake.
- """
-
- def __init__(self, name, status=None):
- super(HandshakeException, self).__init__(name)
- self.status = status
-
-
-class VersionException(Exception):
- """This exception will be raised when a version of client request does not
- match with version the server supports.
- """
-
- def __init__(self, name, supported_versions=''):
- """Construct an instance.
-
- Args:
- supported_version: a str object to show supported hybi versions.
- (e.g. '8, 13')
- """
- super(VersionException, self).__init__(name)
- self.supported_versions = supported_versions
-
-
-def get_default_port(is_secure):
- if is_secure:
- return common.DEFAULT_WEB_SOCKET_SECURE_PORT
- else:
- return common.DEFAULT_WEB_SOCKET_PORT
-
-
-def validate_subprotocol(subprotocol):
- """Validate a value in the Sec-WebSocket-Protocol field.
-
- See the Section 4.1., 4.2.2., and 4.3. of RFC 6455.
- """
-
- if not subprotocol:
- raise HandshakeException('Invalid subprotocol name: empty')
-
- # Parameter should be encoded HTTP token.
- state = http_header_util.ParsingState(subprotocol)
- token = http_header_util.consume_token(state)
- rest = http_header_util.peek(state)
- # If |rest| is not None, |subprotocol| is not one token or invalid. If
- # |rest| is None, |token| must not be None because |subprotocol| is
- # concatenation of |token| and |rest| and is not None.
- if rest is not None:
- raise HandshakeException('Invalid non-token string in subprotocol '
- 'name: %r' % rest)
-
-
-def parse_host_header(request):
- fields = request.headers_in[common.HOST_HEADER].split(':', 1)
- if len(fields) == 1:
- return fields[0], get_default_port(request.is_https())
- try:
- return fields[0], int(fields[1])
- except ValueError, e:
- raise HandshakeException('Invalid port number format: %r' % e)
-
-
-def format_header(name, value):
- return '%s: %s\r\n' % (name, value)
-
-
-def get_mandatory_header(request, key):
- value = request.headers_in.get(key)
- if value is None:
- raise HandshakeException('Header %s is not defined' % key)
- return value
-
-
-def validate_mandatory_header(request, key, expected_value, fail_status=None):
- value = get_mandatory_header(request, key)
-
- if value.lower() != expected_value.lower():
- raise HandshakeException(
- 'Expected %r for header %s but found %r (case-insensitive)' %
- (expected_value, key, value), status=fail_status)
-
-
-def check_request_line(request):
- # 5.1 1. The three character UTF-8 string "GET".
- # 5.1 2. A UTF-8-encoded U+0020 SPACE character (0x20 byte).
- if request.method != 'GET':
- raise HandshakeException('Method is not GET: %r' % request.method)
-
- if request.protocol != 'HTTP/1.1':
- raise HandshakeException('Version is not HTTP/1.1: %r' %
- request.protocol)
-
-
-def parse_token_list(data):
- """Parses a header value which follows 1#token and returns parsed elements
- as a list of strings.
-
- Leading LWSes must be trimmed.
- """
-
- state = http_header_util.ParsingState(data)
-
- token_list = []
-
- while True:
- token = http_header_util.consume_token(state)
- if token is not None:
- token_list.append(token)
-
- http_header_util.consume_lwses(state)
-
- if http_header_util.peek(state) is None:
- break
-
- if not http_header_util.consume_string(state, ','):
- raise HandshakeException(
- 'Expected a comma but found %r' % http_header_util.peek(state))
-
- http_header_util.consume_lwses(state)
-
- if len(token_list) == 0:
- raise HandshakeException('No valid token found')
-
- return token_list
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi.py
deleted file mode 100644
index 1ad10ea37..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi.py
+++ /dev/null
@@ -1,420 +0,0 @@
-# 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.
-
-
-"""This file provides the opening handshake processor for the WebSocket
-protocol (RFC 6455).
-
-Specification:
-http://tools.ietf.org/html/rfc6455
-"""
-
-
-# Note: request.connection.write is used in this module, even though mod_python
-# document says that it should be used only in connection handlers.
-# Unfortunately, we have no other options. For example, request.write is not
-# suitable because it doesn't allow direct raw bytes writing.
-
-
-import base64
-import logging
-import os
-import re
-
-from mod_pywebsocket import common
-from mod_pywebsocket.extensions import get_extension_processor
-from mod_pywebsocket.extensions import is_compression_extension
-from mod_pywebsocket.handshake._base import check_request_line
-from mod_pywebsocket.handshake._base import format_header
-from mod_pywebsocket.handshake._base import get_mandatory_header
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import parse_token_list
-from mod_pywebsocket.handshake._base import validate_mandatory_header
-from mod_pywebsocket.handshake._base import validate_subprotocol
-from mod_pywebsocket.handshake._base import VersionException
-from mod_pywebsocket.stream import Stream
-from mod_pywebsocket.stream import StreamOptions
-from mod_pywebsocket import util
-
-
-# Used to validate the value in the Sec-WebSocket-Key header strictly. RFC 4648
-# disallows non-zero padding, so the character right before == must be any of
-# A, Q, g and w.
-_SEC_WEBSOCKET_KEY_REGEX = re.compile('^[+/0-9A-Za-z]{21}[AQgw]==$')
-
-# Defining aliases for values used frequently.
-_VERSION_LATEST = common.VERSION_HYBI_LATEST
-_VERSION_LATEST_STRING = str(_VERSION_LATEST)
-_SUPPORTED_VERSIONS = [
- _VERSION_LATEST,
-]
-
-
-def compute_accept(key):
- """Computes value for the Sec-WebSocket-Accept header from value of the
- Sec-WebSocket-Key header.
- """
-
- accept_binary = util.sha1_hash(
- key + common.WEBSOCKET_ACCEPT_UUID).digest()
- accept = base64.b64encode(accept_binary)
-
- return (accept, accept_binary)
-
-
-class Handshaker(object):
- """Opening handshake processor for the WebSocket protocol (RFC 6455)."""
-
- def __init__(self, request, dispatcher):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
-
- Handshaker will add attributes such as ws_resource during handshake.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._dispatcher = dispatcher
-
- def _validate_connection_header(self):
- connection = get_mandatory_header(
- self._request, common.CONNECTION_HEADER)
-
- try:
- connection_tokens = parse_token_list(connection)
- except HandshakeException, e:
- raise HandshakeException(
- 'Failed to parse %s: %s' % (common.CONNECTION_HEADER, e))
-
- connection_is_valid = False
- for token in connection_tokens:
- if token.lower() == common.UPGRADE_CONNECTION_TYPE.lower():
- connection_is_valid = True
- break
- if not connection_is_valid:
- raise HandshakeException(
- '%s header doesn\'t contain "%s"' %
- (common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
-
- def do_handshake(self):
- self._request.ws_close_code = None
- self._request.ws_close_reason = None
-
- # Parsing.
-
- check_request_line(self._request)
-
- validate_mandatory_header(
- self._request,
- common.UPGRADE_HEADER,
- common.WEBSOCKET_UPGRADE_TYPE)
-
- self._validate_connection_header()
-
- self._request.ws_resource = self._request.uri
-
- unused_host = get_mandatory_header(self._request, common.HOST_HEADER)
-
- self._request.ws_version = self._check_version()
-
- try:
- self._get_origin()
- self._set_protocol()
- self._parse_extensions()
-
- # Key validation, response generation.
-
- key = self._get_key()
- (accept, accept_binary) = compute_accept(key)
- self._logger.debug(
- '%s: %r (%s)',
- common.SEC_WEBSOCKET_ACCEPT_HEADER,
- accept,
- util.hexify(accept_binary))
-
- self._logger.debug('Protocol version is RFC 6455')
-
- # Setup extension processors.
-
- processors = []
- if self._request.ws_requested_extensions is not None:
- for extension_request in self._request.ws_requested_extensions:
- processor = get_extension_processor(extension_request)
- # Unknown extension requests are just ignored.
- if processor is not None:
- processors.append(processor)
- self._request.ws_extension_processors = processors
-
- # List of extra headers. The extra handshake handler may add header
- # data as name/value pairs to this list and pywebsocket appends
- # them to the WebSocket handshake.
- self._request.extra_headers = []
-
- # Extra handshake handler may modify/remove processors.
- self._dispatcher.do_extra_handshake(self._request)
- processors = filter(lambda processor: processor is not None,
- self._request.ws_extension_processors)
-
- # Ask each processor if there are extensions on the request which
- # cannot co-exist. When processor decided other processors cannot
- # co-exist with it, the processor marks them (or itself) as
- # "inactive". The first extension processor has the right to
- # make the final call.
- for processor in reversed(processors):
- if processor.is_active():
- processor.check_consistency_with_other_processors(
- processors)
- processors = filter(lambda processor: processor.is_active(),
- processors)
-
- accepted_extensions = []
-
- # We need to take into account of mux extension here.
- # If mux extension exists:
- # - Remove processors of extensions for logical channel,
- # which are processors located before the mux processor
- # - Pass extension requests for logical channel to mux processor
- # - Attach the mux processor to the request. It will be referred
- # by dispatcher to see whether the dispatcher should use mux
- # handler or not.
- mux_index = -1
- for i, processor in enumerate(processors):
- if processor.name() == common.MUX_EXTENSION:
- mux_index = i
- break
- if mux_index >= 0:
- logical_channel_extensions = []
- for processor in processors[:mux_index]:
- logical_channel_extensions.append(processor.request())
- processor.set_active(False)
- self._request.mux_processor = processors[mux_index]
- self._request.mux_processor.set_extensions(
- logical_channel_extensions)
- processors = filter(lambda processor: processor.is_active(),
- processors)
-
- stream_options = StreamOptions()
-
- for index, processor in enumerate(processors):
- if not processor.is_active():
- continue
-
- extension_response = processor.get_extension_response()
- if extension_response is None:
- # Rejected.
- continue
-
- accepted_extensions.append(extension_response)
-
- processor.setup_stream_options(stream_options)
-
- if not is_compression_extension(processor.name()):
- continue
-
- # Inactivate all of the following compression extensions.
- for j in xrange(index + 1, len(processors)):
- if is_compression_extension(processors[j].name()):
- processors[j].set_active(False)
-
- if len(accepted_extensions) > 0:
- self._request.ws_extensions = accepted_extensions
- self._logger.debug(
- 'Extensions accepted: %r',
- map(common.ExtensionParameter.name, accepted_extensions))
- else:
- self._request.ws_extensions = None
-
- self._request.ws_stream = self._create_stream(stream_options)
-
- if self._request.ws_requested_protocols is not None:
- if self._request.ws_protocol is None:
- raise HandshakeException(
- 'do_extra_handshake must choose one subprotocol from '
- 'ws_requested_protocols and set it to ws_protocol')
- validate_subprotocol(self._request.ws_protocol)
-
- self._logger.debug(
- 'Subprotocol accepted: %r',
- self._request.ws_protocol)
- else:
- if self._request.ws_protocol is not None:
- raise HandshakeException(
- 'ws_protocol must be None when the client didn\'t '
- 'request any subprotocol')
-
- self._send_handshake(accept)
- except HandshakeException, e:
- if not e.status:
- # Fallback to 400 bad request by default.
- e.status = common.HTTP_STATUS_BAD_REQUEST
- raise e
-
- def _get_origin(self):
- origin_header = common.ORIGIN_HEADER
- origin = self._request.headers_in.get(origin_header)
- if origin is None:
- self._logger.debug('Client request does not have origin header')
- self._request.ws_origin = origin
-
- def _check_version(self):
- version = get_mandatory_header(self._request,
- common.SEC_WEBSOCKET_VERSION_HEADER)
- if version == _VERSION_LATEST_STRING:
- return _VERSION_LATEST
-
- if version.find(',') >= 0:
- raise HandshakeException(
- 'Multiple versions (%r) are not allowed for header %s' %
- (version, common.SEC_WEBSOCKET_VERSION_HEADER),
- status=common.HTTP_STATUS_BAD_REQUEST)
- raise VersionException(
- 'Unsupported version %r for header %s' %
- (version, common.SEC_WEBSOCKET_VERSION_HEADER),
- supported_versions=', '.join(map(str, _SUPPORTED_VERSIONS)))
-
- def _set_protocol(self):
- self._request.ws_protocol = None
-
- protocol_header = self._request.headers_in.get(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER)
-
- if protocol_header is None:
- self._request.ws_requested_protocols = None
- return
-
- self._request.ws_requested_protocols = parse_token_list(
- protocol_header)
- self._logger.debug('Subprotocols requested: %r',
- self._request.ws_requested_protocols)
-
- def _parse_extensions(self):
- extensions_header = self._request.headers_in.get(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER)
- if not extensions_header:
- self._request.ws_requested_extensions = None
- return
-
- try:
- self._request.ws_requested_extensions = common.parse_extensions(
- extensions_header)
- except common.ExtensionParsingException, e:
- raise HandshakeException(
- 'Failed to parse Sec-WebSocket-Extensions header: %r' % e)
-
- self._logger.debug(
- 'Extensions requested: %r',
- map(common.ExtensionParameter.name,
- self._request.ws_requested_extensions))
-
- def _validate_key(self, key):
- if key.find(',') >= 0:
- raise HandshakeException('Request has multiple %s header lines or '
- 'contains illegal character \',\': %r' %
- (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
- # Validate
- key_is_valid = False
- try:
- # Validate key by quick regex match before parsing by base64
- # module. Because base64 module skips invalid characters, we have
- # to do this in advance to make this server strictly reject illegal
- # keys.
- if _SEC_WEBSOCKET_KEY_REGEX.match(key):
- decoded_key = base64.b64decode(key)
- if len(decoded_key) == 16:
- key_is_valid = True
- except TypeError, e:
- pass
-
- if not key_is_valid:
- raise HandshakeException(
- 'Illegal value for header %s: %r' %
- (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
- return decoded_key
-
- def _get_key(self):
- key = get_mandatory_header(
- self._request, common.SEC_WEBSOCKET_KEY_HEADER)
-
- decoded_key = self._validate_key(key)
-
- self._logger.debug(
- '%s: %r (%s)',
- common.SEC_WEBSOCKET_KEY_HEADER,
- key,
- util.hexify(decoded_key))
-
- return key
-
- def _create_stream(self, stream_options):
- return Stream(self._request, stream_options)
-
- def _create_handshake_response(self, accept):
- response = []
-
- response.append('HTTP/1.1 101 Switching Protocols\r\n')
-
- # WebSocket headers
- response.append(format_header(
- common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE))
- response.append(format_header(
- common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
- response.append(format_header(
- common.SEC_WEBSOCKET_ACCEPT_HEADER, accept))
- if self._request.ws_protocol is not None:
- response.append(format_header(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER,
- self._request.ws_protocol))
- if (self._request.ws_extensions is not None and
- len(self._request.ws_extensions) != 0):
- response.append(format_header(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
- common.format_extensions(self._request.ws_extensions)))
-
- # Headers not specific for WebSocket
- for name, value in self._request.extra_headers:
- response.append(format_header(name, value))
-
- response.append('\r\n')
-
- return ''.join(response)
-
- def _send_handshake(self, accept):
- raw_response = self._create_handshake_response(accept)
- self._request.connection.write(raw_response)
- self._logger.debug('Sent server\'s opening handshake: %r',
- raw_response)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi00.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi00.py
deleted file mode 100644
index 8757717a6..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/handshake/hybi00.py
+++ /dev/null
@@ -1,293 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""This file provides the opening handshake processor for the WebSocket
-protocol version HyBi 00.
-
-Specification:
-http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
-"""
-
-
-# Note: request.connection.write/read are used in this module, even though
-# mod_python document says that they should be used only in connection
-# handlers. Unfortunately, we have no other options. For example,
-# request.write/read are not suitable because they don't allow direct raw bytes
-# writing/reading.
-
-
-import logging
-import re
-import struct
-
-from mod_pywebsocket import common
-from mod_pywebsocket.stream import StreamHixie75
-from mod_pywebsocket import util
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import check_request_line
-from mod_pywebsocket.handshake._base import format_header
-from mod_pywebsocket.handshake._base import get_default_port
-from mod_pywebsocket.handshake._base import get_mandatory_header
-from mod_pywebsocket.handshake._base import parse_host_header
-from mod_pywebsocket.handshake._base import validate_mandatory_header
-
-
-_MANDATORY_HEADERS = [
- # key, expected value or None
- [common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75],
- [common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE],
-]
-
-
-def _validate_subprotocol(subprotocol):
- """Checks if characters in subprotocol are in range between U+0020 and
- U+007E. A value in the Sec-WebSocket-Protocol field need to satisfy this
- requirement.
-
- See the Section 4.1. Opening handshake of the spec.
- """
-
- if not subprotocol:
- raise HandshakeException('Invalid subprotocol name: empty')
-
- # Parameter should be in the range U+0020 to U+007E.
- for c in subprotocol:
- if not 0x20 <= ord(c) <= 0x7e:
- raise HandshakeException(
- 'Illegal character in subprotocol name: %r' % c)
-
-
-def _check_header_lines(request, mandatory_headers):
- check_request_line(request)
-
- # The expected field names, and the meaning of their corresponding
- # values, are as follows.
- # |Upgrade| and |Connection|
- for key, expected_value in mandatory_headers:
- validate_mandatory_header(request, key, expected_value)
-
-
-def _build_location(request):
- """Build WebSocket location for request."""
-
- location_parts = []
- if request.is_https():
- location_parts.append(common.WEB_SOCKET_SECURE_SCHEME)
- else:
- location_parts.append(common.WEB_SOCKET_SCHEME)
- location_parts.append('://')
- host, port = parse_host_header(request)
- connection_port = request.connection.local_addr[1]
- if port != connection_port:
- raise HandshakeException('Header/connection port mismatch: %d/%d' %
- (port, connection_port))
- location_parts.append(host)
- if (port != get_default_port(request.is_https())):
- location_parts.append(':')
- location_parts.append(str(port))
- location_parts.append(request.unparsed_uri)
- return ''.join(location_parts)
-
-
-class Handshaker(object):
- """Opening handshake processor for the WebSocket protocol version HyBi 00.
- """
-
- def __init__(self, request, dispatcher):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
-
- Handshaker will add attributes such as ws_resource in performing
- handshake.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._dispatcher = dispatcher
-
- def do_handshake(self):
- """Perform WebSocket Handshake.
-
- On _request, we set
- ws_resource, ws_protocol, ws_location, ws_origin, ws_challenge,
- ws_challenge_md5: WebSocket handshake information.
- ws_stream: Frame generation/parsing class.
- ws_version: Protocol version.
-
- Raises:
- HandshakeException: when any error happened in parsing the opening
- handshake request.
- """
-
- # 5.1 Reading the client's opening handshake.
- # dispatcher sets it in self._request.
- _check_header_lines(self._request, _MANDATORY_HEADERS)
- self._set_resource()
- self._set_subprotocol()
- self._set_location()
- self._set_origin()
- self._set_challenge_response()
- self._set_protocol_version()
-
- self._dispatcher.do_extra_handshake(self._request)
-
- self._send_handshake()
-
- def _set_resource(self):
- self._request.ws_resource = self._request.uri
-
- def _set_subprotocol(self):
- # |Sec-WebSocket-Protocol|
- subprotocol = self._request.headers_in.get(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER)
- if subprotocol is not None:
- _validate_subprotocol(subprotocol)
- self._request.ws_protocol = subprotocol
-
- def _set_location(self):
- # |Host|
- host = self._request.headers_in.get(common.HOST_HEADER)
- if host is not None:
- self._request.ws_location = _build_location(self._request)
- # TODO(ukai): check host is this host.
-
- def _set_origin(self):
- # |Origin|
- origin = self._request.headers_in.get(common.ORIGIN_HEADER)
- if origin is not None:
- self._request.ws_origin = origin
-
- def _set_protocol_version(self):
- # |Sec-WebSocket-Draft|
- draft = self._request.headers_in.get(common.SEC_WEBSOCKET_DRAFT_HEADER)
- if draft is not None and draft != '0':
- raise HandshakeException('Illegal value for %s: %s' %
- (common.SEC_WEBSOCKET_DRAFT_HEADER,
- draft))
-
- self._logger.debug('Protocol version is HyBi 00')
- self._request.ws_version = common.VERSION_HYBI00
- self._request.ws_stream = StreamHixie75(self._request, True)
-
- def _set_challenge_response(self):
- # 5.2 4-8.
- self._request.ws_challenge = self._get_challenge()
- # 5.2 9. let /response/ be the MD5 finterprint of /challenge/
- self._request.ws_challenge_md5 = util.md5_hash(
- self._request.ws_challenge).digest()
- self._logger.debug(
- 'Challenge: %r (%s)',
- self._request.ws_challenge,
- util.hexify(self._request.ws_challenge))
- self._logger.debug(
- 'Challenge response: %r (%s)',
- self._request.ws_challenge_md5,
- util.hexify(self._request.ws_challenge_md5))
-
- def _get_key_value(self, key_field):
- key_value = get_mandatory_header(self._request, key_field)
-
- self._logger.debug('%s: %r', key_field, key_value)
-
- # 5.2 4. let /key-number_n/ be the digits (characters in the range
- # U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_n/,
- # interpreted as a base ten integer, ignoring all other characters
- # in /key_n/.
- try:
- key_number = int(re.sub("\\D", "", key_value))
- except:
- raise HandshakeException('%s field contains no digit' % key_field)
- # 5.2 5. let /spaces_n/ be the number of U+0020 SPACE characters
- # in /key_n/.
- spaces = re.subn(" ", "", key_value)[1]
- if spaces == 0:
- raise HandshakeException('%s field contains no space' % key_field)
-
- self._logger.debug(
- '%s: Key-number is %d and number of spaces is %d',
- key_field, key_number, spaces)
-
- # 5.2 6. if /key-number_n/ is not an integral multiple of /spaces_n/
- # then abort the WebSocket connection.
- if key_number % spaces != 0:
- raise HandshakeException(
- '%s: Key-number (%d) is not an integral multiple of spaces '
- '(%d)' % (key_field, key_number, spaces))
- # 5.2 7. let /part_n/ be /key-number_n/ divided by /spaces_n/.
- part = key_number / spaces
- self._logger.debug('%s: Part is %d', key_field, part)
- return part
-
- def _get_challenge(self):
- # 5.2 4-7.
- key1 = self._get_key_value(common.SEC_WEBSOCKET_KEY1_HEADER)
- key2 = self._get_key_value(common.SEC_WEBSOCKET_KEY2_HEADER)
- # 5.2 8. let /challenge/ be the concatenation of /part_1/,
- challenge = ''
- challenge += struct.pack('!I', key1) # network byteorder int
- challenge += struct.pack('!I', key2) # network byteorder int
- challenge += self._request.connection.read(8)
- return challenge
-
- def _send_handshake(self):
- response = []
-
- # 5.2 10. send the following line.
- response.append('HTTP/1.1 101 WebSocket Protocol Handshake\r\n')
-
- # 5.2 11. send the following fields to the client.
- response.append(format_header(
- common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75))
- response.append(format_header(
- common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
- response.append(format_header(
- common.SEC_WEBSOCKET_LOCATION_HEADER, self._request.ws_location))
- response.append(format_header(
- common.SEC_WEBSOCKET_ORIGIN_HEADER, self._request.ws_origin))
- if self._request.ws_protocol:
- response.append(format_header(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER,
- self._request.ws_protocol))
- # 5.2 12. send two bytes 0x0D 0x0A.
- response.append('\r\n')
- # 5.2 13. send /response/
- response.append(self._request.ws_challenge_md5)
-
- raw_response = ''.join(response)
- self._request.connection.write(raw_response)
- self._logger.debug('Sent server\'s opening handshake: %r',
- raw_response)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/headerparserhandler.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/headerparserhandler.py
deleted file mode 100644
index c244421cf..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/headerparserhandler.py
+++ /dev/null
@@ -1,254 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""PythonHeaderParserHandler for mod_pywebsocket.
-
-Apache HTTP Server and mod_python must be configured such that this
-function is called to handle WebSocket request.
-"""
-
-
-import logging
-
-from mod_python import apache
-
-from mod_pywebsocket import common
-from mod_pywebsocket import dispatch
-from mod_pywebsocket import handshake
-from mod_pywebsocket import util
-
-
-# PythonOption to specify the handler root directory.
-_PYOPT_HANDLER_ROOT = 'mod_pywebsocket.handler_root'
-
-# PythonOption to specify the handler scan directory.
-# This must be a directory under the root directory.
-# The default is the root directory.
-_PYOPT_HANDLER_SCAN = 'mod_pywebsocket.handler_scan'
-
-# PythonOption to allow handlers whose canonical path is
-# not under the root directory. It's disallowed by default.
-# Set this option with value of 'yes' to allow.
-_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT = (
- 'mod_pywebsocket.allow_handlers_outside_root_dir')
-# Map from values to their meanings. 'Yes' and 'No' are allowed just for
-# compatibility.
-_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION = {
- 'off': False, 'no': False, 'on': True, 'yes': True}
-
-# (Obsolete option. Ignored.)
-# PythonOption to specify to allow handshake defined in Hixie 75 version
-# protocol. The default is None (Off)
-_PYOPT_ALLOW_DRAFT75 = 'mod_pywebsocket.allow_draft75'
-# Map from values to their meanings.
-_PYOPT_ALLOW_DRAFT75_DEFINITION = {'off': False, 'on': True}
-
-
-class ApacheLogHandler(logging.Handler):
- """Wrapper logging.Handler to emit log message to apache's error.log."""
-
- _LEVELS = {
- logging.DEBUG: apache.APLOG_DEBUG,
- logging.INFO: apache.APLOG_INFO,
- logging.WARNING: apache.APLOG_WARNING,
- logging.ERROR: apache.APLOG_ERR,
- logging.CRITICAL: apache.APLOG_CRIT,
- }
-
- def __init__(self, request=None):
- logging.Handler.__init__(self)
- self._log_error = apache.log_error
- if request is not None:
- self._log_error = request.log_error
-
- # Time and level will be printed by Apache.
- self._formatter = logging.Formatter('%(name)s: %(message)s')
-
- def emit(self, record):
- apache_level = apache.APLOG_DEBUG
- if record.levelno in ApacheLogHandler._LEVELS:
- apache_level = ApacheLogHandler._LEVELS[record.levelno]
-
- msg = self._formatter.format(record)
-
- # "server" parameter must be passed to have "level" parameter work.
- # If only "level" parameter is passed, nothing shows up on Apache's
- # log. However, at this point, we cannot get the server object of the
- # virtual host which will process WebSocket requests. The only server
- # object we can get here is apache.main_server. But Wherever (server
- # configuration context or virtual host context) we put
- # PythonHeaderParserHandler directive, apache.main_server just points
- # the main server instance (not any of virtual server instance). Then,
- # Apache follows LogLevel directive in the server configuration context
- # to filter logs. So, we need to specify LogLevel in the server
- # configuration context. Even if we specify "LogLevel debug" in the
- # virtual host context which actually handles WebSocket connections,
- # DEBUG level logs never show up unless "LogLevel debug" is specified
- # in the server configuration context.
- #
- # TODO(tyoshino): Provide logging methods on request object. When
- # request is mp_request object (when used together with Apache), the
- # methods call request.log_error indirectly. When request is
- # _StandaloneRequest, the methods call Python's logging facility which
- # we create in standalone.py.
- self._log_error(msg, apache_level, apache.main_server)
-
-
-def _configure_logging():
- logger = logging.getLogger()
- # Logs are filtered by Apache based on LogLevel directive in Apache
- # configuration file. We must just pass logs for all levels to
- # ApacheLogHandler.
- logger.setLevel(logging.DEBUG)
- logger.addHandler(ApacheLogHandler())
-
-
-_configure_logging()
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def _parse_option(name, value, definition):
- if value is None:
- return False
-
- meaning = definition.get(value.lower())
- if meaning is None:
- raise Exception('Invalid value for PythonOption %s: %r' %
- (name, value))
- return meaning
-
-
-def _create_dispatcher():
- _LOGGER.info('Initializing Dispatcher')
-
- options = apache.main_server.get_options()
-
- handler_root = options.get(_PYOPT_HANDLER_ROOT, None)
- if not handler_root:
- raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT,
- apache.APLOG_ERR)
-
- handler_scan = options.get(_PYOPT_HANDLER_SCAN, handler_root)
-
- allow_handlers_outside_root = _parse_option(
- _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT,
- options.get(_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT),
- _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION)
-
- dispatcher = dispatch.Dispatcher(
- handler_root, handler_scan, allow_handlers_outside_root)
-
- for warning in dispatcher.source_warnings():
- apache.log_error(
- 'mod_pywebsocket: Warning in source loading: %s' % warning,
- apache.APLOG_WARNING)
-
- return dispatcher
-
-
-# Initialize
-_dispatcher = _create_dispatcher()
-
-
-def headerparserhandler(request):
- """Handle request.
-
- Args:
- request: mod_python request.
-
- This function is named headerparserhandler because it is the default
- name for a PythonHeaderParserHandler.
- """
-
- handshake_is_done = False
- try:
- # Fallback to default http handler for request paths for which
- # we don't have request handlers.
- if not _dispatcher.get_handler_suite(request.uri):
- request.log_error(
- 'mod_pywebsocket: No handler for resource: %r' % request.uri,
- apache.APLOG_INFO)
- request.log_error(
- 'mod_pywebsocket: Fallback to Apache', apache.APLOG_INFO)
- return apache.DECLINED
- except dispatch.DispatchException, e:
- request.log_error(
- 'mod_pywebsocket: Dispatch failed for error: %s' % e,
- apache.APLOG_INFO)
- if not handshake_is_done:
- return e.status
-
- try:
- allow_draft75 = _parse_option(
- _PYOPT_ALLOW_DRAFT75,
- apache.main_server.get_options().get(_PYOPT_ALLOW_DRAFT75),
- _PYOPT_ALLOW_DRAFT75_DEFINITION)
-
- try:
- handshake.do_handshake(
- request, _dispatcher, allowDraft75=allow_draft75)
- except handshake.VersionException, e:
- request.log_error(
- 'mod_pywebsocket: Handshake failed for version error: %s' % e,
- apache.APLOG_INFO)
- request.err_headers_out.add(common.SEC_WEBSOCKET_VERSION_HEADER,
- e.supported_versions)
- return apache.HTTP_BAD_REQUEST
- except handshake.HandshakeException, e:
- # Handshake for ws/wss failed.
- # Send http response with error status.
- request.log_error(
- 'mod_pywebsocket: Handshake failed for error: %s' % e,
- apache.APLOG_INFO)
- return e.status
-
- handshake_is_done = True
- request._dispatcher = _dispatcher
- _dispatcher.transfer_data(request)
- except handshake.AbortedByUserException, e:
- request.log_error('mod_pywebsocket: Aborted: %s' % e, apache.APLOG_INFO)
- except Exception, e:
- # DispatchException can also be thrown if something is wrong in
- # pywebsocket code. It's caught here, then.
-
- request.log_error('mod_pywebsocket: Exception occurred: %s\n%s' %
- (e, util.get_stack_trace()),
- apache.APLOG_ERR)
- # Unknown exceptions before handshake mean Apache must handle its
- # request with another handler.
- if not handshake_is_done:
- return apache.DECLINED
- # Set assbackwards to suppress response header generation by Apache.
- request.assbackwards = 1
- return apache.DONE # Return DONE such that no other handlers are invoked.
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/http_header_util.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/http_header_util.py
deleted file mode 100644
index b77465393..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/http_header_util.py
+++ /dev/null
@@ -1,263 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""Utilities for parsing and formatting headers that follow the grammar defined
-in HTTP RFC http://www.ietf.org/rfc/rfc2616.txt.
-"""
-
-
-import urlparse
-
-
-_SEPARATORS = '()<>@,;:\\"/[]?={} \t'
-
-
-def _is_char(c):
- """Returns true iff c is in CHAR as specified in HTTP RFC."""
-
- return ord(c) <= 127
-
-
-def _is_ctl(c):
- """Returns true iff c is in CTL as specified in HTTP RFC."""
-
- return ord(c) <= 31 or ord(c) == 127
-
-
-class ParsingState(object):
-
- def __init__(self, data):
- self.data = data
- self.head = 0
-
-
-def peek(state, pos=0):
- """Peeks the character at pos from the head of data."""
-
- if state.head + pos >= len(state.data):
- return None
-
- return state.data[state.head + pos]
-
-
-def consume(state, amount=1):
- """Consumes specified amount of bytes from the head and returns the
- consumed bytes. If there's not enough bytes to consume, returns None.
- """
-
- if state.head + amount > len(state.data):
- return None
-
- result = state.data[state.head:state.head + amount]
- state.head = state.head + amount
- return result
-
-
-def consume_string(state, expected):
- """Given a parsing state and a expected string, consumes the string from
- the head. Returns True if consumed successfully. Otherwise, returns
- False.
- """
-
- pos = 0
-
- for c in expected:
- if c != peek(state, pos):
- return False
- pos += 1
-
- consume(state, pos)
- return True
-
-
-def consume_lws(state):
- """Consumes a LWS from the head. Returns True if any LWS is consumed.
- Otherwise, returns False.
-
- LWS = [CRLF] 1*( SP | HT )
- """
-
- original_head = state.head
-
- consume_string(state, '\r\n')
-
- pos = 0
-
- while True:
- c = peek(state, pos)
- if c == ' ' or c == '\t':
- pos += 1
- else:
- if pos == 0:
- state.head = original_head
- return False
- else:
- consume(state, pos)
- return True
-
-
-def consume_lwses(state):
- """Consumes *LWS from the head."""
-
- while consume_lws(state):
- pass
-
-
-def consume_token(state):
- """Consumes a token from the head. Returns the token or None if no token
- was found.
- """
-
- pos = 0
-
- while True:
- c = peek(state, pos)
- if c is None or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
- if pos == 0:
- return None
-
- return consume(state, pos)
- else:
- pos += 1
-
-
-def consume_token_or_quoted_string(state):
- """Consumes a token or a quoted-string, and returns the token or unquoted
- string. If no token or quoted-string was found, returns None.
- """
-
- original_head = state.head
-
- if not consume_string(state, '"'):
- return consume_token(state)
-
- result = []
-
- expect_quoted_pair = False
-
- while True:
- if not expect_quoted_pair and consume_lws(state):
- result.append(' ')
- continue
-
- c = consume(state)
- if c is None:
- # quoted-string is not enclosed with double quotation
- state.head = original_head
- return None
- elif expect_quoted_pair:
- expect_quoted_pair = False
- if _is_char(c):
- result.append(c)
- else:
- # Non CHAR character found in quoted-pair
- state.head = original_head
- return None
- elif c == '\\':
- expect_quoted_pair = True
- elif c == '"':
- return ''.join(result)
- elif _is_ctl(c):
- # Invalid character %r found in qdtext
- state.head = original_head
- return None
- else:
- result.append(c)
-
-
-def quote_if_necessary(s):
- """Quotes arbitrary string into quoted-string."""
-
- quote = False
- if s == '':
- return '""'
-
- result = []
- for c in s:
- if c == '"' or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
- quote = True
-
- if c == '"' or _is_ctl(c):
- result.append('\\' + c)
- else:
- result.append(c)
-
- if quote:
- return '"' + ''.join(result) + '"'
- else:
- return ''.join(result)
-
-
-def parse_uri(uri):
- """Parse absolute URI then return host, port and resource."""
-
- parsed = urlparse.urlsplit(uri)
- if parsed.scheme != 'wss' and parsed.scheme != 'ws':
- # |uri| must be a relative URI.
- # TODO(toyoshim): Should validate |uri|.
- return None, None, uri
-
- if parsed.hostname is None:
- return None, None, None
-
- port = None
- try:
- port = parsed.port
- except ValueError, e:
- # port property cause ValueError on invalid null port description like
- # 'ws://host:/path'.
- return None, None, None
-
- if port is None:
- if parsed.scheme == 'ws':
- port = 80
- else:
- port = 443
-
- path = parsed.path
- if not path:
- path += '/'
- if parsed.query:
- path += '?' + parsed.query
- if parsed.fragment:
- path += '#' + parsed.fragment
-
- return parsed.hostname, port, path
-
-
-try:
- urlparse.uses_netloc.index('ws')
-except ValueError, e:
- # urlparse in Python2.5.1 doesn't have 'ws' and 'wss' entries.
- urlparse.uses_netloc.append('ws')
- urlparse.uses_netloc.append('wss')
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/memorizingfile.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/memorizingfile.py
deleted file mode 100644
index 4d4cd9585..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/memorizingfile.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Memorizing file.
-
-A memorizing file wraps a file and memorizes lines read by readline.
-"""
-
-
-import sys
-
-
-class MemorizingFile(object):
- """MemorizingFile wraps a file and memorizes lines read by readline.
-
- Note that data read by other methods are not memorized. This behavior
- is good enough for memorizing lines SimpleHTTPServer reads before
- the control reaches WebSocketRequestHandler.
- """
-
- def __init__(self, file_, max_memorized_lines=sys.maxint):
- """Construct an instance.
-
- Args:
- file_: the file object to wrap.
- max_memorized_lines: the maximum number of lines to memorize.
- Only the first max_memorized_lines are memorized.
- Default: sys.maxint.
- """
-
- self._file = file_
- self._memorized_lines = []
- self._max_memorized_lines = max_memorized_lines
- self._buffered = False
- self._buffered_line = None
-
- def __getattribute__(self, name):
- if name in ('_file', '_memorized_lines', '_max_memorized_lines',
- '_buffered', '_buffered_line', 'readline',
- 'get_memorized_lines'):
- return object.__getattribute__(self, name)
- return self._file.__getattribute__(name)
-
- def readline(self, size=-1):
- """Override file.readline and memorize the line read.
-
- Note that even if size is specified and smaller than actual size,
- the whole line will be read out from underlying file object by
- subsequent readline calls.
- """
-
- if self._buffered:
- line = self._buffered_line
- self._buffered = False
- else:
- line = self._file.readline()
- if line and len(self._memorized_lines) < self._max_memorized_lines:
- self._memorized_lines.append(line)
- if size >= 0 and size < len(line):
- self._buffered = True
- self._buffered_line = line[size:]
- return line[:size]
- return line
-
- def get_memorized_lines(self):
- """Get lines memorized so far."""
- return self._memorized_lines
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/msgutil.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/msgutil.py
deleted file mode 100644
index 4c1a0114b..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/msgutil.py
+++ /dev/null
@@ -1,219 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""Message related utilities.
-
-Note: request.connection.write/read are used in this module, even though
-mod_python document says that they should be used only in connection
-handlers. Unfortunately, we have no other options. For example,
-request.write/read are not suitable because they don't allow direct raw
-bytes writing/reading.
-"""
-
-
-import Queue
-import threading
-
-
-# Export Exception symbols from msgutil for backward compatibility
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import InvalidFrameException
-from mod_pywebsocket._stream_base import BadOperationException
-from mod_pywebsocket._stream_base import UnsupportedFrameException
-
-
-# An API for handler to send/receive WebSocket messages.
-def close_connection(request):
- """Close connection.
-
- Args:
- request: mod_python request.
- """
- request.ws_stream.close_connection()
-
-
-def send_message(request, payload_data, end=True, binary=False):
- """Send a message (or part of a message).
-
- Args:
- request: mod_python request.
- payload_data: unicode text or str binary to send.
- end: True to terminate a message.
- False to send payload_data as part of a message that is to be
- terminated by next or later send_message call with end=True.
- binary: send payload_data as binary frame(s).
- Raises:
- BadOperationException: when server already terminated.
- """
- request.ws_stream.send_message(payload_data, end, binary)
-
-
-def receive_message(request):
- """Receive a WebSocket frame and return its payload as a text in
- unicode or a binary in str.
-
- Args:
- request: mod_python request.
- Raises:
- InvalidFrameException: when client send invalid frame.
- UnsupportedFrameException: when client send unsupported frame e.g. some
- of reserved bit is set but no extension can
- recognize it.
- InvalidUTF8Exception: when client send a text frame containing any
- invalid UTF-8 string.
- ConnectionTerminatedException: when the connection is closed
- unexpectedly.
- BadOperationException: when client already terminated.
- """
- return request.ws_stream.receive_message()
-
-
-def send_ping(request, body=''):
- request.ws_stream.send_ping(body)
-
-
-class MessageReceiver(threading.Thread):
- """This class receives messages from the client.
-
- This class provides three ways to receive messages: blocking,
- non-blocking, and via callback. Callback has the highest precedence.
-
- Note: This class should not be used with the standalone server for wss
- because pyOpenSSL used by the server raises a fatal error if the socket
- is accessed from multiple threads.
- """
-
- def __init__(self, request, onmessage=None):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- onmessage: a function to be called when a message is received.
- May be None. If not None, the function is called on
- another thread. In that case, MessageReceiver.receive
- and MessageReceiver.receive_nowait are useless
- because they will never return any messages.
- """
-
- threading.Thread.__init__(self)
- self._request = request
- self._queue = Queue.Queue()
- self._onmessage = onmessage
- self._stop_requested = False
- self.setDaemon(True)
- self.start()
-
- def run(self):
- try:
- while not self._stop_requested:
- message = receive_message(self._request)
- if self._onmessage:
- self._onmessage(message)
- else:
- self._queue.put(message)
- finally:
- close_connection(self._request)
-
- def receive(self):
- """ Receive a message from the channel, blocking.
-
- Returns:
- message as a unicode string.
- """
- return self._queue.get()
-
- def receive_nowait(self):
- """ Receive a message from the channel, non-blocking.
-
- Returns:
- message as a unicode string if available. None otherwise.
- """
- try:
- message = self._queue.get_nowait()
- except Queue.Empty:
- message = None
- return message
-
- def stop(self):
- """Request to stop this instance.
-
- The instance will be stopped after receiving the next message.
- This method may not be very useful, but there is no clean way
- in Python to forcefully stop a running thread.
- """
- self._stop_requested = True
-
-
-class MessageSender(threading.Thread):
- """This class sends messages to the client.
-
- This class provides both synchronous and asynchronous ways to send
- messages.
-
- Note: This class should not be used with the standalone server for wss
- because pyOpenSSL used by the server raises a fatal error if the socket
- is accessed from multiple threads.
- """
-
- def __init__(self, request):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- """
- threading.Thread.__init__(self)
- self._request = request
- self._queue = Queue.Queue()
- self.setDaemon(True)
- self.start()
-
- def run(self):
- while True:
- message, condition = self._queue.get()
- condition.acquire()
- send_message(self._request, message)
- condition.notify()
- condition.release()
-
- def send(self, message):
- """Send a message, blocking."""
-
- condition = threading.Condition()
- condition.acquire()
- self._queue.put((message, condition))
- condition.wait()
-
- def send_nowait(self, message):
- """Send a message, non-blocking."""
-
- self._queue.put((message, threading.Condition()))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/mux.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/mux.py
deleted file mode 100644
index 76334685b..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/mux.py
+++ /dev/null
@@ -1,1889 +0,0 @@
-# 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.
-
-
-"""This file provides classes and helper functions for multiplexing extension.
-
-Specification:
-http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-06
-"""
-
-
-import collections
-import copy
-import email
-import email.parser
-import logging
-import math
-import struct
-import threading
-import traceback
-
-from mod_pywebsocket import common
-from mod_pywebsocket import handshake
-from mod_pywebsocket import util
-from mod_pywebsocket._stream_base import BadOperationException
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import InvalidFrameException
-from mod_pywebsocket._stream_hybi import Frame
-from mod_pywebsocket._stream_hybi import Stream
-from mod_pywebsocket._stream_hybi import StreamOptions
-from mod_pywebsocket._stream_hybi import create_binary_frame
-from mod_pywebsocket._stream_hybi import create_closing_handshake_body
-from mod_pywebsocket._stream_hybi import create_header
-from mod_pywebsocket._stream_hybi import create_length_header
-from mod_pywebsocket._stream_hybi import parse_frame
-from mod_pywebsocket.handshake import hybi
-
-
-_CONTROL_CHANNEL_ID = 0
-_DEFAULT_CHANNEL_ID = 1
-
-_MUX_OPCODE_ADD_CHANNEL_REQUEST = 0
-_MUX_OPCODE_ADD_CHANNEL_RESPONSE = 1
-_MUX_OPCODE_FLOW_CONTROL = 2
-_MUX_OPCODE_DROP_CHANNEL = 3
-_MUX_OPCODE_NEW_CHANNEL_SLOT = 4
-
-_MAX_CHANNEL_ID = 2 ** 29 - 1
-
-_INITIAL_NUMBER_OF_CHANNEL_SLOTS = 64
-_INITIAL_QUOTA_FOR_CLIENT = 8 * 1024
-
-_HANDSHAKE_ENCODING_IDENTITY = 0
-_HANDSHAKE_ENCODING_DELTA = 1
-
-# We need only these status code for now.
-_HTTP_BAD_RESPONSE_MESSAGES = {
- common.HTTP_STATUS_BAD_REQUEST: 'Bad Request',
-}
-
-# DropChannel reason code
-# TODO(bashi): Define all reason code defined in -05 draft.
-_DROP_CODE_NORMAL_CLOSURE = 1000
-
-_DROP_CODE_INVALID_ENCAPSULATING_MESSAGE = 2001
-_DROP_CODE_CHANNEL_ID_TRUNCATED = 2002
-_DROP_CODE_ENCAPSULATED_FRAME_IS_TRUNCATED = 2003
-_DROP_CODE_UNKNOWN_MUX_OPCODE = 2004
-_DROP_CODE_INVALID_MUX_CONTROL_BLOCK = 2005
-_DROP_CODE_CHANNEL_ALREADY_EXISTS = 2006
-_DROP_CODE_NEW_CHANNEL_SLOT_VIOLATION = 2007
-_DROP_CODE_UNKNOWN_REQUEST_ENCODING = 2010
-
-_DROP_CODE_SEND_QUOTA_VIOLATION = 3005
-_DROP_CODE_SEND_QUOTA_OVERFLOW = 3006
-_DROP_CODE_ACKNOWLEDGED = 3008
-_DROP_CODE_BAD_FRAGMENTATION = 3009
-
-
-class MuxUnexpectedException(Exception):
- """Exception in handling multiplexing extension."""
- pass
-
-
-# Temporary
-class MuxNotImplementedException(Exception):
- """Raised when a flow enters unimplemented code path."""
- pass
-
-
-class LogicalConnectionClosedException(Exception):
- """Raised when logical connection is gracefully closed."""
- pass
-
-
-class PhysicalConnectionError(Exception):
- """Raised when there is a physical connection error."""
- def __init__(self, drop_code, message=''):
- super(PhysicalConnectionError, self).__init__(
- 'code=%d, message=%r' % (drop_code, message))
- self.drop_code = drop_code
- self.message = message
-
-
-class LogicalChannelError(Exception):
- """Raised when there is a logical channel error."""
- def __init__(self, channel_id, drop_code, message=''):
- super(LogicalChannelError, self).__init__(
- 'channel_id=%d, code=%d, message=%r' % (
- channel_id, drop_code, message))
- self.channel_id = channel_id
- self.drop_code = drop_code
- self.message = message
-
-
-def _encode_channel_id(channel_id):
- if channel_id < 0:
- raise ValueError('Channel id %d must not be negative' % channel_id)
-
- if channel_id < 2 ** 7:
- return chr(channel_id)
- if channel_id < 2 ** 14:
- return struct.pack('!H', 0x8000 + channel_id)
- if channel_id < 2 ** 21:
- first = chr(0xc0 + (channel_id >> 16))
- return first + struct.pack('!H', channel_id & 0xffff)
- if channel_id < 2 ** 29:
- return struct.pack('!L', 0xe0000000 + channel_id)
-
- raise ValueError('Channel id %d is too large' % channel_id)
-
-
-def _encode_number(number):
- return create_length_header(number, False)
-
-
-def _create_add_channel_response(channel_id, encoded_handshake,
- encoding=0, rejected=False):
- if encoding != 0 and encoding != 1:
- raise ValueError('Invalid encoding %d' % encoding)
-
- first_byte = ((_MUX_OPCODE_ADD_CHANNEL_RESPONSE << 5) |
- (rejected << 4) | encoding)
- block = (chr(first_byte) +
- _encode_channel_id(channel_id) +
- _encode_number(len(encoded_handshake)) +
- encoded_handshake)
- return block
-
-
-def _create_drop_channel(channel_id, code=None, message=''):
- if len(message) > 0 and code is None:
- raise ValueError('Code must be specified if message is specified')
-
- first_byte = _MUX_OPCODE_DROP_CHANNEL << 5
- block = chr(first_byte) + _encode_channel_id(channel_id)
- if code is None:
- block += _encode_number(0) # Reason size
- else:
- reason = struct.pack('!H', code) + message
- reason_size = _encode_number(len(reason))
- block += reason_size + reason
-
- return block
-
-
-def _create_flow_control(channel_id, replenished_quota):
- first_byte = _MUX_OPCODE_FLOW_CONTROL << 5
- block = (chr(first_byte) +
- _encode_channel_id(channel_id) +
- _encode_number(replenished_quota))
- return block
-
-
-def _create_new_channel_slot(slots, send_quota):
- if slots < 0 or send_quota < 0:
- raise ValueError('slots and send_quota must be non-negative.')
- first_byte = _MUX_OPCODE_NEW_CHANNEL_SLOT << 5
- block = (chr(first_byte) +
- _encode_number(slots) +
- _encode_number(send_quota))
- return block
-
-
-def _create_fallback_new_channel_slot():
- first_byte = (_MUX_OPCODE_NEW_CHANNEL_SLOT << 5) | 1 # Set the F flag
- block = (chr(first_byte) + _encode_number(0) + _encode_number(0))
- return block
-
-
-def _parse_request_text(request_text):
- request_line, header_lines = request_text.split('\r\n', 1)
-
- words = request_line.split(' ')
- if len(words) != 3:
- raise ValueError('Bad Request-Line syntax %r' % request_line)
- [command, path, version] = words
- if version != 'HTTP/1.1':
- raise ValueError('Bad request version %r' % version)
-
- # email.parser.Parser() parses RFC 2822 (RFC 822) style headers.
- # RFC 6455 refers RFC 2616 for handshake parsing, and RFC 2616 refers
- # RFC 822.
- headers = email.parser.Parser().parsestr(header_lines)
- return command, path, version, headers
-
-
-class _ControlBlock(object):
- """A structure that holds parsing result of multiplexing control block.
- Control block specific attributes will be added by _MuxFramePayloadParser.
- (e.g. encoded_handshake will be added for AddChannelRequest and
- AddChannelResponse)
- """
-
- def __init__(self, opcode):
- self.opcode = opcode
-
-
-class _MuxFramePayloadParser(object):
- """A class that parses multiplexed frame payload."""
-
- def __init__(self, payload):
- self._data = payload
- self._read_position = 0
- self._logger = util.get_class_logger(self)
-
- def read_channel_id(self):
- """Reads channel id.
-
- Raises:
- ValueError: when the payload doesn't contain
- valid channel id.
- """
-
- remaining_length = len(self._data) - self._read_position
- pos = self._read_position
- if remaining_length == 0:
- raise ValueError('Invalid channel id format')
-
- channel_id = ord(self._data[pos])
- channel_id_length = 1
- if channel_id & 0xe0 == 0xe0:
- if remaining_length < 4:
- raise ValueError('Invalid channel id format')
- channel_id = struct.unpack('!L',
- self._data[pos:pos+4])[0] & 0x1fffffff
- channel_id_length = 4
- elif channel_id & 0xc0 == 0xc0:
- if remaining_length < 3:
- raise ValueError('Invalid channel id format')
- channel_id = (((channel_id & 0x1f) << 16) +
- struct.unpack('!H', self._data[pos+1:pos+3])[0])
- channel_id_length = 3
- elif channel_id & 0x80 == 0x80:
- if remaining_length < 2:
- raise ValueError('Invalid channel id format')
- channel_id = struct.unpack('!H',
- self._data[pos:pos+2])[0] & 0x3fff
- channel_id_length = 2
- self._read_position += channel_id_length
-
- return channel_id
-
- def read_inner_frame(self):
- """Reads an inner frame.
-
- Raises:
- PhysicalConnectionError: when the inner frame is invalid.
- """
-
- if len(self._data) == self._read_position:
- raise PhysicalConnectionError(
- _DROP_CODE_ENCAPSULATED_FRAME_IS_TRUNCATED)
-
- bits = ord(self._data[self._read_position])
- self._read_position += 1
- fin = (bits & 0x80) == 0x80
- rsv1 = (bits & 0x40) == 0x40
- rsv2 = (bits & 0x20) == 0x20
- rsv3 = (bits & 0x10) == 0x10
- opcode = bits & 0xf
- payload = self.remaining_data()
- # Consume rest of the message which is payload data of the original
- # frame.
- self._read_position = len(self._data)
- return fin, rsv1, rsv2, rsv3, opcode, payload
-
- def _read_number(self):
- if self._read_position + 1 > len(self._data):
- raise ValueError(
- 'Cannot read the first byte of number field')
-
- number = ord(self._data[self._read_position])
- if number & 0x80 == 0x80:
- raise ValueError(
- 'The most significant bit of the first byte of number should '
- 'be unset')
- self._read_position += 1
- pos = self._read_position
- if number == 127:
- if pos + 8 > len(self._data):
- raise ValueError('Invalid number field')
- self._read_position += 8
- number = struct.unpack('!Q', self._data[pos:pos+8])[0]
- if number > 0x7FFFFFFFFFFFFFFF:
- raise ValueError('Encoded number(%d) >= 2^63' % number)
- if number <= 0xFFFF:
- raise ValueError(
- '%d should not be encoded by 9 bytes encoding' % number)
- return number
- if number == 126:
- if pos + 2 > len(self._data):
- raise ValueError('Invalid number field')
- self._read_position += 2
- number = struct.unpack('!H', self._data[pos:pos+2])[0]
- if number <= 125:
- raise ValueError(
- '%d should not be encoded by 3 bytes encoding' % number)
- return number
-
- def _read_size_and_contents(self):
- """Reads data that consists of followings:
- - the size of the contents encoded the same way as payload length
- of the WebSocket Protocol with 1 bit padding at the head.
- - the contents.
- """
-
- try:
- size = self._read_number()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- str(e))
- pos = self._read_position
- if pos + size > len(self._data):
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Cannot read %d bytes data' % size)
-
- self._read_position += size
- return self._data[pos:pos+size]
-
- def _read_add_channel_request(self, first_byte, control_block):
- reserved = (first_byte >> 2) & 0x7
- if reserved != 0:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Reserved bits must be unset')
-
- # Invalid encoding will be handled by MuxHandler.
- encoding = first_byte & 0x3
- try:
- control_block.channel_id = self.read_channel_id()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK)
- control_block.encoding = encoding
- encoded_handshake = self._read_size_and_contents()
- control_block.encoded_handshake = encoded_handshake
- return control_block
-
- def _read_add_channel_response(self, first_byte, control_block):
- reserved = (first_byte >> 2) & 0x3
- if reserved != 0:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Reserved bits must be unset')
-
- control_block.accepted = (first_byte >> 4) & 1
- control_block.encoding = first_byte & 0x3
- try:
- control_block.channel_id = self.read_channel_id()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK)
- control_block.encoded_handshake = self._read_size_and_contents()
- return control_block
-
- def _read_flow_control(self, first_byte, control_block):
- reserved = first_byte & 0x1f
- if reserved != 0:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Reserved bits must be unset')
-
- try:
- control_block.channel_id = self.read_channel_id()
- control_block.send_quota = self._read_number()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- str(e))
-
- return control_block
-
- def _read_drop_channel(self, first_byte, control_block):
- reserved = first_byte & 0x1f
- if reserved != 0:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Reserved bits must be unset')
-
- try:
- control_block.channel_id = self.read_channel_id()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK)
- reason = self._read_size_and_contents()
- if len(reason) == 0:
- control_block.drop_code = None
- control_block.drop_message = ''
- elif len(reason) >= 2:
- control_block.drop_code = struct.unpack('!H', reason[:2])[0]
- control_block.drop_message = reason[2:]
- else:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Received DropChannel that conains only 1-byte reason')
- return control_block
-
- def _read_new_channel_slot(self, first_byte, control_block):
- reserved = first_byte & 0x1e
- if reserved != 0:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Reserved bits must be unset')
- control_block.fallback = first_byte & 1
- try:
- control_block.slots = self._read_number()
- control_block.send_quota = self._read_number()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- str(e))
- return control_block
-
- def read_control_blocks(self):
- """Reads control block(s).
-
- Raises:
- PhysicalConnectionError: when the payload contains invalid control
- block(s).
- StopIteration: when no control blocks left.
- """
-
- while self._read_position < len(self._data):
- first_byte = ord(self._data[self._read_position])
- self._read_position += 1
- opcode = (first_byte >> 5) & 0x7
- control_block = _ControlBlock(opcode=opcode)
- if opcode == _MUX_OPCODE_ADD_CHANNEL_REQUEST:
- yield self._read_add_channel_request(first_byte, control_block)
- elif opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
- yield self._read_add_channel_response(
- first_byte, control_block)
- elif opcode == _MUX_OPCODE_FLOW_CONTROL:
- yield self._read_flow_control(first_byte, control_block)
- elif opcode == _MUX_OPCODE_DROP_CHANNEL:
- yield self._read_drop_channel(first_byte, control_block)
- elif opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
- yield self._read_new_channel_slot(first_byte, control_block)
- else:
- raise PhysicalConnectionError(
- _DROP_CODE_UNKNOWN_MUX_OPCODE,
- 'Invalid opcode %d' % opcode)
-
- assert self._read_position == len(self._data)
- raise StopIteration
-
- def remaining_data(self):
- """Returns remaining data."""
-
- return self._data[self._read_position:]
-
-
-class _LogicalRequest(object):
- """Mimics mod_python request."""
-
- def __init__(self, channel_id, command, path, protocol, headers,
- connection):
- """Constructs an instance.
-
- Args:
- channel_id: the channel id of the logical channel.
- command: HTTP request command.
- path: HTTP request path.
- headers: HTTP headers.
- connection: _LogicalConnection instance.
- """
-
- self.channel_id = channel_id
- self.method = command
- self.uri = path
- self.protocol = protocol
- self.headers_in = headers
- self.connection = connection
- self.server_terminated = False
- self.client_terminated = False
-
- def is_https(self):
- """Mimics request.is_https(). Returns False because this method is
- used only by old protocols (hixie and hybi00).
- """
-
- return False
-
-
-class _LogicalConnection(object):
- """Mimics mod_python mp_conn."""
-
- # For details, see the comment of set_read_state().
- STATE_ACTIVE = 1
- STATE_GRACEFULLY_CLOSED = 2
- STATE_TERMINATED = 3
-
- def __init__(self, mux_handler, channel_id):
- """Constructs an instance.
-
- Args:
- mux_handler: _MuxHandler instance.
- channel_id: channel id of this connection.
- """
-
- self._mux_handler = mux_handler
- self._channel_id = channel_id
- self._incoming_data = ''
-
- # - Protects _waiting_write_completion
- # - Signals the thread waiting for completion of write by mux handler
- self._write_condition = threading.Condition()
- self._waiting_write_completion = False
-
- self._read_condition = threading.Condition()
- self._read_state = self.STATE_ACTIVE
-
- def get_local_addr(self):
- """Getter to mimic mp_conn.local_addr."""
-
- return self._mux_handler.physical_connection.get_local_addr()
- local_addr = property(get_local_addr)
-
- def get_remote_addr(self):
- """Getter to mimic mp_conn.remote_addr."""
-
- return self._mux_handler.physical_connection.get_remote_addr()
- remote_addr = property(get_remote_addr)
-
- def get_memorized_lines(self):
- """Gets memorized lines. Not supported."""
-
- raise MuxUnexpectedException('_LogicalConnection does not support '
- 'get_memorized_lines')
-
- def write(self, data):
- """Writes data. mux_handler sends data asynchronously. The caller will
- be suspended until write done.
-
- Args:
- data: data to be written.
-
- Raises:
- MuxUnexpectedException: when called before finishing the previous
- write.
- """
-
- try:
- self._write_condition.acquire()
- if self._waiting_write_completion:
- raise MuxUnexpectedException(
- 'Logical connection %d is already waiting the completion '
- 'of write' % self._channel_id)
-
- self._waiting_write_completion = True
- self._mux_handler.send_data(self._channel_id, data)
- self._write_condition.wait()
- # TODO(tyoshino): Raise an exception if woke up by on_writer_done.
- finally:
- self._write_condition.release()
-
- def write_control_data(self, data):
- """Writes data via the control channel. Don't wait finishing write
- because this method can be called by mux dispatcher.
-
- Args:
- data: data to be written.
- """
-
- self._mux_handler.send_control_data(data)
-
- def on_write_data_done(self):
- """Called when sending data is completed."""
-
- try:
- self._write_condition.acquire()
- if not self._waiting_write_completion:
- raise MuxUnexpectedException(
- 'Invalid call of on_write_data_done for logical '
- 'connection %d' % self._channel_id)
- self._waiting_write_completion = False
- self._write_condition.notify()
- finally:
- self._write_condition.release()
-
- def on_writer_done(self):
- """Called by the mux handler when the writer thread has finished."""
-
- try:
- self._write_condition.acquire()
- self._waiting_write_completion = False
- self._write_condition.notify()
- finally:
- self._write_condition.release()
-
-
- def append_frame_data(self, frame_data):
- """Appends incoming frame data. Called when mux_handler dispatches
- frame data to the corresponding application.
-
- Args:
- frame_data: incoming frame data.
- """
-
- self._read_condition.acquire()
- self._incoming_data += frame_data
- self._read_condition.notify()
- self._read_condition.release()
-
- def read(self, length):
- """Reads data. Blocks until enough data has arrived via physical
- connection.
-
- Args:
- length: length of data to be read.
- Raises:
- LogicalConnectionClosedException: when closing handshake for this
- logical channel has been received.
- ConnectionTerminatedException: when the physical connection has
- closed, or an error is caused on the reader thread.
- """
-
- self._read_condition.acquire()
- while (self._read_state == self.STATE_ACTIVE and
- len(self._incoming_data) < length):
- self._read_condition.wait()
-
- try:
- if self._read_state == self.STATE_GRACEFULLY_CLOSED:
- raise LogicalConnectionClosedException(
- 'Logical channel %d has closed.' % self._channel_id)
- elif self._read_state == self.STATE_TERMINATED:
- raise ConnectionTerminatedException(
- 'Receiving %d byte failed. Logical channel (%d) closed' %
- (length, self._channel_id))
-
- value = self._incoming_data[:length]
- self._incoming_data = self._incoming_data[length:]
- finally:
- self._read_condition.release()
-
- return value
-
- def set_read_state(self, new_state):
- """Sets the state of this connection. Called when an event for this
- connection has occurred.
-
- Args:
- new_state: state to be set. new_state must be one of followings:
- - STATE_GRACEFULLY_CLOSED: when closing handshake for this
- connection has been received.
- - STATE_TERMINATED: when the physical connection has closed or
- DropChannel of this connection has received.
- """
-
- self._read_condition.acquire()
- self._read_state = new_state
- self._read_condition.notify()
- self._read_condition.release()
-
-
-class _InnerMessage(object):
- """Holds the result of _InnerMessageBuilder.build().
- """
-
- def __init__(self, opcode, payload):
- self.opcode = opcode
- self.payload = payload
-
-
-class _InnerMessageBuilder(object):
- """A class that holds the context of inner message fragmentation and
- builds a message from fragmented inner frame(s).
- """
-
- def __init__(self):
- self._control_opcode = None
- self._pending_control_fragments = []
- self._message_opcode = None
- self._pending_message_fragments = []
- self._frame_handler = self._handle_first
-
- def _handle_first(self, frame):
- if frame.opcode == common.OPCODE_CONTINUATION:
- raise InvalidFrameException('Sending invalid continuation opcode')
-
- if common.is_control_opcode(frame.opcode):
- return self._process_first_fragmented_control(frame)
- else:
- return self._process_first_fragmented_message(frame)
-
- def _process_first_fragmented_control(self, frame):
- self._control_opcode = frame.opcode
- self._pending_control_fragments.append(frame.payload)
- if not frame.fin:
- self._frame_handler = self._handle_fragmented_control
- return None
- return self._reassemble_fragmented_control()
-
- def _process_first_fragmented_message(self, frame):
- self._message_opcode = frame.opcode
- self._pending_message_fragments.append(frame.payload)
- if not frame.fin:
- self._frame_handler = self._handle_fragmented_message
- return None
- return self._reassemble_fragmented_message()
-
- def _handle_fragmented_control(self, frame):
- if frame.opcode != common.OPCODE_CONTINUATION:
- raise InvalidFrameException(
- 'Sending invalid opcode %d while sending fragmented control '
- 'message' % frame.opcode)
- self._pending_control_fragments.append(frame.payload)
- if not frame.fin:
- return None
- return self._reassemble_fragmented_control()
-
- def _reassemble_fragmented_control(self):
- opcode = self._control_opcode
- payload = ''.join(self._pending_control_fragments)
- self._control_opcode = None
- self._pending_control_fragments = []
- if self._message_opcode is not None:
- self._frame_handler = self._handle_fragmented_message
- else:
- self._frame_handler = self._handle_first
- return _InnerMessage(opcode, payload)
-
- def _handle_fragmented_message(self, frame):
- # Sender can interleave a control message while sending fragmented
- # messages.
- if common.is_control_opcode(frame.opcode):
- if self._control_opcode is not None:
- raise MuxUnexpectedException(
- 'Should not reach here(Bug in builder)')
- return self._process_first_fragmented_control(frame)
-
- if frame.opcode != common.OPCODE_CONTINUATION:
- raise InvalidFrameException(
- 'Sending invalid opcode %d while sending fragmented message' %
- frame.opcode)
- self._pending_message_fragments.append(frame.payload)
- if not frame.fin:
- return None
- return self._reassemble_fragmented_message()
-
- def _reassemble_fragmented_message(self):
- opcode = self._message_opcode
- payload = ''.join(self._pending_message_fragments)
- self._message_opcode = None
- self._pending_message_fragments = []
- self._frame_handler = self._handle_first
- return _InnerMessage(opcode, payload)
-
- def build(self, frame):
- """Build an inner message. Returns an _InnerMessage instance when
- the given frame is the last fragmented frame. Returns None otherwise.
-
- Args:
- frame: an inner frame.
- Raises:
- InvalidFrameException: when received invalid opcode. (e.g.
- receiving non continuation data opcode but the fin flag of
- the previous inner frame was not set.)
- """
-
- return self._frame_handler(frame)
-
-
-class _LogicalStream(Stream):
- """Mimics the Stream class. This class interprets multiplexed WebSocket
- frames.
- """
-
- def __init__(self, request, stream_options, send_quota, receive_quota):
- """Constructs an instance.
-
- Args:
- request: _LogicalRequest instance.
- stream_options: StreamOptions instance.
- send_quota: Initial send quota.
- receive_quota: Initial receive quota.
- """
-
- # Physical stream is responsible for masking.
- stream_options.unmask_receive = False
- Stream.__init__(self, request, stream_options)
-
- self._send_closed = False
- self._send_quota = send_quota
- # - Protects _send_closed and _send_quota
- # - Signals the thread waiting for send quota replenished
- self._send_condition = threading.Condition()
-
- # The opcode of the first frame in messages.
- self._message_opcode = common.OPCODE_TEXT
- # True when the last message was fragmented.
- self._last_message_was_fragmented = False
-
- self._receive_quota = receive_quota
- self._write_inner_frame_semaphore = threading.Semaphore()
-
- self._inner_message_builder = _InnerMessageBuilder()
-
- def _create_inner_frame(self, opcode, payload, end=True):
- frame = Frame(fin=end, opcode=opcode, payload=payload)
- for frame_filter in self._options.outgoing_frame_filters:
- frame_filter.filter(frame)
-
- if len(payload) != len(frame.payload):
- raise MuxUnexpectedException(
- 'Mux extension must not be used after extensions which change '
- ' frame boundary')
-
- first_byte = ((frame.fin << 7) | (frame.rsv1 << 6) |
- (frame.rsv2 << 5) | (frame.rsv3 << 4) | frame.opcode)
- return chr(first_byte) + frame.payload
-
- def _write_inner_frame(self, opcode, payload, end=True):
- payload_length = len(payload)
- write_position = 0
-
- try:
- # An inner frame will be fragmented if there is no enough send
- # quota. This semaphore ensures that fragmented inner frames are
- # sent in order on the logical channel.
- # Note that frames that come from other logical channels or
- # multiplexing control blocks can be inserted between fragmented
- # inner frames on the physical channel.
- self._write_inner_frame_semaphore.acquire()
-
- # Consume an octet quota when this is the first fragmented frame.
- if opcode != common.OPCODE_CONTINUATION:
- try:
- self._send_condition.acquire()
- while (not self._send_closed) and self._send_quota == 0:
- self._send_condition.wait()
-
- if self._send_closed:
- raise BadOperationException(
- 'Logical connection %d is closed' %
- self._request.channel_id)
-
- self._send_quota -= 1
- finally:
- self._send_condition.release()
-
- while write_position < payload_length:
- try:
- self._send_condition.acquire()
- while (not self._send_closed) and self._send_quota == 0:
- self._logger.debug(
- 'No quota. Waiting FlowControl message for %d.' %
- self._request.channel_id)
- self._send_condition.wait()
-
- if self._send_closed:
- raise BadOperationException(
- 'Logical connection %d is closed' %
- self.request._channel_id)
-
- remaining = payload_length - write_position
- write_length = min(self._send_quota, remaining)
- inner_frame_end = (
- end and
- (write_position + write_length == payload_length))
-
- inner_frame = self._create_inner_frame(
- opcode,
- payload[write_position:write_position+write_length],
- inner_frame_end)
- self._send_quota -= write_length
- self._logger.debug('Consumed quota=%d, remaining=%d' %
- (write_length, self._send_quota))
- finally:
- self._send_condition.release()
-
- # Writing data will block the worker so we need to release
- # _send_condition before writing.
- self._logger.debug('Sending inner frame: %r' % inner_frame)
- self._request.connection.write(inner_frame)
- write_position += write_length
-
- opcode = common.OPCODE_CONTINUATION
-
- except ValueError, e:
- raise BadOperationException(e)
- finally:
- self._write_inner_frame_semaphore.release()
-
- def replenish_send_quota(self, send_quota):
- """Replenish send quota."""
-
- try:
- self._send_condition.acquire()
- if self._send_quota + send_quota > 0x7FFFFFFFFFFFFFFF:
- self._send_quota = 0
- raise LogicalChannelError(
- self._request.channel_id, _DROP_CODE_SEND_QUOTA_OVERFLOW)
- self._send_quota += send_quota
- self._logger.debug('Replenished send quota for channel id %d: %d' %
- (self._request.channel_id, self._send_quota))
- finally:
- self._send_condition.notify()
- self._send_condition.release()
-
- def consume_receive_quota(self, amount):
- """Consumes receive quota. Returns False on failure."""
-
- if self._receive_quota < amount:
- self._logger.debug('Violate quota on channel id %d: %d < %d' %
- (self._request.channel_id,
- self._receive_quota, amount))
- return False
- self._receive_quota -= amount
- return True
-
- def send_message(self, message, end=True, binary=False):
- """Override Stream.send_message."""
-
- if self._request.server_terminated:
- raise BadOperationException(
- 'Requested send_message after sending out a closing handshake')
-
- if binary and isinstance(message, unicode):
- raise BadOperationException(
- 'Message for binary frame must be instance of str')
-
- if binary:
- opcode = common.OPCODE_BINARY
- else:
- opcode = common.OPCODE_TEXT
- message = message.encode('utf-8')
-
- for message_filter in self._options.outgoing_message_filters:
- message = message_filter.filter(message, end, binary)
-
- if self._last_message_was_fragmented:
- if opcode != self._message_opcode:
- raise BadOperationException('Message types are different in '
- 'frames for the same message')
- opcode = common.OPCODE_CONTINUATION
- else:
- self._message_opcode = opcode
-
- self._write_inner_frame(opcode, message, end)
- self._last_message_was_fragmented = not end
-
- def _receive_frame(self):
- """Overrides Stream._receive_frame.
-
- In addition to call Stream._receive_frame, this method adds the amount
- of payload to receiving quota and sends FlowControl to the client.
- We need to do it here because Stream.receive_message() handles
- control frames internally.
- """
-
- opcode, payload, fin, rsv1, rsv2, rsv3 = Stream._receive_frame(self)
- amount = len(payload)
- # Replenish extra one octet when receiving the first fragmented frame.
- if opcode != common.OPCODE_CONTINUATION:
- amount += 1
- self._receive_quota += amount
- frame_data = _create_flow_control(self._request.channel_id,
- amount)
- self._logger.debug('Sending flow control for %d, replenished=%d' %
- (self._request.channel_id, amount))
- self._request.connection.write_control_data(frame_data)
- return opcode, payload, fin, rsv1, rsv2, rsv3
-
- def _get_message_from_frame(self, frame):
- """Overrides Stream._get_message_from_frame.
- """
-
- try:
- inner_message = self._inner_message_builder.build(frame)
- except InvalidFrameException:
- raise LogicalChannelError(
- self._request.channel_id, _DROP_CODE_BAD_FRAGMENTATION)
-
- if inner_message is None:
- return None
- self._original_opcode = inner_message.opcode
- return inner_message.payload
-
- def receive_message(self):
- """Overrides Stream.receive_message."""
-
- # Just call Stream.receive_message(), but catch
- # LogicalConnectionClosedException, which is raised when the logical
- # connection has closed gracefully.
- try:
- return Stream.receive_message(self)
- except LogicalConnectionClosedException, e:
- self._logger.debug('%s', e)
- return None
-
- def _send_closing_handshake(self, code, reason):
- """Overrides Stream._send_closing_handshake."""
-
- body = create_closing_handshake_body(code, reason)
- self._logger.debug('Sending closing handshake for %d: (%r, %r)' %
- (self._request.channel_id, code, reason))
- self._write_inner_frame(common.OPCODE_CLOSE, body, end=True)
-
- self._request.server_terminated = True
-
- def send_ping(self, body=''):
- """Overrides Stream.send_ping"""
-
- self._logger.debug('Sending ping on logical channel %d: %r' %
- (self._request.channel_id, body))
- self._write_inner_frame(common.OPCODE_PING, body, end=True)
-
- self._ping_queue.append(body)
-
- def _send_pong(self, body):
- """Overrides Stream._send_pong"""
-
- self._logger.debug('Sending pong on logical channel %d: %r' %
- (self._request.channel_id, body))
- self._write_inner_frame(common.OPCODE_PONG, body, end=True)
-
- def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason=''):
- """Overrides Stream.close_connection."""
-
- # TODO(bashi): Implement
- self._logger.debug('Closing logical connection %d' %
- self._request.channel_id)
- self._request.server_terminated = True
-
- def stop_sending(self):
- """Stops accepting new send operation (_write_inner_frame)."""
-
- self._send_condition.acquire()
- self._send_closed = True
- self._send_condition.notify()
- self._send_condition.release()
-
-
-class _OutgoingData(object):
- """A structure that holds data to be sent via physical connection and
- origin of the data.
- """
-
- def __init__(self, channel_id, data):
- self.channel_id = channel_id
- self.data = data
-
-
-class _PhysicalConnectionWriter(threading.Thread):
- """A thread that is responsible for writing data to physical connection.
-
- TODO(bashi): Make sure there is no thread-safety problem when the reader
- thread reads data from the same socket at a time.
- """
-
- def __init__(self, mux_handler):
- """Constructs an instance.
-
- Args:
- mux_handler: _MuxHandler instance.
- """
-
- threading.Thread.__init__(self)
- self._logger = util.get_class_logger(self)
- self._mux_handler = mux_handler
- self.setDaemon(True)
-
- # When set, make this thread stop accepting new data, flush pending
- # data and exit.
- self._stop_requested = False
- # The close code of the physical connection.
- self._close_code = common.STATUS_NORMAL_CLOSURE
- # Deque for passing write data. It's protected by _deque_condition
- # until _stop_requested is set.
- self._deque = collections.deque()
- # - Protects _deque, _stop_requested and _close_code
- # - Signals threads waiting for them to be available
- self._deque_condition = threading.Condition()
-
- def put_outgoing_data(self, data):
- """Puts outgoing data.
-
- Args:
- data: _OutgoingData instance.
-
- Raises:
- BadOperationException: when the thread has been requested to
- terminate.
- """
-
- try:
- self._deque_condition.acquire()
- if self._stop_requested:
- raise BadOperationException('Cannot write data anymore')
-
- self._deque.append(data)
- self._deque_condition.notify()
- finally:
- self._deque_condition.release()
-
- def _write_data(self, outgoing_data):
- message = (_encode_channel_id(outgoing_data.channel_id) +
- outgoing_data.data)
- try:
- self._mux_handler.physical_stream.send_message(
- message=message, end=True, binary=True)
- except Exception, e:
- util.prepend_message_to_exception(
- 'Failed to send message to %r: ' %
- (self._mux_handler.physical_connection.remote_addr,), e)
- raise
-
- # TODO(bashi): It would be better to block the thread that sends
- # control data as well.
- if outgoing_data.channel_id != _CONTROL_CHANNEL_ID:
- self._mux_handler.notify_write_data_done(outgoing_data.channel_id)
-
- def run(self):
- try:
- self._deque_condition.acquire()
- while not self._stop_requested:
- if len(self._deque) == 0:
- self._deque_condition.wait()
- continue
-
- outgoing_data = self._deque.popleft()
-
- self._deque_condition.release()
- self._write_data(outgoing_data)
- self._deque_condition.acquire()
-
- # Flush deque.
- #
- # At this point, self._deque_condition is always acquired.
- try:
- while len(self._deque) > 0:
- outgoing_data = self._deque.popleft()
- self._write_data(outgoing_data)
- finally:
- self._deque_condition.release()
-
- # Close physical connection.
- try:
- # Don't wait the response here. The response will be read
- # by the reader thread.
- self._mux_handler.physical_stream.close_connection(
- self._close_code, wait_response=False)
- except Exception, e:
- util.prepend_message_to_exception(
- 'Failed to close the physical connection: %r' % e)
- raise
- finally:
- self._mux_handler.notify_writer_done()
-
- def stop(self, close_code=common.STATUS_NORMAL_CLOSURE):
- """Stops the writer thread."""
-
- self._deque_condition.acquire()
- self._stop_requested = True
- self._close_code = close_code
- self._deque_condition.notify()
- self._deque_condition.release()
-
-
-class _PhysicalConnectionReader(threading.Thread):
- """A thread that is responsible for reading data from physical connection.
- """
-
- def __init__(self, mux_handler):
- """Constructs an instance.
-
- Args:
- mux_handler: _MuxHandler instance.
- """
-
- threading.Thread.__init__(self)
- self._logger = util.get_class_logger(self)
- self._mux_handler = mux_handler
- self.setDaemon(True)
-
- def run(self):
- while True:
- try:
- physical_stream = self._mux_handler.physical_stream
- message = physical_stream.receive_message()
- if message is None:
- break
- # Below happens only when a data message is received.
- opcode = physical_stream.get_last_received_opcode()
- if opcode != common.OPCODE_BINARY:
- self._mux_handler.fail_physical_connection(
- _DROP_CODE_INVALID_ENCAPSULATING_MESSAGE,
- 'Received a text message on physical connection')
- break
-
- except ConnectionTerminatedException, e:
- self._logger.debug('%s', e)
- break
-
- try:
- self._mux_handler.dispatch_message(message)
- except PhysicalConnectionError, e:
- self._mux_handler.fail_physical_connection(
- e.drop_code, e.message)
- break
- except LogicalChannelError, e:
- self._mux_handler.fail_logical_channel(
- e.channel_id, e.drop_code, e.message)
- except Exception, e:
- self._logger.debug(traceback.format_exc())
- break
-
- self._mux_handler.notify_reader_done()
-
-
-class _Worker(threading.Thread):
- """A thread that is responsible for running the corresponding application
- handler.
- """
-
- def __init__(self, mux_handler, request):
- """Constructs an instance.
-
- Args:
- mux_handler: _MuxHandler instance.
- request: _LogicalRequest instance.
- """
-
- threading.Thread.__init__(self)
- self._logger = util.get_class_logger(self)
- self._mux_handler = mux_handler
- self._request = request
- self.setDaemon(True)
-
- def run(self):
- self._logger.debug('Logical channel worker started. (id=%d)' %
- self._request.channel_id)
- try:
- # Non-critical exceptions will be handled by dispatcher.
- self._mux_handler.dispatcher.transfer_data(self._request)
- except LogicalChannelError, e:
- self._mux_handler.fail_logical_channel(
- e.channel_id, e.drop_code, e.message)
- finally:
- self._mux_handler.notify_worker_done(self._request.channel_id)
-
-
-class _MuxHandshaker(hybi.Handshaker):
- """Opening handshake processor for multiplexing."""
-
- _DUMMY_WEBSOCKET_KEY = 'dGhlIHNhbXBsZSBub25jZQ=='
-
- def __init__(self, request, dispatcher, send_quota, receive_quota):
- """Constructs an instance.
- Args:
- request: _LogicalRequest instance.
- dispatcher: Dispatcher instance (dispatch.Dispatcher).
- send_quota: Initial send quota.
- receive_quota: Initial receive quota.
- """
-
- hybi.Handshaker.__init__(self, request, dispatcher)
- self._send_quota = send_quota
- self._receive_quota = receive_quota
-
- # Append headers which should not be included in handshake field of
- # AddChannelRequest.
- # TODO(bashi): Make sure whether we should raise exception when
- # these headers are included already.
- request.headers_in[common.UPGRADE_HEADER] = (
- common.WEBSOCKET_UPGRADE_TYPE)
- request.headers_in[common.SEC_WEBSOCKET_VERSION_HEADER] = (
- str(common.VERSION_HYBI_LATEST))
- request.headers_in[common.SEC_WEBSOCKET_KEY_HEADER] = (
- self._DUMMY_WEBSOCKET_KEY)
-
- def _create_stream(self, stream_options):
- """Override hybi.Handshaker._create_stream."""
-
- self._logger.debug('Creating logical stream for %d' %
- self._request.channel_id)
- return _LogicalStream(
- self._request, stream_options, self._send_quota,
- self._receive_quota)
-
- def _create_handshake_response(self, accept):
- """Override hybi._create_handshake_response."""
-
- response = []
-
- response.append('HTTP/1.1 101 Switching Protocols\r\n')
-
- # Upgrade and Sec-WebSocket-Accept should be excluded.
- response.append('%s: %s\r\n' % (
- common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
- if self._request.ws_protocol is not None:
- response.append('%s: %s\r\n' % (
- common.SEC_WEBSOCKET_PROTOCOL_HEADER,
- self._request.ws_protocol))
- if (self._request.ws_extensions is not None and
- len(self._request.ws_extensions) != 0):
- response.append('%s: %s\r\n' % (
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
- common.format_extensions(self._request.ws_extensions)))
- response.append('\r\n')
-
- return ''.join(response)
-
- def _send_handshake(self, accept):
- """Override hybi.Handshaker._send_handshake."""
-
- # Don't send handshake response for the default channel
- if self._request.channel_id == _DEFAULT_CHANNEL_ID:
- return
-
- handshake_response = self._create_handshake_response(accept)
- frame_data = _create_add_channel_response(
- self._request.channel_id,
- handshake_response)
- self._logger.debug('Sending handshake response for %d: %r' %
- (self._request.channel_id, frame_data))
- self._request.connection.write_control_data(frame_data)
-
-
-class _LogicalChannelData(object):
- """A structure that holds information about logical channel.
- """
-
- def __init__(self, request, worker):
- self.request = request
- self.worker = worker
- self.drop_code = _DROP_CODE_NORMAL_CLOSURE
- self.drop_message = ''
-
-
-class _HandshakeDeltaBase(object):
- """A class that holds information for delta-encoded handshake."""
-
- def __init__(self, headers):
- self._headers = headers
-
- def create_headers(self, delta=None):
- """Creates request headers for an AddChannelRequest that has
- delta-encoded handshake.
-
- Args:
- delta: headers should be overridden.
- """
-
- headers = copy.copy(self._headers)
- if delta:
- for key, value in delta.items():
- # The spec requires that a header with an empty value is
- # removed from the delta base.
- if len(value) == 0 and headers.has_key(key):
- del headers[key]
- else:
- headers[key] = value
- return headers
-
-
-class _MuxHandler(object):
- """Multiplexing handler. When a handler starts, it launches three
- threads; the reader thread, the writer thread, and a worker thread.
-
- The reader thread reads data from the physical stream, i.e., the
- ws_stream object of the underlying websocket connection. The reader
- thread interprets multiplexed frames and dispatches them to logical
- channels. Methods of this class are mostly called by the reader thread.
-
- The writer thread sends multiplexed frames which are created by
- logical channels via the physical connection.
-
- The worker thread launched at the starting point handles the
- "Implicitly Opened Connection". If multiplexing handler receives
- an AddChannelRequest and accepts it, the handler will launch a new worker
- thread and dispatch the request to it.
- """
-
- def __init__(self, request, dispatcher):
- """Constructs an instance.
-
- Args:
- request: mod_python request of the physical connection.
- dispatcher: Dispatcher instance (dispatch.Dispatcher).
- """
-
- self.original_request = request
- self.dispatcher = dispatcher
- self.physical_connection = request.connection
- self.physical_stream = request.ws_stream
- self._logger = util.get_class_logger(self)
- self._logical_channels = {}
- self._logical_channels_condition = threading.Condition()
- # Holds client's initial quota
- self._channel_slots = collections.deque()
- self._handshake_base = None
- self._worker_done_notify_received = False
- self._reader = None
- self._writer = None
-
- def start(self):
- """Starts the handler.
-
- Raises:
- MuxUnexpectedException: when the handler already started, or when
- opening handshake of the default channel fails.
- """
-
- if self._reader or self._writer:
- raise MuxUnexpectedException('MuxHandler already started')
-
- self._reader = _PhysicalConnectionReader(self)
- self._writer = _PhysicalConnectionWriter(self)
- self._reader.start()
- self._writer.start()
-
- # Create "Implicitly Opened Connection".
- logical_connection = _LogicalConnection(self, _DEFAULT_CHANNEL_ID)
- headers = copy.copy(self.original_request.headers_in)
- # Add extensions for logical channel.
- headers[common.SEC_WEBSOCKET_EXTENSIONS_HEADER] = (
- common.format_extensions(
- self.original_request.mux_processor.extensions()))
- self._handshake_base = _HandshakeDeltaBase(headers)
- logical_request = _LogicalRequest(
- _DEFAULT_CHANNEL_ID,
- self.original_request.method,
- self.original_request.uri,
- self.original_request.protocol,
- self._handshake_base.create_headers(),
- logical_connection)
- # Client's send quota for the implicitly opened connection is zero,
- # but we will send FlowControl later so set the initial quota to
- # _INITIAL_QUOTA_FOR_CLIENT.
- self._channel_slots.append(_INITIAL_QUOTA_FOR_CLIENT)
- send_quota = self.original_request.mux_processor.quota()
- if not self._do_handshake_for_logical_request(
- logical_request, send_quota=send_quota):
- raise MuxUnexpectedException(
- 'Failed handshake on the default channel id')
- self._add_logical_channel(logical_request)
-
- # Send FlowControl for the implicitly opened connection.
- frame_data = _create_flow_control(_DEFAULT_CHANNEL_ID,
- _INITIAL_QUOTA_FOR_CLIENT)
- logical_request.connection.write_control_data(frame_data)
-
- def add_channel_slots(self, slots, send_quota):
- """Adds channel slots.
-
- Args:
- slots: number of slots to be added.
- send_quota: initial send quota for slots.
- """
-
- self._channel_slots.extend([send_quota] * slots)
- # Send NewChannelSlot to client.
- frame_data = _create_new_channel_slot(slots, send_quota)
- self.send_control_data(frame_data)
-
- def wait_until_done(self, timeout=None):
- """Waits until all workers are done. Returns False when timeout has
- occurred. Returns True on success.
-
- Args:
- timeout: timeout in sec.
- """
-
- self._logical_channels_condition.acquire()
- try:
- while len(self._logical_channels) > 0:
- self._logger.debug('Waiting workers(%d)...' %
- len(self._logical_channels))
- self._worker_done_notify_received = False
- self._logical_channels_condition.wait(timeout)
- if not self._worker_done_notify_received:
- self._logger.debug('Waiting worker(s) timed out')
- return False
- finally:
- self._logical_channels_condition.release()
-
- # Flush pending outgoing data
- self._writer.stop()
- self._writer.join()
-
- return True
-
- def notify_write_data_done(self, channel_id):
- """Called by the writer thread when a write operation has done.
-
- Args:
- channel_id: objective channel id.
- """
-
- try:
- self._logical_channels_condition.acquire()
- if channel_id in self._logical_channels:
- channel_data = self._logical_channels[channel_id]
- channel_data.request.connection.on_write_data_done()
- else:
- self._logger.debug('Seems that logical channel for %d has gone'
- % channel_id)
- finally:
- self._logical_channels_condition.release()
-
- def send_control_data(self, data):
- """Sends data via the control channel.
-
- Args:
- data: data to be sent.
- """
-
- self._writer.put_outgoing_data(_OutgoingData(
- channel_id=_CONTROL_CHANNEL_ID, data=data))
-
- def send_data(self, channel_id, data):
- """Sends data via given logical channel. This method is called by
- worker threads.
-
- Args:
- channel_id: objective channel id.
- data: data to be sent.
- """
-
- self._writer.put_outgoing_data(_OutgoingData(
- channel_id=channel_id, data=data))
-
- def _send_drop_channel(self, channel_id, code=None, message=''):
- frame_data = _create_drop_channel(channel_id, code, message)
- self._logger.debug(
- 'Sending drop channel for channel id %d' % channel_id)
- self.send_control_data(frame_data)
-
- def _send_error_add_channel_response(self, channel_id, status=None):
- if status is None:
- status = common.HTTP_STATUS_BAD_REQUEST
-
- if status in _HTTP_BAD_RESPONSE_MESSAGES:
- message = _HTTP_BAD_RESPONSE_MESSAGES[status]
- else:
- self._logger.debug('Response message for %d is not found' % status)
- message = '???'
-
- response = 'HTTP/1.1 %d %s\r\n\r\n' % (status, message)
- frame_data = _create_add_channel_response(channel_id,
- encoded_handshake=response,
- encoding=0, rejected=True)
- self.send_control_data(frame_data)
-
- def _create_logical_request(self, block):
- if block.channel_id == _CONTROL_CHANNEL_ID:
- # TODO(bashi): Raise PhysicalConnectionError with code 2006
- # instead of MuxUnexpectedException.
- raise MuxUnexpectedException(
- 'Received the control channel id (0) as objective channel '
- 'id for AddChannel')
-
- if block.encoding > _HANDSHAKE_ENCODING_DELTA:
- raise PhysicalConnectionError(
- _DROP_CODE_UNKNOWN_REQUEST_ENCODING)
-
- method, path, version, headers = _parse_request_text(
- block.encoded_handshake)
- if block.encoding == _HANDSHAKE_ENCODING_DELTA:
- headers = self._handshake_base.create_headers(headers)
-
- connection = _LogicalConnection(self, block.channel_id)
- request = _LogicalRequest(block.channel_id, method, path, version,
- headers, connection)
- return request
-
- def _do_handshake_for_logical_request(self, request, send_quota=0):
- try:
- receive_quota = self._channel_slots.popleft()
- except IndexError:
- raise LogicalChannelError(
- request.channel_id, _DROP_CODE_NEW_CHANNEL_SLOT_VIOLATION)
-
- handshaker = _MuxHandshaker(request, self.dispatcher,
- send_quota, receive_quota)
- try:
- handshaker.do_handshake()
- except handshake.VersionException, e:
- self._logger.info('%s', e)
- self._send_error_add_channel_response(
- request.channel_id, status=common.HTTP_STATUS_BAD_REQUEST)
- return False
- except handshake.HandshakeException, e:
- # TODO(bashi): Should we _Fail the Logical Channel_ with 3001
- # instead?
- self._logger.info('%s', e)
- self._send_error_add_channel_response(request.channel_id,
- status=e.status)
- return False
- except handshake.AbortedByUserException, e:
- self._logger.info('%s', e)
- self._send_error_add_channel_response(request.channel_id)
- return False
-
- return True
-
- def _add_logical_channel(self, logical_request):
- try:
- self._logical_channels_condition.acquire()
- if logical_request.channel_id in self._logical_channels:
- self._logger.debug('Channel id %d already exists' %
- logical_request.channel_id)
- raise PhysicalConnectionError(
- _DROP_CODE_CHANNEL_ALREADY_EXISTS,
- 'Channel id %d already exists' %
- logical_request.channel_id)
- worker = _Worker(self, logical_request)
- channel_data = _LogicalChannelData(logical_request, worker)
- self._logical_channels[logical_request.channel_id] = channel_data
- worker.start()
- finally:
- self._logical_channels_condition.release()
-
- def _process_add_channel_request(self, block):
- try:
- logical_request = self._create_logical_request(block)
- except ValueError, e:
- self._logger.debug('Failed to create logical request: %r' % e)
- self._send_error_add_channel_response(
- block.channel_id, status=common.HTTP_STATUS_BAD_REQUEST)
- return
- if self._do_handshake_for_logical_request(logical_request):
- if block.encoding == _HANDSHAKE_ENCODING_IDENTITY:
- # Update handshake base.
- # TODO(bashi): Make sure this is the right place to update
- # handshake base.
- self._handshake_base = _HandshakeDeltaBase(
- logical_request.headers_in)
- self._add_logical_channel(logical_request)
- else:
- self._send_error_add_channel_response(
- block.channel_id, status=common.HTTP_STATUS_BAD_REQUEST)
-
- def _process_flow_control(self, block):
- try:
- self._logical_channels_condition.acquire()
- if not block.channel_id in self._logical_channels:
- return
- channel_data = self._logical_channels[block.channel_id]
- channel_data.request.ws_stream.replenish_send_quota(
- block.send_quota)
- finally:
- self._logical_channels_condition.release()
-
- def _process_drop_channel(self, block):
- self._logger.debug(
- 'DropChannel received for %d: code=%r, reason=%r' %
- (block.channel_id, block.drop_code, block.drop_message))
- try:
- self._logical_channels_condition.acquire()
- if not block.channel_id in self._logical_channels:
- return
- channel_data = self._logical_channels[block.channel_id]
- channel_data.drop_code = _DROP_CODE_ACKNOWLEDGED
-
- # Close the logical channel
- channel_data.request.connection.set_read_state(
- _LogicalConnection.STATE_TERMINATED)
- channel_data.request.ws_stream.stop_sending()
- finally:
- self._logical_channels_condition.release()
-
- def _process_control_blocks(self, parser):
- for control_block in parser.read_control_blocks():
- opcode = control_block.opcode
- self._logger.debug('control block received, opcode: %d' % opcode)
- if opcode == _MUX_OPCODE_ADD_CHANNEL_REQUEST:
- self._process_add_channel_request(control_block)
- elif opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Received AddChannelResponse')
- elif opcode == _MUX_OPCODE_FLOW_CONTROL:
- self._process_flow_control(control_block)
- elif opcode == _MUX_OPCODE_DROP_CHANNEL:
- self._process_drop_channel(control_block)
- elif opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
- raise PhysicalConnectionError(
- _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- 'Received NewChannelSlot')
- else:
- raise MuxUnexpectedException(
- 'Unexpected opcode %r' % opcode)
-
- def _process_logical_frame(self, channel_id, parser):
- self._logger.debug('Received a frame. channel id=%d' % channel_id)
- try:
- self._logical_channels_condition.acquire()
- if not channel_id in self._logical_channels:
- # We must ignore the message for an inactive channel.
- return
- channel_data = self._logical_channels[channel_id]
- fin, rsv1, rsv2, rsv3, opcode, payload = parser.read_inner_frame()
- consuming_byte = len(payload)
- if opcode != common.OPCODE_CONTINUATION:
- consuming_byte += 1
- if not channel_data.request.ws_stream.consume_receive_quota(
- consuming_byte):
- # The client violates quota. Close logical channel.
- raise LogicalChannelError(
- channel_id, _DROP_CODE_SEND_QUOTA_VIOLATION)
- header = create_header(opcode, len(payload), fin, rsv1, rsv2, rsv3,
- mask=False)
- frame_data = header + payload
- channel_data.request.connection.append_frame_data(frame_data)
- finally:
- self._logical_channels_condition.release()
-
- def dispatch_message(self, message):
- """Dispatches message. The reader thread calls this method.
-
- Args:
- message: a message that contains encapsulated frame.
- Raises:
- PhysicalConnectionError: if the message contains physical
- connection level errors.
- LogicalChannelError: if the message contains logical channel
- level errors.
- """
-
- parser = _MuxFramePayloadParser(message)
- try:
- channel_id = parser.read_channel_id()
- except ValueError, e:
- raise PhysicalConnectionError(_DROP_CODE_CHANNEL_ID_TRUNCATED)
- if channel_id == _CONTROL_CHANNEL_ID:
- self._process_control_blocks(parser)
- else:
- self._process_logical_frame(channel_id, parser)
-
- def notify_worker_done(self, channel_id):
- """Called when a worker has finished.
-
- Args:
- channel_id: channel id corresponded with the worker.
- """
-
- self._logger.debug('Worker for channel id %d terminated' % channel_id)
- try:
- self._logical_channels_condition.acquire()
- if not channel_id in self._logical_channels:
- raise MuxUnexpectedException(
- 'Channel id %d not found' % channel_id)
- channel_data = self._logical_channels.pop(channel_id)
- finally:
- self._worker_done_notify_received = True
- self._logical_channels_condition.notify()
- self._logical_channels_condition.release()
-
- if not channel_data.request.server_terminated:
- self._send_drop_channel(
- channel_id, code=channel_data.drop_code,
- message=channel_data.drop_message)
-
- def notify_reader_done(self):
- """This method is called by the reader thread when the reader has
- finished.
- """
-
- self._logger.debug(
- 'Termiating all logical connections waiting for incoming data '
- '...')
- self._logical_channels_condition.acquire()
- for channel_data in self._logical_channels.values():
- try:
- channel_data.request.connection.set_read_state(
- _LogicalConnection.STATE_TERMINATED)
- except Exception:
- self._logger.debug(traceback.format_exc())
- self._logical_channels_condition.release()
-
- def notify_writer_done(self):
- """This method is called by the writer thread when the writer has
- finished.
- """
-
- self._logger.debug(
- 'Termiating all logical connections waiting for write '
- 'completion ...')
- self._logical_channels_condition.acquire()
- for channel_data in self._logical_channels.values():
- try:
- channel_data.request.connection.on_writer_done()
- except Exception:
- self._logger.debug(traceback.format_exc())
- self._logical_channels_condition.release()
-
- def fail_physical_connection(self, code, message):
- """Fail the physical connection.
-
- Args:
- code: drop reason code.
- message: drop message.
- """
-
- self._logger.debug('Failing the physical connection...')
- self._send_drop_channel(_CONTROL_CHANNEL_ID, code, message)
- self._writer.stop(common.STATUS_INTERNAL_ENDPOINT_ERROR)
-
- def fail_logical_channel(self, channel_id, code, message):
- """Fail a logical channel.
-
- Args:
- channel_id: channel id.
- code: drop reason code.
- message: drop message.
- """
-
- self._logger.debug('Failing logical channel %d...' % channel_id)
- try:
- self._logical_channels_condition.acquire()
- if channel_id in self._logical_channels:
- channel_data = self._logical_channels[channel_id]
- # Close the logical channel. notify_worker_done() will be
- # called later and it will send DropChannel.
- channel_data.drop_code = code
- channel_data.drop_message = message
-
- channel_data.request.connection.set_read_state(
- _LogicalConnection.STATE_TERMINATED)
- channel_data.request.ws_stream.stop_sending()
- else:
- self._send_drop_channel(channel_id, code, message)
- finally:
- self._logical_channels_condition.release()
-
-
-def use_mux(request):
- return hasattr(request, 'mux_processor') and (
- request.mux_processor.is_active())
-
-
-def start(request, dispatcher):
- mux_handler = _MuxHandler(request, dispatcher)
- mux_handler.start()
-
- mux_handler.add_channel_slots(_INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- _INITIAL_QUOTA_FOR_CLIENT)
-
- mux_handler.wait_until_done()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/standalone.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/standalone.py
deleted file mode 100755
index 24c299eaf..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/standalone.py
+++ /dev/null
@@ -1,1193 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""Standalone WebSocket server.
-
-Use this file to launch pywebsocket without Apache HTTP Server.
-
-
-BASIC USAGE
-===========
-
-Go to the src directory and run
-
- $ python mod_pywebsocket/standalone.py [-p <ws_port>]
- [-w <websock_handlers>]
- [-d <document_root>]
-
-<ws_port> is the port number to use for ws:// connection.
-
-<document_root> is the path to the root directory of HTML files.
-
-<websock_handlers> is the path to the root directory of WebSocket handlers.
-If not specified, <document_root> will be used. See __init__.py (or
-run $ pydoc mod_pywebsocket) for how to write WebSocket handlers.
-
-For more detail and other options, run
-
- $ python mod_pywebsocket/standalone.py --help
-
-or see _build_option_parser method below.
-
-For trouble shooting, adding "--log_level debug" might help you.
-
-
-TRY DEMO
-========
-
-Go to the src directory and run standalone.py with -d option to set the
-document root to the directory containing example HTMLs and handlers like this:
-
- $ cd src
- $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example
-
-to launch pywebsocket with the sample handler and html on port 80. Open
-http://localhost/console.html, click the connect button, type something into
-the text box next to the send button and click the send button. If everything
-is working, you'll see the message you typed echoed by the server.
-
-
-USING TLS
-=========
-
-To run the standalone server with TLS support, run it with -t, -k, and -c
-options. When TLS is enabled, the standalone server accepts only TLS connection.
-
-Note that when ssl module is used and the key/cert location is incorrect,
-TLS connection silently fails while pyOpenSSL fails on startup.
-
-Example:
-
- $ PYTHONPATH=. python mod_pywebsocket/standalone.py \
- -d example \
- -p 10443 \
- -t \
- -c ../test/cert/cert.pem \
- -k ../test/cert/key.pem \
-
-Note that when passing a relative path to -c and -k option, it will be resolved
-using the document root directory as the base.
-
-
-USING CLIENT AUTHENTICATION
-===========================
-
-To run the standalone server with TLS client authentication support, run it with
---tls-client-auth and --tls-client-ca options in addition to ones required for
-TLS support.
-
-Example:
-
- $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example -p 10443 -t \
- -c ../test/cert/cert.pem -k ../test/cert/key.pem \
- --tls-client-auth \
- --tls-client-ca=../test/cert/cacert.pem
-
-Note that when passing a relative path to --tls-client-ca option, it will be
-resolved using the document root directory as the base.
-
-
-CONFIGURATION FILE
-==================
-
-You can also write a configuration file and use it by specifying the path to
-the configuration file by --config option. Please write a configuration file
-following the documentation of the Python ConfigParser library. Name of each
-entry must be the long version argument name. E.g. to set log level to debug,
-add the following line:
-
-log_level=debug
-
-For options which doesn't take value, please add some fake value. E.g. for
---tls option, add the following line:
-
-tls=True
-
-Note that tls will be enabled even if you write tls=False as the value part is
-fake.
-
-When both a command line argument and a configuration file entry are set for
-the same configuration item, the command line value will override one in the
-configuration file.
-
-
-THREADING
-=========
-
-This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
-used for each request.
-
-
-SECURITY WARNING
-================
-
-This uses CGIHTTPServer and CGIHTTPServer is not secure.
-It may execute arbitrary Python code or external programs. It should not be
-used outside a firewall.
-"""
-
-import BaseHTTPServer
-import CGIHTTPServer
-import SimpleHTTPServer
-import SocketServer
-import ConfigParser
-import base64
-import httplib
-import logging
-import logging.handlers
-import optparse
-import os
-import re
-import select
-import socket
-import sys
-import threading
-import time
-
-from mod_pywebsocket import common
-from mod_pywebsocket import dispatch
-from mod_pywebsocket import handshake
-from mod_pywebsocket import http_header_util
-from mod_pywebsocket import memorizingfile
-from mod_pywebsocket import util
-from mod_pywebsocket.xhr_benchmark_handler import XHRBenchmarkHandler
-
-
-_DEFAULT_LOG_MAX_BYTES = 1024 * 256
-_DEFAULT_LOG_BACKUP_COUNT = 5
-
-_DEFAULT_REQUEST_QUEUE_SIZE = 128
-
-# 1024 is practically large enough to contain WebSocket handshake lines.
-_MAX_MEMORIZED_LINES = 1024
-
-# Constants for the --tls_module flag.
-_TLS_BY_STANDARD_MODULE = 'ssl'
-_TLS_BY_PYOPENSSL = 'pyopenssl'
-
-
-class _StandaloneConnection(object):
- """Mimic mod_python mp_conn."""
-
- def __init__(self, request_handler):
- """Construct an instance.
-
- Args:
- request_handler: A WebSocketRequestHandler instance.
- """
-
- self._request_handler = request_handler
-
- def get_local_addr(self):
- """Getter to mimic mp_conn.local_addr."""
-
- return (self._request_handler.server.server_name,
- self._request_handler.server.server_port)
- local_addr = property(get_local_addr)
-
- def get_remote_addr(self):
- """Getter to mimic mp_conn.remote_addr.
-
- Setting the property in __init__ won't work because the request
- handler is not initialized yet there."""
-
- return self._request_handler.client_address
- remote_addr = property(get_remote_addr)
-
- def write(self, data):
- """Mimic mp_conn.write()."""
-
- return self._request_handler.wfile.write(data)
-
- def read(self, length):
- """Mimic mp_conn.read()."""
-
- return self._request_handler.rfile.read(length)
-
- def get_memorized_lines(self):
- """Get memorized lines."""
-
- return self._request_handler.rfile.get_memorized_lines()
-
-
-class _StandaloneRequest(object):
- """Mimic mod_python request."""
-
- def __init__(self, request_handler, use_tls):
- """Construct an instance.
-
- Args:
- request_handler: A WebSocketRequestHandler instance.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request_handler = request_handler
- self.connection = _StandaloneConnection(request_handler)
- self._use_tls = use_tls
- self.headers_in = request_handler.headers
-
- def get_uri(self):
- """Getter to mimic request.uri.
-
- This method returns the raw data at the Request-URI part of the
- Request-Line, while the uri method on the request object of mod_python
- returns the path portion after parsing the raw data. This behavior is
- kept for compatibility.
- """
-
- return self._request_handler.path
- uri = property(get_uri)
-
- def get_unparsed_uri(self):
- """Getter to mimic request.unparsed_uri."""
-
- return self._request_handler.path
- unparsed_uri = property(get_unparsed_uri)
-
- def get_method(self):
- """Getter to mimic request.method."""
-
- return self._request_handler.command
- method = property(get_method)
-
- def get_protocol(self):
- """Getter to mimic request.protocol."""
-
- return self._request_handler.request_version
- protocol = property(get_protocol)
-
- def is_https(self):
- """Mimic request.is_https()."""
-
- return self._use_tls
-
-
-def _import_ssl():
- global ssl
- try:
- import ssl
- return True
- except ImportError:
- return False
-
-
-def _import_pyopenssl():
- global OpenSSL
- try:
- import OpenSSL.SSL
- return True
- except ImportError:
- return False
-
-
-class _StandaloneSSLConnection(object):
- """A wrapper class for OpenSSL.SSL.Connection to
- - provide makefile method which is not supported by the class
- - tweak shutdown method since OpenSSL.SSL.Connection.shutdown doesn't
- accept the "how" argument.
- - convert SysCallError exceptions that its recv method may raise into a
- return value of '', meaning EOF. We cannot overwrite the recv method on
- self._connection since it's immutable.
- """
-
- _OVERRIDDEN_ATTRIBUTES = ['_connection', 'makefile', 'shutdown', 'recv']
-
- def __init__(self, connection):
- self._connection = connection
-
- def __getattribute__(self, name):
- if name in _StandaloneSSLConnection._OVERRIDDEN_ATTRIBUTES:
- return object.__getattribute__(self, name)
- return self._connection.__getattribute__(name)
-
- def __setattr__(self, name, value):
- if name in _StandaloneSSLConnection._OVERRIDDEN_ATTRIBUTES:
- return object.__setattr__(self, name, value)
- return self._connection.__setattr__(name, value)
-
- def makefile(self, mode='r', bufsize=-1):
- return socket._fileobject(self, mode, bufsize)
-
- def shutdown(self, unused_how):
- self._connection.shutdown()
-
- def recv(self, bufsize, flags=0):
- if flags != 0:
- raise ValueError('Non-zero flags not allowed')
-
- try:
- return self._connection.recv(bufsize)
- except OpenSSL.SSL.SysCallError, (err, message):
- if err == -1:
- # Suppress "unexpected EOF" exception. See the OpenSSL document
- # for SSL_get_error.
- return ''
- raise
-
-
-def _alias_handlers(dispatcher, websock_handlers_map_file):
- """Set aliases specified in websock_handler_map_file in dispatcher.
-
- Args:
- dispatcher: dispatch.Dispatcher instance
- websock_handler_map_file: alias map file
- """
-
- fp = open(websock_handlers_map_file)
- try:
- for line in fp:
- if line[0] == '#' or line.isspace():
- continue
- m = re.match('(\S+)\s+(\S+)', line)
- if not m:
- logging.warning('Wrong format in map file:' + line)
- continue
- try:
- dispatcher.add_resource_path_alias(
- m.group(1), m.group(2))
- except dispatch.DispatchException, e:
- logging.error(str(e))
- finally:
- fp.close()
-
-
-class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
- """HTTPServer specialized for WebSocket."""
-
- # Overrides SocketServer.ThreadingMixIn.daemon_threads
- daemon_threads = True
- # Overrides BaseHTTPServer.HTTPServer.allow_reuse_address
- allow_reuse_address = True
-
- def __init__(self, options):
- """Override SocketServer.TCPServer.__init__ to set SSL enabled
- socket object to self.socket before server_bind and server_activate,
- if necessary.
- """
-
- # Share a Dispatcher among request handlers to save time for
- # instantiation. Dispatcher can be shared because it is thread-safe.
- options.dispatcher = dispatch.Dispatcher(
- options.websock_handlers,
- options.scan_dir,
- options.allow_handlers_outside_root_dir)
- if options.websock_handlers_map_file:
- _alias_handlers(options.dispatcher,
- options.websock_handlers_map_file)
- warnings = options.dispatcher.source_warnings()
- if warnings:
- for warning in warnings:
- logging.warning('Warning in source loading: %s' % warning)
-
- self._logger = util.get_class_logger(self)
-
- self.request_queue_size = options.request_queue_size
- self.__ws_is_shut_down = threading.Event()
- self.__ws_serving = False
-
- SocketServer.BaseServer.__init__(
- self, (options.server_host, options.port), WebSocketRequestHandler)
-
- # Expose the options object to allow handler objects access it. We name
- # it with websocket_ prefix to avoid conflict.
- self.websocket_server_options = options
-
- self._create_sockets()
- self.server_bind()
- self.server_activate()
-
- def _create_sockets(self):
- self.server_name, self.server_port = self.server_address
- self._sockets = []
- if not self.server_name:
- # On platforms that doesn't support IPv6, the first bind fails.
- # On platforms that supports IPv6
- # - If it binds both IPv4 and IPv6 on call with AF_INET6, the
- # first bind succeeds and the second fails (we'll see 'Address
- # already in use' error).
- # - If it binds only IPv6 on call with AF_INET6, both call are
- # expected to succeed to listen both protocol.
- addrinfo_array = [
- (socket.AF_INET6, socket.SOCK_STREAM, '', '', ''),
- (socket.AF_INET, socket.SOCK_STREAM, '', '', '')]
- else:
- addrinfo_array = socket.getaddrinfo(self.server_name,
- self.server_port,
- socket.AF_UNSPEC,
- socket.SOCK_STREAM,
- socket.IPPROTO_TCP)
- for addrinfo in addrinfo_array:
- self._logger.info('Create socket on: %r', addrinfo)
- family, socktype, proto, canonname, sockaddr = addrinfo
- try:
- socket_ = socket.socket(family, socktype)
- except Exception, e:
- self._logger.info('Skip by failure: %r', e)
- continue
- server_options = self.websocket_server_options
- if server_options.use_tls:
- # For the case of _HAS_OPEN_SSL, we do wrapper setup after
- # accept.
- if server_options.tls_module == _TLS_BY_STANDARD_MODULE:
- if server_options.tls_client_auth:
- if server_options.tls_client_cert_optional:
- client_cert_ = ssl.CERT_OPTIONAL
- else:
- client_cert_ = ssl.CERT_REQUIRED
- else:
- client_cert_ = ssl.CERT_NONE
- socket_ = ssl.wrap_socket(socket_,
- keyfile=server_options.private_key,
- certfile=server_options.certificate,
- ssl_version=ssl.PROTOCOL_SSLv23,
- ca_certs=server_options.tls_client_ca,
- cert_reqs=client_cert_,
- do_handshake_on_connect=False)
- self._sockets.append((socket_, addrinfo))
-
- def server_bind(self):
- """Override SocketServer.TCPServer.server_bind to enable multiple
- sockets bind.
- """
-
- failed_sockets = []
-
- for socketinfo in self._sockets:
- socket_, addrinfo = socketinfo
- self._logger.info('Bind on: %r', addrinfo)
- if self.allow_reuse_address:
- socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- try:
- socket_.bind(self.server_address)
- except Exception, e:
- self._logger.info('Skip by failure: %r', e)
- socket_.close()
- failed_sockets.append(socketinfo)
- if self.server_address[1] == 0:
- # The operating system assigns the actual port number for port
- # number 0. This case, the second and later sockets should use
- # the same port number. Also self.server_port is rewritten
- # because it is exported, and will be used by external code.
- self.server_address = (
- self.server_name, socket_.getsockname()[1])
- self.server_port = self.server_address[1]
- self._logger.info('Port %r is assigned', self.server_port)
-
- for socketinfo in failed_sockets:
- self._sockets.remove(socketinfo)
-
- def server_activate(self):
- """Override SocketServer.TCPServer.server_activate to enable multiple
- sockets listen.
- """
-
- failed_sockets = []
-
- for socketinfo in self._sockets:
- socket_, addrinfo = socketinfo
- self._logger.info('Listen on: %r', addrinfo)
- try:
- socket_.listen(self.request_queue_size)
- except Exception, e:
- self._logger.info('Skip by failure: %r', e)
- socket_.close()
- failed_sockets.append(socketinfo)
-
- for socketinfo in failed_sockets:
- self._sockets.remove(socketinfo)
-
- if len(self._sockets) == 0:
- self._logger.critical(
- 'No sockets activated. Use info log level to see the reason.')
-
- def server_close(self):
- """Override SocketServer.TCPServer.server_close to enable multiple
- sockets close.
- """
-
- for socketinfo in self._sockets:
- socket_, addrinfo = socketinfo
- self._logger.info('Close on: %r', addrinfo)
- socket_.close()
-
- def fileno(self):
- """Override SocketServer.TCPServer.fileno."""
-
- self._logger.critical('Not supported: fileno')
- return self._sockets[0][0].fileno()
-
- def handle_error(self, request, client_address):
- """Override SocketServer.handle_error."""
-
- self._logger.error(
- 'Exception in processing request from: %r\n%s',
- client_address,
- util.get_stack_trace())
- # Note: client_address is a tuple.
-
- def get_request(self):
- """Override TCPServer.get_request to wrap OpenSSL.SSL.Connection
- object with _StandaloneSSLConnection to provide makefile method. We
- cannot substitute OpenSSL.SSL.Connection.makefile since it's readonly
- attribute.
- """
-
- accepted_socket, client_address = self.socket.accept()
-
- server_options = self.websocket_server_options
- if server_options.use_tls:
- if server_options.tls_module == _TLS_BY_STANDARD_MODULE:
- try:
- accepted_socket.do_handshake()
- except ssl.SSLError, e:
- self._logger.debug('%r', e)
- raise
-
- # Print cipher in use. Handshake is done on accept.
- self._logger.debug('Cipher: %s', accepted_socket.cipher())
- self._logger.debug('Client cert: %r',
- accepted_socket.getpeercert())
- elif server_options.tls_module == _TLS_BY_PYOPENSSL:
- # We cannot print the cipher in use. pyOpenSSL doesn't provide
- # any method to fetch that.
-
- ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
- ctx.use_privatekey_file(server_options.private_key)
- ctx.use_certificate_file(server_options.certificate)
-
- def default_callback(conn, cert, errnum, errdepth, ok):
- return ok == 1
-
- # See the OpenSSL document for SSL_CTX_set_verify.
- if server_options.tls_client_auth:
- verify_mode = OpenSSL.SSL.VERIFY_PEER
- if not server_options.tls_client_cert_optional:
- verify_mode |= OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT
- ctx.set_verify(verify_mode, default_callback)
- ctx.load_verify_locations(server_options.tls_client_ca,
- None)
- else:
- ctx.set_verify(OpenSSL.SSL.VERIFY_NONE, default_callback)
-
- accepted_socket = OpenSSL.SSL.Connection(ctx, accepted_socket)
- accepted_socket.set_accept_state()
-
- # Convert SSL related error into socket.error so that
- # SocketServer ignores them and keeps running.
- #
- # TODO(tyoshino): Convert all kinds of errors.
- try:
- accepted_socket.do_handshake()
- except OpenSSL.SSL.Error, e:
- # Set errno part to 1 (SSL_ERROR_SSL) like the ssl module
- # does.
- self._logger.debug('%r', e)
- raise socket.error(1, '%r' % e)
- cert = accepted_socket.get_peer_certificate()
- if cert is not None:
- self._logger.debug('Client cert subject: %r',
- cert.get_subject().get_components())
- accepted_socket = _StandaloneSSLConnection(accepted_socket)
- else:
- raise ValueError('No TLS support module is available')
-
- return accepted_socket, client_address
-
- def serve_forever(self, poll_interval=0.5):
- """Override SocketServer.BaseServer.serve_forever."""
-
- self.__ws_serving = True
- self.__ws_is_shut_down.clear()
- handle_request = self.handle_request
- if hasattr(self, '_handle_request_noblock'):
- handle_request = self._handle_request_noblock
- else:
- self._logger.warning('Fallback to blocking request handler')
- try:
- while self.__ws_serving:
- r, w, e = select.select(
- [socket_[0] for socket_ in self._sockets],
- [], [], poll_interval)
- for socket_ in r:
- self.socket = socket_
- handle_request()
- self.socket = None
- finally:
- self.__ws_is_shut_down.set()
-
- def shutdown(self):
- """Override SocketServer.BaseServer.shutdown."""
-
- self.__ws_serving = False
- self.__ws_is_shut_down.wait()
-
-
-class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
- """CGIHTTPRequestHandler specialized for WebSocket."""
-
- # Use httplib.HTTPMessage instead of mimetools.Message.
- MessageClass = httplib.HTTPMessage
-
- protocol_version = "HTTP/1.1"
-
- def setup(self):
- """Override SocketServer.StreamRequestHandler.setup to wrap rfile
- with MemorizingFile.
-
- This method will be called by BaseRequestHandler's constructor
- before calling BaseHTTPRequestHandler.handle.
- BaseHTTPRequestHandler.handle will call
- BaseHTTPRequestHandler.handle_one_request and it will call
- WebSocketRequestHandler.parse_request.
- """
-
- # Call superclass's setup to prepare rfile, wfile, etc. See setup
- # definition on the root class SocketServer.StreamRequestHandler to
- # understand what this does.
- CGIHTTPServer.CGIHTTPRequestHandler.setup(self)
-
- self.rfile = memorizingfile.MemorizingFile(
- self.rfile,
- max_memorized_lines=_MAX_MEMORIZED_LINES)
-
- def __init__(self, request, client_address, server):
- self._logger = util.get_class_logger(self)
-
- self._options = server.websocket_server_options
-
- # Overrides CGIHTTPServerRequestHandler.cgi_directories.
- self.cgi_directories = self._options.cgi_directories
- # Replace CGIHTTPRequestHandler.is_executable method.
- if self._options.is_executable_method is not None:
- self.is_executable = self._options.is_executable_method
-
- # This actually calls BaseRequestHandler.__init__.
- CGIHTTPServer.CGIHTTPRequestHandler.__init__(
- self, request, client_address, server)
-
- def parse_request(self):
- """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
-
- Return True to continue processing for HTTP(S), False otherwise.
-
- See BaseHTTPRequestHandler.handle_one_request method which calls
- this method to understand how the return value will be handled.
- """
-
- # We hook parse_request method, but also call the original
- # CGIHTTPRequestHandler.parse_request since when we return False,
- # CGIHTTPRequestHandler.handle_one_request continues processing and
- # it needs variables set by CGIHTTPRequestHandler.parse_request.
- #
- # Variables set by this method will be also used by WebSocket request
- # handling (self.path, self.command, self.requestline, etc. See also
- # how _StandaloneRequest's members are implemented using these
- # attributes).
- if not CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self):
- return False
-
- if self.command == "CONNECT":
- self.send_response(200, "Connected")
- self.send_header("Connection", "keep-alive")
- self.end_headers()
- return False
-
- if self._options.use_basic_auth:
- auth = self.headers.getheader('Authorization')
- if auth != self._options.basic_auth_credential:
- self.send_response(401)
- self.send_header('WWW-Authenticate',
- 'Basic realm="Pywebsocket"')
- self.end_headers()
- self._logger.info('Request basic authentication')
- return False
-
- host, port, resource = http_header_util.parse_uri(self.path)
-
- # Special paths for XMLHttpRequest benchmark
- xhr_benchmark_helper_prefix = '/073be001e10950692ccbf3a2ad21c245'
- if resource == (xhr_benchmark_helper_prefix + '_send'):
- xhr_benchmark_handler = XHRBenchmarkHandler(
- self.headers, self.rfile, self.wfile)
- xhr_benchmark_handler.do_send()
- return False
- if resource == (xhr_benchmark_helper_prefix + '_receive'):
- xhr_benchmark_handler = XHRBenchmarkHandler(
- self.headers, self.rfile, self.wfile)
- xhr_benchmark_handler.do_receive()
- return False
-
- if resource is None:
- self._logger.info('Invalid URI: %r', self.path)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- server_options = self.server.websocket_server_options
- if host is not None:
- validation_host = server_options.validation_host
- if validation_host is not None and host != validation_host:
- self._logger.info('Invalid host: %r (expected: %r)',
- host,
- validation_host)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- if port is not None:
- validation_port = server_options.validation_port
- if validation_port is not None and port != validation_port:
- self._logger.info('Invalid port: %r (expected: %r)',
- port,
- validation_port)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- self.path = resource
-
- request = _StandaloneRequest(self, self._options.use_tls)
-
- try:
- # Fallback to default http handler for request paths for which
- # we don't have request handlers.
- if not self._options.dispatcher.get_handler_suite(self.path):
- self._logger.info('No handler for resource: %r',
- self.path)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- except dispatch.DispatchException, e:
- self._logger.info('Dispatch failed for error: %s', e)
- self.send_error(e.status)
- return False
-
- # If any Exceptions without except clause setup (including
- # DispatchException) is raised below this point, it will be caught
- # and logged by WebSocketServer.
-
- try:
- try:
- handshake.do_handshake(
- request,
- self._options.dispatcher,
- allowDraft75=self._options.allow_draft75,
- strict=self._options.strict)
- except handshake.VersionException, e:
- self._logger.info('Handshake failed for version error: %s', e)
- self.send_response(common.HTTP_STATUS_BAD_REQUEST)
- self.send_header(common.SEC_WEBSOCKET_VERSION_HEADER,
- e.supported_versions)
- self.end_headers()
- return False
- except handshake.HandshakeException, e:
- # Handshake for ws(s) failed.
- self._logger.info('Handshake failed for error: %s', e)
- self.send_error(e.status)
- return False
-
- request._dispatcher = self._options.dispatcher
- self._options.dispatcher.transfer_data(request)
- except handshake.AbortedByUserException, e:
- self._logger.info('Aborted: %s', e)
- return False
-
- def log_request(self, code='-', size='-'):
- """Override BaseHTTPServer.log_request."""
-
- self._logger.info('"%s" %s %s',
- self.requestline, str(code), str(size))
-
- def log_error(self, *args):
- """Override BaseHTTPServer.log_error."""
-
- # Despite the name, this method is for warnings than for errors.
- # For example, HTTP status code is logged by this method.
- self._logger.warning('%s - %s',
- self.address_string(),
- args[0] % args[1:])
-
- def is_cgi(self):
- """Test whether self.path corresponds to a CGI script.
-
- Add extra check that self.path doesn't contains ..
- Also check if the file is a executable file or not.
- If the file is not executable, it is handled as static file or dir
- rather than a CGI script.
- """
-
- if CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self):
- if '..' in self.path:
- return False
- # strip query parameter from request path
- resource_name = self.path.split('?', 2)[0]
- # convert resource_name into real path name in filesystem.
- scriptfile = self.translate_path(resource_name)
- if not os.path.isfile(scriptfile):
- return False
- if not self.is_executable(scriptfile):
- return False
- return True
- return False
-
-
-def _get_logger_from_class(c):
- return logging.getLogger('%s.%s' % (c.__module__, c.__name__))
-
-
-def _configure_logging(options):
- logging.addLevelName(common.LOGLEVEL_FINE, 'FINE')
-
- logger = logging.getLogger()
- logger.setLevel(logging.getLevelName(options.log_level.upper()))
- if options.log_file:
- handler = logging.handlers.RotatingFileHandler(
- options.log_file, 'a', options.log_max, options.log_count)
- else:
- handler = logging.StreamHandler()
- formatter = logging.Formatter(
- '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s')
- handler.setFormatter(formatter)
- logger.addHandler(handler)
-
- deflate_log_level_name = logging.getLevelName(
- options.deflate_log_level.upper())
- _get_logger_from_class(util._Deflater).setLevel(
- deflate_log_level_name)
- _get_logger_from_class(util._Inflater).setLevel(
- deflate_log_level_name)
-
-
-def _build_option_parser():
- parser = optparse.OptionParser()
-
- parser.add_option('--config', dest='config_file', type='string',
- default=None,
- help=('Path to configuration file. See the file comment '
- 'at the top of this file for the configuration '
- 'file format'))
- parser.add_option('-H', '--server-host', '--server_host',
- dest='server_host',
- default='',
- help='server hostname to listen to')
- parser.add_option('-V', '--validation-host', '--validation_host',
- dest='validation_host',
- default=None,
- help='server hostname to validate in absolute path.')
- parser.add_option('-p', '--port', dest='port', type='int',
- default=common.DEFAULT_WEB_SOCKET_PORT,
- help='port to listen to')
- parser.add_option('-P', '--validation-port', '--validation_port',
- dest='validation_port', type='int',
- default=None,
- help='server port to validate in absolute path.')
- parser.add_option('-w', '--websock-handlers', '--websock_handlers',
- dest='websock_handlers',
- default='.',
- help=('The root directory of WebSocket handler files. '
- 'If the path is relative, --document-root is used '
- 'as the base.'))
- parser.add_option('-m', '--websock-handlers-map-file',
- '--websock_handlers_map_file',
- dest='websock_handlers_map_file',
- default=None,
- help=('WebSocket handlers map file. '
- 'Each line consists of alias_resource_path and '
- 'existing_resource_path, separated by spaces.'))
- parser.add_option('-s', '--scan-dir', '--scan_dir', dest='scan_dir',
- default=None,
- help=('Must be a directory under --websock-handlers. '
- 'Only handlers under this directory are scanned '
- 'and registered to the server. '
- 'Useful for saving scan time when the handler '
- 'root directory contains lots of files that are '
- 'not handler file or are handler files but you '
- 'don\'t want them to be registered. '))
- parser.add_option('--allow-handlers-outside-root-dir',
- '--allow_handlers_outside_root_dir',
- dest='allow_handlers_outside_root_dir',
- action='store_true',
- default=False,
- help=('Scans WebSocket handlers even if their canonical '
- 'path is not under --websock-handlers.'))
- parser.add_option('-d', '--document-root', '--document_root',
- dest='document_root', default='.',
- help='Document root directory.')
- parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths',
- default=None,
- help=('CGI paths relative to document_root.'
- 'Comma-separated. (e.g -x /cgi,/htbin) '
- 'Files under document_root/cgi_path are handled '
- 'as CGI programs. Must be executable.'))
- parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
- default=False, help='use TLS (wss://)')
- parser.add_option('--tls-module', '--tls_module', dest='tls_module',
- type='choice',
- choices = [_TLS_BY_STANDARD_MODULE, _TLS_BY_PYOPENSSL],
- help='Use ssl module if "%s" is specified. '
- 'Use pyOpenSSL module if "%s" is specified' %
- (_TLS_BY_STANDARD_MODULE, _TLS_BY_PYOPENSSL))
- parser.add_option('-k', '--private-key', '--private_key',
- dest='private_key',
- default='', help='TLS private key file.')
- parser.add_option('-c', '--certificate', dest='certificate',
- default='', help='TLS certificate file.')
- parser.add_option('--tls-client-auth', dest='tls_client_auth',
- action='store_true', default=False,
- help='Requests TLS client auth on every connection.')
- parser.add_option('--tls-client-cert-optional',
- dest='tls_client_cert_optional',
- action='store_true', default=False,
- help=('Makes client certificate optional even though '
- 'TLS client auth is enabled.'))
- parser.add_option('--tls-client-ca', dest='tls_client_ca', default='',
- help=('Specifies a pem file which contains a set of '
- 'concatenated CA certificates which are used to '
- 'validate certificates passed from clients'))
- parser.add_option('--basic-auth', dest='use_basic_auth',
- action='store_true', default=False,
- help='Requires Basic authentication.')
- parser.add_option('--basic-auth-credential',
- dest='basic_auth_credential', default='test:test',
- help='Specifies the credential of basic authentication '
- 'by username:password pair (e.g. test:test).')
- parser.add_option('-l', '--log-file', '--log_file', dest='log_file',
- default='', help='Log file.')
- # Custom log level:
- # - FINE: Prints status of each frame processing step
- parser.add_option('--log-level', '--log_level', type='choice',
- dest='log_level', default='warn',
- choices=['fine',
- 'debug', 'info', 'warning', 'warn', 'error',
- 'critical'],
- help='Log level.')
- parser.add_option('--deflate-log-level', '--deflate_log_level',
- type='choice',
- dest='deflate_log_level', default='warn',
- choices=['debug', 'info', 'warning', 'warn', 'error',
- 'critical'],
- help='Log level for _Deflater and _Inflater.')
- parser.add_option('--thread-monitor-interval-in-sec',
- '--thread_monitor_interval_in_sec',
- dest='thread_monitor_interval_in_sec',
- type='int', default=-1,
- help=('If positive integer is specified, run a thread '
- 'monitor to show the status of server threads '
- 'periodically in the specified inteval in '
- 'second. If non-positive integer is specified, '
- 'disable the thread monitor.'))
- parser.add_option('--log-max', '--log_max', dest='log_max', type='int',
- default=_DEFAULT_LOG_MAX_BYTES,
- help='Log maximum bytes')
- parser.add_option('--log-count', '--log_count', dest='log_count',
- type='int', default=_DEFAULT_LOG_BACKUP_COUNT,
- help='Log backup count')
- parser.add_option('--allow-draft75', dest='allow_draft75',
- action='store_true', default=False,
- help='Obsolete option. Ignored.')
- parser.add_option('--strict', dest='strict', action='store_true',
- default=False, help='Obsolete option. Ignored.')
- parser.add_option('-q', '--queue', dest='request_queue_size', type='int',
- default=_DEFAULT_REQUEST_QUEUE_SIZE,
- help='request queue size')
-
- return parser
-
-
-class ThreadMonitor(threading.Thread):
- daemon = True
-
- def __init__(self, interval_in_sec):
- threading.Thread.__init__(self, name='ThreadMonitor')
-
- self._logger = util.get_class_logger(self)
-
- self._interval_in_sec = interval_in_sec
-
- def run(self):
- while True:
- thread_name_list = []
- for thread in threading.enumerate():
- thread_name_list.append(thread.name)
- self._logger.info(
- "%d active threads: %s",
- threading.active_count(),
- ', '.join(thread_name_list))
- time.sleep(self._interval_in_sec)
-
-
-def _parse_args_and_config(args):
- parser = _build_option_parser()
-
- # First, parse options without configuration file.
- temporary_options, temporary_args = parser.parse_args(args=args)
- if temporary_args:
- logging.critical(
- 'Unrecognized positional arguments: %r', temporary_args)
- sys.exit(1)
-
- if temporary_options.config_file:
- try:
- config_fp = open(temporary_options.config_file, 'r')
- except IOError, e:
- logging.critical(
- 'Failed to open configuration file %r: %r',
- temporary_options.config_file,
- e)
- sys.exit(1)
-
- config_parser = ConfigParser.SafeConfigParser()
- config_parser.readfp(config_fp)
- config_fp.close()
-
- args_from_config = []
- for name, value in config_parser.items('pywebsocket'):
- args_from_config.append('--' + name)
- args_from_config.append(value)
- if args is None:
- args = args_from_config
- else:
- args = args_from_config + args
- return parser.parse_args(args=args)
- else:
- return temporary_options, temporary_args
-
-
-def _main(args=None):
- """You can call this function from your own program, but please note that
- this function has some side-effects that might affect your program. For
- example, util.wrap_popen3_for_win use in this method replaces implementation
- of os.popen3.
- """
-
- options, args = _parse_args_and_config(args=args)
-
- os.chdir(options.document_root)
-
- _configure_logging(options)
-
- if options.allow_draft75:
- logging.warning('--allow_draft75 option is obsolete.')
-
- if options.strict:
- logging.warning('--strict option is obsolete.')
-
- # TODO(tyoshino): Clean up initialization of CGI related values. Move some
- # of code here to WebSocketRequestHandler class if it's better.
- options.cgi_directories = []
- options.is_executable_method = None
- if options.cgi_paths:
- options.cgi_directories = options.cgi_paths.split(',')
- if sys.platform in ('cygwin', 'win32'):
- cygwin_path = None
- # For Win32 Python, it is expected that CYGWIN_PATH
- # is set to a directory of cygwin binaries.
- # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
- # full path of third_party/cygwin/bin.
- if 'CYGWIN_PATH' in os.environ:
- cygwin_path = os.environ['CYGWIN_PATH']
- util.wrap_popen3_for_win(cygwin_path)
-
- def __check_script(scriptpath):
- return util.get_script_interp(scriptpath, cygwin_path)
-
- options.is_executable_method = __check_script
-
- if options.use_tls:
- if options.tls_module is None:
- if _import_ssl():
- options.tls_module = _TLS_BY_STANDARD_MODULE
- logging.debug('Using ssl module')
- elif _import_pyopenssl():
- options.tls_module = _TLS_BY_PYOPENSSL
- logging.debug('Using pyOpenSSL module')
- else:
- logging.critical(
- 'TLS support requires ssl or pyOpenSSL module.')
- sys.exit(1)
- elif options.tls_module == _TLS_BY_STANDARD_MODULE:
- if not _import_ssl():
- logging.critical('ssl module is not available')
- sys.exit(1)
- elif options.tls_module == _TLS_BY_PYOPENSSL:
- if not _import_pyopenssl():
- logging.critical('pyOpenSSL module is not available')
- sys.exit(1)
- else:
- logging.critical('Invalid --tls-module option: %r',
- options.tls_module)
- sys.exit(1)
-
- if not options.private_key or not options.certificate:
- logging.critical(
- 'To use TLS, specify private_key and certificate.')
- sys.exit(1)
-
- if (options.tls_client_cert_optional and
- not options.tls_client_auth):
- logging.critical('Client authentication must be enabled to '
- 'specify tls_client_cert_optional')
- sys.exit(1)
- else:
- if options.tls_module is not None:
- logging.critical('Use --tls-module option only together with '
- '--use-tls option.')
- sys.exit(1)
-
- if options.tls_client_auth:
- logging.critical('TLS must be enabled for client authentication.')
- sys.exit(1)
-
- if options.tls_client_cert_optional:
- logging.critical('TLS must be enabled for client authentication.')
- sys.exit(1)
-
- if not options.scan_dir:
- options.scan_dir = options.websock_handlers
-
- if options.use_basic_auth:
- options.basic_auth_credential = 'Basic ' + base64.b64encode(
- options.basic_auth_credential)
-
- try:
- if options.thread_monitor_interval_in_sec > 0:
- # Run a thread monitor to show the status of server threads for
- # debugging.
- ThreadMonitor(options.thread_monitor_interval_in_sec).start()
-
- server = WebSocketServer(options)
- server.serve_forever()
- except Exception, e:
- logging.critical('mod_pywebsocket: %s' % e)
- logging.critical('mod_pywebsocket: %s' % util.get_stack_trace())
- sys.exit(1)
-
-
-if __name__ == '__main__':
- _main(sys.argv[1:])
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/stream.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/stream.py
deleted file mode 100644
index edc533279..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/stream.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""This file exports public symbols.
-"""
-
-
-from mod_pywebsocket._stream_base import BadOperationException
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import InvalidFrameException
-from mod_pywebsocket._stream_base import InvalidUTF8Exception
-from mod_pywebsocket._stream_base import UnsupportedFrameException
-from mod_pywebsocket._stream_hixie75 import StreamHixie75
-from mod_pywebsocket._stream_hybi import Frame
-from mod_pywebsocket._stream_hybi import Stream
-from mod_pywebsocket._stream_hybi import StreamOptions
-
-# These methods are intended to be used by WebSocket client developers to have
-# their implementations receive broken data in tests.
-from mod_pywebsocket._stream_hybi import create_close_frame
-from mod_pywebsocket._stream_hybi import create_header
-from mod_pywebsocket._stream_hybi import create_length_header
-from mod_pywebsocket._stream_hybi import create_ping_frame
-from mod_pywebsocket._stream_hybi import create_pong_frame
-from mod_pywebsocket._stream_hybi import create_binary_frame
-from mod_pywebsocket._stream_hybi import create_text_frame
-from mod_pywebsocket._stream_hybi import create_closing_handshake_body
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/util.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/util.py
deleted file mode 100644
index d224ae394..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/util.py
+++ /dev/null
@@ -1,416 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""WebSocket utilities.
-"""
-
-
-import array
-import errno
-
-# Import hash classes from a module available and recommended for each Python
-# version and re-export those symbol. Use sha and md5 module in Python 2.4, and
-# hashlib module in Python 2.6.
-try:
- import hashlib
- md5_hash = hashlib.md5
- sha1_hash = hashlib.sha1
-except ImportError:
- import md5
- import sha
- md5_hash = md5.md5
- sha1_hash = sha.sha
-
-import StringIO
-import logging
-import os
-import re
-import socket
-import traceback
-import zlib
-
-try:
- from mod_pywebsocket import fast_masking
-except ImportError:
- pass
-
-
-def get_stack_trace():
- """Get the current stack trace as string.
-
- This is needed to support Python 2.3.
- TODO: Remove this when we only support Python 2.4 and above.
- Use traceback.format_exc instead.
- """
-
- out = StringIO.StringIO()
- traceback.print_exc(file=out)
- return out.getvalue()
-
-
-def prepend_message_to_exception(message, exc):
- """Prepend message to the exception."""
-
- exc.args = (message + str(exc),)
- return
-
-
-def __translate_interp(interp, cygwin_path):
- """Translate interp program path for Win32 python to run cygwin program
- (e.g. perl). Note that it doesn't support path that contains space,
- which is typically true for Unix, where #!-script is written.
- For Win32 python, cygwin_path is a directory of cygwin binaries.
-
- Args:
- interp: interp command line
- cygwin_path: directory name of cygwin binary, or None
- Returns:
- translated interp command line.
- """
- if not cygwin_path:
- return interp
- m = re.match('^[^ ]*/([^ ]+)( .*)?', interp)
- if m:
- cmd = os.path.join(cygwin_path, m.group(1))
- return cmd + m.group(2)
- return interp
-
-
-def get_script_interp(script_path, cygwin_path=None):
- """Gets #!-interpreter command line from the script.
-
- It also fixes command path. When Cygwin Python is used, e.g. in WebKit,
- it could run "/usr/bin/perl -wT hello.pl".
- When Win32 Python is used, e.g. in Chromium, it couldn't. So, fix
- "/usr/bin/perl" to "<cygwin_path>\perl.exe".
-
- Args:
- script_path: pathname of the script
- cygwin_path: directory name of cygwin binary, or None
- Returns:
- #!-interpreter command line, or None if it is not #!-script.
- """
- fp = open(script_path)
- line = fp.readline()
- fp.close()
- m = re.match('^#!(.*)', line)
- if m:
- return __translate_interp(m.group(1), cygwin_path)
- return None
-
-
-def wrap_popen3_for_win(cygwin_path):
- """Wrap popen3 to support #!-script on Windows.
-
- Args:
- cygwin_path: path for cygwin binary if command path is needed to be
- translated. None if no translation required.
- """
-
- __orig_popen3 = os.popen3
-
- def __wrap_popen3(cmd, mode='t', bufsize=-1):
- cmdline = cmd.split(' ')
- interp = get_script_interp(cmdline[0], cygwin_path)
- if interp:
- cmd = interp + ' ' + cmd
- return __orig_popen3(cmd, mode, bufsize)
-
- os.popen3 = __wrap_popen3
-
-
-def hexify(s):
- return ' '.join(map(lambda x: '%02x' % ord(x), s))
-
-
-def get_class_logger(o):
- return logging.getLogger(
- '%s.%s' % (o.__class__.__module__, o.__class__.__name__))
-
-
-class NoopMasker(object):
- """A masking object that has the same interface as RepeatedXorMasker but
- just returns the string passed in without making any change.
- """
-
- def __init__(self):
- pass
-
- def mask(self, s):
- return s
-
-
-class RepeatedXorMasker(object):
- """A masking object that applies XOR on the string given to mask method
- with the masking bytes given to the constructor repeatedly. This object
- remembers the position in the masking bytes the last mask method call
- ended and resumes from that point on the next mask method call.
- """
-
- def __init__(self, masking_key):
- self._masking_key = masking_key
- self._masking_key_index = 0
-
- def _mask_using_swig(self, s):
- masked_data = fast_masking.mask(
- s, self._masking_key, self._masking_key_index)
- self._masking_key_index = (
- (self._masking_key_index + len(s)) % len(self._masking_key))
- return masked_data
-
- def _mask_using_array(self, s):
- result = array.array('B')
- result.fromstring(s)
-
- # Use temporary local variables to eliminate the cost to access
- # attributes
- masking_key = map(ord, self._masking_key)
- masking_key_size = len(masking_key)
- masking_key_index = self._masking_key_index
-
- for i in xrange(len(result)):
- result[i] ^= masking_key[masking_key_index]
- masking_key_index = (masking_key_index + 1) % masking_key_size
-
- self._masking_key_index = masking_key_index
-
- return result.tostring()
-
- if 'fast_masking' in globals():
- mask = _mask_using_swig
- else:
- mask = _mask_using_array
-
-
-# By making wbits option negative, we can suppress CMF/FLG (2 octet) and
-# ADLER32 (4 octet) fields of zlib so that we can use zlib module just as
-# deflate library. DICTID won't be added as far as we don't set dictionary.
-# LZ77 window of 32K will be used for both compression and decompression.
-# For decompression, we can just use 32K to cover any windows size. For
-# compression, we use 32K so receivers must use 32K.
-#
-# Compression level is Z_DEFAULT_COMPRESSION. We don't have to match level
-# to decode.
-#
-# See zconf.h, deflate.cc, inflate.cc of zlib library, and zlibmodule.c of
-# Python. See also RFC1950 (ZLIB 3.3).
-
-
-class _Deflater(object):
-
- def __init__(self, window_bits):
- self._logger = get_class_logger(self)
-
- self._compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -window_bits)
-
- def compress(self, bytes):
- compressed_bytes = self._compress.compress(bytes)
- self._logger.debug('Compress input %r', bytes)
- self._logger.debug('Compress result %r', compressed_bytes)
- return compressed_bytes
-
- def compress_and_flush(self, bytes):
- compressed_bytes = self._compress.compress(bytes)
- compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH)
- self._logger.debug('Compress input %r', bytes)
- self._logger.debug('Compress result %r', compressed_bytes)
- return compressed_bytes
-
- def compress_and_finish(self, bytes):
- compressed_bytes = self._compress.compress(bytes)
- compressed_bytes += self._compress.flush(zlib.Z_FINISH)
- self._logger.debug('Compress input %r', bytes)
- self._logger.debug('Compress result %r', compressed_bytes)
- return compressed_bytes
-
-
-class _Inflater(object):
-
- def __init__(self, window_bits):
- self._logger = get_class_logger(self)
- self._window_bits = window_bits
-
- self._unconsumed = ''
-
- self.reset()
-
- def decompress(self, size):
- if not (size == -1 or size > 0):
- raise Exception('size must be -1 or positive')
-
- data = ''
-
- while True:
- if size == -1:
- data += self._decompress.decompress(self._unconsumed)
- # See Python bug http://bugs.python.org/issue12050 to
- # understand why the same code cannot be used for updating
- # self._unconsumed for here and else block.
- self._unconsumed = ''
- else:
- data += self._decompress.decompress(
- self._unconsumed, size - len(data))
- self._unconsumed = self._decompress.unconsumed_tail
- if self._decompress.unused_data:
- # Encountered a last block (i.e. a block with BFINAL = 1) and
- # found a new stream (unused_data). We cannot use the same
- # zlib.Decompress object for the new stream. Create a new
- # Decompress object to decompress the new one.
- #
- # It's fine to ignore unconsumed_tail if unused_data is not
- # empty.
- self._unconsumed = self._decompress.unused_data
- self.reset()
- if size >= 0 and len(data) == size:
- # data is filled. Don't call decompress again.
- break
- else:
- # Re-invoke Decompress.decompress to try to decompress all
- # available bytes before invoking read which blocks until
- # any new byte is available.
- continue
- else:
- # Here, since unused_data is empty, even if unconsumed_tail is
- # not empty, bytes of requested length are already in data. We
- # don't have to "continue" here.
- break
-
- if data:
- self._logger.debug('Decompressed %r', data)
- return data
-
- def append(self, data):
- self._logger.debug('Appended %r', data)
- self._unconsumed += data
-
- def reset(self):
- self._logger.debug('Reset')
- self._decompress = zlib.decompressobj(-self._window_bits)
-
-
-# Compresses/decompresses given octets using the method introduced in RFC1979.
-
-
-class _RFC1979Deflater(object):
- """A compressor class that applies DEFLATE to given byte sequence and
- flushes using the algorithm described in the RFC1979 section 2.1.
- """
-
- def __init__(self, window_bits, no_context_takeover):
- self._deflater = None
- if window_bits is None:
- window_bits = zlib.MAX_WBITS
- self._window_bits = window_bits
- self._no_context_takeover = no_context_takeover
-
- def filter(self, bytes, end=True, bfinal=False):
- if self._deflater is None:
- self._deflater = _Deflater(self._window_bits)
-
- if bfinal:
- result = self._deflater.compress_and_finish(bytes)
- # Add a padding block with BFINAL = 0 and BTYPE = 0.
- result = result + chr(0)
- self._deflater = None
- return result
-
- result = self._deflater.compress_and_flush(bytes)
- if end:
- # Strip last 4 octets which is LEN and NLEN field of a
- # non-compressed block added for Z_SYNC_FLUSH.
- result = result[:-4]
-
- if self._no_context_takeover and end:
- self._deflater = None
-
- return result
-
-
-class _RFC1979Inflater(object):
- """A decompressor class for byte sequence compressed and flushed following
- the algorithm described in the RFC1979 section 2.1.
- """
-
- def __init__(self, window_bits=zlib.MAX_WBITS):
- self._inflater = _Inflater(window_bits)
-
- def filter(self, bytes):
- # Restore stripped LEN and NLEN field of a non-compressed block added
- # for Z_SYNC_FLUSH.
- self._inflater.append(bytes + '\x00\x00\xff\xff')
- return self._inflater.decompress(-1)
-
-
-class DeflateSocket(object):
- """A wrapper class for socket object to intercept send and recv to perform
- deflate compression and decompression transparently.
- """
-
- # Size of the buffer passed to recv to receive compressed data.
- _RECV_SIZE = 4096
-
- def __init__(self, socket):
- self._socket = socket
-
- self._logger = get_class_logger(self)
-
- self._deflater = _Deflater(zlib.MAX_WBITS)
- self._inflater = _Inflater(zlib.MAX_WBITS)
-
- def recv(self, size):
- """Receives data from the socket specified on the construction up
- to the specified size. Once any data is available, returns it even
- if it's smaller than the specified size.
- """
-
- # TODO(tyoshino): Allow call with size=0. It should block until any
- # decompressed data is available.
- if size <= 0:
- raise Exception('Non-positive size passed')
- while True:
- data = self._inflater.decompress(size)
- if len(data) != 0:
- return data
-
- read_data = self._socket.recv(DeflateSocket._RECV_SIZE)
- if not read_data:
- return ''
- self._inflater.append(read_data)
-
- def sendall(self, bytes):
- self.send(bytes)
-
- def send(self, bytes):
- self._socket.sendall(self._deflater.compress_and_flush(bytes))
- return len(bytes)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/xhr_benchmark_handler.py b/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/xhr_benchmark_handler.py
deleted file mode 100644
index 6735c7e2a..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/mod_pywebsocket/xhr_benchmark_handler.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright 2014 Google Inc. All rights reserved.
-#
-# Use of this source code is governed by a BSD-style
-# license that can be found in the COPYING file or at
-# https://developers.google.com/open-source/licenses/bsd
-
-
-from mod_pywebsocket import util
-
-
-class XHRBenchmarkHandler(object):
- def __init__(self, headers, rfile, wfile):
- self._logger = util.get_class_logger(self)
-
- self.headers = headers
- self.rfile = rfile
- self.wfile = wfile
-
- def do_send(self):
- content_length = int(self.headers.getheader('Content-Length'))
-
- self._logger.debug('Requested to receive %s bytes', content_length)
-
- RECEIVE_BLOCK_SIZE = 1024 * 1024
-
- bytes_to_receive = content_length
- while bytes_to_receive > 0:
- bytes_to_receive_in_this_loop = bytes_to_receive
- if bytes_to_receive_in_this_loop > RECEIVE_BLOCK_SIZE:
- bytes_to_receive_in_this_loop = RECEIVE_BLOCK_SIZE
- received_data = self.rfile.read(bytes_to_receive_in_this_loop)
- if received_data != ('a' * bytes_to_receive_in_this_loop):
- self._logger.debug('Request body verification failed')
- return
- bytes_to_receive -= len(received_data)
- if bytes_to_receive < 0:
- self._logger.debug('Received %d more bytes than expected' %
- (-bytes_to_receive))
- return
-
- # Return the number of received bytes back to the client.
- response_body = '%d' % content_length
- self.wfile.write(
- 'HTTP/1.1 200 OK\r\n'
- 'Content-Type: text/html\r\n'
- 'Content-Length: %d\r\n'
- '\r\n%s' % (len(response_body), response_body))
- self.wfile.flush()
-
- def do_receive(self):
- content_length = int(self.headers.getheader('Content-Length'))
- request_body = self.rfile.read(content_length)
-
- request_array = request_body.split(' ')
- if len(request_array) < 2:
- self._logger.debug('Malformed request body: %r', request_body)
- return
-
- # Parse the size parameter.
- bytes_to_send = request_array[0]
- try:
- bytes_to_send = int(bytes_to_send)
- except ValueError, e:
- self._logger.debug('Malformed size parameter: %r', bytes_to_send)
- return
- self._logger.debug('Requested to send %s bytes', bytes_to_send)
-
- # Parse the transfer encoding parameter.
- chunked_mode = False
- mode_parameter = request_array[1]
- if mode_parameter == 'chunked':
- self._logger.debug('Requested chunked transfer encoding')
- chunked_mode = True
- elif mode_parameter != 'none':
- self._logger.debug('Invalid mode parameter: %r', mode_parameter)
- return
-
- # Write a header
- response_header = (
- 'HTTP/1.1 200 OK\r\n'
- 'Content-Type: application/octet-stream\r\n')
- if chunked_mode:
- response_header += 'Transfer-Encoding: chunked\r\n\r\n'
- else:
- response_header += (
- 'Content-Length: %d\r\n\r\n' % bytes_to_send)
- self.wfile.write(response_header)
- self.wfile.flush()
-
- # Write a body
- SEND_BLOCK_SIZE = 1024 * 1024
-
- while bytes_to_send > 0:
- bytes_to_send_in_this_loop = bytes_to_send
- if bytes_to_send_in_this_loop > SEND_BLOCK_SIZE:
- bytes_to_send_in_this_loop = SEND_BLOCK_SIZE
-
- if chunked_mode:
- self.wfile.write('%x\r\n' % bytes_to_send_in_this_loop)
- self.wfile.write('a' * bytes_to_send_in_this_loop)
- if chunked_mode:
- self.wfile.write('\r\n')
- self.wfile.flush()
-
- bytes_to_send -= bytes_to_send_in_this_loop
-
- if chunked_mode:
- self.wfile.write('0\r\n\r\n')
- self.wfile.flush()
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/setup.py b/testing/web-platform/tests/tools/pywebsocket/src/setup.py
deleted file mode 100755
index ada8db3e1..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/setup.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""Set up script for mod_pywebsocket.
-"""
-
-
-from distutils.core import setup, Extension
-import sys
-
-
-_PACKAGE_NAME = 'mod_pywebsocket'
-
-# Build and use a C++ extension for faster masking. SWIG is required.
-_USE_FAST_MASKING = False
-
-if sys.version < '2.3':
- print >> sys.stderr, '%s requires Python 2.3 or later.' % _PACKAGE_NAME
- sys.exit(1)
-
-if _USE_FAST_MASKING:
- setup(ext_modules=[
- Extension(
- 'mod_pywebsocket/_fast_masking',
- ['mod_pywebsocket/fast_masking.i'],
- swig_opts=['-c++'])])
-
-setup(author='Yuzo Fujishima',
- author_email='yuzo@chromium.org',
- description='WebSocket extension for Apache HTTP Server.',
- long_description=(
- 'mod_pywebsocket is an Apache HTTP Server extension for '
- 'the WebSocket Protocol (RFC 6455). '
- 'See mod_pywebsocket/__init__.py for more detail.'),
- license='See COPYING',
- name=_PACKAGE_NAME,
- packages=[_PACKAGE_NAME, _PACKAGE_NAME + '.handshake'],
- url='http://code.google.com/p/pywebsocket/',
- # See the source of distutils.version, distutils.versionpredicate and
- # distutils.dist to understand how to name version numbers.
- version='0.7.9',
- )
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/__init__.py b/testing/web-platform/tests/tools/pywebsocket/src/test/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/__init__.py
+++ /dev/null
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/cacert.pem b/testing/web-platform/tests/tools/pywebsocket/src/test/cert/cacert.pem
deleted file mode 100644
index 4dadae121..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/cacert.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICvDCCAiWgAwIBAgIJAKqVghkGF1rSMA0GCSqGSIb3DQEBBQUAMEkxCzAJBgNV
-BAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEUMBIGA1UEChMLcHl3ZWJzb2NrZXQxFDAS
-BgNVBAMTC3B5d2Vic29ja2V0MB4XDTEyMDYwNjA3MjQzM1oXDTM5MTAyMzA3MjQz
-M1owSTELMAkGA1UEBhMCSlAxDjAMBgNVBAgTBVRva3lvMRQwEgYDVQQKEwtweXdl
-YnNvY2tldDEUMBIGA1UEAxMLcHl3ZWJzb2NrZXQwgZ8wDQYJKoZIhvcNAQEBBQAD
-gY0AMIGJAoGBAKoSEW2biQxVrMMKdn/8PJzDYiSXDPR9WQbLRRQ1Gm5jkCYiahXW
-u2CbTThfPPfi2NHA3I+HlT7gO9yR7RVUvN6ISUzGwXDEq4f4UNqtQOhQaqqK+CZ9
-LO/BhO/YYfNrbSPlYzHUKaT9ese7xO9VzVKLW+qUf2Mjh4/+SzxBDNP7AgMBAAGj
-gaswgagwHQYDVR0OBBYEFOsWdxCSuyhwaZeab6BoTho3++bzMHkGA1UdIwRyMHCA
-FOsWdxCSuyhwaZeab6BoTho3++bzoU2kSzBJMQswCQYDVQQGEwJKUDEOMAwGA1UE
-CBMFVG9reW8xFDASBgNVBAoTC3B5d2Vic29ja2V0MRQwEgYDVQQDEwtweXdlYnNv
-Y2tldIIJAKqVghkGF1rSMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
-gsMI1WEYqNw/jhUIdrTBcCxJ0X6hJvA9ziKANVm1Rs+4P3YDArkQ8bCr6xY+Kw7s
-Zp0yE7dM8GMdi+DU6hL3t3E5eMkTS1yZr9WCK4f2RLo+et98selZydpHemF3DJJ3
-gAj8Sx4LBaG8Cb/WnEMPv3MxG3fBE5favF6V4jU07hQ=
------END CERTIFICATE-----
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/cert.pem b/testing/web-platform/tests/tools/pywebsocket/src/test/cert/cert.pem
deleted file mode 100644
index 25379a72b..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/cert.pem
+++ /dev/null
@@ -1,61 +0,0 @@
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number: 1 (0x1)
- Signature Algorithm: sha1WithRSAEncryption
- Issuer: C=JP, ST=Tokyo, O=pywebsocket, CN=pywebsocket
- Validity
- Not Before: Jun 6 07:25:08 2012 GMT
- Not After : Oct 23 07:25:08 2039 GMT
- Subject: C=JP, ST=Tokyo, O=pywebsocket, CN=pywebsocket
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public Key: (1024 bit)
- Modulus (1024 bit):
- 00:de:10:ce:3a:5a:04:a4:1c:29:93:5c:23:82:1a:
- f2:06:01:e6:2b:a4:0f:dd:77:49:76:89:03:a2:21:
- de:04:75:c6:e2:dd:fb:35:27:3a:a2:92:8e:12:62:
- 2b:3e:1f:f4:78:df:b6:94:cb:27:d6:cb:d6:37:d7:
- 5c:08:f0:09:3e:c9:ce:24:2d:00:c9:df:4a:e0:99:
- e5:fb:23:a9:e2:d6:c9:3d:96:fa:01:88:de:5a:89:
- b0:cf:03:67:6f:04:86:1d:ef:62:1c:55:a9:07:9a:
- 2e:66:2a:73:5b:4c:62:03:f9:82:83:db:68:bf:b8:
- 4b:0b:8b:93:11:b8:54:73:7b
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Basic Constraints:
- CA:FALSE
- Netscape Cert Type:
- SSL Server
- Netscape Comment:
- OpenSSL Generated Certificate
- X509v3 Subject Key Identifier:
- 82:A1:73:8B:16:0C:7C:E4:D3:46:95:13:95:1A:32:C1:84:E9:06:00
- X509v3 Authority Key Identifier:
- keyid:EB:16:77:10:92:BB:28:70:69:97:9A:6F:A0:68:4E:1A:37:FB:E6:F3
-
- Signature Algorithm: sha1WithRSAEncryption
- 6b:b3:46:29:02:df:b0:c8:8e:c4:d7:7f:a0:1e:0d:1a:eb:2f:
- df:d1:48:57:36:5f:95:8c:1b:f0:51:d6:52:e7:8d:84:3b:9f:
- d8:ed:22:9c:aa:bd:ee:9b:90:1d:84:a3:4c:0b:cb:eb:64:73:
- ba:f7:15:ce:da:5f:db:8b:15:07:a6:28:7f:b9:8c:11:9b:64:
- d3:f1:be:52:4f:c3:d8:58:fe:de:56:63:63:3b:51:ed:a7:81:
- f9:05:51:70:63:32:09:0e:94:7e:05:fe:a1:56:18:34:98:d5:
- 99:1e:4e:27:38:89:90:6a:e5:ce:60:35:01:f5:de:34:60:b1:
- cb:ae
------BEGIN CERTIFICATE-----
-MIICmDCCAgGgAwIBAgIBATANBgkqhkiG9w0BAQUFADBJMQswCQYDVQQGEwJKUDEO
-MAwGA1UECBMFVG9reW8xFDASBgNVBAoTC3B5d2Vic29ja2V0MRQwEgYDVQQDEwtw
-eXdlYnNvY2tldDAeFw0xMjA2MDYwNzI1MDhaFw0zOTEwMjMwNzI1MDhaMEkxCzAJ
-BgNVBAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEUMBIGA1UEChMLcHl3ZWJzb2NrZXQx
-FDASBgNVBAMTC3B5d2Vic29ja2V0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
-gQDeEM46WgSkHCmTXCOCGvIGAeYrpA/dd0l2iQOiId4Edcbi3fs1Jzqiko4SYis+
-H/R437aUyyfWy9Y311wI8Ak+yc4kLQDJ30rgmeX7I6ni1sk9lvoBiN5aibDPA2dv
-BIYd72IcVakHmi5mKnNbTGID+YKD22i/uEsLi5MRuFRzewIDAQABo4GPMIGMMAkG
-A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMCwGCWCGSAGG+EIBDQQfFh1PcGVu
-U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUgqFzixYMfOTTRpUT
-lRoywYTpBgAwHwYDVR0jBBgwFoAU6xZ3EJK7KHBpl5pvoGhOGjf75vMwDQYJKoZI
-hvcNAQEFBQADgYEAa7NGKQLfsMiOxNd/oB4NGusv39FIVzZflYwb8FHWUueNhDuf
-2O0inKq97puQHYSjTAvL62RzuvcVztpf24sVB6Yof7mMEZtk0/G+Uk/D2Fj+3lZj
-YztR7aeB+QVRcGMyCQ6UfgX+oVYYNJjVmR5OJziJkGrlzmA1AfXeNGCxy64=
------END CERTIFICATE-----
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/client_cert.p12 b/testing/web-platform/tests/tools/pywebsocket/src/test/cert/client_cert.p12
deleted file mode 100644
index 14e139927..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/client_cert.p12
+++ /dev/null
Binary files differ
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/key.pem b/testing/web-platform/tests/tools/pywebsocket/src/test/cert/key.pem
deleted file mode 100644
index fae858318..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/cert/key.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDeEM46WgSkHCmTXCOCGvIGAeYrpA/dd0l2iQOiId4Edcbi3fs1
-Jzqiko4SYis+H/R437aUyyfWy9Y311wI8Ak+yc4kLQDJ30rgmeX7I6ni1sk9lvoB
-iN5aibDPA2dvBIYd72IcVakHmi5mKnNbTGID+YKD22i/uEsLi5MRuFRzewIDAQAB
-AoGBAIuCuV1Vcnb7rm8CwtgZP5XgmY8vSjxTldafa6XvawEYUTP0S77v/1llg1Yv
-UIV+I+PQgG9oVoYOl22LoimHS/Z3e1fsot5tDYszGe8/Gkst4oaReSoxvBUa6WXp
-QSo7YFCajuHtE+W/gzF+UHbdzzXIDjQZ314LNF5t+4UnsEPBAkEA+girImqWoM2t
-3UR8f8oekERwsmEMf9DH5YpH4cvUnvI+kwesC/r2U8Sho++fyEMUNm7aIXGqNLga
-ogAM+4NX4QJBAONdSxSay22egTGNoIhLndljWkuOt/9FWj2klf/4QxD4blMJQ5Oq
-QdOGAh7nVQjpPLQ5D7CBVAKpGM2CD+QJBtsCQEP2kz35pxPylG3urcC2mfQxBkkW
-ZCViBNP58GwJ0bOauTOSBEwFXWuLqTw8aDwxL49UNmqc0N0fpe2fAehj3UECQQCm
-FH/DjU8Lw7ybddjNtm6XXPuYNagxz3cbkB4B3FchDleIUDwMoVF0MW9bI5/54mV1
-QDk1tUKortxvQZJaAD4BAkEAhGOHQqPd6bBBoFBvpaLzPJMxwLKrB+Wtkq/QlC72
-ClRiMn2g8SALiIL3BDgGXKcKE/Wy7jo/af/JCzQ/cPqt/A==
------END RSA PRIVATE KEY-----
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/client_for_testing.py b/testing/web-platform/tests/tools/pywebsocket/src/test/client_for_testing.py
deleted file mode 100644
index c7f805ee9..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/client_for_testing.py
+++ /dev/null
@@ -1,1100 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""WebSocket client utility for testing.
-
-This module contains helper methods for performing handshake, frame
-sending/receiving as a WebSocket client.
-
-This is code for testing mod_pywebsocket. Keep this code independent from
-mod_pywebsocket. Don't import e.g. Stream class for generating frame for
-testing. Using util.hexify, etc. that are not related to protocol processing
-is allowed.
-
-Note:
-This code is far from robust, e.g., we cut corners in handshake.
-"""
-
-
-import base64
-import errno
-import logging
-import os
-import random
-import re
-import socket
-import struct
-import time
-
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-
-
-DEFAULT_PORT = 80
-DEFAULT_SECURE_PORT = 443
-
-# Opcodes introduced in IETF HyBi 01 for the new framing format
-OPCODE_CONTINUATION = 0x0
-OPCODE_CLOSE = 0x8
-OPCODE_PING = 0x9
-OPCODE_PONG = 0xa
-OPCODE_TEXT = 0x1
-OPCODE_BINARY = 0x2
-
-# Strings used for handshake
-_UPGRADE_HEADER = 'Upgrade: websocket\r\n'
-_UPGRADE_HEADER_HIXIE75 = 'Upgrade: WebSocket\r\n'
-_CONNECTION_HEADER = 'Connection: Upgrade\r\n'
-
-WEBSOCKET_ACCEPT_UUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
-
-# Status codes
-STATUS_NORMAL_CLOSURE = 1000
-STATUS_GOING_AWAY = 1001
-STATUS_PROTOCOL_ERROR = 1002
-STATUS_UNSUPPORTED_DATA = 1003
-STATUS_NO_STATUS_RECEIVED = 1005
-STATUS_ABNORMAL_CLOSURE = 1006
-STATUS_INVALID_FRAME_PAYLOAD_DATA = 1007
-STATUS_POLICY_VIOLATION = 1008
-STATUS_MESSAGE_TOO_BIG = 1009
-STATUS_MANDATORY_EXT = 1010
-STATUS_INTERNAL_ENDPOINT_ERROR = 1011
-STATUS_TLS_HANDSHAKE = 1015
-
-# Extension tokens
-_DEFLATE_FRAME_EXTENSION = 'deflate-frame'
-# TODO(bashi): Update after mux implementation finished.
-_MUX_EXTENSION = 'mux_DO_NOT_USE'
-_PERMESSAGE_DEFLATE_EXTENSION = 'permessage-deflate'
-
-def _method_line(resource):
- return 'GET %s HTTP/1.1\r\n' % resource
-
-
-def _sec_origin_header(origin):
- return 'Sec-WebSocket-Origin: %s\r\n' % origin.lower()
-
-
-def _origin_header(origin):
- # 4.1 13. concatenation of the string "Origin:", a U+0020 SPACE character,
- # and the /origin/ value, converted to ASCII lowercase, to /fields/.
- return 'Origin: %s\r\n' % origin.lower()
-
-
-def _format_host_header(host, port, secure):
- # 4.1 9. Let /hostport/ be an empty string.
- # 4.1 10. Append the /host/ value, converted to ASCII lowercase, to
- # /hostport/
- hostport = host.lower()
- # 4.1 11. If /secure/ is false, and /port/ is not 80, or if /secure/
- # is true, and /port/ is not 443, then append a U+003A COLON character
- # (:) followed by the value of /port/, expressed as a base-ten integer,
- # to /hostport/
- if ((not secure and port != DEFAULT_PORT) or
- (secure and port != DEFAULT_SECURE_PORT)):
- hostport += ':' + str(port)
- # 4.1 12. concatenation of the string "Host:", a U+0020 SPACE
- # character, and /hostport/, to /fields/.
- return 'Host: %s\r\n' % hostport
-
-
-# TODO(tyoshino): Define a base class and move these shared methods to that.
-
-
-def receive_bytes(socket, length):
- received_bytes = []
- remaining = length
- while remaining > 0:
- new_received_bytes = socket.recv(remaining)
- if not new_received_bytes:
- raise Exception(
- 'Connection closed before receiving requested length '
- '(requested %d bytes but received only %d bytes)' %
- (length, length - remaining))
- received_bytes.append(new_received_bytes)
- remaining -= len(new_received_bytes)
- return ''.join(received_bytes)
-
-
-# TODO(tyoshino): Now the WebSocketHandshake class diverts these methods. We
-# should move to HTTP parser as specified in RFC 6455. For HyBi 00 and
-# Hixie 75, pack these methods as some parser class.
-
-
-def _read_fields(socket):
- # 4.1 32. let /fields/ be a list of name-value pairs, initially empty.
- fields = {}
- while True:
- # 4.1 33. let /name/ and /value/ be empty byte arrays
- name = ''
- value = ''
- # 4.1 34. read /name/
- name = _read_name(socket)
- if name is None:
- break
- # 4.1 35. read spaces
- # TODO(tyoshino): Skip only one space as described in the spec.
- ch = _skip_spaces(socket)
- # 4.1 36. read /value/
- value = _read_value(socket, ch)
- # 4.1 37. read a byte from the server
- ch = receive_bytes(socket, 1)
- if ch != '\n': # 0x0A
- raise Exception(
- 'Expected LF but found %r while reading value %r for header '
- '%r' % (ch, name, value))
- # 4.1 38. append an entry to the /fields/ list that has the name
- # given by the string obtained by interpreting the /name/ byte
- # array as a UTF-8 stream and the value given by the string
- # obtained by interpreting the /value/ byte array as a UTF-8 byte
- # stream.
- fields.setdefault(name, []).append(value)
- # 4.1 39. return to the "Field" step above
- return fields
-
-
-def _read_name(socket):
- # 4.1 33. let /name/ be empty byte arrays
- name = ''
- while True:
- # 4.1 34. read a byte from the server
- ch = receive_bytes(socket, 1)
- if ch == '\r': # 0x0D
- return None
- elif ch == '\n': # 0x0A
- raise Exception(
- 'Unexpected LF when reading header name %r' % name)
- elif ch == ':': # 0x3A
- return name
- elif ch >= 'A' and ch <= 'Z': # range 0x31 to 0x5A
- ch = chr(ord(ch) + 0x20)
- name += ch
- else:
- name += ch
-
-
-def _skip_spaces(socket):
- # 4.1 35. read a byte from the server
- while True:
- ch = receive_bytes(socket, 1)
- if ch == ' ': # 0x20
- continue
- return ch
-
-
-def _read_value(socket, ch):
- # 4.1 33. let /value/ be empty byte arrays
- value = ''
- # 4.1 36. read a byte from server.
- while True:
- if ch == '\r': # 0x0D
- return value
- elif ch == '\n': # 0x0A
- raise Exception(
- 'Unexpected LF when reading header value %r' % value)
- else:
- value += ch
- ch = receive_bytes(socket, 1)
-
-
-def read_frame_header(socket):
- received = receive_bytes(socket, 2)
-
- first_byte = ord(received[0])
- fin = (first_byte >> 7) & 1
- rsv1 = (first_byte >> 6) & 1
- rsv2 = (first_byte >> 5) & 1
- rsv3 = (first_byte >> 4) & 1
- opcode = first_byte & 0xf
-
- second_byte = ord(received[1])
- mask = (second_byte >> 7) & 1
- payload_length = second_byte & 0x7f
-
- if mask != 0:
- raise Exception(
- 'Mask bit must be 0 for frames coming from server')
-
- if payload_length == 127:
- extended_payload_length = receive_bytes(socket, 8)
- payload_length = struct.unpack(
- '!Q', extended_payload_length)[0]
- if payload_length > 0x7FFFFFFFFFFFFFFF:
- raise Exception('Extended payload length >= 2^63')
- elif payload_length == 126:
- extended_payload_length = receive_bytes(socket, 2)
- payload_length = struct.unpack(
- '!H', extended_payload_length)[0]
-
- return fin, rsv1, rsv2, rsv3, opcode, payload_length
-
-
-class _TLSSocket(object):
- """Wrapper for a TLS connection."""
-
- def __init__(self, raw_socket):
- self._ssl = socket.ssl(raw_socket)
-
- def send(self, bytes):
- return self._ssl.write(bytes)
-
- def recv(self, size=-1):
- return self._ssl.read(size)
-
- def close(self):
- # Nothing to do.
- pass
-
-
-class HttpStatusException(Exception):
- """This exception will be raised when unexpected http status code was
- received as a result of handshake.
- """
-
- def __init__(self, name, status):
- super(HttpStatusException, self).__init__(name)
- self.status = status
-
-
-class WebSocketHandshake(object):
- """Opening handshake processor for the WebSocket protocol (RFC 6455)."""
-
- def __init__(self, options):
- self._logger = util.get_class_logger(self)
-
- self._options = options
-
- def handshake(self, socket):
- """Handshake WebSocket.
-
- Raises:
- Exception: handshake failed.
- """
-
- self._socket = socket
-
- request_line = _method_line(self._options.resource)
- self._logger.debug('Opening handshake Request-Line: %r', request_line)
- self._socket.sendall(request_line)
-
- fields = []
- fields.append(_UPGRADE_HEADER)
- fields.append(_CONNECTION_HEADER)
-
- fields.append(_format_host_header(
- self._options.server_host,
- self._options.server_port,
- self._options.use_tls))
-
- if self._options.version is 8:
- fields.append(_sec_origin_header(self._options.origin))
- else:
- fields.append(_origin_header(self._options.origin))
-
- original_key = os.urandom(16)
- key = base64.b64encode(original_key)
- self._logger.debug(
- 'Sec-WebSocket-Key: %s (%s)', key, util.hexify(original_key))
- fields.append('Sec-WebSocket-Key: %s\r\n' % key)
-
- fields.append('Sec-WebSocket-Version: %d\r\n' % self._options.version)
-
- # Setting up extensions.
- if len(self._options.extensions) > 0:
- fields.append('Sec-WebSocket-Extensions: %s\r\n' %
- ', '.join(self._options.extensions))
-
- self._logger.debug('Opening handshake request headers: %r', fields)
-
- for field in fields:
- self._socket.sendall(field)
- self._socket.sendall('\r\n')
-
- self._logger.info('Sent opening handshake request')
-
- field = ''
- while True:
- ch = receive_bytes(self._socket, 1)
- field += ch
- if ch == '\n':
- break
-
- self._logger.debug('Opening handshake Response-Line: %r', field)
-
- if len(field) < 7 or not field.endswith('\r\n'):
- raise Exception('Wrong status line: %r' % field)
- m = re.match('[^ ]* ([^ ]*) .*', field)
- if m is None:
- raise Exception(
- 'No HTTP status code found in status line: %r' % field)
- code = m.group(1)
- if not re.match('[0-9][0-9][0-9]', code):
- raise Exception(
- 'HTTP status code %r is not three digit in status line: %r' %
- (code, field))
- if code != '101':
- raise HttpStatusException(
- 'Expected HTTP status code 101 but found %r in status line: '
- '%r' % (code, field), int(code))
- fields = _read_fields(self._socket)
- ch = receive_bytes(self._socket, 1)
- if ch != '\n': # 0x0A
- raise Exception('Expected LF but found: %r' % ch)
-
- self._logger.debug('Opening handshake response headers: %r', fields)
-
- # Check /fields/
- if len(fields['upgrade']) != 1:
- raise Exception(
- 'Multiple Upgrade headers found: %s' % fields['upgrade'])
- if len(fields['connection']) != 1:
- raise Exception(
- 'Multiple Connection headers found: %s' % fields['connection'])
- if fields['upgrade'][0] != 'websocket':
- raise Exception(
- 'Unexpected Upgrade header value: %s' % fields['upgrade'][0])
- if fields['connection'][0].lower() != 'upgrade':
- raise Exception(
- 'Unexpected Connection header value: %s' %
- fields['connection'][0])
-
- if len(fields['sec-websocket-accept']) != 1:
- raise Exception(
- 'Multiple Sec-WebSocket-Accept headers found: %s' %
- fields['sec-websocket-accept'])
-
- accept = fields['sec-websocket-accept'][0]
-
- # Validate
- try:
- decoded_accept = base64.b64decode(accept)
- except TypeError, e:
- raise HandshakeException(
- 'Illegal value for header Sec-WebSocket-Accept: ' + accept)
-
- if len(decoded_accept) != 20:
- raise HandshakeException(
- 'Decoded value of Sec-WebSocket-Accept is not 20-byte long')
-
- self._logger.debug('Actual Sec-WebSocket-Accept: %r (%s)',
- accept, util.hexify(decoded_accept))
-
- original_expected_accept = util.sha1_hash(
- key + WEBSOCKET_ACCEPT_UUID).digest()
- expected_accept = base64.b64encode(original_expected_accept)
-
- self._logger.debug('Expected Sec-WebSocket-Accept: %r (%s)',
- expected_accept,
- util.hexify(original_expected_accept))
-
- if accept != expected_accept:
- raise Exception(
- 'Invalid Sec-WebSocket-Accept header: %r (expected) != %r '
- '(actual)' % (accept, expected_accept))
-
- server_extensions_header = fields.get('sec-websocket-extensions')
- accepted_extensions = []
- if server_extensions_header is not None:
- accepted_extensions = common.parse_extensions(
- ', '.join(server_extensions_header))
-
- # Scan accepted extension list to check if there is any unrecognized
- # extensions or extensions we didn't request in it. Then, for
- # extensions we request, parse them and store parameters. They will be
- # used later by each extension.
- deflate_frame_accepted = False
- mux_accepted = False
- for extension in accepted_extensions:
- if extension.name() == _DEFLATE_FRAME_EXTENSION:
- if self._options.use_deflate_frame:
- deflate_frame_accepted = True
- continue
- if extension.name() == _MUX_EXTENSION:
- if self._options.use_mux:
- mux_accepted = True
- continue
- if extension.name() == _PERMESSAGE_DEFLATE_EXTENSION:
- checker = self._options.check_permessage_deflate
- if checker:
- checker(extension)
- continue
-
- raise Exception(
- 'Received unrecognized extension: %s' % extension.name())
-
- # Let all extensions check the response for extension request.
-
- if (self._options.use_deflate_frame and
- not deflate_frame_accepted):
- raise Exception('%s extension not accepted' %
- _DEFLATE_FRAME_EXTENSION)
-
- if self._options.use_mux and not mux_accepted:
- raise Exception('%s extension not accepted' % _MUX_EXTENSION)
-
-
-class WebSocketHybi00Handshake(object):
- """Opening handshake processor for the WebSocket protocol version HyBi 00.
- """
-
- def __init__(self, options, draft_field):
- self._logger = util.get_class_logger(self)
-
- self._options = options
- self._draft_field = draft_field
-
- def handshake(self, socket):
- """Handshake WebSocket.
-
- Raises:
- Exception: handshake failed.
- """
-
- self._socket = socket
-
- # 4.1 5. send request line.
- request_line = _method_line(self._options.resource)
- self._logger.debug('Opening handshake Request-Line: %r', request_line)
- self._socket.sendall(request_line)
- # 4.1 6. Let /fields/ be an empty list of strings.
- fields = []
- # 4.1 7. Add the string "Upgrade: WebSocket" to /fields/.
- fields.append(_UPGRADE_HEADER_HIXIE75)
- # 4.1 8. Add the string "Connection: Upgrade" to /fields/.
- fields.append(_CONNECTION_HEADER)
- # 4.1 9-12. Add Host: field to /fields/.
- fields.append(_format_host_header(
- self._options.server_host,
- self._options.server_port,
- self._options.use_tls))
- # 4.1 13. Add Origin: field to /fields/.
- fields.append(_origin_header(self._options.origin))
- # TODO: 4.1 14 Add Sec-WebSocket-Protocol: field to /fields/.
- # TODO: 4.1 15 Add cookie headers to /fields/.
-
- # 4.1 16-23. Add Sec-WebSocket-Key<n> to /fields/.
- self._number1, key1 = self._generate_sec_websocket_key()
- self._logger.debug('Number1: %d', self._number1)
- fields.append('Sec-WebSocket-Key1: %s\r\n' % key1)
- self._number2, key2 = self._generate_sec_websocket_key()
- self._logger.debug('Number2: %d', self._number1)
- fields.append('Sec-WebSocket-Key2: %s\r\n' % key2)
-
- fields.append('Sec-WebSocket-Draft: %s\r\n' % self._draft_field)
-
- # 4.1 24. For each string in /fields/, in a random order: send the
- # string, encoded as UTF-8, followed by a UTF-8 encoded U+000D CARRIAGE
- # RETURN U+000A LINE FEED character pair (CRLF).
- random.shuffle(fields)
-
- self._logger.debug('Opening handshake request headers: %r', fields)
- for field in fields:
- self._socket.sendall(field)
-
- # 4.1 25. send a UTF-8-encoded U+000D CARRIAGE RETURN U+000A LINE FEED
- # character pair (CRLF).
- self._socket.sendall('\r\n')
- # 4.1 26. let /key3/ be a string consisting of eight random bytes (or
- # equivalently, a random 64 bit integer encoded in a big-endian order).
- self._key3 = self._generate_key3()
- # 4.1 27. send /key3/ to the server.
- self._socket.sendall(self._key3)
- self._logger.debug(
- 'Key3: %r (%s)', self._key3, util.hexify(self._key3))
-
- self._logger.info('Sent opening handshake request')
-
- # 4.1 28. Read bytes from the server until either the connection
- # closes, or a 0x0A byte is read. let /field/ be these bytes, including
- # the 0x0A bytes.
- field = ''
- while True:
- ch = receive_bytes(self._socket, 1)
- field += ch
- if ch == '\n':
- break
-
- self._logger.debug('Opening handshake Response-Line: %r', field)
-
- # if /field/ is not at least seven bytes long, or if the last
- # two bytes aren't 0x0D and 0x0A respectively, or if it does not
- # contain at least two 0x20 bytes, then fail the WebSocket connection
- # and abort these steps.
- if len(field) < 7 or not field.endswith('\r\n'):
- raise Exception('Wrong status line: %r' % field)
- m = re.match('[^ ]* ([^ ]*) .*', field)
- if m is None:
- raise Exception('No code found in status line: %r' % field)
- # 4.1 29. let /code/ be the substring of /field/ that starts from the
- # byte after the first 0x20 byte, and ends with the byte before the
- # second 0x20 byte.
- code = m.group(1)
- # 4.1 30. if /code/ is not three bytes long, or if any of the bytes in
- # /code/ are not in the range 0x30 to 0x90, then fail the WebSocket
- # connection and abort these steps.
- if not re.match('[0-9][0-9][0-9]', code):
- raise Exception(
- 'HTTP status code %r is not three digit in status line: %r' %
- (code, field))
- # 4.1 31. if /code/, interpreted as UTF-8, is "101", then move to the
- # next step.
- if code != '101':
- raise HttpStatusException(
- 'Expected HTTP status code 101 but found %r in status line: '
- '%r' % (code, field), int(code))
- # 4.1 32-39. read fields into /fields/
- fields = _read_fields(self._socket)
-
- self._logger.debug('Opening handshake response headers: %r', fields)
-
- # 4.1 40. _Fields processing_
- # read a byte from server
- ch = receive_bytes(self._socket, 1)
- if ch != '\n': # 0x0A
- raise Exception('Expected LF but found %r' % ch)
- # 4.1 41. check /fields/
- if len(fields['upgrade']) != 1:
- raise Exception(
- 'Multiple Upgrade headers found: %s' % fields['upgrade'])
- if len(fields['connection']) != 1:
- raise Exception(
- 'Multiple Connection headers found: %s' % fields['connection'])
- if len(fields['sec-websocket-origin']) != 1:
- raise Exception(
- 'Multiple Sec-WebSocket-Origin headers found: %s' %
- fields['sec-sebsocket-origin'])
- if len(fields['sec-websocket-location']) != 1:
- raise Exception(
- 'Multiple Sec-WebSocket-Location headers found: %s' %
- fields['sec-sebsocket-location'])
- # TODO(ukai): protocol
- # if the entry's name is "upgrade"
- # if the value is not exactly equal to the string "WebSocket",
- # then fail the WebSocket connection and abort these steps.
- if fields['upgrade'][0] != 'WebSocket':
- raise Exception(
- 'Unexpected Upgrade header value: %s' % fields['upgrade'][0])
- # if the entry's name is "connection"
- # if the value, converted to ASCII lowercase, is not exactly equal
- # to the string "upgrade", then fail the WebSocket connection and
- # abort these steps.
- if fields['connection'][0].lower() != 'upgrade':
- raise Exception(
- 'Unexpected Connection header value: %s' %
- fields['connection'][0])
- # TODO(ukai): check origin, location, cookie, ..
-
- # 4.1 42. let /challenge/ be the concatenation of /number_1/,
- # expressed as a big endian 32 bit integer, /number_2/, expressed
- # as big endian 32 bit integer, and the eight bytes of /key_3/ in the
- # order they were sent on the wire.
- challenge = struct.pack('!I', self._number1)
- challenge += struct.pack('!I', self._number2)
- challenge += self._key3
-
- self._logger.debug(
- 'Challenge: %r (%s)', challenge, util.hexify(challenge))
-
- # 4.1 43. let /expected/ be the MD5 fingerprint of /challenge/ as a
- # big-endian 128 bit string.
- expected = util.md5_hash(challenge).digest()
- self._logger.debug(
- 'Expected challenge response: %r (%s)',
- expected, util.hexify(expected))
-
- # 4.1 44. read sixteen bytes from the server.
- # let /reply/ be those bytes.
- reply = receive_bytes(self._socket, 16)
- self._logger.debug(
- 'Actual challenge response: %r (%s)', reply, util.hexify(reply))
-
- # 4.1 45. if /reply/ does not exactly equal /expected/, then fail
- # the WebSocket connection and abort these steps.
- if expected != reply:
- raise Exception(
- 'Bad challenge response: %r (expected) != %r (actual)' %
- (expected, reply))
- # 4.1 46. The *WebSocket connection is established*.
-
- def _generate_sec_websocket_key(self):
- # 4.1 16. let /spaces_n/ be a random integer from 1 to 12 inclusive.
- spaces = random.randint(1, 12)
- # 4.1 17. let /max_n/ be the largest integer not greater than
- # 4,294,967,295 divided by /spaces_n/.
- maxnum = 4294967295 / spaces
- # 4.1 18. let /number_n/ be a random integer from 0 to /max_n/
- # inclusive.
- number = random.randint(0, maxnum)
- # 4.1 19. let /product_n/ be the result of multiplying /number_n/ and
- # /spaces_n/ together.
- product = number * spaces
- # 4.1 20. let /key_n/ be a string consisting of /product_n/, expressed
- # in base ten using the numerals in the range U+0030 DIGIT ZERO (0) to
- # U+0039 DIGIT NINE (9).
- key = str(product)
- # 4.1 21. insert between one and twelve random characters from the
- # range U+0021 to U+002F and U+003A to U+007E into /key_n/ at random
- # positions.
- available_chars = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1)
- n = random.randint(1, 12)
- for _ in xrange(n):
- ch = random.choice(available_chars)
- pos = random.randint(0, len(key))
- key = key[0:pos] + chr(ch) + key[pos:]
- # 4.1 22. insert /spaces_n/ U+0020 SPACE characters into /key_n/ at
- # random positions other than start or end of the string.
- for _ in xrange(spaces):
- pos = random.randint(1, len(key) - 1)
- key = key[0:pos] + ' ' + key[pos:]
- return number, key
-
- def _generate_key3(self):
- # 4.1 26. let /key3/ be a string consisting of eight random bytes (or
- # equivalently, a random 64 bit integer encoded in a big-endian order).
- return ''.join([chr(random.randint(0, 255)) for _ in xrange(8)])
-
-
-class WebSocketHixie75Handshake(object):
- """WebSocket handshake processor for IETF Hixie 75."""
-
- _EXPECTED_RESPONSE = (
- 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
- _UPGRADE_HEADER_HIXIE75 +
- _CONNECTION_HEADER)
-
- def __init__(self, options):
- self._logger = util.get_class_logger(self)
-
- self._options = options
-
- def _skip_headers(self):
- terminator = '\r\n\r\n'
- pos = 0
- while pos < len(terminator):
- received = receive_bytes(self._socket, 1)
- if received == terminator[pos]:
- pos += 1
- elif received == terminator[0]:
- pos = 1
- else:
- pos = 0
-
- def handshake(self, socket):
- self._socket = socket
-
- request_line = _method_line(self._options.resource)
- self._logger.debug('Opening handshake Request-Line: %r', request_line)
- self._socket.sendall(request_line)
-
- headers = _UPGRADE_HEADER_HIXIE75 + _CONNECTION_HEADER
- headers += _format_host_header(
- self._options.server_host,
- self._options.server_port,
- self._options.use_tls)
- headers += _origin_header(self._options.origin)
- self._logger.debug('Opening handshake request headers: %r', headers)
- self._socket.sendall(headers)
-
- self._socket.sendall('\r\n')
-
- self._logger.info('Sent opening handshake request')
-
- for expected_char in WebSocketHixie75Handshake._EXPECTED_RESPONSE:
- received = receive_bytes(self._socket, 1)
- if expected_char != received:
- raise Exception('Handshake failure')
- # We cut corners and skip other headers.
- self._skip_headers()
-
-
-class WebSocketStream(object):
- """Frame processor for the WebSocket protocol (RFC 6455)."""
-
- def __init__(self, socket, handshake):
- self._handshake = handshake
- self._socket = socket
-
- # Filters applied to application data part of data frames.
- self._outgoing_frame_filter = None
- self._incoming_frame_filter = None
-
- if self._handshake._options.use_deflate_frame:
- self._outgoing_frame_filter = (
- util._RFC1979Deflater(None, False))
- self._incoming_frame_filter = util._RFC1979Inflater()
-
- self._fragmented = False
-
- def _mask_hybi(self, s):
- # TODO(tyoshino): os.urandom does open/read/close for every call. If
- # performance matters, change this to some library call that generates
- # cryptographically secure pseudo random number sequence.
- masking_nonce = os.urandom(4)
- result = [masking_nonce]
- count = 0
- for c in s:
- result.append(chr(ord(c) ^ ord(masking_nonce[count])))
- count = (count + 1) % len(masking_nonce)
- return ''.join(result)
-
- def send_frame_of_arbitrary_bytes(self, header, body):
- self._socket.sendall(header + self._mask_hybi(body))
-
- def send_data(self, payload, frame_type, end=True, mask=True,
- rsv1=0, rsv2=0, rsv3=0):
- if self._outgoing_frame_filter is not None:
- payload = self._outgoing_frame_filter.filter(payload)
-
- if self._fragmented:
- opcode = OPCODE_CONTINUATION
- else:
- opcode = frame_type
-
- if end:
- self._fragmented = False
- fin = 1
- else:
- self._fragmented = True
- fin = 0
-
- if self._handshake._options.use_deflate_frame:
- rsv1 = 1
-
- if mask:
- mask_bit = 1 << 7
- else:
- mask_bit = 0
-
- header = chr(fin << 7 | rsv1 << 6 | rsv2 << 5 | rsv3 << 4 | opcode)
- payload_length = len(payload)
- if payload_length <= 125:
- header += chr(mask_bit | payload_length)
- elif payload_length < 1 << 16:
- header += chr(mask_bit | 126) + struct.pack('!H', payload_length)
- elif payload_length < 1 << 63:
- header += chr(mask_bit | 127) + struct.pack('!Q', payload_length)
- else:
- raise Exception('Too long payload (%d byte)' % payload_length)
- if mask:
- payload = self._mask_hybi(payload)
- self._socket.sendall(header + payload)
-
- def send_binary(self, payload, end=True, mask=True):
- self.send_data(payload, OPCODE_BINARY, end, mask)
-
- def send_text(self, payload, end=True, mask=True):
- self.send_data(payload.encode('utf-8'), OPCODE_TEXT, end, mask)
-
- def _assert_receive_data(self, payload, opcode, fin, rsv1, rsv2, rsv3):
- (actual_fin, actual_rsv1, actual_rsv2, actual_rsv3, actual_opcode,
- payload_length) = read_frame_header(self._socket)
-
- if actual_opcode != opcode:
- raise Exception(
- 'Unexpected opcode: %d (expected) vs %d (actual)' %
- (opcode, actual_opcode))
-
- if actual_fin != fin:
- raise Exception(
- 'Unexpected fin: %d (expected) vs %d (actual)' %
- (fin, actual_fin))
-
- if rsv1 is None:
- rsv1 = 0
- if self._handshake._options.use_deflate_frame:
- rsv1 = 1
-
- if rsv2 is None:
- rsv2 = 0
-
- if rsv3 is None:
- rsv3 = 0
-
- if actual_rsv1 != rsv1:
- raise Exception(
- 'Unexpected rsv1: %r (expected) vs %r (actual)' %
- (rsv1, actual_rsv1))
-
- if actual_rsv2 != rsv2:
- raise Exception(
- 'Unexpected rsv2: %r (expected) vs %r (actual)' %
- (rsv2, actual_rsv2))
-
- if actual_rsv3 != rsv3:
- raise Exception(
- 'Unexpected rsv3: %r (expected) vs %r (actual)' %
- (rsv3, actual_rsv3))
-
- received = receive_bytes(self._socket, payload_length)
-
- if self._incoming_frame_filter is not None:
- received = self._incoming_frame_filter.filter(received)
-
- if len(received) != len(payload):
- raise Exception(
- 'Unexpected payload length: %d (expected) vs %d (actual)' %
- (len(payload), len(received)))
-
- if payload != received:
- raise Exception(
- 'Unexpected payload: %r (expected) vs %r (actual)' %
- (payload, received))
-
- def assert_receive_binary(self, payload, opcode=OPCODE_BINARY, fin=1,
- rsv1=None, rsv2=None, rsv3=None):
- self._assert_receive_data(payload, opcode, fin, rsv1, rsv2, rsv3)
-
- def assert_receive_text(self, payload, opcode=OPCODE_TEXT, fin=1,
- rsv1=None, rsv2=None, rsv3=None):
- self._assert_receive_data(payload.encode('utf-8'), opcode, fin, rsv1,
- rsv2, rsv3)
-
- def _build_close_frame(self, code, reason, mask):
- frame = chr(1 << 7 | OPCODE_CLOSE)
-
- if code is not None:
- body = struct.pack('!H', code) + reason.encode('utf-8')
- else:
- body = ''
- if mask:
- frame += chr(1 << 7 | len(body)) + self._mask_hybi(body)
- else:
- frame += chr(len(body)) + body
- return frame
-
- def send_close(self, code, reason):
- self._socket.sendall(
- self._build_close_frame(code, reason, True))
-
- def assert_receive_close(self, code, reason):
- expected_frame = self._build_close_frame(code, reason, False)
- actual_frame = receive_bytes(self._socket, len(expected_frame))
- if actual_frame != expected_frame:
- raise Exception(
- 'Unexpected close frame: %r (expected) vs %r (actual)' %
- (expected_frame, actual_frame))
-
-
-class WebSocketStreamHixie75(object):
- """Frame processor for the WebSocket protocol version Hixie 75 and HyBi 00.
- """
-
- _CLOSE_FRAME = '\xff\x00'
-
- def __init__(self, socket, unused_handshake):
- self._socket = socket
-
- def send_frame_of_arbitrary_bytes(self, header, body):
- self._socket.sendall(header + body)
-
- def send_data(self, payload, unused_frame_typem, unused_end, unused_mask):
- frame = ''.join(['\x00', payload, '\xff'])
- self._socket.sendall(frame)
-
- def send_binary(self, unused_payload, unused_end, unused_mask):
- pass
-
- def send_text(self, payload, unused_end, unused_mask):
- encoded_payload = payload.encode('utf-8')
- frame = ''.join(['\x00', encoded_payload, '\xff'])
- self._socket.sendall(frame)
-
- def assert_receive_binary(self, payload, opcode=OPCODE_BINARY, fin=1,
- rsv1=0, rsv2=0, rsv3=0):
- raise Exception('Binary frame is not supported in hixie75')
-
- def assert_receive_text(self, payload):
- received = receive_bytes(self._socket, 1)
-
- if received != '\x00':
- raise Exception(
- 'Unexpected frame type: %d (expected) vs %d (actual)' %
- (0, ord(received)))
-
- received = receive_bytes(self._socket, len(payload) + 1)
- if received[-1] != '\xff':
- raise Exception(
- 'Termination expected: 0xff (expected) vs %r (actual)' %
- received)
-
- if received[0:-1] != payload:
- raise Exception(
- 'Unexpected payload: %r (expected) vs %r (actual)' %
- (payload, received[0:-1]))
-
- def send_close(self, code, reason):
- self._socket.sendall(self._CLOSE_FRAME)
-
- def assert_receive_close(self, unused_code, unused_reason):
- closing = receive_bytes(self._socket, len(self._CLOSE_FRAME))
- if closing != self._CLOSE_FRAME:
- raise Exception('Didn\'t receive closing handshake')
-
-
-class ClientOptions(object):
- """Holds option values to configure the Client object."""
-
- def __init__(self):
- self.version = 13
- self.server_host = ''
- self.origin = ''
- self.resource = ''
- self.server_port = -1
- self.socket_timeout = 1000
- self.use_tls = False
- self.extensions = []
- # Enable deflate-application-data.
- self.use_deflate_frame = False
- # Enable mux
- self.use_mux = False
-
- def enable_deflate_frame(self):
- self.use_deflate_frame = True
- self.extensions.append(_DEFLATE_FRAME_EXTENSION)
-
- def enable_mux(self):
- self.use_mux = True
- self.extensions.append(_MUX_EXTENSION)
-
-
-def connect_socket_with_retry(host, port, timeout, use_tls,
- retry=10, sleep_sec=0.1):
- retry_count = 0
- while retry_count < retry:
- try:
- s = socket.socket()
- s.settimeout(timeout)
- s.connect((host, port))
- if use_tls:
- return _TLSSocket(s)
- return s
- except socket.error, e:
- if e.errno != errno.ECONNREFUSED:
- raise
- else:
- retry_count = retry_count + 1
- time.sleep(sleep_sec)
-
- return None
-
-
-class Client(object):
- """WebSocket client."""
-
- def __init__(self, options, handshake, stream_class):
- self._logger = util.get_class_logger(self)
-
- self._options = options
- self._socket = None
-
- self._handshake = handshake
- self._stream_class = stream_class
-
- def connect(self):
- self._socket = connect_socket_with_retry(
- self._options.server_host,
- self._options.server_port,
- self._options.socket_timeout,
- self._options.use_tls)
-
- self._handshake.handshake(self._socket)
-
- self._stream = self._stream_class(self._socket, self._handshake)
-
- self._logger.info('Connection established')
-
- def send_frame_of_arbitrary_bytes(self, header, body):
- self._stream.send_frame_of_arbitrary_bytes(header, body)
-
- def send_message(self, message, end=True, binary=False, raw=False,
- mask=True):
- if binary:
- self._stream.send_binary(message, end, mask)
- elif raw:
- self._stream.send_data(message, OPCODE_TEXT, end, mask)
- else:
- self._stream.send_text(message, end, mask)
-
- def assert_receive(self, payload, binary=False):
- if binary:
- self._stream.assert_receive_binary(payload)
- else:
- self._stream.assert_receive_text(payload)
-
- def send_close(self, code=STATUS_NORMAL_CLOSURE, reason=''):
- self._stream.send_close(code, reason)
-
- def assert_receive_close(self, code=STATUS_NORMAL_CLOSURE, reason=''):
- self._stream.assert_receive_close(code, reason)
-
- def close_socket(self):
- self._socket.close()
-
- def assert_connection_closed(self):
- try:
- read_data = receive_bytes(self._socket, 1)
- except Exception, e:
- if str(e).find(
- 'Connection closed before receiving requested length ') == 0:
- return
- try:
- error_number, message = e
- for error_name in ['ECONNRESET', 'WSAECONNRESET']:
- if (error_name in dir(errno) and
- error_number == getattr(errno, error_name)):
- return
- except:
- raise e
- raise e
-
- raise Exception('Connection is not closed (Read: %r)' % read_data)
-
-
-def create_client(options):
- return Client(
- options, WebSocketHandshake(options), WebSocketStream)
-
-
-def create_client_hybi00(options):
- return Client(
- options,
- WebSocketHybi00Handshake(options, '0'),
- WebSocketStreamHixie75)
-
-
-def create_client_hixie75(options):
- return Client(
- options, WebSocketHixie75Handshake(options), WebSocketStreamHixie75)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/endtoend_with_external_server.py b/testing/web-platform/tests/tools/pywebsocket/src/test/endtoend_with_external_server.py
deleted file mode 100755
index 47f86fdb4..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/endtoend_with_external_server.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Test for end-to-end with external server.
-
-This test is not run by run_all.py because it requires some preparations.
-If you would like to run this test correctly, launch Apache with mod_python
-and mod_pywebsocket manually. In addition, you should pass allow_draft75 option
-and example path as handler_scan option and Apache's DocumentRoot.
-"""
-
-
-import optparse
-import sys
-import test.test_endtoend
-import unittest
-
-
-_DEFAULT_WEB_SOCKET_PORT = 80
-
-
-class EndToEndTestWithExternalServer(test.test_endtoend.EndToEndTest):
- pass
-
-if __name__ == '__main__':
- parser = optparse.OptionParser()
- parser.add_option('-p', '--port', dest='port', type='int',
- default=_DEFAULT_WEB_SOCKET_PORT,
- help='external test server port.')
- (options, args) = parser.parse_args()
-
- test.test_endtoend._use_external_server = True
- test.test_endtoend._external_server_port = options.port
-
- unittest.main(argv=[sys.argv[0]])
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/mock.py b/testing/web-platform/tests/tools/pywebsocket/src/test/mock.py
deleted file mode 100644
index 6bffcac48..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/mock.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# Copyright 2011, 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.
-
-
-"""Mocks for testing.
-"""
-
-
-import Queue
-import threading
-
-from mod_pywebsocket import common
-from mod_pywebsocket.stream import StreamHixie75
-
-
-class _MockConnBase(object):
- """Base class of mocks for mod_python.apache.mp_conn.
-
- This enables tests to check what is written to a (mock) mp_conn.
- """
-
- def __init__(self):
- self._write_data = []
- self.remote_addr = 'fake_address'
-
- def write(self, data):
- """Override mod_python.apache.mp_conn.write."""
-
- self._write_data.append(data)
-
- def written_data(self):
- """Get bytes written to this mock."""
-
- return ''.join(self._write_data)
-
-
-class MockConn(_MockConnBase):
- """Mock for mod_python.apache.mp_conn.
-
- This enables tests to specify what should be read from a (mock) mp_conn as
- well as to check what is written to it.
- """
-
- def __init__(self, read_data):
- """Constructs an instance.
-
- Args:
- read_data: bytes that should be returned when read* methods are
- called.
- """
-
- _MockConnBase.__init__(self)
- self._read_data = read_data
- self._read_pos = 0
-
- def readline(self):
- """Override mod_python.apache.mp_conn.readline."""
-
- if self._read_pos >= len(self._read_data):
- return ''
- end_index = self._read_data.find('\n', self._read_pos) + 1
- if not end_index:
- end_index = len(self._read_data)
- return self._read_up_to(end_index)
-
- def read(self, length):
- """Override mod_python.apache.mp_conn.read."""
-
- if self._read_pos >= len(self._read_data):
- return ''
- end_index = min(len(self._read_data), self._read_pos + length)
- return self._read_up_to(end_index)
-
- def _read_up_to(self, end_index):
- line = self._read_data[self._read_pos:end_index]
- self._read_pos = end_index
- return line
-
-
-class MockBlockingConn(_MockConnBase):
- """Blocking mock for mod_python.apache.mp_conn.
-
- This enables tests to specify what should be read from a (mock) mp_conn as
- well as to check what is written to it.
- Callers of read* methods will block if there is no bytes available.
- """
-
- def __init__(self):
- _MockConnBase.__init__(self)
- self._queue = Queue.Queue()
-
- def readline(self):
- """Override mod_python.apache.mp_conn.readline."""
- line = ''
- while True:
- c = self._queue.get()
- line += c
- if c == '\n':
- return line
-
- def read(self, length):
- """Override mod_python.apache.mp_conn.read."""
-
- data = ''
- for unused in range(length):
- data += self._queue.get()
- return data
-
- def put_bytes(self, bytes):
- """Put bytes to be read from this mock.
-
- Args:
- bytes: bytes to be read.
- """
-
- for byte in bytes:
- self._queue.put(byte)
-
-
-class MockTable(dict):
- """Mock table.
-
- This mimics mod_python mp_table. Note that only the methods used by
- tests are overridden.
- """
-
- def __init__(self, copy_from={}):
- if isinstance(copy_from, dict):
- copy_from = copy_from.items()
- for key, value in copy_from:
- self.__setitem__(key, value)
-
- def __getitem__(self, key):
- return super(MockTable, self).__getitem__(key.lower())
-
- def __setitem__(self, key, value):
- super(MockTable, self).__setitem__(key.lower(), value)
-
- def get(self, key, def_value=None):
- return super(MockTable, self).get(key.lower(), def_value)
-
-
-class MockRequest(object):
- """Mock request.
-
- This mimics mod_python request.
- """
-
- def __init__(self, uri=None, headers_in={}, connection=None, method='GET',
- protocol='HTTP/1.1', is_https=False):
- """Construct an instance.
-
- Arguments:
- uri: URI of the request.
- headers_in: Request headers.
- connection: Connection used for the request.
- method: request method.
- is_https: Whether this request is over SSL.
-
- See the document of mod_python Request for details.
- """
- self.uri = uri
- self.unparsed_uri = uri
- self.connection = connection
- self.method = method
- self.protocol = protocol
- self.headers_in = MockTable(headers_in)
- # self.is_https_ needs to be accessible from tests. To avoid name
- # conflict with self.is_https(), it is named as such.
- self.is_https_ = is_https
- self.ws_stream = StreamHixie75(self, True)
- self.ws_close_code = None
- self.ws_close_reason = None
- self.ws_version = common.VERSION_HYBI00
- self.ws_deflate = False
-
- def is_https(self):
- """Return whether this request is over SSL."""
- return self.is_https_
-
-
-class MockDispatcher(object):
- """Mock for dispatch.Dispatcher."""
-
- def __init__(self):
- self.do_extra_handshake_called = False
-
- def do_extra_handshake(self, conn_context):
- self.do_extra_handshake_called = True
-
- def transfer_data(self, conn_context):
- pass
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/mux_client_for_testing.py b/testing/web-platform/tests/tools/pywebsocket/src/test/mux_client_for_testing.py
deleted file mode 100644
index dd5435a8c..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/mux_client_for_testing.py
+++ /dev/null
@@ -1,690 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""WebSocket client utility for testing mux extension.
-
-This code should be independent from mod_pywebsocket. See the comment of
-client_for_testing.py.
-
-NOTE: This code is far from robust like client_for_testing.py.
-"""
-
-
-
-import Queue
-import base64
-import collections
-import email
-import email.parser
-import logging
-import math
-import os
-import random
-import socket
-import struct
-import threading
-
-from mod_pywebsocket import util
-
-from test import client_for_testing
-
-
-_CONTROL_CHANNEL_ID = 0
-_DEFAULT_CHANNEL_ID = 1
-
-_MUX_OPCODE_ADD_CHANNEL_REQUEST = 0
-_MUX_OPCODE_ADD_CHANNEL_RESPONSE = 1
-_MUX_OPCODE_FLOW_CONTROL = 2
-_MUX_OPCODE_DROP_CHANNEL = 3
-_MUX_OPCODE_NEW_CHANNEL_SLOT = 4
-
-
-class _ControlBlock:
- def __init__(self, opcode):
- self.opcode = opcode
-
-
-def _parse_handshake_response(response):
- status_line, header_lines = response.split('\r\n', 1)
-
- words = status_line.split(' ')
- if len(words) < 3:
- raise ValueError('Bad Status-Line syntax %r' % status_line)
- [version, response_code] = words[:2]
- if version != 'HTTP/1.1':
- raise ValueError('Bad response version %r' % version)
-
- if response_code != '101':
- raise ValueError('Bad response code %r ' % response_code)
- headers = email.parser.Parser().parsestr(header_lines)
- return headers
-
-
-def _parse_channel_id(data, offset=0):
- length = len(data)
- remaining = length - offset
-
- if remaining <= 0:
- raise Exception('No channel id found')
-
- channel_id = ord(data[offset])
- channel_id_length = 1
- if channel_id & 0xe0 == 0xe0:
- if remaining < 4:
- raise Exception('Invalid channel id format')
- channel_id = struct.unpack('!L',
- data[offset:offset+4])[0] & 0x1fffffff
- channel_id_length = 4
- elif channel_id & 0xc0 == 0xc0:
- if remaining < 3:
- raise Exception('Invalid channel id format')
- channel_id = (((channel_id & 0x1f) << 16) +
- struct.unpack('!H', data[offset+1:offset+3])[0])
- channel_id_length = 3
- elif channel_id & 0x80 == 0x80:
- if remaining < 2:
- raise Exception('Invalid channel id format')
- channel_id = struct.unpack('!H', data[offset:offset+2])[0] & 0x3fff
- channel_id_length = 2
-
- return channel_id, channel_id_length
-
-
-def _parse_number(data, offset=0):
- first_byte = ord(data[offset])
- if (first_byte & 0x80) != 0:
- raise Exception('The MSB of number field must be unset')
- first_byte = first_byte & 0x7f
- if first_byte == 127:
- if offset + 9 > len(data):
- raise Exception('Invalid number')
- return struct.unpack('!Q', data[offset+1:offset+9])[0], 9
- if first_byte == 126:
- if offset + 3 > len(data):
- raise Exception('Invalid number')
- return struct.unpack('!H', data[offset+1:offset+3])[0], 3
- return first_byte, 1
-
-
-def _parse_size_and_contents(data, offset=0):
- size, advance = _parse_number(data, offset)
- start_position = offset + advance
- end_position = start_position + size
- if len(data) < end_position:
- raise Exception('Invalid size of control block (%d < %d)' % (
- len(data), end_position))
- return data[start_position:end_position], size + advance
-
-
-def _parse_control_blocks(data):
- blocks = []
- length = len(data)
- pos = 0
-
- while pos < length:
- first_byte = ord(data[pos])
- pos += 1
- opcode = (first_byte >> 5) & 0x7
- block = _ControlBlock(opcode)
-
- # TODO(bashi): Support more opcode
- if opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
- block.encode = first_byte & 3
- block.rejected = (first_byte >> 4) & 1
-
- channel_id, advance = _parse_channel_id(data, pos)
- block.channel_id = channel_id
- pos += advance
-
- encoded_handshake, advance = _parse_size_and_contents(data, pos)
- block.encoded_handshake = encoded_handshake
- pos += advance
- blocks.append(block)
- elif opcode == _MUX_OPCODE_DROP_CHANNEL:
- block.mux_error = (first_byte >> 4) & 1
-
- channel_id, advance = _parse_channel_id(data, pos)
- block.channel_id = channel_id
- pos += advance
-
- reason, advance = _parse_size_and_contents(data, pos)
- if len(reason) == 0:
- block.drop_code = None
- block.drop_message = ''
- elif len(reason) >= 2:
- block.drop_code = struct.unpack('!H', reason[:2])[0]
- block.drop_message = reason[2:]
- else:
- raise Exception('Invalid DropChannel')
- pos += advance
- blocks.append(block)
- elif opcode == _MUX_OPCODE_FLOW_CONTROL:
- channel_id, advance = _parse_channel_id(data, pos)
- block.channel_id = channel_id
- pos += advance
- send_quota, advance = _parse_number(data, pos)
- block.send_quota = send_quota
- pos += advance
- blocks.append(block)
- elif opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
- fallback = first_byte & 1
- slots, advance = _parse_number(data, pos)
- pos += advance
- send_quota, advance = _parse_number(data, pos)
- pos += advance
- if fallback == 1 and (slots != 0 or send_quota != 0):
- raise Exception('slots and send_quota must be zero if F bit '
- 'is set')
- block.fallback = fallback
- block.slots = slots
- block.send_quota = send_quota
- blocks.append(block)
- else:
- raise Exception(
- 'Unsupported mux opcode %d received' % opcode)
-
- return blocks
-
-
-def _encode_channel_id(channel_id):
- if channel_id < 0:
- raise ValueError('Channel id %d must not be negative' % channel_id)
-
- if channel_id < 2 ** 7:
- return chr(channel_id)
- if channel_id < 2 ** 14:
- return struct.pack('!H', 0x8000 + channel_id)
- if channel_id < 2 ** 21:
- first = chr(0xc0 + (channel_id >> 16))
- return first + struct.pack('!H', channel_id & 0xffff)
- if channel_id < 2 ** 29:
- return struct.pack('!L', 0xe0000000 + channel_id)
-
- raise ValueError('Channel id %d is too large' % channel_id)
-
-
-def _encode_number(number):
- if number <= 125:
- return chr(number)
- elif number < (1 << 16):
- return chr(0x7e) + struct.pack('!H', number)
- elif number < (1 << 63):
- return chr(0x7f) + struct.pack('!Q', number)
- else:
- raise Exception('Invalid number')
-
-
-def _create_add_channel_request(channel_id, encoded_handshake,
- encoding=0):
- length = len(encoded_handshake)
- handshake_length = _encode_number(length)
-
- first_byte = (_MUX_OPCODE_ADD_CHANNEL_REQUEST << 5) | encoding
- return (chr(first_byte) + _encode_channel_id(channel_id) +
- handshake_length + encoded_handshake)
-
-
-def _create_flow_control(channel_id, replenished_quota):
- first_byte = (_MUX_OPCODE_FLOW_CONTROL << 5)
- return (chr(first_byte) + _encode_channel_id(channel_id) +
- _encode_number(replenished_quota))
-
-
-class _MuxReaderThread(threading.Thread):
- """Mux reader thread.
-
- Reads frames and passes them to the mux client. This thread accesses
- private functions/variables of the mux client.
- """
-
- def __init__(self, mux):
- threading.Thread.__init__(self)
- self.setDaemon(True)
- self._mux = mux
- self._stop_requested = False
-
- def _receive_message(self):
- first_opcode = None
- pending_payload = []
- while not self._stop_requested:
- fin, rsv1, rsv2, rsv3, opcode, payload_length = (
- client_for_testing.read_frame_header(self._mux._socket))
-
- if not first_opcode:
- if opcode == client_for_testing.OPCODE_TEXT:
- raise Exception('Received a text message on physical '
- 'connection')
- if opcode == client_for_testing.OPCODE_CONTINUATION:
- raise Exception('Received an intermediate frame but '
- 'fragmentation was not started')
- if (opcode == client_for_testing.OPCODE_BINARY or
- opcode == client_for_testing.OPCODE_PONG or
- opcode == client_for_testing.OPCODE_PONG or
- opcode == client_for_testing.OPCODE_CLOSE):
- first_opcode = opcode
- else:
- raise Exception('Received an undefined opcode frame: %d' %
- opcode)
-
- elif opcode != client_for_testing.OPCODE_CONTINUATION:
- raise Exception('Received a new opcode before '
- 'terminating fragmentation')
-
- payload = client_for_testing.receive_bytes(
- self._mux._socket, payload_length)
-
- if self._mux._incoming_frame_filter is not None:
- payload = self._mux._incoming_frame_filter.filter(payload)
-
- pending_payload.append(payload)
-
- if fin:
- break
-
- if self._stop_requested:
- return None, None
-
- message = ''.join(pending_payload)
- return first_opcode, message
-
- def request_stop(self):
- self._stop_requested = True
-
- def run(self):
- try:
- while not self._stop_requested:
- # opcode is OPCODE_BINARY or control opcodes when a message
- # is succesfully received.
- opcode, message = self._receive_message()
- if not opcode:
- return
- if opcode == client_for_testing.OPCODE_BINARY:
- channel_id, advance = _parse_channel_id(message)
- self._mux._dispatch_frame(channel_id, message[advance:])
- else:
- self._mux._process_control_message(opcode, message)
- finally:
- self._mux._notify_reader_done()
-
-
-class _InnerFrame(object):
- def __init__(self, fin, rsv1, rsv2, rsv3, opcode, payload):
- self.fin = fin
- self.rsv1 = rsv1
- self.rsv2 = rsv2
- self.rsv3 = rsv3
- self.opcode = opcode
- self.payload = payload
-
-
-class _LogicalChannelData(object):
- def __init__(self):
- self.queue = Queue.Queue()
- self.send_quota = 0
- self.receive_quota = 0
-
-
-class MuxClient(object):
- """WebSocket mux client.
-
- Note that this class is NOT thread-safe. Do not access an instance of this
- class from multiple threads at a same time.
- """
-
- def __init__(self, options):
- self._logger = util.get_class_logger(self)
-
- self._options = options
- self._options.enable_mux()
- self._stream = None
- self._socket = None
- self._handshake = client_for_testing.WebSocketHandshake(self._options)
- self._incoming_frame_filter = None
- self._outgoing_frame_filter = None
-
- self._is_active = False
- self._read_thread = None
- self._control_blocks_condition = threading.Condition()
- self._control_blocks = []
- self._channel_slots = collections.deque()
- self._logical_channels_condition = threading.Condition();
- self._logical_channels = {}
- self._timeout = 2
- self._physical_connection_close_event = None
- self._physical_connection_close_message = None
-
- def _parse_inner_frame(self, data):
- if len(data) == 0:
- raise Exception('Invalid encapsulated frame received')
-
- first_byte = ord(data[0])
- fin = (first_byte << 7) & 1
- rsv1 = (first_byte << 6) & 1
- rsv2 = (first_byte << 5) & 1
- rsv3 = (first_byte << 4) & 1
- opcode = first_byte & 0xf
-
- if self._outgoing_frame_filter:
- payload = self._outgoing_frame_filter.filter(
- data[1:])
- else:
- payload = data[1:]
-
- return _InnerFrame(fin, rsv1, rsv2, rsv3, opcode, payload)
-
- def _process_mux_control_blocks(self):
- for block in self._control_blocks:
- if block.opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
- # AddChannelResponse will be handled in add_channel().
- continue
- elif block.opcode == _MUX_OPCODE_FLOW_CONTROL:
- try:
- self._logical_channels_condition.acquire()
- if not block.channel_id in self._logical_channels:
- raise Exception('Invalid flow control received for '
- 'channel id %d' % block.channel_id)
- self._logical_channels[block.channel_id].send_quota += (
- block.send_quota)
- self._logical_channels_condition.notify()
- finally:
- self._logical_channels_condition.release()
- elif block.opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
- self._channel_slots.extend([block.send_quota] * block.slots)
-
- def _dispatch_frame(self, channel_id, payload):
- if channel_id == _CONTROL_CHANNEL_ID:
- try:
- self._control_blocks_condition.acquire()
- self._control_blocks += _parse_control_blocks(payload)
- self._process_mux_control_blocks()
- self._control_blocks_condition.notify()
- finally:
- self._control_blocks_condition.release()
- else:
- try:
- self._logical_channels_condition.acquire()
- if not channel_id in self._logical_channels:
- raise Exception('Received logical frame on channel id '
- '%d, which is not established' %
- channel_id)
-
- inner_frame = self._parse_inner_frame(payload)
- self._logical_channels[channel_id].receive_quota -= (
- len(inner_frame.payload))
- if self._logical_channels[channel_id].receive_quota < 0:
- raise Exception('The server violates quota on '
- 'channel id %d' % channel_id)
- finally:
- self._logical_channels_condition.release()
- self._logical_channels[channel_id].queue.put(inner_frame)
-
- def _process_control_message(self, opcode, message):
- # Ping/Pong are not supported.
- if opcode == client_for_testing.OPCODE_CLOSE:
- self._physical_connection_close_message = message
- if self._is_active:
- self._stream.send_close(
- code=client_for_testing.STATUS_NORMAL_CLOSURE, reason='')
- self._read_thread.request_stop()
-
- if self._physical_connection_close_event:
- self._physical_connection_close_event.set()
-
- def _notify_reader_done(self):
- self._logger.debug('Read thread terminated.')
- self.close_socket()
-
- def _assert_channel_slot_available(self):
- try:
- self._control_blocks_condition.acquire()
- if len(self._channel_slots) == 0:
- # Wait once
- self._control_blocks_condition.wait(timeout=self._timeout)
- finally:
- self._control_blocks_condition.release()
-
- if len(self._channel_slots) == 0:
- raise Exception('Failed to receive NewChannelSlot')
-
- def _assert_send_quota_available(self, channel_id):
- try:
- self._logical_channels_condition.acquire()
- if self._logical_channels[channel_id].send_quota == 0:
- # Wait once
- self._logical_channels_condition.wait(timeout=self._timeout)
- finally:
- self._logical_channels_condition.release()
-
- if self._logical_channels[channel_id].send_quota == 0:
- raise Exception('Failed to receive FlowControl for channel id %d' %
- channel_id)
-
- def connect(self):
- self._socket = client_for_testing.connect_socket_with_retry(
- self._options.server_host,
- self._options.server_port,
- self._options.socket_timeout,
- self._options.use_tls)
-
- self._handshake.handshake(self._socket)
- self._stream = client_for_testing.WebSocketStream(
- self._socket, self._handshake)
-
- self._logical_channels[_DEFAULT_CHANNEL_ID] = _LogicalChannelData()
-
- self._read_thread = _MuxReaderThread(self)
- self._read_thread.start()
-
- self._assert_channel_slot_available()
- self._assert_send_quota_available(_DEFAULT_CHANNEL_ID)
-
- self._is_active = True
- self._logger.info('Connection established')
-
- def add_channel(self, channel_id, options):
- if not self._is_active:
- raise Exception('Mux client is not active')
-
- if channel_id in self._logical_channels:
- raise Exception('Channel id %d already exists' % channel_id)
-
- try:
- send_quota = self._channel_slots.popleft()
- except IndexError, e:
- raise Exception('No channel slots: %r' % e)
-
- # Create AddChannel request
- request_line = 'GET %s HTTP/1.1\r\n' % options.resource
- fields = []
- if options.server_port == client_for_testing.DEFAULT_PORT:
- fields.append('Host: %s\r\n' % options.server_host.lower())
- else:
- fields.append('Host: %s:%d\r\n' % (options.server_host.lower(),
- options.server_port))
- fields.append('Origin: %s\r\n' % options.origin.lower())
- fields.append('Connection: Upgrade\r\n')
-
- if len(options.extensions) > 0:
- fields.append('Sec-WebSocket-Extensions: %s\r\n' %
- ', '.join(options.extensions))
-
- handshake = request_line + ''.join(fields) + '\r\n'
- add_channel_request = _create_add_channel_request(
- channel_id, handshake)
- payload = _encode_channel_id(_CONTROL_CHANNEL_ID) + add_channel_request
- self._stream.send_binary(payload)
-
- # Wait AddChannelResponse
- self._logger.debug('Waiting AddChannelResponse for the request...')
- response = None
- try:
- self._control_blocks_condition.acquire()
- while True:
- for block in self._control_blocks:
- if block.opcode != _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
- continue
- if block.channel_id == channel_id:
- response = block
- self._control_blocks.remove(response)
- break
- if response:
- break
- self._control_blocks_condition.wait(self._timeout)
- if not self._is_active:
- raise Exception('AddChannelRequest timed out')
- finally:
- self._control_blocks_condition.release()
-
- # Validate AddChannelResponse
- if response.rejected:
- raise Exception('The server rejected AddChannelRequest')
-
- fields = _parse_handshake_response(response.encoded_handshake)
-
- # Should we reject when Upgrade, Connection, or Sec-WebSocket-Accept
- # headers exist?
-
- self._logical_channels_condition.acquire()
- self._logical_channels[channel_id] = _LogicalChannelData()
- self._logical_channels[channel_id].send_quota = send_quota
- self._logical_channels_condition.release()
-
- self._logger.debug('Logical channel %d established' % channel_id)
-
- def _check_logical_channel_is_opened(self, channel_id):
- if not self._is_active:
- raise Exception('Mux client is not active')
-
- if not channel_id in self._logical_channels:
- raise Exception('Logical channel %d is not established.')
-
- def drop_channel(self, channel_id):
- # TODO(bashi): Implement
- pass
-
- def send_flow_control(self, channel_id, replenished_quota):
- self._check_logical_channel_is_opened(channel_id)
- flow_control = _create_flow_control(channel_id, replenished_quota)
- payload = _encode_channel_id(_CONTROL_CHANNEL_ID) + flow_control
- # Replenish receive quota
- try:
- self._logical_channels_condition.acquire()
- self._logical_channels[channel_id].receive_quota += (
- replenished_quota)
- finally:
- self._logical_channels_condition.release()
- self._stream.send_binary(payload)
-
- def send_message(self, channel_id, message, end=True, binary=False):
- self._check_logical_channel_is_opened(channel_id)
-
- if binary:
- first_byte = (end << 7) | client_for_testing.OPCODE_BINARY
- else:
- first_byte = (end << 7) | client_for_testing.OPCODE_TEXT
- message = message.encode('utf-8')
-
- try:
- self._logical_channels_condition.acquire()
- if self._logical_channels[channel_id].send_quota < len(message):
- raise Exception('Send quota violation: %d < %d' % (
- self._logical_channels[channel_id].send_quota,
- len(message)))
-
- self._logical_channels[channel_id].send_quota -= len(message)
- finally:
- self._logical_channels_condition.release()
- payload = _encode_channel_id(channel_id) + chr(first_byte) + message
- self._stream.send_binary(payload)
-
- def assert_receive(self, channel_id, payload, binary=False):
- self._check_logical_channel_is_opened(channel_id)
-
- try:
- inner_frame = self._logical_channels[channel_id].queue.get(
- timeout=self._timeout)
- except Queue.Empty, e:
- raise Exception('Cannot receive message from channel id %d' %
- channel_id)
-
- if binary:
- opcode = client_for_testing.OPCODE_BINARY
- else:
- opcode = client_for_testing.OPCODE_TEXT
-
- if inner_frame.opcode != opcode:
- raise Exception('Unexpected opcode received (%r != %r)' %
- (expected_opcode, inner_frame.opcode))
-
- if inner_frame.payload != payload:
- raise Exception('Unexpected payload received')
-
- def send_close(self, channel_id, code=None, reason=''):
- self._check_logical_channel_is_opened(channel_id)
-
- if code is not None:
- body = struct.pack('!H', code) + reason.encode('utf-8')
- else:
- body = ''
-
- first_byte = (1 << 7) | client_for_testing.OPCODE_CLOSE
- payload = _encode_channel_id(channel_id) + chr(first_byte) + body
- self._stream.send_binary(payload)
-
- def assert_receive_close(self, channel_id):
- self._check_logical_channel_is_opened(channel_id)
-
- try:
- inner_frame = self._logical_channels[channel_id].queue.get(
- timeout=self._timeout)
- except Queue.Empty, e:
- raise Exception('Cannot receive message from channel id %d' %
- channel_id)
- if inner_frame.opcode != client_for_testing.OPCODE_CLOSE:
- raise Exception('Didn\'t receive close frame')
-
- def send_physical_connection_close(self, code=None, reason=''):
- self._physical_connection_close_event = threading.Event()
- self._stream.send_close(code, reason)
-
- # This method can be used only after calling
- # send_physical_connection_close().
- def assert_physical_connection_receive_close(
- self, code=client_for_testing.STATUS_NORMAL_CLOSURE, reason=''):
- self._physical_connection_close_event.wait(timeout=self._timeout)
- if (not self._physical_connection_close_event.isSet() or
- not self._physical_connection_close_message):
- raise Exception('Didn\'t receive closing handshake')
-
- def close_socket(self):
- self._is_active = False
- self._socket.close()
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/run_all.py b/testing/web-platform/tests/tools/pywebsocket/src/test/run_all.py
deleted file mode 100755
index 80a5d87d8..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/run_all.py
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Run all tests in the same directory.
-
-This suite is expected to be run under pywebsocket's src directory, i.e. the
-directory containing mod_pywebsocket, test, etc.
-
-To change loggin level, please specify --log-level option.
- python test/run_test.py --log-level debug
-
-To pass any option to unittest module, please specify options after '--'. For
-example, run this for making the test runner verbose.
- python test/run_test.py --log-level debug -- -v
-"""
-
-
-import logging
-import optparse
-import os
-import re
-import sys
-import unittest
-
-
-_TEST_MODULE_PATTERN = re.compile(r'^(test_.+)\.py$')
-
-
-def _list_test_modules(directory):
- module_names = []
- for filename in os.listdir(directory):
- match = _TEST_MODULE_PATTERN.search(filename)
- if match:
- module_names.append(match.group(1))
- return module_names
-
-
-def _suite():
- loader = unittest.TestLoader()
- return loader.loadTestsFromNames(
- _list_test_modules(os.path.join(os.path.split(__file__)[0], '.')))
-
-
-if __name__ == '__main__':
- parser = optparse.OptionParser()
- parser.add_option('--log-level', '--log_level', type='choice',
- dest='log_level', default='warning',
- choices=['debug', 'info', 'warning', 'warn', 'error',
- 'critical'])
- options, args = parser.parse_args()
- logging.basicConfig(
- level=logging.getLevelName(options.log_level.upper()),
- format='%(levelname)s %(asctime)s '
- '%(filename)s:%(lineno)d] '
- '%(message)s',
- datefmt='%H:%M:%S')
- unittest.main(defaultTest='_suite', argv=[sys.argv[0]] + args)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/set_sys_path.py b/testing/web-platform/tests/tools/pywebsocket/src/test/set_sys_path.py
deleted file mode 100644
index e3c6db9ea..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/set_sys_path.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Configuration for testing.
-
-Test files should import this module before mod_pywebsocket.
-"""
-
-
-import os
-import sys
-
-
-# Add the parent directory to sys.path to enable importing mod_pywebsocket.
-sys.path.insert(0, os.path.join(os.path.split(__file__)[0], '..'))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_dispatch.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_dispatch.py
deleted file mode 100755
index 9ca3d4f3a..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_dispatch.py
+++ /dev/null
@@ -1,288 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for dispatch module."""
-
-
-import os
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import dispatch
-from mod_pywebsocket import handshake
-from test import mock
-
-
-_TEST_HANDLERS_DIR = os.path.join(
- os.path.split(__file__)[0], 'testdata', 'handlers')
-
-_TEST_HANDLERS_SUB_DIR = os.path.join(_TEST_HANDLERS_DIR, 'sub')
-
-
-class DispatcherTest(unittest.TestCase):
- """A unittest for dispatch module."""
-
- def test_normalize_path(self):
- self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'),
- dispatch._normalize_path('/a/b'))
- self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'),
- dispatch._normalize_path('\\a\\b'))
- self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'),
- dispatch._normalize_path('/a/c/../b'))
- self.assertEqual(os.path.abspath('abc').replace('\\', '/'),
- dispatch._normalize_path('abc'))
-
- def test_converter(self):
- converter = dispatch._create_path_to_resource_converter('/a/b')
- # Python built by MSC inserts a drive name like 'C:\' via realpath().
- # Converter Generator expands provided path using realpath() and uses
- # the path including a drive name to verify the prefix.
- os_root = os.path.realpath('/')
- self.assertEqual('/h', converter(os_root + 'a/b/h_wsh.py'))
- self.assertEqual('/c/h', converter(os_root + 'a/b/c/h_wsh.py'))
- self.assertEqual(None, converter(os_root + 'a/b/h.py'))
- self.assertEqual(None, converter('a/b/h_wsh.py'))
-
- converter = dispatch._create_path_to_resource_converter('a/b')
- self.assertEqual('/h', converter(dispatch._normalize_path(
- 'a/b/h_wsh.py')))
-
- converter = dispatch._create_path_to_resource_converter('/a/b///')
- self.assertEqual('/h', converter(os_root + 'a/b/h_wsh.py'))
- self.assertEqual('/h', converter(dispatch._normalize_path(
- '/a/b/../b/h_wsh.py')))
-
- converter = dispatch._create_path_to_resource_converter(
- '/a/../a/b/../b/')
- self.assertEqual('/h', converter(os_root + 'a/b/h_wsh.py'))
-
- converter = dispatch._create_path_to_resource_converter(r'\a\b')
- self.assertEqual('/h', converter(os_root + r'a\b\h_wsh.py'))
- self.assertEqual('/h', converter(os_root + r'a/b/h_wsh.py'))
-
- def test_enumerate_handler_file_paths(self):
- paths = list(
- dispatch._enumerate_handler_file_paths(_TEST_HANDLERS_DIR))
- paths.sort()
- self.assertEqual(8, len(paths))
- expected_paths = [
- os.path.join(_TEST_HANDLERS_DIR, 'abort_by_user_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'origin_check_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'sub',
- 'exception_in_transfer_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'sub', 'plain_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'sub',
- 'wrong_handshake_sig_wsh.py'),
- os.path.join(_TEST_HANDLERS_DIR, 'sub',
- 'wrong_transfer_sig_wsh.py'),
- ]
- for expected, actual in zip(expected_paths, paths):
- self.assertEqual(expected, actual)
-
- def test_source_handler_file(self):
- self.assertRaises(
- dispatch.DispatchException, dispatch._source_handler_file, '')
- self.assertRaises(
- dispatch.DispatchException, dispatch._source_handler_file, 'def')
- self.assertRaises(
- dispatch.DispatchException, dispatch._source_handler_file, '1/0')
- self.failUnless(dispatch._source_handler_file(
- 'def web_socket_do_extra_handshake(request):pass\n'
- 'def web_socket_transfer_data(request):pass\n'))
-
- def test_source_warnings(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- warnings = dispatcher.source_warnings()
- warnings.sort()
- expected_warnings = [
- (os.path.realpath(os.path.join(
- _TEST_HANDLERS_DIR, 'blank_wsh.py')) +
- ': web_socket_do_extra_handshake is not defined.'),
- (os.path.realpath(os.path.join(
- _TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py')) +
- ': web_socket_do_extra_handshake is not callable.'),
- (os.path.realpath(os.path.join(
- _TEST_HANDLERS_DIR, 'sub', 'wrong_handshake_sig_wsh.py')) +
- ': web_socket_do_extra_handshake is not defined.'),
- (os.path.realpath(os.path.join(
- _TEST_HANDLERS_DIR, 'sub', 'wrong_transfer_sig_wsh.py')) +
- ': web_socket_transfer_data is not defined.'),
- ]
- self.assertEquals(4, len(warnings))
- for expected, actual in zip(expected_warnings, warnings):
- self.assertEquals(expected, actual)
-
- def test_do_extra_handshake(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- request = mock.MockRequest()
- request.ws_resource = '/origin_check'
- request.ws_origin = 'http://example.com'
- dispatcher.do_extra_handshake(request) # Must not raise exception.
-
- request.ws_origin = 'http://bad.example.com'
- try:
- dispatcher.do_extra_handshake(request)
- self.fail('Could not catch HandshakeException with 403 status')
- except handshake.HandshakeException, e:
- self.assertEquals(403, e.status)
- except Exception, e:
- self.fail('Unexpected exception: %r' % e)
-
- def test_abort_extra_handshake(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- request = mock.MockRequest()
- request.ws_resource = '/abort_by_user'
- self.assertRaises(handshake.AbortedByUserException,
- dispatcher.do_extra_handshake, request)
-
- def test_transfer_data(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
-
- request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
- request.ws_resource = '/origin_check'
- request.ws_protocol = 'p1'
- dispatcher.transfer_data(request)
- self.assertEqual('origin_check_wsh.py is called for /origin_check, p1'
- '\xff\x00',
- request.connection.written_data())
-
- request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
- request.ws_resource = '/sub/plain'
- request.ws_protocol = None
- dispatcher.transfer_data(request)
- self.assertEqual('sub/plain_wsh.py is called for /sub/plain, None'
- '\xff\x00',
- request.connection.written_data())
-
- request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
- request.ws_resource = '/sub/plain?'
- request.ws_protocol = None
- dispatcher.transfer_data(request)
- self.assertEqual('sub/plain_wsh.py is called for /sub/plain?, None'
- '\xff\x00',
- request.connection.written_data())
-
- request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
- request.ws_resource = '/sub/plain?q=v'
- request.ws_protocol = None
- dispatcher.transfer_data(request)
- self.assertEqual('sub/plain_wsh.py is called for /sub/plain?q=v, None'
- '\xff\x00',
- request.connection.written_data())
-
- def test_transfer_data_no_handler(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- for resource in ['/blank', '/sub/non_callable',
- '/sub/no_wsh_at_the_end', '/does/not/exist']:
- request = mock.MockRequest(connection=mock.MockConn(''))
- request.ws_resource = resource
- request.ws_protocol = 'p2'
- try:
- dispatcher.transfer_data(request)
- self.fail()
- except dispatch.DispatchException, e:
- self.failUnless(str(e).find('No handler') != -1)
- except Exception:
- self.fail()
-
- def test_transfer_data_handler_exception(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- request = mock.MockRequest(connection=mock.MockConn(''))
- request.ws_resource = '/sub/exception_in_transfer'
- request.ws_protocol = 'p3'
- try:
- dispatcher.transfer_data(request)
- self.fail()
- except Exception, e:
- self.failUnless(str(e).find('Intentional') != -1,
- 'Unexpected exception: %s' % e)
-
- def test_abort_transfer_data(self):
- dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- request = mock.MockRequest()
- request.ws_resource = '/abort_by_user'
- self.assertRaises(handshake.AbortedByUserException,
- dispatcher.transfer_data, request)
-
- def test_scan_dir(self):
- disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- self.assertEqual(4, len(disp._handler_suite_map))
- self.failUnless('/origin_check' in disp._handler_suite_map)
- self.failUnless(
- '/sub/exception_in_transfer' in disp._handler_suite_map)
- self.failUnless('/sub/plain' in disp._handler_suite_map)
-
- def test_scan_sub_dir(self):
- disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, _TEST_HANDLERS_SUB_DIR)
- self.assertEqual(2, len(disp._handler_suite_map))
- self.failIf('/origin_check' in disp._handler_suite_map)
- self.failUnless(
- '/sub/exception_in_transfer' in disp._handler_suite_map)
- self.failUnless('/sub/plain' in disp._handler_suite_map)
-
- def test_scan_sub_dir_as_root(self):
- disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR,
- _TEST_HANDLERS_SUB_DIR)
- self.assertEqual(2, len(disp._handler_suite_map))
- self.failIf('/origin_check' in disp._handler_suite_map)
- self.failIf('/sub/exception_in_transfer' in disp._handler_suite_map)
- self.failIf('/sub/plain' in disp._handler_suite_map)
- self.failUnless('/exception_in_transfer' in disp._handler_suite_map)
- self.failUnless('/plain' in disp._handler_suite_map)
-
- def test_scan_dir_must_under_root(self):
- dispatch.Dispatcher('a/b', 'a/b/c') # OK
- dispatch.Dispatcher('a/b///', 'a/b') # OK
- self.assertRaises(dispatch.DispatchException,
- dispatch.Dispatcher, 'a/b/c', 'a/b')
-
- def test_resource_path_alias(self):
- disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
- disp.add_resource_path_alias('/', '/origin_check')
- self.assertEqual(5, len(disp._handler_suite_map))
- self.failUnless('/origin_check' in disp._handler_suite_map)
- self.failUnless(
- '/sub/exception_in_transfer' in disp._handler_suite_map)
- self.failUnless('/sub/plain' in disp._handler_suite_map)
- self.failUnless('/' in disp._handler_suite_map)
- self.assertRaises(dispatch.DispatchException,
- disp.add_resource_path_alias, '/alias', '/not-exist')
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_endtoend.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_endtoend.py
deleted file mode 100755
index 5e5cf6157..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_endtoend.py
+++ /dev/null
@@ -1,753 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""End-to-end tests for pywebsocket. Tests standalone.py by default. You
-can also test mod_pywebsocket hosted on an Apache server by setting
-_use_external_server to True and modifying _external_server_port to point to
-the port on which the Apache server is running.
-"""
-
-
-import logging
-import os
-import signal
-import socket
-import subprocess
-import sys
-import time
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from test import client_for_testing
-from test import mux_client_for_testing
-
-
-# Special message that tells the echo server to start closing handshake
-_GOODBYE_MESSAGE = 'Goodbye'
-
-_SERVER_WARMUP_IN_SEC = 0.2
-
-# If you want to use external server to run end to end tests, set following
-# parameters correctly.
-_use_external_server = False
-_external_server_port = 0
-
-
-# Test body functions
-def _echo_check_procedure(client):
- client.connect()
-
- client.send_message('test')
- client.assert_receive('test')
- client.send_message('helloworld')
- client.assert_receive('helloworld')
-
- client.send_close()
- client.assert_receive_close()
-
- client.assert_connection_closed()
-
-
-def _echo_check_procedure_with_binary(client):
- client.connect()
-
- client.send_message('binary', binary=True)
- client.assert_receive('binary', binary=True)
- client.send_message('\x00\x80\xfe\xff\x00\x80', binary=True)
- client.assert_receive('\x00\x80\xfe\xff\x00\x80', binary=True)
-
- client.send_close()
- client.assert_receive_close()
-
- client.assert_connection_closed()
-
-
-def _echo_check_procedure_with_goodbye(client):
- client.connect()
-
- client.send_message('test')
- client.assert_receive('test')
-
- client.send_message(_GOODBYE_MESSAGE)
- client.assert_receive(_GOODBYE_MESSAGE)
-
- client.assert_receive_close()
- client.send_close()
-
- client.assert_connection_closed()
-
-
-def _echo_check_procedure_with_code_and_reason(client, code, reason):
- client.connect()
-
- client.send_close(code, reason)
- client.assert_receive_close(code, reason)
-
- client.assert_connection_closed()
-
-
-def _unmasked_frame_check_procedure(client):
- client.connect()
-
- client.send_message('test', mask=False)
- client.assert_receive_close(client_for_testing.STATUS_PROTOCOL_ERROR, '')
-
- client.assert_connection_closed()
-
-
-def _mux_echo_check_procedure(mux_client):
- mux_client.connect()
- mux_client.send_flow_control(1, 1024)
-
- logical_channel_options = client_for_testing.ClientOptions()
- logical_channel_options.server_host = 'localhost'
- logical_channel_options.server_port = 80
- logical_channel_options.origin = 'http://localhost'
- logical_channel_options.resource = '/echo'
- mux_client.add_channel(2, logical_channel_options)
- mux_client.send_flow_control(2, 1024)
-
- mux_client.send_message(2, 'test')
- mux_client.assert_receive(2, 'test')
-
- mux_client.add_channel(3, logical_channel_options)
- mux_client.send_flow_control(3, 1024)
-
- mux_client.send_message(2, 'hello')
- mux_client.send_message(3, 'world')
- mux_client.assert_receive(2, 'hello')
- mux_client.assert_receive(3, 'world')
-
- # Don't send close message on channel id 1 so that server-initiated
- # closing handshake won't occur.
- mux_client.send_close(2)
- mux_client.send_close(3)
- mux_client.assert_receive_close(2)
- mux_client.assert_receive_close(3)
-
- mux_client.send_physical_connection_close()
- mux_client.assert_physical_connection_receive_close()
-
-
-class EndToEndTestBase(unittest.TestCase):
- """Base class for end-to-end tests that launch pywebsocket standalone
- server as a separate process, connect to it using the client_for_testing
- module, and check if the server behaves correctly by exchanging opening
- handshake and frames over a TCP connection.
- """
-
- def setUp(self):
- self.server_stderr = None
- self.top_dir = os.path.join(os.path.split(__file__)[0], '..')
- os.putenv('PYTHONPATH', os.path.pathsep.join(sys.path))
- self.standalone_command = os.path.join(
- self.top_dir, 'mod_pywebsocket', 'standalone.py')
- self.document_root = os.path.join(self.top_dir, 'example')
- s = socket.socket()
- s.bind(('localhost', 0))
- (_, self.test_port) = s.getsockname()
- s.close()
-
- self._options = client_for_testing.ClientOptions()
- self._options.server_host = 'localhost'
- self._options.origin = 'http://localhost'
- self._options.resource = '/echo'
-
- # TODO(toyoshim): Eliminate launching a standalone server on using
- # external server.
-
- if _use_external_server:
- self._options.server_port = _external_server_port
- else:
- self._options.server_port = self.test_port
-
- # TODO(tyoshino): Use tearDown to kill the server.
-
- def _run_python_command(self, commandline, stdout=None, stderr=None):
- return subprocess.Popen([sys.executable] + commandline, close_fds=True,
- stdout=stdout, stderr=stderr)
-
- def _run_server(self):
- args = [self.standalone_command,
- '-H', 'localhost',
- '-V', 'localhost',
- '-p', str(self.test_port),
- '-P', str(self.test_port),
- '-d', self.document_root]
-
- # Inherit the level set to the root logger by test runner.
- root_logger = logging.getLogger()
- log_level = root_logger.getEffectiveLevel()
- if log_level != logging.NOTSET:
- args.append('--log-level')
- args.append(logging.getLevelName(log_level).lower())
-
- return self._run_python_command(args,
- stderr=self.server_stderr)
-
- def _kill_process(self, pid):
- if sys.platform in ('win32', 'cygwin'):
- subprocess.call(
- ('taskkill.exe', '/f', '/pid', str(pid)), close_fds=True)
- else:
- os.kill(pid, signal.SIGKILL)
-
-
-class EndToEndHyBiTest(EndToEndTestBase):
- def setUp(self):
- EndToEndTestBase.setUp(self)
-
- def _run_test_with_client_options(self, test_function, options):
- server = self._run_server()
- try:
- # TODO(tyoshino): add some logic to poll the server until it
- # becomes ready
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- client = client_for_testing.create_client(options)
- try:
- test_function(client)
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def _run_test(self, test_function):
- self._run_test_with_client_options(test_function, self._options)
-
- def _run_deflate_frame_test(self, test_function):
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- self._options.enable_deflate_frame()
- client = client_for_testing.create_client(self._options)
- try:
- test_function(client)
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def _run_permessage_deflate_test(
- self, offer, response_checker, test_function):
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- self._options.extensions += offer
- self._options.check_permessage_deflate = response_checker
- client = client_for_testing.create_client(self._options)
-
- try:
- client.connect()
-
- if test_function is not None:
- test_function(client)
-
- client.assert_connection_closed()
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def _run_close_with_code_and_reason_test(self, test_function, code,
- reason):
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- client = client_for_testing.create_client(self._options)
- try:
- test_function(client, code, reason)
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def _run_http_fallback_test(self, options, status):
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- client = client_for_testing.create_client(options)
- try:
- client.connect()
- self.fail('Could not catch HttpStatusException')
- except client_for_testing.HttpStatusException, e:
- self.assertEqual(status, e.status)
- except Exception, e:
- self.fail('Catch unexpected exception')
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def _run_mux_test(self, test_function):
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- client = mux_client_for_testing.MuxClient(self._options)
- try:
- test_function(client)
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def test_echo(self):
- self._run_test(_echo_check_procedure)
-
- def test_echo_binary(self):
- self._run_test(_echo_check_procedure_with_binary)
-
- def test_echo_server_close(self):
- self._run_test(_echo_check_procedure_with_goodbye)
-
- def test_unmasked_frame(self):
- self._run_test(_unmasked_frame_check_procedure)
-
- def test_echo_deflate_frame(self):
- self._run_deflate_frame_test(_echo_check_procedure)
-
- def test_echo_deflate_frame_server_close(self):
- self._run_deflate_frame_test(
- _echo_check_procedure_with_goodbye)
-
- def test_echo_permessage_deflate(self):
- def test_function(client):
- # From the examples in the spec.
- compressed_hello = '\xf2\x48\xcd\xc9\xc9\x07\x00'
- client._stream.send_data(
- compressed_hello,
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- compressed_hello,
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
-
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- self.assertEquals('permessage-deflate', parameter.name())
- self.assertEquals([], parameter.get_parameters())
-
- self._run_permessage_deflate_test(
- ['permessage-deflate'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_two_frames(self):
- def test_function(client):
- # From the examples in the spec.
- client._stream.send_data(
- '\xf2\x48\xcd',
- client_for_testing.OPCODE_TEXT,
- end=False,
- rsv1=1)
- client._stream.send_data(
- '\xc9\xc9\x07\x00',
- client_for_testing.OPCODE_TEXT)
- client._stream.assert_receive_binary(
- '\xf2\x48\xcd\xc9\xc9\x07\x00',
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
-
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- self.assertEquals('permessage-deflate', parameter.name())
- self.assertEquals([], parameter.get_parameters())
-
- self._run_permessage_deflate_test(
- ['permessage-deflate'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_two_messages(self):
- def test_function(client):
- # From the examples in the spec.
- client._stream.send_data(
- '\xf2\x48\xcd\xc9\xc9\x07\x00',
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.send_data(
- '\xf2\x00\x11\x00\x00',
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- '\xf2\x48\xcd\xc9\xc9\x07\x00',
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- '\xf2\x00\x11\x00\x00',
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
-
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- self.assertEquals('permessage-deflate', parameter.name())
- self.assertEquals([], parameter.get_parameters())
-
- self._run_permessage_deflate_test(
- ['permessage-deflate'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_two_msgs_server_no_context_takeover(self):
- def test_function(client):
- # From the examples in the spec.
- client._stream.send_data(
- '\xf2\x48\xcd\xc9\xc9\x07\x00',
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.send_data(
- '\xf2\x00\x11\x00\x00',
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- '\xf2\x48\xcd\xc9\xc9\x07\x00',
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- '\xf2\x48\xcd\xc9\xc9\x07\x00',
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
-
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- self.assertEquals('permessage-deflate', parameter.name())
- self.assertEquals([('server_no_context_takeover', None)],
- parameter.get_parameters())
-
- self._run_permessage_deflate_test(
- ['permessage-deflate; server_no_context_takeover'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_preference(self):
- def test_function(client):
- # From the examples in the spec.
- compressed_hello = '\xf2\x48\xcd\xc9\xc9\x07\x00'
- client._stream.send_data(
- compressed_hello,
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- compressed_hello,
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
-
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- self.assertEquals('permessage-deflate', parameter.name())
- self.assertEquals([], parameter.get_parameters())
-
- self._run_permessage_deflate_test(
- ['permessage-deflate', 'deflate-frame'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_with_parameters(self):
- def test_function(client):
- # From the examples in the spec.
- compressed_hello = '\xf2\x48\xcd\xc9\xc9\x07\x00'
- client._stream.send_data(
- compressed_hello,
- client_for_testing.OPCODE_TEXT,
- rsv1=1)
- client._stream.assert_receive_binary(
- compressed_hello,
- opcode=client_for_testing.OPCODE_TEXT,
- rsv1=1)
-
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- self.assertEquals('permessage-deflate', parameter.name())
- self.assertEquals([('server_max_window_bits', '10'),
- ('server_no_context_takeover', None)],
- parameter.get_parameters())
-
- self._run_permessage_deflate_test(
- ['permessage-deflate; server_max_window_bits=10; '
- 'server_no_context_takeover'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_with_bad_server_max_window_bits(self):
- def test_function(client):
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- raise Exception('Unexpected acceptance of permessage-deflate')
-
- self._run_permessage_deflate_test(
- ['permessage-deflate; server_max_window_bits=3000000'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_with_bad_server_max_window_bits(self):
- def test_function(client):
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- raise Exception('Unexpected acceptance of permessage-deflate')
-
- self._run_permessage_deflate_test(
- ['permessage-deflate; server_max_window_bits=3000000'],
- response_checker,
- test_function)
-
- def test_echo_permessage_deflate_with_undefined_parameter(self):
- def test_function(client):
- client.send_close()
- client.assert_receive_close()
-
- def response_checker(parameter):
- raise Exception('Unexpected acceptance of permessage-deflate')
-
- self._run_permessage_deflate_test(
- ['permessage-deflate; foo=bar'],
- response_checker,
- test_function)
-
- def test_echo_close_with_code_and_reason(self):
- self._options.resource = '/close'
- self._run_close_with_code_and_reason_test(
- _echo_check_procedure_with_code_and_reason, 3333, 'sunsunsunsun')
-
- def test_echo_close_with_empty_body(self):
- self._options.resource = '/close'
- self._run_close_with_code_and_reason_test(
- _echo_check_procedure_with_code_and_reason, None, '')
-
- def test_mux_echo(self):
- self._run_mux_test(_mux_echo_check_procedure)
-
- def test_close_on_protocol_error(self):
- """Tests that the server sends a close frame with protocol error status
- code when the client sends data with some protocol error.
- """
-
- def test_function(client):
- client.connect()
-
- # Intermediate frame without any preceding start of fragmentation
- # frame.
- client.send_frame_of_arbitrary_bytes('\x80\x80', '')
- client.assert_receive_close(
- client_for_testing.STATUS_PROTOCOL_ERROR)
-
- self._run_test(test_function)
-
- def test_close_on_unsupported_frame(self):
- """Tests that the server sends a close frame with unsupported operation
- status code when the client sends data asking some operation that is
- not supported by the server.
- """
-
- def test_function(client):
- client.connect()
-
- # Text frame with RSV3 bit raised.
- client.send_frame_of_arbitrary_bytes('\x91\x80', '')
- client.assert_receive_close(
- client_for_testing.STATUS_UNSUPPORTED_DATA)
-
- self._run_test(test_function)
-
- def test_close_on_invalid_frame(self):
- """Tests that the server sends a close frame with invalid frame payload
- data status code when the client sends an invalid frame like containing
- invalid UTF-8 character.
- """
-
- def test_function(client):
- client.connect()
-
- # Text frame with invalid UTF-8 string.
- client.send_message('\x80', raw=True)
- client.assert_receive_close(
- client_for_testing.STATUS_INVALID_FRAME_PAYLOAD_DATA)
-
- self._run_test(test_function)
-
- def test_close_on_internal_endpoint_error(self):
- """Tests that the server sends a close frame with internal endpoint
- error status code when the handler does bad operation.
- """
-
- self._options.resource = '/internal_error'
-
- def test_function(client):
- client.connect()
- client.assert_receive_close(
- client_for_testing.STATUS_INTERNAL_ENDPOINT_ERROR)
-
- self._run_test(test_function)
-
- # TODO(toyoshim): Add tests to verify invalid absolute uri handling like
- # host unmatch, port unmatch and invalid port description (':' without port
- # number).
-
- def test_absolute_uri(self):
- """Tests absolute uri request."""
-
- options = self._options
- options.resource = 'ws://localhost:%d/echo' % options.server_port
- self._run_test_with_client_options(_echo_check_procedure, options)
-
- def test_origin_check(self):
- """Tests http fallback on origin check fail."""
-
- options = self._options
- options.resource = '/origin_check'
- # Server shows warning message for http 403 fallback. This warning
- # message is confusing. Following pipe disposes warning messages.
- self.server_stderr = subprocess.PIPE
- self._run_http_fallback_test(options, 403)
-
- def test_version_check(self):
- """Tests http fallback on version check fail."""
-
- options = self._options
- options.version = 99
- self._run_http_fallback_test(options, 400)
-
-
-class EndToEndHyBi00Test(EndToEndTestBase):
- def setUp(self):
- EndToEndTestBase.setUp(self)
-
- def _run_test(self, test_function):
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- client = client_for_testing.create_client_hybi00(self._options)
- try:
- test_function(client)
- finally:
- client.close_socket()
- finally:
- self._kill_process(server.pid)
-
- def test_echo(self):
- self._run_test(_echo_check_procedure)
-
- def test_echo_server_close(self):
- self._run_test(_echo_check_procedure_with_goodbye)
-
-
-class EndToEndTestWithEchoClient(EndToEndTestBase):
- def setUp(self):
- EndToEndTestBase.setUp(self)
-
- def _check_example_echo_client_result(
- self, expected, stdoutdata, stderrdata):
- actual = stdoutdata.decode("utf-8")
- if actual != expected:
- raise Exception('Unexpected result on example echo client: '
- '%r (expected) vs %r (actual)' %
- (expected, actual))
- if stderrdata is not None:
- raise Exception('Unexpected error message on example echo '
- 'client: %r' % stderrdata)
-
- def test_example_echo_client(self):
- """Tests that the echo_client.py example can talk with the server."""
-
- server = self._run_server()
- try:
- time.sleep(_SERVER_WARMUP_IN_SEC)
-
- client_command = os.path.join(
- self.top_dir, 'example', 'echo_client.py')
-
- # Expected output for the default messages.
- default_expectation = ('Send: Hello\n' 'Recv: Hello\n'
- u'Send: \u65e5\u672c\n' u'Recv: \u65e5\u672c\n'
- 'Send close\n' 'Recv ack\n')
-
- args = [client_command,
- '-p', str(self._options.server_port)]
- client = self._run_python_command(args, stdout=subprocess.PIPE)
- stdoutdata, stderrdata = client.communicate()
- self._check_example_echo_client_result(
- default_expectation, stdoutdata, stderrdata)
-
- # Process a big message for which extended payload length is used.
- # To handle extended payload length, ws_version attribute will be
- # accessed. This test checks that ws_version is correctly set.
- big_message = 'a' * 1024
- args = [client_command,
- '-p', str(self._options.server_port),
- '-m', big_message]
- client = self._run_python_command(args, stdout=subprocess.PIPE)
- stdoutdata, stderrdata = client.communicate()
- expected = ('Send: %s\nRecv: %s\nSend close\nRecv ack\n' %
- (big_message, big_message))
- self._check_example_echo_client_result(
- expected, stdoutdata, stderrdata)
-
- # Test the permessage-deflate extension.
- args = [client_command,
- '-p', str(self._options.server_port),
- '--use_permessage_deflate']
- client = self._run_python_command(args, stdout=subprocess.PIPE)
- stdoutdata, stderrdata = client.communicate()
- self._check_example_echo_client_result(
- default_expectation, stdoutdata, stderrdata)
- finally:
- self._kill_process(server.pid)
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_extensions.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_extensions.py
deleted file mode 100755
index 6c8b1262d..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_extensions.py
+++ /dev/null
@@ -1,360 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""Tests for extensions module."""
-
-
-import unittest
-import zlib
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import common
-from mod_pywebsocket import extensions
-
-
-class ExtensionsTest(unittest.TestCase):
- """A unittest for non-class methods in extensions.py"""
-
- def test_parse_window_bits(self):
- self.assertRaises(ValueError, extensions._parse_window_bits, None)
- self.assertRaises(ValueError, extensions._parse_window_bits, 'foobar')
- self.assertRaises(ValueError, extensions._parse_window_bits, ' 8 ')
- self.assertRaises(ValueError, extensions._parse_window_bits, 'a8a')
- self.assertRaises(ValueError, extensions._parse_window_bits, '00000')
- self.assertRaises(ValueError, extensions._parse_window_bits, '00008')
- self.assertRaises(ValueError, extensions._parse_window_bits, '0x8')
-
- self.assertRaises(ValueError, extensions._parse_window_bits, '9.5')
- self.assertRaises(ValueError, extensions._parse_window_bits, '8.0')
-
- self.assertTrue(extensions._parse_window_bits, '8')
- self.assertTrue(extensions._parse_window_bits, '15')
-
- self.assertRaises(ValueError, extensions._parse_window_bits, '-8')
- self.assertRaises(ValueError, extensions._parse_window_bits, '0')
- self.assertRaises(ValueError, extensions._parse_window_bits, '7')
-
- self.assertRaises(ValueError, extensions._parse_window_bits, '16')
- self.assertRaises(
- ValueError, extensions._parse_window_bits, '10000000')
-
-
-class CompressionMethodParameterParserTest(unittest.TestCase):
- """A unittest for _parse_compression_method which parses the compression
- method description used by perframe-compression and permessage-compression
- extension in their "method" extension parameter.
- """
-
- def test_parse_method_simple(self):
- method_list = extensions._parse_compression_method('foo')
- self.assertEqual(1, len(method_list))
- method = method_list[0]
- self.assertEqual('foo', method.name())
- self.assertEqual(0, len(method.get_parameters()))
-
- def test_parse_method_with_parameter(self):
- method_list = extensions._parse_compression_method('foo; x; y=10')
- self.assertEqual(1, len(method_list))
- method = method_list[0]
- self.assertEqual('foo', method.name())
- self.assertEqual(2, len(method.get_parameters()))
- self.assertTrue(method.has_parameter('x'))
- self.assertEqual(None, method.get_parameter_value('x'))
- self.assertTrue(method.has_parameter('y'))
- self.assertEqual('10', method.get_parameter_value('y'))
-
- def test_parse_method_with_quoted_parameter(self):
- method_list = extensions._parse_compression_method(
- 'foo; x="Hello World"; y=10')
- self.assertEqual(1, len(method_list))
- method = method_list[0]
- self.assertEqual('foo', method.name())
- self.assertEqual(2, len(method.get_parameters()))
- self.assertTrue(method.has_parameter('x'))
- self.assertEqual('Hello World', method.get_parameter_value('x'))
- self.assertTrue(method.has_parameter('y'))
- self.assertEqual('10', method.get_parameter_value('y'))
-
- def test_parse_method_multiple(self):
- method_list = extensions._parse_compression_method('foo, bar')
- self.assertEqual(2, len(method_list))
- self.assertEqual('foo', method_list[0].name())
- self.assertEqual(0, len(method_list[0].get_parameters()))
- self.assertEqual('bar', method_list[1].name())
- self.assertEqual(0, len(method_list[1].get_parameters()))
-
- def test_parse_method_multiple_methods_with_quoted_parameter(self):
- method_list = extensions._parse_compression_method(
- 'foo; x="Hello World", bar; y=10')
- self.assertEqual(2, len(method_list))
- self.assertEqual('foo', method_list[0].name())
- self.assertEqual(1, len(method_list[0].get_parameters()))
- self.assertTrue(method_list[0].has_parameter('x'))
- self.assertEqual('Hello World',
- method_list[0].get_parameter_value('x'))
- self.assertEqual('bar', method_list[1].name())
- self.assertEqual(1, len(method_list[1].get_parameters()))
- self.assertTrue(method_list[1].has_parameter('y'))
- self.assertEqual('10', method_list[1].get_parameter_value('y'))
-
- def test_create_method_desc_simple(self):
- params = common.ExtensionParameter('foo')
- desc = extensions._create_accepted_method_desc('foo',
- params.get_parameters())
- self.assertEqual('foo', desc)
-
- def test_create_method_desc_with_parameters(self):
- params = common.ExtensionParameter('foo')
- params.add_parameter('x', 'Hello, World')
- params.add_parameter('y', '10')
- desc = extensions._create_accepted_method_desc('foo',
- params.get_parameters())
- self.assertEqual('foo; x="Hello, World"; y=10', desc)
-
-
-class DeflateFrameExtensionProcessorParsingTest(unittest.TestCase):
- """A unittest for checking that DeflateFrameExtensionProcessor parses given
- extension parameter correctly.
- """
-
- def test_registry(self):
- processor = extensions.get_extension_processor(
- common.ExtensionParameter('deflate-frame'))
- self.assertIsInstance(processor,
- extensions.DeflateFrameExtensionProcessor)
-
- processor = extensions.get_extension_processor(
- common.ExtensionParameter('x-webkit-deflate-frame'))
- self.assertIsInstance(processor,
- extensions.DeflateFrameExtensionProcessor)
-
- def test_minimal_offer(self):
- processor = extensions.DeflateFrameExtensionProcessor(
- common.ExtensionParameter('perframe-deflate'))
-
- response = processor.get_extension_response()
- self.assertEqual('perframe-deflate', response.name())
- self.assertEqual(0, len(response.get_parameters()))
-
- self.assertEqual(zlib.MAX_WBITS,
- processor._rfc1979_deflater._window_bits)
- self.assertFalse(processor._rfc1979_deflater._no_context_takeover)
-
- def test_offer_with_max_window_bits(self):
- parameter = common.ExtensionParameter('perframe-deflate')
- parameter.add_parameter('max_window_bits', '10')
- processor = extensions.DeflateFrameExtensionProcessor(parameter)
-
- response = processor.get_extension_response()
- self.assertEqual('perframe-deflate', response.name())
- self.assertEqual(0, len(response.get_parameters()))
-
- self.assertEqual(10, processor._rfc1979_deflater._window_bits)
-
- def test_offer_with_out_of_range_max_window_bits(self):
- parameter = common.ExtensionParameter('perframe-deflate')
- parameter.add_parameter('max_window_bits', '0')
- processor = extensions.DeflateFrameExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
- def test_offer_with_max_window_bits_without_value(self):
- parameter = common.ExtensionParameter('perframe-deflate')
- parameter.add_parameter('max_window_bits', None)
- processor = extensions.DeflateFrameExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
- def test_offer_with_no_context_takeover(self):
- parameter = common.ExtensionParameter('perframe-deflate')
- parameter.add_parameter('no_context_takeover', None)
- processor = extensions.DeflateFrameExtensionProcessor(parameter)
-
- response = processor.get_extension_response()
- self.assertEqual('perframe-deflate', response.name())
- self.assertEqual(0, len(response.get_parameters()))
-
- self.assertTrue(processor._rfc1979_deflater._no_context_takeover)
-
- def test_offer_with_no_context_takeover_with_value(self):
- parameter = common.ExtensionParameter('perframe-deflate')
- parameter.add_parameter('no_context_takeover', 'foobar')
- processor = extensions.DeflateFrameExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
- def test_offer_with_unknown_parameter(self):
- parameter = common.ExtensionParameter('perframe-deflate')
- parameter.add_parameter('foo', 'bar')
- processor = extensions.DeflateFrameExtensionProcessor(parameter)
-
- response = processor.get_extension_response()
- self.assertEqual('perframe-deflate', response.name())
- self.assertEqual(0, len(response.get_parameters()))
-
-
-class PerMessageDeflateExtensionProcessorParsingTest(unittest.TestCase):
- """A unittest for checking that PerMessageDeflateExtensionProcessor parses
- given extension parameter correctly.
- """
-
- def test_registry(self):
- processor = extensions.get_extension_processor(
- common.ExtensionParameter('permessage-deflate'))
- self.assertIsInstance(processor,
- extensions.PerMessageDeflateExtensionProcessor)
-
- def test_minimal_offer(self):
- processor = extensions.PerMessageDeflateExtensionProcessor(
- common.ExtensionParameter('permessage-deflate'))
-
- response = processor.get_extension_response()
- self.assertEqual('permessage-deflate', response.name())
- self.assertEqual(0, len(response.get_parameters()))
-
- self.assertEqual(zlib.MAX_WBITS,
- processor._rfc1979_deflater._window_bits)
- self.assertFalse(processor._rfc1979_deflater._no_context_takeover)
-
- def test_offer_with_max_window_bits(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('server_max_window_bits', '10')
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
-
- response = processor.get_extension_response()
- self.assertEqual('permessage-deflate', response.name())
- self.assertEqual([('server_max_window_bits', '10')],
- response.get_parameters())
-
- self.assertEqual(10, processor._rfc1979_deflater._window_bits)
-
- def test_offer_with_out_of_range_max_window_bits(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('server_max_window_bits', '0')
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
- def test_offer_with_max_window_bits_without_value(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('server_max_window_bits', None)
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
- def test_offer_with_no_context_takeover(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('server_no_context_takeover', None)
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
-
- response = processor.get_extension_response()
- self.assertEqual('permessage-deflate', response.name())
- self.assertEqual([('server_no_context_takeover', None)],
- response.get_parameters())
-
- self.assertTrue(processor._rfc1979_deflater._no_context_takeover)
-
- def test_offer_with_no_context_takeover_with_value(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('server_no_context_takeover', 'foobar')
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
- def test_offer_with_unknown_parameter(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('foo', 'bar')
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
-
- self.assertIsNone(processor.get_extension_response())
-
-
-class PerMessageDeflateExtensionProcessorBuildingTest(unittest.TestCase):
- """A unittest for checking that PerMessageDeflateExtensionProcessor builds
- a response based on specified options correctly.
- """
-
- def test_response_with_max_window_bits(self):
- parameter = common.ExtensionParameter('permessage-deflate')
- parameter.add_parameter('client_max_window_bits', None)
- processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
- processor.set_client_max_window_bits(10)
-
- response = processor.get_extension_response()
- self.assertEqual('permessage-deflate', response.name())
- self.assertEqual([('client_max_window_bits', '10')],
- response.get_parameters())
-
- def test_response_with_max_window_bits_without_client_permission(self):
- processor = extensions.PerMessageDeflateExtensionProcessor(
- common.ExtensionParameter('permessage-deflate'))
- processor.set_client_max_window_bits(10)
-
- response = processor.get_extension_response()
- self.assertIsNone(response)
-
- def test_response_with_true_for_no_context_takeover(self):
- processor = extensions.PerMessageDeflateExtensionProcessor(
- common.ExtensionParameter('permessage-deflate'))
-
- processor.set_client_no_context_takeover(True)
-
- response = processor.get_extension_response()
- self.assertEqual('permessage-deflate', response.name())
- self.assertEqual([('client_no_context_takeover', None)],
- response.get_parameters())
-
- def test_response_with_false_for_no_context_takeover(self):
- processor = extensions.PerMessageDeflateExtensionProcessor(
- common.ExtensionParameter('permessage-deflate'))
-
- processor.set_client_no_context_takeover(False)
-
- response = processor.get_extension_response()
- self.assertEqual('permessage-deflate', response.name())
- self.assertEqual(0, len(response.get_parameters()))
-
-
-class PerMessageCompressExtensionProcessorTest(unittest.TestCase):
- def test_registry(self):
- processor = extensions.get_extension_processor(
- common.ExtensionParameter('permessage-compress'))
- self.assertIsInstance(processor,
- extensions.PerMessageCompressExtensionProcessor)
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake.py
deleted file mode 100755
index aa78ac05e..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake.py
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""Tests for handshake._base module."""
-
-
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket.common import ExtensionParameter
-from mod_pywebsocket.common import ExtensionParsingException
-from mod_pywebsocket.common import format_extensions
-from mod_pywebsocket.common import parse_extensions
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import validate_subprotocol
-
-
-class ValidateSubprotocolTest(unittest.TestCase):
- """A unittest for validate_subprotocol method."""
-
- def test_validate_subprotocol(self):
- # Should succeed.
- validate_subprotocol('sample')
- validate_subprotocol('Sample')
- validate_subprotocol('sample\x7eprotocol')
-
- # Should fail.
- self.assertRaises(HandshakeException,
- validate_subprotocol,
- '')
- self.assertRaises(HandshakeException,
- validate_subprotocol,
- 'sample\x09protocol')
- self.assertRaises(HandshakeException,
- validate_subprotocol,
- 'sample\x19protocol')
- self.assertRaises(HandshakeException,
- validate_subprotocol,
- 'sample\x20protocol')
- self.assertRaises(HandshakeException,
- validate_subprotocol,
- 'sample\x7fprotocol')
- self.assertRaises(HandshakeException,
- validate_subprotocol,
- # "Japan" in Japanese
- u'\u65e5\u672c')
-
-
-_TEST_TOKEN_EXTENSION_DATA = [
- ('foo', [('foo', [])]),
- ('foo; bar', [('foo', [('bar', None)])]),
- ('foo; bar=baz', [('foo', [('bar', 'baz')])]),
- ('foo; bar=baz; car=cdr', [('foo', [('bar', 'baz'), ('car', 'cdr')])]),
- ('foo; bar=baz, car; cdr',
- [('foo', [('bar', 'baz')]), ('car', [('cdr', None)])]),
- ('a, b, c, d',
- [('a', []), ('b', []), ('c', []), ('d', [])]),
- ]
-
-
-_TEST_QUOTED_EXTENSION_DATA = [
- ('foo; bar=""', [('foo', [('bar', '')])]),
- ('foo; bar=" baz "', [('foo', [('bar', ' baz ')])]),
- ('foo; bar=",baz;"', [('foo', [('bar', ',baz;')])]),
- ('foo; bar="\\\r\\\nbaz"', [('foo', [('bar', '\r\nbaz')])]),
- ('foo; bar="\\"baz"', [('foo', [('bar', '"baz')])]),
- ('foo; bar="\xbbbaz"', [('foo', [('bar', '\xbbbaz')])]),
- ]
-
-
-_TEST_REDUNDANT_TOKEN_EXTENSION_DATA = [
- ('foo \t ', [('foo', [])]),
- ('foo; \r\n bar', [('foo', [('bar', None)])]),
- ('foo; bar=\r\n \r\n baz', [('foo', [('bar', 'baz')])]),
- ('foo ;bar = baz ', [('foo', [('bar', 'baz')])]),
- ('foo,bar,,baz', [('foo', []), ('bar', []), ('baz', [])]),
- ]
-
-
-_TEST_REDUNDANT_QUOTED_EXTENSION_DATA = [
- ('foo; bar="\r\n \r\n baz"', [('foo', [('bar', ' baz')])]),
- ]
-
-
-class ExtensionsParserTest(unittest.TestCase):
-
- def _verify_extension_list(self, expected_list, actual_list):
- """Verifies that ExtensionParameter objects in actual_list have the
- same members as extension definitions in expected_list. Extension
- definition used in this test is a pair of an extension name and a
- parameter dictionary.
- """
-
- self.assertEqual(len(expected_list), len(actual_list))
- for expected, actual in zip(expected_list, actual_list):
- (name, parameters) = expected
- self.assertEqual(name, actual._name)
- self.assertEqual(parameters, actual._parameters)
-
- def test_parse(self):
- for formatted_string, definition in _TEST_TOKEN_EXTENSION_DATA:
- self._verify_extension_list(
- definition, parse_extensions(formatted_string))
-
- def test_parse_quoted_data(self):
- for formatted_string, definition in _TEST_QUOTED_EXTENSION_DATA:
- self._verify_extension_list(
- definition, parse_extensions(formatted_string))
-
- def test_parse_redundant_data(self):
- for (formatted_string,
- definition) in _TEST_REDUNDANT_TOKEN_EXTENSION_DATA:
- self._verify_extension_list(
- definition, parse_extensions(formatted_string))
-
- def test_parse_redundant_quoted_data(self):
- for (formatted_string,
- definition) in _TEST_REDUNDANT_QUOTED_EXTENSION_DATA:
- self._verify_extension_list(
- definition, parse_extensions(formatted_string))
-
- def test_parse_bad_data(self):
- _TEST_BAD_EXTENSION_DATA = [
- ('foo; ; '),
- ('foo; a a'),
- ('foo foo'),
- (',,,'),
- ('foo; bar='),
- ('foo; bar="hoge'),
- ('foo; bar="a\r"'),
- ('foo; bar="\\\xff"'),
- ('foo; bar=\ra'),
- ]
-
- for formatted_string in _TEST_BAD_EXTENSION_DATA:
- self.assertRaises(
- ExtensionParsingException, parse_extensions, formatted_string)
-
-
-class FormatExtensionsTest(unittest.TestCase):
-
- def test_format_extensions(self):
- for formatted_string, definitions in _TEST_TOKEN_EXTENSION_DATA:
- extensions = []
- for definition in definitions:
- (name, parameters) = definition
- extension = ExtensionParameter(name)
- extension._parameters = parameters
- extensions.append(extension)
- self.assertEqual(
- formatted_string, format_extensions(extensions))
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi.py
deleted file mode 100755
index 6c8713823..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi.py
+++ /dev/null
@@ -1,534 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for handshake module."""
-
-
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-from mod_pywebsocket import common
-from mod_pywebsocket.handshake._base import AbortedByUserException
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import VersionException
-from mod_pywebsocket.handshake.hybi import Handshaker
-
-import mock
-
-
-class RequestDefinition(object):
- """A class for holding data for constructing opening handshake strings for
- testing the opening handshake processor.
- """
-
- def __init__(self, method, uri, headers):
- self.method = method
- self.uri = uri
- self.headers = headers
-
-
-def _create_good_request_def():
- return RequestDefinition(
- 'GET', '/demo',
- {'Host': 'server.example.com',
- 'Upgrade': 'websocket',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
- 'Sec-WebSocket-Version': '13',
- 'Origin': 'http://example.com'})
-
-
-def _create_request(request_def):
- conn = mock.MockConn('')
- return mock.MockRequest(
- method=request_def.method,
- uri=request_def.uri,
- headers_in=request_def.headers,
- connection=conn)
-
-
-def _create_handshaker(request):
- handshaker = Handshaker(request, mock.MockDispatcher())
- return handshaker
-
-
-class SubprotocolChoosingDispatcher(object):
- """A dispatcher for testing. This dispatcher sets the i-th subprotocol
- of requested ones to ws_protocol where i is given on construction as index
- argument. If index is negative, default_value will be set to ws_protocol.
- """
-
- def __init__(self, index, default_value=None):
- self.index = index
- self.default_value = default_value
-
- def do_extra_handshake(self, conn_context):
- if self.index >= 0:
- conn_context.ws_protocol = conn_context.ws_requested_protocols[
- self.index]
- else:
- conn_context.ws_protocol = self.default_value
-
- def transfer_data(self, conn_context):
- pass
-
-
-class HandshakeAbortedException(Exception):
- pass
-
-
-class AbortingDispatcher(object):
- """A dispatcher for testing. This dispatcher raises an exception in
- do_extra_handshake to reject the request.
- """
-
- def do_extra_handshake(self, conn_context):
- raise HandshakeAbortedException('An exception to reject the request')
-
- def transfer_data(self, conn_context):
- pass
-
-
-class AbortedByUserDispatcher(object):
- """A dispatcher for testing. This dispatcher raises an
- AbortedByUserException in do_extra_handshake to reject the request.
- """
-
- def do_extra_handshake(self, conn_context):
- raise AbortedByUserException('An AbortedByUserException to reject the '
- 'request')
-
- def transfer_data(self, conn_context):
- pass
-
-
-_EXPECTED_RESPONSE = (
- 'HTTP/1.1 101 Switching Protocols\r\n'
- 'Upgrade: websocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n')
-
-
-class HandshakerTest(unittest.TestCase):
- """A unittest for draft-ietf-hybi-thewebsocketprotocol-06 and later
- handshake processor.
- """
-
- def test_do_handshake(self):
- request = _create_request(_create_good_request_def())
- dispatcher = mock.MockDispatcher()
- handshaker = Handshaker(request, dispatcher)
- handshaker.do_handshake()
-
- self.assertTrue(dispatcher.do_extra_handshake_called)
-
- self.assertEqual(
- _EXPECTED_RESPONSE, request.connection.written_data())
- self.assertEqual('/demo', request.ws_resource)
- self.assertEqual('http://example.com', request.ws_origin)
- self.assertEqual(None, request.ws_protocol)
- self.assertEqual(None, request.ws_extensions)
- self.assertEqual(common.VERSION_HYBI_LATEST, request.ws_version)
-
- def test_do_handshake_with_extra_headers(self):
- request_def = _create_good_request_def()
- # Add headers not related to WebSocket opening handshake.
- request_def.headers['FooKey'] = 'BarValue'
- request_def.headers['EmptyKey'] = ''
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(
- _EXPECTED_RESPONSE, request.connection.written_data())
-
- def test_do_handshake_with_capitalized_value(self):
- request_def = _create_good_request_def()
- request_def.headers['upgrade'] = 'WEBSOCKET'
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(
- _EXPECTED_RESPONSE, request.connection.written_data())
-
- request_def = _create_good_request_def()
- request_def.headers['Connection'] = 'UPGRADE'
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(
- _EXPECTED_RESPONSE, request.connection.written_data())
-
- def test_do_handshake_with_multiple_connection_values(self):
- request_def = _create_good_request_def()
- request_def.headers['Connection'] = 'Upgrade, keep-alive, , '
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(
- _EXPECTED_RESPONSE, request.connection.written_data())
-
- def test_aborting_handshake(self):
- handshaker = Handshaker(
- _create_request(_create_good_request_def()),
- AbortingDispatcher())
- # do_extra_handshake raises an exception. Check that it's not caught by
- # do_handshake.
- self.assertRaises(HandshakeAbortedException, handshaker.do_handshake)
-
- def test_do_handshake_with_protocol(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Protocol'] = 'chat, superchat'
-
- request = _create_request(request_def)
- handshaker = Handshaker(request, SubprotocolChoosingDispatcher(0))
- handshaker.do_handshake()
-
- EXPECTED_RESPONSE = (
- 'HTTP/1.1 101 Switching Protocols\r\n'
- 'Upgrade: websocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n'
- 'Sec-WebSocket-Protocol: chat\r\n\r\n')
-
- self.assertEqual(EXPECTED_RESPONSE, request.connection.written_data())
- self.assertEqual('chat', request.ws_protocol)
-
- def test_do_handshake_protocol_not_in_request_but_in_response(self):
- request_def = _create_good_request_def()
- request = _create_request(request_def)
- handshaker = Handshaker(
- request, SubprotocolChoosingDispatcher(-1, 'foobar'))
- # No request has been made but ws_protocol is set. HandshakeException
- # must be raised.
- self.assertRaises(HandshakeException, handshaker.do_handshake)
-
- def test_do_handshake_with_protocol_no_protocol_selection(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Protocol'] = 'chat, superchat'
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- # ws_protocol is not set. HandshakeException must be raised.
- self.assertRaises(HandshakeException, handshaker.do_handshake)
-
- def test_do_handshake_with_extensions(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = (
- 'permessage-compress; method=deflate, unknown')
-
- EXPECTED_RESPONSE = (
- 'HTTP/1.1 101 Switching Protocols\r\n'
- 'Upgrade: websocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n'
- 'Sec-WebSocket-Extensions: permessage-compress; method=deflate\r\n'
- '\r\n')
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(EXPECTED_RESPONSE, request.connection.written_data())
- self.assertEqual(1, len(request.ws_extensions))
- extension = request.ws_extensions[0]
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- extension.name())
- self.assertEqual(['method'], extension.get_parameter_names())
- self.assertEqual('deflate', extension.get_parameter_value('method'))
- self.assertEqual(1, len(request.ws_extension_processors))
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- request.ws_extension_processors[0].name())
-
- def test_do_handshake_with_permessage_compress(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = (
- 'permessage-compress; method=deflate')
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(1, len(request.ws_extensions))
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- request.ws_extensions[0].name())
- self.assertEqual(1, len(request.ws_extension_processors))
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- request.ws_extension_processors[0].name())
-
- def test_do_handshake_with_quoted_extensions(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = (
- 'permessage-compress; method=deflate, , '
- 'unknown; e = "mc^2"; ma="\r\n \\\rf "; pv=nrt')
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(2, len(request.ws_requested_extensions))
- first_extension = request.ws_requested_extensions[0]
- self.assertEqual('permessage-compress', first_extension.name())
- self.assertEqual(['method'], first_extension.get_parameter_names())
- self.assertEqual('deflate',
- first_extension.get_parameter_value('method'))
- second_extension = request.ws_requested_extensions[1]
- self.assertEqual('unknown', second_extension.name())
- self.assertEqual(
- ['e', 'ma', 'pv'], second_extension.get_parameter_names())
- self.assertEqual('mc^2', second_extension.get_parameter_value('e'))
- self.assertEqual(' \rf ', second_extension.get_parameter_value('ma'))
- self.assertEqual('nrt', second_extension.get_parameter_value('pv'))
-
- def test_do_handshake_with_optional_headers(self):
- request_def = _create_good_request_def()
- request_def.headers['EmptyValue'] = ''
- request_def.headers['AKey'] = 'AValue'
-
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- self.assertEqual(
- 'AValue', request.headers_in['AKey'])
- self.assertEqual(
- '', request.headers_in['EmptyValue'])
-
- def test_abort_extra_handshake(self):
- handshaker = Handshaker(
- _create_request(_create_good_request_def()),
- AbortedByUserDispatcher())
- # do_extra_handshake raises an AbortedByUserException. Check that it's
- # not caught by do_handshake.
- self.assertRaises(AbortedByUserException, handshaker.do_handshake)
-
- def test_do_handshake_with_mux_and_deflate_frame(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = ('%s, %s' % (
- common.MUX_EXTENSION,
- common.DEFLATE_FRAME_EXTENSION))
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- # mux should be rejected.
- self.assertEqual(1, len(request.ws_extensions))
- self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
- request.ws_extensions[0].name())
- self.assertEqual(2, len(request.ws_extension_processors))
- self.assertEqual(common.MUX_EXTENSION,
- request.ws_extension_processors[0].name())
- self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
- request.ws_extension_processors[1].name())
- self.assertFalse(hasattr(request, 'mux_processor'))
-
- def test_do_handshake_with_deflate_frame_and_mux(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = ('%s, %s' % (
- common.DEFLATE_FRAME_EXTENSION,
- common.MUX_EXTENSION))
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- # mux should be rejected.
- self.assertEqual(1, len(request.ws_extensions))
- first_extension = request.ws_extensions[0]
- self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
- first_extension.name())
- self.assertEqual(2, len(request.ws_extension_processors))
- self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
- request.ws_extension_processors[0].name())
- self.assertEqual(common.MUX_EXTENSION,
- request.ws_extension_processors[1].name())
- self.assertFalse(hasattr(request, 'mux'))
-
- def test_do_handshake_with_permessage_compress_and_mux(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = (
- '%s; method=deflate, %s' % (
- common.PERMESSAGE_COMPRESSION_EXTENSION,
- common.MUX_EXTENSION))
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
-
- self.assertEqual(1, len(request.ws_extensions))
- self.assertEqual(common.MUX_EXTENSION,
- request.ws_extensions[0].name())
- self.assertEqual(2, len(request.ws_extension_processors))
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- request.ws_extension_processors[0].name())
- self.assertEqual(common.MUX_EXTENSION,
- request.ws_extension_processors[1].name())
- self.assertTrue(hasattr(request, 'mux_processor'))
- self.assertTrue(request.mux_processor.is_active())
- mux_extensions = request.mux_processor.extensions()
- self.assertEqual(1, len(mux_extensions))
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- mux_extensions[0].name())
-
- def test_do_handshake_with_mux_and_permessage_compress(self):
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Extensions'] = (
- '%s, %s; method=deflate' % (
- common.MUX_EXTENSION,
- common.PERMESSAGE_COMPRESSION_EXTENSION))
- request = _create_request(request_def)
- handshaker = _create_handshaker(request)
- handshaker.do_handshake()
- # mux should be rejected.
- self.assertEqual(1, len(request.ws_extensions))
- first_extension = request.ws_extensions[0]
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- first_extension.name())
- self.assertEqual(2, len(request.ws_extension_processors))
- self.assertEqual(common.MUX_EXTENSION,
- request.ws_extension_processors[0].name())
- self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
- request.ws_extension_processors[1].name())
- self.assertFalse(hasattr(request, 'mux_processor'))
-
- def test_bad_requests(self):
- bad_cases = [
- ('HTTP request',
- RequestDefinition(
- 'GET', '/demo',
- {'Host': 'www.google.com',
- 'User-Agent':
- 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;'
- ' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'
- ' GTB6 GTBA',
- 'Accept':
- 'text/html,application/xhtml+xml,application/xml;q=0.9,'
- '*/*;q=0.8',
- 'Accept-Language': 'en-us,en;q=0.5',
- 'Accept-Encoding': 'gzip,deflate',
- 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
- 'Keep-Alive': '300',
- 'Connection': 'keep-alive'}), None, True)]
-
- request_def = _create_good_request_def()
- request_def.method = 'POST'
- bad_cases.append(('Wrong method', request_def, None, True))
-
- request_def = _create_good_request_def()
- del request_def.headers['Host']
- bad_cases.append(('Missing Host', request_def, None, True))
-
- request_def = _create_good_request_def()
- del request_def.headers['Upgrade']
- bad_cases.append(('Missing Upgrade', request_def, None, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Upgrade'] = 'nonwebsocket'
- bad_cases.append(('Wrong Upgrade', request_def, None, True))
-
- request_def = _create_good_request_def()
- del request_def.headers['Connection']
- bad_cases.append(('Missing Connection', request_def, None, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Connection'] = 'Downgrade'
- bad_cases.append(('Wrong Connection', request_def, None, True))
-
- request_def = _create_good_request_def()
- del request_def.headers['Sec-WebSocket-Key']
- bad_cases.append(('Missing Sec-WebSocket-Key', request_def, 400, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Key'] = (
- 'dGhlIHNhbXBsZSBub25jZQ==garbage')
- bad_cases.append(('Wrong Sec-WebSocket-Key (with garbage on the tail)',
- request_def, 400, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Key'] = 'YQ==' # BASE64 of 'a'
- bad_cases.append(
- ('Wrong Sec-WebSocket-Key (decoded value is not 16 octets long)',
- request_def, 400, True))
-
- request_def = _create_good_request_def()
- # The last character right before == must be any of A, Q, w and g.
- request_def.headers['Sec-WebSocket-Key'] = (
- 'AQIDBAUGBwgJCgsMDQ4PEC==')
- bad_cases.append(
- ('Wrong Sec-WebSocket-Key (padding bits are not zero)',
- request_def, 400, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Key'] = (
- 'dGhlIHNhbXBsZSBub25jZQ==,dGhlIHNhbXBsZSBub25jZQ==')
- bad_cases.append(
- ('Wrong Sec-WebSocket-Key (multiple values)',
- request_def, 400, True))
-
- request_def = _create_good_request_def()
- del request_def.headers['Sec-WebSocket-Version']
- bad_cases.append(('Missing Sec-WebSocket-Version', request_def, None,
- True))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Version'] = '3'
- bad_cases.append(('Wrong Sec-WebSocket-Version', request_def, None,
- False))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Version'] = '13, 13'
- bad_cases.append(('Wrong Sec-WebSocket-Version (multiple values)',
- request_def, 400, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Protocol'] = 'illegal\x09protocol'
- bad_cases.append(('Illegal Sec-WebSocket-Protocol',
- request_def, 400, True))
-
- request_def = _create_good_request_def()
- request_def.headers['Sec-WebSocket-Protocol'] = ''
- bad_cases.append(('Empty Sec-WebSocket-Protocol',
- request_def, 400, True))
-
- for (case_name, request_def, expected_status,
- expect_handshake_exception) in bad_cases:
- request = _create_request(request_def)
- handshaker = Handshaker(request, mock.MockDispatcher())
- try:
- handshaker.do_handshake()
- self.fail('No exception thrown for \'%s\' case' % case_name)
- except HandshakeException, e:
- self.assertTrue(expect_handshake_exception)
- self.assertEqual(expected_status, e.status)
- except VersionException, e:
- self.assertFalse(expect_handshake_exception)
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi00.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi00.py
deleted file mode 100755
index 73f9f27ca..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_handshake_hybi00.py
+++ /dev/null
@@ -1,516 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for handshake.hybi00 module."""
-
-
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake.hybi00 import Handshaker
-from mod_pywebsocket.handshake.hybi00 import _validate_subprotocol
-from test import mock
-
-
-_TEST_KEY1 = '4 @1 46546xW%0l 1 5'
-_TEST_KEY2 = '12998 5 Y3 1 .P00'
-_TEST_KEY3 = '^n:ds[4U'
-_TEST_CHALLENGE_RESPONSE = '8jKS\'y:G*Co,Wxa-'
-
-
-_GOOD_REQUEST = (
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3)
-
-_GOOD_REQUEST_CAPITALIZED_HEADER_VALUES = (
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'UPGRADE',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WEBSOCKET',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3)
-
-_GOOD_REQUEST_CASE_MIXED_HEADER_NAMES = (
- 80,
- 'GET',
- '/demo',
- {
- 'hOsT': 'example.com',
- 'cOnNeCtIoN': 'Upgrade',
- 'sEc-wEbsOcKeT-kEy2': _TEST_KEY2,
- 'sEc-wEbsOcKeT-pRoToCoL': 'sample',
- 'uPgRaDe': 'WebSocket',
- 'sEc-wEbsOcKeT-kEy1': _TEST_KEY1,
- 'oRiGiN': 'http://example.com',
- },
- _TEST_KEY3)
-
-_GOOD_RESPONSE_DEFAULT_PORT = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: ws://example.com/demo\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- 'Sec-WebSocket-Protocol: sample\r\n'
- '\r\n' +
- _TEST_CHALLENGE_RESPONSE)
-
-_GOOD_RESPONSE_SECURE = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: wss://example.com/demo\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- 'Sec-WebSocket-Protocol: sample\r\n'
- '\r\n' +
- _TEST_CHALLENGE_RESPONSE)
-
-_GOOD_REQUEST_NONDEFAULT_PORT = (
- 8081,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com:8081',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3)
-
-_GOOD_RESPONSE_NONDEFAULT_PORT = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: ws://example.com:8081/demo\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- 'Sec-WebSocket-Protocol: sample\r\n'
- '\r\n' +
- _TEST_CHALLENGE_RESPONSE)
-
-_GOOD_RESPONSE_SECURE_NONDEF = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: wss://example.com:8081/demo\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- 'Sec-WebSocket-Protocol: sample\r\n'
- '\r\n' +
- _TEST_CHALLENGE_RESPONSE)
-
-_GOOD_REQUEST_NO_PROTOCOL = (
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3)
-
-_GOOD_RESPONSE_NO_PROTOCOL = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: ws://example.com/demo\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- '\r\n' +
- _TEST_CHALLENGE_RESPONSE)
-
-_GOOD_REQUEST_WITH_OPTIONAL_HEADERS = (
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'EmptyValue': '',
- 'Sec-WebSocket-Protocol': 'sample',
- 'AKey': 'AValue',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3)
-
-# TODO(tyoshino): Include \r \n in key3, challenge response.
-
-_GOOD_REQUEST_WITH_NONPRINTABLE_KEY = (
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': 'y R2 48 Q1O4 e|BV3 i5 1 u- 65',
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '36 7 74 i 92 2\'m 9 0G',
- 'Origin': 'http://example.com',
- },
- ''.join(map(chr, [0x01, 0xd1, 0xdd, 0x3b, 0xd1, 0x56, 0x63, 0xff])))
-
-_GOOD_RESPONSE_WITH_NONPRINTABLE_KEY = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: ws://example.com/demo\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- 'Sec-WebSocket-Protocol: sample\r\n'
- '\r\n' +
- ''.join(map(chr, [0x0b, 0x99, 0xfa, 0x55, 0xbd, 0x01, 0x23, 0x7b,
- 0x45, 0xa2, 0xf1, 0xd0, 0x87, 0x8a, 0xee, 0xeb])))
-
-_GOOD_REQUEST_WITH_QUERY_PART = (
- 80,
- 'GET',
- '/demo?e=mc2',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3)
-
-_GOOD_RESPONSE_WITH_QUERY_PART = (
- 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
- 'Upgrade: WebSocket\r\n'
- 'Connection: Upgrade\r\n'
- 'Sec-WebSocket-Location: ws://example.com/demo?e=mc2\r\n'
- 'Sec-WebSocket-Origin: http://example.com\r\n'
- 'Sec-WebSocket-Protocol: sample\r\n'
- '\r\n' +
- _TEST_CHALLENGE_RESPONSE)
-
-_BAD_REQUESTS = (
- ( # HTTP request
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'www.google.com',
- 'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;'
- ' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'
- ' GTB6 GTBA',
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,'
- '*/*;q=0.8',
- 'Accept-Language': 'en-us,en;q=0.5',
- 'Accept-Encoding': 'gzip,deflate',
- 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
- 'Keep-Alive': '300',
- 'Connection': 'keep-alive',
- }),
- ( # Wrong method
- 80,
- 'POST',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
- ( # Missing Upgrade
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
- ( # Wrong Upgrade
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'NonWebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
- ( # Empty WebSocket-Protocol
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': '',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
- ( # Wrong port number format
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com:0x50',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
- ( # Header/connection port mismatch
- 8080,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'sample',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
- ( # Illegal WebSocket-Protocol
- 80,
- 'GET',
- '/demo',
- {
- 'Host': 'example.com',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': _TEST_KEY2,
- 'Sec-WebSocket-Protocol': 'illegal\x09protocol',
- 'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': _TEST_KEY1,
- 'Origin': 'http://example.com',
- },
- _TEST_KEY3),
-)
-
-
-def _create_request(request_def):
- data = ''
- if len(request_def) > 4:
- data = request_def[4]
- conn = mock.MockConn(data)
- conn.local_addr = ('0.0.0.0', request_def[0])
- return mock.MockRequest(
- method=request_def[1],
- uri=request_def[2],
- headers_in=request_def[3],
- connection=conn)
-
-
-def _create_get_memorized_lines(lines):
- """Creates a function that returns the given string."""
-
- def get_memorized_lines():
- return lines
- return get_memorized_lines
-
-
-def _create_requests_with_lines(request_lines_set):
- requests = []
- for lines in request_lines_set:
- request = _create_request(_GOOD_REQUEST)
- request.connection.get_memorized_lines = _create_get_memorized_lines(
- lines)
- requests.append(request)
- return requests
-
-
-class HyBi00HandshakerTest(unittest.TestCase):
-
- def test_good_request_default_port(self):
- request = _create_request(_GOOD_REQUEST)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
- request.connection.written_data())
- self.assertEqual('/demo', request.ws_resource)
- self.assertEqual('http://example.com', request.ws_origin)
- self.assertEqual('ws://example.com/demo', request.ws_location)
- self.assertEqual('sample', request.ws_protocol)
-
- def test_good_request_capitalized_header_values(self):
- request = _create_request(_GOOD_REQUEST_CAPITALIZED_HEADER_VALUES)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
- request.connection.written_data())
-
- def test_good_request_case_mixed_header_names(self):
- request = _create_request(_GOOD_REQUEST_CASE_MIXED_HEADER_NAMES)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
- request.connection.written_data())
-
- def test_good_request_secure_default_port(self):
- request = _create_request(_GOOD_REQUEST)
- request.connection.local_addr = ('0.0.0.0', 443)
- request.is_https_ = True
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_SECURE,
- request.connection.written_data())
- self.assertEqual('sample', request.ws_protocol)
-
- def test_good_request_nondefault_port(self):
- request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT)
- handshaker = Handshaker(request,
- mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_NONDEFAULT_PORT,
- request.connection.written_data())
- self.assertEqual('sample', request.ws_protocol)
-
- def test_good_request_secure_non_default_port(self):
- request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT)
- request.is_https_ = True
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_SECURE_NONDEF,
- request.connection.written_data())
- self.assertEqual('sample', request.ws_protocol)
-
- def test_good_request_default_no_protocol(self):
- request = _create_request(_GOOD_REQUEST_NO_PROTOCOL)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_NO_PROTOCOL,
- request.connection.written_data())
- self.assertEqual(None, request.ws_protocol)
-
- def test_good_request_optional_headers(self):
- request = _create_request(_GOOD_REQUEST_WITH_OPTIONAL_HEADERS)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual('AValue',
- request.headers_in['AKey'])
- self.assertEqual('',
- request.headers_in['EmptyValue'])
-
- def test_good_request_with_nonprintable_key(self):
- request = _create_request(_GOOD_REQUEST_WITH_NONPRINTABLE_KEY)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_WITH_NONPRINTABLE_KEY,
- request.connection.written_data())
- self.assertEqual('sample', request.ws_protocol)
-
- def test_good_request_with_query_part(self):
- request = _create_request(_GOOD_REQUEST_WITH_QUERY_PART)
- handshaker = Handshaker(request, mock.MockDispatcher())
- handshaker.do_handshake()
- self.assertEqual(_GOOD_RESPONSE_WITH_QUERY_PART,
- request.connection.written_data())
- self.assertEqual('ws://example.com/demo?e=mc2', request.ws_location)
-
- def test_bad_requests(self):
- for request in map(_create_request, _BAD_REQUESTS):
- handshaker = Handshaker(request, mock.MockDispatcher())
- self.assertRaises(HandshakeException, handshaker.do_handshake)
-
-
-class HyBi00ValidateSubprotocolTest(unittest.TestCase):
- def test_validate_subprotocol(self):
- # should succeed.
- _validate_subprotocol('sample')
- _validate_subprotocol('Sample')
- _validate_subprotocol('sample\x7eprotocol')
- _validate_subprotocol('sample\x20protocol')
-
- # should fail.
- self.assertRaises(HandshakeException,
- _validate_subprotocol,
- '')
- self.assertRaises(HandshakeException,
- _validate_subprotocol,
- 'sample\x19protocol')
- self.assertRaises(HandshakeException,
- _validate_subprotocol,
- 'sample\x7fprotocol')
- self.assertRaises(HandshakeException,
- _validate_subprotocol,
- # "Japan" in Japanese
- u'\u65e5\u672c')
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_http_header_util.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_http_header_util.py
deleted file mode 100755
index 436dc57c3..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_http_header_util.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for http_header_util module."""
-
-
-import unittest
-
-from mod_pywebsocket import http_header_util
-
-
-class UnitTest(unittest.TestCase):
- """A unittest for http_header_util module."""
-
- def test_parse_relative_uri(self):
- host, port, resource = http_header_util.parse_uri('/ws/test')
- self.assertEqual(None, host)
- self.assertEqual(None, port)
- self.assertEqual('/ws/test', resource)
-
- def test_parse_absolute_uri(self):
- host, port, resource = http_header_util.parse_uri(
- 'ws://localhost:10080/ws/test')
- self.assertEqual('localhost', host)
- self.assertEqual(10080, port)
- self.assertEqual('/ws/test', resource)
-
- host, port, resource = http_header_util.parse_uri(
- 'ws://example.com/ws/test')
- self.assertEqual('example.com', host)
- self.assertEqual(80, port)
- self.assertEqual('/ws/test', resource)
-
- host, port, resource = http_header_util.parse_uri(
- 'wss://example.com/')
- self.assertEqual('example.com', host)
- self.assertEqual(443, port)
- self.assertEqual('/', resource)
-
- host, port, resource = http_header_util.parse_uri(
- 'ws://example.com:8080')
- self.assertEqual('example.com', host)
- self.assertEqual(8080, port)
- self.assertEqual('/', resource)
-
- def test_parse_invalid_uri(self):
- host, port, resource = http_header_util.parse_uri('ws:///')
- self.assertEqual(None, resource)
-
- host, port, resource = http_header_util.parse_uri('ws://localhost:')
- self.assertEqual(None, resource)
-
- host, port, resource = http_header_util.parse_uri('ws://localhost:/ws')
- self.assertEqual(None, resource)
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_memorizingfile.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_memorizingfile.py
deleted file mode 100755
index 8f1b8eef4..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_memorizingfile.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for memorizingfile module."""
-
-
-import StringIO
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import memorizingfile
-
-
-class UtilTest(unittest.TestCase):
- """A unittest for memorizingfile module."""
-
- def check(self, memorizing_file, num_read, expected_list):
- for unused in range(num_read):
- memorizing_file.readline()
- actual_list = memorizing_file.get_memorized_lines()
- self.assertEqual(len(expected_list), len(actual_list))
- for expected, actual in zip(expected_list, actual_list):
- self.assertEqual(expected, actual)
-
- def check_with_size(self, memorizing_file, read_size, expected_list):
- read_list = []
- read_line = ''
- while True:
- line = memorizing_file.readline(read_size)
- line_length = len(line)
- self.assertTrue(line_length <= read_size)
- if line_length == 0:
- if read_line != '':
- read_list.append(read_line)
- break
- read_line += line
- if line[line_length - 1] == '\n':
- read_list.append(read_line)
- read_line = ''
- actual_list = memorizing_file.get_memorized_lines()
- self.assertEqual(len(expected_list), len(actual_list))
- self.assertEqual(len(expected_list), len(read_list))
- for expected, actual, read in zip(expected_list, actual_list,
- read_list):
- self.assertEqual(expected, actual)
- self.assertEqual(expected, read)
-
- def test_get_memorized_lines(self):
- memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
- 'Hello\nWorld\nWelcome'))
- self.check(memorizing_file, 3, ['Hello\n', 'World\n', 'Welcome'])
-
- def test_get_memorized_lines_limit_memorized_lines(self):
- memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
- 'Hello\nWorld\nWelcome'), 2)
- self.check(memorizing_file, 3, ['Hello\n', 'World\n'])
-
- def test_get_memorized_lines_empty_file(self):
- memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
- ''))
- self.check(memorizing_file, 10, [])
-
- def test_get_memorized_lines_with_size(self):
- for size in range(1, 10):
- memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
- 'Hello\nWorld\nWelcome'))
- self.check_with_size(memorizing_file, size,
- ['Hello\n', 'World\n', 'Welcome'])
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_mock.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_mock.py
deleted file mode 100755
index 7dc23a73d..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_mock.py
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for mock module."""
-
-
-import Queue
-import threading
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from test import mock
-
-
-class MockConnTest(unittest.TestCase):
- """A unittest for MockConn class."""
-
- def setUp(self):
- self._conn = mock.MockConn('ABC\r\nDEFG\r\n\r\nHIJK')
-
- def test_readline(self):
- self.assertEqual('ABC\r\n', self._conn.readline())
- self.assertEqual('DEFG\r\n', self._conn.readline())
- self.assertEqual('\r\n', self._conn.readline())
- self.assertEqual('HIJK', self._conn.readline())
- self.assertEqual('', self._conn.readline())
-
- def test_read(self):
- self.assertEqual('ABC\r\nD', self._conn.read(6))
- self.assertEqual('EFG\r\n\r\nHI', self._conn.read(9))
- self.assertEqual('JK', self._conn.read(10))
- self.assertEqual('', self._conn.read(10))
-
- def test_read_and_readline(self):
- self.assertEqual('ABC\r\nD', self._conn.read(6))
- self.assertEqual('EFG\r\n', self._conn.readline())
- self.assertEqual('\r\nHIJK', self._conn.read(9))
- self.assertEqual('', self._conn.readline())
-
- def test_write(self):
- self._conn.write('Hello\r\n')
- self._conn.write('World\r\n')
- self.assertEqual('Hello\r\nWorld\r\n', self._conn.written_data())
-
-
-class MockBlockingConnTest(unittest.TestCase):
- """A unittest for MockBlockingConn class."""
-
- def test_read(self):
- """Tests that data put to MockBlockingConn by put_bytes method can be
- read from it.
- """
-
- class LineReader(threading.Thread):
- """A test class that launches a thread, calls readline on the
- specified conn repeatedly and puts the read data to the specified
- queue.
- """
-
- def __init__(self, conn, queue):
- threading.Thread.__init__(self)
- self._queue = queue
- self._conn = conn
- self.setDaemon(True)
- self.start()
-
- def run(self):
- while True:
- data = self._conn.readline()
- self._queue.put(data)
-
- conn = mock.MockBlockingConn()
- queue = Queue.Queue()
- reader = LineReader(conn, queue)
- self.failUnless(queue.empty())
- conn.put_bytes('Foo bar\r\n')
- read = queue.get()
- self.assertEqual('Foo bar\r\n', read)
-
-
-class MockTableTest(unittest.TestCase):
- """A unittest for MockTable class."""
-
- def test_create_from_dict(self):
- table = mock.MockTable({'Key': 'Value'})
- self.assertEqual('Value', table.get('KEY'))
- self.assertEqual('Value', table['key'])
-
- def test_create_from_list(self):
- table = mock.MockTable([('Key', 'Value')])
- self.assertEqual('Value', table.get('KEY'))
- self.assertEqual('Value', table['key'])
-
- def test_create_from_tuple(self):
- table = mock.MockTable((('Key', 'Value'),))
- self.assertEqual('Value', table.get('KEY'))
- self.assertEqual('Value', table['key'])
-
- def test_set_and_get(self):
- table = mock.MockTable()
- self.assertEqual(None, table.get('Key'))
- table['Key'] = 'Value'
- self.assertEqual('Value', table.get('Key'))
- self.assertEqual('Value', table.get('key'))
- self.assertEqual('Value', table.get('KEY'))
- self.assertEqual('Value', table['Key'])
- self.assertEqual('Value', table['key'])
- self.assertEqual('Value', table['KEY'])
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_msgutil.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_msgutil.py
deleted file mode 100755
index 5fedcf92f..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_msgutil.py
+++ /dev/null
@@ -1,1356 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""Tests for msgutil module."""
-
-
-import array
-import Queue
-import random
-import struct
-import unittest
-import zlib
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import common
-from mod_pywebsocket.extensions import DeflateFrameExtensionProcessor
-from mod_pywebsocket.extensions import PerMessageCompressExtensionProcessor
-from mod_pywebsocket.extensions import PerMessageDeflateExtensionProcessor
-from mod_pywebsocket import msgutil
-from mod_pywebsocket.stream import InvalidUTF8Exception
-from mod_pywebsocket.stream import Stream
-from mod_pywebsocket.stream import StreamHixie75
-from mod_pywebsocket.stream import StreamOptions
-from mod_pywebsocket import util
-from test import mock
-
-
-# We use one fixed nonce for testing instead of cryptographically secure PRNG.
-_MASKING_NONCE = 'ABCD'
-
-
-def _mask_hybi(frame):
- frame_key = map(ord, _MASKING_NONCE)
- frame_key_len = len(frame_key)
- result = array.array('B')
- result.fromstring(frame)
- count = 0
- for i in xrange(len(result)):
- result[i] ^= frame_key[count]
- count = (count + 1) % frame_key_len
- return _MASKING_NONCE + result.tostring()
-
-
-def _install_extension_processor(processor, request, stream_options):
- response = processor.get_extension_response()
- if response is not None:
- processor.setup_stream_options(stream_options)
- request.ws_extension_processors.append(processor)
-
-
-def _create_request_from_rawdata(
- read_data,
- deflate_frame_request=None,
- permessage_compression_request=None,
- permessage_deflate_request=None):
- req = mock.MockRequest(connection=mock.MockConn(''.join(read_data)))
- req.ws_version = common.VERSION_HYBI_LATEST
- req.ws_extension_processors = []
-
- processor = None
- if deflate_frame_request is not None:
- processor = DeflateFrameExtensionProcessor(deflate_frame_request)
- elif permessage_compression_request is not None:
- processor = PerMessageCompressExtensionProcessor(
- permessage_compression_request)
- elif permessage_deflate_request is not None:
- processor = PerMessageDeflateExtensionProcessor(
- permessage_deflate_request)
-
- stream_options = StreamOptions()
- if processor is not None:
- _install_extension_processor(processor, req, stream_options)
- req.ws_stream = Stream(req, stream_options)
-
- return req
-
-
-def _create_request(*frames):
- """Creates MockRequest using data given as frames.
-
- frames will be returned on calling request.connection.read() where request
- is MockRequest returned by this function.
- """
-
- read_data = []
- for (header, body) in frames:
- read_data.append(header + _mask_hybi(body))
-
- return _create_request_from_rawdata(read_data)
-
-
-def _create_blocking_request():
- """Creates MockRequest.
-
- Data written to a MockRequest can be read out by calling
- request.connection.written_data().
- """
-
- req = mock.MockRequest(connection=mock.MockBlockingConn())
- req.ws_version = common.VERSION_HYBI_LATEST
- stream_options = StreamOptions()
- req.ws_stream = Stream(req, stream_options)
- return req
-
-
-def _create_request_hixie75(read_data=''):
- req = mock.MockRequest(connection=mock.MockConn(read_data))
- req.ws_stream = StreamHixie75(req)
- return req
-
-
-def _create_blocking_request_hixie75():
- req = mock.MockRequest(connection=mock.MockBlockingConn())
- req.ws_stream = StreamHixie75(req)
- return req
-
-
-class BasicMessageTest(unittest.TestCase):
- """Basic tests for Stream."""
-
- def test_send_message(self):
- request = _create_request()
- msgutil.send_message(request, 'Hello')
- self.assertEqual('\x81\x05Hello', request.connection.written_data())
-
- payload = 'a' * 125
- request = _create_request()
- msgutil.send_message(request, payload)
- self.assertEqual('\x81\x7d' + payload,
- request.connection.written_data())
-
- def test_send_medium_message(self):
- payload = 'a' * 126
- request = _create_request()
- msgutil.send_message(request, payload)
- self.assertEqual('\x81\x7e\x00\x7e' + payload,
- request.connection.written_data())
-
- payload = 'a' * ((1 << 16) - 1)
- request = _create_request()
- msgutil.send_message(request, payload)
- self.assertEqual('\x81\x7e\xff\xff' + payload,
- request.connection.written_data())
-
- def test_send_large_message(self):
- payload = 'a' * (1 << 16)
- request = _create_request()
- msgutil.send_message(request, payload)
- self.assertEqual('\x81\x7f\x00\x00\x00\x00\x00\x01\x00\x00' + payload,
- request.connection.written_data())
-
- def test_send_message_unicode(self):
- request = _create_request()
- msgutil.send_message(request, u'\u65e5')
- # U+65e5 is encoded as e6,97,a5 in UTF-8
- self.assertEqual('\x81\x03\xe6\x97\xa5',
- request.connection.written_data())
-
- def test_send_message_fragments(self):
- request = _create_request()
- msgutil.send_message(request, 'Hello', False)
- msgutil.send_message(request, ' ', False)
- msgutil.send_message(request, 'World', False)
- msgutil.send_message(request, '!', True)
- self.assertEqual('\x01\x05Hello\x00\x01 \x00\x05World\x80\x01!',
- request.connection.written_data())
-
- def test_send_fragments_immediate_zero_termination(self):
- request = _create_request()
- msgutil.send_message(request, 'Hello World!', False)
- msgutil.send_message(request, '', True)
- self.assertEqual('\x01\x0cHello World!\x80\x00',
- request.connection.written_data())
-
- def test_receive_message(self):
- request = _create_request(
- ('\x81\x85', 'Hello'), ('\x81\x86', 'World!'))
- self.assertEqual('Hello', msgutil.receive_message(request))
- self.assertEqual('World!', msgutil.receive_message(request))
-
- payload = 'a' * 125
- request = _create_request(('\x81\xfd', payload))
- self.assertEqual(payload, msgutil.receive_message(request))
-
- def test_receive_medium_message(self):
- payload = 'a' * 126
- request = _create_request(('\x81\xfe\x00\x7e', payload))
- self.assertEqual(payload, msgutil.receive_message(request))
-
- payload = 'a' * ((1 << 16) - 1)
- request = _create_request(('\x81\xfe\xff\xff', payload))
- self.assertEqual(payload, msgutil.receive_message(request))
-
- def test_receive_large_message(self):
- payload = 'a' * (1 << 16)
- request = _create_request(
- ('\x81\xff\x00\x00\x00\x00\x00\x01\x00\x00', payload))
- self.assertEqual(payload, msgutil.receive_message(request))
-
- def test_receive_length_not_encoded_using_minimal_number_of_bytes(self):
- # Log warning on receiving bad payload length field that doesn't use
- # minimal number of bytes but continue processing.
-
- payload = 'a'
- # 1 byte can be represented without extended payload length field.
- request = _create_request(
- ('\x81\xff\x00\x00\x00\x00\x00\x00\x00\x01', payload))
- self.assertEqual(payload, msgutil.receive_message(request))
-
- def test_receive_message_unicode(self):
- request = _create_request(('\x81\x83', '\xe6\x9c\xac'))
- # U+672c is encoded as e6,9c,ac in UTF-8
- self.assertEqual(u'\u672c', msgutil.receive_message(request))
-
- def test_receive_message_erroneous_unicode(self):
- # \x80 and \x81 are invalid as UTF-8.
- request = _create_request(('\x81\x82', '\x80\x81'))
- # Invalid characters should raise InvalidUTF8Exception
- self.assertRaises(InvalidUTF8Exception,
- msgutil.receive_message,
- request)
-
- def test_receive_fragments(self):
- request = _create_request(
- ('\x01\x85', 'Hello'),
- ('\x00\x81', ' '),
- ('\x00\x85', 'World'),
- ('\x80\x81', '!'))
- self.assertEqual('Hello World!', msgutil.receive_message(request))
-
- def test_receive_fragments_unicode(self):
- # UTF-8 encodes U+6f22 into e6bca2 and U+5b57 into e5ad97.
- request = _create_request(
- ('\x01\x82', '\xe6\xbc'),
- ('\x00\x82', '\xa2\xe5'),
- ('\x80\x82', '\xad\x97'))
- self.assertEqual(u'\u6f22\u5b57', msgutil.receive_message(request))
-
- def test_receive_fragments_immediate_zero_termination(self):
- request = _create_request(
- ('\x01\x8c', 'Hello World!'), ('\x80\x80', ''))
- self.assertEqual('Hello World!', msgutil.receive_message(request))
-
- def test_receive_fragments_duplicate_start(self):
- request = _create_request(
- ('\x01\x85', 'Hello'), ('\x01\x85', 'World'))
- self.assertRaises(msgutil.InvalidFrameException,
- msgutil.receive_message,
- request)
-
- def test_receive_fragments_intermediate_but_not_started(self):
- request = _create_request(('\x00\x85', 'Hello'))
- self.assertRaises(msgutil.InvalidFrameException,
- msgutil.receive_message,
- request)
-
- def test_receive_fragments_end_but_not_started(self):
- request = _create_request(('\x80\x85', 'Hello'))
- self.assertRaises(msgutil.InvalidFrameException,
- msgutil.receive_message,
- request)
-
- def test_receive_message_discard(self):
- request = _create_request(
- ('\x8f\x86', 'IGNORE'), ('\x81\x85', 'Hello'),
- ('\x8f\x89', 'DISREGARD'), ('\x81\x86', 'World!'))
- self.assertRaises(msgutil.UnsupportedFrameException,
- msgutil.receive_message, request)
- self.assertEqual('Hello', msgutil.receive_message(request))
- self.assertRaises(msgutil.UnsupportedFrameException,
- msgutil.receive_message, request)
- self.assertEqual('World!', msgutil.receive_message(request))
-
- def test_receive_close(self):
- request = _create_request(
- ('\x88\x8a', struct.pack('!H', 1000) + 'Good bye'))
- self.assertEqual(None, msgutil.receive_message(request))
- self.assertEqual(1000, request.ws_close_code)
- self.assertEqual('Good bye', request.ws_close_reason)
-
- def test_send_longest_close(self):
- reason = 'a' * 123
- request = _create_request(
- ('\x88\xfd',
- struct.pack('!H', common.STATUS_NORMAL_CLOSURE) + reason))
- request.ws_stream.close_connection(common.STATUS_NORMAL_CLOSURE,
- reason)
- self.assertEqual(request.ws_close_code, common.STATUS_NORMAL_CLOSURE)
- self.assertEqual(request.ws_close_reason, reason)
-
- def test_send_close_too_long(self):
- request = _create_request()
- self.assertRaises(msgutil.BadOperationException,
- Stream.close_connection,
- request.ws_stream,
- common.STATUS_NORMAL_CLOSURE,
- 'a' * 124)
-
- def test_send_close_inconsistent_code_and_reason(self):
- request = _create_request()
- # reason parameter must not be specified when code is None.
- self.assertRaises(msgutil.BadOperationException,
- Stream.close_connection,
- request.ws_stream,
- None,
- 'a')
-
- def test_send_ping(self):
- request = _create_request()
- msgutil.send_ping(request, 'Hello World!')
- self.assertEqual('\x89\x0cHello World!',
- request.connection.written_data())
-
- def test_send_longest_ping(self):
- request = _create_request()
- msgutil.send_ping(request, 'a' * 125)
- self.assertEqual('\x89\x7d' + 'a' * 125,
- request.connection.written_data())
-
- def test_send_ping_too_long(self):
- request = _create_request()
- self.assertRaises(msgutil.BadOperationException,
- msgutil.send_ping,
- request,
- 'a' * 126)
-
- def test_receive_ping(self):
- """Tests receiving a ping control frame."""
-
- def handler(request, message):
- request.called = True
-
- # Stream automatically respond to ping with pong without any action
- # by application layer.
- request = _create_request(
- ('\x89\x85', 'Hello'), ('\x81\x85', 'World'))
- self.assertEqual('World', msgutil.receive_message(request))
- self.assertEqual('\x8a\x05Hello',
- request.connection.written_data())
-
- request = _create_request(
- ('\x89\x85', 'Hello'), ('\x81\x85', 'World'))
- request.on_ping_handler = handler
- self.assertEqual('World', msgutil.receive_message(request))
- self.assertTrue(request.called)
-
- def test_receive_longest_ping(self):
- request = _create_request(
- ('\x89\xfd', 'a' * 125), ('\x81\x85', 'World'))
- self.assertEqual('World', msgutil.receive_message(request))
- self.assertEqual('\x8a\x7d' + 'a' * 125,
- request.connection.written_data())
-
- def test_receive_ping_too_long(self):
- request = _create_request(('\x89\xfe\x00\x7e', 'a' * 126))
- self.assertRaises(msgutil.InvalidFrameException,
- msgutil.receive_message,
- request)
-
- def test_receive_pong(self):
- """Tests receiving a pong control frame."""
-
- def handler(request, message):
- request.called = True
-
- request = _create_request(
- ('\x8a\x85', 'Hello'), ('\x81\x85', 'World'))
- request.on_pong_handler = handler
- msgutil.send_ping(request, 'Hello')
- self.assertEqual('\x89\x05Hello',
- request.connection.written_data())
- # Valid pong is received, but receive_message won't return for it.
- self.assertEqual('World', msgutil.receive_message(request))
- # Check that nothing was written after receive_message call.
- self.assertEqual('\x89\x05Hello',
- request.connection.written_data())
-
- self.assertTrue(request.called)
-
- def test_receive_unsolicited_pong(self):
- # Unsolicited pong is allowed from HyBi 07.
- request = _create_request(
- ('\x8a\x85', 'Hello'), ('\x81\x85', 'World'))
- msgutil.receive_message(request)
-
- request = _create_request(
- ('\x8a\x85', 'Hello'), ('\x81\x85', 'World'))
- msgutil.send_ping(request, 'Jumbo')
- # Body mismatch.
- msgutil.receive_message(request)
-
- def test_ping_cannot_be_fragmented(self):
- request = _create_request(('\x09\x85', 'Hello'))
- self.assertRaises(msgutil.InvalidFrameException,
- msgutil.receive_message,
- request)
-
- def test_ping_with_too_long_payload(self):
- request = _create_request(('\x89\xfe\x01\x00', 'a' * 256))
- self.assertRaises(msgutil.InvalidFrameException,
- msgutil.receive_message,
- request)
-
-
-class DeflateFrameTest(unittest.TestCase):
- """Tests for checking deflate-frame extension."""
-
- def test_send_message(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- '', deflate_frame_request=extension)
- msgutil.send_message(request, 'Hello')
- msgutil.send_message(request, 'World')
-
- expected = ''
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- expected += '\xc1%c' % len(compressed_hello)
- expected += compressed_hello
-
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_world = compressed_world[:-4]
- expected += '\xc1%c' % len(compressed_world)
- expected += compressed_world
-
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_bfinal(self):
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- '', deflate_frame_request=extension)
- self.assertEquals(1, len(request.ws_extension_processors))
- deflate_frame_processor = request.ws_extension_processors[0]
- deflate_frame_processor.set_bfinal(True)
- msgutil.send_message(request, 'Hello')
- msgutil.send_message(request, 'World')
-
- expected = ''
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_FINISH)
- compressed_hello = compressed_hello + chr(0)
- expected += '\xc1%c' % len(compressed_hello)
- expected += compressed_hello
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_FINISH)
- compressed_world = compressed_world + chr(0)
- expected += '\xc1%c' % len(compressed_world)
- expected += compressed_world
-
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_comp_bit(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- '', deflate_frame_request=extension)
- self.assertEquals(1, len(request.ws_extension_processors))
- deflate_frame_processor = request.ws_extension_processors[0]
- msgutil.send_message(request, 'Hello')
- deflate_frame_processor.disable_outgoing_compression()
- msgutil.send_message(request, 'Hello')
- deflate_frame_processor.enable_outgoing_compression()
- msgutil.send_message(request, 'Hello')
-
- expected = ''
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- expected += '\xc1%c' % len(compressed_hello)
- expected += compressed_hello
-
- expected += '\x81\x05Hello'
-
- compressed_2nd_hello = compress.compress('Hello')
- compressed_2nd_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_2nd_hello = compressed_2nd_hello[:-4]
- expected += '\xc1%c' % len(compressed_2nd_hello)
- expected += compressed_2nd_hello
-
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_no_context_takeover_parameter(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- extension.add_parameter('no_context_takeover', None)
- request = _create_request_from_rawdata(
- '', deflate_frame_request=extension)
- for i in xrange(3):
- msgutil.send_message(request, 'Hello')
-
- compressed_message = compress.compress('Hello')
- compressed_message += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_message = compressed_message[:-4]
- expected = '\xc1%c' % len(compressed_message)
- expected += compressed_message
-
- self.assertEqual(
- expected + expected + expected, request.connection.written_data())
-
- def test_bad_request_parameters(self):
- """Tests that if there's anything wrong with deflate-frame extension
- request, deflate-frame is rejected.
- """
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- # max_window_bits less than 8 is illegal.
- extension.add_parameter('max_window_bits', '7')
- processor = DeflateFrameExtensionProcessor(extension)
- self.assertEqual(None, processor.get_extension_response())
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- # max_window_bits greater than 15 is illegal.
- extension.add_parameter('max_window_bits', '16')
- processor = DeflateFrameExtensionProcessor(extension)
- self.assertEqual(None, processor.get_extension_response())
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- # Non integer max_window_bits is illegal.
- extension.add_parameter('max_window_bits', 'foobar')
- processor = DeflateFrameExtensionProcessor(extension)
- self.assertEqual(None, processor.get_extension_response())
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- # no_context_takeover must not have any value.
- extension.add_parameter('no_context_takeover', 'foobar')
- processor = DeflateFrameExtensionProcessor(extension)
- self.assertEqual(None, processor.get_extension_response())
-
- def test_response_parameters(self):
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- processor = DeflateFrameExtensionProcessor(extension)
- processor.set_response_window_bits(8)
- response = processor.get_extension_response()
- self.assertTrue(response.has_parameter('max_window_bits'))
- self.assertEqual('8', response.get_parameter_value('max_window_bits'))
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- processor = DeflateFrameExtensionProcessor(extension)
- processor.set_response_no_context_takeover(True)
- response = processor.get_extension_response()
- self.assertTrue(response.has_parameter('no_context_takeover'))
- self.assertTrue(
- response.get_parameter_value('no_context_takeover') is None)
-
- def test_receive_message(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- data = ''
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- data += '\xc1%c' % (len(compressed_hello) | 0x80)
- data += _mask_hybi(compressed_hello)
-
- compressed_websocket = compress.compress('WebSocket')
- compressed_websocket += compress.flush(zlib.Z_FINISH)
- compressed_websocket += '\x00'
- data += '\xc1%c' % (len(compressed_websocket) | 0x80)
- data += _mask_hybi(compressed_websocket)
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_world = compressed_world[:-4]
- data += '\xc1%c' % (len(compressed_world) | 0x80)
- data += _mask_hybi(compressed_world)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- data, deflate_frame_request=extension)
- self.assertEqual('Hello', msgutil.receive_message(request))
- self.assertEqual('WebSocket', msgutil.receive_message(request))
- self.assertEqual('World', msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
- def test_receive_message_client_using_smaller_window(self):
- """Test that frames coming from a client which is using smaller window
- size that the server are correctly received.
- """
-
- # Using the smallest window bits of 8 for generating input frames.
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -8)
-
- data = ''
-
- # Use a frame whose content is bigger than the clients' DEFLATE window
- # size before compression. The content mainly consists of 'a' but
- # repetition of 'b' is put at the head and tail so that if the window
- # size is big, the head is back-referenced but if small, not.
- payload = 'b' * 64 + 'a' * 1024 + 'b' * 64
- compressed_hello = compress.compress(payload)
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- data += '\xc1%c' % (len(compressed_hello) | 0x80)
- data += _mask_hybi(compressed_hello)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- data, deflate_frame_request=extension)
- self.assertEqual(payload, msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
- def test_receive_message_comp_bit(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- data = ''
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- data += '\xc1%c' % (len(compressed_hello) | 0x80)
- data += _mask_hybi(compressed_hello)
-
- data += '\x81\x85' + _mask_hybi('Hello')
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- compressed_2nd_hello = compress.compress('Hello')
- compressed_2nd_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_2nd_hello = compressed_2nd_hello[:-4]
- data += '\xc1%c' % (len(compressed_2nd_hello) | 0x80)
- data += _mask_hybi(compressed_2nd_hello)
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- data, deflate_frame_request=extension)
- for i in xrange(3):
- self.assertEqual('Hello', msgutil.receive_message(request))
-
- def test_receive_message_various_btype(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- data = ''
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- data += '\xc1%c' % (len(compressed_hello) | 0x80)
- data += _mask_hybi(compressed_hello)
-
- compressed_websocket = compress.compress('WebSocket')
- compressed_websocket += compress.flush(zlib.Z_FINISH)
- compressed_websocket += '\x00'
- data += '\xc1%c' % (len(compressed_websocket) | 0x80)
- data += _mask_hybi(compressed_websocket)
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_world = compressed_world[:-4]
- data += '\xc1%c' % (len(compressed_world) | 0x80)
- data += _mask_hybi(compressed_world)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
- request = _create_request_from_rawdata(
- data, deflate_frame_request=extension)
- self.assertEqual('Hello', msgutil.receive_message(request))
- self.assertEqual('WebSocket', msgutil.receive_message(request))
- self.assertEqual('World', msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
-
-class PerMessageDeflateTest(unittest.TestCase):
- """Tests for permessage-deflate extension."""
-
- def test_send_message(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- msgutil.send_message(request, 'Hello')
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- expected = '\xc1%c' % len(compressed_hello)
- expected += compressed_hello
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_empty_message(self):
- """Test that an empty message is compressed correctly."""
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
-
- msgutil.send_message(request, '')
-
- # Payload in binary: 0b00000010 0b00000000
- # From LSB,
- # - 1 bit of BFINAL (0)
- # - 2 bits of BTYPE (01 that means fixed Huffman)
- # - 7 bits of the first code (0000000 that is the code for the
- # end-of-block)
- # - 1 bit of BFINAL (0)
- # - 2 bits of BTYPE (no compression)
- # - 3 bits of padding
- self.assertEqual('\xc1\x02\x02\x00',
- request.connection.written_data())
-
- def test_send_message_with_null_character(self):
- """Test that a simple payload (one null) is framed correctly."""
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
-
- msgutil.send_message(request, '\x00')
-
- # Payload in binary: 0b01100010 0b00000000 0b00000000
- # From LSB,
- # - 1 bit of BFINAL (0)
- # - 2 bits of BTYPE (01 that means fixed Huffman)
- # - 8 bits of the first code (00110000 that is the code for the literal
- # alphabet 0x00)
- # - 7 bits of the second code (0000000 that is the code for the
- # end-of-block)
- # - 1 bit of BFINAL (0)
- # - 2 bits of BTYPE (no compression)
- # - 2 bits of padding
- self.assertEqual('\xc1\x03\x62\x00\x00',
- request.connection.written_data())
-
- def test_send_two_messages(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- msgutil.send_message(request, 'Hello')
- msgutil.send_message(request, 'World')
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- expected = ''
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- expected += '\xc1%c' % len(compressed_hello)
- expected += compressed_hello
-
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_world = compressed_world[:-4]
- expected += '\xc1%c' % len(compressed_world)
- expected += compressed_world
-
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_fragmented(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- msgutil.send_message(request, 'Hello', end=False)
- msgutil.send_message(request, 'Goodbye', end=False)
- msgutil.send_message(request, 'World')
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- expected = '\x41%c' % len(compressed_hello)
- expected += compressed_hello
- compressed_goodbye = compress.compress('Goodbye')
- compressed_goodbye += compress.flush(zlib.Z_SYNC_FLUSH)
- expected += '\x00%c' % len(compressed_goodbye)
- expected += compressed_goodbye
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_world = compressed_world[:-4]
- expected += '\x80%c' % len(compressed_world)
- expected += compressed_world
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_fragmented_empty_first_frame(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- msgutil.send_message(request, '', end=False)
- msgutil.send_message(request, 'Hello')
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- expected = '\x41%c' % len(compressed_hello)
- expected += compressed_hello
- compressed_empty = compress.compress('Hello')
- compressed_empty += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_empty = compressed_empty[:-4]
- expected += '\x80%c' % len(compressed_empty)
- expected += compressed_empty
- print '%r' % expected
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_fragmented_empty_last_frame(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- msgutil.send_message(request, 'Hello', end=False)
- msgutil.send_message(request, '')
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- expected = '\x41%c' % len(compressed_hello)
- expected += compressed_hello
- compressed_empty = compress.compress('')
- compressed_empty += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_empty = compressed_empty[:-4]
- expected += '\x80%c' % len(compressed_empty)
- expected += compressed_empty
- self.assertEqual(expected, request.connection.written_data())
-
- def test_send_message_using_small_window(self):
- common_part = 'abcdefghijklmnopqrstuvwxyz'
- test_message = common_part + '-' * 30000 + common_part
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- extension.add_parameter('server_max_window_bits', '8')
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- msgutil.send_message(request, test_message)
-
- expected_websocket_header_size = 2
- expected_websocket_payload_size = 91
-
- actual_frame = request.connection.written_data()
- self.assertEqual(expected_websocket_header_size +
- expected_websocket_payload_size,
- len(actual_frame))
- actual_header = actual_frame[0:expected_websocket_header_size]
- actual_payload = actual_frame[expected_websocket_header_size:]
-
- self.assertEqual(
- '\xc1%c' % expected_websocket_payload_size, actual_header)
- decompress = zlib.decompressobj(-8)
- decompressed_message = decompress.decompress(
- actual_payload + '\x00\x00\xff\xff')
- decompressed_message += decompress.flush()
- self.assertEqual(test_message, decompressed_message)
- self.assertEqual(0, len(decompress.unused_data))
- self.assertEqual(0, len(decompress.unconsumed_tail))
-
- def test_send_message_no_context_takeover_parameter(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- extension.add_parameter('server_no_context_takeover', None)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- for i in xrange(3):
- msgutil.send_message(request, 'Hello', end=False)
- msgutil.send_message(request, 'Hello', end=True)
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- first_hello = compress.compress('Hello')
- first_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- expected = '\x41%c' % len(first_hello)
- expected += first_hello
- second_hello = compress.compress('Hello')
- second_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- second_hello = second_hello[:-4]
- expected += '\x80%c' % len(second_hello)
- expected += second_hello
-
- self.assertEqual(
- expected + expected + expected,
- request.connection.written_data())
-
- def test_send_message_fragmented_bfinal(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- '', permessage_deflate_request=extension)
- self.assertEquals(1, len(request.ws_extension_processors))
- request.ws_extension_processors[0].set_bfinal(True)
- msgutil.send_message(request, 'Hello', end=False)
- msgutil.send_message(request, 'World', end=True)
-
- expected = ''
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_FINISH)
- compressed_hello = compressed_hello + chr(0)
- expected += '\x41%c' % len(compressed_hello)
- expected += compressed_hello
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_FINISH)
- compressed_world = compressed_world + chr(0)
- expected += '\x80%c' % len(compressed_world)
- expected += compressed_world
-
- self.assertEqual(expected, request.connection.written_data())
-
- def test_receive_message_deflate(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- data = '\xc1%c' % (len(compressed_hello) | 0x80)
- data += _mask_hybi(compressed_hello)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- data, permessage_deflate_request=extension)
- self.assertEqual('Hello', msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
- def test_receive_message_random_section(self):
- """Test that a compressed message fragmented into lots of chunks is
- correctly received.
- """
-
- random.seed(a=0)
- payload = ''.join(
- [chr(random.randint(0, 255)) for i in xrange(1000)])
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_payload = compress.compress(payload)
- compressed_payload += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_payload = compressed_payload[:-4]
-
- # Fragment the compressed payload into lots of frames.
- bytes_chunked = 0
- data = ''
- frame_count = 0
-
- chunk_sizes = []
-
- while bytes_chunked < len(compressed_payload):
- # Make sure that
- # - the length of chunks are equal or less than 125 so that we can
- # use 1 octet length header format for all frames.
- # - at least 10 chunks are created.
- chunk_size = random.randint(
- 1, min(125,
- len(compressed_payload) / 10,
- len(compressed_payload) - bytes_chunked))
- chunk_sizes.append(chunk_size)
- chunk = compressed_payload[
- bytes_chunked:bytes_chunked + chunk_size]
- bytes_chunked += chunk_size
-
- first_octet = 0x00
- if len(data) == 0:
- first_octet = first_octet | 0x42
- if bytes_chunked == len(compressed_payload):
- first_octet = first_octet | 0x80
-
- data += '%c%c' % (first_octet, chunk_size | 0x80)
- data += _mask_hybi(chunk)
-
- frame_count += 1
-
- print "Chunk sizes: %r" % chunk_sizes
- self.assertTrue(len(chunk_sizes) > 10)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- data, permessage_deflate_request=extension)
- self.assertEqual(payload, msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
- def test_receive_two_messages(self):
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- data = ''
-
- compressed_hello = compress.compress('HelloWebSocket')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- split_position = len(compressed_hello) / 2
- data += '\x41%c' % (split_position | 0x80)
- data += _mask_hybi(compressed_hello[:split_position])
-
- data += '\x80%c' % ((len(compressed_hello) - split_position) | 0x80)
- data += _mask_hybi(compressed_hello[split_position:])
-
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-
- compressed_world = compress.compress('World')
- compressed_world += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_world = compressed_world[:-4]
- data += '\xc1%c' % (len(compressed_world) | 0x80)
- data += _mask_hybi(compressed_world)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- data, permessage_deflate_request=extension)
- self.assertEqual('HelloWebSocket', msgutil.receive_message(request))
- self.assertEqual('World', msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
- def test_receive_message_mixed_btype(self):
- """Test that a message compressed using lots of DEFLATE blocks with
- various flush mode is correctly received.
- """
-
- random.seed(a=0)
- payload = ''.join(
- [chr(random.randint(0, 255)) for i in xrange(1000)])
-
- compress = None
-
- # Fragment the compressed payload into lots of frames.
- bytes_chunked = 0
- compressed_payload = ''
-
- chunk_sizes = []
- methods = []
- sync_used = False
- finish_used = False
-
- while bytes_chunked < len(payload):
- # Make sure at least 10 chunks are created.
- chunk_size = random.randint(
- 1, min(100, len(payload) - bytes_chunked))
- chunk_sizes.append(chunk_size)
- chunk = payload[bytes_chunked:bytes_chunked + chunk_size]
-
- bytes_chunked += chunk_size
-
- if compress is None:
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION,
- zlib.DEFLATED,
- -zlib.MAX_WBITS)
-
- if bytes_chunked == len(payload):
- compressed_payload += compress.compress(chunk)
- compressed_payload += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_payload = compressed_payload[:-4]
- else:
- method = random.randint(0, 1)
- methods.append(method)
- if method == 0:
- compressed_payload += compress.compress(chunk)
- compressed_payload += compress.flush(zlib.Z_SYNC_FLUSH)
- sync_used = True
- else:
- compressed_payload += compress.compress(chunk)
- compressed_payload += compress.flush(zlib.Z_FINISH)
- compress = None
- finish_used = True
-
- print "Chunk sizes: %r" % chunk_sizes
- self.assertTrue(len(chunk_sizes) > 10)
- print "Methods: %r" % methods
- self.assertTrue(sync_used)
- self.assertTrue(finish_used)
-
- self.assertTrue(125 < len(compressed_payload))
- self.assertTrue(len(compressed_payload) < 65536)
- data = '\xc2\xfe' + struct.pack('!H', len(compressed_payload))
- data += _mask_hybi(compressed_payload)
-
- # Close frame
- data += '\x88\x8a' + _mask_hybi(struct.pack('!H', 1000) + 'Good bye')
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_DEFLATE_EXTENSION)
- request = _create_request_from_rawdata(
- data, permessage_deflate_request=extension)
- self.assertEqual(payload, msgutil.receive_message(request))
-
- self.assertEqual(None, msgutil.receive_message(request))
-
-
-class PerMessageCompressTest(unittest.TestCase):
- """Tests for checking permessage-compression extension."""
-
- def test_deflate_response_parameters(self):
- extension = common.ExtensionParameter(
- common.PERMESSAGE_COMPRESSION_EXTENSION)
- extension.add_parameter('method', 'deflate')
- processor = PerMessageCompressExtensionProcessor(extension)
- response = processor.get_extension_response()
- self.assertEqual('deflate',
- response.get_parameter_value('method'))
-
- extension = common.ExtensionParameter(
- common.PERMESSAGE_COMPRESSION_EXTENSION)
- extension.add_parameter('method', 'deflate')
- processor = PerMessageCompressExtensionProcessor(extension)
-
- def _compression_processor_hook(compression_processor):
- compression_processor.set_client_max_window_bits(8)
- compression_processor.set_client_no_context_takeover(True)
- processor.set_compression_processor_hook(
- _compression_processor_hook)
- response = processor.get_extension_response()
- self.assertEqual(
- 'deflate; client_max_window_bits=8; client_no_context_takeover',
- response.get_parameter_value('method'))
-
-
-class MessageTestHixie75(unittest.TestCase):
- """Tests for draft-hixie-thewebsocketprotocol-76 stream class."""
-
- def test_send_message(self):
- request = _create_request_hixie75()
- msgutil.send_message(request, 'Hello')
- self.assertEqual('\x00Hello\xff', request.connection.written_data())
-
- def test_send_message_unicode(self):
- request = _create_request_hixie75()
- msgutil.send_message(request, u'\u65e5')
- # U+65e5 is encoded as e6,97,a5 in UTF-8
- self.assertEqual('\x00\xe6\x97\xa5\xff',
- request.connection.written_data())
-
- def test_receive_message(self):
- request = _create_request_hixie75('\x00Hello\xff\x00World!\xff')
- self.assertEqual('Hello', msgutil.receive_message(request))
- self.assertEqual('World!', msgutil.receive_message(request))
-
- def test_receive_message_unicode(self):
- request = _create_request_hixie75('\x00\xe6\x9c\xac\xff')
- # U+672c is encoded as e6,9c,ac in UTF-8
- self.assertEqual(u'\u672c', msgutil.receive_message(request))
-
- def test_receive_message_erroneous_unicode(self):
- # \x80 and \x81 are invalid as UTF-8.
- request = _create_request_hixie75('\x00\x80\x81\xff')
- # Invalid characters should be replaced with
- # U+fffd REPLACEMENT CHARACTER
- self.assertEqual(u'\ufffd\ufffd', msgutil.receive_message(request))
-
- def test_receive_message_discard(self):
- request = _create_request_hixie75('\x80\x06IGNORE\x00Hello\xff'
- '\x01DISREGARD\xff\x00World!\xff')
- self.assertEqual('Hello', msgutil.receive_message(request))
- self.assertEqual('World!', msgutil.receive_message(request))
-
-
-class MessageReceiverTest(unittest.TestCase):
- """Tests the Stream class using MessageReceiver."""
-
- def test_queue(self):
- request = _create_blocking_request()
- receiver = msgutil.MessageReceiver(request)
-
- self.assertEqual(None, receiver.receive_nowait())
-
- request.connection.put_bytes('\x81\x86' + _mask_hybi('Hello!'))
- self.assertEqual('Hello!', receiver.receive())
-
- def test_onmessage(self):
- onmessage_queue = Queue.Queue()
-
- def onmessage_handler(message):
- onmessage_queue.put(message)
-
- request = _create_blocking_request()
- receiver = msgutil.MessageReceiver(request, onmessage_handler)
-
- request.connection.put_bytes('\x81\x86' + _mask_hybi('Hello!'))
- self.assertEqual('Hello!', onmessage_queue.get())
-
-
-class MessageReceiverHixie75Test(unittest.TestCase):
- """Tests the StreamHixie75 class using MessageReceiver."""
-
- def test_queue(self):
- request = _create_blocking_request_hixie75()
- receiver = msgutil.MessageReceiver(request)
-
- self.assertEqual(None, receiver.receive_nowait())
-
- request.connection.put_bytes('\x00Hello!\xff')
- self.assertEqual('Hello!', receiver.receive())
-
- def test_onmessage(self):
- onmessage_queue = Queue.Queue()
-
- def onmessage_handler(message):
- onmessage_queue.put(message)
-
- request = _create_blocking_request_hixie75()
- receiver = msgutil.MessageReceiver(request, onmessage_handler)
-
- request.connection.put_bytes('\x00Hello!\xff')
- self.assertEqual('Hello!', onmessage_queue.get())
-
-
-class MessageSenderTest(unittest.TestCase):
- """Tests the Stream class using MessageSender."""
-
- def test_send(self):
- request = _create_blocking_request()
- sender = msgutil.MessageSender(request)
-
- sender.send('World')
- self.assertEqual('\x81\x05World', request.connection.written_data())
-
- def test_send_nowait(self):
- # Use a queue to check the bytes written by MessageSender.
- # request.connection.written_data() cannot be used here because
- # MessageSender runs in a separate thread.
- send_queue = Queue.Queue()
-
- def write(bytes):
- send_queue.put(bytes)
-
- request = _create_blocking_request()
- request.connection.write = write
-
- sender = msgutil.MessageSender(request)
-
- sender.send_nowait('Hello')
- sender.send_nowait('World')
- self.assertEqual('\x81\x05Hello', send_queue.get())
- self.assertEqual('\x81\x05World', send_queue.get())
-
-
-class MessageSenderHixie75Test(unittest.TestCase):
- """Tests the StreamHixie75 class using MessageSender."""
-
- def test_send(self):
- request = _create_blocking_request_hixie75()
- sender = msgutil.MessageSender(request)
-
- sender.send('World')
- self.assertEqual('\x00World\xff', request.connection.written_data())
-
- def test_send_nowait(self):
- # Use a queue to check the bytes written by MessageSender.
- # request.connection.written_data() cannot be used here because
- # MessageSender runs in a separate thread.
- send_queue = Queue.Queue()
-
- def write(bytes):
- send_queue.put(bytes)
-
- request = _create_blocking_request_hixie75()
- request.connection.write = write
-
- sender = msgutil.MessageSender(request)
-
- sender.send_nowait('Hello')
- sender.send_nowait('World')
- self.assertEqual('\x00Hello\xff', send_queue.get())
- self.assertEqual('\x00World\xff', send_queue.get())
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_mux.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_mux.py
deleted file mode 100644
index d4598944e..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_mux.py
+++ /dev/null
@@ -1,2089 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-
-"""Tests for mux module."""
-
-import Queue
-import copy
-import logging
-import optparse
-import struct
-import sys
-import unittest
-import time
-import zlib
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import common
-from mod_pywebsocket import mux
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import UnsupportedFrameException
-from mod_pywebsocket._stream_hybi import Frame
-from mod_pywebsocket._stream_hybi import Stream
-from mod_pywebsocket._stream_hybi import StreamOptions
-from mod_pywebsocket._stream_hybi import create_binary_frame
-from mod_pywebsocket._stream_hybi import create_close_frame
-from mod_pywebsocket._stream_hybi import create_closing_handshake_body
-from mod_pywebsocket._stream_hybi import parse_frame
-from mod_pywebsocket.extensions import MuxExtensionProcessor
-
-
-import mock
-
-
-_TEST_HEADERS = {'Host': 'server.example.com',
- 'Upgrade': 'websocket',
- 'Connection': 'Upgrade',
- 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
- 'Sec-WebSocket-Version': '13',
- 'Origin': 'http://example.com'}
-
-
-class _OutgoingChannelData(object):
- def __init__(self):
- self.messages = []
- self.control_messages = []
-
- self.builder = mux._InnerMessageBuilder()
-
-class _MockMuxConnection(mock.MockBlockingConn):
- """Mock class of mod_python connection for mux."""
-
- def __init__(self):
- mock.MockBlockingConn.__init__(self)
- self._control_blocks = []
- self._channel_data = {}
-
- self._current_opcode = None
- self._pending_fragments = []
-
- self.server_close_code = None
-
- def write(self, data):
- """Override MockBlockingConn.write."""
-
- self._current_data = data
- self._position = 0
-
- def _receive_bytes(length):
- if self._position + length > len(self._current_data):
- raise ConnectionTerminatedException(
- 'Failed to receive %d bytes from encapsulated '
- 'frame' % length)
- data = self._current_data[self._position:self._position+length]
- self._position += length
- return data
-
- # Parse physical frames and assemble a message if the message is
- # fragmented.
- opcode, payload, fin, rsv1, rsv2, rsv3 = (
- parse_frame(_receive_bytes, unmask_receive=False))
-
- self._pending_fragments.append(payload)
-
- if self._current_opcode is None:
- if opcode == common.OPCODE_CONTINUATION:
- raise Exception('Sending invalid continuation opcode')
- self._current_opcode = opcode
- else:
- if opcode != common.OPCODE_CONTINUATION:
- raise Exception('Sending invalid opcode %d' % opcode)
- if not fin:
- return
-
- inner_frame_data = ''.join(self._pending_fragments)
- self._pending_fragments = []
- self._current_opcode = None
-
- # Handle a control message on the physical channel.
- # TODO(bashi): Support other opcodes if needed.
- if opcode == common.OPCODE_CLOSE:
- if len(payload) >= 2:
- self.server_close_code = struct.unpack('!H', payload[:2])[0]
- close_body = create_closing_handshake_body(
- common.STATUS_NORMAL_CLOSURE, '')
- close_frame = create_close_frame(close_body, mask=True)
- self.put_bytes(close_frame)
- return
-
- # Parse the payload of the message on physical channel.
- parser = mux._MuxFramePayloadParser(inner_frame_data)
- channel_id = parser.read_channel_id()
- if channel_id == mux._CONTROL_CHANNEL_ID:
- self._control_blocks.extend(list(parser.read_control_blocks()))
- return
-
- if not channel_id in self._channel_data:
- self._channel_data[channel_id] = _OutgoingChannelData()
- channel_data = self._channel_data[channel_id]
-
- # Parse logical frames and assemble an inner (logical) message.
- (inner_fin, inner_rsv1, inner_rsv2, inner_rsv3, inner_opcode,
- inner_payload) = parser.read_inner_frame()
- inner_frame = Frame(inner_fin, inner_rsv1, inner_rsv2, inner_rsv3,
- inner_opcode, inner_payload)
- message = channel_data.builder.build(inner_frame)
- if message is None:
- return
-
- if (message.opcode == common.OPCODE_TEXT or
- message.opcode == common.OPCODE_BINARY):
- channel_data.messages.append(message.payload)
-
- self.on_data_message(message.payload)
- else:
- channel_data.control_messages.append(
- {'opcode': message.opcode,
- 'message': message.payload})
-
- def on_data_message(self, message):
- pass
-
- def get_written_control_blocks(self):
- return self._control_blocks
-
- def get_written_messages(self, channel_id):
- return self._channel_data[channel_id].messages
-
- def get_written_control_messages(self, channel_id):
- return self._channel_data[channel_id].control_messages
-
-
-class _FailOnWriteConnection(_MockMuxConnection):
- """Specicialized version of _MockMuxConnection. Its write() method raises
- an exception for testing when a data message is written.
- """
-
- def on_data_message(self, message):
- """Override to raise an exception."""
-
- raise Exception('Intentional failure')
-
-
-class _ChannelEvent(object):
- """A structure that records channel events."""
-
- def __init__(self):
- self.request = None
- self.messages = []
- self.exception = None
- self.client_initiated_closing = False
-
-
-class _MuxMockDispatcher(object):
- """Mock class of dispatch.Dispatcher for mux."""
-
- def __init__(self):
- self.channel_events = {}
-
- def do_extra_handshake(self, request):
- if request.ws_requested_protocols is not None:
- request.ws_protocol = request.ws_requested_protocols[0]
-
- def _do_echo(self, request, channel_events):
- while True:
- message = request.ws_stream.receive_message()
- if message == None:
- channel_events.client_initiated_closing = True
- return
- if message == 'Goodbye':
- return
- channel_events.messages.append(message)
- # echo back
- request.ws_stream.send_message(message)
-
- def _do_ping(self, request, channel_events):
- request.ws_stream.send_ping('Ping!')
-
- def _do_ping_while_hello_world(self, request, channel_events):
- request.ws_stream.send_message('Hello ', end=False)
- request.ws_stream.send_ping('Ping!')
- request.ws_stream.send_message('World!', end=True)
-
- def _do_two_ping_while_hello_world(self, request, channel_events):
- request.ws_stream.send_message('Hello ', end=False)
- request.ws_stream.send_ping('Ping!')
- request.ws_stream.send_ping('Pong!')
- request.ws_stream.send_message('World!', end=True)
-
- def transfer_data(self, request):
- self.channel_events[request.channel_id] = _ChannelEvent()
- self.channel_events[request.channel_id].request = request
-
- try:
- # Note: more handler will be added.
- if request.uri.endswith('echo'):
- self._do_echo(request,
- self.channel_events[request.channel_id])
- elif request.uri.endswith('ping'):
- self._do_ping(request,
- self.channel_events[request.channel_id])
- elif request.uri.endswith('two_ping_while_hello_world'):
- self._do_two_ping_while_hello_world(
- request, self.channel_events[request.channel_id])
- elif request.uri.endswith('ping_while_hello_world'):
- self._do_ping_while_hello_world(
- request, self.channel_events[request.channel_id])
- else:
- raise ValueError('Cannot handle path %r' % request.path)
- if not request.server_terminated:
- request.ws_stream.close_connection()
- except ConnectionTerminatedException, e:
- self.channel_events[request.channel_id].exception = e
- except Exception, e:
- self.channel_events[request.channel_id].exception = e
- raise
-
-
-def _create_mock_request(connection=None, logical_channel_extensions=None):
- if connection is None:
- connection = _MockMuxConnection()
-
- request = mock.MockRequest(uri='/echo',
- headers_in=_TEST_HEADERS,
- connection=connection)
- request.ws_stream = Stream(request, options=StreamOptions())
- request.mux_processor = MuxExtensionProcessor(
- common.ExtensionParameter(common.MUX_EXTENSION))
- if logical_channel_extensions is not None:
- request.mux_processor.set_extensions(logical_channel_extensions)
- request.mux_processor.set_quota(8 * 1024)
- return request
-
-
-def _create_add_channel_request_frame(channel_id, encoding, encoded_handshake):
- # Allow invalid encoding for testing.
- first_byte = ((mux._MUX_OPCODE_ADD_CHANNEL_REQUEST << 5) | encoding)
- payload = (chr(first_byte) +
- mux._encode_channel_id(channel_id) +
- mux._encode_number(len(encoded_handshake)) +
- encoded_handshake)
- return create_binary_frame(
- (mux._encode_channel_id(mux._CONTROL_CHANNEL_ID) + payload), mask=True)
-
-
-def _create_drop_channel_frame(channel_id, code=None, message=''):
- payload = mux._create_drop_channel(channel_id, code, message)
- return create_binary_frame(
- (mux._encode_channel_id(mux._CONTROL_CHANNEL_ID) + payload), mask=True)
-
-
-def _create_flow_control_frame(channel_id, replenished_quota):
- payload = mux._create_flow_control(channel_id, replenished_quota)
- return create_binary_frame(
- (mux._encode_channel_id(mux._CONTROL_CHANNEL_ID) + payload), mask=True)
-
-
-def _create_logical_frame(channel_id, message, opcode=common.OPCODE_BINARY,
- fin=True, rsv1=False, rsv2=False, rsv3=False,
- mask=True):
- bits = chr((fin << 7) | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4) | opcode)
- payload = mux._encode_channel_id(channel_id) + bits + message
- return create_binary_frame(payload, mask=True)
-
-
-def _create_request_header(path='/echo', extensions=None):
- headers = (
- 'GET %s HTTP/1.1\r\n'
- 'Host: server.example.com\r\n'
- 'Connection: Upgrade\r\n'
- 'Origin: http://example.com\r\n') % path
- if extensions:
- headers += '%s: %s' % (
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER, extensions)
- return headers
-
-
-class MuxTest(unittest.TestCase):
- """A unittest for mux module."""
-
- def test_channel_id_decode(self):
- data = '\x00\x01\xbf\xff\xdf\xff\xff\xff\xff\xff\xff'
- parser = mux._MuxFramePayloadParser(data)
- channel_id = parser.read_channel_id()
- self.assertEqual(0, channel_id)
- channel_id = parser.read_channel_id()
- self.assertEqual(1, channel_id)
- channel_id = parser.read_channel_id()
- self.assertEqual(2 ** 14 - 1, channel_id)
- channel_id = parser.read_channel_id()
- self.assertEqual(2 ** 21 - 1, channel_id)
- channel_id = parser.read_channel_id()
- self.assertEqual(2 ** 29 - 1, channel_id)
- self.assertEqual(len(data), parser._read_position)
-
- def test_channel_id_encode(self):
- encoded = mux._encode_channel_id(0)
- self.assertEqual('\x00', encoded)
- encoded = mux._encode_channel_id(2 ** 14 - 1)
- self.assertEqual('\xbf\xff', encoded)
- encoded = mux._encode_channel_id(2 ** 14)
- self.assertEqual('\xc0@\x00', encoded)
- encoded = mux._encode_channel_id(2 ** 21 - 1)
- self.assertEqual('\xdf\xff\xff', encoded)
- encoded = mux._encode_channel_id(2 ** 21)
- self.assertEqual('\xe0 \x00\x00', encoded)
- encoded = mux._encode_channel_id(2 ** 29 - 1)
- self.assertEqual('\xff\xff\xff\xff', encoded)
- # channel_id is too large
- self.assertRaises(ValueError,
- mux._encode_channel_id,
- 2 ** 29)
-
- def test_read_multiple_control_blocks(self):
- # Use AddChannelRequest because it can contain arbitrary length of data
- data = ('\x00\x01\x01a'
- '\x00\x02\x7d%s'
- '\x00\x03\x7e\xff\xff%s'
- '\x00\x04\x7f\x00\x00\x00\x00\x00\x01\x00\x00%s') % (
- 'a' * 0x7d, 'b' * 0xffff, 'c' * 0x10000)
- parser = mux._MuxFramePayloadParser(data)
- blocks = list(parser.read_control_blocks())
- self.assertEqual(4, len(blocks))
-
- self.assertEqual(mux._MUX_OPCODE_ADD_CHANNEL_REQUEST, blocks[0].opcode)
- self.assertEqual(1, blocks[0].channel_id)
- self.assertEqual(1, len(blocks[0].encoded_handshake))
-
- self.assertEqual(mux._MUX_OPCODE_ADD_CHANNEL_REQUEST, blocks[1].opcode)
- self.assertEqual(2, blocks[1].channel_id)
- self.assertEqual(0x7d, len(blocks[1].encoded_handshake))
-
- self.assertEqual(mux._MUX_OPCODE_ADD_CHANNEL_REQUEST, blocks[2].opcode)
- self.assertEqual(3, blocks[2].channel_id)
- self.assertEqual(0xffff, len(blocks[2].encoded_handshake))
-
- self.assertEqual(mux._MUX_OPCODE_ADD_CHANNEL_REQUEST, blocks[3].opcode)
- self.assertEqual(4, blocks[3].channel_id)
- self.assertEqual(0x10000, len(blocks[3].encoded_handshake))
-
- self.assertEqual(len(data), parser._read_position)
-
- def test_read_add_channel_request(self):
- data = '\x00\x01\x01a'
- parser = mux._MuxFramePayloadParser(data)
- blocks = list(parser.read_control_blocks())
- self.assertEqual(mux._MUX_OPCODE_ADD_CHANNEL_REQUEST, blocks[0].opcode)
- self.assertEqual(1, blocks[0].channel_id)
- self.assertEqual(1, len(blocks[0].encoded_handshake))
-
- def test_read_drop_channel(self):
- data = '\x60\x01\x00'
- parser = mux._MuxFramePayloadParser(data)
- blocks = list(parser.read_control_blocks())
- self.assertEqual(1, len(blocks))
- self.assertEqual(1, blocks[0].channel_id)
- self.assertEqual(mux._MUX_OPCODE_DROP_CHANNEL, blocks[0].opcode)
- self.assertEqual(None, blocks[0].drop_code)
- self.assertEqual(0, len(blocks[0].drop_message))
-
- data = '\x60\x02\x09\x03\xe8Success'
- parser = mux._MuxFramePayloadParser(data)
- blocks = list(parser.read_control_blocks())
- self.assertEqual(1, len(blocks))
- self.assertEqual(2, blocks[0].channel_id)
- self.assertEqual(mux._MUX_OPCODE_DROP_CHANNEL, blocks[0].opcode)
- self.assertEqual(1000, blocks[0].drop_code)
- self.assertEqual('Success', blocks[0].drop_message)
-
- # Reason is too short.
- data = '\x60\x01\x01\x00'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(mux.PhysicalConnectionError,
- lambda: list(parser.read_control_blocks()))
-
- def test_read_flow_control(self):
- data = '\x40\x01\x02'
- parser = mux._MuxFramePayloadParser(data)
- blocks = list(parser.read_control_blocks())
- self.assertEqual(1, len(blocks))
- self.assertEqual(1, blocks[0].channel_id)
- self.assertEqual(mux._MUX_OPCODE_FLOW_CONTROL, blocks[0].opcode)
- self.assertEqual(2, blocks[0].send_quota)
-
- def test_read_new_channel_slot(self):
- data = '\x80\x01\x02\x02\x03'
- parser = mux._MuxFramePayloadParser(data)
- # TODO(bashi): Implement
- self.assertRaises(mux.PhysicalConnectionError,
- lambda: list(parser.read_control_blocks()))
-
- def test_read_invalid_number_field_in_control_block(self):
- # No number field.
- data = ''
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- # The last two bytes are missing.
- data = '\x7e'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- # Missing the last one byte.
- data = '\x7f\x00\x00\x00\x00\x00\x01\x00'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- # The length of number field is too large.
- data = '\x7f\xff\xff\xff\xff\xff\xff\xff\xff'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- # The msb of the first byte is set.
- data = '\x80'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- # Using 3 bytes encoding for 125.
- data = '\x7e\x00\x7d'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- # Using 9 bytes encoding for 0xffff
- data = '\x7f\x00\x00\x00\x00\x00\x00\xff\xff'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(ValueError, parser._read_number)
-
- def test_read_invalid_size_and_contents(self):
- # Only contain number field.
- data = '\x01'
- parser = mux._MuxFramePayloadParser(data)
- self.assertRaises(mux.PhysicalConnectionError,
- parser._read_size_and_contents)
-
- def test_create_add_channel_response(self):
- data = mux._create_add_channel_response(channel_id=1,
- encoded_handshake='FooBar',
- encoding=0,
- rejected=False)
- self.assertEqual('\x20\x01\x06FooBar', data)
-
- data = mux._create_add_channel_response(channel_id=2,
- encoded_handshake='Hello',
- encoding=1,
- rejected=True)
- self.assertEqual('\x31\x02\x05Hello', data)
-
- def test_create_drop_channel(self):
- data = mux._create_drop_channel(channel_id=1)
- self.assertEqual('\x60\x01\x00', data)
-
- data = mux._create_drop_channel(channel_id=1,
- code=2000,
- message='error')
- self.assertEqual('\x60\x01\x07\x07\xd0error', data)
-
- # reason must be empty if code is None
- self.assertRaises(ValueError,
- mux._create_drop_channel,
- 1, None, 'FooBar')
-
- def test_parse_request_text(self):
- request_text = _create_request_header()
- command, path, version, headers = mux._parse_request_text(request_text)
- self.assertEqual('GET', command)
- self.assertEqual('/echo', path)
- self.assertEqual('HTTP/1.1', version)
- self.assertEqual(3, len(headers))
- self.assertEqual('server.example.com', headers['Host'])
- self.assertEqual('http://example.com', headers['Origin'])
-
-
-class MuxHandlerTest(unittest.TestCase):
-
- def test_add_channel(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=3, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=3,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='World'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual([], dispatcher.channel_events[1].messages)
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- self.assertEqual(['World'], dispatcher.channel_events[3].messages)
- # Channel 2
- messages = request.connection.get_written_messages(2)
- self.assertEqual(1, len(messages))
- self.assertEqual('Hello', messages[0])
- # Channel 3
- messages = request.connection.get_written_messages(3)
- self.assertEqual(1, len(messages))
- self.assertEqual('World', messages[0])
- control_blocks = request.connection.get_written_control_blocks()
- # There should be 8 control blocks:
- # - 1 NewChannelSlot
- # - 2 AddChannelResponses for channel id 2 and 3
- # - 6 FlowControls for channel id 1 (initialize), 'Hello', 'World',
- # and 3 'Goodbye's
- self.assertEqual(9, len(control_blocks))
-
- def test_physical_connection_write_failure(self):
- # Use _FailOnWriteConnection.
- request = _create_mock_request(connection=_FailOnWriteConnection())
-
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- # Let the worker echo back 'Hello'. It causes _FailOnWriteConnection
- # raising an exception.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Hello'))
-
- # Let the worker exit. This will be unnecessary when
- # _LogicalConnection.write() is changed to throw an exception if
- # woke up by on_writer_done.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- # All threads should be done.
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- def test_send_blocked(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # On receiving this 'Hello', the server tries to echo back 'Hello',
- # but it will be blocked since there's no send quota available for the
- # channel 2.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
-
- # Wait until the worker is blocked due to send quota shortage.
- time.sleep(1)
-
- # Close the channel 2. The worker should be notified of the end of
- # writer thread and stop waiting for send quota to be replenished.
- drop_channel = _create_drop_channel_frame(channel_id=2)
-
- request.connection.put_bytes(drop_channel)
-
- # Make sure the channel 1 is also closed.
- drop_channel = _create_drop_channel_frame(channel_id=1)
- request.connection.put_bytes(drop_channel)
-
- # All threads should be done.
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- def test_add_channel_delta_encoding(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- delta = 'GET /echo HTTP/1.1\r\n\r\n'
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=1, encoded_handshake=delta)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- messages = request.connection.get_written_messages(2)
- self.assertEqual(1, len(messages))
- self.assertEqual('Hello', messages[0])
-
- def test_add_channel_delta_encoding_override(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- # Override Sec-WebSocket-Protocol.
- delta = ('GET /echo HTTP/1.1\r\n'
- 'Sec-WebSocket-Protocol: x-foo\r\n'
- '\r\n')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=1, encoded_handshake=delta)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- messages = request.connection.get_written_messages(2)
- self.assertEqual(1, len(messages))
- self.assertEqual('Hello', messages[0])
- self.assertEqual('x-foo',
- dispatcher.channel_events[2].request.ws_protocol)
-
- def test_add_channel_delta_after_identity(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
- # Sec-WebSocket-Protocol is different from client's opening handshake
- # of the physical connection.
- # TODO(bashi): Remove Upgrade, Connection, Sec-WebSocket-Key and
- # Sec-WebSocket-Version.
- encoded_handshake = (
- 'GET /echo HTTP/1.1\r\n'
- 'Host: server.example.com\r\n'
- 'Sec-WebSocket-Protocol: x-foo\r\n'
- 'Connection: Upgrade\r\n'
- 'Origin: http://example.com\r\n'
- '\r\n')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- delta = 'GET /echo HTTP/1.1\r\n\r\n'
- add_channel_request = _create_add_channel_request_frame(
- channel_id=3, encoding=1, encoded_handshake=delta)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=3,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='World'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual([], dispatcher.channel_events[1].messages)
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- self.assertEqual(['World'], dispatcher.channel_events[3].messages)
- # Channel 2
- messages = request.connection.get_written_messages(2)
- self.assertEqual(1, len(messages))
- self.assertEqual('Hello', messages[0])
- # Channel 3
- messages = request.connection.get_written_messages(3)
- self.assertEqual(1, len(messages))
- self.assertEqual('World', messages[0])
- # Handshake base should be updated.
- self.assertEqual(
- 'x-foo',
- mux_handler._handshake_base._headers['Sec-WebSocket-Protocol'])
-
- def test_add_channel_delta_remove_header(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
- # Override handshake delta base.
- encoded_handshake = (
- 'GET /echo HTTP/1.1\r\n'
- 'Host: server.example.com\r\n'
- 'Sec-WebSocket-Protocol: x-foo\r\n'
- 'Connection: Upgrade\r\n'
- 'Origin: http://example.com\r\n'
- '\r\n')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- # Remove Sec-WebSocket-Protocol header.
- delta = ('GET /echo HTTP/1.1\r\n'
- 'Sec-WebSocket-Protocol:'
- '\r\n')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=3, encoding=1, encoded_handshake=delta)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=3,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='World'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual([], dispatcher.channel_events[1].messages)
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- self.assertEqual(['World'], dispatcher.channel_events[3].messages)
- # Channel 2
- messages = request.connection.get_written_messages(2)
- self.assertEqual(1, len(messages))
- self.assertEqual('Hello', messages[0])
- # Channel 3
- messages = request.connection.get_written_messages(3)
- self.assertEqual(1, len(messages))
- self.assertEqual('World', messages[0])
- self.assertEqual(
- 'x-foo',
- dispatcher.channel_events[2].request.ws_protocol)
- self.assertEqual(
- None,
- dispatcher.channel_events[3].request.ws_protocol)
-
- def test_add_channel_delta_encoding_permessage_compress(self):
- # Enable permessage compress extension on the implicitly opened channel.
- extensions = common.parse_extensions(
- '%s; method=deflate' % common.PERMESSAGE_COMPRESSION_EXTENSION)
- request = _create_mock_request(
- logical_channel_extensions=extensions)
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- delta = 'GET /echo HTTP/1.1\r\n\r\n'
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=1, encoded_handshake=delta)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=20)
- request.connection.put_bytes(flow_control)
-
- # Send compressed 'Hello' on logical channel 1 and 2.
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message=compressed_hello,
- rsv1=True))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message=compressed_hello,
- rsv1=True))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual(['Hello'], dispatcher.channel_events[1].messages)
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- # Written 'Hello's should be compressed.
- messages = request.connection.get_written_messages(1)
- self.assertEqual(1, len(messages))
- self.assertEqual(compressed_hello, messages[0])
- messages = request.connection.get_written_messages(2)
- self.assertEqual(1, len(messages))
- self.assertEqual(compressed_hello, messages[0])
-
- def test_add_channel_delta_encoding_remove_extensions(self):
- # Enable permessage compress extension on the implicitly opened channel.
- extensions = common.parse_extensions(
- '%s; method=deflate' % common.PERMESSAGE_COMPRESSION_EXTENSION)
- request = _create_mock_request(
- logical_channel_extensions=extensions)
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- # Remove permessage compress extension.
- delta = ('GET /echo HTTP/1.1\r\n'
- 'Sec-WebSocket-Extensions:\r\n'
- '\r\n')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=1, encoded_handshake=delta)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=20)
- request.connection.put_bytes(flow_control)
-
- # Send compressed message on logical channel 2. The message should
- # be rejected (since rsv1 is set).
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('Hello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message=compressed_hello,
- rsv1=True))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_NORMAL_CLOSURE, drop_channel.drop_code)
- self.assertEqual(2, drop_channel.channel_id)
- # UnsupportedFrameException should be raised on logical channel 2.
- self.assertTrue(isinstance(dispatcher.channel_events[2].exception,
- UnsupportedFrameException))
-
- def test_add_channel_invalid_encoding(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=3,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_UNKNOWN_REQUEST_ENCODING,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_add_channel_incomplete_handshake(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- incomplete_encoded_handshake = 'GET /echo HTTP/1.1'
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=incomplete_encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertTrue(1 in dispatcher.channel_events)
- self.assertTrue(not 2 in dispatcher.channel_events)
-
- def test_add_channel_duplicate_channel_id(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_CHANNEL_ALREADY_EXISTS,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_receive_drop_channel(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- drop_channel = _create_drop_channel_frame(channel_id=2)
- request.connection.put_bytes(drop_channel)
-
- # Terminate implicitly opened channel.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- exception = dispatcher.channel_events[2].exception
- self.assertTrue(exception.__class__ == ConnectionTerminatedException)
-
- def test_receive_ping_frame(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=13)
- request.connection.put_bytes(flow_control)
-
- ping_frame = _create_logical_frame(channel_id=2,
- message='Hello World!',
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(ping_frame)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PONG, messages[0]['opcode'])
- self.assertEqual('Hello World!', messages[0]['message'])
-
- def test_receive_fragmented_ping(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=13)
- request.connection.put_bytes(flow_control)
-
- # Send a ping with message 'Hello world!' in two fragmented frames.
- ping_frame1 = _create_logical_frame(channel_id=2,
- message='Hello ',
- fin=False,
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(ping_frame1)
- ping_frame2 = _create_logical_frame(channel_id=2,
- message='World!',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(ping_frame2)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PONG, messages[0]['opcode'])
- self.assertEqual('Hello World!', messages[0]['message'])
-
- def test_receive_fragmented_ping_while_receiving_fragmented_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=19)
- request.connection.put_bytes(flow_control)
-
- # Send a fragmented frame of message 'Hello '.
- hello = _create_logical_frame(channel_id=2,
- message='Hello ',
- fin=False)
- request.connection.put_bytes(hello)
-
- # Before sending the last fragmented frame of the message, send a
- # fragmented ping.
- ping1 = _create_logical_frame(channel_id=2,
- message='Pi',
- fin=False,
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(ping1)
- ping2 = _create_logical_frame(channel_id=2,
- message='ng!',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(ping2)
-
- # Send the last fragmented frame of the message.
- world = _create_logical_frame(channel_id=2,
- message='World!',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(world)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['Hello World!'], messages)
- control_messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PONG, control_messages[0]['opcode'])
- self.assertEqual('Ping!', control_messages[0]['message'])
-
- def test_receive_two_ping_while_receiving_fragmented_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=25)
- request.connection.put_bytes(flow_control)
-
- # Send a fragmented frame of message 'Hello '.
- hello = _create_logical_frame(channel_id=2,
- message='Hello ',
- fin=False)
- request.connection.put_bytes(hello)
-
- # Before sending the last fragmented frame of the message, send a
- # fragmented ping and a non-fragmented ping.
- ping1 = _create_logical_frame(channel_id=2,
- message='Pi',
- fin=False,
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(ping1)
- ping2 = _create_logical_frame(channel_id=2,
- message='ng!',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(ping2)
- ping3 = _create_logical_frame(channel_id=2,
- message='Pong!',
- fin=True,
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(ping3)
-
- # Send the last fragmented frame of the message.
- world = _create_logical_frame(channel_id=2,
- message='World!',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(world)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['Hello World!'], messages)
- control_messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PONG, control_messages[0]['opcode'])
- self.assertEqual('Ping!', control_messages[0]['message'])
- self.assertEqual(common.OPCODE_PONG, control_messages[1]['opcode'])
- self.assertEqual('Pong!', control_messages[1]['message'])
-
- def test_receive_message_while_receiving_fragmented_ping(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=19)
- request.connection.put_bytes(flow_control)
-
- # Send a fragmented ping.
- ping1 = _create_logical_frame(channel_id=2,
- message='Pi',
- fin=False,
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(ping1)
-
- # Before sending the last fragmented ping, send a message.
- # The logical channel (2) should be dropped.
- message = _create_logical_frame(channel_id=2,
- message='Hello world!',
- fin=True)
- request.connection.put_bytes(message)
-
- # Send the last fragmented frame of the message.
- ping2 = _create_logical_frame(channel_id=2,
- message='ng!',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(ping2)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(2, drop_channel.channel_id)
- # No message should be sent on channel 2.
- self.assertRaises(KeyError,
- request.connection.get_written_messages,
- 2)
- self.assertRaises(KeyError,
- request.connection.get_written_control_messages,
- 2)
-
- def test_send_ping(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/ping')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PING, messages[0]['opcode'])
- self.assertEqual('Ping!', messages[0]['message'])
-
- def test_send_fragmented_ping(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/ping')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Replenish 3 bytes. This isn't enough to send the whole ping frame
- # because the frame will have 5 bytes message('Ping!'). The frame
- # should be fragmented.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=3)
- request.connection.put_bytes(flow_control)
-
- # Wait until the worker is blocked due to send quota shortage.
- time.sleep(1)
-
- # Replenish remaining 2 + 1 bytes (including extra cost).
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=3)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PING, messages[0]['opcode'])
- self.assertEqual('Ping!', messages[0]['message'])
-
- def test_send_fragmented_ping_while_sending_fragmented_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(
- path='/ping_while_hello_world')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Application will send:
- # - text message 'Hello ' with fin=0
- # - ping with 'Ping!' message
- # - text message 'World!' with fin=1
- # Replenish (6 + 1) + (2 + 1) bytes so that the ping will be
- # fragmented on the logical channel.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=10)
- request.connection.put_bytes(flow_control)
-
- time.sleep(1)
-
- # Replenish remaining 3 + 6 bytes.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=9)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['Hello World!'], messages)
- control_messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PING, control_messages[0]['opcode'])
- self.assertEqual('Ping!', control_messages[0]['message'])
-
- def test_send_fragmented_two_ping_while_sending_fragmented_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(
- path='/two_ping_while_hello_world')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Application will send:
- # - text message 'Hello ' with fin=0
- # - ping with 'Ping!' message
- # - ping with 'Pong!' message
- # - text message 'World!' with fin=1
- # Replenish (6 + 1) + (2 + 1) bytes so that the first ping will be
- # fragmented on the logical channel.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=10)
- request.connection.put_bytes(flow_control)
-
- time.sleep(1)
-
- # Replenish remaining 3 + (5 + 1) + 6 bytes. The second ping won't
- # be fragmented on the logical channel.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=15)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['Hello World!'], messages)
- control_messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PING, control_messages[0]['opcode'])
- self.assertEqual('Ping!', control_messages[0]['message'])
- self.assertEqual(common.OPCODE_PING, control_messages[1]['opcode'])
- self.assertEqual('Pong!', control_messages[1]['message'])
-
- def test_send_drop_channel(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- # DropChannel for channel id 1 which doesn't have reason.
- frame = create_binary_frame('\x00\x60\x01\x00', mask=True)
- request.connection.put_bytes(frame)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_ACKNOWLEDGED,
- drop_channel.drop_code)
- self.assertEqual(1, drop_channel.channel_id)
-
- def test_two_flow_control(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Replenish 5 bytes.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=5)
- request.connection.put_bytes(flow_control)
-
- # Send 10 bytes. The server will try echo back 10 bytes.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='HelloWorld'))
-
- # Replenish 5 + 1 (per-message extra cost) bytes.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['HelloWorld'], messages)
- received_flow_controls = [
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_FLOW_CONTROL and b.channel_id == 2]
- # Replenishment for 'HelloWorld' + 1
- self.assertEqual(11, received_flow_controls[0].send_quota)
- # Replenishment for 'Goodbye' + 1
- self.assertEqual(8, received_flow_controls[1].send_quota)
-
- def test_no_send_quota_on_server(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='HelloWorld'))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- # Just wait for 1 sec so that the server attempts to echo back
- # 'HelloWorld'.
- self.assertFalse(mux_handler.wait_until_done(timeout=1))
-
- # No message should be sent on channel 2.
- self.assertRaises(KeyError,
- request.connection.get_written_messages,
- 2)
-
- def test_no_send_quota_on_server_for_permessage_extra_cost(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
- # Replenish only len('World') bytes.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=5)
- request.connection.put_bytes(flow_control)
- # Server should not callback for this message.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='World'))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- # Just wait for 1 sec so that the server attempts to echo back
- # 'World'.
- self.assertFalse(mux_handler.wait_until_done(timeout=1))
-
- # Only one message should be sent on channel 2.
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['Hello'], messages)
-
- def test_quota_violation_by_client(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS, 0)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='HelloWorld'))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- control_blocks = request.connection.get_written_control_blocks()
- self.assertEqual(5, len(control_blocks))
- drop_channel = next(
- b for b in control_blocks
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_SEND_QUOTA_VIOLATION,
- drop_channel.drop_code)
-
- def test_consume_quota_empty_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- # Client has 1 byte quota.
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS, 1)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=2)
- request.connection.put_bytes(flow_control)
- # Send an empty message. Pywebsocket always replenishes 1 byte quota
- # for empty message
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message=''))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- # This message violates quota on channel id 2.
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual(1, len(dispatcher.channel_events[2].messages))
- self.assertEqual('', dispatcher.channel_events[2].messages[0])
-
- received_flow_controls = [
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_FLOW_CONTROL and b.channel_id == 2]
- self.assertEqual(1, len(received_flow_controls))
- self.assertEqual(1, received_flow_controls[0].send_quota)
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(2, drop_channel.channel_id)
- self.assertEqual(mux._DROP_CODE_SEND_QUOTA_VIOLATION,
- drop_channel.drop_code)
-
- def test_consume_quota_fragmented_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- # Client has len('Hello') + len('Goodbye') + 2 bytes quota.
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS, 14)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='He', fin=False,
- opcode=common.OPCODE_TEXT))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='llo', fin=True,
- opcode=common.OPCODE_CONTINUATION))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_messages(2)
- self.assertEqual(['Hello'], messages)
-
- def test_fragmented_control_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/ping')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Replenish total 6 bytes in 3 FlowControls.
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=1)
- request.connection.put_bytes(flow_control)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=2)
- request.connection.put_bytes(flow_control)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=3)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- messages = request.connection.get_written_control_messages(2)
- self.assertEqual(common.OPCODE_PING, messages[0]['opcode'])
- self.assertEqual('Ping!', messages[0]['message'])
-
- def test_channel_slot_violation_by_client(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(slots=1,
- send_quota=mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Hello'))
-
- # This request should be rejected.
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=3, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
- flow_control = _create_flow_control_frame(channel_id=3,
- replenished_quota=6)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=3, message='Hello'))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual([], dispatcher.channel_events[1].messages)
- self.assertEqual(['Hello'], dispatcher.channel_events[2].messages)
- self.assertFalse(dispatcher.channel_events.has_key(3))
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(3, drop_channel.channel_id)
- self.assertEqual(mux._DROP_CODE_NEW_CHANNEL_SLOT_VIOLATION,
- drop_channel.drop_code)
-
- def test_quota_overflow_by_client(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(slots=1,
- send_quota=mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
- # Replenish 0x7FFFFFFFFFFFFFFF bytes twice.
- flow_control = _create_flow_control_frame(
- channel_id=2,
- replenished_quota=0x7FFFFFFFFFFFFFFF)
- request.connection.put_bytes(flow_control)
- request.connection.put_bytes(flow_control)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(2, drop_channel.channel_id)
- self.assertEqual(mux._DROP_CODE_SEND_QUOTA_OVERFLOW,
- drop_channel.drop_code)
-
- def test_invalid_encapsulated_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- first_byte = (mux._MUX_OPCODE_ADD_CHANNEL_REQUEST << 5)
- block = (chr(first_byte) +
- mux._encode_channel_id(1) +
- mux._encode_number(0))
- payload = mux._encode_channel_id(mux._CONTROL_CHANNEL_ID) + block
- text_frame = create_binary_frame(payload, opcode=common.OPCODE_TEXT,
- mask=True)
- request.connection.put_bytes(text_frame)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_INVALID_ENCAPSULATING_MESSAGE,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_channel_id_truncated(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- # The last byte of the channel id is missing.
- frame = create_binary_frame('\x80', mask=True)
- request.connection.put_bytes(frame)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_CHANNEL_ID_TRUNCATED,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_inner_frame_truncated(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- # Just contain channel id 1.
- frame = create_binary_frame('\x01', mask=True)
- request.connection.put_bytes(frame)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_ENCAPSULATED_FRAME_IS_TRUNCATED,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_unknown_mux_opcode(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- # Undefined opcode 5
- frame = create_binary_frame('\x00\xa0', mask=True)
- request.connection.put_bytes(frame)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_UNKNOWN_MUX_OPCODE,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_invalid_mux_control_block(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
-
- # DropChannel contains 1 byte reason
- frame = create_binary_frame('\x00\x60\x00\x01\x00', mask=True)
- request.connection.put_bytes(frame)
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channel = next(
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL)
- self.assertEqual(mux._DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
- drop_channel.drop_code)
- self.assertEqual(common.STATUS_INTERNAL_ENDPOINT_ERROR,
- request.connection.server_close_code)
-
- def test_permessage_compress(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- # Enable permessage compress extension on logical channel 2.
- extensions = '%s; method=deflate' % (
- common.PERMESSAGE_COMPRESSION_EXTENSION)
- encoded_handshake = _create_request_header(path='/echo',
- extensions=extensions)
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- flow_control = _create_flow_control_frame(channel_id=2,
- replenished_quota=20)
- request.connection.put_bytes(flow_control)
-
- # Send compressed 'Hello' twice.
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello1 = compress.compress('Hello')
- compressed_hello1 += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello1 = compressed_hello1[:-4]
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message=compressed_hello1,
- rsv1=True))
- compressed_hello2 = compress.compress('Hello')
- compressed_hello2 += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello2 = compressed_hello2[:-4]
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message=compressed_hello2,
- rsv1=True))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=2, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual(['Hello', 'Hello'],
- dispatcher.channel_events[2].messages)
- # Written 'Hello's should be compressed.
- messages = request.connection.get_written_messages(2)
- self.assertEqual(2, len(messages))
- self.assertEqual(compressed_hello1, messages[0])
- self.assertEqual(compressed_hello2, messages[1])
-
-
- def test_permessage_compress_fragmented_message(self):
- extensions = common.parse_extensions(
- '%s; method=deflate' % common.PERMESSAGE_COMPRESSION_EXTENSION)
- request = _create_mock_request(
- logical_channel_extensions=extensions)
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- # Send compressed 'HelloHelloHello' as fragmented message.
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
- compressed_hello = compress.compress('HelloHelloHello')
- compressed_hello += compress.flush(zlib.Z_SYNC_FLUSH)
- compressed_hello = compressed_hello[:-4]
-
- m = len(compressed_hello) / 2
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1,
- message=compressed_hello[:m],
- fin=False, rsv1=True,
- opcode=common.OPCODE_TEXT))
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1,
- message=compressed_hello[m:],
- fin=True, rsv1=False,
- opcode=common.OPCODE_CONTINUATION))
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- self.assertEqual(['HelloHelloHello'],
- dispatcher.channel_events[1].messages)
- messages = request.connection.get_written_messages(1)
- self.assertEqual(1, len(messages))
- self.assertEqual(compressed_hello, messages[0])
-
- def test_receive_bad_fragmented_message(self):
- request = _create_mock_request()
- dispatcher = _MuxMockDispatcher()
- mux_handler = mux._MuxHandler(request, dispatcher)
- mux_handler.start()
- mux_handler.add_channel_slots(mux._INITIAL_NUMBER_OF_CHANNEL_SLOTS,
- mux._INITIAL_QUOTA_FOR_CLIENT)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=2, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Send a frame with fin=False, and then send a frame with
- # opcode=TEXT (not CONTINUATION). Logical channel 2 should be dropped.
- frame1 = _create_logical_frame(channel_id=2,
- message='Hello ',
- fin=False,
- opcode=common.OPCODE_TEXT)
- request.connection.put_bytes(frame1)
- frame2 = _create_logical_frame(channel_id=2,
- message='World!',
- fin=True,
- opcode=common.OPCODE_TEXT)
- request.connection.put_bytes(frame2)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=3, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Send a frame with opcode=CONTINUATION without a preceding frame
- # the fin of which is not set. Logical channel 3 should be dropped.
- frame3 = _create_logical_frame(channel_id=3,
- message='Hello',
- fin=True,
- opcode=common.OPCODE_CONTINUATION)
- request.connection.put_bytes(frame3)
-
- encoded_handshake = _create_request_header(path='/echo')
- add_channel_request = _create_add_channel_request_frame(
- channel_id=4, encoding=0,
- encoded_handshake=encoded_handshake)
- request.connection.put_bytes(add_channel_request)
-
- # Send a frame with opcode=PING and fin=False, and then send a frame
- # with opcode=TEXT (not CONTINUATION). Logical channel 4 should be
- # dropped.
- frame4 = _create_logical_frame(channel_id=4,
- message='Ping',
- fin=False,
- opcode=common.OPCODE_PING)
- request.connection.put_bytes(frame4)
- frame5 = _create_logical_frame(channel_id=4,
- message='Hello',
- fin=True,
- opcode=common.OPCODE_TEXT)
- request.connection.put_bytes(frame5)
-
- request.connection.put_bytes(
- _create_logical_frame(channel_id=1, message='Goodbye'))
-
- self.assertTrue(mux_handler.wait_until_done(timeout=2))
-
- drop_channels = [
- b for b in request.connection.get_written_control_blocks()
- if b.opcode == mux._MUX_OPCODE_DROP_CHANNEL]
- self.assertEqual(3, len(drop_channels))
- for d in drop_channels:
- self.assertEqual(mux._DROP_CODE_BAD_FRAGMENTATION,
- d.drop_code)
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_stream.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_stream.py
deleted file mode 100755
index 81acfeb04..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_stream.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for stream module."""
-
-
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import common
-from mod_pywebsocket import stream
-
-
-class StreamTest(unittest.TestCase):
- """A unittest for stream module."""
-
- def test_create_header(self):
- # more, rsv1, ..., rsv4 are all true
- header = stream.create_header(common.OPCODE_TEXT, 1, 1, 1, 1, 1, 1)
- self.assertEqual('\xf1\x81', header)
-
- # Maximum payload size
- header = stream.create_header(
- common.OPCODE_TEXT, (1 << 63) - 1, 0, 0, 0, 0, 0)
- self.assertEqual('\x01\x7f\x7f\xff\xff\xff\xff\xff\xff\xff', header)
-
- # Invalid opcode 0x10
- self.assertRaises(ValueError,
- stream.create_header,
- 0x10, 0, 0, 0, 0, 0, 0)
-
- # Invalid value 0xf passed to more parameter
- self.assertRaises(ValueError,
- stream.create_header,
- common.OPCODE_TEXT, 0, 0xf, 0, 0, 0, 0)
-
- # Too long payload_length
- self.assertRaises(ValueError,
- stream.create_header,
- common.OPCODE_TEXT, 1 << 63, 0, 0, 0, 0, 0)
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_stream_hixie75.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_stream_hixie75.py
deleted file mode 100755
index ca9ac7130..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_stream_hixie75.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for stream module."""
-
-
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket.stream import StreamHixie75
-from test.test_msgutil import _create_request_hixie75
-
-
-class StreamHixie75Test(unittest.TestCase):
- """A unittest for StreamHixie75 class."""
-
- def test_payload_length(self):
- for length, bytes in ((0, '\x00'), (0x7f, '\x7f'), (0x80, '\x81\x00'),
- (0x1234, '\x80\xa4\x34')):
- test_stream = StreamHixie75(_create_request_hixie75(bytes))
- self.assertEqual(
- length, test_stream._read_payload_length_hixie75())
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/test_util.py b/testing/web-platform/tests/tools/pywebsocket/src/test/test_util.py
deleted file mode 100755
index 20f4ab059..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/test_util.py
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, 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.
-
-
-"""Tests for util module."""
-
-
-import os
-import random
-import sys
-import unittest
-
-import set_sys_path # Update sys.path to locate mod_pywebsocket module.
-
-from mod_pywebsocket import util
-
-
-_TEST_DATA_DIR = os.path.join(os.path.split(__file__)[0], 'testdata')
-
-
-class UtilTest(unittest.TestCase):
- """A unittest for util module."""
-
- def test_get_stack_trace(self):
- self.assertEqual('None\n', util.get_stack_trace())
- try:
- a = 1 / 0 # Intentionally raise exception.
- except Exception:
- trace = util.get_stack_trace()
- self.failUnless(trace.startswith('Traceback'))
- self.failUnless(trace.find('ZeroDivisionError') != -1)
-
- def test_prepend_message_to_exception(self):
- exc = Exception('World')
- self.assertEqual('World', str(exc))
- util.prepend_message_to_exception('Hello ', exc)
- self.assertEqual('Hello World', str(exc))
-
- def test_get_script_interp(self):
- cygwin_path = 'c:\\cygwin\\bin'
- cygwin_perl = os.path.join(cygwin_path, 'perl')
- self.assertEqual(None, util.get_script_interp(
- os.path.join(_TEST_DATA_DIR, 'README')))
- self.assertEqual(None, util.get_script_interp(
- os.path.join(_TEST_DATA_DIR, 'README'), cygwin_path))
- self.assertEqual('/usr/bin/perl -wT', util.get_script_interp(
- os.path.join(_TEST_DATA_DIR, 'hello.pl')))
- self.assertEqual(cygwin_perl + ' -wT', util.get_script_interp(
- os.path.join(_TEST_DATA_DIR, 'hello.pl'), cygwin_path))
-
- def test_hexify(self):
- self.assertEqual('61 7a 41 5a 30 39 20 09 0d 0a 00 ff',
- util.hexify('azAZ09 \t\r\n\x00\xff'))
-
-
-class RepeatedXorMaskerTest(unittest.TestCase):
- """A unittest for RepeatedXorMasker class."""
-
- def test_mask(self):
- # Sample input e6,97,a5 is U+65e5 in UTF-8
- masker = util.RepeatedXorMasker('\xff\xff\xff\xff')
- result = masker.mask('\xe6\x97\xa5')
- self.assertEqual('\x19\x68\x5a', result)
-
- masker = util.RepeatedXorMasker('\x00\x00\x00\x00')
- result = masker.mask('\xe6\x97\xa5')
- self.assertEqual('\xe6\x97\xa5', result)
-
- masker = util.RepeatedXorMasker('\xe6\x97\xa5\x20')
- result = masker.mask('\xe6\x97\xa5')
- self.assertEqual('\x00\x00\x00', result)
-
- def test_mask_twice(self):
- masker = util.RepeatedXorMasker('\x00\x7f\xff\x20')
- # mask[0], mask[1], ... will be used.
- result = masker.mask('\x00\x00\x00\x00\x00')
- self.assertEqual('\x00\x7f\xff\x20\x00', result)
- # mask[2], mask[0], ... will be used for the next call.
- result = masker.mask('\x00\x00\x00\x00\x00')
- self.assertEqual('\x7f\xff\x20\x00\x7f', result)
-
- def test_mask_large_data(self):
- masker = util.RepeatedXorMasker('mASk')
- original = ''.join([chr(i % 256) for i in xrange(1000)])
- result = masker.mask(original)
- expected = ''.join(
- [chr((i % 256) ^ ord('mASk'[i % 4])) for i in xrange(1000)])
- self.assertEqual(expected, result)
-
- masker = util.RepeatedXorMasker('MaSk')
- first_part = 'The WebSocket Protocol enables two-way communication.'
- result = masker.mask(first_part)
- self.assertEqual(
- '\x19\t6K\x1a\x0418"\x028\x0e9A\x03\x19"\x15<\x08"\rs\x0e#'
- '\x001\x07(\x12s\x1f:\x0e~\x1c,\x18s\x08"\x0c>\x1e#\x080\n9'
- '\x08<\x05c',
- result)
- second_part = 'It has two parts: a handshake and the data transfer.'
- result = masker.mask(second_part)
- self.assertEqual(
- "('K%\x00 K9\x16<K=\x00!\x1f>[s\nm\t2\x05)\x12;\n&\x04s\n#"
- "\x05s\x1f%\x04s\x0f,\x152K9\x132\x05>\x076\x19c",
- result)
-
-
-def get_random_section(source, min_num_chunks):
- chunks = []
- bytes_chunked = 0
-
- while bytes_chunked < len(source):
- chunk_size = random.randint(
- 1,
- min(len(source) / min_num_chunks, len(source) - bytes_chunked))
- chunk = source[bytes_chunked:bytes_chunked + chunk_size]
- chunks.append(chunk)
- bytes_chunked += chunk_size
-
- return chunks
-
-
-class InflaterDeflaterTest(unittest.TestCase):
- """A unittest for _Inflater and _Deflater class."""
-
- def test_inflate_deflate_default(self):
- input = b'hello' + '-' * 30000 + b'hello'
- inflater15 = util._Inflater(15)
- deflater15 = util._Deflater(15)
- inflater8 = util._Inflater(8)
- deflater8 = util._Deflater(8)
-
- compressed15 = deflater15.compress_and_finish(input)
- compressed8 = deflater8.compress_and_finish(input)
-
- inflater15.append(compressed15)
- inflater8.append(compressed8)
-
- self.assertNotEqual(compressed15, compressed8)
- self.assertEqual(input, inflater15.decompress(-1))
- self.assertEqual(input, inflater8.decompress(-1))
-
- def test_random_section(self):
- random.seed(a=0)
- source = ''.join(
- [chr(random.randint(0, 255)) for i in xrange(100 * 1024)])
-
- chunked_input = get_random_section(source, 10)
- print "Input chunk sizes: %r" % [len(c) for c in chunked_input]
-
- deflater = util._Deflater(15)
- compressed = []
- for chunk in chunked_input:
- compressed.append(deflater.compress(chunk))
- compressed.append(deflater.compress_and_finish(''))
-
- chunked_expectation = get_random_section(source, 10)
- print ("Expectation chunk sizes: %r" %
- [len(c) for c in chunked_expectation])
-
- inflater = util._Inflater(15)
- inflater.append(''.join(compressed))
- for chunk in chunked_expectation:
- decompressed = inflater.decompress(len(chunk))
- self.assertEqual(chunk, decompressed)
-
- self.assertEqual('', inflater.decompress(-1))
-
-
-if __name__ == '__main__':
- unittest.main()
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/README b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/README
deleted file mode 100644
index c001aa559..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/README
+++ /dev/null
@@ -1 +0,0 @@
-Test data directory
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/abort_by_user_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/abort_by_user_wsh.py
deleted file mode 100644
index 367f9930f..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/abort_by_user_wsh.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2011, 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 handshake
-
-
-def web_socket_do_extra_handshake(request):
- raise handshake.AbortedByUserException("abort for test")
-
-
-def web_socket_transfer_data(request):
- raise handshake.AbortedByUserException("abort for test")
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/blank_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/blank_wsh.py
deleted file mode 100644
index 7f87c6af2..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/blank_wsh.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2009, 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.
-
-
-# intentionally left blank
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/origin_check_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/origin_check_wsh.py
deleted file mode 100644
index 2c139fa17..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/origin_check_wsh.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2009, 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.
-
-
-def web_socket_do_extra_handshake(request):
- if request.ws_origin == 'http://example.com':
- return
- raise ValueError('Unacceptable origin: %r' % request.ws_origin)
-
-
-def web_socket_transfer_data(request):
- request.connection.write('origin_check_wsh.py is called for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/exception_in_transfer_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/exception_in_transfer_wsh.py
deleted file mode 100644
index b982d0231..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/exception_in_transfer_wsh.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Exception in web_socket_transfer_data().
-"""
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- raise Exception('Intentional Exception for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/no_wsh_at_the_end.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/no_wsh_at_the_end.py
deleted file mode 100644
index 17e7be180..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/no_wsh_at_the_end.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Correct signatures, wrong file name.
-"""
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- request.connection.write(
- 'sub/no_wsh_at_the_end.py is called for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/non_callable_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/non_callable_wsh.py
deleted file mode 100644
index 26352eb4c..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/non_callable_wsh.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Non-callable handlers.
-"""
-
-
-web_socket_do_extra_handshake = True
-web_socket_transfer_data = 1
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/plain_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/plain_wsh.py
deleted file mode 100644
index db3ff6930..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/plain_wsh.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2009, 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.
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- request.connection.write('sub/plain_wsh.py is called for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
deleted file mode 100644
index 6bf659bc9..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Wrong web_socket_do_extra_handshake signature.
-"""
-
-
-def no_web_socket_do_extra_handshake(request):
- pass
-
-
-def web_socket_transfer_data(request):
- request.connection.write(
- 'sub/wrong_handshake_sig_wsh.py is called for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
deleted file mode 100644
index e0e2e5507..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Wrong web_socket_transfer_data() signature.
-"""
-
-
-def web_socket_do_extra_handshake(request):
- pass
-
-
-def no_web_socket_transfer_data(request):
- request.connection.write(
- 'sub/wrong_transfer_sig_wsh.py is called for %s, %s' %
- (request.ws_resource, request.ws_protocol))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/hello.pl b/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/hello.pl
deleted file mode 100644
index 882ef5a10..000000000
--- a/testing/web-platform/tests/tools/pywebsocket/src/test/testdata/hello.pl
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/perl -wT
-#
-# 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.
-
-print "Hello\n";