summaryrefslogtreecommitdiffstats
path: root/module/lib/mod_pywebsocket/handshake
diff options
context:
space:
mode:
authorGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2013-03-11 21:09:59 +0100
committerGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2013-03-11 21:09:59 +0100
commit5e27b8b8ff3e7f4c3f2b8ebe525b716f1c211d12 (patch)
treeacdca4b00fc1a29dc8fec823f5b45cb80cf719b0 /module/lib/mod_pywebsocket/handshake
parentbetter time formatting (diff)
downloadpyload-5e27b8b8ff3e7f4c3f2b8ebe525b716f1c211d12.tar.xz
updated pywebsocket
Diffstat (limited to 'module/lib/mod_pywebsocket/handshake')
-rw-r--r--module/lib/mod_pywebsocket/handshake/__init__.py10
-rw-r--r--module/lib/mod_pywebsocket/handshake/_base.py15
-rw-r--r--module/lib/mod_pywebsocket/handshake/draft75.py190
-rw-r--r--module/lib/mod_pywebsocket/handshake/hybi.py64
4 files changed, 61 insertions, 218 deletions
diff --git a/module/lib/mod_pywebsocket/handshake/__init__.py b/module/lib/mod_pywebsocket/handshake/__init__.py
index 10a178314..194f6b395 100644
--- a/module/lib/mod_pywebsocket/handshake/__init__.py
+++ b/module/lib/mod_pywebsocket/handshake/__init__.py
@@ -37,7 +37,6 @@ successfully established.
import logging
from mod_pywebsocket import common
-from mod_pywebsocket.handshake import draft75
from mod_pywebsocket.handshake import hybi00
from mod_pywebsocket.handshake import hybi
# Export AbortedByUserException, HandshakeException, and VersionException
@@ -56,10 +55,8 @@ def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
Args:
request: mod_python request.
dispatcher: Dispatcher (dispatch.Dispatcher).
- allowDraft75: allow draft 75 handshake protocol.
- strict: Strictly check handshake request in draft 75.
- Default: False. If True, request.connection must provide
- get_memorized_lines method.
+ allowDraft75: obsolete argument. ignored.
+ strict: obsolete argument. ignored.
Handshaker will add attributes such as ws_resource in performing
handshake.
@@ -86,9 +83,6 @@ def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
('RFC 6455', hybi.Handshaker(request, dispatcher)))
handshakers.append(
('HyBi 00', hybi00.Handshaker(request, dispatcher)))
- if allowDraft75:
- handshakers.append(
- ('Hixie 75', draft75.Handshaker(request, dispatcher, strict)))
for name, handshaker in handshakers:
_LOGGER.debug('Trying protocol version %s', name)
diff --git a/module/lib/mod_pywebsocket/handshake/_base.py b/module/lib/mod_pywebsocket/handshake/_base.py
index bc095b129..e5c94ca90 100644
--- a/module/lib/mod_pywebsocket/handshake/_base.py
+++ b/module/lib/mod_pywebsocket/handshake/_base.py
@@ -85,13 +85,16 @@ def get_default_port(is_secure):
def validate_subprotocol(subprotocol, hixie):
- """Validate a value in subprotocol fields such as WebSocket-Protocol,
- Sec-WebSocket-Protocol.
+ """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
- - Hixie 75: Section 4.1. 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:
@@ -170,7 +173,11 @@ 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')
+ 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):
diff --git a/module/lib/mod_pywebsocket/handshake/draft75.py b/module/lib/mod_pywebsocket/handshake/draft75.py
deleted file mode 100644
index 802a31c9a..000000000
--- a/module/lib/mod_pywebsocket/handshake/draft75.py
+++ /dev/null
@@ -1,190 +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 handshaking defined in draft-hixie-thewebsocketprotocol-75."""
-
-
-# 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 logging
-import re
-
-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 validate_subprotocol
-
-
-_MANDATORY_HEADERS = [
- # key, expected value or None
- ['Upgrade', 'WebSocket'],
- ['Connection', 'Upgrade'],
- ['Host', None],
- ['Origin', None],
-]
-
-_FIRST_FIVE_LINES = map(re.compile, [
- r'^GET /[\S]* HTTP/1.1\r\n$',
- r'^Upgrade: WebSocket\r\n$',
- r'^Connection: Upgrade\r\n$',
- r'^Host: [\S]+\r\n$',
- r'^Origin: [\S]+\r\n$',
-])
-
-_SIXTH_AND_LATER = re.compile(
- r'^'
- r'(WebSocket-Protocol: [\x20-\x7e]+\r\n)?'
- r'(Cookie: [^\r]*\r\n)*'
- r'(Cookie2: [^\r]*\r\n)?'
- r'(Cookie: [^\r]*\r\n)*'
- r'\r\n')
-
-
-class Handshaker(object):
- """This class performs WebSocket handshake."""
-
- def __init__(self, request, dispatcher, strict=False):
- """Construct an instance.
-
- Args:
- request: mod_python request.
- dispatcher: Dispatcher (dispatch.Dispatcher).
- strict: Strictly check handshake request. Default: False.
- If True, request.connection must provide get_memorized_lines
- method.
-
- Handshaker will add attributes such as ws_resource in performing
- handshake.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._request = request
- self._dispatcher = dispatcher
- self._strict = strict
-
- def do_handshake(self):
- """Perform WebSocket Handshake.
-
- On _request, we set
- ws_resource, ws_origin, ws_location, ws_protocol
- ws_challenge_md5: WebSocket handshake information.
- ws_stream: Frame generation/parsing class.
- ws_version: Protocol version.
- """
-
- self._check_header_lines()
- self._set_resource()
- self._set_origin()
- self._set_location()
- self._set_subprotocol()
- self._set_protocol_version()
-
- self._dispatcher.do_extra_handshake(self._request)
-
- self._send_handshake()
-
- self._logger.debug('Sent opening handshake response')
-
- def _set_resource(self):
- self._request.ws_resource = self._request.uri
-
- def _set_origin(self):
- self._request.ws_origin = self._request.headers_in['Origin']
-
- def _set_location(self):
- self._request.ws_location = build_location(self._request)
-
- def _set_subprotocol(self):
- subprotocol = self._request.headers_in.get('WebSocket-Protocol')
- if subprotocol is not None:
- validate_subprotocol(subprotocol, hixie=True)
- self._request.ws_protocol = subprotocol
-
- def _set_protocol_version(self):
- self._logger.debug('IETF Hixie 75 protocol')
- self._request.ws_version = common.VERSION_HIXIE75
- self._request.ws_stream = StreamHixie75(self._request)
-
- def _sendall(self, data):
- self._request.connection.write(data)
-
- def _send_handshake(self):
- self._sendall('HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
- self._sendall('Upgrade: WebSocket\r\n')
- self._sendall('Connection: Upgrade\r\n')
- self._sendall('WebSocket-Origin: %s\r\n' % self._request.ws_origin)
- self._sendall('WebSocket-Location: %s\r\n' % self._request.ws_location)
- if self._request.ws_protocol:
- self._sendall(
- 'WebSocket-Protocol: %s\r\n' % self._request.ws_protocol)
- self._sendall('\r\n')
-
- def _check_header_lines(self):
- for key, expected_value in _MANDATORY_HEADERS:
- actual_value = self._request.headers_in.get(key)
- if not actual_value:
- raise HandshakeException('Header %s is not defined' % key)
- if expected_value:
- if actual_value != expected_value:
- raise HandshakeException(
- 'Expected %r for header %s but found %r' %
- (expected_value, key, actual_value))
- if self._strict:
- try:
- lines = self._request.connection.get_memorized_lines()
- except AttributeError, e:
- raise AttributeError(
- 'Strict handshake is specified but the connection '
- 'doesn\'t provide get_memorized_lines()')
- self._check_first_lines(lines)
-
- def _check_first_lines(self, lines):
- if len(lines) < len(_FIRST_FIVE_LINES):
- raise HandshakeException('Too few header lines: %d' % len(lines))
- for line, regexp in zip(lines, _FIRST_FIVE_LINES):
- if not regexp.search(line):
- raise HandshakeException(
- 'Unexpected header: %r doesn\'t match %r'
- % (line, regexp.pattern))
- sixth_and_later = ''.join(lines[5:])
- if not _SIXTH_AND_LATER.search(sixth_and_later):
- raise HandshakeException(
- 'Unexpected header: %r doesn\'t match %r'
- % (sixth_and_later, _SIXTH_AND_LATER.pattern))
-
-
-# vi:sts=4 sw=4 et
diff --git a/module/lib/mod_pywebsocket/handshake/hybi.py b/module/lib/mod_pywebsocket/handshake/hybi.py
index 2883acbf8..fc0e2a096 100644
--- a/module/lib/mod_pywebsocket/handshake/hybi.py
+++ b/module/lib/mod_pywebsocket/handshake/hybi.py
@@ -182,34 +182,60 @@ class Handshaker(object):
# 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()
- self._request.ws_extensions = None
- for processor in self._request.ws_extension_processors:
- if processor is None:
- # Some processors may be removed by extra handshake
- # handler.
- continue
+ # 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
- if self._request.ws_extensions is None:
- self._request.ws_extensions = []
- self._request.ws_extensions.append(extension_response)
+ accepted_extensions.append(extension_response)
processor.setup_stream_options(stream_options)
- if self._request.ws_extensions is not None:
+ if len(accepted_extensions) > 0:
+ self._request.ws_extensions = accepted_extensions
self._logger.debug(
'Extensions accepted: %r',
- map(common.ExtensionParameter.name,
- self._request.ws_extensions))
+ map(common.ExtensionParameter.name, accepted_extensions))
+ else:
+ self._request.ws_extensions = None
- self._request.ws_stream = Stream(self._request, stream_options)
+ 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:
@@ -268,7 +294,7 @@ class Handshaker(object):
protocol_header = self._request.headers_in.get(
common.SEC_WEBSOCKET_PROTOCOL_HEADER)
- if not protocol_header:
+ if protocol_header is None:
self._request.ws_requested_protocols = None
return
@@ -341,7 +367,10 @@ class Handshaker(object):
return key
- def _send_handshake(self, accept):
+ 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')
@@ -363,7 +392,10 @@ class Handshaker(object):
common.format_extensions(self._request.ws_extensions)))
response.append('\r\n')
- raw_response = ''.join(response)
+ 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)