bug 675038 - websockets wss:// tests r=biesi
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 03 Aug 2011 15:38:56 -0400
changeset 73797 3cca53b77c110c9c406c376e6be6ff2d9018fb07
parent 73796 e054dec99f3e9559772ec78a46b6cf957754d4f2
child 73798 c6912479afdd7c987d061c32a185f69e75a3c148
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersbiesi
bugs675038
milestone8.0a1
bug 675038 - websockets wss:// tests r=biesi
content/base/test/test_websocket.html
testing/mochitest/pywebsocket/README
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -56,20 +56,23 @@
  * 31. ctor using valid 2 element sub-protocol array with 1 element server
  *     will reject and one server will accept.
  * 32. ctor using invalid sub-protocol array that contains duplicate items
  * 33. default close code test
  * 34. test for receiving custom close code and reason
  * 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;
 
 var all_ws = [];
 
 function shouldNotOpen(e)
 {
   var ws = e.target;
@@ -1051,16 +1054,68 @@ function test37()
          ok(e.code != 3101, "test 37c custom server code not present");
          ok(e.reason == "", "test 37c custom server reason not present");
          doTest(38);  
       }
     }
   }
 }
 
+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()
 {
   if (!ranAllTests)
     return;
 
   if (waitTest2Part1 || waitTest2Part2 || waitTest9 || waitTest10 ||
--- a/testing/mochitest/pywebsocket/README
+++ b/testing/mochitest/pywebsocket/README
@@ -6,16 +6,19 @@ includes the following minor patch:: (fi
 files, the second allows python 2.5 to work)
 
 also includes patch for 663096 to drain input buffers before closing
 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
 @@ -60,17 +60,18 @@ def _normalize_path(path):
          path: the path to normalize.
  
      Path is converted to the absolute path.
      The input path can use either '\\' or '/' as the separator.
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
@@ -155,28 +155,66 @@ def validate_mandatory_header(request, k
     value = get_mandatory_header(request, key)
 
     if value.lower() != expected_value.lower():
         raise HandshakeError(
             'Expected %r for header %s but found %r (case-insensitive)' %
             (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|
     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 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)
 
     if param_name is None:
         raise HandshakeError('No valid parameter name found')
 
     http_header_util.consume_lwses(state)
 
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
@@ -41,32 +41,27 @@ import base64
 import logging
 import os
 import re
 
 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]*=*$')
 
 
 def compute_accept(key):
     """Computes value for the Sec-WebSocket-Accept header from value of the
     Sec-WebSocket-Key header.
     """
 
@@ -91,17 +86,42 @@ class Handshaker(object):
         """
 
         self._logger = util.get_class_logger(self)
 
         self._request = request
         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)
 
         self._get_origin()
         self._check_version()
         self._set_protocol()
         self._set_extensions()