diff options
author | Andy McCurdy <andy@andymccurdy.com> | 2019-07-30 15:14:32 -0700 |
---|---|---|
committer | Andy McCurdy <andy@andymccurdy.com> | 2019-07-30 15:14:32 -0700 |
commit | 885ce770856a1ee5ebc51d7c5390c88fb62bc93e (patch) | |
tree | ea1f83c8382f289f96cdf7007c891d2835ff0699 | |
parent | 6b6e394ccb7199cb5e0d2e8192dc904aa0a8b347 (diff) | |
download | redis-py-885ce770856a1ee5ebc51d7c5390c88fb62bc93e.tar.gz |
version 3.3.4, more specifically identify nonblocking read errors
versions 3.3.1, 3.3.2 and 3.3.3 could potentially hide ConnectionErrors
on Python 2.7. This change accurately identifies errors by both
exception class and errno to determine whether a nonblocking socket can
be read
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | redis/__init__.py | 2 | ||||
-rwxr-xr-x | redis/connection.py | 31 |
3 files changed, 22 insertions, 15 deletions
@@ -1,3 +1,7 @@ +* 3.3.4 + * More specifically identify nonblocking read errors for both SSL and + non-SSL connections. 3.3.1, 3.3.2 and 3.3.3 on Python 2.7 could + potentially mask a ConnectionError. #1197 * 3.3.3 * The SSL module in Python < 2.7.9 handles non-blocking sockets differently than 2.7.9+. This patch accommodates older versions. #1197 diff --git a/redis/__init__.py b/redis/__init__.py index 2d586c6..71bc217 100644 --- a/redis/__init__.py +++ b/redis/__init__.py @@ -29,7 +29,7 @@ def int_or_str(value): return value -__version__ = '3.3.3' +__version__ = '3.3.4' VERSION = tuple(map(int_or_str, __version__.split('.'))) __all__ = [ diff --git a/redis/connection.py b/redis/connection.py index e753bcb..8b87ead 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -34,17 +34,18 @@ try: except ImportError: ssl_available = False -if ssl_available and hasattr(ssl, 'SSLWantReadError'): - # note that when using nonblocking sockets over ssl, the ssl module - # in python > 2.7.9 raises its own exceptions rather than BlockingIOError - blocking_exceptions = ( - BlockingIOError, - ssl.SSLWantReadError, - ssl.SSLWantWriteError - ) -else: - blocking_exceptions = (BlockingIOError,) +NONBLOCKING_EXCEPTION_ERROR_NUMBERS = { + BlockingIOError: 35, +} + +if ssl_available: + if hasattr(ssl, 'SSLWantReadError'): + NONBLOCKING_EXCEPTION_ERROR_NUMBERS[ssl.SSLWantReadError] = 2 + NONBLOCKING_EXCEPTION_ERROR_NUMBERS[ssl.SSLWantWriteError] = 2 + else: + NONBLOCKING_EXCEPTION_ERROR_NUMBERS[ssl.SSLError] = 2 +NONBLOCKING_EXCEPTIONS = tuple(NONBLOCKING_EXCEPTION_ERROR_NUMBERS.keys()) if HIREDIS_AVAILABLE: import hiredis @@ -180,12 +181,13 @@ class SocketBuffer(object): if length is not None and length > marker: continue return True - except blocking_exceptions as ex: + except NONBLOCKING_EXCEPTIONS as ex: # if we're in nonblocking mode and the recv raises a # blocking error, simply return False indicating that # there's no data to be read. otherwise raise the # original exception. - if raise_on_timeout: + allowed_errno = NONBLOCKING_EXCEPTION_ERROR_NUMBERS[ex.__class__] + if raise_on_timeout or ex.errno != allowed_errno: raise return False except socket.timeout: @@ -409,12 +411,13 @@ class HiredisParser(BaseParser): # data was read from the socket and added to the buffer. # return True to indicate that data was read. return True - except blocking_exceptions as ex: + except NONBLOCKING_EXCEPTIONS as ex: # if we're in nonblocking mode and the recv raises a # blocking error, simply return False indicating that # there's no data to be read. otherwise raise the # original exception. - if raise_on_timeout: + allowed_errno = NONBLOCKING_EXCEPTION_ERROR_NUMBERS[ex.__class__] + if raise_on_timeout or ex.errno != allowed_errno: raise return False except socket.timeout: |