mirror of
https://github.com/alexta69/metube.git
synced 2026-09-21 21:45:04 +00:00
fix: accept HOST=* so IPv6 users get a dual-stack bind (closes #795)
aiohttp hands HOST straight to getaddrinfo, which has no notion of a '*' wildcard: the lookup fails and MeTube dies at startup on an opaque DNS error. '*' is nevertheless what people reach for when they want to serve both IP stacks -- it is the answer given on #795 -- while the value that actually does it, an empty string, is undiscoverable. asyncio expands an empty host to one listening socket per address family, so map '*' onto it. Verified against the real stack: '' -> [('0.0.0.0', p), ('::', p, 0, 0)] '0.0.0.0' -> [('0.0.0.0', p)] '::' -> [('::', p, 0, 0)] '*' -> gaierror (before this change) The README claimed the 0.0.0.0 default was "all interfaces", which is only true of IPv4; document the three modes instead. Note that '::' is IPv6-only whatever the host's bindv6only says, because asyncio always sets IPV6_V6ONLY on the sockets it binds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,24 @@ class ConfigTests(unittest.TestCase):
|
||||
self.assertEqual(c.PUBLIC_HOST_URL, "")
|
||||
self.assertEqual(c.PUBLIC_HOST_AUDIO_URL, "")
|
||||
|
||||
def test_host_wildcard_becomes_empty_for_dual_stack(self):
|
||||
# Regression: aiohttp passes HOST to getaddrinfo, which does not resolve
|
||||
# '*' -- the server died at startup on a DNS error. '*' now selects the
|
||||
# empty string, the only value asyncio expands to a listening socket per
|
||||
# address family.
|
||||
for raw in ("*", " * "):
|
||||
with self.subTest(raw=raw):
|
||||
with patch.dict(os.environ, _base_env(HOST=raw), clear=False):
|
||||
c = Config()
|
||||
self.assertEqual(c.HOST, "")
|
||||
|
||||
def test_host_literal_addresses_are_untouched(self):
|
||||
for raw in ("0.0.0.0", "::", "127.0.0.1", ""):
|
||||
with self.subTest(raw=raw):
|
||||
with patch.dict(os.environ, _base_env(HOST=raw), clear=False):
|
||||
c = Config()
|
||||
self.assertEqual(c.HOST, raw)
|
||||
|
||||
def test_blank_audio_host_falls_back_to_audio_download_route(self):
|
||||
# Regression: a present-but-blank PUBLIC_HOST_AUDIO_URL must not stay empty
|
||||
# (which produced root-relative, 404ing audio links). It falls back to the
|
||||
|
||||
Reference in New Issue
Block a user