Skip to content

Commit cafddd3

Browse files
committed
Implement restricting the IP protocol version for /dns4 and /dns6 multiaddr items in the HTTPx backend
1 parent 9533777 commit cafddd3

File tree

4 files changed

+35
-19
lines changed

4 files changed

+35
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Bugfixes:
99

1010
* The value of the `timeout` parameter on `ipfshttpclient.{connect,Client}` is no longer ignored when using the `requests` HTTP backend (default)
1111
* (The per-API-call `timeout` parameter was unaffected by this.)
12+
* The HTTPx HTTP backend now properly applies address family restrictions encoded as part of the daemon MultiAddr (needed minor upstream change)
1213

1314
py-ipfs-http-client 0.6.0 (30.06.2020)
1415
--------------------------------------

ipfshttpclient/http_httpx.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import math
88
import socket
99
import typing as ty
10-
import warnings
1110

11+
import httpcore
1212
import httpx
1313

1414
from . import encoding
@@ -26,7 +26,7 @@
2626
import typing_extensions
2727

2828
# By using the precise types from HTTPx we'll also get type errors if our
29-
# types become somehow incompatible with the one from that library
29+
# types become somehow incompatible with the ones from that library
3030
RequestArgs = typing_extensions.TypedDict("RequestArgs", {
3131
"auth": "httpx._types.AuthTypes",
3232
"cookies": "httpx._types.CookieTypes",
@@ -75,9 +75,10 @@ def map_args_to_httpx(
7575

7676

7777
class ClientSync(ClientSyncBase[httpx.Client]):
78-
__slots__ = ("_session_base", "_session_kwargs")
78+
__slots__ = ("_session_base", "_session_kwargs", "_session_laddr")
7979
_session_base: "httpx._types.URLTypes"
8080
_session_kwargs: RequestArgs
81+
_session_laddr: ty.Optional[str]
8182

8283
def _init(self, addr: addr_t, base: str, *, # type: ignore[no-any-unimported]
8384
auth: auth_t,
@@ -90,12 +91,15 @@ def _init(self, addr: addr_t, base: str, *, # type: ignore[no-any-unimported]
9091
host_numeric: bool
9192
base_url, family, host_numeric = multiaddr_to_url_data(addr, base)
9293

93-
#FIXME once https://github.com/encode/httpcore/pull/100 is released
94-
if family != socket.AF_UNSPEC and not host_numeric:
95-
warnings.warn(
96-
"Restricting the address family on name lookups is not yet supported by HTTPx",
97-
UserWarning
98-
)
94+
self._session_laddr = None
95+
if family != socket.AF_UNSPEC:
96+
if family == socket.AF_INET:
97+
self._session_laddr = "0.0.0.0"
98+
elif family == socket.AF_INET6:
99+
self._session_laddr = "::"
100+
else:
101+
assert False, ("multiaddr_to_url_data should only return a socket "
102+
"address family of AF_INET, AF_INET6 or AF_UNSPEC")
99103

100104
self._session_base = base_url
101105
self._session_kwargs = map_args_to_httpx(
@@ -107,7 +111,18 @@ def _init(self, addr: addr_t, base: str, *, # type: ignore[no-any-unimported]
107111
)
108112

109113
def _make_session(self) -> httpx.Client:
110-
return httpx.Client(**self._session_kwargs, base_url=self._session_base)
114+
connection_pool = httpcore.SyncConnectionPool(
115+
local_address = self._session_laddr,
116+
117+
#XXX: Argument values duplicated from httpx._client.Client._init_transport:
118+
keepalive_expiry = 5.0, #XXX: Value duplicated from httpx._client.KEEPALIVE_EXPIRY
119+
max_connections = 100, #XXX: Value duplicated from httpx._config.DEFAULT_LIMITS
120+
max_keepalive_connections = 20, #XXX: Value duplicated from httpx._config.DEFAULT_LIMITS
121+
ssl_context = httpx.create_ssl_context(trust_env=True),
122+
)
123+
return httpx.Client(**self._session_kwargs,
124+
base_url = self._session_base,
125+
transport = connection_pool)
111126

112127
def _do_raise_for_status(self, response: httpx.Response) -> None:
113128
try:

test/unit/test_http_httpx.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@
4747
def test_map_args_to_httpx(kwargs, expected):
4848
assert ipfshttpclient.http_httpx.map_args_to_httpx(**kwargs) == expected
4949

50-
@pytest.mark.parametrize("args,kwargs,expected_kwargs,expected_base", [
50+
@pytest.mark.parametrize("args,kwargs,expected_kwargs,expected_base,expected_laddr", [
5151
(("/dns/localhost/tcp/5001/http", "api/v0"), {}, {
5252
"params": [("stream-channels", "true")],
53-
}, "http://localhost:5001/api/v0/"),
53+
}, "http://localhost:5001/api/v0/", None),
5454
5555
(("/dns6/ietf.org/tcp/443/https", "/base/"), {
5656
"auth": ("user", "pass"),
@@ -59,15 +59,15 @@ def test_map_args_to_httpx(kwargs, expected):
5959
"offline": True,
6060
"timeout": (math.inf, math.inf),
6161
}, {
62-
#XXX: This should also store the expected address family somewhere
6362
"auth": ("user", "pass"),
6463
"cookies": cookiejar,
6564
"headers": {"name": "value"},
6665
"params": [("offline", "true"), ("stream-channels", "true")],
6766
"timeout": (None, None, None, None),
68-
}, "https://ietf.org:443/base/"),
67+
}, "https://ietf.org:443/base/", "::"),
6968
])
70-
def test_client_args_to_session_kwargs(args, kwargs, expected_kwargs, expected_base):
69+
def test_client_args_to_session_kwargs(args, kwargs, expected_kwargs, expected_base, expected_laddr):
7170
client = ipfshttpclient.http_httpx.ClientSync(*args, **kwargs)
7271
assert client._session_kwargs == expected_kwargs
73-
assert client._session_base == expected_base
72+
assert client._session_base == expected_base
73+
assert client._session_laddr == expected_laddr

tox.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
minversion = 3.3
44
envlist =
55
py3,
6-
pypy3,
6+
py3-httpx,
77
styleck,
88
typeck
99

@@ -45,7 +45,7 @@ deps =
4545
[testenv:py3-httpx]
4646
deps =
4747
{[testenv]deps}
48-
httpx (~=0.13.0)
48+
httpx (~=0.14.0) # Including its httpcore 0.10.* dependency
4949
setenv =
5050
{[testenv]setenv}
5151
PY_IPFS_HTTP_CLIENT_PREFER_HTTPX = yes
@@ -69,7 +69,7 @@ skip_install = true
6969
deps =
7070
mypy ~= 0.782
7171
pytest ~= 5.0
72-
httpx ~= 0.13.0
72+
httpx ~= 0.14.0
7373
commands =
7474
mypy --config-file=tox.ini {posargs} -p ipfshttpclient
7575

0 commit comments

Comments
 (0)