Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Add connection/read timeout for requests adapter #342

Merged
merged 9 commits into from
Jul 27, 2017
7 changes: 5 additions & 2 deletions hyper/common/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,23 @@ def __init__(self,
proxy_host=None,
proxy_port=None,
proxy_headers=None,
timeout=None,
**kwargs):

self._host = host
self._port = port
self._h1_kwargs = {
'secure': secure, 'ssl_context': ssl_context,
'proxy_host': proxy_host, 'proxy_port': proxy_port,
'proxy_headers': proxy_headers, 'enable_push': enable_push
'proxy_headers': proxy_headers, 'enable_push': enable_push,
'timeout': timeout
}
self._h2_kwargs = {
'window_manager': window_manager, 'enable_push': enable_push,
'secure': secure, 'ssl_context': ssl_context,
'proxy_host': proxy_host, 'proxy_port': proxy_port,
'proxy_headers': proxy_headers
'proxy_headers': proxy_headers,
'timeout': timeout
}

# Add any unexpected kwargs to both dictionaries.
Expand Down
10 changes: 6 additions & 4 deletions hyper/contrib.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, *args, **kwargs):
self.connections = {}

def get_connection(self, host, port, scheme, cert=None, verify=True,
proxy=None):
proxy=None, timeout=None):
"""
Gets an appropriate HTTP/2 connection object based on
host/port/scheme/cert tuples.
Expand Down Expand Up @@ -77,13 +77,14 @@ def get_connection(self, host, port, scheme, cert=None, verify=True,
secure=secure,
ssl_context=ssl_context,
proxy_host=proxy_netloc,
proxy_headers=proxy_headers)
proxy_headers=proxy_headers,
timeout=timeout)
self.connections[connection_key] = conn

return conn

def send(self, request, stream=False, cert=None, verify=True, proxies=None,
**kwargs):
timeout=None, **kwargs):
"""
Sends a HTTP message to the server.
"""
Expand All @@ -98,7 +99,8 @@ def send(self, request, stream=False, cert=None, verify=True, proxies=None,
parsed.scheme,
cert=cert,
verify=verify,
proxy=proxy)
proxy=proxy,
timeout=timeout)

# Build the selector.
selector = parsed.path
Expand Down
27 changes: 21 additions & 6 deletions hyper/http11/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@


def _create_tunnel(proxy_host, proxy_port, target_host, target_port,
proxy_headers=None):
proxy_headers=None, timeout=None):
"""
Sends CONNECT method to a proxy and returns a socket with established
connection to the target.

:returns: socket
"""
conn = HTTP11Connection(proxy_host, proxy_port)
conn = HTTP11Connection(proxy_host, proxy_port, timeout=timeout)
conn.request('CONNECT', '%s:%d' % (target_host, target_port),
headers=proxy_headers)

Expand Down Expand Up @@ -101,7 +101,7 @@ class HTTP11Connection(object):

def __init__(self, host, port=None, secure=None, ssl_context=None,
proxy_host=None, proxy_port=None, proxy_headers=None,
**kwargs):
timeout=None, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add this to the common HTTPConnection object as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok,I just added

if port is None:
self.host, self.port = to_host_port_tuple(host, default_port=80)
else:
Expand Down Expand Up @@ -150,6 +150,9 @@ def __init__(self, host, port=None, secure=None, ssl_context=None,
#: the standard hyper parsing interface.
self.parser = Parser()

# timeout
self._timeout = timeout

def connect(self):
"""
Connect to the server specified when the object was created. This is a
Expand All @@ -159,23 +162,32 @@ def connect(self):
"""
if self._sock is None:

if isinstance(self._timeout, tuple):
connect_timeout = self._timeout[0]
read_timeout = self._timeout[1]
else:
connect_timeout = self._timeout
read_timeout = self._timeout

if self.proxy_host and self.secure:
# Send http CONNECT method to a proxy and acquire the socket
sock = _create_tunnel(
self.proxy_host,
self.proxy_port,
self.host,
self.port,
proxy_headers=self.proxy_headers
proxy_headers=self.proxy_headers,
timeout=self._timeout
)
elif self.proxy_host:
# Simple http proxy
sock = socket.create_connection(
(self.proxy_host, self.proxy_port),
5
timeout=connect_timeout
)
else:
sock = socket.create_connection((self.host, self.port), 5)
sock = socket.create_connection((self.host, self.port),
timeout=connect_timeout)
proto = None

if self.secure:
Expand All @@ -184,6 +196,9 @@ def connect(self):
log.debug("Selected protocol: %s", proto)
sock = BufferedSocket(sock, self.network_buffer_size)

# Set read timeout
sock.settimeout(read_timeout)

if proto not in ('http/1.1', None):
raise TLSUpgrade(proto, sock)

Expand Down
24 changes: 20 additions & 4 deletions hyper/http20/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class HTTP20Connection(object):
def __init__(self, host, port=None, secure=None, window_manager=None,
enable_push=False, ssl_context=None, proxy_host=None,
proxy_port=None, force_proto=None, proxy_headers=None,
**kwargs):
timeout=None, **kwargs):
"""
Creates an HTTP/2 connection to a specific server.
"""
Expand Down Expand Up @@ -151,6 +151,9 @@ def __init__(self, host, port=None, secure=None, window_manager=None,
self.__wm_class = window_manager or FlowControlManager
self.__init_state()

# timeout
self._timeout = timeout

return

def __init_state(self):
Expand Down Expand Up @@ -343,22 +346,32 @@ def connect(self):
if self._sock is not None:
return

if isinstance(self._timeout, tuple):
connect_timeout = self._timeout[0]
read_timeout = self._timeout[1]
else:
connect_timeout = self._timeout
read_timeout = self._timeout

if self.proxy_host and self.secure:
# Send http CONNECT method to a proxy and acquire the socket
sock = _create_tunnel(
self.proxy_host,
self.proxy_port,
self.host,
self.port,
proxy_headers=self.proxy_headers
proxy_headers=self.proxy_headers,
timeout=self._timeout
)
elif self.proxy_host:
# Simple http proxy
sock = socket.create_connection(
(self.proxy_host, self.proxy_port)
(self.proxy_host, self.proxy_port),
timeout=connect_timeout
)
else:
sock = socket.create_connection((self.host, self.port))
sock = socket.create_connection((self.host, self.port),
timeout=connect_timeout)

if self.secure:
sock, proto = wrap_socket(sock, self.host, self.ssl_context,
Expand All @@ -374,6 +387,9 @@ def connect(self):

self._sock = BufferedSocket(sock, self.network_buffer_size)

# Set read timeout
self._sock.settimeout(read_timeout)

self._send_preamble()

def _connect_upgrade(self, sock):
Expand Down
15 changes: 10 additions & 5 deletions test/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ class SocketLevelTest(object):
A test-class that defines a few helper methods for running socket-level
tests.
"""
def set_up(self, secure=True, proxy=False):
def set_up(self, secure=True, proxy=False, timeout=None):
self.host = None
self.port = None
self.socket_security = SocketSecuritySetting(secure)
self.proxy = proxy
self.server_thread = None
self.timeout = timeout

def _start_server(self, socket_handler):
"""
Expand Down Expand Up @@ -146,18 +147,22 @@ def secure(self, value):
def get_connection(self):
if self.h2:
if not self.proxy:
return HTTP20Connection(self.host, self.port, self.secure)
return HTTP20Connection(self.host, self.port, self.secure,
timeout=self.timeout)
else:
return HTTP20Connection('http2bin.org', secure=self.secure,
proxy_host=self.host,
proxy_port=self.port)
proxy_port=self.port,
timeout=self.timeout)
else:
if not self.proxy:
return HTTP11Connection(self.host, self.port, self.secure)
return HTTP11Connection(self.host, self.port, self.secure,
timeout=self.timeout)
else:
return HTTP11Connection('httpbin.org', secure=self.secure,
proxy_host=self.host,
proxy_port=self.port)
proxy_port=self.port,
timeout=self.timeout)

def get_encoder(self):
"""
Expand Down
6 changes: 4 additions & 2 deletions test/test_abstraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_h1_kwargs(self):
c = HTTPConnection(
'test', 443, secure=False, window_manager=True, enable_push=True,
ssl_context=False, proxy_host=False, proxy_port=False,
proxy_headers=False, other_kwarg=True
proxy_headers=False, other_kwarg=True, timeout=5
)

assert c._h1_kwargs == {
Expand All @@ -21,13 +21,14 @@ def test_h1_kwargs(self):
'proxy_headers': False,
'other_kwarg': True,
'enable_push': True,
'timeout': 5,
}

def test_h2_kwargs(self):
c = HTTPConnection(
'test', 443, secure=False, window_manager=True, enable_push=True,
ssl_context=True, proxy_host=False, proxy_port=False,
proxy_headers=False, other_kwarg=True
proxy_headers=False, other_kwarg=True, timeout=(10, 30)
)

assert c._h2_kwargs == {
Expand All @@ -39,6 +40,7 @@ def test_h2_kwargs(self):
'proxy_port': False,
'proxy_headers': False,
'other_kwarg': True,
'timeout': (10, 30),
}

def test_tls_upgrade(self, monkeypatch):
Expand Down
10 changes: 10 additions & 0 deletions test/test_http11.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ def test_initialization_with_ipv6_addresses_proxy_inline_port(self):
assert c.proxy_host == 'ffff:aaaa::1'
assert c.proxy_port == 8443

def test_initialization_timeout(self):
c = HTTP11Connection('httpbin.org', timeout=30)

assert c._timeout == 30

def test_initialization_tuple_timeout(self):
c = HTTP11Connection('httpbin.org', timeout=(5, 60))

assert c._timeout == (5, 60)

def test_basic_request(self):
c = HTTP11Connection('httpbin.org')
c._sock = sock = DummySocket()
Expand Down
10 changes: 10 additions & 0 deletions test/test_hyper.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ def test_connection_version(self):
c = HTTP20Connection('www.google.com')
assert c.version is HTTPVersion.http20

def test_connection_timeout(self):
c = HTTP20Connection('httpbin.org', timeout=30)

assert c._timeout == 30

def test_connection_tuple_timeout(self):
c = HTTP20Connection('httpbin.org', timeout=(5, 60))

assert c._timeout == (5, 60)

def test_ping(self, frame_buffer):
def data_callback(chunk, **kwargs):
frame_buffer.add_data(chunk)
Expand Down
Loading