Skip to content

Expose lifecycle cleanup for replaced middleware instances #1290

Description

@OskarEichler

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions