Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | # Copyright 2011, Google Inc. |
michael@0 | 2 | # All rights reserved. |
michael@0 | 3 | # |
michael@0 | 4 | # Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | # modification, are permitted provided that the following conditions are |
michael@0 | 6 | # met: |
michael@0 | 7 | # |
michael@0 | 8 | # * Redistributions of source code must retain the above copyright |
michael@0 | 9 | # notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | # * Redistributions in binary form must reproduce the above |
michael@0 | 11 | # copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | # in the documentation and/or other materials provided with the |
michael@0 | 13 | # distribution. |
michael@0 | 14 | # * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | # contributors may be used to endorse or promote products derived from |
michael@0 | 16 | # this software without specific prior written permission. |
michael@0 | 17 | # |
michael@0 | 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | |
michael@0 | 31 | """WebSocket utilities. |
michael@0 | 32 | """ |
michael@0 | 33 | |
michael@0 | 34 | |
michael@0 | 35 | import array |
michael@0 | 36 | import errno |
michael@0 | 37 | |
michael@0 | 38 | # Import hash classes from a module available and recommended for each Python |
michael@0 | 39 | # version and re-export those symbol. Use sha and md5 module in Python 2.4, and |
michael@0 | 40 | # hashlib module in Python 2.6. |
michael@0 | 41 | try: |
michael@0 | 42 | import hashlib |
michael@0 | 43 | md5_hash = hashlib.md5 |
michael@0 | 44 | sha1_hash = hashlib.sha1 |
michael@0 | 45 | except ImportError: |
michael@0 | 46 | import md5 |
michael@0 | 47 | import sha |
michael@0 | 48 | md5_hash = md5.md5 |
michael@0 | 49 | sha1_hash = sha.sha |
michael@0 | 50 | |
michael@0 | 51 | import StringIO |
michael@0 | 52 | import logging |
michael@0 | 53 | import os |
michael@0 | 54 | import re |
michael@0 | 55 | import socket |
michael@0 | 56 | import traceback |
michael@0 | 57 | import zlib |
michael@0 | 58 | |
michael@0 | 59 | |
michael@0 | 60 | def get_stack_trace(): |
michael@0 | 61 | """Get the current stack trace as string. |
michael@0 | 62 | |
michael@0 | 63 | This is needed to support Python 2.3. |
michael@0 | 64 | TODO: Remove this when we only support Python 2.4 and above. |
michael@0 | 65 | Use traceback.format_exc instead. |
michael@0 | 66 | """ |
michael@0 | 67 | |
michael@0 | 68 | out = StringIO.StringIO() |
michael@0 | 69 | traceback.print_exc(file=out) |
michael@0 | 70 | return out.getvalue() |
michael@0 | 71 | |
michael@0 | 72 | |
michael@0 | 73 | def prepend_message_to_exception(message, exc): |
michael@0 | 74 | """Prepend message to the exception.""" |
michael@0 | 75 | |
michael@0 | 76 | exc.args = (message + str(exc),) |
michael@0 | 77 | return |
michael@0 | 78 | |
michael@0 | 79 | |
michael@0 | 80 | def __translate_interp(interp, cygwin_path): |
michael@0 | 81 | """Translate interp program path for Win32 python to run cygwin program |
michael@0 | 82 | (e.g. perl). Note that it doesn't support path that contains space, |
michael@0 | 83 | which is typically true for Unix, where #!-script is written. |
michael@0 | 84 | For Win32 python, cygwin_path is a directory of cygwin binaries. |
michael@0 | 85 | |
michael@0 | 86 | Args: |
michael@0 | 87 | interp: interp command line |
michael@0 | 88 | cygwin_path: directory name of cygwin binary, or None |
michael@0 | 89 | Returns: |
michael@0 | 90 | translated interp command line. |
michael@0 | 91 | """ |
michael@0 | 92 | if not cygwin_path: |
michael@0 | 93 | return interp |
michael@0 | 94 | m = re.match('^[^ ]*/([^ ]+)( .*)?', interp) |
michael@0 | 95 | if m: |
michael@0 | 96 | cmd = os.path.join(cygwin_path, m.group(1)) |
michael@0 | 97 | return cmd + m.group(2) |
michael@0 | 98 | return interp |
michael@0 | 99 | |
michael@0 | 100 | |
michael@0 | 101 | def get_script_interp(script_path, cygwin_path=None): |
michael@0 | 102 | """Gets #!-interpreter command line from the script. |
michael@0 | 103 | |
michael@0 | 104 | It also fixes command path. When Cygwin Python is used, e.g. in WebKit, |
michael@0 | 105 | it could run "/usr/bin/perl -wT hello.pl". |
michael@0 | 106 | When Win32 Python is used, e.g. in Chromium, it couldn't. So, fix |
michael@0 | 107 | "/usr/bin/perl" to "<cygwin_path>\perl.exe". |
michael@0 | 108 | |
michael@0 | 109 | Args: |
michael@0 | 110 | script_path: pathname of the script |
michael@0 | 111 | cygwin_path: directory name of cygwin binary, or None |
michael@0 | 112 | Returns: |
michael@0 | 113 | #!-interpreter command line, or None if it is not #!-script. |
michael@0 | 114 | """ |
michael@0 | 115 | fp = open(script_path) |
michael@0 | 116 | line = fp.readline() |
michael@0 | 117 | fp.close() |
michael@0 | 118 | m = re.match('^#!(.*)', line) |
michael@0 | 119 | if m: |
michael@0 | 120 | return __translate_interp(m.group(1), cygwin_path) |
michael@0 | 121 | return None |
michael@0 | 122 | |
michael@0 | 123 | |
michael@0 | 124 | def wrap_popen3_for_win(cygwin_path): |
michael@0 | 125 | """Wrap popen3 to support #!-script on Windows. |
michael@0 | 126 | |
michael@0 | 127 | Args: |
michael@0 | 128 | cygwin_path: path for cygwin binary if command path is needed to be |
michael@0 | 129 | translated. None if no translation required. |
michael@0 | 130 | """ |
michael@0 | 131 | |
michael@0 | 132 | __orig_popen3 = os.popen3 |
michael@0 | 133 | |
michael@0 | 134 | def __wrap_popen3(cmd, mode='t', bufsize=-1): |
michael@0 | 135 | cmdline = cmd.split(' ') |
michael@0 | 136 | interp = get_script_interp(cmdline[0], cygwin_path) |
michael@0 | 137 | if interp: |
michael@0 | 138 | cmd = interp + ' ' + cmd |
michael@0 | 139 | return __orig_popen3(cmd, mode, bufsize) |
michael@0 | 140 | |
michael@0 | 141 | os.popen3 = __wrap_popen3 |
michael@0 | 142 | |
michael@0 | 143 | |
michael@0 | 144 | def hexify(s): |
michael@0 | 145 | return ' '.join(map(lambda x: '%02x' % ord(x), s)) |
michael@0 | 146 | |
michael@0 | 147 | |
michael@0 | 148 | def get_class_logger(o): |
michael@0 | 149 | return logging.getLogger( |
michael@0 | 150 | '%s.%s' % (o.__class__.__module__, o.__class__.__name__)) |
michael@0 | 151 | |
michael@0 | 152 | |
michael@0 | 153 | class NoopMasker(object): |
michael@0 | 154 | """A masking object that has the same interface as RepeatedXorMasker but |
michael@0 | 155 | just returns the string passed in without making any change. |
michael@0 | 156 | """ |
michael@0 | 157 | |
michael@0 | 158 | def __init__(self): |
michael@0 | 159 | pass |
michael@0 | 160 | |
michael@0 | 161 | def mask(self, s): |
michael@0 | 162 | return s |
michael@0 | 163 | |
michael@0 | 164 | |
michael@0 | 165 | class RepeatedXorMasker(object): |
michael@0 | 166 | """A masking object that applies XOR on the string given to mask method |
michael@0 | 167 | with the masking bytes given to the constructor repeatedly. This object |
michael@0 | 168 | remembers the position in the masking bytes the last mask method call |
michael@0 | 169 | ended and resumes from that point on the next mask method call. |
michael@0 | 170 | """ |
michael@0 | 171 | |
michael@0 | 172 | def __init__(self, mask): |
michael@0 | 173 | self._mask = map(ord, mask) |
michael@0 | 174 | self._mask_size = len(self._mask) |
michael@0 | 175 | self._count = 0 |
michael@0 | 176 | |
michael@0 | 177 | def mask(self, s): |
michael@0 | 178 | result = array.array('B') |
michael@0 | 179 | result.fromstring(s) |
michael@0 | 180 | # Use temporary local variables to eliminate the cost to access |
michael@0 | 181 | # attributes |
michael@0 | 182 | count = self._count |
michael@0 | 183 | mask = self._mask |
michael@0 | 184 | mask_size = self._mask_size |
michael@0 | 185 | for i in xrange(len(result)): |
michael@0 | 186 | result[i] ^= mask[count] |
michael@0 | 187 | count = (count + 1) % mask_size |
michael@0 | 188 | self._count = count |
michael@0 | 189 | |
michael@0 | 190 | return result.tostring() |
michael@0 | 191 | |
michael@0 | 192 | |
michael@0 | 193 | class DeflateRequest(object): |
michael@0 | 194 | """A wrapper class for request object to intercept send and recv to perform |
michael@0 | 195 | deflate compression and decompression transparently. |
michael@0 | 196 | """ |
michael@0 | 197 | |
michael@0 | 198 | def __init__(self, request): |
michael@0 | 199 | self._request = request |
michael@0 | 200 | self.connection = DeflateConnection(request.connection) |
michael@0 | 201 | |
michael@0 | 202 | def __getattribute__(self, name): |
michael@0 | 203 | if name in ('_request', 'connection'): |
michael@0 | 204 | return object.__getattribute__(self, name) |
michael@0 | 205 | return self._request.__getattribute__(name) |
michael@0 | 206 | |
michael@0 | 207 | def __setattr__(self, name, value): |
michael@0 | 208 | if name in ('_request', 'connection'): |
michael@0 | 209 | return object.__setattr__(self, name, value) |
michael@0 | 210 | return self._request.__setattr__(name, value) |
michael@0 | 211 | |
michael@0 | 212 | |
michael@0 | 213 | # By making wbits option negative, we can suppress CMF/FLG (2 octet) and |
michael@0 | 214 | # ADLER32 (4 octet) fields of zlib so that we can use zlib module just as |
michael@0 | 215 | # deflate library. DICTID won't be added as far as we don't set dictionary. |
michael@0 | 216 | # LZ77 window of 32K will be used for both compression and decompression. |
michael@0 | 217 | # For decompression, we can just use 32K to cover any windows size. For |
michael@0 | 218 | # compression, we use 32K so receivers must use 32K. |
michael@0 | 219 | # |
michael@0 | 220 | # Compression level is Z_DEFAULT_COMPRESSION. We don't have to match level |
michael@0 | 221 | # to decode. |
michael@0 | 222 | # |
michael@0 | 223 | # See zconf.h, deflate.cc, inflate.cc of zlib library, and zlibmodule.c of |
michael@0 | 224 | # Python. See also RFC1950 (ZLIB 3.3). |
michael@0 | 225 | |
michael@0 | 226 | |
michael@0 | 227 | class _Deflater(object): |
michael@0 | 228 | |
michael@0 | 229 | def __init__(self, window_bits): |
michael@0 | 230 | self._logger = get_class_logger(self) |
michael@0 | 231 | |
michael@0 | 232 | self._compress = zlib.compressobj( |
michael@0 | 233 | zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -window_bits) |
michael@0 | 234 | |
michael@0 | 235 | def compress_and_flush(self, bytes): |
michael@0 | 236 | compressed_bytes = self._compress.compress(bytes) |
michael@0 | 237 | compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH) |
michael@0 | 238 | self._logger.debug('Compress input %r', bytes) |
michael@0 | 239 | self._logger.debug('Compress result %r', compressed_bytes) |
michael@0 | 240 | return compressed_bytes |
michael@0 | 241 | |
michael@0 | 242 | |
michael@0 | 243 | class _Inflater(object): |
michael@0 | 244 | |
michael@0 | 245 | def __init__(self): |
michael@0 | 246 | self._logger = get_class_logger(self) |
michael@0 | 247 | |
michael@0 | 248 | self._unconsumed = '' |
michael@0 | 249 | |
michael@0 | 250 | self.reset() |
michael@0 | 251 | |
michael@0 | 252 | def decompress(self, size): |
michael@0 | 253 | if not (size == -1 or size > 0): |
michael@0 | 254 | raise Exception('size must be -1 or positive') |
michael@0 | 255 | |
michael@0 | 256 | data = '' |
michael@0 | 257 | |
michael@0 | 258 | while True: |
michael@0 | 259 | if size == -1: |
michael@0 | 260 | data += self._decompress.decompress(self._unconsumed) |
michael@0 | 261 | # See Python bug http://bugs.python.org/issue12050 to |
michael@0 | 262 | # understand why the same code cannot be used for updating |
michael@0 | 263 | # self._unconsumed for here and else block. |
michael@0 | 264 | self._unconsumed = '' |
michael@0 | 265 | else: |
michael@0 | 266 | data += self._decompress.decompress( |
michael@0 | 267 | self._unconsumed, size - len(data)) |
michael@0 | 268 | self._unconsumed = self._decompress.unconsumed_tail |
michael@0 | 269 | if self._decompress.unused_data: |
michael@0 | 270 | # Encountered a last block (i.e. a block with BFINAL = 1) and |
michael@0 | 271 | # found a new stream (unused_data). We cannot use the same |
michael@0 | 272 | # zlib.Decompress object for the new stream. Create a new |
michael@0 | 273 | # Decompress object to decompress the new one. |
michael@0 | 274 | # |
michael@0 | 275 | # It's fine to ignore unconsumed_tail if unused_data is not |
michael@0 | 276 | # empty. |
michael@0 | 277 | self._unconsumed = self._decompress.unused_data |
michael@0 | 278 | self.reset() |
michael@0 | 279 | if size >= 0 and len(data) == size: |
michael@0 | 280 | # data is filled. Don't call decompress again. |
michael@0 | 281 | break |
michael@0 | 282 | else: |
michael@0 | 283 | # Re-invoke Decompress.decompress to try to decompress all |
michael@0 | 284 | # available bytes before invoking read which blocks until |
michael@0 | 285 | # any new byte is available. |
michael@0 | 286 | continue |
michael@0 | 287 | else: |
michael@0 | 288 | # Here, since unused_data is empty, even if unconsumed_tail is |
michael@0 | 289 | # not empty, bytes of requested length are already in data. We |
michael@0 | 290 | # don't have to "continue" here. |
michael@0 | 291 | break |
michael@0 | 292 | |
michael@0 | 293 | if data: |
michael@0 | 294 | self._logger.debug('Decompressed %r', data) |
michael@0 | 295 | return data |
michael@0 | 296 | |
michael@0 | 297 | def append(self, data): |
michael@0 | 298 | self._logger.debug('Appended %r', data) |
michael@0 | 299 | self._unconsumed += data |
michael@0 | 300 | |
michael@0 | 301 | def reset(self): |
michael@0 | 302 | self._logger.debug('Reset') |
michael@0 | 303 | self._decompress = zlib.decompressobj(-zlib.MAX_WBITS) |
michael@0 | 304 | |
michael@0 | 305 | |
michael@0 | 306 | # Compresses/decompresses given octets using the method introduced in RFC1979. |
michael@0 | 307 | |
michael@0 | 308 | |
michael@0 | 309 | class _RFC1979Deflater(object): |
michael@0 | 310 | """A compressor class that applies DEFLATE to given byte sequence and |
michael@0 | 311 | flushes using the algorithm described in the RFC1979 section 2.1. |
michael@0 | 312 | """ |
michael@0 | 313 | |
michael@0 | 314 | def __init__(self, window_bits, no_context_takeover): |
michael@0 | 315 | self._deflater = None |
michael@0 | 316 | if window_bits is None: |
michael@0 | 317 | window_bits = zlib.MAX_WBITS |
michael@0 | 318 | self._window_bits = window_bits |
michael@0 | 319 | self._no_context_takeover = no_context_takeover |
michael@0 | 320 | |
michael@0 | 321 | def filter(self, bytes): |
michael@0 | 322 | if self._deflater is None or self._no_context_takeover: |
michael@0 | 323 | self._deflater = _Deflater(self._window_bits) |
michael@0 | 324 | |
michael@0 | 325 | # Strip last 4 octets which is LEN and NLEN field of a non-compressed |
michael@0 | 326 | # block added for Z_SYNC_FLUSH. |
michael@0 | 327 | return self._deflater.compress_and_flush(bytes)[:-4] |
michael@0 | 328 | |
michael@0 | 329 | |
michael@0 | 330 | class _RFC1979Inflater(object): |
michael@0 | 331 | """A decompressor class for byte sequence compressed and flushed following |
michael@0 | 332 | the algorithm described in the RFC1979 section 2.1. |
michael@0 | 333 | """ |
michael@0 | 334 | |
michael@0 | 335 | def __init__(self): |
michael@0 | 336 | self._inflater = _Inflater() |
michael@0 | 337 | |
michael@0 | 338 | def filter(self, bytes): |
michael@0 | 339 | # Restore stripped LEN and NLEN field of a non-compressed block added |
michael@0 | 340 | # for Z_SYNC_FLUSH. |
michael@0 | 341 | self._inflater.append(bytes + '\x00\x00\xff\xff') |
michael@0 | 342 | return self._inflater.decompress(-1) |
michael@0 | 343 | |
michael@0 | 344 | |
michael@0 | 345 | class DeflateSocket(object): |
michael@0 | 346 | """A wrapper class for socket object to intercept send and recv to perform |
michael@0 | 347 | deflate compression and decompression transparently. |
michael@0 | 348 | """ |
michael@0 | 349 | |
michael@0 | 350 | # Size of the buffer passed to recv to receive compressed data. |
michael@0 | 351 | _RECV_SIZE = 4096 |
michael@0 | 352 | |
michael@0 | 353 | def __init__(self, socket): |
michael@0 | 354 | self._socket = socket |
michael@0 | 355 | |
michael@0 | 356 | self._logger = get_class_logger(self) |
michael@0 | 357 | |
michael@0 | 358 | self._deflater = _Deflater(zlib.MAX_WBITS) |
michael@0 | 359 | self._inflater = _Inflater() |
michael@0 | 360 | |
michael@0 | 361 | def recv(self, size): |
michael@0 | 362 | """Receives data from the socket specified on the construction up |
michael@0 | 363 | to the specified size. Once any data is available, returns it even |
michael@0 | 364 | if it's smaller than the specified size. |
michael@0 | 365 | """ |
michael@0 | 366 | |
michael@0 | 367 | # TODO(tyoshino): Allow call with size=0. It should block until any |
michael@0 | 368 | # decompressed data is available. |
michael@0 | 369 | if size <= 0: |
michael@0 | 370 | raise Exception('Non-positive size passed') |
michael@0 | 371 | while True: |
michael@0 | 372 | data = self._inflater.decompress(size) |
michael@0 | 373 | if len(data) != 0: |
michael@0 | 374 | return data |
michael@0 | 375 | |
michael@0 | 376 | read_data = self._socket.recv(DeflateSocket._RECV_SIZE) |
michael@0 | 377 | if not read_data: |
michael@0 | 378 | return '' |
michael@0 | 379 | self._inflater.append(read_data) |
michael@0 | 380 | |
michael@0 | 381 | def sendall(self, bytes): |
michael@0 | 382 | self.send(bytes) |
michael@0 | 383 | |
michael@0 | 384 | def send(self, bytes): |
michael@0 | 385 | self._socket.sendall(self._deflater.compress_and_flush(bytes)) |
michael@0 | 386 | return len(bytes) |
michael@0 | 387 | |
michael@0 | 388 | |
michael@0 | 389 | class DeflateConnection(object): |
michael@0 | 390 | """A wrapper class for request object to intercept write and read to |
michael@0 | 391 | perform deflate compression and decompression transparently. |
michael@0 | 392 | """ |
michael@0 | 393 | |
michael@0 | 394 | def __init__(self, connection): |
michael@0 | 395 | self._connection = connection |
michael@0 | 396 | |
michael@0 | 397 | self._logger = get_class_logger(self) |
michael@0 | 398 | |
michael@0 | 399 | self._deflater = _Deflater(zlib.MAX_WBITS) |
michael@0 | 400 | self._inflater = _Inflater() |
michael@0 | 401 | |
michael@0 | 402 | def get_remote_addr(self): |
michael@0 | 403 | return self._connection.remote_addr |
michael@0 | 404 | remote_addr = property(get_remote_addr) |
michael@0 | 405 | |
michael@0 | 406 | def put_bytes(self, bytes): |
michael@0 | 407 | self.write(bytes) |
michael@0 | 408 | |
michael@0 | 409 | def read(self, size=-1): |
michael@0 | 410 | """Reads at most size bytes. Blocks until there's at least one byte |
michael@0 | 411 | available. |
michael@0 | 412 | """ |
michael@0 | 413 | |
michael@0 | 414 | # TODO(tyoshino): Allow call with size=0. |
michael@0 | 415 | if not (size == -1 or size > 0): |
michael@0 | 416 | raise Exception('size must be -1 or positive') |
michael@0 | 417 | |
michael@0 | 418 | data = '' |
michael@0 | 419 | while True: |
michael@0 | 420 | if size == -1: |
michael@0 | 421 | data += self._inflater.decompress(-1) |
michael@0 | 422 | else: |
michael@0 | 423 | data += self._inflater.decompress(size - len(data)) |
michael@0 | 424 | |
michael@0 | 425 | if size >= 0 and len(data) != 0: |
michael@0 | 426 | break |
michael@0 | 427 | |
michael@0 | 428 | # TODO(tyoshino): Make this read efficient by some workaround. |
michael@0 | 429 | # |
michael@0 | 430 | # In 3.0.3 and prior of mod_python, read blocks until length bytes |
michael@0 | 431 | # was read. We don't know the exact size to read while using |
michael@0 | 432 | # deflate, so read byte-by-byte. |
michael@0 | 433 | # |
michael@0 | 434 | # _StandaloneRequest.read that ultimately performs |
michael@0 | 435 | # socket._fileobject.read also blocks until length bytes was read |
michael@0 | 436 | read_data = self._connection.read(1) |
michael@0 | 437 | if not read_data: |
michael@0 | 438 | break |
michael@0 | 439 | self._inflater.append(read_data) |
michael@0 | 440 | return data |
michael@0 | 441 | |
michael@0 | 442 | def write(self, bytes): |
michael@0 | 443 | self._connection.write(self._deflater.compress_and_flush(bytes)) |
michael@0 | 444 | |
michael@0 | 445 | |
michael@0 | 446 | def _is_ewouldblock_errno(error_number): |
michael@0 | 447 | """Returns True iff error_number indicates that receive operation would |
michael@0 | 448 | block. To make this portable, we check availability of errno and then |
michael@0 | 449 | compare them. |
michael@0 | 450 | """ |
michael@0 | 451 | |
michael@0 | 452 | for error_name in ['WSAEWOULDBLOCK', 'EWOULDBLOCK', 'EAGAIN']: |
michael@0 | 453 | if (error_name in dir(errno) and |
michael@0 | 454 | error_number == getattr(errno, error_name)): |
michael@0 | 455 | return True |
michael@0 | 456 | return False |
michael@0 | 457 | |
michael@0 | 458 | |
michael@0 | 459 | def drain_received_data(raw_socket): |
michael@0 | 460 | # Set the socket non-blocking. |
michael@0 | 461 | original_timeout = raw_socket.gettimeout() |
michael@0 | 462 | raw_socket.settimeout(0.0) |
michael@0 | 463 | |
michael@0 | 464 | drained_data = [] |
michael@0 | 465 | |
michael@0 | 466 | # Drain until the socket is closed or no data is immediately |
michael@0 | 467 | # available for read. |
michael@0 | 468 | while True: |
michael@0 | 469 | try: |
michael@0 | 470 | data = raw_socket.recv(1) |
michael@0 | 471 | if not data: |
michael@0 | 472 | break |
michael@0 | 473 | drained_data.append(data) |
michael@0 | 474 | except socket.error, e: |
michael@0 | 475 | # e can be either a pair (errno, string) or just a string (or |
michael@0 | 476 | # something else) telling what went wrong. We suppress only |
michael@0 | 477 | # the errors that indicates that the socket blocks. Those |
michael@0 | 478 | # exceptions can be parsed as a pair (errno, string). |
michael@0 | 479 | try: |
michael@0 | 480 | error_number, message = e |
michael@0 | 481 | except: |
michael@0 | 482 | # Failed to parse socket.error. |
michael@0 | 483 | raise e |
michael@0 | 484 | |
michael@0 | 485 | if _is_ewouldblock_errno(error_number): |
michael@0 | 486 | break |
michael@0 | 487 | else: |
michael@0 | 488 | raise e |
michael@0 | 489 | |
michael@0 | 490 | # Rollback timeout value. |
michael@0 | 491 | raw_socket.settimeout(original_timeout) |
michael@0 | 492 | |
michael@0 | 493 | return ''.join(drained_data) |
michael@0 | 494 | |
michael@0 | 495 | |
michael@0 | 496 | # vi:sts=4 sw=4 et |