Problem
A host that replaces a proxy middleware instance at runtime cannot detach the old instance safely. After its first HTTP request, each instance installs its own anonymous close listener and bound upgrade listener on the HTTP server. RequestHandler exposes upgrade, but no lifecycle/disposal method.
This affects webpack-dev-server callback proxy configurations: recreating middleware accumulates WebSocket upgrade handlers. Its current workaround removes every server close listener, which also removes listeners owned by the host or other middleware and still cannot remove the old upgrade handler.
Minimal reproduction
Using http-proxy-middleware@4.2.0:
let middleware = createProxyMiddleware({ target, ws: true });
const server = http.createServer((req, res) => middleware(req, res));
await request(server);
console.log(server.listenerCount("close"), server.listenerCount("upgrade"));
// 1 1
middleware = createProxyMiddleware({ target, ws: true });
await request(server);
console.log(server.listenerCount("close"), server.listenerCount("upgrade"));
// 2 2
The exact result was reproduced on Node 22.23.2. The counts continue increasing with replacement.
Expected lifecycle contract
Could RequestHandler expose an idempotent lifecycle method such as dispose() or close() that:
- removes only the
close and internally registered upgrade listeners owned by that middleware instance
- handles every HTTP server to which the instance subscribed
- releases the underlying proxy resources with defined behavior for active requests/WebSockets
- is safe to call more than once
I have not proposed a listener-diff workaround because concurrent requests can register the anonymous listeners after replacement, and removing all listeners is unsafe. I would be happy to prepare a focused implementation and regression tests once the desired active-request/WebSocket semantics and method name are confirmed.
Problem
A host that replaces a proxy middleware instance at runtime cannot detach the old instance safely. After its first HTTP request, each instance installs its own anonymous
closelistener and boundupgradelistener on the HTTP server.RequestHandlerexposesupgrade, but no lifecycle/disposal method.This affects webpack-dev-server callback proxy configurations: recreating middleware accumulates WebSocket upgrade handlers. Its current workaround removes every server
closelistener, which also removes listeners owned by the host or other middleware and still cannot remove the old upgrade handler.Minimal reproduction
Using
http-proxy-middleware@4.2.0:The exact result was reproduced on Node 22.23.2. The counts continue increasing with replacement.
Expected lifecycle contract
Could
RequestHandlerexpose an idempotent lifecycle method such asdispose()orclose()that:closeand internally registeredupgradelisteners owned by that middleware instanceI have not proposed a listener-diff workaround because concurrent requests can register the anonymous listeners after replacement, and removing all listeners is unsafe. I would be happy to prepare a focused implementation and regression tests once the desired active-request/WebSocket semantics and method name are confirmed.