Files
PacketStation/tests/server/test_proxy.py
T

84 lines
2.7 KiB
Python
Raw Normal View History

from __future__ import annotations
import asyncio
from typing import Any
from packetstation.proxy import TrustedProxies, TrustedProxyMiddleware, forwarded
PROXY = "172.18.0.5"
def trusted(addresses: set[str] | None = None) -> TrustedProxies:
t = TrustedProxies("npm_app_1", resolver=lambda _h: set(addresses or {PROXY}))
t.refresh()
return t
def H(**kw: str) -> list[tuple[bytes, bytes]]:
return [(k.replace("_", "-").encode(), v.encode()) for k, v in kw.items()]
def test_trusted_peer_uses_first_xff_and_proto() -> None:
ip, scheme = forwarded(PROXY, H(x_forwarded_for="10.1.2.3, 172.18.0.9", x_forwarded_proto="https"), trusted())
assert ip == "10.1.2.3" and scheme == "https"
def test_untrusted_peer_ignores_headers() -> None:
ip, scheme = forwarded("10.9.9.9", H(x_forwarded_for="1.2.3.4", x_forwarded_proto="https"), trusted())
assert ip == "10.9.9.9" and scheme is None
def test_empty_host_trusts_nothing() -> None:
t = TrustedProxies("")
t.refresh()
assert forwarded(PROXY, H(x_forwarded_for="1.2.3.4"), t) == (PROXY, None)
def test_garbage_xff_and_proto_ignored() -> None:
ip, scheme = forwarded(PROXY, H(x_forwarded_for="not-an-ip", x_forwarded_proto="gopher"), trusted())
assert ip == PROXY and scheme is None
def test_resolution_failure_keeps_last_good_set() -> None:
calls = {"n": 0}
def resolver(_h: str) -> set[str]:
calls["n"] += 1
if calls["n"] > 1:
raise OSError("no such host")
return {PROXY}
t = TrustedProxies("npm_app_1", resolver=resolver)
t.refresh()
t.refresh()
assert t.addresses == frozenset({PROXY})
def test_re_resolve_picks_up_new_address() -> None:
addrs = [{PROXY}, {"172.18.0.77"}]
t = TrustedProxies("npm_app_1", resolver=lambda _h: addrs.pop(0))
t.refresh()
t.refresh()
assert t.trusts("172.18.0.77") and not t.trusts(PROXY)
def _run_mw(peer: str, headers: list[tuple[bytes, bytes]], t: TrustedProxies) -> dict[str, Any]:
seen: dict[str, Any] = {}
async def app(scope: Any, receive: Any, send: Any) -> None:
seen.update(scope)
scope = {"type": "http", "client": (peer, 5555), "scheme": "http", "headers": headers, "path": "/"}
asyncio.run(TrustedProxyMiddleware(app, t)(scope, None, None)) # type: ignore[arg-type]
return seen
def test_middleware_rewrites_for_trusted_peer() -> None:
s = _run_mw(PROXY, H(x_forwarded_for="10.1.2.3", x_forwarded_proto="https"), trusted())
assert s["client"] == ("10.1.2.3", 5555) and s["scheme"] == "https"
def test_middleware_leaves_untrusted_peer() -> None:
s = _run_mw("10.5.5.5", H(x_forwarded_for="10.1.2.3", x_forwarded_proto="https"), trusted())
assert s["client"] == ("10.5.5.5", 5555) and s["scheme"] == "http"