From 0fce6d3ee4f440abd1512924b776acd69181fbfe Mon Sep 17 00:00:00 2001 From: Florimond Manca Date: Fri, 20 Nov 2020 12:47:26 +0100 Subject: [PATCH] Fix import of HTTP/1.1 connection causing thread-safety bug on 3.9 (#237) * Fix import of HTTP/1.1 connection causing thread-safety bug on 3.9 * Typo --- httpcore/_async/connection.py | 3 +-- httpcore/_sync/connection.py | 3 +-- tests/test_threadsafety.py | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 tests/test_threadsafety.py diff --git a/httpcore/_async/connection.py b/httpcore/_async/connection.py index d3d7b41b..530663d8 100644 --- a/httpcore/_async/connection.py +++ b/httpcore/_async/connection.py @@ -12,6 +12,7 @@ NewConnectionRequired, ) from .http import AsyncBaseHTTPConnection +from .http11 import AsyncHTTP11Connection logger = get_logger(__name__) @@ -150,8 +151,6 @@ def _create_connection(self, socket: AsyncSocketStream) -> None: socket=socket, backend=self.backend, ssl_context=self.ssl_context ) else: - from .http11 import AsyncHTTP11Connection - self.is_http11 = True self.connection = AsyncHTTP11Connection( socket=socket, ssl_context=self.ssl_context diff --git a/httpcore/_sync/connection.py b/httpcore/_sync/connection.py index f10f2aad..04042227 100644 --- a/httpcore/_sync/connection.py +++ b/httpcore/_sync/connection.py @@ -12,6 +12,7 @@ NewConnectionRequired, ) from .http import SyncBaseHTTPConnection +from .http11 import SyncHTTP11Connection logger = get_logger(__name__) @@ -150,8 +151,6 @@ def _create_connection(self, socket: SyncSocketStream) -> None: socket=socket, backend=self.backend, ssl_context=self.ssl_context ) else: - from .http11 import SyncHTTP11Connection - self.is_http11 = True self.connection = SyncHTTP11Connection( socket=socket, ssl_context=self.ssl_context diff --git a/tests/test_threadsafety.py b/tests/test_threadsafety.py new file mode 100644 index 00000000..81cdd95f --- /dev/null +++ b/tests/test_threadsafety.py @@ -0,0 +1,46 @@ +import concurrent.futures + +import pytest + +import httpcore + +from .utils import Server + + +def read_body(stream: httpcore.SyncByteStream) -> bytes: + try: + return b"".join(chunk for chunk in stream) + finally: + stream.close() + + +@pytest.mark.parametrize( + "http2", [pytest.param(False, id="h11"), pytest.param(True, id="h2")] +) +def test_threadsafe_basic(server: Server, http2: bool) -> None: + """ + The sync connection pool can be used to perform requests concurrently using + threads. + + Also a regression test for: https://github.com/encode/httpx/issues/1393 + """ + with httpcore.SyncConnectionPool(http2=http2) as http: + + def request(http: httpcore.SyncHTTPTransport) -> int: + method = b"GET" + url = (b"http", *server.netloc, b"/") + headers = [server.host_header] + status_code, headers, stream, ext = http.request(method, url, headers) + read_body(stream) + return status_code + + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: + futures = [executor.submit(request, http) for _ in range(10)] + num_results = 0 + + for future in concurrent.futures.as_completed(futures): + status_code = future.result() + assert status_code == 200 + num_results += 1 + + assert num_results == 10