diff --git a/src/hip/_connection.py b/src/hip/_connection.py index f8f4f4c..5e36a47 100644 --- a/src/hip/_connection.py +++ b/src/hip/_connection.py @@ -19,7 +19,6 @@ "Connection", "ResponseStream", "Transport", - "open_connection", ] @@ -80,11 +79,30 @@ def stream( class Connection(Transport): - def __init__(self, stream: Stream, origin: URL | str): - self._stream = stream + def __init__(self, origin: URL | str, stream: Stream | None = None): + self._stream = Stream() if (stream is None) else stream self._origin = URL(origin) if not isinstance(origin, URL) else origin self._request_lock = Lock() - self._parser = HTTPParser(stream, mode="CLIENT") + self._parser = HTTPParser(self._stream, mode="CLIENT") + + # Ensure valid URL + if self._origin.scheme not in ("http", "https"): + raise ValueError( + f"Invalid URL. Expected http:// or https://, got {self._origin.scheme}://" + ) + + def open(self, backend: NetworkBackend | None = None) -> None: + backend = NetworkBackend() if (backend is None) else backend + + scheme = self._origin.scheme + host = self._origin.host + port = self._origin.port or {"http": 80, "https": 443}[scheme] + + if scheme == "https": + self._stream = backend.connect_tls(host, port) + else: + self._stream = backend.connect(host, port) + self._parser = HTTPParser(self._stream, mode="CLIENT") # API for connection pool management... def origin(self) -> URL: @@ -180,6 +198,7 @@ def __repr__(self) -> str: return f"" def __enter__(self) -> "Self": + self.open() return self def __exit__( @@ -189,28 +208,3 @@ def __exit__( traceback: types.TracebackType | None = None, ): self.close() - - -def open_connection( - url: URL | str, - hostname: str = "", - backend: NetworkBackend | None = None, -) -> Connection: - - if isinstance(url, str): - url = URL(url) - - if url.scheme not in ("http", "https"): - raise ValueError("URL scheme must be 'http://' or 'https://'.") - if backend is None: - backend = NetworkBackend() - - host = url.host - port = url.port or {"http": 80, "https": 443}[url.scheme] - - if url.scheme == "https": - stream = backend.connect_tls(host, port, hostname) - else: - stream = backend.connect(host, port) - - return Connection(stream, url) diff --git a/src/hip/_pool.py b/src/hip/_pool.py index 812056c..0527a2c 100644 --- a/src/hip/_pool.py +++ b/src/hip/_pool.py @@ -6,7 +6,7 @@ # Once we drop 3.10 support, we can use 'typing.Self'. from typing_extensions import Self -from ._connection import Connection, ResponseStream, Transport, open_connection +from ._connection import Connection, ResponseStream, Transport from ._request import Request from ._urls import URL from .backends.sync import NetworkBackend, Semaphore @@ -53,9 +53,8 @@ def _get_connection(self, request: Request) -> "Connection": return conn # Or else create a new connection. - conn = open_connection( - origin, hostname=request.headers["Host"], backend=self._network_backend - ) + conn = Connection(origin) + conn.open(backend=self._network_backend) self._connections.append(conn) return conn diff --git a/src/hip/transports.py b/src/hip/transports.py index e1e24a4..4ec7c0d 100644 --- a/src/hip/transports.py +++ b/src/hip/transports.py @@ -1,4 +1,4 @@ -from ._connection import Connection, ResponseStream, Transport, open_connection +from ._connection import Connection, ResponseStream, Transport from ._pool import ConnectionPool __all__ = [ @@ -6,5 +6,4 @@ "ConnectionPool", "ResponseStream", "Transport", - "open_connection", ] diff --git a/tests/test_pool.py b/tests/test_pool.py index b48f71e..b3978a1 100644 --- a/tests/test_pool.py +++ b/tests/test_pool.py @@ -80,7 +80,7 @@ def test_connection_pool_should_have_managed_lifespan(server): def test_connection_request(server): - with hip.transports.open_connection(server.url) as conn: + with hip.transports.Connection(server.url) as conn: assert repr(conn) == f"" r = conn.request("GET", "/") @@ -90,7 +90,7 @@ def test_connection_request(server): def test_connection_stream(server): - with hip.transports.open_connection(server.url) as conn: + with hip.transports.Connection(server.url) as conn: assert repr(conn) == f"" with conn.stream("GET", "/") as r: assert r.status_code == 200 @@ -101,7 +101,7 @@ def test_connection_stream(server): def test_connection_stream_read_in_chunks(server): with ( - hip.transports.open_connection(server.url) as conn, + hip.transports.Connection(server.url) as conn, conn.stream("GET", "/") as response, ): assert response.read(5) == b"Hello" @@ -110,19 +110,17 @@ def test_connection_stream_read_in_chunks(server): class TLSBackend: - def connect_tls(self, host, port, hostname): - self.connect_tls_args = (host, port, hostname) + def connect_tls(self, host, port): + self.connect_tls_args = (host, port) return hip.Binary(b"").open() def test_open_https_connection(): backend = TLSBackend() - with hip.transports.open_connection( - "https://example.com", - hostname="example.com", - backend=backend, - ): - pass - - assert backend.connect_tls_args == ("example.com", 443, "example.com") + conn = hip.transports.Connection("https://example.com") + conn.open(backend=backend) + try: + assert backend.connect_tls_args == ("example.com", 443) + finally: + conn.close()