summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket')
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/__init__.py172
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/_stream_exceptions.py82
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/common.py273
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/dispatch.py389
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py474
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/fast_masking.i98
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/__init__.py101
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/base.py396
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/hybi.py223
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py254
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/memorizingfile.py99
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/msgutil.py214
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py319
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/server_util.py87
-rwxr-xr-xtesting/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py491
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/stream.py950
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/util.py386
-rw-r--r--testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/websocket_server.py289
18 files changed, 0 insertions, 5297 deletions
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/__init__.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/__init__.py
deleted file mode 100644
index 28d5f5950f..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/__init__.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-""" A Standalone WebSocket Server for testing purposes
-
-mod_pywebsocket is an API that provides WebSocket functionalities with
-a standalone WebSocket server. It is intended for testing or
-experimental purposes.
-
-Installation
-============
-1. Follow standalone server documentation to start running the
-standalone server. It can be read by running the following command:
-
- $ pydoc mod_pywebsocket.standalone
-
-2. Once the standalone server is launched verify it by accessing
-http://localhost[:port]/console.html. Include the port number when
-specified on launch. If everything is working correctly, you
-will see a simple echo console.
-
-
-Writing WebSocket handlers
-==========================
-
-When a WebSocket request comes in, the resource name
-specified in the handshake is considered as if it is a file path under
-<websock_handlers> and the handler defined in
-<websock_handlers>/<resource_name>_wsh.py is invoked.
-
-For example, if the resource name is /example/chat, the handler defined in
-<websock_handlers>/example/chat_wsh.py is invoked.
-
-A WebSocket handler is composed of the following three functions:
-
- web_socket_do_extra_handshake(request)
- web_socket_transfer_data(request)
- web_socket_passive_closing_handshake(request)
-
-where:
- request: mod_python request.
-
-web_socket_do_extra_handshake is called during the handshake after the
-headers are successfully parsed and WebSocket properties (ws_origin,
-and ws_resource) are added to request. A handler
-can reject the request by raising an exception.
-
-A request object has the following properties that you can use during the
-extra handshake (web_socket_do_extra_handshake):
-- ws_resource
-- ws_origin
-- ws_version
-- ws_extensions
-- ws_deflate
-- ws_protocol
-- ws_requested_protocols
-
-The last two are a bit tricky. See the next subsection.
-
-
-Subprotocol Negotiation
------------------------
-
-ws_protocol is always set to None when
-web_socket_do_extra_handshake is called. If ws_requested_protocols is not
-None, you must choose one subprotocol from this list and set it to
-ws_protocol.
-
-Data Transfer
--------------
-
-web_socket_transfer_data is called after the handshake completed
-successfully. A handler can receive/send messages from/to the client
-using request. mod_pywebsocket.msgutil module provides utilities
-for data transfer.
-
-You can receive a message by the following statement.
-
- message = request.ws_stream.receive_message()
-
-This call blocks until any complete text frame arrives, and the payload data
-of the incoming frame will be stored into message. When you're using IETF
-HyBi 00 or later protocol, receive_message() will return None on receiving
-client-initiated closing handshake. When any error occurs, receive_message()
-will raise some exception.
-
-You can send a message by the following statement.
-
- request.ws_stream.send_message(message)
-
-
-Closing Connection
-------------------
-
-Executing the following statement or just return-ing from
-web_socket_transfer_data cause connection close.
-
- request.ws_stream.close_connection()
-
-close_connection will wait
-for closing handshake acknowledgement coming from the client. When it
-couldn't receive a valid acknowledgement, raises an exception.
-
-web_socket_passive_closing_handshake is called after the server receives
-incoming closing frame from the client peer immediately. You can specify
-code and reason by return values. They are sent as a outgoing closing frame
-from the server. A request object has the following properties that you can
-use in web_socket_passive_closing_handshake.
-- ws_close_code
-- ws_close_reason
-
-
-Threading
----------
-
-A WebSocket handler must be thread-safe. The standalone
-server uses threads by default.
-
-
-Configuring WebSocket Extension Processors
-------------------------------------------
-
-See extensions.py for supported WebSocket extensions. Note that they are
-unstable and their APIs are subject to change substantially.
-
-A request object has these extension processing related attributes.
-
-- ws_requested_extensions:
-
- A list of common.ExtensionParameter instances representing extension
- parameters received from the client in the client's opening handshake.
- You shouldn't modify it manually.
-
-- ws_extensions:
-
- A list of common.ExtensionParameter instances representing extension
- parameters to send back to the client in the server's opening handshake.
- You shouldn't touch it directly. Instead, call methods on extension
- processors.
-
-- ws_extension_processors:
-
- A list of loaded extension processors. Find the processor for the
- extension you want to configure from it, and call its methods.
-"""
-
-# vi:sts=4 sw=4 et tw=72
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/_stream_exceptions.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/_stream_exceptions.py
deleted file mode 100644
index b47878bc4a..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/_stream_exceptions.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright 2020, 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.
-"""Stream Exceptions.
-"""
-
-# 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.
-
-
-# Exceptions
-class ConnectionTerminatedException(Exception):
- """This exception will be raised when a connection is terminated
- unexpectedly.
- """
-
- pass
-
-
-class InvalidFrameException(ConnectionTerminatedException):
- """This exception will be raised when we received an invalid frame we
- cannot parse.
- """
-
- pass
-
-
-class BadOperationException(Exception):
- """This exception will be raised when send_message() is called on
- server-terminated connection or receive_message() is called on
- client-terminated connection.
- """
-
- pass
-
-
-class UnsupportedFrameException(Exception):
- """This exception will be raised when we receive a frame with flag, opcode
- we cannot handle. Handlers can just catch and ignore this exception and
- call receive_message() again to continue processing the next frame.
- """
-
- pass
-
-
-class InvalidUTF8Exception(Exception):
- """This exception will be raised when we receive a text frame which
- contains invalid UTF-8 strings.
- """
-
- pass
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/common.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/common.py
deleted file mode 100644
index 9cb11f15cb..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/common.py
+++ /dev/null
@@ -1,273 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""This file must not depend on any module specific to the WebSocket protocol.
-"""
-
-from __future__ import absolute_import
-from mod_pywebsocket import http_header_util
-
-# Additional log level definitions.
-LOGLEVEL_FINE = 9
-
-# Constants indicating WebSocket protocol version.
-VERSION_HYBI13 = 13
-VERSION_HYBI14 = 13
-VERSION_HYBI15 = 13
-VERSION_HYBI16 = 13
-VERSION_HYBI17 = 13
-
-# Constants indicating WebSocket protocol latest version.
-VERSION_HYBI_LATEST = VERSION_HYBI13
-
-# Port numbers
-DEFAULT_WEB_SOCKET_PORT = 80
-DEFAULT_WEB_SOCKET_SECURE_PORT = 443
-
-# Schemes
-WEB_SOCKET_SCHEME = 'ws'
-WEB_SOCKET_SECURE_SCHEME = 'wss'
-
-# Frame opcodes defined in the spec.
-OPCODE_CONTINUATION = 0x0
-OPCODE_TEXT = 0x1
-OPCODE_BINARY = 0x2
-OPCODE_CLOSE = 0x8
-OPCODE_PING = 0x9
-OPCODE_PONG = 0xa
-
-# UUID for the opening handshake and frame masking.
-WEBSOCKET_ACCEPT_UUID = b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
-
-# Opening handshake header names and expected values.
-UPGRADE_HEADER = 'Upgrade'
-WEBSOCKET_UPGRADE_TYPE = 'websocket'
-CONNECTION_HEADER = 'Connection'
-UPGRADE_CONNECTION_TYPE = 'Upgrade'
-HOST_HEADER = 'Host'
-ORIGIN_HEADER = 'Origin'
-SEC_WEBSOCKET_KEY_HEADER = 'Sec-WebSocket-Key'
-SEC_WEBSOCKET_ACCEPT_HEADER = 'Sec-WebSocket-Accept'
-SEC_WEBSOCKET_VERSION_HEADER = 'Sec-WebSocket-Version'
-SEC_WEBSOCKET_PROTOCOL_HEADER = 'Sec-WebSocket-Protocol'
-SEC_WEBSOCKET_EXTENSIONS_HEADER = 'Sec-WebSocket-Extensions'
-
-# Extensions
-PERMESSAGE_DEFLATE_EXTENSION = 'permessage-deflate'
-
-# Status codes
-# Code STATUS_NO_STATUS_RECEIVED, STATUS_ABNORMAL_CLOSURE, and
-# STATUS_TLS_HANDSHAKE are pseudo codes to indicate specific error cases.
-# Could not be used for codes in actual closing frames.
-# Application level errors must use codes in the range
-# STATUS_USER_REGISTERED_BASE to STATUS_USER_PRIVATE_MAX. The codes in the
-# range STATUS_USER_REGISTERED_BASE to STATUS_USER_REGISTERED_MAX are managed
-# by IANA. Usually application must define user protocol level errors in the
-# range STATUS_USER_PRIVATE_BASE to STATUS_USER_PRIVATE_MAX.
-STATUS_NORMAL_CLOSURE = 1000
-STATUS_GOING_AWAY = 1001
-STATUS_PROTOCOL_ERROR = 1002
-STATUS_UNSUPPORTED_DATA = 1003
-STATUS_NO_STATUS_RECEIVED = 1005
-STATUS_ABNORMAL_CLOSURE = 1006
-STATUS_INVALID_FRAME_PAYLOAD_DATA = 1007
-STATUS_POLICY_VIOLATION = 1008
-STATUS_MESSAGE_TOO_BIG = 1009
-STATUS_MANDATORY_EXTENSION = 1010
-STATUS_INTERNAL_ENDPOINT_ERROR = 1011
-STATUS_TLS_HANDSHAKE = 1015
-STATUS_USER_REGISTERED_BASE = 3000
-STATUS_USER_REGISTERED_MAX = 3999
-STATUS_USER_PRIVATE_BASE = 4000
-STATUS_USER_PRIVATE_MAX = 4999
-# Following definitions are aliases to keep compatibility. Applications must
-# not use these obsoleted definitions anymore.
-STATUS_NORMAL = STATUS_NORMAL_CLOSURE
-STATUS_UNSUPPORTED = STATUS_UNSUPPORTED_DATA
-STATUS_CODE_NOT_AVAILABLE = STATUS_NO_STATUS_RECEIVED
-STATUS_ABNORMAL_CLOSE = STATUS_ABNORMAL_CLOSURE
-STATUS_INVALID_FRAME_PAYLOAD = STATUS_INVALID_FRAME_PAYLOAD_DATA
-STATUS_MANDATORY_EXT = STATUS_MANDATORY_EXTENSION
-
-# HTTP status codes
-HTTP_STATUS_BAD_REQUEST = 400
-HTTP_STATUS_FORBIDDEN = 403
-HTTP_STATUS_NOT_FOUND = 404
-
-
-def is_control_opcode(opcode):
- return (opcode >> 3) == 1
-
-
-class ExtensionParameter(object):
- """This is exchanged on extension negotiation in opening handshake."""
- def __init__(self, name):
- self._name = name
- # TODO(tyoshino): Change the data structure to more efficient one such
- # as dict when the spec changes to say like
- # - Parameter names must be unique
- # - The order of parameters is not significant
- self._parameters = []
-
- def name(self):
- """Return the extension name."""
- return self._name
-
- def add_parameter(self, name, value):
- """Add a parameter."""
- self._parameters.append((name, value))
-
- def get_parameters(self):
- """Return the parameters."""
- return self._parameters
-
- def get_parameter_names(self):
- """Return the names of the parameters."""
- return [name for name, unused_value in self._parameters]
-
- def has_parameter(self, name):
- """Test if a parameter exists."""
- for param_name, param_value in self._parameters:
- if param_name == name:
- return True
- return False
-
- def get_parameter_value(self, name):
- """Get the value of a specific parameter."""
- for param_name, param_value in self._parameters:
- if param_name == name:
- return param_value
-
-
-class ExtensionParsingException(Exception):
- """Exception to handle errors in extension parsing."""
- def __init__(self, name):
- super(ExtensionParsingException, self).__init__(name)
-
-
-def _parse_extension_param(state, definition):
- param_name = http_header_util.consume_token(state)
-
- if param_name is None:
- raise ExtensionParsingException('No valid parameter name found')
-
- http_header_util.consume_lwses(state)
-
- if not http_header_util.consume_string(state, '='):
- definition.add_parameter(param_name, None)
- return
-
- http_header_util.consume_lwses(state)
-
- # TODO(tyoshino): Add code to validate that parsed param_value is token
- param_value = http_header_util.consume_token_or_quoted_string(state)
- if param_value is None:
- raise ExtensionParsingException(
- 'No valid parameter value found on the right-hand side of '
- 'parameter %r' % param_name)
-
- definition.add_parameter(param_name, param_value)
-
-
-def _parse_extension(state):
- extension_token = http_header_util.consume_token(state)
- if extension_token is None:
- return None
-
- extension = ExtensionParameter(extension_token)
-
- while True:
- http_header_util.consume_lwses(state)
-
- if not http_header_util.consume_string(state, ';'):
- break
-
- http_header_util.consume_lwses(state)
-
- try:
- _parse_extension_param(state, extension)
- except ExtensionParsingException as e:
- raise ExtensionParsingException(
- 'Failed to parse parameter for %r (%r)' % (extension_token, e))
-
- return extension
-
-
-def parse_extensions(data):
- """Parse Sec-WebSocket-Extensions header value.
-
- Returns a list of ExtensionParameter objects.
- Leading LWSes must be trimmed.
- """
- state = http_header_util.ParsingState(data)
-
- extension_list = []
- while True:
- extension = _parse_extension(state)
- if extension is not None:
- extension_list.append(extension)
-
- http_header_util.consume_lwses(state)
-
- if http_header_util.peek(state) is None:
- break
-
- if not http_header_util.consume_string(state, ','):
- raise ExtensionParsingException(
- 'Failed to parse Sec-WebSocket-Extensions header: '
- 'Expected a comma but found %r' % http_header_util.peek(state))
-
- http_header_util.consume_lwses(state)
-
- if len(extension_list) == 0:
- raise ExtensionParsingException('No valid extension entry found')
-
- return extension_list
-
-
-def format_extension(extension):
- """Format an ExtensionParameter object."""
- formatted_params = [extension.name()]
- for param_name, param_value in extension.get_parameters():
- if param_value is None:
- formatted_params.append(param_name)
- else:
- quoted_value = http_header_util.quote_if_necessary(param_value)
- formatted_params.append('%s=%s' % (param_name, quoted_value))
- return '; '.join(formatted_params)
-
-
-def format_extensions(extension_list):
- """Format a list of ExtensionParameter objects."""
- formatted_extension_list = []
- for extension in extension_list:
- formatted_extension_list.append(format_extension(extension))
- return ', '.join(formatted_extension_list)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/dispatch.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/dispatch.py
deleted file mode 100644
index 56cbb3c8a5..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/dispatch.py
+++ /dev/null
@@ -1,389 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Dispatch WebSocket request.
-"""
-
-from __future__ import absolute_import
-import io
-import logging
-import os
-import re
-import traceback
-
-from mod_pywebsocket import common
-from mod_pywebsocket import handshake
-from mod_pywebsocket import msgutil
-from mod_pywebsocket import stream
-from mod_pywebsocket import util
-
-_SOURCE_PATH_PATTERN = re.compile(r'(?i)_wsh\.py$')
-_SOURCE_SUFFIX = '_wsh.py'
-_DO_EXTRA_HANDSHAKE_HANDLER_NAME = 'web_socket_do_extra_handshake'
-_TRANSFER_DATA_HANDLER_NAME = 'web_socket_transfer_data'
-_PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME = (
- 'web_socket_passive_closing_handshake')
-
-
-class DispatchException(Exception):
- """Exception in dispatching WebSocket request."""
- def __init__(self, name, status=common.HTTP_STATUS_NOT_FOUND):
- super(DispatchException, self).__init__(name)
- self.status = status
-
-
-def _default_passive_closing_handshake_handler(request):
- """Default web_socket_passive_closing_handshake handler."""
-
- return common.STATUS_NORMAL_CLOSURE, ''
-
-
-def _normalize_path(path):
- """Normalize path.
-
- Args:
- path: the path to normalize.
-
- Path is converted to the absolute path.
- The input path can use either '\\' or '/' as the separator.
- The normalized path always uses '/' regardless of the platform.
- """
-
- path = path.replace('\\', os.path.sep)
- path = os.path.realpath(path)
- path = path.replace('\\', '/')
- return path
-
-
-def _create_path_to_resource_converter(base_dir):
- """Returns a function that converts the path of a WebSocket handler source
- file to a resource string by removing the path to the base directory from
- its head, removing _SOURCE_SUFFIX from its tail, and replacing path
- separators in it with '/'.
-
- Args:
- base_dir: the path to the base directory.
- """
-
- base_dir = _normalize_path(base_dir)
-
- base_len = len(base_dir)
- suffix_len = len(_SOURCE_SUFFIX)
-
- def converter(path):
- if not path.endswith(_SOURCE_SUFFIX):
- return None
- # _normalize_path must not be used because resolving symlink breaks
- # following path check.
- path = path.replace('\\', '/')
- if not path.startswith(base_dir):
- return None
- return path[base_len:-suffix_len]
-
- return converter
-
-
-def _enumerate_handler_file_paths(directory):
- """Returns a generator that enumerates WebSocket Handler source file names
- in the given directory.
- """
-
- for root, unused_dirs, files in os.walk(directory):
- for base in files:
- path = os.path.join(root, base)
- if _SOURCE_PATH_PATTERN.search(path):
- yield path
-
-
-class _HandlerSuite(object):
- """A handler suite holder class."""
- def __init__(self, do_extra_handshake, transfer_data,
- passive_closing_handshake):
- self.do_extra_handshake = do_extra_handshake
- self.transfer_data = transfer_data
- self.passive_closing_handshake = passive_closing_handshake
-
-
-def _source_handler_file(handler_definition):
- """Source a handler definition string.
-
- Args:
- handler_definition: a string containing Python statements that define
- handler functions.
- """
-
- global_dic = {}
- try:
- # This statement is gramatically different in python 2 and 3.
- # Hence, yapf will complain about this. To overcome this, we disable
- # yapf for this line.
- exec(handler_definition, global_dic) # yapf: disable
- except Exception:
- raise DispatchException('Error in sourcing handler:' +
- traceback.format_exc())
- passive_closing_handshake_handler = None
- try:
- passive_closing_handshake_handler = _extract_handler(
- global_dic, _PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME)
- except Exception:
- passive_closing_handshake_handler = (
- _default_passive_closing_handshake_handler)
- return _HandlerSuite(
- _extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME),
- _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME),
- passive_closing_handshake_handler)
-
-
-def _extract_handler(dic, name):
- """Extracts a callable with the specified name from the given dictionary
- dic.
- """
-
- if name not in dic:
- raise DispatchException('%s is not defined.' % name)
- handler = dic[name]
- if not callable(handler):
- raise DispatchException('%s is not callable.' % name)
- return handler
-
-
-class Dispatcher(object):
- """Dispatches WebSocket requests.
-
- This class maintains a map from resource name to handlers.
- """
- def __init__(self,
- root_dir,
- scan_dir=None,
- allow_handlers_outside_root_dir=True,
- handler_encoding=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.
- allow_handlers_outside_root_dir: Scans handler files even if their
- canonical path is not under root_dir.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._handler_suite_map = {}
- self._source_warnings = []
- if scan_dir is None:
- scan_dir = root_dir
- if not os.path.realpath(scan_dir).startswith(
- os.path.realpath(root_dir)):
- raise DispatchException('scan_dir:%s must be a directory under '
- 'root_dir:%s.' % (scan_dir, root_dir))
- self._source_handler_files_in_dir(root_dir, scan_dir,
- allow_handlers_outside_root_dir,
- handler_encoding)
-
- def add_resource_path_alias(self, alias_resource_path,
- existing_resource_path):
- """Add resource path alias.
-
- Once added, request to alias_resource_path would be handled by
- handler registered for existing_resource_path.
-
- Args:
- alias_resource_path: alias resource path
- existing_resource_path: existing resource path
- """
- try:
- handler_suite = self._handler_suite_map[existing_resource_path]
- self._handler_suite_map[alias_resource_path] = handler_suite
- except KeyError:
- raise DispatchException('No handler for: %r' %
- existing_resource_path)
-
- def source_warnings(self):
- """Return warnings in sourcing handlers."""
-
- return self._source_warnings
-
- def do_extra_handshake(self, request):
- """Do extra checking in WebSocket handshake.
-
- Select a handler based on request.uri and call its
- web_socket_do_extra_handshake function.
-
- Args:
- request: mod_python request.
-
- Raises:
- DispatchException: when handler was not found
- AbortedByUserException: when user handler abort connection
- HandshakeException: when opening handshake failed
- """
-
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- raise DispatchException('No handler for: %r' % request.ws_resource)
- do_extra_handshake_ = handler_suite.do_extra_handshake
- try:
- do_extra_handshake_(request)
- except handshake.AbortedByUserException as e:
- # Re-raise to tell the caller of this function to finish this
- # connection without sending any error.
- self._logger.debug('%s', traceback.format_exc())
- raise
- except Exception as e:
- util.prepend_message_to_exception(
- '%s raised exception for %s: ' %
- (_DO_EXTRA_HANDSHAKE_HANDLER_NAME, request.ws_resource), e)
- raise handshake.HandshakeException(e, common.HTTP_STATUS_FORBIDDEN)
-
- def transfer_data(self, request):
- """Let a handler transfer_data with a WebSocket client.
-
- Select a handler based on request.ws_resource and call its
- web_socket_transfer_data function.
-
- Args:
- request: mod_python request.
-
- Raises:
- DispatchException: when handler was not found
- AbortedByUserException: when user handler abort connection
- """
-
- # TODO(tyoshino): Terminate underlying TCP connection if possible.
- try:
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- raise DispatchException('No handler for: %r' %
- request.ws_resource)
- transfer_data_ = handler_suite.transfer_data
- transfer_data_(request)
-
- if not request.server_terminated:
- request.ws_stream.close_connection()
- # Catch non-critical exceptions the handler didn't handle.
- except handshake.AbortedByUserException as e:
- self._logger.debug('%s', traceback.format_exc())
- raise
- except msgutil.BadOperationException as e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(
- common.STATUS_INTERNAL_ENDPOINT_ERROR)
- except msgutil.InvalidFrameException as e:
- # InvalidFrameException must be caught before
- # ConnectionTerminatedException that catches InvalidFrameException.
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_PROTOCOL_ERROR)
- except msgutil.UnsupportedFrameException as e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_UNSUPPORTED_DATA)
- except stream.InvalidUTF8Exception as e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(
- common.STATUS_INVALID_FRAME_PAYLOAD_DATA)
- except msgutil.ConnectionTerminatedException as e:
- self._logger.debug('%s', e)
- except Exception as e:
- # Any other exceptions are forwarded to the caller of this
- # function.
- util.prepend_message_to_exception(
- '%s raised exception for %s: ' %
- (_TRANSFER_DATA_HANDLER_NAME, request.ws_resource), e)
- raise
-
- def passive_closing_handshake(self, request):
- """Prepare code and reason for responding client initiated closing
- handshake.
- """
-
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- return _default_passive_closing_handshake_handler(request)
- return handler_suite.passive_closing_handshake(request)
-
- def get_handler_suite(self, resource):
- """Retrieves two handlers (one for extra handshake processing, and one
- for data transfer) for the given request as a HandlerSuite object.
- """
-
- fragment = None
- if '#' in resource:
- resource, fragment = resource.split('#', 1)
- if '?' in resource:
- resource = resource.split('?', 1)[0]
- handler_suite = self._handler_suite_map.get(resource)
- if handler_suite and fragment:
- raise DispatchException(
- 'Fragment identifiers MUST NOT be used on WebSocket URIs',
- common.HTTP_STATUS_BAD_REQUEST)
- return handler_suite
-
- def _source_handler_files_in_dir(self, root_dir, scan_dir,
- allow_handlers_outside_root_dir,
- handler_encoding):
- """Source all the handler source files in the scan_dir directory.
-
- The resource path is determined relative to root_dir.
- """
-
- # We build a map from resource to handler code assuming that there's
- # only one path from root_dir to scan_dir and it can be obtained by
- # comparing realpath of them.
-
- # Here we cannot use abspath. See
- # https://bugs.webkit.org/show_bug.cgi?id=31603
-
- convert = _create_path_to_resource_converter(root_dir)
- scan_realpath = os.path.realpath(scan_dir)
- root_realpath = os.path.realpath(root_dir)
- for path in _enumerate_handler_file_paths(scan_realpath):
- if (not allow_handlers_outside_root_dir and
- (not os.path.realpath(path).startswith(root_realpath))):
- self._logger.debug(
- 'Canonical path of %s is not under root directory' % path)
- continue
- try:
- with io.open(path, encoding=handler_encoding) as handler_file:
- handler_suite = _source_handler_file(handler_file.read())
- except DispatchException as e:
- self._source_warnings.append('%s: %s' % (path, e))
- continue
- resource = convert(path)
- if resource is None:
- self._logger.debug('Path to resource conversion on %s failed' %
- path)
- else:
- self._handler_suite_map[convert(path)] = handler_suite
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py
deleted file mode 100644
index 314a949d45..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py
+++ /dev/null
@@ -1,474 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import absolute_import
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-from mod_pywebsocket.http_header_util import quote_if_necessary
-
-# The list of available server side extension processor classes.
-_available_processors = {}
-
-
-class ExtensionProcessorInterface(object):
- def __init__(self, request):
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._active = True
-
- def request(self):
- return self._request
-
- def name(self):
- return None
-
- def check_consistency_with_other_processors(self, processors):
- pass
-
- def set_active(self, active):
- self._active = active
-
- def is_active(self):
- return self._active
-
- def _get_extension_response_internal(self):
- return None
-
- def get_extension_response(self):
- if not self._active:
- self._logger.debug('Extension %s is deactivated', self.name())
- return None
-
- response = self._get_extension_response_internal()
- if response is None:
- self._active = False
- return response
-
- def _setup_stream_options_internal(self, stream_options):
- pass
-
- def setup_stream_options(self, stream_options):
- if self._active:
- self._setup_stream_options_internal(stream_options)
-
-
-def _log_outgoing_compression_ratio(logger, original_bytes, filtered_bytes,
- average_ratio):
- # Print inf when ratio is not available.
- ratio = float('inf')
- if original_bytes != 0:
- ratio = float(filtered_bytes) / original_bytes
-
- logger.debug('Outgoing compression ratio: %f (average: %f)' %
- (ratio, average_ratio))
-
-
-def _log_incoming_compression_ratio(logger, received_bytes, filtered_bytes,
- average_ratio):
- # Print inf when ratio is not available.
- ratio = float('inf')
- if filtered_bytes != 0:
- ratio = float(received_bytes) / filtered_bytes
-
- logger.debug('Incoming compression ratio: %f (average: %f)' %
- (ratio, average_ratio))
-
-
-def _parse_window_bits(bits):
- """Return parsed integer value iff the given string conforms to the
- grammar of the window bits extension parameters.
- """
-
- if bits is None:
- raise ValueError('Value is required')
-
- # For non integer values such as "10.0", ValueError will be raised.
- int_bits = int(bits)
-
- # First condition is to drop leading zero case e.g. "08".
- if bits != str(int_bits) or int_bits < 8 or int_bits > 15:
- raise ValueError('Invalid value: %r' % bits)
-
- return int_bits
-
-
-class _AverageRatioCalculator(object):
- """Stores total bytes of original and result data, and calculates average
- result / original ratio.
- """
- def __init__(self):
- self._total_original_bytes = 0
- self._total_result_bytes = 0
-
- def add_original_bytes(self, value):
- self._total_original_bytes += value
-
- def add_result_bytes(self, value):
- self._total_result_bytes += value
-
- def get_average_ratio(self):
- if self._total_original_bytes != 0:
- return (float(self._total_result_bytes) /
- self._total_original_bytes)
- else:
- return float('inf')
-
-
-class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface):
- """permessage-deflate extension processor.
-
- Specification:
- http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-08
- """
-
- _SERVER_MAX_WINDOW_BITS_PARAM = 'server_max_window_bits'
- _SERVER_NO_CONTEXT_TAKEOVER_PARAM = 'server_no_context_takeover'
- _CLIENT_MAX_WINDOW_BITS_PARAM = 'client_max_window_bits'
- _CLIENT_NO_CONTEXT_TAKEOVER_PARAM = 'client_no_context_takeover'
-
- def __init__(self, request):
- """Construct PerMessageDeflateExtensionProcessor."""
-
- ExtensionProcessorInterface.__init__(self, request)
- self._logger = util.get_class_logger(self)
-
- self._preferred_client_max_window_bits = None
- self._client_no_context_takeover = False
-
- def name(self):
- # This method returns "deflate" (not "permessage-deflate") for
- # compatibility.
- return 'deflate'
-
- def _get_extension_response_internal(self):
- for name in self._request.get_parameter_names():
- if name not in [
- self._SERVER_MAX_WINDOW_BITS_PARAM,
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
- self._CLIENT_MAX_WINDOW_BITS_PARAM
- ]:
- self._logger.debug('Unknown parameter: %r', name)
- return None
-
- server_max_window_bits = None
- if self._request.has_parameter(self._SERVER_MAX_WINDOW_BITS_PARAM):
- server_max_window_bits = self._request.get_parameter_value(
- self._SERVER_MAX_WINDOW_BITS_PARAM)
- try:
- server_max_window_bits = _parse_window_bits(
- server_max_window_bits)
- except ValueError as e:
- self._logger.debug('Bad %s parameter: %r',
- self._SERVER_MAX_WINDOW_BITS_PARAM, e)
- return None
-
- server_no_context_takeover = self._request.has_parameter(
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM)
- if (server_no_context_takeover and self._request.get_parameter_value(
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM) is not None):
- self._logger.debug('%s parameter must not have a value: %r',
- self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
- server_no_context_takeover)
- return None
-
- # client_max_window_bits from a client indicates whether the client can
- # accept client_max_window_bits from a server or not.
- client_client_max_window_bits = self._request.has_parameter(
- self._CLIENT_MAX_WINDOW_BITS_PARAM)
- if (client_client_max_window_bits
- and self._request.get_parameter_value(
- self._CLIENT_MAX_WINDOW_BITS_PARAM) is not None):
- self._logger.debug(
- '%s parameter must not have a value in a '
- 'client\'s opening handshake: %r',
- self._CLIENT_MAX_WINDOW_BITS_PARAM,
- client_client_max_window_bits)
- return None
-
- self._rfc1979_deflater = util._RFC1979Deflater(
- server_max_window_bits, server_no_context_takeover)
-
- # Note that we prepare for incoming messages compressed with window
- # bits upto 15 regardless of the client_max_window_bits value to be
- # sent to the client.
- self._rfc1979_inflater = util._RFC1979Inflater()
-
- self._framer = _PerMessageDeflateFramer(server_max_window_bits,
- server_no_context_takeover)
- self._framer.set_bfinal(False)
- self._framer.set_compress_outgoing_enabled(True)
-
- response = common.ExtensionParameter(self._request.name())
-
- if server_max_window_bits is not None:
- response.add_parameter(self._SERVER_MAX_WINDOW_BITS_PARAM,
- str(server_max_window_bits))
-
- if server_no_context_takeover:
- response.add_parameter(self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
- None)
-
- if self._preferred_client_max_window_bits is not None:
- if not client_client_max_window_bits:
- self._logger.debug(
- 'Processor is configured to use %s but '
- 'the client cannot accept it',
- self._CLIENT_MAX_WINDOW_BITS_PARAM)
- return None
- response.add_parameter(self._CLIENT_MAX_WINDOW_BITS_PARAM,
- str(self._preferred_client_max_window_bits))
-
- if self._client_no_context_takeover:
- response.add_parameter(self._CLIENT_NO_CONTEXT_TAKEOVER_PARAM,
- None)
-
- self._logger.debug('Enable %s extension ('
- 'request: server_max_window_bits=%s; '
- 'server_no_context_takeover=%r, '
- 'response: client_max_window_bits=%s; '
- 'client_no_context_takeover=%r)' %
- (self._request.name(), server_max_window_bits,
- server_no_context_takeover,
- self._preferred_client_max_window_bits,
- self._client_no_context_takeover))
-
- return response
-
- def _setup_stream_options_internal(self, stream_options):
- self._framer.setup_stream_options(stream_options)
-
- def set_client_max_window_bits(self, value):
- """If this option is specified, this class adds the
- client_max_window_bits extension parameter to the handshake response,
- but doesn't reduce the LZ77 sliding window size of its inflater.
- I.e., you can use this for testing client implementation but cannot
- reduce memory usage of this class.
-
- If this method has been called with True and an offer without the
- client_max_window_bits extension parameter is received,
-
- - (When processing the permessage-deflate extension) this processor
- declines the request.
- - (When processing the permessage-compress extension) this processor
- accepts the request.
- """
-
- self._preferred_client_max_window_bits = value
-
- def set_client_no_context_takeover(self, value):
- """If this option is specified, this class adds the
- client_no_context_takeover extension parameter to the handshake
- response, but doesn't reset inflater for each message. I.e., you can
- use this for testing client implementation but cannot reduce memory
- usage of this class.
- """
-
- self._client_no_context_takeover = value
-
- def set_bfinal(self, value):
- self._framer.set_bfinal(value)
-
- def enable_outgoing_compression(self):
- self._framer.set_compress_outgoing_enabled(True)
-
- def disable_outgoing_compression(self):
- self._framer.set_compress_outgoing_enabled(False)
-
-
-class _PerMessageDeflateFramer(object):
- """A framer for extensions with per-message DEFLATE feature."""
- def __init__(self, deflate_max_window_bits, deflate_no_context_takeover):
- self._logger = util.get_class_logger(self)
-
- self._rfc1979_deflater = util._RFC1979Deflater(
- deflate_max_window_bits, deflate_no_context_takeover)
-
- self._rfc1979_inflater = util._RFC1979Inflater()
-
- self._bfinal = False
-
- self._compress_outgoing_enabled = False
-
- # True if a message is fragmented and compression is ongoing.
- self._compress_ongoing = False
-
- # Calculates
- # (Total outgoing bytes supplied to this filter) /
- # (Total bytes sent to the network after applying this filter)
- self._outgoing_average_ratio_calculator = _AverageRatioCalculator()
-
- # Calculates
- # (Total bytes received from the network) /
- # (Total incoming bytes obtained after applying this filter)
- self._incoming_average_ratio_calculator = _AverageRatioCalculator()
-
- def set_bfinal(self, value):
- self._bfinal = value
-
- def set_compress_outgoing_enabled(self, value):
- self._compress_outgoing_enabled = value
-
- def _process_incoming_message(self, message, decompress):
- if not decompress:
- return message
-
- received_payload_size = len(message)
- self._incoming_average_ratio_calculator.add_result_bytes(
- received_payload_size)
-
- message = self._rfc1979_inflater.filter(message)
-
- filtered_payload_size = len(message)
- self._incoming_average_ratio_calculator.add_original_bytes(
- filtered_payload_size)
-
- _log_incoming_compression_ratio(
- self._logger, received_payload_size, filtered_payload_size,
- self._incoming_average_ratio_calculator.get_average_ratio())
-
- return message
-
- def _process_outgoing_message(self, message, end, binary):
- if not binary:
- message = message.encode('utf-8')
-
- if not self._compress_outgoing_enabled:
- return message
-
- original_payload_size = len(message)
- self._outgoing_average_ratio_calculator.add_original_bytes(
- original_payload_size)
-
- message = self._rfc1979_deflater.filter(message,
- end=end,
- bfinal=self._bfinal)
-
- filtered_payload_size = len(message)
- self._outgoing_average_ratio_calculator.add_result_bytes(
- filtered_payload_size)
-
- _log_outgoing_compression_ratio(
- self._logger, original_payload_size, filtered_payload_size,
- self._outgoing_average_ratio_calculator.get_average_ratio())
-
- if not self._compress_ongoing:
- self._outgoing_frame_filter.set_compression_bit()
- self._compress_ongoing = not end
- return message
-
- def _process_incoming_frame(self, frame):
- if frame.rsv1 == 1 and not common.is_control_opcode(frame.opcode):
- self._incoming_message_filter.decompress_next_message()
- frame.rsv1 = 0
-
- def _process_outgoing_frame(self, frame, compression_bit):
- if (not compression_bit or common.is_control_opcode(frame.opcode)):
- return
-
- frame.rsv1 = 1
-
- def setup_stream_options(self, stream_options):
- """Creates filters and sets them to the StreamOptions."""
- class _OutgoingMessageFilter(object):
- def __init__(self, parent):
- self._parent = parent
-
- def filter(self, message, end=True, binary=False):
- return self._parent._process_outgoing_message(
- message, end, binary)
-
- class _IncomingMessageFilter(object):
- def __init__(self, parent):
- self._parent = parent
- self._decompress_next_message = False
-
- def decompress_next_message(self):
- self._decompress_next_message = True
-
- def filter(self, message):
- message = self._parent._process_incoming_message(
- message, self._decompress_next_message)
- self._decompress_next_message = False
- return message
-
- self._outgoing_message_filter = _OutgoingMessageFilter(self)
- self._incoming_message_filter = _IncomingMessageFilter(self)
- stream_options.outgoing_message_filters.append(
- self._outgoing_message_filter)
- stream_options.incoming_message_filters.append(
- self._incoming_message_filter)
-
- class _OutgoingFrameFilter(object):
- def __init__(self, parent):
- self._parent = parent
- self._set_compression_bit = False
-
- def set_compression_bit(self):
- self._set_compression_bit = True
-
- def filter(self, frame):
- self._parent._process_outgoing_frame(frame,
- self._set_compression_bit)
- self._set_compression_bit = False
-
- class _IncomingFrameFilter(object):
- def __init__(self, parent):
- self._parent = parent
-
- def filter(self, frame):
- self._parent._process_incoming_frame(frame)
-
- self._outgoing_frame_filter = _OutgoingFrameFilter(self)
- self._incoming_frame_filter = _IncomingFrameFilter(self)
- stream_options.outgoing_frame_filters.append(
- self._outgoing_frame_filter)
- stream_options.incoming_frame_filters.append(
- self._incoming_frame_filter)
-
- stream_options.encode_text_message_to_utf8 = False
-
-
-_available_processors[common.PERMESSAGE_DEFLATE_EXTENSION] = (
- PerMessageDeflateExtensionProcessor)
-
-
-def get_extension_processor(extension_request):
- """Given an ExtensionParameter representing an extension offer received
- from a client, configures and returns an instance of the corresponding
- extension processor class.
- """
-
- processor_class = _available_processors.get(extension_request.name())
- if processor_class is None:
- return None
- return processor_class(extension_request)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/fast_masking.i b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/fast_masking.i
deleted file mode 100644
index ddaad27f53..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/fast_masking.i
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2013, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-%module fast_masking
-
-%include "cstring.i"
-
-%{
-#include <cstring>
-
-#ifdef __SSE2__
-#include <emmintrin.h>
-#endif
-%}
-
-%apply (char *STRING, int LENGTH) {
- (const char* payload, int payload_length),
- (const char* masking_key, int masking_key_length) };
-%cstring_output_allocate_size(
- char** result, int* result_length, delete [] *$1);
-
-%inline %{
-
-void mask(
- const char* payload, int payload_length,
- const char* masking_key, int masking_key_length,
- int masking_key_index,
- char** result, int* result_length) {
- *result = new char[payload_length];
- *result_length = payload_length;
- memcpy(*result, payload, payload_length);
-
- char* cursor = *result;
- char* cursor_end = *result + *result_length;
-
-#ifdef __SSE2__
- while ((cursor < cursor_end) &&
- (reinterpret_cast<size_t>(cursor) & 0xf)) {
- *cursor ^= masking_key[masking_key_index];
- ++cursor;
- masking_key_index = (masking_key_index + 1) % masking_key_length;
- }
- if (cursor == cursor_end) {
- return;
- }
-
- const int kBlockSize = 16;
- __m128i masking_key_block;
- for (int i = 0; i < kBlockSize; ++i) {
- *(reinterpret_cast<char*>(&masking_key_block) + i) =
- masking_key[masking_key_index];
- masking_key_index = (masking_key_index + 1) % masking_key_length;
- }
-
- while (cursor + kBlockSize <= cursor_end) {
- __m128i payload_block =
- _mm_load_si128(reinterpret_cast<__m128i*>(cursor));
- _mm_stream_si128(reinterpret_cast<__m128i*>(cursor),
- _mm_xor_si128(payload_block, masking_key_block));
- cursor += kBlockSize;
- }
-#endif
-
- while (cursor < cursor_end) {
- *cursor ^= masking_key[masking_key_index];
- ++cursor;
- masking_key_index = (masking_key_index + 1) % masking_key_length;
- }
-}
-
-%}
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/__init__.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/__init__.py
deleted file mode 100644
index 4bc1c67c57..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/__init__.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""WebSocket opening handshake processor. This class try to apply available
-opening handshake processors for each protocol version until a connection is
-successfully established.
-"""
-
-from __future__ import absolute_import
-import logging
-
-from mod_pywebsocket import common
-from mod_pywebsocket.handshake import hybi
-# Export AbortedByUserException, HandshakeException, and VersionException
-# symbol from this module.
-from mod_pywebsocket.handshake.base import AbortedByUserException
-from mod_pywebsocket.handshake.base import HandshakeException
-from mod_pywebsocket.handshake.base import VersionException
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def do_handshake(request, dispatcher):
- """Performs WebSocket handshake.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
-
- Handshaker will add attributes such as ws_resource in performing
- handshake.
- """
-
- _LOGGER.debug('Client\'s opening handshake resource: %r', request.uri)
- # To print mimetools.Message as escaped one-line string, we converts
- # headers_in to dict object. Without conversion, if we use %r, it just
- # prints the type and address, and if we use %s, it prints the original
- # header string as multiple lines.
- #
- # Both mimetools.Message and MpTable_Type of mod_python can be
- # converted to dict.
- #
- # mimetools.Message.__str__ returns the original header string.
- # dict(mimetools.Message object) returns the map from header names to
- # header values. While MpTable_Type doesn't have such __str__ but just
- # __repr__ which formats itself as well as dictionary object.
- _LOGGER.debug('Client\'s opening handshake headers: %r',
- dict(request.headers_in))
-
- handshakers = []
- handshakers.append(('RFC 6455', hybi.Handshaker(request, dispatcher)))
-
- for name, handshaker in handshakers:
- _LOGGER.debug('Trying protocol version %s', name)
- try:
- handshaker.do_handshake()
- _LOGGER.info('Established (%s protocol)', name)
- return
- except HandshakeException as e:
- _LOGGER.debug(
- 'Failed to complete opening handshake as %s protocol: %r',
- name, e)
- if e.status:
- raise e
- except AbortedByUserException as e:
- raise
- except VersionException as e:
- raise
-
- # TODO(toyoshim): Add a test to cover the case all handshakers fail.
- raise HandshakeException(
- 'Failed to complete opening handshake for all available protocols',
- status=common.HTTP_STATUS_BAD_REQUEST)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/base.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/base.py
deleted file mode 100644
index ffad0614d6..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/base.py
+++ /dev/null
@@ -1,396 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Common functions and exceptions used by WebSocket opening handshake
-processors.
-"""
-
-from __future__ import absolute_import
-
-from mod_pywebsocket import common
-from mod_pywebsocket import http_header_util
-from mod_pywebsocket.extensions import get_extension_processor
-from mod_pywebsocket.stream import StreamOptions
-from mod_pywebsocket.stream import Stream
-from mod_pywebsocket import util
-
-from six.moves import map
-from six.moves import range
-
-# Defining aliases for values used frequently.
-_VERSION_LATEST = common.VERSION_HYBI_LATEST
-_VERSION_LATEST_STRING = str(_VERSION_LATEST)
-_SUPPORTED_VERSIONS = [
- _VERSION_LATEST,
-]
-
-
-class AbortedByUserException(Exception):
- """Exception for aborting a connection intentionally.
-
- If this exception is raised in do_extra_handshake handler, the connection
- will be abandoned. No other WebSocket or HTTP(S) handler will be invoked.
-
- If this exception is raised in transfer_data_handler, the connection will
- be closed without closing handshake. No other WebSocket or HTTP(S) handler
- will be invoked.
- """
-
- pass
-
-
-class HandshakeException(Exception):
- """This exception will be raised when an error occurred while processing
- WebSocket initial handshake.
- """
- def __init__(self, name, status=None):
- super(HandshakeException, self).__init__(name)
- self.status = status
-
-
-class VersionException(Exception):
- """This exception will be raised when a version of client request does not
- match with version the server supports.
- """
- def __init__(self, name, supported_versions=''):
- """Construct an instance.
-
- Args:
- supported_version: a str object to show supported hybi versions.
- (e.g. '13')
- """
- super(VersionException, self).__init__(name)
- self.supported_versions = supported_versions
-
-
-def get_default_port(is_secure):
- if is_secure:
- return common.DEFAULT_WEB_SOCKET_SECURE_PORT
- else:
- return common.DEFAULT_WEB_SOCKET_PORT
-
-
-def validate_subprotocol(subprotocol):
- """Validate a value in the Sec-WebSocket-Protocol field.
-
- See the Section 4.1., 4.2.2., and 4.3. of RFC 6455.
- """
-
- if not subprotocol:
- raise HandshakeException('Invalid subprotocol name: empty')
-
- # Parameter should be encoded HTTP token.
- state = http_header_util.ParsingState(subprotocol)
- token = http_header_util.consume_token(state)
- rest = http_header_util.peek(state)
- # If |rest| is not None, |subprotocol| is not one token or invalid. If
- # |rest| is None, |token| must not be None because |subprotocol| is
- # concatenation of |token| and |rest| and is not None.
- if rest is not None:
- raise HandshakeException('Invalid non-token string in subprotocol '
- 'name: %r' % rest)
-
-
-def parse_host_header(request):
- fields = request.headers_in[common.HOST_HEADER].split(':', 1)
- if len(fields) == 1:
- return fields[0], get_default_port(request.is_https())
- try:
- return fields[0], int(fields[1])
- except ValueError as e:
- raise HandshakeException('Invalid port number format: %r' % e)
-
-
-def get_mandatory_header(request, key):
- value = request.headers_in.get(key)
- if value is None:
- raise HandshakeException('Header %s is not defined' % key)
- return value
-
-
-def validate_mandatory_header(request, key, expected_value, fail_status=None):
- value = get_mandatory_header(request, key)
-
- if value.lower() != expected_value.lower():
- raise HandshakeException(
- 'Expected %r for header %s but found %r (case-insensitive)' %
- (expected_value, key, value),
- status=fail_status)
-
-
-def parse_token_list(data):
- """Parses a header value which follows 1#token and returns parsed elements
- as a list of strings.
-
- Leading LWSes must be trimmed.
- """
-
- state = http_header_util.ParsingState(data)
-
- token_list = []
-
- while True:
- token = http_header_util.consume_token(state)
- if token is not None:
- token_list.append(token)
-
- http_header_util.consume_lwses(state)
-
- if http_header_util.peek(state) is None:
- break
-
- if not http_header_util.consume_string(state, ','):
- raise HandshakeException('Expected a comma but found %r' %
- http_header_util.peek(state))
-
- http_header_util.consume_lwses(state)
-
- if len(token_list) == 0:
- raise HandshakeException('No valid token found')
-
- return token_list
-
-
-class HandshakerBase(object):
- def __init__(self, request, dispatcher):
- self._logger = util.get_class_logger(self)
- self._request = request
- self._dispatcher = dispatcher
-
- """ subclasses must implement the five following methods """
-
- def _protocol_rfc(self):
- """ Return the name of the RFC that the handshake class is implementing.
- """
-
- raise AssertionError("subclasses should implement this method")
-
- def _transform_header(self, header):
- """
- :param header: header name
-
- transform the header name if needed. For example, HTTP/2 subclass will
- return the name of the header in lower case.
- """
-
- raise AssertionError("subclasses should implement this method")
-
- def _validate_request(self):
- """ validate that all the mandatory fields are set """
-
- raise AssertionError("subclasses should implement this method")
-
- def _set_accept(self):
- """ Computes accept value based on Sec-WebSocket-Accept if needed. """
-
- raise AssertionError("subclasses should implement this method")
-
- def _send_handshake(self):
- """ Prepare and send the response after it has been parsed and processed.
- """
-
- raise AssertionError("subclasses should implement this method")
-
- def do_handshake(self):
- self._request.ws_close_code = None
- self._request.ws_close_reason = None
-
- # Parsing.
- self._validate_request()
- self._request.ws_resource = self._request.uri
- self._request.ws_version = self._check_version()
-
- try:
- self._get_origin()
- self._set_protocol()
- self._parse_extensions()
-
- self._set_accept()
-
- self._logger.debug('Protocol version is ' + self._protocol_rfc())
-
- # Setup extension processors.
- self._request.ws_extension_processors = self._get_extension_processors_requested(
- )
-
- # List of extra headers. The extra handshake handler may add header
- # data as name/value pairs to this list and pywebsocket appends
- # them to the WebSocket handshake.
- self._request.extra_headers = []
-
- # Extra handshake handler may modify/remove processors.
- self._dispatcher.do_extra_handshake(self._request)
-
- stream_options = StreamOptions()
- self._process_extensions(stream_options)
-
- self._request.ws_stream = Stream(self._request, stream_options)
-
- if self._request.ws_requested_protocols is not None:
- if self._request.ws_protocol is None:
- raise HandshakeException(
- 'do_extra_handshake must choose one subprotocol from '
- 'ws_requested_protocols and set it to ws_protocol')
- validate_subprotocol(self._request.ws_protocol)
-
- self._logger.debug('Subprotocol accepted: %r',
- self._request.ws_protocol)
- else:
- if self._request.ws_protocol is not None:
- raise HandshakeException(
- 'ws_protocol must be None when the client didn\'t '
- 'request any subprotocol')
-
- self._send_handshake()
- except HandshakeException as e:
- if not e.status:
- # Fallback to 400 bad request by default.
- e.status = common.HTTP_STATUS_BAD_REQUEST
- raise e
-
- def _check_version(self):
- sec_websocket_version_header = self._transform_header(
- common.SEC_WEBSOCKET_VERSION_HEADER)
- version = get_mandatory_header(self._request,
- sec_websocket_version_header)
- if version == _VERSION_LATEST_STRING:
- return _VERSION_LATEST
-
- if version.find(',') >= 0:
- raise HandshakeException(
- 'Multiple versions (%r) are not allowed for header %s' %
- (version, sec_websocket_version_header),
- status=common.HTTP_STATUS_BAD_REQUEST)
- raise VersionException('Unsupported version %r for header %s' %
- (version, sec_websocket_version_header),
- supported_versions=', '.join(
- map(str, _SUPPORTED_VERSIONS)))
-
- def _get_origin(self):
- origin_header = self._transform_header(common.ORIGIN_HEADER)
- origin = self._request.headers_in.get(origin_header)
- if origin is None:
- self._logger.debug('Client request does not have origin header')
- self._request.ws_origin = origin
-
- def _set_protocol(self):
- self._request.ws_protocol = None
-
- sec_websocket_protocol_header = self._transform_header(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER)
- protocol_header = self._request.headers_in.get(
- sec_websocket_protocol_header)
-
- if protocol_header is None:
- self._request.ws_requested_protocols = None
- return
-
- self._request.ws_requested_protocols = parse_token_list(
- protocol_header)
- self._logger.debug('Subprotocols requested: %r',
- self._request.ws_requested_protocols)
-
- def _parse_extensions(self):
- sec_websocket_extensions_header = self._transform_header(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER)
- extensions_header = self._request.headers_in.get(
- sec_websocket_extensions_header)
- if not extensions_header:
- self._request.ws_requested_extensions = None
- return
-
- try:
- self._request.ws_requested_extensions = common.parse_extensions(
- extensions_header)
- except common.ExtensionParsingException as e:
- raise HandshakeException(
- 'Failed to parse sec-websocket-extensions header: %r' % e)
-
- self._logger.debug(
- 'Extensions requested: %r',
- list(
- map(common.ExtensionParameter.name,
- self._request.ws_requested_extensions)))
-
- def _get_extension_processors_requested(self):
- processors = []
- if self._request.ws_requested_extensions is not None:
- for extension_request in self._request.ws_requested_extensions:
- processor = get_extension_processor(extension_request)
- # Unknown extension requests are just ignored.
- if processor is not None:
- processors.append(processor)
- return processors
-
- def _process_extensions(self, stream_options):
- processors = [
- processor for processor in self._request.ws_extension_processors
- if processor is not None
- ]
-
- # Ask each processor if there are extensions on the request which
- # cannot co-exist. When processor decided other processors cannot
- # co-exist with it, the processor marks them (or itself) as
- # "inactive". The first extension processor has the right to
- # make the final call.
- for processor in reversed(processors):
- if processor.is_active():
- processor.check_consistency_with_other_processors(processors)
- processors = [
- processor for processor in processors if processor.is_active()
- ]
-
- accepted_extensions = []
-
- for index, processor in enumerate(processors):
- if not processor.is_active():
- continue
-
- extension_response = processor.get_extension_response()
- if extension_response is None:
- # Rejected.
- continue
-
- accepted_extensions.append(extension_response)
-
- processor.setup_stream_options(stream_options)
-
- # Inactivate all of the following compression extensions.
- for j in range(index + 1, len(processors)):
- processors[j].set_active(False)
-
- if len(accepted_extensions) > 0:
- self._request.ws_extensions = accepted_extensions
- self._logger.debug(
- 'Extensions accepted: %r',
- list(map(common.ExtensionParameter.name, accepted_extensions)))
- else:
- self._request.ws_extensions = None
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/hybi.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/hybi.py
deleted file mode 100644
index cf931db5a5..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/handshake/hybi.py
+++ /dev/null
@@ -1,223 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""This file provides the opening handshake processor for the WebSocket
-protocol (RFC 6455).
-
-Specification:
-http://tools.ietf.org/html/rfc6455
-"""
-
-from __future__ import absolute_import
-import base64
-import re
-from hashlib import sha1
-
-from mod_pywebsocket import common
-from mod_pywebsocket.handshake.base import get_mandatory_header
-from mod_pywebsocket.handshake.base import HandshakeException
-from mod_pywebsocket.handshake.base import parse_token_list
-from mod_pywebsocket.handshake.base import validate_mandatory_header
-from mod_pywebsocket.handshake.base import HandshakerBase
-from mod_pywebsocket import util
-
-# Used to validate the value in the Sec-WebSocket-Key header strictly. RFC 4648
-# disallows non-zero padding, so the character right before == must be any of
-# A, Q, g and w.
-_SEC_WEBSOCKET_KEY_REGEX = re.compile('^[+/0-9A-Za-z]{21}[AQgw]==$')
-
-
-def check_request_line(request):
- # 5.1 1. The three character UTF-8 string "GET".
- # 5.1 2. A UTF-8-encoded U+0020 SPACE character (0x20 byte).
- if request.method != u'GET':
- raise HandshakeException('Method is not GET: %r' % request.method)
-
- if request.protocol != u'HTTP/1.1':
- raise HandshakeException('Version is not HTTP/1.1: %r' %
- request.protocol)
-
-
-def compute_accept(key):
- """Computes value for the Sec-WebSocket-Accept header from value of the
- Sec-WebSocket-Key header.
- """
-
- accept_binary = sha1(key + common.WEBSOCKET_ACCEPT_UUID).digest()
- accept = base64.b64encode(accept_binary)
-
- return accept
-
-
-def compute_accept_from_unicode(unicode_key):
- """A wrapper function for compute_accept which takes a unicode string as an
- argument, and encodes it to byte string. It then passes it on to
- compute_accept.
- """
-
- key = unicode_key.encode('UTF-8')
- return compute_accept(key)
-
-
-def format_header(name, value):
- return u'%s: %s\r\n' % (name, value)
-
-
-class Handshaker(HandshakerBase):
- """Opening handshake processor for the WebSocket protocol (RFC 6455)."""
- def __init__(self, request, dispatcher):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
-
- Handshaker will add attributes such as ws_resource during handshake.
- """
- super(Handshaker, self).__init__(request, dispatcher)
-
- def _transform_header(self, header):
- return header
-
- def _protocol_rfc(self):
- return 'RFC 6455'
-
- def _validate_connection_header(self):
- connection = get_mandatory_header(self._request,
- common.CONNECTION_HEADER)
-
- try:
- connection_tokens = parse_token_list(connection)
- except HandshakeException as e:
- raise HandshakeException('Failed to parse %s: %s' %
- (common.CONNECTION_HEADER, e))
-
- connection_is_valid = False
- for token in connection_tokens:
- if token.lower() == common.UPGRADE_CONNECTION_TYPE.lower():
- connection_is_valid = True
- break
- if not connection_is_valid:
- raise HandshakeException(
- '%s header doesn\'t contain "%s"' %
- (common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
-
- def _validate_request(self):
- check_request_line(self._request)
- validate_mandatory_header(self._request, common.UPGRADE_HEADER,
- common.WEBSOCKET_UPGRADE_TYPE)
- self._validate_connection_header()
- unused_host = get_mandatory_header(self._request, common.HOST_HEADER)
-
- def _set_accept(self):
- # Key validation, response generation.
- key = self._get_key()
- accept = compute_accept(key)
- self._logger.debug('%s: %r (%s)', common.SEC_WEBSOCKET_ACCEPT_HEADER,
- accept, util.hexify(base64.b64decode(accept)))
- self._request._accept = accept
-
- def _validate_key(self, key):
- if key.find(',') >= 0:
- raise HandshakeException('Request has multiple %s header lines or '
- 'contains illegal character \',\': %r' %
- (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
- # Validate
- key_is_valid = False
- try:
- # Validate key by quick regex match before parsing by base64
- # module. Because base64 module skips invalid characters, we have
- # to do this in advance to make this server strictly reject illegal
- # keys.
- if _SEC_WEBSOCKET_KEY_REGEX.match(key):
- decoded_key = base64.b64decode(key)
- if len(decoded_key) == 16:
- key_is_valid = True
- except TypeError as e:
- pass
-
- if not key_is_valid:
- raise HandshakeException('Illegal value for header %s: %r' %
- (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
- return decoded_key
-
- def _get_key(self):
- key = get_mandatory_header(self._request,
- common.SEC_WEBSOCKET_KEY_HEADER)
-
- decoded_key = self._validate_key(key)
-
- self._logger.debug('%s: %r (%s)', common.SEC_WEBSOCKET_KEY_HEADER, key,
- util.hexify(decoded_key))
-
- return key.encode('UTF-8')
-
- def _create_handshake_response(self, accept):
- response = []
-
- response.append(u'HTTP/1.1 101 Switching Protocols\r\n')
-
- # WebSocket headers
- response.append(
- format_header(common.UPGRADE_HEADER,
- common.WEBSOCKET_UPGRADE_TYPE))
- response.append(
- format_header(common.CONNECTION_HEADER,
- common.UPGRADE_CONNECTION_TYPE))
- response.append(
- format_header(common.SEC_WEBSOCKET_ACCEPT_HEADER,
- accept.decode('UTF-8')))
- if self._request.ws_protocol is not None:
- response.append(
- format_header(common.SEC_WEBSOCKET_PROTOCOL_HEADER,
- self._request.ws_protocol))
- if (self._request.ws_extensions is not None
- and len(self._request.ws_extensions) != 0):
- response.append(
- format_header(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
- common.format_extensions(self._request.ws_extensions)))
-
- # Headers not specific for WebSocket
- for name, value in self._request.extra_headers:
- response.append(format_header(name, value))
-
- response.append(u'\r\n')
-
- return u''.join(response)
-
- def _send_handshake(self):
- raw_response = self._create_handshake_response(self._request._accept)
- self._request.connection.write(raw_response.encode('UTF-8'))
- self._logger.debug('Sent server\'s opening handshake: %r',
- raw_response)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py
deleted file mode 100644
index 21fde59af1..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py
+++ /dev/null
@@ -1,254 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Utilities for parsing and formatting headers that follow the grammar defined
-in HTTP RFC http://www.ietf.org/rfc/rfc2616.txt.
-"""
-
-from __future__ import absolute_import
-import six.moves.urllib.parse
-
-_SEPARATORS = '()<>@,;:\\"/[]?={} \t'
-
-
-def _is_char(c):
- """Returns true iff c is in CHAR as specified in HTTP RFC."""
-
- return ord(c) <= 127
-
-
-def _is_ctl(c):
- """Returns true iff c is in CTL as specified in HTTP RFC."""
-
- return ord(c) <= 31 or ord(c) == 127
-
-
-class ParsingState(object):
- def __init__(self, data):
- self.data = data
- self.head = 0
-
-
-def peek(state, pos=0):
- """Peeks the character at pos from the head of data."""
-
- if state.head + pos >= len(state.data):
- return None
-
- return state.data[state.head + pos]
-
-
-def consume(state, amount=1):
- """Consumes specified amount of bytes from the head and returns the
- consumed bytes. If there's not enough bytes to consume, returns None.
- """
-
- if state.head + amount > len(state.data):
- return None
-
- result = state.data[state.head:state.head + amount]
- state.head = state.head + amount
- return result
-
-
-def consume_string(state, expected):
- """Given a parsing state and a expected string, consumes the string from
- the head. Returns True if consumed successfully. Otherwise, returns
- False.
- """
-
- pos = 0
-
- for c in expected:
- if c != peek(state, pos):
- return False
- pos += 1
-
- consume(state, pos)
- return True
-
-
-def consume_lws(state):
- """Consumes a LWS from the head. Returns True if any LWS is consumed.
- Otherwise, returns False.
-
- LWS = [CRLF] 1*( SP | HT )
- """
-
- original_head = state.head
-
- consume_string(state, '\r\n')
-
- pos = 0
-
- while True:
- c = peek(state, pos)
- if c == ' ' or c == '\t':
- pos += 1
- else:
- if pos == 0:
- state.head = original_head
- return False
- else:
- consume(state, pos)
- return True
-
-
-def consume_lwses(state):
- r"""Consumes \*LWS from the head."""
-
- while consume_lws(state):
- pass
-
-
-def consume_token(state):
- """Consumes a token from the head. Returns the token or None if no token
- was found.
- """
-
- pos = 0
-
- while True:
- c = peek(state, pos)
- if c is None or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
- if pos == 0:
- return None
-
- return consume(state, pos)
- else:
- pos += 1
-
-
-def consume_token_or_quoted_string(state):
- """Consumes a token or a quoted-string, and returns the token or unquoted
- string. If no token or quoted-string was found, returns None.
- """
-
- original_head = state.head
-
- if not consume_string(state, '"'):
- return consume_token(state)
-
- result = []
-
- expect_quoted_pair = False
-
- while True:
- if not expect_quoted_pair and consume_lws(state):
- result.append(' ')
- continue
-
- c = consume(state)
- if c is None:
- # quoted-string is not enclosed with double quotation
- state.head = original_head
- return None
- elif expect_quoted_pair:
- expect_quoted_pair = False
- if _is_char(c):
- result.append(c)
- else:
- # Non CHAR character found in quoted-pair
- state.head = original_head
- return None
- elif c == '\\':
- expect_quoted_pair = True
- elif c == '"':
- return ''.join(result)
- elif _is_ctl(c):
- # Invalid character %r found in qdtext
- state.head = original_head
- return None
- else:
- result.append(c)
-
-
-def quote_if_necessary(s):
- """Quotes arbitrary string into quoted-string."""
-
- quote = False
- if s == '':
- return '""'
-
- result = []
- for c in s:
- if c == '"' or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
- quote = True
-
- if c == '"' or _is_ctl(c):
- result.append('\\' + c)
- else:
- result.append(c)
-
- if quote:
- return '"' + ''.join(result) + '"'
- else:
- return ''.join(result)
-
-
-def parse_uri(uri):
- """Parse absolute URI then return host, port and resource."""
-
- parsed = six.moves.urllib.parse.urlsplit(uri)
- if parsed.scheme != 'wss' and parsed.scheme != 'ws':
- # |uri| must be a relative URI.
- # TODO(toyoshim): Should validate |uri|.
- return None, None, uri
-
- if parsed.hostname is None:
- return None, None, None
-
- port = None
- try:
- port = parsed.port
- except ValueError:
- # The port property cause ValueError on invalid null port descriptions
- # like 'ws://host:INVALID_PORT/path', where the assigned port is not
- # *DIGIT. For python 3.6 and later, ValueError also raises when
- # assigning invalid port numbers such as 'ws://host:-1/path'. Earlier
- # versions simply return None and ignore invalid port attributes.
- return None, None, None
-
- if port is None:
- if parsed.scheme == 'ws':
- port = 80
- else:
- port = 443
-
- path = parsed.path
- if not path:
- path += '/'
- if parsed.query:
- path += '?' + parsed.query
- if parsed.fragment:
- path += '#' + parsed.fragment
-
- return parsed.hostname, port, path
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/memorizingfile.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/memorizingfile.py
deleted file mode 100644
index d353967618..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/memorizingfile.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Memorizing file.
-
-A memorizing file wraps a file and memorizes lines read by readline.
-"""
-
-from __future__ import absolute_import
-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.maxsize):
- """Construct an instance.
-
- Args:
- file_: the file object to wrap.
- max_memorized_lines: the maximum number of lines to memorize.
- Only the first max_memorized_lines are memorized.
- Default: sys.maxint.
- """
- self._file = file_
- self._memorized_lines = []
- self._max_memorized_lines = max_memorized_lines
- self._buffered = False
- self._buffered_line = None
-
- def __getattribute__(self, name):
- """Return a file attribute.
-
- Returns the value overridden by this class for some attributes,
- and forwards the call to _file for the other attributes.
- """
- if name in ('_file', '_memorized_lines', '_max_memorized_lines',
- '_buffered', '_buffered_line', 'readline',
- 'get_memorized_lines'):
- return object.__getattribute__(self, name)
- return self._file.__getattribute__(name)
-
- def readline(self, size=-1):
- """Override file.readline and memorize the line read.
-
- Note that even if size is specified and smaller than actual size,
- the whole line will be read out from underlying file object by
- subsequent readline calls.
- """
- if self._buffered:
- line = self._buffered_line
- self._buffered = False
- else:
- line = self._file.readline()
- if line and len(self._memorized_lines) < self._max_memorized_lines:
- self._memorized_lines.append(line)
- if size >= 0 and size < len(line):
- self._buffered = True
- self._buffered_line = line[size:]
- return line[:size]
- return line
-
- def get_memorized_lines(self):
- """Get lines memorized so far."""
- return self._memorized_lines
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/msgutil.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/msgutil.py
deleted file mode 100644
index f58ca78e14..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/msgutil.py
+++ /dev/null
@@ -1,214 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Message related utilities.
-
-Note: request.connection.write/read are used in this module, even though
-mod_python document says that they should be used only in connection
-handlers. Unfortunately, we have no other options. For example,
-request.write/read are not suitable because they don't allow direct raw
-bytes writing/reading.
-"""
-
-from __future__ import absolute_import
-import six.moves.queue
-import threading
-
-# Export Exception symbols from msgutil for backward compatibility
-from mod_pywebsocket._stream_exceptions import ConnectionTerminatedException
-from mod_pywebsocket._stream_exceptions import InvalidFrameException
-from mod_pywebsocket._stream_exceptions import BadOperationException
-from mod_pywebsocket._stream_exceptions import UnsupportedFrameException
-
-
-# An API for handler to send/receive WebSocket messages.
-def close_connection(request):
- """Close connection.
-
- Args:
- request: mod_python request.
- """
- request.ws_stream.close_connection()
-
-
-def send_message(request, payload_data, end=True, binary=False):
- """Send a message (or part of a message).
-
- Args:
- request: mod_python request.
- payload_data: unicode text or str binary to send.
- end: True to terminate a message.
- False to send payload_data as part of a message that is to be
- terminated by next or later send_message call with end=True.
- binary: send payload_data as binary frame(s).
- Raises:
- BadOperationException: when server already terminated.
- """
- request.ws_stream.send_message(payload_data, end, binary)
-
-
-def receive_message(request):
- """Receive a WebSocket frame and return its payload as a text in
- unicode or a binary in str.
-
- Args:
- request: mod_python request.
- Raises:
- InvalidFrameException: when client send invalid frame.
- UnsupportedFrameException: when client send unsupported frame e.g. some
- of reserved bit is set but no extension can
- recognize it.
- InvalidUTF8Exception: when client send a text frame containing any
- invalid UTF-8 string.
- ConnectionTerminatedException: when the connection is closed
- unexpectedly.
- BadOperationException: when client already terminated.
- """
- return request.ws_stream.receive_message()
-
-
-def send_ping(request, body):
- request.ws_stream.send_ping(body)
-
-
-class MessageReceiver(threading.Thread):
- """This class receives messages from the client.
-
- This class provides three ways to receive messages: blocking,
- non-blocking, and via callback. Callback has the highest precedence.
-
- Note: This class should not be used with the standalone server for wss
- because pyOpenSSL used by the server raises a fatal error if the socket
- is accessed from multiple threads.
- """
- def __init__(self, request, onmessage=None):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- onmessage: a function to be called when a message is received.
- May be None. If not None, the function is called on
- another thread. In that case, MessageReceiver.receive
- and MessageReceiver.receive_nowait are useless
- because they will never return any messages.
- """
-
- threading.Thread.__init__(self)
- self._request = request
- self._queue = six.moves.queue.Queue()
- self._onmessage = onmessage
- self._stop_requested = False
- self.setDaemon(True)
- self.start()
-
- def run(self):
- try:
- while not self._stop_requested:
- message = receive_message(self._request)
- if self._onmessage:
- self._onmessage(message)
- else:
- self._queue.put(message)
- finally:
- close_connection(self._request)
-
- def receive(self):
- """ Receive a message from the channel, blocking.
-
- Returns:
- message as a unicode string.
- """
- return self._queue.get()
-
- def receive_nowait(self):
- """ Receive a message from the channel, non-blocking.
-
- Returns:
- message as a unicode string if available. None otherwise.
- """
- try:
- message = self._queue.get_nowait()
- except six.moves.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 = six.moves.queue.Queue()
- self.setDaemon(True)
- self.start()
-
- def run(self):
- while True:
- message, condition = self._queue.get()
- condition.acquire()
- send_message(self._request, message)
- condition.notify()
- condition.release()
-
- def send(self, message):
- """Send a message, blocking."""
-
- condition = threading.Condition()
- condition.acquire()
- self._queue.put((message, condition))
- condition.wait()
-
- def send_nowait(self, message):
- """Send a message, non-blocking."""
-
- self._queue.put((message, threading.Condition()))
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py
deleted file mode 100644
index 5e9c875dc7..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py
+++ /dev/null
@@ -1,319 +0,0 @@
-# Copyright 2020, 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.
-"""Request Handler and Request/Connection classes for standalone server.
-"""
-
-import os
-
-from six.moves import CGIHTTPServer
-from six.moves import http_client
-
-from mod_pywebsocket import common
-from mod_pywebsocket import dispatch
-from mod_pywebsocket import handshake
-from mod_pywebsocket import http_header_util
-from mod_pywebsocket import memorizingfile
-from mod_pywebsocket import util
-
-# 1024 is practically large enough to contain WebSocket handshake lines.
-_MAX_MEMORIZED_LINES = 1024
-
-
-class _StandaloneConnection(object):
- """Mimic mod_python mp_conn."""
- def __init__(self, request_handler):
- """Construct an instance.
-
- Args:
- request_handler: A WebSocketRequestHandler instance.
- """
-
- self._request_handler = request_handler
-
- def get_local_addr(self):
- """Getter to mimic mp_conn.local_addr."""
-
- return (self._request_handler.server.server_name,
- self._request_handler.server.server_port)
-
- local_addr = property(get_local_addr)
-
- def get_remote_addr(self):
- """Getter to mimic mp_conn.remote_addr.
-
- Setting the property in __init__ won't work because the request
- handler is not initialized yet there."""
-
- return self._request_handler.client_address
-
- remote_addr = property(get_remote_addr)
-
- def write(self, data):
- """Mimic mp_conn.write()."""
-
- return self._request_handler.wfile.write(data)
-
- def read(self, length):
- """Mimic mp_conn.read()."""
-
- return self._request_handler.rfile.read(length)
-
- def get_memorized_lines(self):
- """Get memorized lines."""
-
- return self._request_handler.rfile.get_memorized_lines()
-
-
-class _StandaloneRequest(object):
- """Mimic mod_python request."""
- def __init__(self, request_handler, use_tls):
- """Construct an instance.
-
- Args:
- request_handler: A WebSocketRequestHandler instance.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request_handler = request_handler
- self.connection = _StandaloneConnection(request_handler)
- self._use_tls = use_tls
- self.headers_in = request_handler.headers
-
- def get_uri(self):
- """Getter to mimic request.uri.
-
- This method returns the raw data at the Request-URI part of the
- Request-Line, while the uri method on the request object of mod_python
- returns the path portion after parsing the raw data. This behavior is
- kept for compatibility.
- """
-
- return self._request_handler.path
-
- uri = property(get_uri)
-
- def get_unparsed_uri(self):
- """Getter to mimic request.unparsed_uri."""
-
- return self._request_handler.path
-
- unparsed_uri = property(get_unparsed_uri)
-
- def get_method(self):
- """Getter to mimic request.method."""
-
- return self._request_handler.command
-
- method = property(get_method)
-
- def get_protocol(self):
- """Getter to mimic request.protocol."""
-
- return self._request_handler.request_version
-
- protocol = property(get_protocol)
-
- def is_https(self):
- """Mimic request.is_https()."""
-
- return self._use_tls
-
-
-class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
- """CGIHTTPRequestHandler specialized for WebSocket."""
-
- # Use httplib.HTTPMessage instead of mimetools.Message.
- MessageClass = http_client.HTTPMessage
-
- def setup(self):
- """Override SocketServer.StreamRequestHandler.setup to wrap rfile
- with MemorizingFile.
-
- This method will be called by BaseRequestHandler's constructor
- before calling BaseHTTPRequestHandler.handle.
- BaseHTTPRequestHandler.handle will call
- BaseHTTPRequestHandler.handle_one_request and it will call
- WebSocketRequestHandler.parse_request.
- """
-
- # Call superclass's setup to prepare rfile, wfile, etc. See setup
- # definition on the root class SocketServer.StreamRequestHandler to
- # understand what this does.
- CGIHTTPServer.CGIHTTPRequestHandler.setup(self)
-
- self.rfile = memorizingfile.MemorizingFile(
- self.rfile, max_memorized_lines=_MAX_MEMORIZED_LINES)
-
- def __init__(self, request, client_address, server):
- self._logger = util.get_class_logger(self)
-
- self._options = server.websocket_server_options
-
- # Overrides CGIHTTPServerRequestHandler.cgi_directories.
- self.cgi_directories = self._options.cgi_directories
- # Replace CGIHTTPRequestHandler.is_executable method.
- if self._options.is_executable_method is not None:
- self.is_executable = self._options.is_executable_method
-
- # This actually calls BaseRequestHandler.__init__.
- CGIHTTPServer.CGIHTTPRequestHandler.__init__(self, request,
- client_address, server)
-
- def parse_request(self):
- """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
-
- Return True to continue processing for HTTP(S), False otherwise.
-
- See BaseHTTPRequestHandler.handle_one_request method which calls
- this method to understand how the return value will be handled.
- """
-
- # We hook parse_request method, but also call the original
- # CGIHTTPRequestHandler.parse_request since when we return False,
- # CGIHTTPRequestHandler.handle_one_request continues processing and
- # it needs variables set by CGIHTTPRequestHandler.parse_request.
- #
- # Variables set by this method will be also used by WebSocket request
- # handling (self.path, self.command, self.requestline, etc. See also
- # how _StandaloneRequest's members are implemented using these
- # attributes).
- if not CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self):
- return False
-
- if self._options.use_basic_auth:
- auth = self.headers.get('Authorization')
- if auth != self._options.basic_auth_credential:
- self.send_response(401)
- self.send_header('WWW-Authenticate',
- 'Basic realm="Pywebsocket"')
- self.end_headers()
- self._logger.info('Request basic authentication')
- return False
-
- host, port, resource = http_header_util.parse_uri(self.path)
- if resource is None:
- self._logger.info('Invalid URI: %r', self.path)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- server_options = self.server.websocket_server_options
- if host is not None:
- validation_host = server_options.validation_host
- if validation_host is not None and host != validation_host:
- self._logger.info('Invalid host: %r (expected: %r)', host,
- validation_host)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- if port is not None:
- validation_port = server_options.validation_port
- if validation_port is not None and port != validation_port:
- self._logger.info('Invalid port: %r (expected: %r)', port,
- validation_port)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- self.path = resource
-
- request = _StandaloneRequest(self, self._options.use_tls)
-
- try:
- # Fallback to default http handler for request paths for which
- # we don't have request handlers.
- if not self._options.dispatcher.get_handler_suite(self.path):
- self._logger.info('No handler for resource: %r', self.path)
- self._logger.info('Fallback to CGIHTTPRequestHandler')
- return True
- except dispatch.DispatchException as e:
- self._logger.info('Dispatch failed for error: %s', e)
- self.send_error(e.status)
- return False
-
- # If any Exceptions without except clause setup (including
- # DispatchException) is raised below this point, it will be caught
- # and logged by WebSocketServer.
-
- try:
- try:
- handshake.do_handshake(request, self._options.dispatcher)
- except handshake.VersionException as e:
- self._logger.info('Handshake failed for version error: %s', e)
- self.send_response(common.HTTP_STATUS_BAD_REQUEST)
- self.send_header(common.SEC_WEBSOCKET_VERSION_HEADER,
- e.supported_versions)
- self.end_headers()
- return False
- except handshake.HandshakeException as e:
- # Handshake for ws(s) failed.
- self._logger.info('Handshake failed for error: %s', e)
- self.send_error(e.status)
- return False
-
- request._dispatcher = self._options.dispatcher
- self._options.dispatcher.transfer_data(request)
- except handshake.AbortedByUserException as e:
- self._logger.info('Aborted: %s', e)
- return False
-
- def log_request(self, code='-', size='-'):
- """Override BaseHTTPServer.log_request."""
-
- self._logger.info('"%s" %s %s', self.requestline, str(code), str(size))
-
- def log_error(self, *args):
- """Override BaseHTTPServer.log_error."""
-
- # Despite the name, this method is for warnings than for errors.
- # For example, HTTP status code is logged by this method.
- self._logger.warning('%s - %s', self.address_string(),
- args[0] % args[1:])
-
- def is_cgi(self):
- """Test whether self.path corresponds to a CGI script.
-
- Add extra check that self.path doesn't contains ..
- Also check if the file is a executable file or not.
- If the file is not executable, it is handled as static file or dir
- rather than a CGI script.
- """
-
- if CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self):
- if '..' in self.path:
- return False
- # strip query parameter from request path
- resource_name = self.path.split('?', 2)[0]
- # convert resource_name into real path name in filesystem.
- scriptfile = self.translate_path(resource_name)
- if not os.path.isfile(scriptfile):
- return False
- if not self.is_executable(scriptfile):
- return False
- return True
- return False
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/server_util.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/server_util.py
deleted file mode 100644
index 8f9e273e97..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/server_util.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright 2020, 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.
-"""Server related utilities."""
-
-import logging
-import logging.handlers
-import threading
-import time
-
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-
-
-def _get_logger_from_class(c):
- return logging.getLogger('%s.%s' % (c.__module__, c.__name__))
-
-
-def configure_logging(options):
- logging.addLevelName(common.LOGLEVEL_FINE, 'FINE')
-
- logger = logging.getLogger()
- logger.setLevel(logging.getLevelName(options.log_level.upper()))
- if options.log_file:
- handler = logging.handlers.RotatingFileHandler(options.log_file, 'a',
- options.log_max,
- options.log_count)
- else:
- handler = logging.StreamHandler()
- formatter = logging.Formatter(
- '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s')
- handler.setFormatter(formatter)
- logger.addHandler(handler)
-
- deflate_log_level_name = logging.getLevelName(
- options.deflate_log_level.upper())
- _get_logger_from_class(util._Deflater).setLevel(deflate_log_level_name)
- _get_logger_from_class(util._Inflater).setLevel(deflate_log_level_name)
-
-
-class ThreadMonitor(threading.Thread):
- daemon = True
-
- def __init__(self, interval_in_sec):
- threading.Thread.__init__(self, name='ThreadMonitor')
-
- self._logger = util.get_class_logger(self)
-
- self._interval_in_sec = interval_in_sec
-
- def run(self):
- while True:
- thread_name_list = []
- for thread in threading.enumerate():
- thread_name_list.append(thread.name)
- self._logger.info("%d active threads: %s",
- threading.active_count(),
- ', '.join(thread_name_list))
- time.sleep(self._interval_in_sec)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
deleted file mode 100755
index bd32158790..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
+++ /dev/null
@@ -1,491 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Standalone WebSocket server.
-
-Use this file to launch pywebsocket as a standalone server.
-
-
-BASIC USAGE
-===========
-
-Go to the src directory and run
-
- $ python mod_pywebsocket/standalone.py [-p <ws_port>]
- [-w <websock_handlers>]
- [-d <document_root>]
-
-<ws_port> is the port number to use for ws:// connection.
-
-<document_root> is the path to the root directory of HTML files.
-
-<websock_handlers> is the path to the root directory of WebSocket handlers.
-If not specified, <document_root> will be used. See __init__.py (or
-run $ pydoc mod_pywebsocket) for how to write WebSocket handlers.
-
-For more detail and other options, run
-
- $ python mod_pywebsocket/standalone.py --help
-
-or see _build_option_parser method below.
-
-For trouble shooting, adding "--log_level debug" might help you.
-
-
-TRY DEMO
-========
-
-Go to the src directory and run standalone.py with -d option to set the
-document root to the directory containing example HTMLs and handlers like this:
-
- $ cd src
- $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example
-
-to launch pywebsocket with the sample handler and html on port 80. Open
-http://localhost/console.html, click the connect button, type something into
-the text box next to the send button and click the send button. If everything
-is working, you'll see the message you typed echoed by the server.
-
-
-USING TLS
-=========
-
-To run the standalone server with TLS support, run it with -t, -k, and -c
-options. When TLS is enabled, the standalone server accepts only TLS connection.
-
-Note that when ssl module is used and the key/cert location is incorrect,
-TLS connection silently fails while pyOpenSSL fails on startup.
-
-Example:
-
- $ PYTHONPATH=. python mod_pywebsocket/standalone.py \
- -d example \
- -p 10443 \
- -t \
- -c ../test/cert/cert.pem \
- -k ../test/cert/key.pem \
-
-Note that when passing a relative path to -c and -k option, it will be resolved
-using the document root directory as the base.
-
-
-USING CLIENT AUTHENTICATION
-===========================
-
-To run the standalone server with TLS client authentication support, run it with
---tls-client-auth and --tls-client-ca options in addition to ones required for
-TLS support.
-
-Example:
-
- $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example -p 10443 -t \
- -c ../test/cert/cert.pem -k ../test/cert/key.pem \
- --tls-client-auth \
- --tls-client-ca=../test/cert/cacert.pem
-
-Note that when passing a relative path to --tls-client-ca option, it will be
-resolved using the document root directory as the base.
-
-
-CONFIGURATION FILE
-==================
-
-You can also write a configuration file and use it by specifying the path to
-the configuration file by --config option. Please write a configuration file
-following the documentation of the Python ConfigParser library. Name of each
-entry must be the long version argument name. E.g. to set log level to debug,
-add the following line:
-
-log_level=debug
-
-For options which doesn't take value, please add some fake value. E.g. for
---tls option, add the following line:
-
-tls=True
-
-Note that tls will be enabled even if you write tls=False as the value part is
-fake.
-
-When both a command line argument and a configuration file entry are set for
-the same configuration item, the command line value will override one in the
-configuration file.
-
-
-THREADING
-=========
-
-This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
-used for each request.
-
-
-SECURITY WARNING
-================
-
-This uses CGIHTTPServer and CGIHTTPServer is not secure.
-It may execute arbitrary Python code or external programs. It should not be
-used outside a firewall.
-"""
-
-from __future__ import absolute_import
-from six.moves import configparser
-import base64
-import logging
-import argparse
-import os
-import six
-import sys
-import traceback
-
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-from mod_pywebsocket import server_util
-from mod_pywebsocket.websocket_server import WebSocketServer
-
-_DEFAULT_LOG_MAX_BYTES = 1024 * 256
-_DEFAULT_LOG_BACKUP_COUNT = 5
-
-_DEFAULT_REQUEST_QUEUE_SIZE = 128
-
-
-def _build_option_parser():
- parser = argparse.ArgumentParser()
-
- parser.add_argument(
- '--config',
- dest='config_file',
- type=six.text_type,
- default=None,
- help=('Path to configuration file. See the file comment '
- 'at the top of this file for the configuration '
- 'file format'))
- parser.add_argument('-H',
- '--server-host',
- '--server_host',
- dest='server_host',
- default='',
- help='server hostname to listen to')
- parser.add_argument('-V',
- '--validation-host',
- '--validation_host',
- dest='validation_host',
- default=None,
- help='server hostname to validate in absolute path.')
- parser.add_argument('-p',
- '--port',
- dest='port',
- type=int,
- default=common.DEFAULT_WEB_SOCKET_PORT,
- help='port to listen to')
- parser.add_argument('-P',
- '--validation-port',
- '--validation_port',
- dest='validation_port',
- type=int,
- default=None,
- help='server port to validate in absolute path.')
- parser.add_argument(
- '-w',
- '--websock-handlers',
- '--websock_handlers',
- dest='websock_handlers',
- default='.',
- help=('The root directory of WebSocket handler files. '
- 'If the path is relative, --document-root is used '
- 'as the base.'))
- parser.add_argument('-m',
- '--websock-handlers-map-file',
- '--websock_handlers_map_file',
- dest='websock_handlers_map_file',
- default=None,
- help=('WebSocket handlers map file. '
- 'Each line consists of alias_resource_path and '
- 'existing_resource_path, separated by spaces.'))
- parser.add_argument('-s',
- '--scan-dir',
- '--scan_dir',
- dest='scan_dir',
- default=None,
- help=('Must be a directory under --websock-handlers. '
- 'Only handlers under this directory are scanned '
- 'and registered to the server. '
- 'Useful for saving scan time when the handler '
- 'root directory contains lots of files that are '
- 'not handler file or are handler files but you '
- 'don\'t want them to be registered. '))
- parser.add_argument(
- '--allow-handlers-outside-root-dir',
- '--allow_handlers_outside_root_dir',
- dest='allow_handlers_outside_root_dir',
- action='store_true',
- default=False,
- help=('Scans WebSocket handlers even if their canonical '
- 'path is not under --websock-handlers.'))
- parser.add_argument('-d',
- '--document-root',
- '--document_root',
- dest='document_root',
- default='.',
- help='Document root directory.')
- parser.add_argument('-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_argument('-t',
- '--tls',
- dest='use_tls',
- action='store_true',
- default=False,
- help='use TLS (wss://)')
- parser.add_argument('-k',
- '--private-key',
- '--private_key',
- dest='private_key',
- default='',
- help='TLS private key file.')
- parser.add_argument('-c',
- '--certificate',
- dest='certificate',
- default='',
- help='TLS certificate file.')
- parser.add_argument('--tls-client-auth',
- dest='tls_client_auth',
- action='store_true',
- default=False,
- help='Requests TLS client auth on every connection.')
- parser.add_argument('--tls-client-cert-optional',
- dest='tls_client_cert_optional',
- action='store_true',
- default=False,
- help=('Makes client certificate optional even though '
- 'TLS client auth is enabled.'))
- parser.add_argument('--tls-client-ca',
- dest='tls_client_ca',
- default='',
- help=('Specifies a pem file which contains a set of '
- 'concatenated CA certificates which are used to '
- 'validate certificates passed from clients'))
- parser.add_argument('--basic-auth',
- dest='use_basic_auth',
- action='store_true',
- default=False,
- help='Requires Basic authentication.')
- parser.add_argument(
- '--basic-auth-credential',
- dest='basic_auth_credential',
- default='test:test',
- help='Specifies the credential of basic authentication '
- 'by username:password pair (e.g. test:test).')
- parser.add_argument('-l',
- '--log-file',
- '--log_file',
- dest='log_file',
- default='',
- help='Log file.')
- # Custom log level:
- # - FINE: Prints status of each frame processing step
- parser.add_argument('--log-level',
- '--log_level',
- type=six.text_type,
- dest='log_level',
- default='warn',
- choices=[
- 'fine', 'debug', 'info', 'warning', 'warn',
- 'error', 'critical'
- ],
- help='Log level.')
- parser.add_argument(
- '--deflate-log-level',
- '--deflate_log_level',
- type=six.text_type,
- dest='deflate_log_level',
- default='warn',
- choices=['debug', 'info', 'warning', 'warn', 'error', 'critical'],
- help='Log level for _Deflater and _Inflater.')
- parser.add_argument('--thread-monitor-interval-in-sec',
- '--thread_monitor_interval_in_sec',
- dest='thread_monitor_interval_in_sec',
- type=int,
- default=-1,
- help=('If positive integer is specified, run a thread '
- 'monitor to show the status of server threads '
- 'periodically in the specified inteval in '
- 'second. If non-positive integer is specified, '
- 'disable the thread monitor.'))
- parser.add_argument('--log-max',
- '--log_max',
- dest='log_max',
- type=int,
- default=_DEFAULT_LOG_MAX_BYTES,
- help='Log maximum bytes')
- parser.add_argument('--log-count',
- '--log_count',
- dest='log_count',
- type=int,
- default=_DEFAULT_LOG_BACKUP_COUNT,
- help='Log backup count')
- parser.add_argument('-q',
- '--queue',
- dest='request_queue_size',
- type=int,
- default=_DEFAULT_REQUEST_QUEUE_SIZE,
- help='request queue size')
- parser.add_argument(
- '--handler-encoding',
- '--handler_encoding',
- dest='handler_encoding',
- type=six.text_type,
- default=None,
- help=('Text encoding used for loading handlers. '
- 'By default, the encoding from the locale is used when '
- 'reading handler files, but this option can override it. '
- 'Any encoding supported by the codecs module may be used.'))
-
- return parser
-
-
-def _parse_args_and_config(args):
- parser = _build_option_parser()
-
- # First, parse options without configuration file.
- temporary_options, temporary_args = parser.parse_known_args(args=args)
- if temporary_args:
- logging.critical('Unrecognized positional arguments: %r',
- temporary_args)
- sys.exit(1)
-
- if temporary_options.config_file:
- try:
- config_fp = open(temporary_options.config_file, 'r')
- except IOError as e:
- logging.critical('Failed to open configuration file %r: %r',
- temporary_options.config_file, e)
- sys.exit(1)
-
- config_parser = configparser.SafeConfigParser()
- config_parser.readfp(config_fp)
- config_fp.close()
-
- args_from_config = []
- for name, value in config_parser.items('pywebsocket'):
- args_from_config.append('--' + name)
- args_from_config.append(value)
- if args is None:
- args = args_from_config
- else:
- args = args_from_config + args
- return parser.parse_known_args(args=args)
- else:
- return temporary_options, temporary_args
-
-
-def _main(args=None):
- """You can call this function from your own program, but please note that
- this function has some side-effects that might affect your program. For
- example, it changes the current directory.
- """
-
- options, args = _parse_args_and_config(args=args)
-
- os.chdir(options.document_root)
-
- server_util.configure_logging(options)
-
- # TODO(tyoshino): Clean up initialization of CGI related values. Move some
- # of code here to WebSocketRequestHandler class if it's better.
- options.cgi_directories = []
- options.is_executable_method = None
- if options.cgi_paths:
- options.cgi_directories = options.cgi_paths.split(',')
- if sys.platform in ('cygwin', 'win32'):
- cygwin_path = None
- # For Win32 Python, it is expected that CYGWIN_PATH
- # is set to a directory of cygwin binaries.
- # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
- # full path of third_party/cygwin/bin.
- if 'CYGWIN_PATH' in os.environ:
- cygwin_path = os.environ['CYGWIN_PATH']
-
- def __check_script(scriptpath):
- return util.get_script_interp(scriptpath, cygwin_path)
-
- options.is_executable_method = __check_script
-
- if options.use_tls:
- logging.debug('Using ssl module')
-
- if not options.private_key or not options.certificate:
- logging.critical(
- 'To use TLS, specify private_key and certificate.')
- sys.exit(1)
-
- if (options.tls_client_cert_optional and not options.tls_client_auth):
- logging.critical('Client authentication must be enabled to '
- 'specify tls_client_cert_optional')
- sys.exit(1)
- else:
- if options.tls_client_auth:
- logging.critical('TLS must be enabled for client authentication.')
- sys.exit(1)
-
- if options.tls_client_cert_optional:
- logging.critical('TLS must be enabled for client authentication.')
- sys.exit(1)
-
- if not options.scan_dir:
- options.scan_dir = options.websock_handlers
-
- if options.use_basic_auth:
- options.basic_auth_credential = 'Basic ' + base64.b64encode(
- options.basic_auth_credential.encode('UTF-8')).decode()
-
- try:
- if options.thread_monitor_interval_in_sec > 0:
- # Run a thread monitor to show the status of server threads for
- # debugging.
- server_util.ThreadMonitor(
- options.thread_monitor_interval_in_sec).start()
-
- server = WebSocketServer(options)
- server.serve_forever()
- except Exception as e:
- logging.critical('mod_pywebsocket: %s' % e)
- logging.critical('mod_pywebsocket: %s' % traceback.format_exc())
- sys.exit(1)
-
-
-if __name__ == '__main__':
- _main(sys.argv[1:])
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/stream.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/stream.py
deleted file mode 100644
index 82d1ea619c..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/stream.py
+++ /dev/null
@@ -1,950 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""This file provides classes and helper functions for parsing/building frames
-of the WebSocket protocol (RFC 6455).
-
-Specification:
-http://tools.ietf.org/html/rfc6455
-"""
-
-from collections import deque
-import logging
-import os
-import struct
-import time
-import socket
-import six
-
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-from mod_pywebsocket._stream_exceptions import BadOperationException
-from mod_pywebsocket._stream_exceptions import ConnectionTerminatedException
-from mod_pywebsocket._stream_exceptions import InvalidFrameException
-from mod_pywebsocket._stream_exceptions import InvalidUTF8Exception
-from mod_pywebsocket._stream_exceptions import UnsupportedFrameException
-
-_NOOP_MASKER = util.NoopMasker()
-
-
-class Frame(object):
- def __init__(self,
- fin=1,
- rsv1=0,
- rsv2=0,
- rsv3=0,
- opcode=None,
- payload=b''):
- self.fin = fin
- self.rsv1 = rsv1
- self.rsv2 = rsv2
- self.rsv3 = rsv3
- self.opcode = opcode
- self.payload = payload
-
-
-# Helper functions made public to be used for writing unittests for WebSocket
-# clients.
-
-
-def create_length_header(length, mask):
- """Creates a length header.
-
- Args:
- length: Frame length. Must be less than 2^63.
- mask: Mask bit. Must be boolean.
-
- Raises:
- ValueError: when bad data is given.
- """
-
- if mask:
- mask_bit = 1 << 7
- else:
- mask_bit = 0
-
- if length < 0:
- raise ValueError('length must be non negative integer')
- elif length <= 125:
- return util.pack_byte(mask_bit | length)
- elif length < (1 << 16):
- return util.pack_byte(mask_bit | 126) + struct.pack('!H', length)
- elif length < (1 << 63):
- return util.pack_byte(mask_bit | 127) + struct.pack('!Q', length)
- else:
- raise ValueError('Payload is too big for one frame')
-
-
-def create_header(opcode, payload_length, fin, rsv1, rsv2, rsv3, mask):
- """Creates a frame header.
-
- Raises:
- Exception: when bad data is given.
- """
-
- if opcode < 0 or 0xf < opcode:
- raise ValueError('Opcode out of range')
-
- if payload_length < 0 or (1 << 63) <= payload_length:
- raise ValueError('payload_length out of range')
-
- if (fin | rsv1 | rsv2 | rsv3) & ~1:
- raise ValueError('FIN bit and Reserved bit parameter must be 0 or 1')
-
- header = b''
-
- first_byte = ((fin << 7)
- | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4)
- | opcode)
- header += util.pack_byte(first_byte)
- header += create_length_header(payload_length, mask)
-
- return header
-
-
-def _build_frame(header, body, mask):
- if not mask:
- return header + body
-
- masking_nonce = os.urandom(4)
- masker = util.RepeatedXorMasker(masking_nonce)
-
- return header + masking_nonce + masker.mask(body)
-
-
-def _filter_and_format_frame_object(frame, mask, frame_filters):
- for frame_filter in frame_filters:
- frame_filter.filter(frame)
-
- header = create_header(frame.opcode, len(frame.payload), frame.fin,
- frame.rsv1, frame.rsv2, frame.rsv3, mask)
- return _build_frame(header, frame.payload, mask)
-
-
-def create_binary_frame(message,
- opcode=common.OPCODE_BINARY,
- fin=1,
- mask=False,
- frame_filters=[]):
- """Creates a simple binary frame with no extension, reserved bit."""
-
- frame = Frame(fin=fin, opcode=opcode, payload=message)
- return _filter_and_format_frame_object(frame, mask, frame_filters)
-
-
-def create_text_frame(message,
- opcode=common.OPCODE_TEXT,
- fin=1,
- mask=False,
- frame_filters=[]):
- """Creates a simple text frame with no extension, reserved bit."""
-
- encoded_message = message.encode('utf-8')
- return create_binary_frame(encoded_message, opcode, fin, mask,
- frame_filters)
-
-
-def parse_frame(receive_bytes,
- logger=None,
- ws_version=common.VERSION_HYBI_LATEST,
- unmask_receive=True):
- """Parses a frame. Returns a tuple containing each header field and
- payload.
-
- Args:
- receive_bytes: a function that reads frame data from a stream or
- something similar. The function takes length of the bytes to be
- read. The function must raise ConnectionTerminatedException if
- there is not enough data to be read.
- logger: a logging object.
- ws_version: the version of WebSocket protocol.
- unmask_receive: unmask received frames. When received unmasked
- frame, raises InvalidFrameException.
-
- Raises:
- ConnectionTerminatedException: when receive_bytes raises it.
- InvalidFrameException: when the frame contains invalid data.
- """
-
- if not logger:
- logger = logging.getLogger()
-
- logger.log(common.LOGLEVEL_FINE, 'Receive the first 2 octets of a frame')
-
- first_byte = ord(receive_bytes(1))
- fin = (first_byte >> 7) & 1
- rsv1 = (first_byte >> 6) & 1
- rsv2 = (first_byte >> 5) & 1
- rsv3 = (first_byte >> 4) & 1
- opcode = first_byte & 0xf
-
- second_byte = ord(receive_bytes(1))
- mask = (second_byte >> 7) & 1
- payload_length = second_byte & 0x7f
-
- logger.log(
- common.LOGLEVEL_FINE, 'FIN=%s, RSV1=%s, RSV2=%s, RSV3=%s, opcode=%s, '
- 'Mask=%s, Payload_length=%s', fin, rsv1, rsv2, rsv3, opcode, mask,
- payload_length)
-
- if (mask == 1) != unmask_receive:
- raise InvalidFrameException(
- 'Mask bit on the received frame did\'nt match masking '
- 'configuration for received frames')
-
- # The HyBi and later specs disallow putting a value in 0x0-0xFFFF
- # into the 8-octet extended payload length field (or 0x0-0xFD in
- # 2-octet field).
- valid_length_encoding = True
- length_encoding_bytes = 1
- if payload_length == 127:
- logger.log(common.LOGLEVEL_FINE,
- 'Receive 8-octet extended payload length')
-
- extended_payload_length = receive_bytes(8)
- payload_length = struct.unpack('!Q', extended_payload_length)[0]
- if payload_length > 0x7FFFFFFFFFFFFFFF:
- raise InvalidFrameException('Extended payload length >= 2^63')
- if ws_version >= 13 and payload_length < 0x10000:
- valid_length_encoding = False
- length_encoding_bytes = 8
-
- logger.log(common.LOGLEVEL_FINE, 'Decoded_payload_length=%s',
- payload_length)
- elif payload_length == 126:
- logger.log(common.LOGLEVEL_FINE,
- 'Receive 2-octet extended payload length')
-
- extended_payload_length = receive_bytes(2)
- payload_length = struct.unpack('!H', extended_payload_length)[0]
- if ws_version >= 13 and payload_length < 126:
- valid_length_encoding = False
- length_encoding_bytes = 2
-
- logger.log(common.LOGLEVEL_FINE, 'Decoded_payload_length=%s',
- payload_length)
-
- if not valid_length_encoding:
- logger.warning(
- 'Payload length is not encoded using the minimal number of '
- 'bytes (%d is encoded using %d bytes)', payload_length,
- length_encoding_bytes)
-
- if mask == 1:
- logger.log(common.LOGLEVEL_FINE, 'Receive mask')
-
- masking_nonce = receive_bytes(4)
- masker = util.RepeatedXorMasker(masking_nonce)
-
- logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce)
- else:
- masker = _NOOP_MASKER
-
- logger.log(common.LOGLEVEL_FINE, 'Receive payload data')
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- receive_start = time.time()
-
- raw_payload_bytes = receive_bytes(payload_length)
-
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- logger.log(
- common.LOGLEVEL_FINE, 'Done receiving payload data at %s MB/s',
- payload_length / (time.time() - receive_start) / 1000 / 1000)
- logger.log(common.LOGLEVEL_FINE, 'Unmask payload data')
-
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- unmask_start = time.time()
-
- unmasked_bytes = masker.mask(raw_payload_bytes)
-
- if logger.isEnabledFor(common.LOGLEVEL_FINE):
- logger.log(common.LOGLEVEL_FINE,
- 'Done unmasking payload data at %s MB/s',
- payload_length / (time.time() - unmask_start) / 1000 / 1000)
-
- return opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3
-
-
-class FragmentedFrameBuilder(object):
- """A stateful class to send a message as fragments."""
- def __init__(self, mask, frame_filters=[], encode_utf8=True):
- """Constructs an instance."""
-
- self._mask = mask
- self._frame_filters = frame_filters
- # This is for skipping UTF-8 encoding when building text type frames
- # from compressed data.
- self._encode_utf8 = encode_utf8
-
- self._started = False
-
- # Hold opcode of the first frame in messages to verify types of other
- # frames in the message are all the same.
- self._opcode = common.OPCODE_TEXT
-
- def build(self, payload_data, end, binary):
- if binary:
- frame_type = common.OPCODE_BINARY
- else:
- frame_type = common.OPCODE_TEXT
- if self._started:
- if self._opcode != frame_type:
- raise ValueError('Message types are different in frames for '
- 'the same message')
- opcode = common.OPCODE_CONTINUATION
- else:
- opcode = frame_type
- self._opcode = frame_type
-
- if end:
- self._started = False
- fin = 1
- else:
- self._started = True
- fin = 0
-
- if binary or not self._encode_utf8:
- return create_binary_frame(payload_data, opcode, fin, self._mask,
- self._frame_filters)
- else:
- return create_text_frame(payload_data, opcode, fin, self._mask,
- self._frame_filters)
-
-
-def _create_control_frame(opcode, body, mask, frame_filters):
- frame = Frame(opcode=opcode, payload=body)
-
- for frame_filter in frame_filters:
- frame_filter.filter(frame)
-
- if len(frame.payload) > 125:
- raise BadOperationException(
- 'Payload data size of control frames must be 125 bytes or less')
-
- header = create_header(frame.opcode, len(frame.payload), frame.fin,
- frame.rsv1, frame.rsv2, frame.rsv3, mask)
- return _build_frame(header, frame.payload, mask)
-
-
-def create_ping_frame(body, mask=False, frame_filters=[]):
- return _create_control_frame(common.OPCODE_PING, body, mask, frame_filters)
-
-
-def create_pong_frame(body, mask=False, frame_filters=[]):
- return _create_control_frame(common.OPCODE_PONG, body, mask, frame_filters)
-
-
-def create_close_frame(body, mask=False, frame_filters=[]):
- return _create_control_frame(common.OPCODE_CLOSE, body, mask,
- frame_filters)
-
-
-def create_closing_handshake_body(code, reason):
- body = b''
- if code is not None:
- if (code > common.STATUS_USER_PRIVATE_MAX
- or code < common.STATUS_NORMAL_CLOSURE):
- raise BadOperationException('Status code is out of range')
- if (code == common.STATUS_NO_STATUS_RECEIVED
- or code == common.STATUS_ABNORMAL_CLOSURE
- or code == common.STATUS_TLS_HANDSHAKE):
- raise BadOperationException('Status code is reserved pseudo '
- 'code')
- encoded_reason = reason.encode('utf-8')
- body = struct.pack('!H', code) + encoded_reason
- return body
-
-
-class StreamOptions(object):
- """Holds option values to configure Stream objects."""
- def __init__(self):
- """Constructs StreamOptions."""
-
- # Filters applied to frames.
- self.outgoing_frame_filters = []
- self.incoming_frame_filters = []
-
- # Filters applied to messages. Control frames are not affected by them.
- self.outgoing_message_filters = []
- self.incoming_message_filters = []
-
- self.encode_text_message_to_utf8 = True
- self.mask_send = False
- self.unmask_receive = True
-
-
-class Stream(object):
- """A class for parsing/building frames of the WebSocket protocol
- (RFC 6455).
- """
- def __init__(self, request, options):
- """Constructs an instance.
-
- Args:
- request: mod_python request.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._options = options
- self._request = request
-
- self._request.client_terminated = False
- self._request.server_terminated = False
-
- # Holds body of received fragments.
- self._received_fragments = []
- # Holds the opcode of the first fragment.
- self._original_opcode = None
-
- self._writer = FragmentedFrameBuilder(
- self._options.mask_send, self._options.outgoing_frame_filters,
- self._options.encode_text_message_to_utf8)
-
- self._ping_queue = deque()
-
- def _read(self, length):
- """Reads length bytes from connection. In case we catch any exception,
- prepends remote address to the exception message and raise again.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- try:
- read_bytes = self._request.connection.read(length)
- if not read_bytes:
- raise ConnectionTerminatedException(
- 'Receiving %d byte failed. Peer (%r) closed connection' %
- (length, (self._request.connection.remote_addr, )))
- return read_bytes
- except IOError as e:
- # Also catch an IOError because mod_python throws it.
- raise ConnectionTerminatedException(
- 'Receiving %d byte failed. IOError (%s) occurred' %
- (length, e))
-
- def _write(self, bytes_to_write):
- """Writes given bytes to connection. In case we catch any exception,
- prepends remote address to the exception message and raise again.
- """
-
- try:
- self._request.connection.write(bytes_to_write)
- except Exception as e:
- util.prepend_message_to_exception(
- 'Failed to send message to %r: ' %
- (self._request.connection.remote_addr, ), e)
- raise
-
- def receive_bytes(self, length):
- """Receives multiple bytes. Retries read when we couldn't receive the
- specified amount. This method returns byte strings.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- read_bytes = []
- while length > 0:
- new_read_bytes = self._read(length)
- read_bytes.append(new_read_bytes)
- length -= len(new_read_bytes)
- return b''.join(read_bytes)
-
- def _read_until(self, delim_char):
- """Reads bytes until we encounter delim_char. The result will not
- contain delim_char.
-
- Raises:
- ConnectionTerminatedException: when read returns empty string.
- """
-
- read_bytes = []
- while True:
- ch = self._read(1)
- if ch == delim_char:
- break
- read_bytes.append(ch)
- return b''.join(read_bytes)
-
- def _receive_frame(self):
- """Receives a frame and return data in the frame as a tuple containing
- each header field and payload separately.
-
- Raises:
- ConnectionTerminatedException: when read returns empty
- string.
- InvalidFrameException: when the frame contains invalid data.
- """
- def _receive_bytes(length):
- return self.receive_bytes(length)
-
- return parse_frame(receive_bytes=_receive_bytes,
- logger=self._logger,
- ws_version=self._request.ws_version,
- unmask_receive=self._options.unmask_receive)
-
- def _receive_frame_as_frame_object(self):
- opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
-
- return Frame(fin=fin,
- rsv1=rsv1,
- rsv2=rsv2,
- rsv3=rsv3,
- opcode=opcode,
- payload=unmasked_bytes)
-
- def receive_filtered_frame(self):
- """Receives a frame and applies frame filters and message filters.
- The frame to be received must satisfy following conditions:
- - The frame is not fragmented.
- - The opcode of the frame is TEXT or BINARY.
-
- DO NOT USE this method except for testing purpose.
- """
-
- frame = self._receive_frame_as_frame_object()
- if not frame.fin:
- raise InvalidFrameException(
- 'Segmented frames must not be received via '
- 'receive_filtered_frame()')
- if (frame.opcode != common.OPCODE_TEXT
- and frame.opcode != common.OPCODE_BINARY):
- raise InvalidFrameException(
- 'Control frames must not be received via '
- 'receive_filtered_frame()')
-
- for frame_filter in self._options.incoming_frame_filters:
- frame_filter.filter(frame)
- for message_filter in self._options.incoming_message_filters:
- frame.payload = message_filter.filter(frame.payload)
- return frame
-
- def send_message(self, message, end=True, binary=False):
- """Send message.
-
- Args:
- message: text in unicode or binary in str to send.
- binary: send message as binary frame.
-
- Raises:
- BadOperationException: when called on a server-terminated
- connection or called with inconsistent message type or
- binary parameter.
- """
-
- if self._request.server_terminated:
- raise BadOperationException(
- 'Requested send_message after sending out a closing handshake')
-
- if binary and isinstance(message, six.text_type):
- raise BadOperationException(
- 'Message for binary frame must not be instance of Unicode')
-
- for message_filter in self._options.outgoing_message_filters:
- message = message_filter.filter(message, end, binary)
-
- try:
- # Set this to any positive integer to limit maximum size of data in
- # payload data of each frame.
- MAX_PAYLOAD_DATA_SIZE = -1
-
- if MAX_PAYLOAD_DATA_SIZE <= 0:
- self._write(self._writer.build(message, end, binary))
- return
-
- bytes_written = 0
- while True:
- end_for_this_frame = end
- bytes_to_write = len(message) - bytes_written
- if (MAX_PAYLOAD_DATA_SIZE > 0
- and bytes_to_write > MAX_PAYLOAD_DATA_SIZE):
- end_for_this_frame = False
- bytes_to_write = MAX_PAYLOAD_DATA_SIZE
-
- frame = self._writer.build(
- message[bytes_written:bytes_written + bytes_to_write],
- end_for_this_frame, binary)
- self._write(frame)
-
- bytes_written += bytes_to_write
-
- # This if must be placed here (the end of while block) so that
- # at least one frame is sent.
- if len(message) <= bytes_written:
- break
- except ValueError as e:
- raise BadOperationException(e)
-
- def _get_message_from_frame(self, frame):
- """Gets a message from frame. If the message is composed of fragmented
- frames and the frame is not the last fragmented frame, this method
- returns None. The whole message will be returned when the last
- fragmented frame is passed to this method.
-
- Raises:
- InvalidFrameException: when the frame doesn't match defragmentation
- context, or the frame contains invalid data.
- """
-
- if frame.opcode == common.OPCODE_CONTINUATION:
- if not self._received_fragments:
- if frame.fin:
- raise InvalidFrameException(
- 'Received a termination frame but fragmentation '
- 'not started')
- else:
- raise InvalidFrameException(
- 'Received an intermediate frame but '
- 'fragmentation not started')
-
- if frame.fin:
- # End of fragmentation frame
- self._received_fragments.append(frame.payload)
- message = b''.join(self._received_fragments)
- self._received_fragments = []
- return message
- else:
- # Intermediate frame
- self._received_fragments.append(frame.payload)
- return None
- else:
- if self._received_fragments:
- if frame.fin:
- raise InvalidFrameException(
- 'Received an unfragmented frame without '
- 'terminating existing fragmentation')
- else:
- raise InvalidFrameException(
- 'New fragmentation started without terminating '
- 'existing fragmentation')
-
- if frame.fin:
- # Unfragmented frame
-
- self._original_opcode = frame.opcode
- return frame.payload
- else:
- # Start of fragmentation frame
-
- if common.is_control_opcode(frame.opcode):
- raise InvalidFrameException(
- 'Control frames must not be fragmented')
-
- self._original_opcode = frame.opcode
- self._received_fragments.append(frame.payload)
- return None
-
- def _process_close_message(self, message):
- """Processes close message.
-
- Args:
- message: close message.
-
- Raises:
- InvalidFrameException: when the message is invalid.
- """
-
- self._request.client_terminated = True
-
- # Status code is optional. We can have status reason only if we
- # have status code. Status reason can be empty string. So,
- # allowed cases are
- # - no application data: no code no reason
- # - 2 octet of application data: has code but no reason
- # - 3 or more octet of application data: both code and reason
- if len(message) == 0:
- self._logger.debug('Received close frame (empty body)')
- self._request.ws_close_code = common.STATUS_NO_STATUS_RECEIVED
- elif len(message) == 1:
- raise InvalidFrameException(
- 'If a close frame has status code, the length of '
- 'status code must be 2 octet')
- elif len(message) >= 2:
- self._request.ws_close_code = struct.unpack('!H', message[0:2])[0]
- self._request.ws_close_reason = message[2:].decode(
- 'utf-8', 'replace')
- self._logger.debug('Received close frame (code=%d, reason=%r)',
- self._request.ws_close_code,
- self._request.ws_close_reason)
-
- # As we've received a close frame, no more data is coming over the
- # socket. We can now safely close the socket without worrying about
- # RST sending.
-
- if self._request.server_terminated:
- self._logger.debug(
- 'Received ack for server-initiated closing handshake')
- return
-
- self._logger.debug('Received client-initiated closing handshake')
-
- code = common.STATUS_NORMAL_CLOSURE
- reason = ''
- if hasattr(self._request, '_dispatcher'):
- dispatcher = self._request._dispatcher
- code, reason = dispatcher.passive_closing_handshake(self._request)
- if code is None and reason is not None and len(reason) > 0:
- self._logger.warning(
- 'Handler specified reason despite code being None')
- reason = ''
- if reason is None:
- reason = ''
- self._send_closing_handshake(code, reason)
- self._logger.debug(
- 'Acknowledged closing handshake initiated by the peer '
- '(code=%r, reason=%r)', code, reason)
-
- def _process_ping_message(self, message):
- """Processes ping message.
-
- Args:
- message: ping message.
- """
-
- try:
- handler = self._request.on_ping_handler
- if handler:
- handler(self._request, message)
- return
- except AttributeError:
- pass
- self._send_pong(message)
-
- def _process_pong_message(self, message):
- """Processes pong message.
-
- Args:
- message: pong message.
- """
-
- # TODO(tyoshino): Add ping timeout handling.
-
- inflight_pings = deque()
-
- while True:
- try:
- expected_body = self._ping_queue.popleft()
- if expected_body == message:
- # inflight_pings contains pings ignored by the
- # other peer. Just forget them.
- self._logger.debug(
- 'Ping %r is acked (%d pings were ignored)',
- expected_body, len(inflight_pings))
- break
- else:
- inflight_pings.append(expected_body)
- except IndexError:
- # The received pong was unsolicited pong. Keep the
- # ping queue as is.
- self._ping_queue = inflight_pings
- self._logger.debug('Received a unsolicited pong')
- break
-
- try:
- handler = self._request.on_pong_handler
- if handler:
- handler(self._request, message)
- except AttributeError:
- pass
-
- def receive_message(self):
- """Receive a WebSocket frame and return its payload as a text in
- unicode or a binary in str.
-
- Returns:
- payload data of the frame
- - as unicode instance if received text frame
- - as str instance if received binary frame
- or None iff received closing handshake.
- Raises:
- BadOperationException: when called on a client-terminated
- connection.
- ConnectionTerminatedException: when read returns empty
- string.
- InvalidFrameException: when the frame contains invalid
- data.
- UnsupportedFrameException: when the received frame has
- flags, opcode we cannot handle. You can ignore this
- exception and continue receiving the next frame.
- """
-
- if self._request.client_terminated:
- raise BadOperationException(
- 'Requested receive_message after receiving a closing '
- 'handshake')
-
- while True:
- # mp_conn.read will block if no bytes are available.
-
- frame = self._receive_frame_as_frame_object()
-
- # Check the constraint on the payload size for control frames
- # before extension processes the frame.
- # See also http://tools.ietf.org/html/rfc6455#section-5.5
- if (common.is_control_opcode(frame.opcode)
- and len(frame.payload) > 125):
- raise InvalidFrameException(
- 'Payload data size of control frames must be 125 bytes or '
- 'less')
-
- for frame_filter in self._options.incoming_frame_filters:
- frame_filter.filter(frame)
-
- if frame.rsv1 or frame.rsv2 or frame.rsv3:
- raise UnsupportedFrameException(
- 'Unsupported flag is set (rsv = %d%d%d)' %
- (frame.rsv1, frame.rsv2, frame.rsv3))
-
- message = self._get_message_from_frame(frame)
- if message is None:
- continue
-
- for message_filter in self._options.incoming_message_filters:
- message = message_filter.filter(message)
-
- if self._original_opcode == common.OPCODE_TEXT:
- # The WebSocket protocol section 4.4 specifies that invalid
- # characters must be replaced with U+fffd REPLACEMENT
- # CHARACTER.
- try:
- return message.decode('utf-8')
- except UnicodeDecodeError as e:
- raise InvalidUTF8Exception(e)
- elif self._original_opcode == common.OPCODE_BINARY:
- return message
- elif self._original_opcode == common.OPCODE_CLOSE:
- self._process_close_message(message)
- return None
- elif self._original_opcode == common.OPCODE_PING:
- self._process_ping_message(message)
- elif self._original_opcode == common.OPCODE_PONG:
- self._process_pong_message(message)
- else:
- raise UnsupportedFrameException('Opcode %d is not supported' %
- self._original_opcode)
-
- def _send_closing_handshake(self, code, reason):
- body = create_closing_handshake_body(code, reason)
- frame = create_close_frame(
- body,
- mask=self._options.mask_send,
- frame_filters=self._options.outgoing_frame_filters)
-
- self._request.server_terminated = True
-
- self._write(frame)
-
- def close_connection(self,
- code=common.STATUS_NORMAL_CLOSURE,
- reason='',
- wait_response=True):
- """Closes a WebSocket connection. Note that this method blocks until
- it receives acknowledgement to the closing handshake.
-
- Args:
- code: Status code for close frame. If code is None, a close
- frame with empty body will be sent.
- reason: string representing close reason.
- wait_response: True when caller want to wait the response.
- Raises:
- BadOperationException: when reason is specified with code None
- or reason is not an instance of both str and unicode.
- """
-
- if self._request.server_terminated:
- self._logger.debug(
- 'Requested close_connection but server is already terminated')
- return
-
- # When we receive a close frame, we call _process_close_message().
- # _process_close_message() immediately acknowledges to the
- # server-initiated closing handshake and sets server_terminated to
- # True. So, here we can assume that we haven't received any close
- # frame. We're initiating a closing handshake.
-
- if code is None:
- if reason is not None and len(reason) > 0:
- raise BadOperationException(
- 'close reason must not be specified if code is None')
- reason = ''
- else:
- if not isinstance(reason, bytes) and not isinstance(
- reason, six.text_type):
- raise BadOperationException(
- 'close reason must be an instance of bytes or unicode')
-
- self._send_closing_handshake(code, reason)
- self._logger.debug('Initiated closing handshake (code=%r, reason=%r)',
- code, reason)
-
- if (code == common.STATUS_GOING_AWAY
- or code == common.STATUS_PROTOCOL_ERROR) or not wait_response:
- # It doesn't make sense to wait for a close frame if the reason is
- # protocol error or that the server is going away. For some of
- # other reasons, it might not make sense to wait for a close frame,
- # but it's not clear, yet.
- return
-
- # TODO(ukai): 2. wait until the /client terminated/ flag has been set,
- # or until a server-defined timeout expires.
- #
- # For now, we expect receiving closing handshake right after sending
- # out closing handshake.
- message = self.receive_message()
- if message is not None:
- raise ConnectionTerminatedException(
- 'Didn\'t receive valid ack for closing handshake')
- # TODO: 3. close the WebSocket connection.
- # note: mod_python Connection (mp_conn) doesn't have close method.
-
- def send_ping(self, body, binary=False):
- if not binary and isinstance(body, six.text_type):
- body = body.encode('UTF-8')
- frame = create_ping_frame(body, self._options.mask_send,
- self._options.outgoing_frame_filters)
- self._write(frame)
-
- self._ping_queue.append(body)
-
- def _send_pong(self, body):
- frame = create_pong_frame(body, self._options.mask_send,
- self._options.outgoing_frame_filters)
- self._write(frame)
-
- def get_last_received_opcode(self):
- """Returns the opcode of the WebSocket message which the last received
- frame belongs to. The return value is valid iff immediately after
- receive_message call.
- """
-
- return self._original_opcode
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/util.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/util.py
deleted file mode 100644
index 04006ecacd..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/util.py
+++ /dev/null
@@ -1,386 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""WebSocket utilities."""
-
-from __future__ import absolute_import
-import array
-import errno
-import logging
-import os
-import re
-import six
-from six.moves import map
-from six.moves import range
-import socket
-import struct
-import zlib
-
-try:
- from mod_pywebsocket import fast_masking
-except ImportError:
- pass
-
-
-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):
- r"""Get #!-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 hexify(s):
- return ' '.join(['%02x' % x for x in six.iterbytes(s)])
-
-
-def get_class_logger(o):
- """Return the logging class information."""
- return logging.getLogger('%s.%s' %
- (o.__class__.__module__, o.__class__.__name__))
-
-
-def pack_byte(b):
- """Pack an integer to network-ordered byte"""
- return struct.pack('!B', b)
-
-
-class NoopMasker(object):
- """A NoOp masking object.
-
- This has the same interface as RepeatedXorMasker but just returns
- the string passed in without making any change.
- """
- def __init__(self):
- """NoOp."""
- pass
-
- def mask(self, s):
- """NoOp."""
- return s
-
-
-class RepeatedXorMasker(object):
- """A masking object that applies XOR on the string.
-
- Applies XOR on the byte string given to mask method with the masking bytes
- given to the constructor repeatedly. This object remembers the position
- in the masking bytes the last mask method call ended and resumes from
- that point on the next mask method call.
- """
- def __init__(self, masking_key):
- self._masking_key = masking_key
- self._masking_key_index = 0
-
- def _mask_using_swig(self, s):
- """Perform the mask via SWIG."""
- masked_data = fast_masking.mask(s, self._masking_key,
- self._masking_key_index)
- self._masking_key_index = ((self._masking_key_index + len(s)) %
- len(self._masking_key))
- return masked_data
-
- def _mask_using_array(self, s):
- """Perform the mask via python."""
- if isinstance(s, six.text_type):
- raise Exception(
- 'Masking Operation should not process unicode strings')
-
- result = bytearray(s)
-
- # Use temporary local variables to eliminate the cost to access
- # attributes
- masking_key = [c for c in six.iterbytes(self._masking_key)]
- masking_key_size = len(masking_key)
- masking_key_index = self._masking_key_index
-
- for i in range(len(result)):
- result[i] ^= masking_key[masking_key_index]
- masking_key_index = (masking_key_index + 1) % masking_key_size
-
- self._masking_key_index = masking_key_index
-
- return bytes(result)
-
- if 'fast_masking' in globals():
- mask = _mask_using_swig
- else:
- mask = _mask_using_array
-
-
-# By making wbits option negative, we can suppress CMF/FLG (2 octet) and
-# ADLER32 (4 octet) fields of zlib so that we can use zlib module just as
-# deflate library. DICTID won't be added as far as we don't set dictionary.
-# LZ77 window of 32K will be used for both compression and decompression.
-# For decompression, we can just use 32K to cover any windows size. For
-# compression, we use 32K so receivers must use 32K.
-#
-# Compression level is Z_DEFAULT_COMPRESSION. We don't have to match level
-# to decode.
-#
-# See zconf.h, deflate.cc, inflate.cc of zlib library, and zlibmodule.c of
-# Python. See also RFC1950 (ZLIB 3.3).
-
-
-class _Deflater(object):
- def __init__(self, window_bits):
- self._logger = get_class_logger(self)
-
- # Using the smallest window bits of 9 for generating input frames.
- # On WebSocket spec, the smallest window bit is 8. However, zlib does
- # not accept window_bit = 8.
- #
- # Because of a zlib deflate quirk, back-references will not use the
- # entire range of 1 << window_bits, but will instead use a restricted
- # range of (1 << window_bits) - 262. With an increased window_bits = 9,
- # back-references will be within a range of 250. These can still be
- # decompressed with window_bits = 8 and the 256-byte window used there.
- #
- # Similar disscussions can be found in https://crbug.com/691074
- window_bits = max(window_bits, 9)
-
- self._compress = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
- zlib.DEFLATED, -window_bits)
-
- def compress(self, bytes):
- compressed_bytes = self._compress.compress(bytes)
- self._logger.debug('Compress input %r', bytes)
- self._logger.debug('Compress result %r', compressed_bytes)
- return compressed_bytes
-
- def compress_and_flush(self, bytes):
- compressed_bytes = self._compress.compress(bytes)
- compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH)
- self._logger.debug('Compress input %r', bytes)
- self._logger.debug('Compress result %r', compressed_bytes)
- return compressed_bytes
-
- def compress_and_finish(self, bytes):
- compressed_bytes = self._compress.compress(bytes)
- compressed_bytes += self._compress.flush(zlib.Z_FINISH)
- self._logger.debug('Compress input %r', bytes)
- self._logger.debug('Compress result %r', compressed_bytes)
- return compressed_bytes
-
-
-class _Inflater(object):
- def __init__(self, window_bits):
- self._logger = get_class_logger(self)
- self._window_bits = window_bits
-
- self._unconsumed = b''
-
- self.reset()
-
- def decompress(self, size):
- if not (size == -1 or size > 0):
- raise Exception('size must be -1 or positive')
-
- data = b''
-
- while True:
- data += self._decompress.decompress(self._unconsumed,
- max(0, size - len(data)))
- self._unconsumed = self._decompress.unconsumed_tail
- if self._decompress.unused_data:
- # Encountered a last block (i.e. a block with BFINAL = 1) and
- # found a new stream (unused_data). We cannot use the same
- # zlib.Decompress object for the new stream. Create a new
- # Decompress object to decompress the new one.
- #
- # It's fine to ignore unconsumed_tail if unused_data is not
- # empty.
- self._unconsumed = self._decompress.unused_data
- self.reset()
- if size >= 0 and len(data) == size:
- # data is filled. Don't call decompress again.
- break
- else:
- # Re-invoke Decompress.decompress to try to decompress all
- # available bytes before invoking read which blocks until
- # any new byte is available.
- continue
- else:
- # Here, since unused_data is empty, even if unconsumed_tail is
- # not empty, bytes of requested length are already in data. We
- # don't have to "continue" here.
- break
-
- if data:
- self._logger.debug('Decompressed %r', data)
- return data
-
- def append(self, data):
- self._logger.debug('Appended %r', data)
- self._unconsumed += data
-
- def reset(self):
- self._logger.debug('Reset')
- self._decompress = zlib.decompressobj(-self._window_bits)
-
-
-# Compresses/decompresses given octets using the method introduced in RFC1979.
-
-
-class _RFC1979Deflater(object):
- """A compressor class that applies DEFLATE to given byte sequence and
- flushes using the algorithm described in the RFC1979 section 2.1.
- """
- def __init__(self, window_bits, no_context_takeover):
- self._deflater = None
- if window_bits is None:
- window_bits = zlib.MAX_WBITS
- self._window_bits = window_bits
- self._no_context_takeover = no_context_takeover
-
- def filter(self, bytes, end=True, bfinal=False):
- if self._deflater is None:
- self._deflater = _Deflater(self._window_bits)
-
- if bfinal:
- result = self._deflater.compress_and_finish(bytes)
- # Add a padding block with BFINAL = 0 and BTYPE = 0.
- result = result + pack_byte(0)
- self._deflater = None
- return result
-
- result = self._deflater.compress_and_flush(bytes)
- if end:
- # Strip last 4 octets which is LEN and NLEN field of a
- # non-compressed block added for Z_SYNC_FLUSH.
- result = result[:-4]
-
- if self._no_context_takeover and end:
- self._deflater = None
-
- return result
-
-
-class _RFC1979Inflater(object):
- """A decompressor class a la RFC1979.
-
- A decompressor class for byte sequence compressed and flushed following
- the algorithm described in the RFC1979 section 2.1.
- """
- def __init__(self, window_bits=zlib.MAX_WBITS):
- self._inflater = _Inflater(window_bits)
-
- def filter(self, bytes):
- # Restore stripped LEN and NLEN field of a non-compressed block added
- # for Z_SYNC_FLUSH.
- self._inflater.append(bytes + b'\x00\x00\xff\xff')
- return self._inflater.decompress(-1)
-
-
-class DeflateSocket(object):
- """A wrapper class for socket object to intercept send and recv to perform
- deflate compression and decompression transparently.
- """
-
- # Size of the buffer passed to recv to receive compressed data.
- _RECV_SIZE = 4096
-
- def __init__(self, socket):
- self._socket = socket
-
- self._logger = get_class_logger(self)
-
- self._deflater = _Deflater(zlib.MAX_WBITS)
- self._inflater = _Inflater(zlib.MAX_WBITS)
-
- def recv(self, size):
- """Receives data from the socket specified on the construction up
- to the specified size. Once any data is available, returns it even
- if it's smaller than the specified size.
- """
-
- # TODO(tyoshino): Allow call with size=0. It should block until any
- # decompressed data is available.
- if size <= 0:
- raise Exception('Non-positive size passed')
- while True:
- data = self._inflater.decompress(size)
- if len(data) != 0:
- return data
-
- read_data = self._socket.recv(DeflateSocket._RECV_SIZE)
- if not read_data:
- return b''
- self._inflater.append(read_data)
-
- def sendall(self, bytes):
- self.send(bytes)
-
- def send(self, bytes):
- self._socket.sendall(self._deflater.compress_and_flush(bytes))
- return len(bytes)
-
-
-# vi:sts=4 sw=4 et
diff --git a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/websocket_server.py b/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/websocket_server.py
deleted file mode 100644
index 9f67c9f02d..0000000000
--- a/testing/web-platform/tests/tools/third_party/pywebsocket3/mod_pywebsocket/websocket_server.py
+++ /dev/null
@@ -1,289 +0,0 @@
-# Copyright 2020, 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 WebsocketServer
-
-This file deals with the main module of standalone server. Although it is fine
-to import this file directly to use WebSocketServer, it is strongly recommended
-to use standalone.py, since it is intended to act as a skeleton of this module.
-"""
-
-from __future__ import absolute_import
-from six.moves import BaseHTTPServer
-from six.moves import socketserver
-import logging
-import re
-import select
-import socket
-import ssl
-import threading
-import traceback
-
-from mod_pywebsocket import dispatch
-from mod_pywebsocket import util
-from mod_pywebsocket.request_handler import WebSocketRequestHandler
-
-
-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
- """
-
- with open(websock_handlers_map_file) as f:
- for line in f:
- if line[0] == '#' or line.isspace():
- continue
- m = re.match(r'(\S+)\s+(\S+)$', line)
- if not m:
- logging.warning('Wrong format in map file:' + line)
- continue
- try:
- dispatcher.add_resource_path_alias(m.group(1), m.group(2))
- except dispatch.DispatchException as e:
- logging.error(str(e))
-
-
-class WebSocketServer(socketserver.ThreadingMixIn, BaseHTTPServer.HTTPServer):
- """HTTPServer specialized for WebSocket."""
-
- # Overrides SocketServer.ThreadingMixIn.daemon_threads
- daemon_threads = True
- # Overrides BaseHTTPServer.HTTPServer.allow_reuse_address
- allow_reuse_address = True
-
- def __init__(self, options):
- """Override SocketServer.TCPServer.__init__ to set SSL enabled
- socket object to self.socket before server_bind and server_activate,
- if necessary.
- """
-
- # Fall back to None for embedders that don't know about the
- # handler_encoding option.
- handler_encoding = getattr(options, "handler_encoding", None)
-
- # Share a Dispatcher among request handlers to save time for
- # instantiation. Dispatcher can be shared because it is thread-safe.
- options.dispatcher = dispatch.Dispatcher(
- options.websock_handlers, options.scan_dir,
- options.allow_handlers_outside_root_dir, handler_encoding)
- if options.websock_handlers_map_file:
- _alias_handlers(options.dispatcher,
- options.websock_handlers_map_file)
- warnings = options.dispatcher.source_warnings()
- if warnings:
- for warning in warnings:
- logging.warning('Warning in source loading: %s' % warning)
-
- self._logger = util.get_class_logger(self)
-
- self.request_queue_size = options.request_queue_size
- self.__ws_is_shut_down = threading.Event()
- self.__ws_serving = False
-
- socketserver.BaseServer.__init__(self,
- (options.server_host, options.port),
- WebSocketRequestHandler)
-
- # Expose the options object to allow handler objects access it. We name
- # it with websocket_ prefix to avoid conflict.
- self.websocket_server_options = options
-
- self._create_sockets()
- self.server_bind()
- self.server_activate()
-
- def _create_sockets(self):
- self.server_name, self.server_port = self.server_address
- self._sockets = []
- if not self.server_name:
- # On platforms that doesn't support IPv6, the first bind fails.
- # On platforms that supports IPv6
- # - If it binds both IPv4 and IPv6 on call with AF_INET6, the
- # first bind succeeds and the second fails (we'll see 'Address
- # already in use' error).
- # - If it binds only IPv6 on call with AF_INET6, both call are
- # expected to succeed to listen both protocol.
- addrinfo_array = [(socket.AF_INET6, socket.SOCK_STREAM, '', '',
- ''),
- (socket.AF_INET, socket.SOCK_STREAM, '', '', '')]
- else:
- addrinfo_array = socket.getaddrinfo(self.server_name,
- self.server_port,
- socket.AF_UNSPEC,
- socket.SOCK_STREAM,
- socket.IPPROTO_TCP)
- for addrinfo in addrinfo_array:
- self._logger.info('Create socket on: %r', addrinfo)
- family, socktype, proto, canonname, sockaddr = addrinfo
- try:
- socket_ = socket.socket(family, socktype)
- except Exception as e:
- self._logger.info('Skip by failure: %r', e)
- continue
- server_options = self.websocket_server_options
- if server_options.use_tls:
- if server_options.tls_client_auth:
- if server_options.tls_client_cert_optional:
- client_cert_ = ssl.CERT_OPTIONAL
- else:
- client_cert_ = ssl.CERT_REQUIRED
- else:
- client_cert_ = ssl.CERT_NONE
- socket_ = ssl.wrap_socket(
- socket_,
- keyfile=server_options.private_key,
- certfile=server_options.certificate,
- ca_certs=server_options.tls_client_ca,
- cert_reqs=client_cert_)
- self._sockets.append((socket_, addrinfo))
-
- def server_bind(self):
- """Override SocketServer.TCPServer.server_bind to enable multiple
- sockets bind.
- """
-
- failed_sockets = []
-
- for socketinfo in self._sockets:
- socket_, addrinfo = socketinfo
- self._logger.info('Bind on: %r', addrinfo)
- if self.allow_reuse_address:
- socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- try:
- socket_.bind(self.server_address)
- except Exception as e:
- self._logger.info('Skip by failure: %r', e)
- socket_.close()
- failed_sockets.append(socketinfo)
- if self.server_address[1] == 0:
- # The operating system assigns the actual port number for port
- # number 0. This case, the second and later sockets should use
- # the same port number. Also self.server_port is rewritten
- # because it is exported, and will be used by external code.
- self.server_address = (self.server_name,
- socket_.getsockname()[1])
- self.server_port = self.server_address[1]
- self._logger.info('Port %r is assigned', self.server_port)
-
- for socketinfo in failed_sockets:
- self._sockets.remove(socketinfo)
-
- def server_activate(self):
- """Override SocketServer.TCPServer.server_activate to enable multiple
- sockets listen.
- """
-
- failed_sockets = []
-
- for socketinfo in self._sockets:
- socket_, addrinfo = socketinfo
- self._logger.info('Listen on: %r', addrinfo)
- try:
- socket_.listen(self.request_queue_size)
- except Exception as e:
- self._logger.info('Skip by failure: %r', e)
- socket_.close()
- failed_sockets.append(socketinfo)
-
- for socketinfo in failed_sockets:
- self._sockets.remove(socketinfo)
-
- if len(self._sockets) == 0:
- self._logger.critical(
- 'No sockets activated. Use info log level to see the reason.')
-
- def server_close(self):
- """Override SocketServer.TCPServer.server_close to enable multiple
- sockets close.
- """
-
- for socketinfo in self._sockets:
- socket_, addrinfo = socketinfo
- self._logger.info('Close on: %r', addrinfo)
- socket_.close()
-
- def fileno(self):
- """Override SocketServer.TCPServer.fileno."""
-
- self._logger.critical('Not supported: fileno')
- return self._sockets[0][0].fileno()
-
- def handle_error(self, request, client_address):
- """Override SocketServer.handle_error."""
-
- self._logger.error('Exception in processing request from: %r\n%s',
- client_address, traceback.format_exc())
- # Note: client_address is a tuple.
-
- def get_request(self):
- """Override TCPServer.get_request."""
-
- accepted_socket, client_address = self.socket.accept()
-
- server_options = self.websocket_server_options
- if server_options.use_tls:
- # Print cipher in use. Handshake is done on accept.
- self._logger.debug('Cipher: %s', accepted_socket.cipher())
- self._logger.debug('Client cert: %r',
- accepted_socket.getpeercert())
-
- return accepted_socket, client_address
-
- def serve_forever(self, poll_interval=0.5):
- """Override SocketServer.BaseServer.serve_forever."""
-
- self.__ws_serving = True
- self.__ws_is_shut_down.clear()
- handle_request = self.handle_request
- if hasattr(self, '_handle_request_noblock'):
- handle_request = self._handle_request_noblock
- else:
- self._logger.warning('Fallback to blocking request handler')
- try:
- while self.__ws_serving:
- r, w, e = select.select(
- [socket_[0] for socket_ in self._sockets], [], [],
- poll_interval)
- for socket_ in r:
- self.socket = socket_
- handle_request()
- self.socket = None
- finally:
- self.__ws_is_shut_down.set()
-
- def shutdown(self):
- """Override SocketServer.BaseServer.shutdown."""
-
- self.__ws_serving = False
- self.__ws_is_shut_down.wait()
-
-
-# vi:sts=4 sw=4 et