1 #!/usr/bin/env python 2 # 3 # Copyright 2012, Google Inc. 4 # All rights reserved. 5 # 6 # Redistribution and use in source and binary forms, with or without 7 # modification, are permitted provided that the following conditions are 8 # met: 9 # 10 # * Redistributions of source code must retain the above copyright 11 # notice, this list of conditions and the following disclaimer. 12 # * Redistributions in binary form must reproduce the above 13 # copyright notice, this list of conditions and the following disclaimer 14 # in the documentation and/or other materials provided with the 15 # distribution. 16 # * Neither the name of Google Inc. nor the names of its 17 # contributors may be used to endorse or promote products derived from 18 # this software without specific prior written permission. 19 # 20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32 33 """Tests for handshake module.""" 34 35 36 import unittest 37 38 import set_sys_path # Update sys.path to locate mod_pywebsocket module. 39 40 from mod_pywebsocket.common import ExtensionParameter 41 from mod_pywebsocket.common import ExtensionParsingException 42 from mod_pywebsocket.common import format_extensions 43 from mod_pywebsocket.common import parse_extensions 44 from mod_pywebsocket.handshake._base import HandshakeException 45 from mod_pywebsocket.handshake._base import validate_subprotocol 46 47 48 class HandshakerTest(unittest.TestCase): 49 """A unittest for handshake module.""" 50 51 def test_validate_subprotocol(self): 52 # should succeed. 53 validate_subprotocol('sample', hixie=True) 54 validate_subprotocol('Sample', hixie=True) 55 validate_subprotocol('sample\x7eprotocol', hixie=True) 56 validate_subprotocol('sample\x20protocol', hixie=True) 57 validate_subprotocol('sample', hixie=False) 58 validate_subprotocol('Sample', hixie=False) 59 validate_subprotocol('sample\x7eprotocol', hixie=False) 60 61 # should fail. 62 self.assertRaises(HandshakeException, 63 validate_subprotocol, 64 '', 65 hixie=True) 66 self.assertRaises(HandshakeException, 67 validate_subprotocol, 68 'sample\x19protocol', 69 hixie=True) 70 self.assertRaises(HandshakeException, 71 validate_subprotocol, 72 'sample\x7fprotocol', 73 hixie=True) 74 self.assertRaises(HandshakeException, 75 validate_subprotocol, 76 # "Japan" in Japanese 77 u'\u65e5\u672c', 78 hixie=True) 79 self.assertRaises(HandshakeException, 80 validate_subprotocol, 81 '', 82 hixie=False) 83 self.assertRaises(HandshakeException, 84 validate_subprotocol, 85 'sample\x09protocol', 86 hixie=False) 87 self.assertRaises(HandshakeException, 88 validate_subprotocol, 89 'sample\x19protocol', 90 hixie=False) 91 self.assertRaises(HandshakeException, 92 validate_subprotocol, 93 'sample\x20protocol', 94 hixie=False) 95 self.assertRaises(HandshakeException, 96 validate_subprotocol, 97 'sample\x7fprotocol', 98 hixie=False) 99 self.assertRaises(HandshakeException, 100 validate_subprotocol, 101 # "Japan" in Japanese 102 u'\u65e5\u672c', 103 hixie=False) 104 105 106 _TEST_TOKEN_EXTENSION_DATA = [ 107 ('foo', [('foo', [])]), 108 ('foo; bar', [('foo', [('bar', None)])]), 109 ('foo; bar=baz', [('foo', [('bar', 'baz')])]), 110 ('foo; bar=baz; car=cdr', [('foo', [('bar', 'baz'), ('car', 'cdr')])]), 111 ('foo; bar=baz, car; cdr', 112 [('foo', [('bar', 'baz')]), ('car', [('cdr', None)])]), 113 ('a, b, c, d', 114 [('a', []), ('b', []), ('c', []), ('d', [])]), 115 ] 116 117 118 _TEST_QUOTED_EXTENSION_DATA = [ 119 ('foo; bar=""', [('foo', [('bar', '')])]), 120 ('foo; bar=" baz "', [('foo', [('bar', ' baz ')])]), 121 ('foo; bar=",baz;"', [('foo', [('bar', ',baz;')])]), 122 ('foo; bar="\\\r\\\nbaz"', [('foo', [('bar', '\r\nbaz')])]), 123 ('foo; bar="\\"baz"', [('foo', [('bar', '"baz')])]), 124 ('foo; bar="\xbbbaz"', [('foo', [('bar', '\xbbbaz')])]), 125 ] 126 127 128 _TEST_REDUNDANT_TOKEN_EXTENSION_DATA = [ 129 ('foo \t ', [('foo', [])]), 130 ('foo; \r\n bar', [('foo', [('bar', None)])]), 131 ('foo; bar=\r\n \r\n baz', [('foo', [('bar', 'baz')])]), 132 ('foo ;bar = baz ', [('foo', [('bar', 'baz')])]), 133 ('foo,bar,,baz', [('foo', []), ('bar', []), ('baz', [])]), 134 ] 135 136 137 _TEST_REDUNDANT_QUOTED_EXTENSION_DATA = [ 138 ('foo; bar="\r\n \r\n baz"', [('foo', [('bar', ' baz')])]), 139 ] 140 141 142 class ExtensionsParserTest(unittest.TestCase): 143 144 def _verify_extension_list(self, expected_list, actual_list): 145 """Verifies that ExtensionParameter objects in actual_list have the 146 same members as extension definitions in expected_list. Extension 147 definition used in this test is a pair of an extension name and a 148 parameter dictionary. 149 """ 150 151 self.assertEqual(len(expected_list), len(actual_list)) 152 for expected, actual in zip(expected_list, actual_list): 153 (name, parameters) = expected 154 self.assertEqual(name, actual._name) 155 self.assertEqual(parameters, actual._parameters) 156 157 def test_parse(self): 158 for formatted_string, definition in _TEST_TOKEN_EXTENSION_DATA: 159 self._verify_extension_list( 160 definition, parse_extensions(formatted_string, 161 allow_quoted_string=False)) 162 163 for formatted_string, unused_definition in _TEST_QUOTED_EXTENSION_DATA: 164 self.assertRaises( 165 ExtensionParsingException, parse_extensions, 166 formatted_string, False) 167 168 def test_parse_with_allow_quoted_string(self): 169 for formatted_string, definition in _TEST_TOKEN_EXTENSION_DATA: 170 self._verify_extension_list( 171 definition, parse_extensions(formatted_string, 172 allow_quoted_string=True)) 173 174 for formatted_string, definition in _TEST_QUOTED_EXTENSION_DATA: 175 self._verify_extension_list( 176 definition, parse_extensions(formatted_string, 177 allow_quoted_string=True)) 178 179 def test_parse_redundant_data(self): 180 for (formatted_string, 181 definition) in _TEST_REDUNDANT_TOKEN_EXTENSION_DATA: 182 self._verify_extension_list( 183 definition, parse_extensions(formatted_string, 184 allow_quoted_string=False)) 185 186 for (formatted_string, 187 definition) in _TEST_REDUNDANT_QUOTED_EXTENSION_DATA: 188 self.assertRaises( 189 ExtensionParsingException, parse_extensions, 190 formatted_string, False) 191 192 def test_parse_redundant_data_with_allow_quoted_string(self): 193 for (formatted_string, 194 definition) in _TEST_REDUNDANT_TOKEN_EXTENSION_DATA: 195 self._verify_extension_list( 196 definition, parse_extensions(formatted_string, 197 allow_quoted_string=True)) 198 199 for (formatted_string, 200 definition) in _TEST_REDUNDANT_QUOTED_EXTENSION_DATA: 201 self._verify_extension_list( 202 definition, parse_extensions(formatted_string, 203 allow_quoted_string=True)) 204 205 def test_parse_bad_data(self): 206 _TEST_BAD_EXTENSION_DATA = [ 207 ('foo; ; '), 208 ('foo; a a'), 209 ('foo foo'), 210 (',,,'), 211 ('foo; bar='), 212 ('foo; bar="hoge'), 213 ('foo; bar="a\r"'), 214 ('foo; bar="\\\xff"'), 215 ('foo; bar=\ra'), 216 ] 217 218 for formatted_string in _TEST_BAD_EXTENSION_DATA: 219 self.assertRaises( 220 ExtensionParsingException, parse_extensions, formatted_string) 221 222 223 class FormatExtensionsTest(unittest.TestCase): 224 225 def test_format_extensions(self): 226 for formatted_string, definitions in _TEST_TOKEN_EXTENSION_DATA: 227 extensions = [] 228 for definition in definitions: 229 (name, parameters) = definition 230 extension = ExtensionParameter(name) 231 extension._parameters = parameters 232 extensions.append(extension) 233 self.assertEqual( 234 formatted_string, format_extensions(extensions)) 235 236 237 if __name__ == '__main__': 238 unittest.main() 239 240 241 # vi:sts=4 sw=4 et 242