diff options
author | RaNaN <Mast3rRaNaN@hotmail.de> | 2013-12-15 14:36:38 +0100 |
---|---|---|
committer | RaNaN <Mast3rRaNaN@hotmail.de> | 2013-12-15 14:36:38 +0100 |
commit | 367172406d1382f2fdd39936bde31ebcbcd1c56f (patch) | |
tree | 6abd7b2adffdd7fbb4eb35a9171c25b0e8eee4a0 /pyload/lib/mod_pywebsocket/handshake | |
parent | more options to get webUI through proxy working (diff) | |
download | pyload-367172406d1382f2fdd39936bde31ebcbcd1c56f.tar.xz |
updated pywebsocket
Diffstat (limited to 'pyload/lib/mod_pywebsocket/handshake')
-rw-r--r-- | pyload/lib/mod_pywebsocket/handshake/_base.py | 72 | ||||
-rw-r--r-- | pyload/lib/mod_pywebsocket/handshake/hybi.py | 64 | ||||
-rw-r--r-- | pyload/lib/mod_pywebsocket/handshake/hybi00.py | 63 |
3 files changed, 113 insertions, 86 deletions
diff --git a/pyload/lib/mod_pywebsocket/handshake/_base.py b/pyload/lib/mod_pywebsocket/handshake/_base.py index e5c94ca90..c993a584b 100644 --- a/pyload/lib/mod_pywebsocket/handshake/_base.py +++ b/pyload/lib/mod_pywebsocket/handshake/_base.py @@ -84,42 +84,29 @@ def get_default_port(is_secure): return common.DEFAULT_WEB_SOCKET_PORT -def validate_subprotocol(subprotocol, hixie): +def validate_subprotocol(subprotocol): """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. + See the Section 4.1., 4.2.2., and 4.3. of 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) + + # 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) + fields = request.headers_in[common.HOST_HEADER].split(':', 1) if len(fields) == 1: return fields[0], get_default_port(request.is_https()) try: @@ -132,27 +119,6 @@ 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: @@ -180,16 +146,6 @@ def check_request_line(request): 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. diff --git a/pyload/lib/mod_pywebsocket/handshake/hybi.py b/pyload/lib/mod_pywebsocket/handshake/hybi.py index fc0e2a096..669097d77 100644 --- a/pyload/lib/mod_pywebsocket/handshake/hybi.py +++ b/pyload/lib/mod_pywebsocket/handshake/hybi.py @@ -48,6 +48,7 @@ import os import re from mod_pywebsocket import common +from mod_pywebsocket import deflate_stream_extension 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 @@ -180,44 +181,57 @@ class Handshaker(object): processors.append(processor) self._request.ws_extension_processors = processors + # 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) processors = filter(lambda processor: processor is not None, self._request.ws_extension_processors) + # 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 = filter(lambda processor: processor.is_active(), + processors) + accepted_extensions = [] - # We need to take care of mux extension here. Extensions that - # are placed before mux should be applied to logical channels. + # We need to take into account of mux extension here. + # If mux extension exists: + # - Remove processors of extensions for logical channel, + # which are processors located before the mux processor + # - Pass extension requests for logical channel to mux processor + # - Attach the mux processor to the request. It will be referred + # by dispatcher to see whether the dispatcher should use mux + # handler or not. 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) + logical_channel_extensions = [] + for processor in processors[:mux_index]: + logical_channel_extensions.append(processor.request()) + processor.set_active(False) + self._request.mux_processor = processors[mux_index] + self._request.mux_processor.set_extensions( + logical_channel_extensions) + processors = filter(lambda processor: processor.is_active(), + processors) 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. @@ -242,7 +256,7 @@ class Handshaker(object): 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) + validate_subprotocol(self._request.ws_protocol) self._logger.debug( 'Subprotocol accepted: %r', @@ -375,6 +389,7 @@ class Handshaker(object): response.append('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( @@ -390,6 +405,11 @@ class Handshaker(object): 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('\r\n') return ''.join(response) diff --git a/pyload/lib/mod_pywebsocket/handshake/hybi00.py b/pyload/lib/mod_pywebsocket/handshake/hybi00.py index cc6f8dc43..8757717a6 100644 --- a/pyload/lib/mod_pywebsocket/handshake/hybi00.py +++ b/pyload/lib/mod_pywebsocket/handshake/hybi00.py @@ -51,11 +51,12 @@ 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 check_request_line from mod_pywebsocket.handshake._base import format_header +from mod_pywebsocket.handshake._base import get_default_port from mod_pywebsocket.handshake._base import get_mandatory_header -from mod_pywebsocket.handshake._base import validate_subprotocol +from mod_pywebsocket.handshake._base import parse_host_header +from mod_pywebsocket.handshake._base import validate_mandatory_header _MANDATORY_HEADERS = [ @@ -65,6 +66,56 @@ _MANDATORY_HEADERS = [ ] +def _validate_subprotocol(subprotocol): + """Checks if characters in subprotocol are in range between U+0020 and + U+007E. A value in the Sec-WebSocket-Protocol field need to satisfy this + requirement. + + See the Section 4.1. Opening handshake of the spec. + """ + + if not subprotocol: + raise HandshakeException('Invalid subprotocol name: empty') + + # 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) + + +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 _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.unparsed_uri) + return ''.join(location_parts) + + class Handshaker(object): """Opening handshake processor for the WebSocket protocol version HyBi 00. """ @@ -101,7 +152,7 @@ class Handshaker(object): # 5.1 Reading the client's opening handshake. # dispatcher sets it in self._request. - check_header_lines(self._request, _MANDATORY_HEADERS) + _check_header_lines(self._request, _MANDATORY_HEADERS) self._set_resource() self._set_subprotocol() self._set_location() @@ -121,14 +172,14 @@ class Handshaker(object): subprotocol = self._request.headers_in.get( common.SEC_WEBSOCKET_PROTOCOL_HEADER) if subprotocol is not None: - validate_subprotocol(subprotocol, hixie=True) + _validate_subprotocol(subprotocol) 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) + self._request.ws_location = _build_location(self._request) # TODO(ukai): check host is this host. def _set_origin(self): |