forked from mirrors/gecko-dev
Update testing/mochitest/pywebsocket with the latest version available: pywebsocket3 is python 3 compatible. This keeps the basic structure of the old pywebsocket, but changes the directory name to pywebsocket3 to reflect the project renaming. Differential Revision: https://phabricator.services.mozilla.com/D84455
179 lines
6.1 KiB
Python
179 lines
6.1 KiB
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.
|
|
"""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
|
|
|
|
|
|
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 format_header(name, value):
|
|
return u'%s: %s\r\n' % (name, value)
|
|
|
|
|
|
def get_mandatory_header(request, key):
|
|
value = request.headers_in.get(key)
|
|
if value is None:
|
|
raise HandshakeException('Header %s is not defined' % key)
|
|
return value
|
|
|
|
|
|
def validate_mandatory_header(request, key, expected_value, fail_status=None):
|
|
value = get_mandatory_header(request, key)
|
|
|
|
if value.lower() != expected_value.lower():
|
|
raise HandshakeException(
|
|
'Expected %r for header %s but found %r (case-insensitive)' %
|
|
(expected_value, key, value),
|
|
status=fail_status)
|
|
|
|
|
|
def check_request_line(request):
|
|
# 5.1 1. The three character UTF-8 string "GET".
|
|
# 5.1 2. A UTF-8-encoded U+0020 SPACE character (0x20 byte).
|
|
if request.method != 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 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
|