summaryrefslogtreecommitdiffstats
path: root/module/lib/mod_pywebsocket/handshake
diff options
context:
space:
mode:
Diffstat (limited to 'module/lib/mod_pywebsocket/handshake')
-rw-r--r--module/lib/mod_pywebsocket/handshake/__init__.py110
-rw-r--r--module/lib/mod_pywebsocket/handshake/_base.py226
-rw-r--r--module/lib/mod_pywebsocket/handshake/hybi.py404
-rw-r--r--module/lib/mod_pywebsocket/handshake/hybi00.py242
4 files changed, 0 insertions, 982 deletions
diff --git a/module/lib/mod_pywebsocket/handshake/__init__.py b/module/lib/mod_pywebsocket/handshake/__init__.py
deleted file mode 100644
index 194f6b395..000000000
--- a/module/lib/mod_pywebsocket/handshake/__init__.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""WebSocket opening handshake processor. This class try to apply available
-opening handshake processors for each protocol version until a connection is
-successfully established.
-"""
-
-
-import logging
-
-from mod_pywebsocket import common
-from mod_pywebsocket.handshake import hybi00
-from mod_pywebsocket.handshake import hybi
-# Export AbortedByUserException, HandshakeException, and VersionException
-# symbol from this module.
-from mod_pywebsocket.handshake._base import AbortedByUserException
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import VersionException
-
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
- """Performs WebSocket handshake.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
- allowDraft75: obsolete argument. ignored.
- strict: obsolete argument. ignored.
-
- Handshaker will add attributes such as ws_resource in performing
- handshake.
- """
-
- _LOGGER.debug('Client\'s opening handshake resource: %r', request.uri)
- # To print mimetools.Message as escaped one-line string, we converts
- # headers_in to dict object. Without conversion, if we use %r, it just
- # prints the type and address, and if we use %s, it prints the original
- # header string as multiple lines.
- #
- # Both mimetools.Message and MpTable_Type of mod_python can be
- # converted to dict.
- #
- # mimetools.Message.__str__ returns the original header string.
- # dict(mimetools.Message object) returns the map from header names to
- # header values. While MpTable_Type doesn't have such __str__ but just
- # __repr__ which formats itself as well as dictionary object.
- _LOGGER.debug(
- 'Client\'s opening handshake headers: %r', dict(request.headers_in))
-
- handshakers = []
- handshakers.append(
- ('RFC 6455', hybi.Handshaker(request, dispatcher)))
- handshakers.append(
- ('HyBi 00', hybi00.Handshaker(request, dispatcher)))
-
- for name, handshaker in handshakers:
- _LOGGER.debug('Trying protocol version %s', name)
- try:
- handshaker.do_handshake()
- _LOGGER.info('Established (%s protocol)', name)
- return
- except HandshakeException, e:
- _LOGGER.debug(
- 'Failed to complete opening handshake as %s protocol: %r',
- name, e)
- if e.status:
- raise e
- except AbortedByUserException, e:
- raise
- except VersionException, e:
- raise
-
- # TODO(toyoshim): Add a test to cover the case all handshakers fail.
- raise HandshakeException(
- 'Failed to complete opening handshake for all available protocols',
- status=common.HTTP_STATUS_BAD_REQUEST)
-
-
-# vi:sts=4 sw=4 et
diff --git a/module/lib/mod_pywebsocket/handshake/_base.py b/module/lib/mod_pywebsocket/handshake/_base.py
deleted file mode 100644
index e5c94ca90..000000000
--- a/module/lib/mod_pywebsocket/handshake/_base.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""Common functions and exceptions used by WebSocket opening handshake
-processors.
-"""
-
-
-from mod_pywebsocket import common
-from mod_pywebsocket import http_header_util
-
-
-class AbortedByUserException(Exception):
- """Exception for aborting a connection intentionally.
-
- If this exception is raised in do_extra_handshake handler, the connection
- will be abandoned. No other WebSocket or HTTP(S) handler will be invoked.
-
- If this exception is raised in transfer_data_handler, the connection will
- be closed without closing handshake. No other WebSocket or HTTP(S) handler
- will be invoked.
- """
-
- pass
-
-
-class HandshakeException(Exception):
- """This exception will be raised when an error occurred while processing
- WebSocket initial handshake.
- """
-
- def __init__(self, name, status=None):
- super(HandshakeException, self).__init__(name)
- self.status = status
-
-
-class VersionException(Exception):
- """This exception will be raised when a version of client request does not
- match with version the server supports.
- """
-
- def __init__(self, name, supported_versions=''):
- """Construct an instance.
-
- Args:
- supported_version: a str object to show supported hybi versions.
- (e.g. '8, 13')
- """
- super(VersionException, self).__init__(name)
- self.supported_versions = supported_versions
-
-
-def get_default_port(is_secure):
- if is_secure:
- return common.DEFAULT_WEB_SOCKET_SECURE_PORT
- else:
- return common.DEFAULT_WEB_SOCKET_PORT
-
-
-def validate_subprotocol(subprotocol, hixie):
- """Validate a value in the Sec-WebSocket-Protocol field.
-
- See
- - RFC 6455: Section 4.1., 4.2.2., and 4.3.
- - HyBi 00: Section 4.1. Opening handshake
-
- Args:
- hixie: if True, checks if characters in subprotocol are in range
- between U+0020 and U+007E. It's required by HyBi 00 but not by
- RFC 6455.
- """
-
- if not subprotocol:
- raise HandshakeException('Invalid subprotocol name: empty')
- if hixie:
- # Parameter should be in the range U+0020 to U+007E.
- for c in subprotocol:
- if not 0x20 <= ord(c) <= 0x7e:
- raise HandshakeException(
- 'Illegal character in subprotocol name: %r' % c)
- else:
- # 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['Host'].split(':', 1)
- if len(fields) == 1:
- return fields[0], get_default_port(request.is_https())
- try:
- return fields[0], int(fields[1])
- except ValueError, e:
- raise HandshakeException('Invalid port number format: %r' % e)
-
-
-def format_header(name, value):
- return '%s: %s\r\n' % (name, value)
-
-
-def build_location(request):
- """Build WebSocket location for request."""
- location_parts = []
- if request.is_https():
- location_parts.append(common.WEB_SOCKET_SECURE_SCHEME)
- else:
- location_parts.append(common.WEB_SOCKET_SCHEME)
- location_parts.append('://')
- host, port = parse_host_header(request)
- connection_port = request.connection.local_addr[1]
- if port != connection_port:
- raise HandshakeException('Header/connection port mismatch: %d/%d' %
- (port, connection_port))
- location_parts.append(host)
- if (port != get_default_port(request.is_https())):
- location_parts.append(':')
- location_parts.append(str(port))
- location_parts.append(request.uri)
- return ''.join(location_parts)
-
-
-def get_mandatory_header(request, key):
- value = request.headers_in.get(key)
- if value is None:
- raise HandshakeException('Header %s is not defined' % key)
- return value
-
-
-def validate_mandatory_header(request, key, expected_value, fail_status=None):
- value = get_mandatory_header(request, key)
-
- if value.lower() != expected_value.lower():
- raise HandshakeException(
- 'Expected %r for header %s but found %r (case-insensitive)' %
- (expected_value, key, value), status=fail_status)
-
-
-def check_request_line(request):
- # 5.1 1. The three character UTF-8 string "GET".
- # 5.1 2. A UTF-8-encoded U+0020 SPACE character (0x20 byte).
- if request.method != 'GET':
- raise HandshakeException('Method is not GET: %r' % request.method)
-
- if request.protocol != 'HTTP/1.1':
- raise HandshakeException('Version is not HTTP/1.1: %r' %
- request.protocol)
-
-
-def check_header_lines(request, mandatory_headers):
- check_request_line(request)
-
- # The expected field names, and the meaning of their corresponding
- # values, are as follows.
- # |Upgrade| and |Connection|
- for key, expected_value in mandatory_headers:
- validate_mandatory_header(request, key, expected_value)
-
-
-def parse_token_list(data):
- """Parses a header value which follows 1#token and returns parsed elements
- as a list of strings.
-
- Leading LWSes must be trimmed.
- """
-
- state = http_header_util.ParsingState(data)
-
- token_list = []
-
- while True:
- token = http_header_util.consume_token(state)
- if token is not None:
- token_list.append(token)
-
- http_header_util.consume_lwses(state)
-
- if http_header_util.peek(state) is None:
- break
-
- if not http_header_util.consume_string(state, ','):
- raise HandshakeException(
- 'Expected a comma but found %r' % http_header_util.peek(state))
-
- http_header_util.consume_lwses(state)
-
- if len(token_list) == 0:
- raise HandshakeException('No valid token found')
-
- return token_list
-
-
-# vi:sts=4 sw=4 et
diff --git a/module/lib/mod_pywebsocket/handshake/hybi.py b/module/lib/mod_pywebsocket/handshake/hybi.py
deleted file mode 100644
index fc0e2a096..000000000
--- a/module/lib/mod_pywebsocket/handshake/hybi.py
+++ /dev/null
@@ -1,404 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""This file provides the opening handshake processor for the WebSocket
-protocol (RFC 6455).
-
-Specification:
-http://tools.ietf.org/html/rfc6455
-"""
-
-
-# Note: request.connection.write is used in this module, even though mod_python
-# document says that it should be used only in connection handlers.
-# Unfortunately, we have no other options. For example, request.write is not
-# suitable because it doesn't allow direct raw bytes writing.
-
-
-import base64
-import logging
-import os
-import re
-
-from mod_pywebsocket import common
-from mod_pywebsocket.extensions import get_extension_processor
-from mod_pywebsocket.handshake._base import check_request_line
-from mod_pywebsocket.handshake._base import format_header
-from mod_pywebsocket.handshake._base import get_mandatory_header
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import parse_token_list
-from mod_pywebsocket.handshake._base import validate_mandatory_header
-from mod_pywebsocket.handshake._base import validate_subprotocol
-from mod_pywebsocket.handshake._base import VersionException
-from mod_pywebsocket.stream import Stream
-from mod_pywebsocket.stream import StreamOptions
-from mod_pywebsocket import util
-
-
-# Used to validate the value in the Sec-WebSocket-Key header strictly. RFC 4648
-# disallows non-zero padding, so the character right before == must be any of
-# A, Q, g and w.
-_SEC_WEBSOCKET_KEY_REGEX = re.compile('^[+/0-9A-Za-z]{21}[AQgw]==$')
-
-# Defining aliases for values used frequently.
-_VERSION_HYBI08 = common.VERSION_HYBI08
-_VERSION_HYBI08_STRING = str(_VERSION_HYBI08)
-_VERSION_LATEST = common.VERSION_HYBI_LATEST
-_VERSION_LATEST_STRING = str(_VERSION_LATEST)
-_SUPPORTED_VERSIONS = [
- _VERSION_LATEST,
- _VERSION_HYBI08,
-]
-
-
-def compute_accept(key):
- """Computes value for the Sec-WebSocket-Accept header from value of the
- Sec-WebSocket-Key header.
- """
-
- accept_binary = util.sha1_hash(
- key + common.WEBSOCKET_ACCEPT_UUID).digest()
- accept = base64.b64encode(accept_binary)
-
- return (accept, accept_binary)
-
-
-class Handshaker(object):
- """Opening handshake processor for the WebSocket protocol (RFC 6455)."""
-
- def __init__(self, request, dispatcher):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
-
- Handshaker will add attributes such as ws_resource during handshake.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._dispatcher = dispatcher
-
- def _validate_connection_header(self):
- connection = get_mandatory_header(
- self._request, common.CONNECTION_HEADER)
-
- try:
- connection_tokens = parse_token_list(connection)
- except HandshakeException, e:
- raise HandshakeException(
- 'Failed to parse %s: %s' % (common.CONNECTION_HEADER, e))
-
- connection_is_valid = False
- for token in connection_tokens:
- if token.lower() == common.UPGRADE_CONNECTION_TYPE.lower():
- connection_is_valid = True
- break
- if not connection_is_valid:
- raise HandshakeException(
- '%s header doesn\'t contain "%s"' %
- (common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
-
- def do_handshake(self):
- self._request.ws_close_code = None
- self._request.ws_close_reason = None
-
- # Parsing.
-
- check_request_line(self._request)
-
- validate_mandatory_header(
- self._request,
- common.UPGRADE_HEADER,
- common.WEBSOCKET_UPGRADE_TYPE)
-
- self._validate_connection_header()
-
- self._request.ws_resource = self._request.uri
-
- unused_host = get_mandatory_header(self._request, common.HOST_HEADER)
-
- self._request.ws_version = self._check_version()
-
- # This handshake must be based on latest hybi. We are responsible to
- # fallback to HTTP on handshake failure as latest hybi handshake
- # specifies.
- try:
- self._get_origin()
- self._set_protocol()
- self._parse_extensions()
-
- # Key validation, response generation.
-
- key = self._get_key()
- (accept, accept_binary) = compute_accept(key)
- self._logger.debug(
- '%s: %r (%s)',
- common.SEC_WEBSOCKET_ACCEPT_HEADER,
- accept,
- util.hexify(accept_binary))
-
- self._logger.debug('Protocol version is RFC 6455')
-
- # Setup extension processors.
-
- processors = []
- if self._request.ws_requested_extensions is not None:
- for extension_request in self._request.ws_requested_extensions:
- processor = get_extension_processor(extension_request)
- # Unknown extension requests are just ignored.
- if processor is not None:
- processors.append(processor)
- self._request.ws_extension_processors = processors
-
- # Extra handshake handler may modify/remove processors.
- self._dispatcher.do_extra_handshake(self._request)
- processors = filter(lambda processor: processor is not None,
- self._request.ws_extension_processors)
-
- accepted_extensions = []
-
- # We need to take care of mux extension here. Extensions that
- # are placed before mux should be applied to logical channels.
- mux_index = -1
- for i, processor in enumerate(processors):
- if processor.name() == common.MUX_EXTENSION:
- mux_index = i
- break
- if mux_index >= 0:
- mux_processor = processors[mux_index]
- logical_channel_processors = processors[:mux_index]
- processors = processors[mux_index+1:]
-
- for processor in logical_channel_processors:
- extension_response = processor.get_extension_response()
- if extension_response is None:
- # Rejected.
- continue
- accepted_extensions.append(extension_response)
- # Pass a shallow copy of accepted_extensions as extensions for
- # logical channels.
- mux_response = mux_processor.get_extension_response(
- self._request, accepted_extensions[:])
- if mux_response is not None:
- accepted_extensions.append(mux_response)
-
- stream_options = StreamOptions()
-
- # When there is mux extension, here, |processors| contain only
- # prosessors for extensions placed after mux.
- for processor in processors:
-
- extension_response = processor.get_extension_response()
- if extension_response is None:
- # Rejected.
- continue
-
- accepted_extensions.append(extension_response)
-
- processor.setup_stream_options(stream_options)
-
- if len(accepted_extensions) > 0:
- self._request.ws_extensions = accepted_extensions
- self._logger.debug(
- 'Extensions accepted: %r',
- map(common.ExtensionParameter.name, accepted_extensions))
- else:
- self._request.ws_extensions = None
-
- self._request.ws_stream = self._create_stream(stream_options)
-
- if self._request.ws_requested_protocols is not None:
- if self._request.ws_protocol is None:
- raise HandshakeException(
- 'do_extra_handshake must choose one subprotocol from '
- 'ws_requested_protocols and set it to ws_protocol')
- validate_subprotocol(self._request.ws_protocol, hixie=False)
-
- self._logger.debug(
- 'Subprotocol accepted: %r',
- self._request.ws_protocol)
- else:
- if self._request.ws_protocol is not None:
- raise HandshakeException(
- 'ws_protocol must be None when the client didn\'t '
- 'request any subprotocol')
-
- self._send_handshake(accept)
- except HandshakeException, e:
- if not e.status:
- # Fallback to 400 bad request by default.
- e.status = common.HTTP_STATUS_BAD_REQUEST
- raise e
-
- def _get_origin(self):
- if self._request.ws_version is _VERSION_HYBI08:
- origin_header = common.SEC_WEBSOCKET_ORIGIN_HEADER
- else:
- origin_header = common.ORIGIN_HEADER
- origin = self._request.headers_in.get(origin_header)
- if origin is None:
- self._logger.debug('Client request does not have origin header')
- self._request.ws_origin = origin
-
- def _check_version(self):
- version = get_mandatory_header(self._request,
- common.SEC_WEBSOCKET_VERSION_HEADER)
- if version == _VERSION_HYBI08_STRING:
- return _VERSION_HYBI08
- if version == _VERSION_LATEST_STRING:
- return _VERSION_LATEST
-
- if version.find(',') >= 0:
- raise HandshakeException(
- 'Multiple versions (%r) are not allowed for header %s' %
- (version, common.SEC_WEBSOCKET_VERSION_HEADER),
- status=common.HTTP_STATUS_BAD_REQUEST)
- raise VersionException(
- 'Unsupported version %r for header %s' %
- (version, common.SEC_WEBSOCKET_VERSION_HEADER),
- supported_versions=', '.join(map(str, _SUPPORTED_VERSIONS)))
-
- def _set_protocol(self):
- self._request.ws_protocol = None
-
- protocol_header = self._request.headers_in.get(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER)
-
- if protocol_header is None:
- self._request.ws_requested_protocols = None
- return
-
- self._request.ws_requested_protocols = parse_token_list(
- protocol_header)
- self._logger.debug('Subprotocols requested: %r',
- self._request.ws_requested_protocols)
-
- def _parse_extensions(self):
- extensions_header = self._request.headers_in.get(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER)
- if not extensions_header:
- self._request.ws_requested_extensions = None
- return
-
- if self._request.ws_version is common.VERSION_HYBI08:
- allow_quoted_string=False
- else:
- allow_quoted_string=True
- try:
- self._request.ws_requested_extensions = common.parse_extensions(
- extensions_header, allow_quoted_string=allow_quoted_string)
- except common.ExtensionParsingException, e:
- raise HandshakeException(
- 'Failed to parse Sec-WebSocket-Extensions header: %r' % e)
-
- self._logger.debug(
- 'Extensions requested: %r',
- map(common.ExtensionParameter.name,
- self._request.ws_requested_extensions))
-
- def _validate_key(self, key):
- if key.find(',') >= 0:
- raise HandshakeException('Request has multiple %s header lines or '
- 'contains illegal character \',\': %r' %
- (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
- # Validate
- key_is_valid = False
- try:
- # Validate key by quick regex match before parsing by base64
- # module. Because base64 module skips invalid characters, we have
- # to do this in advance to make this server strictly reject illegal
- # keys.
- if _SEC_WEBSOCKET_KEY_REGEX.match(key):
- decoded_key = base64.b64decode(key)
- if len(decoded_key) == 16:
- key_is_valid = True
- except TypeError, e:
- pass
-
- if not key_is_valid:
- raise HandshakeException(
- 'Illegal value for header %s: %r' %
- (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
- return decoded_key
-
- def _get_key(self):
- key = get_mandatory_header(
- self._request, common.SEC_WEBSOCKET_KEY_HEADER)
-
- decoded_key = self._validate_key(key)
-
- self._logger.debug(
- '%s: %r (%s)',
- common.SEC_WEBSOCKET_KEY_HEADER,
- key,
- util.hexify(decoded_key))
-
- return key
-
- def _create_stream(self, stream_options):
- return Stream(self._request, stream_options)
-
- def _create_handshake_response(self, accept):
- response = []
-
- response.append('HTTP/1.1 101 Switching Protocols\r\n')
-
- response.append(format_header(
- common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE))
- response.append(format_header(
- common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
- response.append(format_header(
- common.SEC_WEBSOCKET_ACCEPT_HEADER, accept))
- if self._request.ws_protocol is not None:
- response.append(format_header(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER,
- self._request.ws_protocol))
- if (self._request.ws_extensions is not None and
- len(self._request.ws_extensions) != 0):
- response.append(format_header(
- common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
- common.format_extensions(self._request.ws_extensions)))
- response.append('\r\n')
-
- return ''.join(response)
-
- def _send_handshake(self, accept):
- raw_response = self._create_handshake_response(accept)
- self._request.connection.write(raw_response)
- self._logger.debug('Sent server\'s opening handshake: %r',
- raw_response)
-
-
-# vi:sts=4 sw=4 et
diff --git a/module/lib/mod_pywebsocket/handshake/hybi00.py b/module/lib/mod_pywebsocket/handshake/hybi00.py
deleted file mode 100644
index cc6f8dc43..000000000
--- a/module/lib/mod_pywebsocket/handshake/hybi00.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""This file provides the opening handshake processor for the WebSocket
-protocol version HyBi 00.
-
-Specification:
-http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
-"""
-
-
-# Note: request.connection.write/read are used in this module, even though
-# mod_python document says that they should be used only in connection
-# handlers. Unfortunately, we have no other options. For example,
-# request.write/read are not suitable because they don't allow direct raw bytes
-# writing/reading.
-
-
-import logging
-import re
-import struct
-
-from mod_pywebsocket import common
-from mod_pywebsocket.stream import StreamHixie75
-from mod_pywebsocket import util
-from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import build_location
-from mod_pywebsocket.handshake._base import check_header_lines
-from mod_pywebsocket.handshake._base import format_header
-from mod_pywebsocket.handshake._base import get_mandatory_header
-from mod_pywebsocket.handshake._base import validate_subprotocol
-
-
-_MANDATORY_HEADERS = [
- # key, expected value or None
- [common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75],
- [common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE],
-]
-
-
-class Handshaker(object):
- """Opening handshake processor for the WebSocket protocol version HyBi 00.
- """
-
- def __init__(self, request, dispatcher):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
-
- Handshaker will add attributes such as ws_resource in performing
- handshake.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._dispatcher = dispatcher
-
- def do_handshake(self):
- """Perform WebSocket Handshake.
-
- On _request, we set
- ws_resource, ws_protocol, ws_location, ws_origin, ws_challenge,
- ws_challenge_md5: WebSocket handshake information.
- ws_stream: Frame generation/parsing class.
- ws_version: Protocol version.
-
- Raises:
- HandshakeException: when any error happened in parsing the opening
- handshake request.
- """
-
- # 5.1 Reading the client's opening handshake.
- # dispatcher sets it in self._request.
- check_header_lines(self._request, _MANDATORY_HEADERS)
- self._set_resource()
- self._set_subprotocol()
- self._set_location()
- self._set_origin()
- self._set_challenge_response()
- self._set_protocol_version()
-
- self._dispatcher.do_extra_handshake(self._request)
-
- self._send_handshake()
-
- def _set_resource(self):
- self._request.ws_resource = self._request.uri
-
- def _set_subprotocol(self):
- # |Sec-WebSocket-Protocol|
- subprotocol = self._request.headers_in.get(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER)
- if subprotocol is not None:
- validate_subprotocol(subprotocol, hixie=True)
- self._request.ws_protocol = subprotocol
-
- def _set_location(self):
- # |Host|
- host = self._request.headers_in.get(common.HOST_HEADER)
- if host is not None:
- self._request.ws_location = build_location(self._request)
- # TODO(ukai): check host is this host.
-
- def _set_origin(self):
- # |Origin|
- origin = self._request.headers_in.get(common.ORIGIN_HEADER)
- if origin is not None:
- self._request.ws_origin = origin
-
- def _set_protocol_version(self):
- # |Sec-WebSocket-Draft|
- draft = self._request.headers_in.get(common.SEC_WEBSOCKET_DRAFT_HEADER)
- if draft is not None and draft != '0':
- raise HandshakeException('Illegal value for %s: %s' %
- (common.SEC_WEBSOCKET_DRAFT_HEADER,
- draft))
-
- self._logger.debug('Protocol version is HyBi 00')
- self._request.ws_version = common.VERSION_HYBI00
- self._request.ws_stream = StreamHixie75(self._request, True)
-
- def _set_challenge_response(self):
- # 5.2 4-8.
- self._request.ws_challenge = self._get_challenge()
- # 5.2 9. let /response/ be the MD5 finterprint of /challenge/
- self._request.ws_challenge_md5 = util.md5_hash(
- self._request.ws_challenge).digest()
- self._logger.debug(
- 'Challenge: %r (%s)',
- self._request.ws_challenge,
- util.hexify(self._request.ws_challenge))
- self._logger.debug(
- 'Challenge response: %r (%s)',
- self._request.ws_challenge_md5,
- util.hexify(self._request.ws_challenge_md5))
-
- def _get_key_value(self, key_field):
- key_value = get_mandatory_header(self._request, key_field)
-
- self._logger.debug('%s: %r', key_field, key_value)
-
- # 5.2 4. let /key-number_n/ be the digits (characters in the range
- # U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_n/,
- # interpreted as a base ten integer, ignoring all other characters
- # in /key_n/.
- try:
- key_number = int(re.sub("\\D", "", key_value))
- except:
- raise HandshakeException('%s field contains no digit' % key_field)
- # 5.2 5. let /spaces_n/ be the number of U+0020 SPACE characters
- # in /key_n/.
- spaces = re.subn(" ", "", key_value)[1]
- if spaces == 0:
- raise HandshakeException('%s field contains no space' % key_field)
-
- self._logger.debug(
- '%s: Key-number is %d and number of spaces is %d',
- key_field, key_number, spaces)
-
- # 5.2 6. if /key-number_n/ is not an integral multiple of /spaces_n/
- # then abort the WebSocket connection.
- if key_number % spaces != 0:
- raise HandshakeException(
- '%s: Key-number (%d) is not an integral multiple of spaces '
- '(%d)' % (key_field, key_number, spaces))
- # 5.2 7. let /part_n/ be /key-number_n/ divided by /spaces_n/.
- part = key_number / spaces
- self._logger.debug('%s: Part is %d', key_field, part)
- return part
-
- def _get_challenge(self):
- # 5.2 4-7.
- key1 = self._get_key_value(common.SEC_WEBSOCKET_KEY1_HEADER)
- key2 = self._get_key_value(common.SEC_WEBSOCKET_KEY2_HEADER)
- # 5.2 8. let /challenge/ be the concatenation of /part_1/,
- challenge = ''
- challenge += struct.pack('!I', key1) # network byteorder int
- challenge += struct.pack('!I', key2) # network byteorder int
- challenge += self._request.connection.read(8)
- return challenge
-
- def _send_handshake(self):
- response = []
-
- # 5.2 10. send the following line.
- response.append('HTTP/1.1 101 WebSocket Protocol Handshake\r\n')
-
- # 5.2 11. send the following fields to the client.
- response.append(format_header(
- common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75))
- response.append(format_header(
- common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
- response.append(format_header(
- common.SEC_WEBSOCKET_LOCATION_HEADER, self._request.ws_location))
- response.append(format_header(
- common.SEC_WEBSOCKET_ORIGIN_HEADER, self._request.ws_origin))
- if self._request.ws_protocol:
- response.append(format_header(
- common.SEC_WEBSOCKET_PROTOCOL_HEADER,
- self._request.ws_protocol))
- # 5.2 12. send two bytes 0x0D 0x0A.
- response.append('\r\n')
- # 5.2 13. send /response/
- response.append(self._request.ws_challenge_md5)
-
- raw_response = ''.join(response)
- self._request.connection.write(raw_response)
- self._logger.debug('Sent server\'s opening handshake: %r',
- raw_response)
-
-
-# vi:sts=4 sw=4 et