diff options
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket')
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"; |