fix: format of IPv6 address logged at PostgREST startup

The IPv6 address logged at the startup like `::1:80` was
wrong because the port isn't clearly separated. This commit
corrects it, now logging as `[::1]:80`.

This is done in accordance to RFC 3986. In short, we did this
have a clear separation between the port and host because
the components of an IPv6 are separated with the ':' character.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
This commit is contained in:
Taimoor Zaeem
2025-08-29 22:31:01 +02:00
committed by Wolfgang Walther
parent 3a28968f3c
commit bf0a1173b5
4 changed files with 25 additions and 6 deletions
+1
View File
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed ### Fixed
- Fix logging the Haskell type instead of the listener error message directly by @laurenceisla in #3588 - Fix logging the Haskell type instead of the listener error message directly by @laurenceisla in #3588
- Fix format of `IPv6` address logged at PostgREST startup by @taimoorzaeem in #4291
## [13.0.5] - 2025-08-24 ## [13.0.5] - 2025-08-24
+5 -1
View File
@@ -13,5 +13,9 @@ resolveHost sock = do
sn <- NS.getSocketName sock sn <- NS.getSocketName sock
case sn of case sn of
NS.SockAddrInet _ hostAddr -> pure $ Just $ fromString $ show $ fromHostAddress hostAddr NS.SockAddrInet _ hostAddr -> pure $ Just $ fromString $ show $ fromHostAddress hostAddr
NS.SockAddrInet6 _ _ hostAddr6 _ -> pure $ Just $ fromString $ show $ fromHostAddress6 hostAddr6 -- The IPv6 addresses are wrapped in [] brackets. This is done in accordance
-- to RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2).
-- In short, we did this to have a clear separation between the port and host
-- because the components of an IPv6 are separated with the ':' character.
NS.SockAddrInet6 _ _ hostAddr6 _ -> pure $ Just $ fromString $ "[" ++ show (fromHostAddress6 hostAddr6) ++ "]"
_ -> pure Nothing _ -> pure Nothing
+13 -2
View File
@@ -96,7 +96,9 @@ def run(
if port: if port:
env["PGRST_SERVER_PORT"] = str(port) env["PGRST_SERVER_PORT"] = str(port)
env["PGRST_SERVER_HOST"] = host or "localhost" env["PGRST_SERVER_HOST"] = host or "localhost"
baseurl = f"http://localhost:{port}" # When constructing IPv6 address, host address should be bracketed like [host]
apihost = f"[{host}]" if host and is_ipv6(host) else "localhost"
baseurl = f"http://{apihost}:{port}"
else: else:
socketfile = pathlib.Path(tmpdir) / "postgrest.sock" socketfile = pathlib.Path(tmpdir) / "postgrest.sock"
env["PGRST_SERVER_UNIX_SOCKET"] = str(socketfile) env["PGRST_SERVER_UNIX_SOCKET"] = str(socketfile)
@@ -104,7 +106,8 @@ def run(
adminport = freeport(port) adminport = freeport(port)
env["PGRST_ADMIN_SERVER_PORT"] = str(adminport) env["PGRST_ADMIN_SERVER_PORT"] = str(adminport)
adminurl = f"http://localhost:{adminport}" adminhost = f"[{host}]" if host and is_ipv6(host) else "localhost"
adminurl = f"http://{adminhost}:{adminport}"
command = [POSTGREST_BIN] command = [POSTGREST_BIN]
env["HPCTIXFILE"] = hpctixfile() env["HPCTIXFILE"] = hpctixfile()
@@ -218,3 +221,11 @@ def sleep_pool_connection(url, seconds):
session.get(url + f"/rpc/sleep?seconds={seconds}", timeout=0.1) session.get(url + f"/rpc/sleep?seconds={seconds}", timeout=0.1)
except requests.exceptions.ReadTimeout: except requests.exceptions.ReadTimeout:
pass pass
def is_ipv6(addr):
try:
socket.inet_pton(socket.AF_INET6, addr)
return True
except OSError:
return False
+6 -3
View File
@@ -1355,9 +1355,9 @@ def test_log_postgrest_version(defaultenv):
assert "Starting PostgREST %s..." % version in output[0] assert "Starting PostgREST %s..." % version in output[0]
def test_log_postgrest_host_and_port(defaultenv): @pytest.mark.parametrize("host", ["127.0.0.1", "::1"])
def test_log_postgrest_host_and_port(host, defaultenv):
"PostgREST should output the host and port it is bound to." "PostgREST should output the host and port it is bound to."
host = "127.0.0.1"
port = freeport() port = freeport()
with run( with run(
@@ -1365,7 +1365,10 @@ def test_log_postgrest_host_and_port(defaultenv):
) as postgrest: ) as postgrest:
output = postgrest.read_stdout(nlines=10) output = postgrest.read_stdout(nlines=10)
assert f"API server listening on {host}:{port}" in output[2] # output-sensitive if is_ipv6(host): # IPv6
assert f"API server listening on [{host}]:{port}" in output[2]
else: # IPv4
assert f"API server listening on {host}:{port}" in output[2]
def test_succeed_w_role_having_superuser_settings(defaultenv): def test_succeed_w_role_having_superuser_settings(defaultenv):