From d2f49856405f6150adf4ca922075f968a627bf01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Falc=C3=A3o?= Date: Mon, 24 May 2021 19:05:20 +0200 Subject: [PATCH 1/3] respect socket timeout closes #430 --- httpretty/core.py | 10 +++- .../nosetests/test_430_respect_timeout.py | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/bugfixes/nosetests/test_430_respect_timeout.py diff --git a/httpretty/core.py b/httpretty/core.py index 302aa256..edec2fde 100644 --- a/httpretty/core.py +++ b/httpretty/core.py @@ -692,13 +692,20 @@ def makefile(self, mode='r', bufsize=-1): target=self._entry.fill_filekind, args=(self.fd,) ) + # execute body callback and send http response in a + # thread, wait for thread to finish within the timeout + # set via socket.settimeout() t.start() if self.timeout == SOCKET_GLOBAL_DEFAULT_TIMEOUT: timeout = get_default_thread_timeout() else: timeout = self.timeout - t.join(None) + + # fake socket timeout error by checking if the thread + # finished in time. + t.join(timeout) if t.is_alive(): + # For more info check issue https://github.com/gabrielfalcao/HTTPretty/issues/430 raise socket.timeout(timeout) return self.fd @@ -1690,6 +1697,7 @@ def request_callback(request, uri, response_headers): del cls._entries[matcher] cls._entries[matcher] = entries_for_this_uri + return matcher, entries_for_this_uri[:] def __str__(self): return '' % len(self._entries) diff --git a/tests/bugfixes/nosetests/test_430_respect_timeout.py b/tests/bugfixes/nosetests/test_430_respect_timeout.py new file mode 100644 index 00000000..f21a5468 --- /dev/null +++ b/tests/bugfixes/nosetests/test_430_respect_timeout.py @@ -0,0 +1,54 @@ +# This test is based on @mariojonke snippet: +# https://github.com/gabrielfalcao/HTTPretty/issues/430 +import time +from requests import Session +from requests.adapters import HTTPAdapter +from requests.exceptions import ReadTimeout + +from threading import Event + +from httpretty import httprettified +from httpretty import HTTPretty + + +def http(max_connections=1): + session = Session() + adapter = HTTPAdapter( + pool_connections=max_connections, + pool_maxsize=max_connections + ) + session.mount('http://', adapter) + session.mount('https://', adapter) + return session + + + +@httprettified(verbose=True, allow_net_connect=False) +def test_read_timeout(): + "#430 httpretty should respect read timeout" + event = Event() + uri = "http://example.com" + + # Given that I register a uri with a callback body that delays 10 seconds + wait_seconds = 10 + + def my_callback(request, url, headers): + event.wait(wait_seconds) + return 200, headers, "Received" + + HTTPretty.register_uri(HTTPretty.GET, uri, body=my_callback) + + # And I use a thread pool with 1 TCP connection max + max_connections = 1 + request = http(max_connections) + started_at = time.time() + # When I make an HTTP request with a read timeout of 0.1 and an indefinite connect timeout + when_called = request.get.when.called_with(uri, timeout=(None, 0.1)) + + # Then the request should have raised a connection timeout + when_called.should.have.raised(ReadTimeout) + + # And the total execution time should be less than 0.2 seconds + event.set() + total_time = time.time() - started_at + total_time.should.be.lower_than(0.2) From 4cc21cd4806d1e1c44e9266808cb5f6fa949696f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Falc=C3=A3o?= Date: Mon, 24 May 2021 21:19:56 +0200 Subject: [PATCH 2/3] socket timeout default 0.1 seconds --- httpretty/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httpretty/core.py b/httpretty/core.py index edec2fde..59c449f1 100644 --- a/httpretty/core.py +++ b/httpretty/core.py @@ -74,7 +74,7 @@ from errno import EAGAIN class __internals__: - thread_timeout = 0 # https://github.com/gabrielfalcao/HTTPretty/issues/426 + thread_timeout = 0.1 # https://github.com/gabrielfalcao/HTTPretty/issues/430 temp_files = [] threads = [] From da624d404205b93c2f7515e35fd1481db2089702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Falc=C3=A3o?= Date: Mon, 24 May 2021 21:22:54 +0200 Subject: [PATCH 3/3] remove return --- httpretty/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/httpretty/core.py b/httpretty/core.py index 59c449f1..c73700cd 100644 --- a/httpretty/core.py +++ b/httpretty/core.py @@ -1697,7 +1697,6 @@ def request_callback(request, uri, response_headers): del cls._entries[matcher] cls._entries[matcher] = entries_for_this_uri - return matcher, entries_for_this_uri[:] def __str__(self): return '' % len(self._entries)