-
Notifications
You must be signed in to change notification settings - Fork 196
Add connection/read timeout for requests adapter #342
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this! I've left a note inline. It'd also be really useful to actually add tests for the timeout functionality that we're implicitly also adding to our lower-level classes.
hyper/contrib.py
Outdated
@@ -77,7 +77,8 @@ 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, | |||
**kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't be sending arbitrary kwargs from Requests to hyper: there's no reason to assume that those kwargs will match in any sensible way. We should aim to be explicit here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I had change to explicit timeout and add more timeout tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this! I was going to work on timeouts this week but you have started earlier :)
I have one more note on this: the _create_tunnel
function of http11 module should respect the timeout
too.
PS: Related issue: #187
hyper/http11/connection.py
Outdated
@@ -150,6 +150,15 @@ def __init__(self, host, port=None, secure=None, ssl_context=None, | |||
#: the standard hyper parsing interface. | |||
self.parser = Parser() | |||
|
|||
# timeout | |||
timeout = kwargs.get('timeout') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout
should be optional. I think it should be None
- that means no timeout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, timeout is optional and default None, is equivalent to socket.setblocking(1)
timeout is optional, the default value is None, means that it will block connection/read, it is the same as request/adapters.py:
it also can set timeout to socket._GLOBAL_DEFAULT_TIMEOUT |
hyper/contrib.py
Outdated
@@ -92,13 +93,15 @@ def send(self, request, stream=False, cert=None, verify=True, proxies=None, | |||
proxy = prepend_scheme_if_needed(proxy, 'http') | |||
|
|||
parsed = urlparse(request.url) | |||
timeout = kwargs.get('timeout') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just add timeout
to our list of kwargs.
test/test_integration.py
Outdated
conn = self.get_connection() | ||
try: | ||
conn.connect() | ||
assert False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be pytest.fail
, and generally should go in an else
block.
test/test_integration.py
Outdated
|
||
# Wait for request | ||
req_event.wait(5) | ||
# Now, send the headers for the response. This response has no body |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is misleading.
test/test_integration.py
Outdated
time.sleep(1) | ||
|
||
# Now, send the headers for the response. This response has no body | ||
f = build_headers_frame( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We never actually need to send this, and doing so could cause exceptions, so let's not send any data here.
test/test_integration.py
Outdated
|
||
try: | ||
conn.get_response() | ||
assert False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same note about pytest.fail
and else
blocks.
test/test_integration.py
Outdated
|
||
send_event.wait(5) | ||
|
||
assert data[0].startswith(b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can this possibly pass? If the connection fails, we're never going to see this data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default timeout None
will block connect, so it will connect sucess and go on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, I see that. Thanks!
test/test_integration.py
Outdated
|
||
def socket_handler(listener): | ||
time.sleep(1) | ||
sock = listener.accept()[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No point calling accept
: it'll usually fail, and throw exceptions, which we don't want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if connection timeout smaller than 1s, it will throw timeout exception, in this test connection timeout is set to 0.5, so it will throw exception as expect
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but this exception is not caught, meaning it'll be logged and generally treated badly. We shouldn't do anything in the background thread that we know will fail, and accept
will fail here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it, I just updated
@@ -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): |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok,I just added
test/test_http11.py
Outdated
|
||
assert c._connect_timeout == 5 | ||
assert c._read_timeout == 60 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need these tests for HTTP/2 as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok,I add this in test/test_hyper.py
test/test_integration.py
Outdated
|
||
def socket_handler(listener): | ||
time.sleep(1) | ||
sock = listener.accept()[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but this exception is not caught, meaning it'll be logged and generally treated badly. We shouldn't do anything in the background thread that we know will fail, and accept
will fail here.
test/test_integration.py
Outdated
# assert 'timed out' in e.message | ||
pass | ||
else: | ||
assert False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest.fail
, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, i'm not familiar with pytest before, i replace it with pytest.raises or pytest.fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest.raises
is the best thing to do if you don't plan to do anything in the except
block.
test/test_integration.py
Outdated
except (SocketTimeout, ssl.SSLError): | ||
# Py2 raises this as a BaseSSLError, | ||
# Py3 raises it as socket timeout. | ||
# assert 'timed out' in e.message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably we don't need the assert
statement in the comment?
test/test_integration.py
Outdated
# assert 'timed out' in e.message | ||
pass | ||
else: | ||
assert False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest.fail
, please.
test/test_integration.py
Outdated
except (SocketTimeout, ssl.SSLError): | ||
# Py2 raises this as a BaseSSLError, | ||
# Py3 raises it as socket timeout. | ||
assert False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest.fail
, please.
test/test_integration.py
Outdated
|
||
send_event.wait(5) | ||
|
||
assert data[0].startswith(b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, I see that. Thanks!
hyper/common/connection.py
Outdated
@@ -78,8 +79,10 @@ def __init__(self, | |||
self._h1_kwargs.update(kwargs) | |||
self._h2_kwargs.update(kwargs) | |||
|
|||
self._timeout = timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should go into _h1_kwargs
and _h2_kwargs
: it'll also need associated tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, it's done
update information: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nicely done, thank you!
[+] Add connection/read timeout for requests adapter, we can use like this: