Changeset 52296 in webkit
- Timestamp:
- Dec 17, 2009 6:38:01 PM (14 years ago)
- Location:
- trunk/WebKitTools
- Files:
-
- 6 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebKitTools/ChangeLog
r52295 r52296 1 2009-12-17 Yuzo Fujishima <yuzo@google.com> 2 3 Reviewed by Alexey Proskuryakov. 4 5 Update pywebsocket to 0.4.5 and make handshake checking stricter 6 https://bugs.webkit.org/show_bug.cgi?id=32249 7 8 * Scripts/run-webkit-tests: 9 * pywebsocket/mod_pywebsocket/handshake.py: 10 * pywebsocket/mod_pywebsocket/memorizingfile.py: Added. 11 * pywebsocket/mod_pywebsocket/standalone.py: 12 * pywebsocket/setup.py: 13 * pywebsocket/test/test_handshake.py: 14 * pywebsocket/test/test_memorizingfile.py: Added. 15 1 16 2009-12-17 Eric Seidel <eric@webkit.org> 2 17 -
trunk/WebKitTools/Scripts/run-webkit-tests
r51550 r52296 1456 1456 "-s", "$webSocketHandlerScanDir", 1457 1457 "-l", "$logFile", 1458 "--strict", 1458 1459 ); 1459 1460 # wss is disabled until all platforms support pyOpenSSL. -
trunk/WebKitTools/pywebsocket/mod_pywebsocket/handshake.py
r49672 r52296 40 40 import re 41 41 42 import util 43 42 44 43 45 _DEFAULT_WEB_SOCKET_PORT = 80 … … 45 47 _WEB_SOCKET_SCHEME = 'ws' 46 48 _WEB_SOCKET_SECURE_SCHEME = 'wss' 47 48 _METHOD_LINE = re.compile(r'^GET ([^ ]+) HTTP/1.1\r\n$')49 49 50 50 _MANDATORY_HEADERS = [ … … 56 56 ] 57 57 58 _FIRST_FIVE_LINES = map(re.compile, [ 59 r'^GET /[\S]+ HTTP/1.1\r\n$', 60 r'^Upgrade: WebSocket\r\n$', 61 r'^Connection: Upgrade\r\n$', 62 r'^Host: [\S]+\r\n$', 63 r'^Origin: [\S]+\r\n$', 64 ]) 65 66 # FIXME: Cookie headers also being in restricted WebSocket syntax. 67 _SIXTH_AND_LATER = re.compile( 68 r'^' 69 r'(WebSocket-Protocol: [\x20-\x7e]+\r\n)?' 70 r'([Cc][Oo][Oo][Kk][Ii][Ee]:[^\r]*\r\n)*' 71 r'([Cc][Oo][Oo][Kk][Ii][Ee]2:[^\r]*\r\n)?' 72 r'([Cc][Oo][Oo][Kk][Ii][Ee]:[^\r]*\r\n)*' 73 r'\r\n') 74 75 58 76 59 77 def _default_port(is_secure): … … 76 94 raise HandshakeError('Invalid WebSocket-Protocol: empty') 77 95 for c in protocol: 78 if not 0x2 1<= ord(c) <= 0x7e:96 if not 0x20 <= ord(c) <= 0x7e: 79 97 raise HandshakeError('Illegal character in protocol: %r' % c) 80 98 … … 83 101 """This class performs Web Socket handshake.""" 84 102 85 def __init__(self, request, dispatcher ):103 def __init__(self, request, dispatcher, strict=False): 86 104 """Construct an instance. 87 105 … … 89 107 request: mod_python request. 90 108 dispatcher: Dispatcher (dispatch.Dispatcher). 109 strict: Strictly check handshake request. Default: False. 110 If True, request.connection must provide get_memorized_lines 111 method. 91 112 92 113 Handshaker will add attributes such as ws_resource in performing … … 96 117 self._request = request 97 118 self._dispatcher = dispatcher 119 self._strict = strict 98 120 99 121 def do_handshake(self): … … 174 196 raise HandshakeError('Illegal value for header %s: %s' % 175 197 (key, actual_value)) 198 if self._strict: 199 try: 200 lines = self._request.connection.get_memorized_lines() 201 except AttributeError, e: 202 util.prepend_message_to_exception( 203 'Strict handshake is specified but the connection ' 204 'doesn\'t provide get_memorized_lines()', e) 205 raise 206 self._check_first_lines(lines) 207 208 def _check_first_lines(self, lines): 209 if len(lines) < len(_FIRST_FIVE_LINES): 210 raise HandshakeError('Too few header lines: %d' % len(lines)) 211 for line, regexp in zip(lines, _FIRST_FIVE_LINES): 212 if not regexp.search(line): 213 raise HandshakeError('Unexpected header: %r doesn\'t match %r' 214 % (line, regexp.pattern)) 215 sixth_and_later = ''.join(lines[5:]) 216 if not _SIXTH_AND_LATER.search(sixth_and_later): 217 raise HandshakeError('Unexpected header: %r doesn\'t match %r' 218 % (sixth_and_later, 219 _SIXTH_AND_LATER.pattern)) 176 220 177 221 -
trunk/WebKitTools/pywebsocket/mod_pywebsocket/memorizingfile.py
r52295 r52296 31 31 32 32 33 """Set up script for mod_pywebsocket. 33 """Memorizing file. 34 35 A memorizing file wraps a file and memorizes lines read by readline. 34 36 """ 35 37 36 38 37 from distutils.core import setup38 39 import sys 39 40 40 41 41 _PACKAGE_NAME = 'mod_pywebsocket' 42 class MemorizingFile(object): 43 """MemorizingFile wraps a file and memorizes lines read by readline. 42 44 43 if sys.version < '2.3': 44 print >>sys.stderr, '%s requires Python 2.3 or later.' % _PACKAGE_NAME 45 sys.exit(1) 45 Note that data read by other methods are not memorized. This behavior 46 is good enough for memorizing lines SimpleHTTPServer reads before 47 the control reaches WebSocketRequestHandler. 48 """ 49 def __init__(self, file_, max_memorized_lines=sys.maxint): 50 """Construct an instance. 46 51 47 setup(author='Yuzo Fujishima', 48 author_email='yuzo@chromium.org', 49 description='Web Socket extension for Apache HTTP Server.', 50 long_description=( 51 'mod_pywebsocket is an Apache HTTP Server extension for ' 52 'Web Socket (http://tools.ietf.org/html/' 53 'draft-hixie-thewebsocketprotocol). ' 54 'See mod_pywebsocket/__init__.py for more detail.'), 55 license='See COPYING', 56 name=_PACKAGE_NAME, 57 packages=[_PACKAGE_NAME], 58 url='http://code.google.com/p/pywebsocket/', 59 version='0.4.3', 60 ) 52 Args: 53 file_: the file object to wrap. 54 max_memorized_lines: the maximum number of lines to memorize. 55 Only the first max_memorized_lines are memorized. 56 Default: sys.maxint. 57 """ 58 self._file = file_ 59 self._memorized_lines = [] 60 self._max_memorized_lines = max_memorized_lines 61 62 def __getattribute__(self, name): 63 if name in ('_file', '_memorized_lines', '_max_memorized_lines', 64 'readline', 'get_memorized_lines'): 65 return object.__getattribute__(self, name) 66 return self._file.__getattribute__(name) 67 68 def readline(self): 69 """Override file.readline and memorize the line read.""" 70 71 line = self._file.readline() 72 if line and len(self._memorized_lines) < self._max_memorized_lines: 73 self._memorized_lines.append(line) 74 return line 75 76 def get_memorized_lines(self): 77 """Get lines memorized so far.""" 78 return self._memorized_lines 61 79 62 80 -
trunk/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py
r51661 r52296 76 76 import dispatch 77 77 import handshake 78 import memorizingfile 78 79 import util 79 80 … … 89 90 _DEFAULT_LOG_BACKUP_COUNT = 5 90 91 92 # 1024 is practically large enough to contain WebSocket handshake lines. 93 _MAX_MEMORIZED_LINES = 1024 91 94 92 95 def _print_warnings_if_any(dispatcher): … … 129 132 """Mimic mp_conn.read().""" 130 133 return self._request_handler.rfile.read(length) 134 135 def get_memorized_lines(self): 136 """Get memorized lines.""" 137 return self._request_handler.rfile.get_memorized_lines() 131 138 132 139 … … 199 206 200 207 self.connection = self.request 201 self.rfile = socket._fileobject(self.request, 'rb', self.rbufsize) 208 self.rfile = memorizingfile.MemorizingFile( 209 socket._fileobject(self.request, 'rb', self.rbufsize), 210 max_memorized_lines=_MAX_MEMORIZED_LINES) 202 211 self.wfile = socket._fileobject(self.request, 'wb', self.wbufsize) 203 212 … … 207 216 self._dispatcher = WebSocketRequestHandler.options.dispatcher 208 217 self._print_warnings_if_any() 209 self._handshaker = handshake.Handshaker(self._request, 210 self._dispatcher) 218 self._handshaker = handshake.Handshaker( 219 self._request, self._dispatcher, 220 WebSocketRequestHandler.options.strict) 211 221 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__( 212 222 self, *args, **keywords) … … 303 313 default=_DEFAULT_LOG_BACKUP_COUNT, 304 314 help='Log backup count') 315 parser.add_option('--strict', dest='strict', action='store_true', 316 default=False, help='Strictly check handshake request') 305 317 options = parser.parse_args()[0] 306 318 -
trunk/WebKitTools/pywebsocket/setup.py
r51661 r52296 57 57 packages=[_PACKAGE_NAME], 58 58 url='http://code.google.com/p/pywebsocket/', 59 version='0.4. 3',59 version='0.4.5', 60 60 ) 61 61 -
trunk/WebKitTools/pywebsocket/test/test_handshake.py
r49672 r52296 215 215 'Host':'example.com', 216 216 'Origin':'http://example.com', 217 'WebSocket-Protocol':'illegal protocol', 218 } 217 'WebSocket-Protocol':'illegal\x09protocol', 218 } 219 ), 220 ) 221 222 _STRICTLY_GOOD_REQUESTS = ( 223 ( 224 'GET /demo HTTP/1.1\r\n', 225 'Upgrade: WebSocket\r\n', 226 'Connection: Upgrade\r\n', 227 'Host: example.com\r\n', 228 'Origin: http://example.com\r\n', 229 '\r\n', 230 ), 231 ( # WebSocket-Protocol 232 'GET /demo HTTP/1.1\r\n', 233 'Upgrade: WebSocket\r\n', 234 'Connection: Upgrade\r\n', 235 'Host: example.com\r\n', 236 'Origin: http://example.com\r\n', 237 'WebSocket-Protocol: sample\r\n', 238 '\r\n', 239 ), 240 ( # WebSocket-Protocol and Cookie 241 'GET /demo HTTP/1.1\r\n', 242 'Upgrade: WebSocket\r\n', 243 'Connection: Upgrade\r\n', 244 'Host: example.com\r\n', 245 'Origin: http://example.com\r\n', 246 'WebSocket-Protocol: sample\r\n', 247 'Cookie: xyz\r\n' 248 '\r\n', 249 ), 250 ( # Cookie 251 'GET /demo HTTP/1.1\r\n', 252 'Upgrade: WebSocket\r\n', 253 'Connection: Upgrade\r\n', 254 'Host: example.com\r\n', 255 'Origin: http://example.com\r\n', 256 'Cookie: abc/xyz\r\n' 257 'Cookie2: $Version=1\r\n' 258 'Cookie: abc\r\n' 259 '\r\n', 260 ), 261 ) 262 263 _NOT_STRICTLY_GOOD_REQUESTS = ( 264 ( # Extra space after GET 265 'GET /demo HTTP/1.1\r\n', 266 'Upgrade: WebSocket\r\n', 267 'Connection: Upgrade\r\n', 268 'Host: example.com\r\n', 269 'Origin: http://example.com\r\n', 270 '\r\n', 271 ), 272 ( # Resource name doesn't stat with '/' 273 'GET demo HTTP/1.1\r\n', 274 'Upgrade: WebSocket\r\n', 275 'Connection: Upgrade\r\n', 276 'Host: example.com\r\n', 277 'Origin: http://example.com\r\n', 278 '\r\n', 279 ), 280 ( # No space after : 281 'GET /demo HTTP/1.1\r\n', 282 'Upgrade:WebSocket\r\n', 283 'Connection: Upgrade\r\n', 284 'Host: example.com\r\n', 285 'Origin: http://example.com\r\n', 286 '\r\n', 287 ), 288 ( # Lower case Upgrade header 289 'GET /demo HTTP/1.1\r\n', 290 'upgrade: WebSocket\r\n', 291 'Connection: Upgrade\r\n', 292 'Host: example.com\r\n', 293 'Origin: http://example.com\r\n', 294 '\r\n', 295 ), 296 ( # Connection comes before Upgrade 297 'GET /demo HTTP/1.1\r\n', 298 'Connection: Upgrade\r\n', 299 'Upgrade: WebSocket\r\n', 300 'Host: example.com\r\n', 301 'Origin: http://example.com\r\n', 302 '\r\n', 303 ), 304 ( # Origin comes before Host 305 'GET /demo HTTP/1.1\r\n', 306 'Upgrade: WebSocket\r\n', 307 'Connection: Upgrade\r\n', 308 'Origin: http://example.com\r\n', 309 'Host: example.com\r\n', 310 '\r\n', 311 ), 312 ( # Host continued to the next line 313 'GET /demo HTTP/1.1\r\n', 314 'Upgrade: WebSocket\r\n', 315 'Connection: Upgrade\r\n', 316 'Host: example\r\n', 317 ' .com\r\n', 318 'Origin: http://example.com\r\n', 319 '\r\n', 320 ), 321 ( # Cookie comes before WebSocket-Protocol 322 'GET /demo HTTP/1.1\r\n', 323 'Upgrade: WebSocket\r\n', 324 'Connection: Upgrade\r\n', 325 'Host: example.com\r\n', 326 'Origin: http://example.com\r\n', 327 'Cookie: xyz\r\n' 328 'WebSocket-Protocol: sample\r\n', 329 '\r\n', 330 ), 331 ( # Unknown header 332 'GET /demo HTTP/1.1\r\n', 333 'Upgrade: WebSocket\r\n', 334 'Connection: Upgrade\r\n', 335 'Host: example.com\r\n', 336 'Origin: http://example.com\r\n', 337 'Content-Type: text/html\r\n' 338 '\r\n', 339 ), 340 ( # Cookie with continuation lines 341 'GET /demo HTTP/1.1\r\n', 342 'Upgrade: WebSocket\r\n', 343 'Connection: Upgrade\r\n', 344 'Host: example.com\r\n', 345 'Origin: http://example.com\r\n', 346 'Cookie: xyz\r\n', 347 ' abc\r\n', 348 ' defg\r\n', 349 '\r\n', 219 350 ), 220 351 ) … … 230 361 231 362 363 def _create_get_memorized_lines(lines): 364 def get_memorized_lines(): 365 return lines 366 return get_memorized_lines 367 368 369 def _create_requests_with_lines(request_lines_set): 370 requests = [] 371 for lines in request_lines_set: 372 request = _create_request(_GOOD_REQUEST) 373 request.connection.get_memorized_lines = _create_get_memorized_lines( 374 lines) 375 requests.append(request) 376 return requests 377 378 232 379 class HandshakerTest(unittest.TestCase): 233 380 def test_validate_protocol(self): 234 381 handshake._validate_protocol('sample') # should succeed. 235 382 handshake._validate_protocol('Sample') # should succeed. 383 handshake._validate_protocol('sample\x20protocol') # should succeed. 384 handshake._validate_protocol('sample\x7eprotocol') # should succeed. 236 385 self.assertRaises(handshake.HandshakeError, 237 386 handshake._validate_protocol, 238 'sample protocol') 387 '') 388 self.assertRaises(handshake.HandshakeError, 389 handshake._validate_protocol, 390 'sample\x19protocol') 391 self.assertRaises(handshake.HandshakeError, 392 handshake._validate_protocol, 393 'sample\x7fprotocol') 239 394 self.assertRaises(handshake.HandshakeError, 240 395 handshake._validate_protocol, … … 309 464 self.assertRaises(handshake.HandshakeError, handshaker.do_handshake) 310 465 466 def test_strictly_good_requests(self): 467 for request in _create_requests_with_lines(_STRICTLY_GOOD_REQUESTS): 468 strict_handshaker = handshake.Handshaker(request, 469 mock.MockDispatcher(), 470 True) 471 strict_handshaker.do_handshake() 472 473 def test_not_strictly_good_requests(self): 474 for request in _create_requests_with_lines(_NOT_STRICTLY_GOOD_REQUESTS): 475 strict_handshaker = handshake.Handshaker(request, 476 mock.MockDispatcher(), 477 True) 478 self.assertRaises(handshake.HandshakeError, 479 strict_handshaker.do_handshake) 480 481 311 482 312 483 if __name__ == '__main__': -
trunk/WebKitTools/pywebsocket/test/test_memorizingfile.py
r52295 r52296 31 31 32 32 33 """Set up script for mod_pywebsocket. 34 """ 33 """Tests for memorizingfile module.""" 35 34 36 35 37 from distutils.core import setup 38 import sys 36 import StringIO 37 import unittest 38 39 import config # This must be imported before mod_pywebsocket. 40 from mod_pywebsocket import memorizingfile 39 41 40 42 41 _PACKAGE_NAME = 'mod_pywebsocket' 43 class UtilTest(unittest.TestCase): 44 def check(self, memorizing_file, num_read, expected_list): 45 for unused in range(num_read): 46 memorizing_file.readline() 47 actual_list = memorizing_file.get_memorized_lines() 48 self.assertEqual(len(expected_list), len(actual_list)) 49 for expected, actual in zip(expected_list, actual_list): 50 self.assertEqual(expected, actual) 42 51 43 if sys.version < '2.3': 44 print >>sys.stderr, '%s requires Python 2.3 or later.' % _PACKAGE_NAME 45 sys.exit(1) 52 def test_get_memorized_lines(self): 53 memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO( 54 'Hello\nWorld\nWelcome')) 55 self.check(memorizing_file, 3, ['Hello\n', 'World\n', 'Welcome']) 46 56 47 setup(author='Yuzo Fujishima', 48 author_email='yuzo@chromium.org', 49 description='Web Socket extension for Apache HTTP Server.', 50 long_description=( 51 'mod_pywebsocket is an Apache HTTP Server extension for ' 52 'Web Socket (http://tools.ietf.org/html/' 53 'draft-hixie-thewebsocketprotocol). ' 54 'See mod_pywebsocket/__init__.py for more detail.'), 55 license='See COPYING', 56 name=_PACKAGE_NAME, 57 packages=[_PACKAGE_NAME], 58 url='http://code.google.com/p/pywebsocket/', 59 version='0.4.3', 60 ) 57 def test_get_memorized_lines_limit_memorized_lines(self): 58 memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO( 59 'Hello\nWorld\nWelcome'), 2) 60 self.check(memorizing_file, 3, ['Hello\n', 'World\n']) 61 62 def test_get_memorized_lines_empty_file(self): 63 memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO( 64 '')) 65 self.check(memorizing_file, 10, []) 66 67 68 if __name__ == '__main__': 69 unittest.main() 61 70 62 71
Note: See TracChangeset
for help on using the changeset viewer.