summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Petrov <andrey.petrov@shazow.net>2014-06-25 15:12:19 -0700
committerAndrey Petrov <andrey.petrov@shazow.net>2014-06-25 15:12:19 -0700
commit24d60ad23cd03d05aa9faa9f91c735fd1dc8b720 (patch)
tree55d3647eee0b4037c76a13060674ee479800b2b5
parent9b4670c861a2a78ae7feb5bd6a1794ab63857069 (diff)
downloadurllib3-24d60ad23cd03d05aa9faa9f91c735fd1dc8b720.tar.gz
Handle SSL read timeouts as ReadTimeoutError.
-rw-r--r--.coveragerc1
-rw-r--r--test/with_dummyserver/test_socketlevel.py61
-rw-r--r--urllib3/response.py11
3 files changed, 62 insertions, 11 deletions
diff --git a/.coveragerc b/.coveragerc
index 17546433..b106d41d 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -11,3 +11,4 @@ exclude_lines =
pass
.* # Abstract
import
+ raise
diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py
index f13d77cc..1273a3e7 100644
--- a/test/with_dummyserver/test_socketlevel.py
+++ b/test/with_dummyserver/test_socketlevel.py
@@ -125,9 +125,10 @@ class TestSocketClosing(SocketDummyServerTestCase):
self._start_server(socket_handler)
pool = HTTPConnectionPool(self.host, self.port, timeout=0.001)
- self.assertRaises(ReadTimeoutError, pool.request, 'GET', '/', retries=0)
-
- timed_out.set()
+ try:
+ self.assertRaises(ReadTimeoutError, pool.request, 'GET', '/', retries=0)
+ finally:
+ timed_out.set()
def test_timeout_errors_cause_retries(self):
def socket_handler(listener):
@@ -193,8 +194,10 @@ class TestSocketClosing(SocketDummyServerTestCase):
response = pool.urlopen('GET', '/', retries=0, preload_content=False,
timeout=util.Timeout(connect=1, read=0.001))
- self.assertRaises(ReadTimeoutError, response.read)
- timed_out.set()
+ try:
+ self.assertRaises(ReadTimeoutError, response.read)
+ finally:
+ timed_out.set()
def test_incomplete_response(self):
body = 'Response'
@@ -400,11 +403,12 @@ class TestSSL(SocketDummyServerTestCase):
buf += ssl_sock.recv(65536)
# Deliberately send from the non-SSL socket.
- sock2.send(('HTTP/1.1 200 OK\r\n'
- 'Content-Type: text/plain\r\n'
- 'Content-Length: 2\r\n'
- '\r\n'
- 'Hi').encode('utf-8'))
+ sock2.send((
+ 'HTTP/1.1 200 OK\r\n'
+ 'Content-Type: text/plain\r\n'
+ 'Content-Length: 2\r\n'
+ '\r\n'
+ 'Hi').encode('utf-8'))
sock2.close()
ssl_sock.close()
@@ -412,3 +416,40 @@ class TestSSL(SocketDummyServerTestCase):
pool = HTTPSConnectionPool(self.host, self.port)
self.assertRaises(SSLError, pool.request, 'GET', '/', retries=0)
+
+ def test_ssl_read_timeout(self):
+ timed_out = Event()
+
+ def socket_handler(listener):
+ sock = listener.accept()[0]
+ ssl_sock = ssl.wrap_socket(sock,
+ server_side=True,
+ keyfile=DEFAULT_CERTS['keyfile'],
+ certfile=DEFAULT_CERTS['certfile'],
+ ca_certs=DEFAULT_CA)
+
+ buf = b''
+ while not buf.endswith(b'\r\n\r\n'):
+ buf += ssl_sock.recv(65536)
+
+ # Send incomplete message (note Content-Length)
+ ssl_sock.send((
+ 'HTTP/1.1 200 OK\r\n'
+ 'Content-Type: text/plain\r\n'
+ 'Content-Length: 10\r\n'
+ '\r\n'
+ 'Hi-').encode('utf-8'))
+ timed_out.wait()
+
+ sock.close()
+ ssl_sock.close()
+
+ self._start_server(socket_handler)
+ pool = HTTPSConnectionPool(self.host, self.port)
+
+ response = pool.urlopen('GET', '/', retries=0, preload_content=False,
+ timeout=util.Timeout(connect=1, read=0.001))
+ try:
+ self.assertRaises(ReadTimeoutError, response.read)
+ finally:
+ timed_out.set()
diff --git a/urllib3/response.py b/urllib3/response.py
index 82d55c96..f1f86cba 100644
--- a/urllib3/response.py
+++ b/urllib3/response.py
@@ -13,7 +13,7 @@ from ._collections import HTTPHeaderDict
from .exceptions import ConnectionError, DecodeError, ReadTimeoutError
from .packages.six import string_types as basestring, binary_type
from .util import is_fp_closed
-from .connection import HTTPException
+from .connection import HTTPException, BaseSSLError
class DeflateDecoder(object):
@@ -203,6 +203,15 @@ class HTTPResponse(io.IOBase):
# there is yet no clean way to get at it from this context.
raise ReadTimeoutError(self._pool, None, 'Read timed out.')
+ except BaseSSLError as e:
+ # FIXME: Is there a better way to differentiate between SSLErrors?
+ if not 'read operation timed out' in e.message:
+ # This shouldn't happen but just in case we're missing an edge
+ # case, let's avoid swallowing SSL errors.
+ raise
+
+ raise ReadTimeoutError(self._pool, None, 'Read timed out.')
+
except HTTPException as e:
# This includes IncompleteRead.
raise ConnectionError('Connection failed: %r' % e, e)