import socket
import sys
import typing
import httpx
import pytest
from .native_fixtures import local_http_server
class TestSocketOptionRepresentation:
def test_single_option_accepted(self):
transport = httpx.HTTPTransport(
socket_options=[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
)
assert transport is not None
def test_multiple_options_accepted(self):
opts = [
(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
]
transport = httpx.HTTPTransport(socket_options=opts)
assert transport is not None
def test_empty_list_accepted(self):
transport = httpx.HTTPTransport(socket_options=[])
assert transport is not None
def test_none_accepted(self):
transport = httpx.HTTPTransport(socket_options=None)
assert transport is not None
@pytest.mark.skipif(
sys.platform == "win32", reason="TCP_NODELAY constant differs on Windows"
)
def test_option_with_bytes_value(self):
value = (0).to_bytes(4, byteorder="little")
transport = httpx.HTTPTransport(
socket_options=[(socket.IPPROTO_TCP, socket.TCP_NODELAY, value)]
)
assert transport is not None
@pytest.mark.skipif(
sys.platform == "win32", reason="TCP_NODELAY constant differs on Windows"
)
def test_option_with_int_value(self):
transport = httpx.HTTPTransport(
socket_options=[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
)
assert transport is not None
def test_large_unsigned_int_value_is_preserved(self):
from eggfetch.compat.httpx._client import _convert_socket_option
value = 2**31
_, _, converted = _convert_socket_option(
(socket.SOL_SOCKET, socket.SO_RCVBUF, value)
)
assert converted == value.to_bytes(4, byteorder=sys.byteorder, signed=False)
def test_valid_four_tuple_is_accepted_until_socket_use(self):
transport = httpx.HTTPTransport(
socket_options=[(socket.SOL_SOCKET, socket.SO_KEEPALIVE, None, 0)]
)
with local_http_server() as (host, port):
with httpx.Client(transport=transport, trust_env=False, timeout=2) as client:
with pytest.raises(httpx.ConnectError):
client.get(f"http://{host}:{port}/health")
def test_four_tuple_with_bytearray_is_accepted(self):
transport = httpx.HTTPTransport(
socket_options=[
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, bytearray(b"\x01\x00\x00\x00"))
]
)
assert transport is not None
@pytest.mark.parametrize(
"value",
[
(socket.SOL_SOCKET, socket.SO_KEEPALIVE),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1, 0, 0),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1, 0),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, object()),
],
)
def test_invalid_socket_option_shapes_are_rejected_at_use_or_construction(self, value):
try:
transport = httpx.HTTPTransport(socket_options=[value])
except (TypeError, ValueError):
return
with local_http_server() as (host, port):
with httpx.Client(transport=transport, trust_env=False, timeout=2) as client:
with pytest.raises((TypeError, ValueError, OSError)):
client.get(f"http://{host}:{port}/health")
class TestLocalAddress:
def test_ipv4_loopback_accepted(self):
transport = httpx.HTTPTransport(local_address="127.0.0.1")
assert transport is not None
def test_none_accepted(self):
transport = httpx.HTTPTransport(local_address=None)
assert transport is not None
class TestUDS:
def test_uds_path_accepted(self):
transport = httpx.HTTPTransport(uds="/tmp/test.sock")
assert transport is not None
def test_none_accepted(self):
transport = httpx.HTTPTransport(uds=None)
assert transport is not None
class TestAsyncTransportOptions:
def test_async_socket_options_accepted(self):
opts = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
transport = httpx.AsyncHTTPTransport(socket_options=opts)
assert transport is not None
def test_async_local_address_accepted(self):
transport = httpx.AsyncHTTPTransport(local_address="127.0.0.1")
assert transport is not None
def test_async_uds_accepted(self):
transport = httpx.AsyncHTTPTransport(uds="/tmp/test.sock")
assert transport is not None