From 77c4d53f193f21ffcd85ba599f5a6b9b53a2c14b Mon Sep 17 00:00:00 2001 From: Jianhong Zhang Date: Tue, 9 Jun 2026 18:15:26 -0700 Subject: [PATCH] [PD] Fix prefill bootstrap registration failure with --host 0.0.0.0 (#27608) --- .../sglang/srt/disaggregation/common/conn.py | 5 ++ .../test_register_to_bootstrap.py | 48 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/python/sglang/srt/disaggregation/common/conn.py b/python/sglang/srt/disaggregation/common/conn.py index f025bbd7a..e88024c5a 100644 --- a/python/sglang/srt/disaggregation/common/conn.py +++ b/python/sglang/srt/disaggregation/common/conn.py @@ -392,6 +392,11 @@ class CommonKVManager(BaseKVManager): else: # Single-node case: bootstrap server's host is the same as http server's host host = self.bootstrap_host + # If the server was bound to the wildcard address (0.0.0.0 / ::), use the + # actual local IP instead — a PUT to http://0.0.0.0:/route is rejected + # with 403 by aiohttp ≥3.9 because 0.0.0.0 is not a valid HTTP Host value. + if host in ("0.0.0.0", "::"): + host = self.local_ip bootstrap_na = NetworkAddress(host, self.bootstrap_port) url = f"{bootstrap_na.to_url()}/route" diff --git a/test/registered/unit/disaggregation/test_register_to_bootstrap.py b/test/registered/unit/disaggregation/test_register_to_bootstrap.py index 2c39681e0..362cefb38 100644 --- a/test/registered/unit/disaggregation/test_register_to_bootstrap.py +++ b/test/registered/unit/disaggregation/test_register_to_bootstrap.py @@ -183,6 +183,54 @@ class TestRegisterToBootstrap(CustomTestCase): url_used = mock_put.call_args[0][0] self.assertIn("10.0.0.1", url_used) + @patch("sglang.srt.disaggregation.common.conn.time") + @patch("sglang.srt.disaggregation.common.conn.requests.put") + def test_wildcard_host_0000_uses_local_ip(self, mock_put, mock_time): + """When --host 0.0.0.0 is used, the PUT must target local_ip not 0.0.0.0. + + Scenario: cross-node P/D disagg where each role runs on a single node + (tp=1). Each machine runs its own SGLang instance with --host 0.0.0.0 + to accept remote connections. dist_init_addr is None because tp=1 + needs no multi-node rendezvous, so register_to_bootstrap takes the + else-branch and would use bootstrap_host="0.0.0.0" as the PUT target. + aiohttp >=3.9 rejects that with HTTP 403 because 0.0.0.0 is not a + valid Host header value. + + Fix: substitute self.local_ip when bootstrap_host is a wildcard. + """ + mock_time.monotonic.return_value = 0.0 + success_resp = MagicMock() + success_resp.status_code = 200 + mock_put.return_value = success_resp + + mgr = self._make_manager() + mgr.bootstrap_host = "0.0.0.0" + mgr.local_ip = "192.168.1.10" + mgr.register_to_bootstrap() + + url_used = mock_put.call_args[0][0] + self.assertNotIn("0.0.0.0", url_used) + self.assertIn("192.168.1.10", url_used) + + @patch("sglang.srt.disaggregation.common.conn.time") + @patch("sglang.srt.disaggregation.common.conn.requests.put") + def test_wildcard_host_ipv6_uses_local_ip(self, mock_put, mock_time): + """Same fix for the IPv6 wildcard \"::\": must use local_ip instead.""" + mock_time.monotonic.return_value = 0.0 + success_resp = MagicMock() + success_resp.status_code = 200 + mock_put.return_value = success_resp + + mgr = self._make_manager() + mgr.bootstrap_host = "::" + mgr.local_ip = "fd00::1" + mgr.register_to_bootstrap() + + url_used = mock_put.call_args[0][0] + # "::" bracketed as "[::]:port" should not appear; local_ip should + self.assertNotIn("[::]", url_used) + self.assertIn("fd00", url_used) + def _make_manager(self, dist_init_addr=None): """Create a lightweight mock manager that has the attributes needed by register_to_bootstrap, without going through CommonKVManager.__init__