summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket')
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING28
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in6
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README6
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit14
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py209
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py49
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt11
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py105
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py231
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py220
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py99
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py81
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py250
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py453
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py121
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py63
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py205
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py64
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py244
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py513
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py72
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py126
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py156
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py78
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README1
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py31
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py42
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py44
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py39
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py40
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl2
35 files changed, 3783 insertions, 0 deletions
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING
new file mode 100644
index 0000000..ab9d52d
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING
@@ -0,0 +1,28 @@
+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.
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in
new file mode 100644
index 0000000..1925688
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in
@@ -0,0 +1,6 @@
+include COPYING
+include MANIFEST.in
+include README
+recursive-include example *.py
+recursive-include mod_pywebsocket *.py
+recursive-include test *.py
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README
new file mode 100644
index 0000000..1f9f05f
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README
@@ -0,0 +1,6 @@
+Install this package by:
+$ python setup.py build
+$ sudo python setup.py install
+
+Then read document by:
+$ pydoc mod_pywebsocket
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit
new file mode 100644
index 0000000..83e3cee
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit
@@ -0,0 +1,14 @@
+This directory contains a copy of the pywebsocket Python module obtained
+from the following location:
+
+http://code.google.com/p/pywebsocket/
+
+This directory currently contains the following version:
+0.4.9.2
+
+The following modifications have been made to this local version:
+minor updates in WebSocketRequestHandler.is_cgi
+
+More information on these local modifications can be found here:
+
+http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py
new file mode 100644
index 0000000..2b976e1
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Web Socket Echo client.
+
+This is an example Web Socket client that talks with echo_wsh.py.
+This may be useful for checking mod_pywebsocket installation.
+
+Note:
+This code is far from robust, e.g., we cut corners in handshake.
+"""
+
+
+import codecs
+from optparse import OptionParser
+import socket
+import sys
+
+
+_TIMEOUT_SEC = 10
+
+_DEFAULT_PORT = 80
+_DEFAULT_SECURE_PORT = 443
+_UNDEFINED_PORT = -1
+
+_UPGRADE_HEADER = 'Upgrade: WebSocket\r\n'
+_CONNECTION_HEADER = 'Connection: Upgrade\r\n'
+_EXPECTED_RESPONSE = (
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
+ _UPGRADE_HEADER +
+ _CONNECTION_HEADER)
+
+_GOODBYE_MESSAGE = 'Goodbye'
+
+
+def _method_line(resource):
+ return 'GET %s HTTP/1.1\r\n' % resource
+
+
+def _origin_header(origin):
+ return 'Origin: %s\r\n' % origin
+
+
+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 EchoClient(object):
+ """Web Socket echo client."""
+
+ def __init__(self, options):
+ self._options = options
+ self._socket = None
+
+ 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._handshake()
+ for line in self._options.message.split(',') + [_GOODBYE_MESSAGE]:
+ frame = '\x00' + line.encode('utf-8') + '\xff'
+ self._socket.send(frame)
+ if self._options.verbose:
+ print 'Send: %s' % line
+ received = self._socket.recv(len(frame))
+ if received != frame:
+ raise Exception('Incorrect echo: %r' % received)
+ if self._options.verbose:
+ print 'Recv: %s' % received[1:-1].decode('utf-8',
+ 'replace')
+ finally:
+ self._socket.close()
+
+ def _handshake(self):
+ self._socket.send(_method_line(self._options.resource))
+ self._socket.send(_UPGRADE_HEADER)
+ self._socket.send(_CONNECTION_HEADER)
+ self._socket.send(self._format_host_header())
+ self._socket.send(_origin_header(self._options.origin))
+ self._socket.send('\r\n')
+
+ for expected_char in _EXPECTED_RESPONSE:
+ received = self._socket.recv(1)[0]
+ if expected_char != received:
+ raise Exception('Handshake failure')
+ # We cut corners and skip other headers.
+ self._skip_headers()
+
+ def _skip_headers(self):
+ terminator = '\r\n\r\n'
+ pos = 0
+ while pos < len(terminator):
+ received = self._socket.recv(1)[0]
+ if received == terminator[pos]:
+ pos += 1
+ elif received == terminator[0]:
+ pos = 1
+ else:
+ pos = 0
+
+ def _format_host_header(self):
+ host = 'Host: ' + self._options.server_host
+ if ((not self._options.use_tls and
+ self._options.server_port != _DEFAULT_PORT) or
+ (self._options.use_tls and
+ self._options.server_port != _DEFAULT_SECURE_PORT)):
+ host += ':' + str(self._options.server_port)
+ host += '\r\n'
+ return host
+
+
+def main():
+ sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
+
+ parser = OptionParser()
+ 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='http://localhost/', 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 excluding "%s" '
+ 'that is always sent at the end' %
+ _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://)')
+ parser.add_option('-k', '--socket-timeout', '--socket_timeout',
+ dest='socket_timeout', type='int', default=_TIMEOUT_SEC,
+ help='Timeout(sec) for sockets')
+
+ (options, unused_args) = parser.parse_args()
+
+ # Default port number depends on whether TLS is used.
+ if options.server_port == _UNDEFINED_PORT:
+ if options.use_tls:
+ options.server_port = _DEFAULT_SECURE_PORT
+ else:
+ options.server_port = _DEFAULT_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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py
new file mode 100644
index 0000000..50cad31
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py
@@ -0,0 +1,49 @@
+# 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.
+
+
+from mod_pywebsocket import msgutil
+
+
+_GOODBYE_MESSAGE = 'Goodbye'
+
+
+def web_socket_do_extra_handshake(request):
+ pass # Always accept.
+
+
+def web_socket_transfer_data(request):
+ while True:
+ line = msgutil.receive_message(request)
+ msgutil.send_message(request, line)
+ if line == _GOODBYE_MESSAGE:
+ return
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt
new file mode 100644
index 0000000..21c4c09
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt
@@ -0,0 +1,11 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py
new file mode 100644
index 0000000..05e80e8
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py
@@ -0,0 +1,105 @@
+# 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.
+
+
+"""Web Socket extension for Apache HTTP Server.
+
+mod_pywebsocket is a Web Socket 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 Web Socket handlers are placed.
+
+ PythonOption mod_pywebsocket.handler_root <websock_handlers>
+ PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
+
+ To limit the search for Web Socket 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-Web Socket handler files.
+
+ Example snippet of httpd.conf:
+ (mod_pywebsocket is in /websock_lib, Web Socket 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>
+
+Writing Web Socket handlers:
+
+When a Web Socket 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 Web Socket handler is composed of the following two functions:
+
+ web_socket_do_extra_handshake(request)
+ web_socket_transfer_data(request)
+
+where:
+ request: mod_python request.
+
+web_socket_do_extra_handshake is called during the handshake after the
+headers are successfully parsed and Web Socket properties (ws_location,
+ws_origin, ws_protocol, and ws_resource) are added to request. A handler
+can reject the request by raising an exception.
+
+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.
+
+A Web Socket handler must be thread-safe if the server (Apache or
+standalone.py) is configured to use threads.
+"""
+
+
+# vi:sts=4 sw=4 et tw=72
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py
new file mode 100644
index 0000000..c52e9eb
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py
@@ -0,0 +1,231 @@
+# 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.
+
+
+"""Dispatch Web Socket request.
+"""
+
+
+import os
+import re
+
+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'
+
+
+class DispatchError(Exception):
+ """Exception in dispatching Web Socket request."""
+
+ pass
+
+
+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 _path_to_resource_converter(base_dir):
+ 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
+ path = _normalize_path(path)
+ if not path.startswith(base_dir):
+ return None
+ return path[base_len:-suffix_len]
+ return converter
+
+
+def _source_file_paths(directory):
+ """Yield Web Socket 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
+
+
+def _source(source_str):
+ """Source a handler definition string."""
+
+ global_dic = {}
+ try:
+ exec source_str in global_dic
+ except Exception:
+ raise DispatchError('Error in sourcing handler:' +
+ util.get_stack_trace())
+ return (_extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME),
+ _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME))
+
+
+def _extract_handler(dic, name):
+ if name not in dic:
+ raise DispatchError('%s is not defined.' % name)
+ handler = dic[name]
+ if not callable(handler):
+ raise DispatchError('%s is not callable.' % name)
+ return handler
+
+
+class Dispatcher(object):
+ """Dispatches Web Socket requests.
+
+ This class maintains a map from resource name to handlers.
+ """
+
+ def __init__(self, root_dir, scan_dir=None):
+ """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.
+ """
+
+ self._handlers = {}
+ 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 DispatchError('scan_dir:%s must be a directory under '
+ 'root_dir:%s.' % (scan_dir, root_dir))
+ self._source_files_in_dir(root_dir, scan_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 = self._handlers[existing_resource_path]
+ self._handlers[alias_resource_path] = handler
+ except KeyError:
+ raise DispatchError('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 Web Socket handshake.
+
+ Select a handler based on request.uri and call its
+ web_socket_do_extra_handshake function.
+
+ Args:
+ request: mod_python request.
+ """
+
+ do_extra_handshake_, unused_transfer_data = self._handler(request)
+ try:
+ do_extra_handshake_(request)
+ except Exception, e:
+ util.prepend_message_to_exception(
+ '%s raised exception for %s: ' % (
+ _DO_EXTRA_HANDSHAKE_HANDLER_NAME,
+ request.ws_resource),
+ e)
+ raise
+
+ def transfer_data(self, request):
+ """Let a handler transfer_data with a Web Socket client.
+
+ Select a handler based on request.ws_resource and call its
+ web_socket_transfer_data function.
+
+ Args:
+ request: mod_python request.
+ """
+
+ unused_do_extra_handshake, transfer_data_ = self._handler(request)
+ try:
+ transfer_data_(request)
+ except Exception, e:
+ util.prepend_message_to_exception(
+ '%s raised exception for %s: ' % (
+ _TRANSFER_DATA_HANDLER_NAME, request.ws_resource),
+ e)
+ raise
+
+ def _handler(self, request):
+ try:
+ ws_resource_path = request.ws_resource.split('?', 1)[0]
+ return self._handlers[ws_resource_path]
+ except KeyError:
+ raise DispatchError('No handler for: %r' % request.ws_resource)
+
+ def _source_files_in_dir(self, root_dir, scan_dir):
+ """Source all the handler source files in the scan_dir directory.
+
+ The resource path is determined relative to root_dir.
+ """
+
+ to_resource = _path_to_resource_converter(root_dir)
+ for path in _source_file_paths(scan_dir):
+ try:
+ handlers = _source(open(path).read())
+ except DispatchError, e:
+ self._source_warnings.append('%s: %s' % (path, e))
+ continue
+ self._handlers[to_resource(path)] = handlers
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py
new file mode 100644
index 0000000..b86278e
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py
@@ -0,0 +1,220 @@
+# 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.
+
+
+"""Web Socket handshaking.
+
+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 re
+
+import util
+
+
+_DEFAULT_WEB_SOCKET_PORT = 80
+_DEFAULT_WEB_SOCKET_SECURE_PORT = 443
+_WEB_SOCKET_SCHEME = 'ws'
+_WEB_SOCKET_SECURE_SCHEME = 'wss'
+
+_MANDATORY_HEADERS = [
+ # key, expected value or None
+ ['Upgrade', 'WebSocket'],
+ ['Connection', 'Upgrade'],
+ ['Host', None],
+ ['Origin', None],
+]
+
+_FIRST_FIVE_LINES = map(re.compile, [
+ r'^GET /[\S]* HTTP/1.1\r\n$',
+ r'^Upgrade: WebSocket\r\n$',
+ r'^Connection: Upgrade\r\n$',
+ r'^Host: [\S]+\r\n$',
+ r'^Origin: [\S]+\r\n$',
+])
+
+_SIXTH_AND_LATER = re.compile(
+ r'^'
+ r'(WebSocket-Protocol: [\x20-\x7e]+\r\n)?'
+ r'(Cookie: [^\r]*\r\n)*'
+ r'(Cookie2: [^\r]*\r\n)?'
+ r'(Cookie: [^\r]*\r\n)*'
+ r'\r\n')
+
+
+def _default_port(is_secure):
+ if is_secure:
+ return _DEFAULT_WEB_SOCKET_SECURE_PORT
+ else:
+ return _DEFAULT_WEB_SOCKET_PORT
+
+
+class HandshakeError(Exception):
+ """Exception in Web Socket Handshake."""
+
+ pass
+
+
+def _validate_protocol(protocol):
+ """Validate WebSocket-Protocol string."""
+
+ if not protocol:
+ raise HandshakeError('Invalid WebSocket-Protocol: empty')
+ for c in protocol:
+ if not 0x20 <= ord(c) <= 0x7e:
+ raise HandshakeError('Illegal character in protocol: %r' % c)
+
+
+class Handshaker(object):
+ """This class performs Web Socket handshake."""
+
+ def __init__(self, request, dispatcher, strict=False):
+ """Construct an instance.
+
+ Args:
+ request: mod_python request.
+ dispatcher: Dispatcher (dispatch.Dispatcher).
+ strict: Strictly check handshake request. Default: False.
+ If True, request.connection must provide get_memorized_lines
+ method.
+
+ Handshaker will add attributes such as ws_resource in performing
+ handshake.
+ """
+
+ self._request = request
+ self._dispatcher = dispatcher
+ self._strict = strict
+
+ def do_handshake(self):
+ """Perform Web Socket Handshake."""
+
+ self._check_header_lines()
+ self._set_resource()
+ self._set_origin()
+ self._set_location()
+ self._set_protocol()
+ self._dispatcher.do_extra_handshake(self._request)
+ self._send_handshake()
+
+ def _set_resource(self):
+ self._request.ws_resource = self._request.uri
+
+ def _set_origin(self):
+ self._request.ws_origin = self._request.headers_in['Origin']
+
+ def _set_location(self):
+ location_parts = []
+ if self._request.is_https():
+ location_parts.append(_WEB_SOCKET_SECURE_SCHEME)
+ else:
+ location_parts.append(_WEB_SOCKET_SCHEME)
+ location_parts.append('://')
+ host, port = self._parse_host_header()
+ connection_port = self._request.connection.local_addr[1]
+ if port != connection_port:
+ raise HandshakeError('Header/connection port mismatch: %d/%d' %
+ (port, connection_port))
+ location_parts.append(host)
+ if (port != _default_port(self._request.is_https())):
+ location_parts.append(':')
+ location_parts.append(str(port))
+ location_parts.append(self._request.uri)
+ self._request.ws_location = ''.join(location_parts)
+
+ def _parse_host_header(self):
+ fields = self._request.headers_in['Host'].split(':', 1)
+ if len(fields) == 1:
+ return fields[0], _default_port(self._request.is_https())
+ try:
+ return fields[0], int(fields[1])
+ except ValueError, e:
+ raise HandshakeError('Invalid port number format: %r' % e)
+
+ def _set_protocol(self):
+ protocol = self._request.headers_in.get('WebSocket-Protocol')
+ if protocol is not None:
+ _validate_protocol(protocol)
+ self._request.ws_protocol = protocol
+
+ def _send_handshake(self):
+ self._request.connection.write(
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
+ self._request.connection.write('Upgrade: WebSocket\r\n')
+ self._request.connection.write('Connection: Upgrade\r\n')
+ self._request.connection.write('WebSocket-Origin: ')
+ self._request.connection.write(self._request.ws_origin)
+ self._request.connection.write('\r\n')
+ self._request.connection.write('WebSocket-Location: ')
+ self._request.connection.write(self._request.ws_location)
+ self._request.connection.write('\r\n')
+ if self._request.ws_protocol:
+ self._request.connection.write('WebSocket-Protocol: ')
+ self._request.connection.write(self._request.ws_protocol)
+ self._request.connection.write('\r\n')
+ self._request.connection.write('\r\n')
+
+ def _check_header_lines(self):
+ for key, expected_value in _MANDATORY_HEADERS:
+ actual_value = self._request.headers_in.get(key)
+ if not actual_value:
+ raise HandshakeError('Header %s is not defined' % key)
+ if expected_value:
+ if actual_value != expected_value:
+ raise HandshakeError('Illegal value for header %s: %s' %
+ (key, actual_value))
+ if self._strict:
+ try:
+ lines = self._request.connection.get_memorized_lines()
+ except AttributeError, e:
+ util.prepend_message_to_exception(
+ 'Strict handshake is specified but the connection '
+ 'doesn\'t provide get_memorized_lines()', e)
+ raise
+ self._check_first_lines(lines)
+
+ def _check_first_lines(self, lines):
+ if len(lines) < len(_FIRST_FIVE_LINES):
+ raise HandshakeError('Too few header lines: %d' % len(lines))
+ for line, regexp in zip(lines, _FIRST_FIVE_LINES):
+ if not regexp.search(line):
+ raise HandshakeError('Unexpected header: %r doesn\'t match %r'
+ % (line, regexp.pattern))
+ sixth_and_later = ''.join(lines[5:])
+ if not _SIXTH_AND_LATER.search(sixth_and_later):
+ raise HandshakeError('Unexpected header: %r doesn\'t match %r'
+ % (sixth_and_later,
+ _SIXTH_AND_LATER.pattern))
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py
new file mode 100644
index 0000000..124b9f1
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py
@@ -0,0 +1,99 @@
+# 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.
+
+
+"""PythonHeaderParserHandler for mod_pywebsocket.
+
+Apache HTTP Server and mod_python must be configured such that this
+function is called to handle Web Socket request.
+"""
+
+
+from mod_python import apache
+
+import dispatch
+import handshake
+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'
+
+
+def _create_dispatcher():
+ _HANDLER_ROOT = apache.main_server.get_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 = apache.main_server.get_options().get(
+ _PYOPT_HANDLER_SCAN, _HANDLER_ROOT)
+ dispatcher = dispatch.Dispatcher(_HANDLER_ROOT, _HANDLER_SCAN)
+ for warning in dispatcher.source_warnings():
+ apache.log_error('mod_pywebsocket: %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.
+ """
+
+ try:
+ handshaker = handshake.Handshaker(request, _dispatcher)
+ handshaker.do_handshake()
+ request.log_error('mod_pywebsocket: resource: %r' % request.ws_resource,
+ apache.APLOG_DEBUG)
+ _dispatcher.transfer_data(request)
+ except handshake.HandshakeError, e:
+ # Handshake for ws/wss failed.
+ # But the request can be valid http/https request.
+ request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
+ return apache.DECLINED
+ except dispatch.DispatchError, e:
+ request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING)
+ return apache.DECLINED
+ return apache.DONE # Return DONE such that no other handlers are invoked.
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py
new file mode 100644
index 0000000..2f8a54e
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""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
+
+ def __getattribute__(self, name):
+ if name in ('_file', '_memorized_lines', '_max_memorized_lines',
+ 'readline', 'get_memorized_lines'):
+ return object.__getattribute__(self, name)
+ return self._file.__getattribute__(name)
+
+ def readline(self):
+ """Override file.readline and memorize the line read."""
+
+ line = self._file.readline()
+ if line and len(self._memorized_lines) < self._max_memorized_lines:
+ self._memorized_lines.append(line)
+ 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py
new file mode 100644
index 0000000..90ae715
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py
@@ -0,0 +1,250 @@
+# 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.
+
+
+"""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
+import util
+
+
+class MsgUtilException(Exception):
+ pass
+
+
+def _read(request, length):
+ bytes = request.connection.read(length)
+ if not bytes:
+ raise MsgUtilException(
+ 'Failed to receive message from %r' %
+ (request.connection.remote_addr,))
+ return bytes
+
+
+def _write(request, bytes):
+ try:
+ request.connection.write(bytes)
+ except Exception, e:
+ util.prepend_message_to_exception(
+ 'Failed to send message to %r: ' %
+ (request.connection.remote_addr,),
+ e)
+ raise
+
+
+def send_message(request, message):
+ """Send message.
+
+ Args:
+ request: mod_python request.
+ message: unicode string to send.
+ """
+
+ _write(request, '\x00' + message.encode('utf-8') + '\xff')
+
+
+def receive_message(request):
+ """Receive a Web Socket frame and return its payload as unicode string.
+
+ Args:
+ request: mod_python request.
+ """
+
+ 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 = _read(request, 1)
+ frame_type = ord(frame_type_str[0])
+ if (frame_type & 0x80) == 0x80:
+ # The payload length is specified in the frame.
+ # Read and discard.
+ length = _payload_length(request)
+ _receive_bytes(request, length)
+ else:
+ # The payload is delimited with \xff.
+ bytes = _read_until(request, '\xff')
+ # The Web Socket 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 _payload_length(request):
+ length = 0
+ while True:
+ b_str = _read(request, 1)
+ b = ord(b_str[0])
+ length = length * 128 + (b & 0x7f)
+ if (b & 0x80) == 0:
+ break
+ return length
+
+
+def _receive_bytes(request, length):
+ bytes = []
+ while length > 0:
+ new_bytes = _read(request, length)
+ bytes.append(new_bytes)
+ length -= len(new_bytes)
+ return ''.join(bytes)
+
+
+def _read_until(request, delim_char):
+ bytes = []
+ while True:
+ ch = _read(request, 1)
+ if ch == delim_char:
+ break
+ bytes.append(ch)
+ return ''.join(bytes)
+
+
+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):
+ while not self._stop_requested:
+ message = receive_message(self._request)
+ if self._onmessage:
+ self._onmessage(message)
+ else:
+ self._queue.put(message)
+
+ 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py
new file mode 100644
index 0000000..f411910
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py
@@ -0,0 +1,453 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Standalone Web Socket server.
+
+Use this server to run mod_pywebsocket without Apache HTTP Server.
+
+Usage:
+ python standalone.py [-p <ws_port>] [-w <websock_handlers>]
+ [-s <scan_dir>]
+ [-d <document_root>]
+ [-m <websock_handlers_map_file>]
+ ... for other options, see _main below ...
+
+<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 Web Socket handlers.
+See __init__.py for details of <websock_handlers> and how to write Web Socket
+handlers. If this path is relative, <document_root> is used as the base.
+
+<scan_dir> is a path under the root directory. If specified, only the handlers
+under scan_dir are scanned. This is useful in saving scan time.
+
+Note:
+This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
+used for each request.
+"""
+
+import BaseHTTPServer
+import CGIHTTPServer
+import SimpleHTTPServer
+import SocketServer
+import logging
+import logging.handlers
+import optparse
+import os
+import re
+import socket
+import sys
+
+_HAS_OPEN_SSL = False
+try:
+ import OpenSSL.SSL
+ _HAS_OPEN_SSL = True
+except ImportError:
+ pass
+
+import dispatch
+import handshake
+import memorizingfile
+import util
+
+
+_LOG_LEVELS = {
+ 'debug': logging.DEBUG,
+ 'info': logging.INFO,
+ 'warn': logging.WARN,
+ 'error': logging.ERROR,
+ 'critical': logging.CRITICAL};
+
+_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
+
+def _print_warnings_if_any(dispatcher):
+ warnings = dispatcher.source_warnings()
+ if warnings:
+ for warning in warnings:
+ logging.warning('mod_pywebsocket: %s' % warning)
+
+
+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._request_handler = request_handler
+ self.connection = _StandaloneConnection(request_handler)
+ self._use_tls = use_tls
+
+ def get_uri(self):
+ """Getter to mimic request.uri."""
+ return self._request_handler.path
+ uri = property(get_uri)
+
+ def get_headers_in(self):
+ """Getter to mimic request.headers_in."""
+ return self._request_handler.headers
+ headers_in = property(get_headers_in)
+
+ def is_https(self):
+ """Mimic request.is_https()."""
+ return self._use_tls
+
+
+class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
+ """HTTPServer specialized for Web Socket."""
+
+ SocketServer.ThreadingMixIn.daemon_threads = True
+
+ def __init__(self, server_address, RequestHandlerClass):
+ """Override SocketServer.BaseServer.__init__."""
+
+ SocketServer.BaseServer.__init__(
+ self, server_address, RequestHandlerClass)
+ self.socket = self._create_socket()
+ self.server_bind()
+ self.server_activate()
+
+ def _create_socket(self):
+ socket_ = socket.socket(self.address_family, self.socket_type)
+ if WebSocketServer.options.use_tls:
+ ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
+ ctx.use_privatekey_file(WebSocketServer.options.private_key)
+ ctx.use_certificate_file(WebSocketServer.options.certificate)
+ socket_ = OpenSSL.SSL.Connection(ctx, socket_)
+ return socket_
+
+ def handle_error(self, rquest, client_address):
+ """Override SocketServer.handle_error."""
+
+ logging.error(
+ ('Exception in processing request from: %r' % (client_address,)) +
+ '\n' + util.get_stack_trace())
+ # Note: client_address is a tuple. To match it against %r, we need the
+ # trailing comma.
+
+
+class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
+ """CGIHTTPRequestHandler specialized for Web Socket."""
+
+ def setup(self):
+ """Override SocketServer.StreamRequestHandler.setup."""
+
+ self.connection = self.request
+ self.rfile = memorizingfile.MemorizingFile(
+ socket._fileobject(self.request, 'rb', self.rbufsize),
+ max_memorized_lines=_MAX_MEMORIZED_LINES)
+ self.wfile = socket._fileobject(self.request, 'wb', self.wbufsize)
+
+ def __init__(self, *args, **keywords):
+ self._request = _StandaloneRequest(
+ self, WebSocketRequestHandler.options.use_tls)
+ self._dispatcher = WebSocketRequestHandler.options.dispatcher
+ self._print_warnings_if_any()
+ self._handshaker = handshake.Handshaker(
+ self._request, self._dispatcher,
+ WebSocketRequestHandler.options.strict)
+ CGIHTTPServer.CGIHTTPRequestHandler.__init__(
+ self, *args, **keywords)
+
+ def _print_warnings_if_any(self):
+ warnings = self._dispatcher.source_warnings()
+ if warnings:
+ for warning in warnings:
+ logging.warning('mod_pywebsocket: %s' % warning)
+
+ def parse_request(self):
+ """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
+
+ Return True to continue processing for HTTP(S), False otherwise.
+ """
+ result = CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self)
+ if result:
+ try:
+ self._handshaker.do_handshake()
+ self._dispatcher.transfer_data(self._request)
+ return False
+ except handshake.HandshakeError, e:
+ # Handshake for ws(s) failed. Assume http(s).
+ logging.info('mod_pywebsocket: %s' % e)
+ return True
+ except dispatch.DispatchError, e:
+ logging.warning('mod_pywebsocket: %s' % e)
+ return False
+ except Exception, e:
+ logging.warning('mod_pywebsocket: %s' % e)
+ logging.info('mod_pywebsocket: %s' % util.get_stack_trace())
+ return False
+ return result
+
+ def log_request(self, code='-', size='-'):
+ """Override BaseHTTPServer.log_request."""
+
+ logging.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.
+ logging.warn('%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 _configure_logging(options):
+ logger = logging.getLogger()
+ logger.setLevel(_LOG_LEVELS[options.log_level])
+ 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)
+
+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.DispatchError, e:
+ logging.error(str(e))
+ finally:
+ fp.close()
+
+
+
+def _main():
+ parser = optparse.OptionParser()
+ parser.add_option('-H', '--server-host', '--server_host',
+ dest='server_host',
+ default='',
+ help='server hostname to listen to')
+ parser.add_option('-p', '--port', dest='port', type='int',
+ default=handshake._DEFAULT_WEB_SOCKET_PORT,
+ help='port to listen to')
+ parser.add_option('-w', '--websock-handlers', '--websock_handlers',
+ dest='websock_handlers',
+ default='.',
+ help='Web Socket handlers root directory.')
+ parser.add_option('-m', '--websock-handlers-map-file',
+ '--websock_handlers_map_file',
+ dest='websock_handlers_map_file',
+ default=None,
+ help=('Web Socket 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=('Web Socket handlers scan directory. '
+ 'Must be a directory 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('-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('-l', '--log-file', '--log_file', dest='log_file',
+ default='', help='Log file.')
+ parser.add_option('--log-level', '--log_level', type='choice',
+ dest='log_level', default='warn',
+ choices=['debug', 'info', 'warn', 'error', 'critical'],
+ help='Log level.')
+ 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('--strict', dest='strict', action='store_true',
+ default=False, help='Strictly check handshake request')
+ parser.add_option('-q', '--queue', dest='request_queue_size', type='int',
+ default=_DEFAULT_REQUEST_QUEUE_SIZE,
+ help='request queue size')
+ options = parser.parse_args()[0]
+
+ os.chdir(options.document_root)
+
+ _configure_logging(options)
+
+ SocketServer.TCPServer.request_queue_size = options.request_queue_size
+ CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = []
+
+ if options.cgi_paths:
+ CGIHTTPServer.CGIHTTPRequestHandler.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)
+ CGIHTTPServer.executable = __check_script
+
+ if options.use_tls:
+ if not _HAS_OPEN_SSL:
+ logging.critical('To use TLS, install pyOpenSSL.')
+ 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 not options.scan_dir:
+ options.scan_dir = options.websock_handlers
+
+ try:
+ # 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)
+ if options.websock_handlers_map_file:
+ _alias_handlers(options.dispatcher,
+ options.websock_handlers_map_file)
+ _print_warnings_if_any(options.dispatcher)
+
+ WebSocketRequestHandler.options = options
+ WebSocketServer.options = options
+
+ server = WebSocketServer((options.server_host, options.port),
+ WebSocketRequestHandler)
+ server.serve_forever()
+ except Exception, e:
+ logging.critical(str(e))
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ _main()
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py
new file mode 100644
index 0000000..8ec9dca
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py
@@ -0,0 +1,121 @@
+# 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.
+
+
+"""Web Sockets utilities.
+"""
+
+
+import StringIO
+import os
+import re
+import traceback
+
+
+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
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py
new file mode 100644
index 0000000..a34a83b
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Set up script for mod_pywebsocket.
+"""
+
+
+from distutils.core import setup
+import sys
+
+
+_PACKAGE_NAME = 'mod_pywebsocket'
+
+if sys.version < '2.3':
+ print >>sys.stderr, '%s requires Python 2.3 or later.' % _PACKAGE_NAME
+ sys.exit(1)
+
+setup(author='Yuzo Fujishima',
+ author_email='yuzo@chromium.org',
+ description='Web Socket extension for Apache HTTP Server.',
+ long_description=(
+ 'mod_pywebsocket is an Apache HTTP Server extension for '
+ 'Web Socket (http://tools.ietf.org/html/'
+ 'draft-hixie-thewebsocketprotocol). '
+ 'See mod_pywebsocket/__init__.py for more detail.'),
+ license='See COPYING',
+ name=_PACKAGE_NAME,
+ packages=[_PACKAGE_NAME],
+ url='http://code.google.com/p/pywebsocket/',
+ version='0.4.9.2',
+ )
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py
new file mode 100644
index 0000000..5aaab8c
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py
@@ -0,0 +1,45 @@
+# 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 += [os.path.join(os.path.split(__file__)[0], '..')]
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py
new file mode 100644
index 0000000..3b85d64
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py
@@ -0,0 +1,205 @@
+# 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.
+
+
+"""Mocks for testing.
+"""
+
+
+import Queue
+import threading
+
+
+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 = []
+
+ 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,
+ is_https=False):
+ """Construct an instance.
+
+ Arguments:
+ uri: URI of the request.
+ headers_in: Request headers.
+ connection: Connection used for the request.
+ is_https: Whether this request is over SSL.
+
+ See the document of mod_python Request for details.
+ """
+ self.uri = uri
+ self.connection = connection
+ 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
+
+ def is_https(self):
+ """Return whether this request is over SSL."""
+ return self.is_https_
+
+
+class MockDispatcher(object):
+ """Mock for dispatch.Dispatcher."""
+
+ def do_extra_handshake(self, conn_context):
+ pass
+
+ def transfer_data(self, conn_context):
+ pass
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py
new file mode 100644
index 0000000..3885618
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Run all tests in the same directory.
+"""
+
+
+import os
+import re
+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__':
+ unittest.main(defaultTest='_suite')
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py
new file mode 100644
index 0000000..5403228
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Tests for dispatch module."""
+
+
+
+import os
+import unittest
+
+import config # This must be imported before mod_pywebsocket.
+from mod_pywebsocket import dispatch
+
+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):
+ 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._path_to_resource_converter('/a/b')
+ self.assertEqual('/h', converter('/a/b/h_wsh.py'))
+ self.assertEqual('/c/h', converter('/a/b/c/h_wsh.py'))
+ self.assertEqual(None, converter('/a/b/h.py'))
+ self.assertEqual(None, converter('a/b/h_wsh.py'))
+
+ converter = dispatch._path_to_resource_converter('a/b')
+ self.assertEqual('/h', converter('a/b/h_wsh.py'))
+
+ converter = dispatch._path_to_resource_converter('/a/b///')
+ self.assertEqual('/h', converter('/a/b/h_wsh.py'))
+ self.assertEqual('/h', converter('/a/b/../b/h_wsh.py'))
+
+ converter = dispatch._path_to_resource_converter('/a/../a/b/../b/')
+ self.assertEqual('/h', converter('/a/b/h_wsh.py'))
+
+ converter = dispatch._path_to_resource_converter(r'\a\b')
+ self.assertEqual('/h', converter(r'\a\b\h_wsh.py'))
+ self.assertEqual('/h', converter(r'/a/b/h_wsh.py'))
+
+ def test_source_file_paths(self):
+ paths = list(dispatch._source_file_paths(_TEST_HANDLERS_DIR))
+ paths.sort()
+ self.assertEqual(7, len(paths))
+ expected_paths = [
+ 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(self):
+ self.assertRaises(dispatch.DispatchError, dispatch._source, '')
+ self.assertRaises(dispatch.DispatchError, dispatch._source, 'def')
+ self.assertRaises(dispatch.DispatchError, dispatch._source, '1/0')
+ self.failUnless(dispatch._source(
+ '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.join(_TEST_HANDLERS_DIR, 'blank_wsh.py') +
+ ': web_socket_do_extra_handshake is not defined.'),
+ (os.path.join(_TEST_HANDLERS_DIR, 'sub',
+ 'non_callable_wsh.py') +
+ ': web_socket_do_extra_handshake is not callable.'),
+ (os.path.join(_TEST_HANDLERS_DIR, 'sub',
+ 'wrong_handshake_sig_wsh.py') +
+ ': web_socket_do_extra_handshake is not defined.'),
+ (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'
+ self.assertRaises(Exception, dispatcher.do_extra_handshake, request)
+
+ def test_transfer_data(self):
+ dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
+ request = mock.MockRequest(connection=mock.MockConn(''))
+ 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',
+ request.connection.written_data())
+
+ request = mock.MockRequest(connection=mock.MockConn(''))
+ 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',
+ request.connection.written_data())
+
+ request = mock.MockRequest(connection=mock.MockConn(''))
+ 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',
+ request.connection.written_data())
+
+ request = mock.MockRequest(connection=mock.MockConn(''))
+ 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',
+ 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.DispatchError, 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)
+
+ def test_scan_dir(self):
+ disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
+ self.assertEqual(3, len(disp._handlers))
+ self.failUnless(disp._handlers.has_key('/origin_check'))
+ self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer'))
+ self.failUnless(disp._handlers.has_key('/sub/plain'))
+
+ def test_scan_sub_dir(self):
+ disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, _TEST_HANDLERS_SUB_DIR)
+ self.assertEqual(2, len(disp._handlers))
+ self.failIf(disp._handlers.has_key('/origin_check'))
+ self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer'))
+ self.failUnless(disp._handlers.has_key('/sub/plain'))
+
+ def test_scan_sub_dir_as_root(self):
+ disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR,
+ _TEST_HANDLERS_SUB_DIR)
+ self.assertEqual(2, len(disp._handlers))
+ self.failIf(disp._handlers.has_key('/origin_check'))
+ self.failIf(disp._handlers.has_key('/sub/exception_in_transfer'))
+ self.failIf(disp._handlers.has_key('/sub/plain'))
+ self.failUnless(disp._handlers.has_key('/exception_in_transfer'))
+ self.failUnless(disp._handlers.has_key('/plain'))
+
+ 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.DispatchError,
+ 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(4, len(disp._handlers))
+ self.failUnless(disp._handlers.has_key('/origin_check'))
+ self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer'))
+ self.failUnless(disp._handlers.has_key('/sub/plain'))
+ self.failUnless(disp._handlers.has_key('/'))
+ self.assertRaises(dispatch.DispatchError,
+ disp.add_resource_path_alias, '/alias', '/not-exist')
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py
new file mode 100644
index 0000000..8bf07be
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py
@@ -0,0 +1,513 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Tests for handshake module."""
+
+
+import unittest
+
+import config # This must be imported before mod_pywebsocket.
+from mod_pywebsocket import handshake
+
+import mock
+
+
+_GOOD_REQUEST = (
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ }
+)
+
+_GOOD_RESPONSE_DEFAULT_PORT = (
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n'
+ 'Upgrade: WebSocket\r\n'
+ 'Connection: Upgrade\r\n'
+ 'WebSocket-Origin: http://example.com\r\n'
+ 'WebSocket-Location: ws://example.com/demo\r\n'
+ 'WebSocket-Protocol: sample\r\n'
+ '\r\n')
+
+_GOOD_RESPONSE_SECURE = (
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n'
+ 'Upgrade: WebSocket\r\n'
+ 'Connection: Upgrade\r\n'
+ 'WebSocket-Origin: http://example.com\r\n'
+ 'WebSocket-Location: wss://example.com/demo\r\n'
+ 'WebSocket-Protocol: sample\r\n'
+ '\r\n')
+
+_GOOD_REQUEST_NONDEFAULT_PORT = (
+ 8081,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com:8081',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ }
+)
+
+_GOOD_RESPONSE_NONDEFAULT_PORT = (
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n'
+ 'Upgrade: WebSocket\r\n'
+ 'Connection: Upgrade\r\n'
+ 'WebSocket-Origin: http://example.com\r\n'
+ 'WebSocket-Location: ws://example.com:8081/demo\r\n'
+ 'WebSocket-Protocol: sample\r\n'
+ '\r\n')
+
+_GOOD_RESPONSE_SECURE_NONDEF = (
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n'
+ 'Upgrade: WebSocket\r\n'
+ 'Connection: Upgrade\r\n'
+ 'WebSocket-Origin: http://example.com\r\n'
+ 'WebSocket-Location: wss://example.com:8081/demo\r\n'
+ 'WebSocket-Protocol: sample\r\n'
+ '\r\n')
+
+_GOOD_REQUEST_NO_PROTOCOL = (
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ }
+)
+
+_GOOD_RESPONSE_NO_PROTOCOL = (
+ 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n'
+ 'Upgrade: WebSocket\r\n'
+ 'Connection: Upgrade\r\n'
+ 'WebSocket-Origin: http://example.com\r\n'
+ 'WebSocket-Location: ws://example.com/demo\r\n'
+ '\r\n')
+
+_GOOD_REQUEST_WITH_OPTIONAL_HEADERS = (
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ 'AKey':'AValue',
+ 'EmptyValue':'',
+ }
+)
+
+_BAD_REQUESTS = (
+ ( # HTTP request
+ 80,
+ '/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',
+ }
+ ),
+ ( # Missing Upgrade
+ 80,
+ '/demo',
+ {
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ }
+ ),
+ ( # Wrong Upgrade
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'NonWebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ }
+ ),
+ ( # Empty WebSocket-Protocol
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'',
+ }
+ ),
+ ( # Wrong port number format
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com:0x50',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ }
+ ),
+ ( # Header/connection port mismatch
+ 8080,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'sample',
+ }
+ ),
+ ( # Illegal WebSocket-Protocol
+ 80,
+ '/demo',
+ {
+ 'Upgrade':'WebSocket',
+ 'Connection':'Upgrade',
+ 'Host':'example.com',
+ 'Origin':'http://example.com',
+ 'WebSocket-Protocol':'illegal\x09protocol',
+ }
+ ),
+)
+
+_STRICTLY_GOOD_REQUESTS = (
+ (
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # WebSocket-Protocol
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'WebSocket-Protocol: sample\r\n',
+ '\r\n',
+ ),
+ ( # WebSocket-Protocol and Cookie
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'WebSocket-Protocol: sample\r\n',
+ 'Cookie: xyz\r\n'
+ '\r\n',
+ ),
+ ( # Cookie
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'Cookie: abc/xyz\r\n'
+ 'Cookie2: $Version=1\r\n'
+ 'Cookie: abc\r\n'
+ '\r\n',
+ ),
+ (
+ 'GET / HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+)
+
+_NOT_STRICTLY_GOOD_REQUESTS = (
+ ( # Extra space after GET
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # Resource name doesn't stat with '/'
+ 'GET demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # No space after :
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade:WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # Lower case Upgrade header
+ 'GET /demo HTTP/1.1\r\n',
+ 'upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # Connection comes before Upgrade
+ 'GET /demo HTTP/1.1\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # Origin comes before Host
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Origin: http://example.com\r\n',
+ 'Host: example.com\r\n',
+ '\r\n',
+ ),
+ ( # Host continued to the next line
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example\r\n',
+ ' .com\r\n',
+ 'Origin: http://example.com\r\n',
+ '\r\n',
+ ),
+ ( # Cookie comes before WebSocket-Protocol
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'Cookie: xyz\r\n'
+ 'WebSocket-Protocol: sample\r\n',
+ '\r\n',
+ ),
+ ( # Unknown header
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'Content-Type: text/html\r\n'
+ '\r\n',
+ ),
+ ( # Cookie with continuation lines
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'Cookie: xyz\r\n',
+ ' abc\r\n',
+ ' defg\r\n',
+ '\r\n',
+ ),
+ ( # Wrong-case cookie
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'cookie: abc/xyz\r\n'
+ '\r\n',
+ ),
+ ( # Cookie, no space after colon
+ 'GET /demo HTTP/1.1\r\n',
+ 'Upgrade: WebSocket\r\n',
+ 'Connection: Upgrade\r\n',
+ 'Host: example.com\r\n',
+ 'Origin: http://example.com\r\n',
+ 'Cookie:abc/xyz\r\n'
+ '\r\n',
+ ),
+)
+
+
+def _create_request(request_def):
+ conn = mock.MockConn('')
+ conn.local_addr = ('0.0.0.0', request_def[0])
+ return mock.MockRequest(
+ uri=request_def[1],
+ headers_in=request_def[2],
+ connection=conn)
+
+
+def _create_get_memorized_lines(lines):
+ 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 HandshakerTest(unittest.TestCase):
+ def test_validate_protocol(self):
+ handshake._validate_protocol('sample') # should succeed.
+ handshake._validate_protocol('Sample') # should succeed.
+ handshake._validate_protocol('sample\x20protocol') # should succeed.
+ handshake._validate_protocol('sample\x7eprotocol') # should succeed.
+ self.assertRaises(handshake.HandshakeError,
+ handshake._validate_protocol,
+ '')
+ self.assertRaises(handshake.HandshakeError,
+ handshake._validate_protocol,
+ 'sample\x19protocol')
+ self.assertRaises(handshake.HandshakeError,
+ handshake._validate_protocol,
+ 'sample\x7fprotocol')
+ self.assertRaises(handshake.HandshakeError,
+ handshake._validate_protocol,
+ # "Japan" in Japanese
+ u'\u65e5\u672c')
+
+ def test_good_request_default_port(self):
+ request = _create_request(_GOOD_REQUEST)
+ handshaker = handshake.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_secure_default_port(self):
+ request = _create_request(_GOOD_REQUEST)
+ request.connection.local_addr = ('0.0.0.0', 443)
+ request.is_https_ = True
+ handshaker = handshake.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 = handshake.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 = handshake.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 = handshake.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 = handshake.Handshaker(request,
+ mock.MockDispatcher())
+ handshaker.do_handshake()
+ self.assertEqual('AValue',
+ request.headers_in['AKey'])
+ self.assertEqual('',
+ request.headers_in['EmptyValue'])
+
+ def test_bad_requests(self):
+ for request in map(_create_request, _BAD_REQUESTS):
+ handshaker = handshake.Handshaker(request,
+ mock.MockDispatcher())
+ self.assertRaises(handshake.HandshakeError, handshaker.do_handshake)
+
+ def test_strictly_good_requests(self):
+ for request in _create_requests_with_lines(_STRICTLY_GOOD_REQUESTS):
+ strict_handshaker = handshake.Handshaker(request,
+ mock.MockDispatcher(),
+ True)
+ strict_handshaker.do_handshake()
+
+ def test_not_strictly_good_requests(self):
+ for request in _create_requests_with_lines(_NOT_STRICTLY_GOOD_REQUESTS):
+ strict_handshaker = handshake.Handshaker(request,
+ mock.MockDispatcher(),
+ True)
+ self.assertRaises(handshake.HandshakeError,
+ strict_handshaker.do_handshake)
+
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py
new file mode 100644
index 0000000..2de77ba
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Tests for memorizingfile module."""
+
+
+import StringIO
+import unittest
+
+import config # This must be imported before mod_pywebsocket.
+from mod_pywebsocket import memorizingfile
+
+
+class UtilTest(unittest.TestCase):
+ 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 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, [])
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py
new file mode 100644
index 0000000..8b137d1
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Tests for mock module."""
+
+
+import Queue
+import threading
+import unittest
+
+import mock
+
+
+class MockConnTest(unittest.TestCase):
+ 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):
+ def test_read(self):
+ class LineReader(threading.Thread):
+ 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):
+ 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py
new file mode 100644
index 0000000..16b88e0
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Tests for msgutil module."""
+
+
+import Queue
+import unittest
+
+import config # This must be imported before mod_pywebsocket.
+from mod_pywebsocket import msgutil
+
+import mock
+
+
+def _create_request(read_data):
+ return mock.MockRequest(connection=mock.MockConn(read_data))
+
+def _create_blocking_request():
+ return mock.MockRequest(connection=mock.MockBlockingConn())
+
+class MessageTest(unittest.TestCase):
+ def test_send_message(self):
+ request = _create_request('')
+ msgutil.send_message(request, 'Hello')
+ self.assertEqual('\x00Hello\xff', 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('\x00\xe6\x97\xa5\xff',
+ request.connection.written_data())
+
+ def test_receive_message(self):
+ request = _create_request('\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('\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('\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('\x80\x06IGNORE\x00Hello\xff'
+ '\x01DISREGARD\xff\x00World!\xff')
+ self.assertEqual('Hello', msgutil.receive_message(request))
+ self.assertEqual('World!', msgutil.receive_message(request))
+
+ def test_payload_length(self):
+ for length, bytes in ((0, '\x00'), (0x7f, '\x7f'), (0x80, '\x81\x00'),
+ (0x1234, '\x80\xa4\x34')):
+ self.assertEqual(length,
+ msgutil._payload_length(_create_request(bytes)))
+
+ def test_receive_bytes(self):
+ request = _create_request('abcdefg')
+ self.assertEqual('abc', msgutil._receive_bytes(request, 3))
+ self.assertEqual('defg', msgutil._receive_bytes(request, 4))
+
+ def test_read_until(self):
+ request = _create_request('abcXdefgX')
+ self.assertEqual('abc', msgutil._read_until(request, 'X'))
+ self.assertEqual('defg', msgutil._read_until(request, 'X'))
+
+
+class MessageReceiverTest(unittest.TestCase):
+ def test_queue(self):
+ request = _create_blocking_request()
+ 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()
+ receiver = msgutil.MessageReceiver(request, onmessage_handler)
+
+ request.connection.put_bytes('\x00Hello!\xff')
+ self.assertEqual('Hello!', onmessage_queue.get())
+
+
+class MessageSenderTest(unittest.TestCase):
+ def test_send(self):
+ request = _create_blocking_request()
+ 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()
+ 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py
new file mode 100644
index 0000000..61f0db5
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Tests for util module."""
+
+
+import os
+import sys
+import unittest
+
+import config # This must be imported before mod_pywebsocket.
+from mod_pywebsocket import util
+
+_TEST_DATA_DIR = os.path.join(os.path.split(__file__)[0], 'testdata')
+
+class UtilTest(unittest.TestCase):
+ 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))
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+# vi:sts=4 sw=4 et
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README
new file mode 100644
index 0000000..c001aa5
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README
@@ -0,0 +1 @@
+Test data directory
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py
new file mode 100644
index 0000000..7f87c6a
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py
@@ -0,0 +1,31 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py
new file mode 100644
index 0000000..2c139fa
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py
@@ -0,0 +1,42 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py
new file mode 100644
index 0000000..b982d02
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py
@@ -0,0 +1,44 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py
new file mode 100644
index 0000000..17e7be1
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py
@@ -0,0 +1,45 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py
new file mode 100644
index 0000000..26352eb
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py
@@ -0,0 +1,39 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py
new file mode 100644
index 0000000..db3ff69
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py
@@ -0,0 +1,40 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
new file mode 100644
index 0000000..6bf659b
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py
@@ -0,0 +1,45 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
new file mode 100644
index 0000000..e0e2e55
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py
@@ -0,0 +1,45 @@
+# 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/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl
new file mode 100644
index 0000000..9dd01b4
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl
@@ -0,0 +1,2 @@
+#!/usr/bin/perl -wT
+print "Hello\n";