Compare commits

...

10 Commits

Author SHA1 Message Date
Kar Petrosyan
acd83dac70
Add documentation for socket_options 2024-06-13 19:25:24 +04:00
Kar Petrosyan
b9d9045a8b
Merge branch 'master' into network-options 2024-06-13 18:46:32 +04:00
Kar Petrosyan
a35ffe6b54
Merge branch 'master' into network-options 2024-06-13 16:45:46 +04:00
Tom Christie
489dda15ce
Merge branch 'master' into network-options 2024-01-16 13:16:39 +00:00
Tom Christie
6ac2101706 Documentation 2024-01-16 10:53:35 +00:00
Tom Christie
157f122742 Merge branch 'network-options' of https://github.com/encode/httpx into network-options 2024-01-16 10:26:20 +00:00
Tom Christie
febc827299 Merge branch 'master' into network-options 2024-01-16 10:26:12 +00:00
Kar Petrosyan
ee7eb664e1
Merge branch 'master' into network-options 2024-01-16 11:10:57 +04:00
Tom Christie
913ea35324 Linting 2024-01-12 12:18:49 +00:00
Tom Christie
83b5e4bf13 Add NetworkOptions 2024-01-12 12:14:15 +00:00
5 changed files with 130 additions and 24 deletions

View File

@ -0,0 +1,44 @@
There are several advanced network options that are made available through the `httpx.NetworkOptions` configuration class.
```python
# Configure an HTTPTransport with some specific network options.
network_options = httpx.NetworkOptions(
connection_retries=1,
local_address="0.0.0.0",
)
transport = httpx.HTTPTransport(network_options=network_options)
# Instantiate a client with the configured transport.
client = httpx.Client(transport=transport)
```
## Configuration
The options available on this class are...
### `connection_retries`
Configure a number of retries that may be attempted when initially establishing a TCP connection. Defaults to `0`.
### `local_address`
Configure the local address that the socket should be bound too. The most common usage is for enforcing binding to either IPv4 `local_address="0.0.0.0"` or IPv6 `local_address="::"`.
### `socket_options`
Configure the list of socket options to be applied to the underlying sockets used for network connections.
For example, you can use it to explicitly specify which network interface should be used for the connection in this manner:
```python
import httpx
socket_options = [(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, b"ETH999")]
network_options = httpx.NetworkOptions(
socket_options=socket_options
)
```
### `uds`
Connect to a Unix Domain Socket, rather than over the network. Should be a string providing the path to the UDS.

View File

@ -63,6 +63,7 @@ __all__ = [
"MockTransport",
"NetRCAuth",
"NetworkError",
"NetworkOptions",
"options",
"patch",
"PoolTimeout",

View File

@ -14,7 +14,13 @@ from ._types import CertTypes, HeaderTypes, TimeoutTypes, URLTypes, VerifyTypes
from ._urls import URL
from ._utils import get_ca_bundle_from_env
__all__ = ["Limits", "Proxy", "Timeout", "create_ssl_context"]
__all__ = ["Limits", "Proxy", "Timeout", "NetworkOptions", "create_ssl_context"]
SOCKET_OPTION = typing.Union[
typing.Tuple[int, int, int],
typing.Tuple[int, int, typing.Union[bytes, bytearray]],
typing.Tuple[int, int, None, int],
]
DEFAULT_CIPHERS = ":".join(
[
@ -367,6 +373,37 @@ class Proxy:
return f"Proxy({url_str}{auth_str}{headers_str})"
class NetworkOptions:
def __init__(
self,
connection_retries: int = 0,
local_address: typing.Optional[str] = None,
socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None,
uds: typing.Optional[str] = None,
) -> None:
self.connection_retries = connection_retries
self.local_address = local_address
self.socket_options = socket_options
self.uds = uds
def __repr__(self) -> str:
defaults = {
"connection_retries": 0,
"local_address": None,
"socket_options": None,
"uds": None,
}
params = ", ".join(
[
f"{attr}={getattr(self, attr)!r}"
for attr, default in defaults.items()
if getattr(self, attr) != default
]
)
return f"NetworkOptions({params})"
DEFAULT_TIMEOUT_CONFIG = Timeout(timeout=5.0)
DEFAULT_LIMITS = Limits(max_connections=100, max_keepalive_connections=20)
DEFAULT_NETWORK_OPTIONS = NetworkOptions(connection_retries=0)
DEFAULT_MAX_REDIRECTS = 20

View File

@ -32,7 +32,14 @@ from types import TracebackType
import httpcore
from .._config import DEFAULT_LIMITS, Limits, Proxy, create_ssl_context
from .._config import (
DEFAULT_LIMITS,
DEFAULT_NETWORK_OPTIONS,
Limits,
NetworkOptions,
Proxy,
create_ssl_context,
)
from .._exceptions import (
ConnectError,
ConnectTimeout,
@ -57,14 +64,16 @@ from .base import AsyncBaseTransport, BaseTransport
T = typing.TypeVar("T", bound="HTTPTransport")
A = typing.TypeVar("A", bound="AsyncHTTPTransport")
__all__ = ["AsyncHTTPTransport", "HTTPTransport"]
SOCKET_OPTION = typing.Union[
typing.Tuple[int, int, int],
typing.Tuple[int, int, typing.Union[bytes, bytearray]],
typing.Tuple[int, int, None, int],
]
__all__ = ["AsyncHTTPTransport", "HTTPTransport"]
@contextlib.contextmanager
def map_httpcore_exceptions() -> typing.Iterator[None]:
@ -130,11 +139,8 @@ class HTTPTransport(BaseTransport):
http2: bool = False,
limits: Limits = DEFAULT_LIMITS,
trust_env: bool = True,
proxy: ProxyTypes | None = None,
uds: str | None = None,
local_address: str | None = None,
retries: int = 0,
socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
proxy: typing.Optional[ProxyTypes] = None,
network_options: NetworkOptions = DEFAULT_NETWORK_OPTIONS,
) -> None:
ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
@ -147,10 +153,10 @@ class HTTPTransport(BaseTransport):
keepalive_expiry=limits.keepalive_expiry,
http1=http1,
http2=http2,
uds=uds,
local_address=local_address,
retries=retries,
socket_options=socket_options,
uds=network_options.uds,
local_address=network_options.local_address,
retries=network_options.connection_retries,
socket_options=network_options.socket_options,
)
elif proxy.url.scheme in ("http", "https"):
self._pool = httpcore.HTTPProxy(
@ -169,7 +175,10 @@ class HTTPTransport(BaseTransport):
keepalive_expiry=limits.keepalive_expiry,
http1=http1,
http2=http2,
socket_options=socket_options,
uds=network_options.uds,
local_address=network_options.local_address,
retries=network_options.connection_retries,
socket_options=network_options.socket_options,
)
elif proxy.url.scheme == "socks5":
try:
@ -271,11 +280,8 @@ class AsyncHTTPTransport(AsyncBaseTransport):
http2: bool = False,
limits: Limits = DEFAULT_LIMITS,
trust_env: bool = True,
proxy: ProxyTypes | None = None,
uds: str | None = None,
local_address: str | None = None,
retries: int = 0,
socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
proxy: typing.Optional[ProxyTypes] = None,
network_options: NetworkOptions = DEFAULT_NETWORK_OPTIONS,
) -> None:
ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
@ -288,10 +294,10 @@ class AsyncHTTPTransport(AsyncBaseTransport):
keepalive_expiry=limits.keepalive_expiry,
http1=http1,
http2=http2,
uds=uds,
local_address=local_address,
retries=retries,
socket_options=socket_options,
uds=network_options.uds,
local_address=network_options.local_address,
retries=network_options.connection_retries,
socket_options=network_options.socket_options,
)
elif proxy.url.scheme in ("http", "https"):
self._pool = httpcore.AsyncHTTPProxy(
@ -310,7 +316,10 @@ class AsyncHTTPTransport(AsyncBaseTransport):
keepalive_expiry=limits.keepalive_expiry,
http1=http1,
http2=http2,
socket_options=socket_options,
uds=network_options.uds,
local_address=network_options.local_address,
retries=network_options.connection_retries,
socket_options=network_options.socket_options,
)
elif proxy.url.scheme == "socks5":
try:

View File

@ -221,3 +221,18 @@ def test_proxy_with_auth_from_url():
def test_invalid_proxy_scheme():
with pytest.raises(ValueError):
httpx.Proxy("invalid://example.com")
def test_network_options():
network_options = httpx.NetworkOptions()
assert repr(network_options) == "NetworkOptions()"
network_options = httpx.NetworkOptions(connection_retries=1)
assert repr(network_options) == "NetworkOptions(connection_retries=1)"
network_options = httpx.NetworkOptions(
connection_retries=1, local_address="0.0.0.0"
)
assert repr(network_options) == (
"NetworkOptions(connection_retries=1, local_address='0.0.0.0')"
)