From 9420ad946aa8252d13fe94cd7fa4a2b562845b1d Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 16 Jul 2026 01:28:18 -0700 Subject: [PATCH] fix(webhook): scope reuse_address=False to macOS only (#65482) On Linux, SO_REUSEADDR only allows rebinding past TIME_WAIT (a second live listener would need SO_REUSEPORT, which we never set), so disabling it bought no protection there while making a quick gateway restart fail to rebind for up to ~60s. Keep the BSD silent-split guard on darwin, default semantics elsewhere. E2E verified: dual-stack v4+v6 bind on one port, immediate rebind after disconnect, and live-listener conflict still rejected. --- gateway/platforms/webhook.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gateway/platforms/webhook.py b/gateway/platforms/webhook.py index 7f104f0d7e9e..d6d6449469ea 100644 --- a/gateway/platforms/webhook.py +++ b/gateway/platforms/webhook.py @@ -39,6 +39,7 @@ import json import logging import re import subprocess +import sys import time from collections import deque from typing import Any, Deque, Dict, List, Optional @@ -251,14 +252,21 @@ class WebhookAdapter(BasePlatformAdapter): await self._runner.setup() # Do not probe only one address family before binding. With the # dual-stack default, an IPv6-only listener can already own this port - # while 127.0.0.1 still looks free. Also disable SO_REUSEADDR for the - # listener: on macOS, two wildcard/specific sockets with SO_REUSEADDR - # can silently split traffic while both servers report success. + # while 127.0.0.1 still looks free. + # + # SO_REUSEADDR is platform-dependent: + # - macOS (BSD semantics): two wildcard/specific sockets with + # SO_REUSEADDR can silently split traffic while both servers + # report success — so disable it there. + # - Linux: SO_REUSEADDR only permits rebinding past TIME_WAIT + # (a second live listener needs SO_REUSEPORT, which we never + # set). Disabling it would make a quick gateway restart fail + # to bind for up to ~60s — so keep the default (enabled). site = web.TCPSite( self._runner, self._host, self._port, - reuse_address=False, + reuse_address=False if sys.platform == "darwin" else None, ) try: await site.start()