From f43072f9551196447ba08b15b803a03d0f4965d3 Mon Sep 17 00:00:00 2001 From: elliotllliu <55885132+elliotllliu@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:56:43 +0000 Subject: [PATCH] fix(feishu): await HTTP server close during monitor cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Feishu monitor's stopFeishuMonitorState() called server.close() without awaiting the callback, then immediately cleared the Map entries. This caused: 1. Memory leak — server objects retained after Map.delete() 2. Port binding issues — ports not fully released before restart 3. Resource leak on repeated gateway restarts Fix: make stopFeishuMonitorState() and stopFeishuMonitor() async, await server.close() via Promise wrapper before clearing state. Update all test afterEach hooks to await the cleanup. Closes #48183 --- extensions/feishu/src/monitor.startup.test.ts | 4 ++-- extensions/feishu/src/monitor.state.ts | 14 +++++++++++--- extensions/feishu/src/monitor.ts | 4 ++-- extensions/feishu/src/monitor.webhook-e2e.test.ts | 4 ++-- .../feishu/src/monitor.webhook-security.test.ts | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/extensions/feishu/src/monitor.startup.test.ts b/extensions/feishu/src/monitor.startup.test.ts index 96dbd52b8ef..2b933e647f2 100644 --- a/extensions/feishu/src/monitor.startup.test.ts +++ b/extensions/feishu/src/monitor.startup.test.ts @@ -44,8 +44,8 @@ async function waitForStartedAccount(started: string[], accountId: string) { } } -afterEach(() => { - stopFeishuMonitor(); +afterEach(async () => { + await stopFeishuMonitor(); }); describe("Feishu monitor startup preflight", () => { diff --git a/extensions/feishu/src/monitor.state.ts b/extensions/feishu/src/monitor.state.ts index 30cada26821..eb7e4c48fad 100644 --- a/extensions/feishu/src/monitor.state.ts +++ b/extensions/feishu/src/monitor.state.ts @@ -132,13 +132,15 @@ export function recordWebhookStatus( }); } -export function stopFeishuMonitorState(accountId?: string): void { +export async function stopFeishuMonitorState(accountId?: string): Promise { if (accountId) { wsClients.delete(accountId); const server = httpServers.get(accountId); if (server) { - server.close(); httpServers.delete(accountId); + await new Promise((resolve) => { + server.close(() => resolve()); + }); } botOpenIds.delete(accountId); botNames.delete(accountId); @@ -146,10 +148,16 @@ export function stopFeishuMonitorState(accountId?: string): void { } wsClients.clear(); + const closePromises: Promise[] = []; for (const server of httpServers.values()) { - server.close(); + closePromises.push( + new Promise((resolve) => { + server.close(() => resolve()); + }), + ); } httpServers.clear(); + await Promise.all(closePromises); botOpenIds.clear(); botNames.clear(); } diff --git a/extensions/feishu/src/monitor.ts b/extensions/feishu/src/monitor.ts index 50241d36baa..5939025f159 100644 --- a/extensions/feishu/src/monitor.ts +++ b/extensions/feishu/src/monitor.ts @@ -90,6 +90,6 @@ export async function monitorFeishuProvider(opts: MonitorFeishuOpts = {}): Promi await Promise.all(monitorPromises); } -export function stopFeishuMonitor(accountId?: string): void { - stopFeishuMonitorState(accountId); +export async function stopFeishuMonitor(accountId?: string): Promise { + await stopFeishuMonitorState(accountId); } diff --git a/extensions/feishu/src/monitor.webhook-e2e.test.ts b/extensions/feishu/src/monitor.webhook-e2e.test.ts index a11957e3393..da712a73271 100644 --- a/extensions/feishu/src/monitor.webhook-e2e.test.ts +++ b/extensions/feishu/src/monitor.webhook-e2e.test.ts @@ -58,8 +58,8 @@ async function postSignedPayload(url: string, payload: Record) }); } -afterEach(() => { - stopFeishuMonitor(); +afterEach(async () => { + await stopFeishuMonitor(); }); describe("Feishu webhook signed-request e2e", () => { diff --git a/extensions/feishu/src/monitor.webhook-security.test.ts b/extensions/feishu/src/monitor.webhook-security.test.ts index 957d874cc3a..405de524f19 100644 --- a/extensions/feishu/src/monitor.webhook-security.test.ts +++ b/extensions/feishu/src/monitor.webhook-security.test.ts @@ -35,9 +35,9 @@ import { stopFeishuMonitor, } from "./monitor.js"; -afterEach(() => { +afterEach(async () => { clearFeishuWebhookRateLimitStateForTest(); - stopFeishuMonitor(); + await stopFeishuMonitor(); }); describe("Feishu webhook security hardening", () => {