diff --git a/httpcore/_models.py b/httpcore/_models.py index 8a65f1334..d63ba4ee4 100644 --- a/httpcore/_models.py +++ b/httpcore/_models.py @@ -275,6 +275,10 @@ def __init__( else: self.scheme = enforce_bytes(scheme, name="scheme") self.host = enforce_bytes(host, name="host") + # bool is a subclass of int; reject it so port=True does not become 1. + if port is not None and (isinstance(port, bool) or not isinstance(port, int)): + seen = type(port).__name__ + raise TypeError(f"port must be int or None, but got {seen}.") self.port = port self.target = enforce_bytes(target, name="target") diff --git a/tests/test_models.py b/tests/test_models.py index 7dd6e419d..d63f55445 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -187,3 +187,19 @@ async def test_response_async_streaming(): with pytest.raises(RuntimeError): async for chunk in response.aiter_stream(): pass # pragma: nocover + + +def test_url_rejects_bool_port(): + # isinstance(True, int) is True; without an explicit check, port=True + # becomes port 1 in bytes(url) ("http://host:1/"). + with pytest.raises(TypeError, match="port must be int or None"): + httpcore.URL(scheme=b"http", host=b"www.example.com", port=True, target=b"/") + with pytest.raises(TypeError, match="port must be int or None"): + httpcore.URL(scheme=b"http", host=b"www.example.com", port=False, target=b"/") + + +def test_url_rejects_non_int_port(): + with pytest.raises(TypeError, match="port must be int or None"): + httpcore.URL(scheme=b"http", host=b"www.example.com", port=1.5, target=b"/") # type: ignore + with pytest.raises(TypeError, match="port must be int or None"): + httpcore.URL(scheme=b"http", host=b"www.example.com", port="80", target=b"/") # type: ignore