Skip to content

Commit b636075

Browse files
fix(fixtures): skip getfqdn (fixes macOS CI hang)
host_guard::run_allows_loopback_when_escape_hatch_set timed out ~30s on macOS CI only. Root cause: Python stdlib http.server.HTTPServer.server_bind() calls socket.getfqdn(host), a reverse-DNS lookup that blocks on macOS GitHub runners. It is the sole fresh consumer of mock-http-server.py (http_transport uses httpmock; this fixture isn't otherwise spawned), so only this test hit it; bumping the test timeout could not help (hang >= the DNS timeout). Fix at the root in both HTTP/SSE fixtures: a _Server subclass binds via socketserver.TCPServer.server_bind (no getfqdn) and sets server_name/port directly, so LISTENING is emitted immediately on every platform. mock-sse-server.py shares the identical latent bug (not yet triggered) — fixed preventively for consistency. Local: ci-checks.sh green; 16/16 host_guard/http/sse tests pass; LISTENING smoke immediate. macOS reverse-DNS path is removed entirely, not merely given more time.
1 parent 4ed745e commit b636075

2 files changed

Lines changed: 39 additions & 6 deletions

File tree

crates/mcp-loadtest/tests/fixtures/mock-http-server.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import argparse
1313
import json
1414
import signal
15+
import socketserver
1516
import sys
1617
import threading
1718
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
@@ -93,13 +94,28 @@ def log_message(self, fmt, *args): # silence per-request stderr logging
9394
pass
9495

9596

97+
class _Server(ThreadingHTTPServer):
98+
"""`http.server.HTTPServer.server_bind()` calls `socket.getfqdn(host)`, a
99+
reverse-DNS lookup that can block ~30s on macOS CI runners (and is not
100+
needed for a loopback test mock). Bind via the plain TCPServer path and
101+
set `server_name`/`server_port` directly so `LISTENING:` is emitted
102+
immediately on every platform.
103+
"""
104+
105+
def server_bind(self):
106+
socketserver.TCPServer.server_bind(self)
107+
host, port = self.server_address[:2]
108+
self.server_name = host
109+
self.server_port = port
110+
111+
96112
def main():
97113
parser = argparse.ArgumentParser()
98114
parser.add_argument("--port", type=int, default=0)
99115
parser.add_argument("--host", default="127.0.0.1")
100116
args = parser.parse_args()
101117

102-
server = ThreadingHTTPServer((args.host, args.port), Handler)
118+
server = _Server((args.host, args.port), Handler)
103119
host, port = server.server_address[0], server.server_address[1]
104120
sys.stdout.write(f"LISTENING: {host}:{port}\n")
105121
sys.stdout.flush()

crates/mcp-loadtest/tests/fixtures/mock-sse-server.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import json
2121
import queue
2222
import signal
23+
import socketserver
2324
import sys
2425
import threading
2526
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
@@ -102,7 +103,10 @@ def do_GET(self): # noqa: N802
102103

103104
my_queue = _swap_outbound()
104105

105-
host = self.headers.get("Host") or f"{self.server.server_address[0]}:{self.server.server_address[1]}" # type: ignore[attr-defined]
106+
host = (
107+
self.headers.get("Host")
108+
or f"{self.server.server_address[0]}:{self.server.server_address[1]}"
109+
) # type: ignore[attr-defined]
106110
endpoint_url = f"http://{host}/post"
107111

108112
self.send_response(200)
@@ -128,9 +132,7 @@ def do_GET(self): # noqa: N802
128132
if item is _STOP:
129133
return
130134
data = json.dumps(item)
131-
self.wfile.write(
132-
f"event: message\ndata: {data}\n\n".encode("utf-8")
133-
)
135+
self.wfile.write(f"event: message\ndata: {data}\n\n".encode("utf-8"))
134136
self.wfile.flush()
135137
except (BrokenPipeError, ConnectionResetError, OSError):
136138
# Client disconnected — that's fine, just exit the handler.
@@ -165,13 +167,28 @@ def log_message(self, fmt, *args):
165167
pass
166168

167169

170+
class _Server(ThreadingHTTPServer):
171+
"""`http.server.HTTPServer.server_bind()` calls `socket.getfqdn(host)`, a
172+
reverse-DNS lookup that can block ~30s on macOS CI runners (and is not
173+
needed for a loopback test mock). Bind via the plain TCPServer path and
174+
set `server_name`/`server_port` directly so `LISTENING:` is emitted
175+
immediately on every platform.
176+
"""
177+
178+
def server_bind(self):
179+
socketserver.TCPServer.server_bind(self)
180+
host, port = self.server_address[:2]
181+
self.server_name = host
182+
self.server_port = port
183+
184+
168185
def main():
169186
parser = argparse.ArgumentParser()
170187
parser.add_argument("--port", type=int, default=0)
171188
parser.add_argument("--host", default="127.0.0.1")
172189
args = parser.parse_args()
173190

174-
server = ThreadingHTTPServer((args.host, args.port), Handler)
191+
server = _Server((args.host, args.port), Handler)
175192
host, port = server.server_address[0], server.server_address[1]
176193
sys.stdout.write(f"LISTENING: {host}:{port}\n")
177194
sys.stdout.flush()

0 commit comments

Comments
 (0)