1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/util.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,496 @@ 1.4 +# Copyright 2011, Google Inc. 1.5 +# All rights reserved. 1.6 +# 1.7 +# Redistribution and use in source and binary forms, with or without 1.8 +# modification, are permitted provided that the following conditions are 1.9 +# met: 1.10 +# 1.11 +# * Redistributions of source code must retain the above copyright 1.12 +# notice, this list of conditions and the following disclaimer. 1.13 +# * Redistributions in binary form must reproduce the above 1.14 +# copyright notice, this list of conditions and the following disclaimer 1.15 +# in the documentation and/or other materials provided with the 1.16 +# distribution. 1.17 +# * Neither the name of Google Inc. nor the names of its 1.18 +# contributors may be used to endorse or promote products derived from 1.19 +# this software without specific prior written permission. 1.20 +# 1.21 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 + 1.34 +"""WebSocket utilities. 1.35 +""" 1.36 + 1.37 + 1.38 +import array 1.39 +import errno 1.40 + 1.41 +# Import hash classes from a module available and recommended for each Python 1.42 +# version and re-export those symbol. Use sha and md5 module in Python 2.4, and 1.43 +# hashlib module in Python 2.6. 1.44 +try: 1.45 + import hashlib 1.46 + md5_hash = hashlib.md5 1.47 + sha1_hash = hashlib.sha1 1.48 +except ImportError: 1.49 + import md5 1.50 + import sha 1.51 + md5_hash = md5.md5 1.52 + sha1_hash = sha.sha 1.53 + 1.54 +import StringIO 1.55 +import logging 1.56 +import os 1.57 +import re 1.58 +import socket 1.59 +import traceback 1.60 +import zlib 1.61 + 1.62 + 1.63 +def get_stack_trace(): 1.64 + """Get the current stack trace as string. 1.65 + 1.66 + This is needed to support Python 2.3. 1.67 + TODO: Remove this when we only support Python 2.4 and above. 1.68 + Use traceback.format_exc instead. 1.69 + """ 1.70 + 1.71 + out = StringIO.StringIO() 1.72 + traceback.print_exc(file=out) 1.73 + return out.getvalue() 1.74 + 1.75 + 1.76 +def prepend_message_to_exception(message, exc): 1.77 + """Prepend message to the exception.""" 1.78 + 1.79 + exc.args = (message + str(exc),) 1.80 + return 1.81 + 1.82 + 1.83 +def __translate_interp(interp, cygwin_path): 1.84 + """Translate interp program path for Win32 python to run cygwin program 1.85 + (e.g. perl). Note that it doesn't support path that contains space, 1.86 + which is typically true for Unix, where #!-script is written. 1.87 + For Win32 python, cygwin_path is a directory of cygwin binaries. 1.88 + 1.89 + Args: 1.90 + interp: interp command line 1.91 + cygwin_path: directory name of cygwin binary, or None 1.92 + Returns: 1.93 + translated interp command line. 1.94 + """ 1.95 + if not cygwin_path: 1.96 + return interp 1.97 + m = re.match('^[^ ]*/([^ ]+)( .*)?', interp) 1.98 + if m: 1.99 + cmd = os.path.join(cygwin_path, m.group(1)) 1.100 + return cmd + m.group(2) 1.101 + return interp 1.102 + 1.103 + 1.104 +def get_script_interp(script_path, cygwin_path=None): 1.105 + """Gets #!-interpreter command line from the script. 1.106 + 1.107 + It also fixes command path. When Cygwin Python is used, e.g. in WebKit, 1.108 + it could run "/usr/bin/perl -wT hello.pl". 1.109 + When Win32 Python is used, e.g. in Chromium, it couldn't. So, fix 1.110 + "/usr/bin/perl" to "<cygwin_path>\perl.exe". 1.111 + 1.112 + Args: 1.113 + script_path: pathname of the script 1.114 + cygwin_path: directory name of cygwin binary, or None 1.115 + Returns: 1.116 + #!-interpreter command line, or None if it is not #!-script. 1.117 + """ 1.118 + fp = open(script_path) 1.119 + line = fp.readline() 1.120 + fp.close() 1.121 + m = re.match('^#!(.*)', line) 1.122 + if m: 1.123 + return __translate_interp(m.group(1), cygwin_path) 1.124 + return None 1.125 + 1.126 + 1.127 +def wrap_popen3_for_win(cygwin_path): 1.128 + """Wrap popen3 to support #!-script on Windows. 1.129 + 1.130 + Args: 1.131 + cygwin_path: path for cygwin binary if command path is needed to be 1.132 + translated. None if no translation required. 1.133 + """ 1.134 + 1.135 + __orig_popen3 = os.popen3 1.136 + 1.137 + def __wrap_popen3(cmd, mode='t', bufsize=-1): 1.138 + cmdline = cmd.split(' ') 1.139 + interp = get_script_interp(cmdline[0], cygwin_path) 1.140 + if interp: 1.141 + cmd = interp + ' ' + cmd 1.142 + return __orig_popen3(cmd, mode, bufsize) 1.143 + 1.144 + os.popen3 = __wrap_popen3 1.145 + 1.146 + 1.147 +def hexify(s): 1.148 + return ' '.join(map(lambda x: '%02x' % ord(x), s)) 1.149 + 1.150 + 1.151 +def get_class_logger(o): 1.152 + return logging.getLogger( 1.153 + '%s.%s' % (o.__class__.__module__, o.__class__.__name__)) 1.154 + 1.155 + 1.156 +class NoopMasker(object): 1.157 + """A masking object that has the same interface as RepeatedXorMasker but 1.158 + just returns the string passed in without making any change. 1.159 + """ 1.160 + 1.161 + def __init__(self): 1.162 + pass 1.163 + 1.164 + def mask(self, s): 1.165 + return s 1.166 + 1.167 + 1.168 +class RepeatedXorMasker(object): 1.169 + """A masking object that applies XOR on the string given to mask method 1.170 + with the masking bytes given to the constructor repeatedly. This object 1.171 + remembers the position in the masking bytes the last mask method call 1.172 + ended and resumes from that point on the next mask method call. 1.173 + """ 1.174 + 1.175 + def __init__(self, mask): 1.176 + self._mask = map(ord, mask) 1.177 + self._mask_size = len(self._mask) 1.178 + self._count = 0 1.179 + 1.180 + def mask(self, s): 1.181 + result = array.array('B') 1.182 + result.fromstring(s) 1.183 + # Use temporary local variables to eliminate the cost to access 1.184 + # attributes 1.185 + count = self._count 1.186 + mask = self._mask 1.187 + mask_size = self._mask_size 1.188 + for i in xrange(len(result)): 1.189 + result[i] ^= mask[count] 1.190 + count = (count + 1) % mask_size 1.191 + self._count = count 1.192 + 1.193 + return result.tostring() 1.194 + 1.195 + 1.196 +class DeflateRequest(object): 1.197 + """A wrapper class for request object to intercept send and recv to perform 1.198 + deflate compression and decompression transparently. 1.199 + """ 1.200 + 1.201 + def __init__(self, request): 1.202 + self._request = request 1.203 + self.connection = DeflateConnection(request.connection) 1.204 + 1.205 + def __getattribute__(self, name): 1.206 + if name in ('_request', 'connection'): 1.207 + return object.__getattribute__(self, name) 1.208 + return self._request.__getattribute__(name) 1.209 + 1.210 + def __setattr__(self, name, value): 1.211 + if name in ('_request', 'connection'): 1.212 + return object.__setattr__(self, name, value) 1.213 + return self._request.__setattr__(name, value) 1.214 + 1.215 + 1.216 +# By making wbits option negative, we can suppress CMF/FLG (2 octet) and 1.217 +# ADLER32 (4 octet) fields of zlib so that we can use zlib module just as 1.218 +# deflate library. DICTID won't be added as far as we don't set dictionary. 1.219 +# LZ77 window of 32K will be used for both compression and decompression. 1.220 +# For decompression, we can just use 32K to cover any windows size. For 1.221 +# compression, we use 32K so receivers must use 32K. 1.222 +# 1.223 +# Compression level is Z_DEFAULT_COMPRESSION. We don't have to match level 1.224 +# to decode. 1.225 +# 1.226 +# See zconf.h, deflate.cc, inflate.cc of zlib library, and zlibmodule.c of 1.227 +# Python. See also RFC1950 (ZLIB 3.3). 1.228 + 1.229 + 1.230 +class _Deflater(object): 1.231 + 1.232 + def __init__(self, window_bits): 1.233 + self._logger = get_class_logger(self) 1.234 + 1.235 + self._compress = zlib.compressobj( 1.236 + zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -window_bits) 1.237 + 1.238 + def compress_and_flush(self, bytes): 1.239 + compressed_bytes = self._compress.compress(bytes) 1.240 + compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH) 1.241 + self._logger.debug('Compress input %r', bytes) 1.242 + self._logger.debug('Compress result %r', compressed_bytes) 1.243 + return compressed_bytes 1.244 + 1.245 + 1.246 +class _Inflater(object): 1.247 + 1.248 + def __init__(self): 1.249 + self._logger = get_class_logger(self) 1.250 + 1.251 + self._unconsumed = '' 1.252 + 1.253 + self.reset() 1.254 + 1.255 + def decompress(self, size): 1.256 + if not (size == -1 or size > 0): 1.257 + raise Exception('size must be -1 or positive') 1.258 + 1.259 + data = '' 1.260 + 1.261 + while True: 1.262 + if size == -1: 1.263 + data += self._decompress.decompress(self._unconsumed) 1.264 + # See Python bug http://bugs.python.org/issue12050 to 1.265 + # understand why the same code cannot be used for updating 1.266 + # self._unconsumed for here and else block. 1.267 + self._unconsumed = '' 1.268 + else: 1.269 + data += self._decompress.decompress( 1.270 + self._unconsumed, size - len(data)) 1.271 + self._unconsumed = self._decompress.unconsumed_tail 1.272 + if self._decompress.unused_data: 1.273 + # Encountered a last block (i.e. a block with BFINAL = 1) and 1.274 + # found a new stream (unused_data). We cannot use the same 1.275 + # zlib.Decompress object for the new stream. Create a new 1.276 + # Decompress object to decompress the new one. 1.277 + # 1.278 + # It's fine to ignore unconsumed_tail if unused_data is not 1.279 + # empty. 1.280 + self._unconsumed = self._decompress.unused_data 1.281 + self.reset() 1.282 + if size >= 0 and len(data) == size: 1.283 + # data is filled. Don't call decompress again. 1.284 + break 1.285 + else: 1.286 + # Re-invoke Decompress.decompress to try to decompress all 1.287 + # available bytes before invoking read which blocks until 1.288 + # any new byte is available. 1.289 + continue 1.290 + else: 1.291 + # Here, since unused_data is empty, even if unconsumed_tail is 1.292 + # not empty, bytes of requested length are already in data. We 1.293 + # don't have to "continue" here. 1.294 + break 1.295 + 1.296 + if data: 1.297 + self._logger.debug('Decompressed %r', data) 1.298 + return data 1.299 + 1.300 + def append(self, data): 1.301 + self._logger.debug('Appended %r', data) 1.302 + self._unconsumed += data 1.303 + 1.304 + def reset(self): 1.305 + self._logger.debug('Reset') 1.306 + self._decompress = zlib.decompressobj(-zlib.MAX_WBITS) 1.307 + 1.308 + 1.309 +# Compresses/decompresses given octets using the method introduced in RFC1979. 1.310 + 1.311 + 1.312 +class _RFC1979Deflater(object): 1.313 + """A compressor class that applies DEFLATE to given byte sequence and 1.314 + flushes using the algorithm described in the RFC1979 section 2.1. 1.315 + """ 1.316 + 1.317 + def __init__(self, window_bits, no_context_takeover): 1.318 + self._deflater = None 1.319 + if window_bits is None: 1.320 + window_bits = zlib.MAX_WBITS 1.321 + self._window_bits = window_bits 1.322 + self._no_context_takeover = no_context_takeover 1.323 + 1.324 + def filter(self, bytes): 1.325 + if self._deflater is None or self._no_context_takeover: 1.326 + self._deflater = _Deflater(self._window_bits) 1.327 + 1.328 + # Strip last 4 octets which is LEN and NLEN field of a non-compressed 1.329 + # block added for Z_SYNC_FLUSH. 1.330 + return self._deflater.compress_and_flush(bytes)[:-4] 1.331 + 1.332 + 1.333 +class _RFC1979Inflater(object): 1.334 + """A decompressor class for byte sequence compressed and flushed following 1.335 + the algorithm described in the RFC1979 section 2.1. 1.336 + """ 1.337 + 1.338 + def __init__(self): 1.339 + self._inflater = _Inflater() 1.340 + 1.341 + def filter(self, bytes): 1.342 + # Restore stripped LEN and NLEN field of a non-compressed block added 1.343 + # for Z_SYNC_FLUSH. 1.344 + self._inflater.append(bytes + '\x00\x00\xff\xff') 1.345 + return self._inflater.decompress(-1) 1.346 + 1.347 + 1.348 +class DeflateSocket(object): 1.349 + """A wrapper class for socket object to intercept send and recv to perform 1.350 + deflate compression and decompression transparently. 1.351 + """ 1.352 + 1.353 + # Size of the buffer passed to recv to receive compressed data. 1.354 + _RECV_SIZE = 4096 1.355 + 1.356 + def __init__(self, socket): 1.357 + self._socket = socket 1.358 + 1.359 + self._logger = get_class_logger(self) 1.360 + 1.361 + self._deflater = _Deflater(zlib.MAX_WBITS) 1.362 + self._inflater = _Inflater() 1.363 + 1.364 + def recv(self, size): 1.365 + """Receives data from the socket specified on the construction up 1.366 + to the specified size. Once any data is available, returns it even 1.367 + if it's smaller than the specified size. 1.368 + """ 1.369 + 1.370 + # TODO(tyoshino): Allow call with size=0. It should block until any 1.371 + # decompressed data is available. 1.372 + if size <= 0: 1.373 + raise Exception('Non-positive size passed') 1.374 + while True: 1.375 + data = self._inflater.decompress(size) 1.376 + if len(data) != 0: 1.377 + return data 1.378 + 1.379 + read_data = self._socket.recv(DeflateSocket._RECV_SIZE) 1.380 + if not read_data: 1.381 + return '' 1.382 + self._inflater.append(read_data) 1.383 + 1.384 + def sendall(self, bytes): 1.385 + self.send(bytes) 1.386 + 1.387 + def send(self, bytes): 1.388 + self._socket.sendall(self._deflater.compress_and_flush(bytes)) 1.389 + return len(bytes) 1.390 + 1.391 + 1.392 +class DeflateConnection(object): 1.393 + """A wrapper class for request object to intercept write and read to 1.394 + perform deflate compression and decompression transparently. 1.395 + """ 1.396 + 1.397 + def __init__(self, connection): 1.398 + self._connection = connection 1.399 + 1.400 + self._logger = get_class_logger(self) 1.401 + 1.402 + self._deflater = _Deflater(zlib.MAX_WBITS) 1.403 + self._inflater = _Inflater() 1.404 + 1.405 + def get_remote_addr(self): 1.406 + return self._connection.remote_addr 1.407 + remote_addr = property(get_remote_addr) 1.408 + 1.409 + def put_bytes(self, bytes): 1.410 + self.write(bytes) 1.411 + 1.412 + def read(self, size=-1): 1.413 + """Reads at most size bytes. Blocks until there's at least one byte 1.414 + available. 1.415 + """ 1.416 + 1.417 + # TODO(tyoshino): Allow call with size=0. 1.418 + if not (size == -1 or size > 0): 1.419 + raise Exception('size must be -1 or positive') 1.420 + 1.421 + data = '' 1.422 + while True: 1.423 + if size == -1: 1.424 + data += self._inflater.decompress(-1) 1.425 + else: 1.426 + data += self._inflater.decompress(size - len(data)) 1.427 + 1.428 + if size >= 0 and len(data) != 0: 1.429 + break 1.430 + 1.431 + # TODO(tyoshino): Make this read efficient by some workaround. 1.432 + # 1.433 + # In 3.0.3 and prior of mod_python, read blocks until length bytes 1.434 + # was read. We don't know the exact size to read while using 1.435 + # deflate, so read byte-by-byte. 1.436 + # 1.437 + # _StandaloneRequest.read that ultimately performs 1.438 + # socket._fileobject.read also blocks until length bytes was read 1.439 + read_data = self._connection.read(1) 1.440 + if not read_data: 1.441 + break 1.442 + self._inflater.append(read_data) 1.443 + return data 1.444 + 1.445 + def write(self, bytes): 1.446 + self._connection.write(self._deflater.compress_and_flush(bytes)) 1.447 + 1.448 + 1.449 +def _is_ewouldblock_errno(error_number): 1.450 + """Returns True iff error_number indicates that receive operation would 1.451 + block. To make this portable, we check availability of errno and then 1.452 + compare them. 1.453 + """ 1.454 + 1.455 + for error_name in ['WSAEWOULDBLOCK', 'EWOULDBLOCK', 'EAGAIN']: 1.456 + if (error_name in dir(errno) and 1.457 + error_number == getattr(errno, error_name)): 1.458 + return True 1.459 + return False 1.460 + 1.461 + 1.462 +def drain_received_data(raw_socket): 1.463 + # Set the socket non-blocking. 1.464 + original_timeout = raw_socket.gettimeout() 1.465 + raw_socket.settimeout(0.0) 1.466 + 1.467 + drained_data = [] 1.468 + 1.469 + # Drain until the socket is closed or no data is immediately 1.470 + # available for read. 1.471 + while True: 1.472 + try: 1.473 + data = raw_socket.recv(1) 1.474 + if not data: 1.475 + break 1.476 + drained_data.append(data) 1.477 + except socket.error, e: 1.478 + # e can be either a pair (errno, string) or just a string (or 1.479 + # something else) telling what went wrong. We suppress only 1.480 + # the errors that indicates that the socket blocks. Those 1.481 + # exceptions can be parsed as a pair (errno, string). 1.482 + try: 1.483 + error_number, message = e 1.484 + except: 1.485 + # Failed to parse socket.error. 1.486 + raise e 1.487 + 1.488 + if _is_ewouldblock_errno(error_number): 1.489 + break 1.490 + else: 1.491 + raise e 1.492 + 1.493 + # Rollback timeout value. 1.494 + raw_socket.settimeout(original_timeout) 1.495 + 1.496 + return ''.join(drained_data) 1.497 + 1.498 + 1.499 +# vi:sts=4 sw=4 et