From fca47cdeb4621588bd0c73f28d9751a2c257cab6 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Wed, 3 Aug 2011 15:38:56 -0400 Subject: [PATCH] bug 675038 - websockets wss:// tests r=biesi --HG-- extra : rebase_source : 0d1c6c22d20124d7456eea91a2263eec9100517a --- content/base/test/test_websocket.html | 57 ++++++++++++++++++- testing/mochitest/pywebsocket/README | 3 + .../mod_pywebsocket/handshake/_base.py | 40 ++++++++++++- .../mod_pywebsocket/handshake/hybi06.py | 36 +++++++++--- 4 files changed, 126 insertions(+), 10 deletions(-) diff --git a/content/base/test/test_websocket.html b/content/base/test/test_websocket.html index 458c585a4cf7..a0a25371100a 100644 --- a/content/base/test/test_websocket.html +++ b/content/base/test/test_websocket.html @@ -61,10 +61,13 @@ * 35. test for sending custom close code and reason * 36. negative test for sending out of range close code * 37. negative test for too long of a close reason + * 38. reserved for extension test + * 39. a basic wss:// connectivity test + * 40. negative test for wss:// with no cert */ var first_test = 1; -var last_test = 37; +var last_test = 40; var current_test = first_test; @@ -1056,6 +1059,58 @@ function test37() } } +function test38() +{ + ok(true, "test 38 reserved for extension test."); + current_test++; + doTest(39); +} + +function test39() +{ + var prots=["test-39"]; + + var ws = CreateTestWS("wss://example.com/tests/content/base/test/file_websocket", prots); + status_test39 = "started"; + ws.onopen = function(e) + { + status_test39 = "opened"; + ok(true, "test 39 open"); + ws.close(); + }; + + ws.onclose = function(e) + { + ok(true, "test 39 close"); + ok(status_test39 == "opened", "test 39 did open"); + doTest(40); + }; +} + +function test40() +{ + var prots=["test-40"]; + + var ws = CreateTestWS("wss://nocert.example.com/tests/content/base/test/file_websocket", prots); + + status_test40 = "started"; + ws.onerror = ignoreError; + + ws.onopen = function(e) + { + status_test40 = "opened"; + ok(false, "test 40 open"); + ws.close(); + }; + + ws.onclose = function(e) + { + ok(true, "test 40 close"); + ok(status_test40 == "started", "test 40 did not open"); + doTest(41); + }; +} + var ranAllTests = false; function maybeFinished() diff --git a/testing/mochitest/pywebsocket/README b/testing/mochitest/pywebsocket/README index 5771243d0073..3f18f6220f51 100644 --- a/testing/mochitest/pywebsocket/README +++ b/testing/mochitest/pywebsocket/README @@ -11,6 +11,9 @@ in order to avoid RST also updates blindly version 7 to be version 8 until upstream makes real version 8 available +also includes changeset 491 from mod_pywebsocket repo - necessary to +enable wss:// testing + diff --git a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py --- a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py diff --git a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py index 7a668c7501ce..e9b99615969a 100644 --- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py @@ -160,11 +160,16 @@ def validate_mandatory_header(request, key, expected_value): (expected_value, key, value)) -def check_header_lines(request, mandatory_headers): +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 HandshakeError('Method is not GET') + + +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| @@ -172,6 +177,39 @@ def check_header_lines(request, 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 HandshakeError( + 'Expected a comma but found %r' % http_header_util.peek(state)) + + http_header_util.consume_lwses(state) + + if len(token_list) == 0: + raise HandshakeError('No valid token found') + + return token_list + + def _parse_extension_param(state, definition): param_name = http_header_util.consume_token(state) diff --git a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py index 7cdd78f5a545..cd7ba6a5065f 100644 --- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py @@ -46,22 +46,17 @@ from mod_pywebsocket import common from mod_pywebsocket.stream import Stream from mod_pywebsocket.stream import StreamOptions from mod_pywebsocket import util -from mod_pywebsocket.handshake._base import check_header_lines +from mod_pywebsocket.handshake._base import check_request_line from mod_pywebsocket.handshake._base import Extension from mod_pywebsocket.handshake._base import format_extensions from mod_pywebsocket.handshake._base import format_header from mod_pywebsocket.handshake._base import get_mandatory_header from mod_pywebsocket.handshake._base import HandshakeError from mod_pywebsocket.handshake._base import parse_extensions +from mod_pywebsocket.handshake._base import parse_token_list from mod_pywebsocket.handshake._base import validate_mandatory_header -_MANDATORY_HEADERS = [ - # key, expected value or None - [common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE], - [common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE], -] - _BASE64_REGEX = re.compile('^[+/0-9A-Za-z]*=*$') @@ -96,7 +91,32 @@ class Handshaker(object): self._dispatcher = dispatcher def do_handshake(self): - check_header_lines(self._request, _MANDATORY_HEADERS) + check_request_line(self._request) + + validate_mandatory_header( + self._request, + common.UPGRADE_HEADER, + common.WEBSOCKET_UPGRADE_TYPE) + + connection = get_mandatory_header( + self._request, common.CONNECTION_HEADER) + + try: + connection_tokens = parse_token_list(connection) + except HandshakeError, e: + raise HandshakeError( + '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 HandshakeError( + '%s header doesn\'t contain "%s"' % + (common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) + self._request.ws_resource = self._request.uri unused_host = get_mandatory_header(self._request, common.HOST_HEADER)