chimurai / http-proxy-middleware

:zap: The one-liner node.js http-proxy middleware for connect, express, next.js and more
MIT License
10.6k stars 828 forks source link

Too many subscriptions to Server.close event cause a OOM? #954

Open LRagji opened 7 months ago

LRagji commented 7 months ago

Checks

Describe the bug (be clear and concise)

This is a observation which is pointing out to a potential OOM, in our application we are using this package as shown in your first example.

import * as express from 'express';
import { createProxyMiddleware, Filter, Options, RequestHandler } from 'http-proxy-middleware';

const app = express();

app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);

and we are stress testing this API with multiple requests, it has been observed that following code is attaching multiple event handlers on the server.close, which prompts node to think its a memory leak. MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 connected listeners added to [NativeConnection].

The obvious way to fix this is to create the middleware once capture it in a variable and use that as shown in your 3rd example on the readme page instead of recreating the middleware every time as shown by the first example.

If our conclusion is right then i think you should remove inline creation of the middleware from your examples, so that other don't fall for the same issue.

Step-by-step reproduction instructions

1. Create the example one as shown in the readme page. Node v18, Install latest of this package.
2. Make sure the upstream server takes 1 second to complete the request.
3. Nuke the api with any performance tool or stress test tool.

Expected behavior (be clear and concise)

Should not see this warning in the console MaxListenersExceededWarning: Possible EventEmitter memory leak detected

How is http-proxy-middleware used in your project?

As shown in example 1:

import * as express from 'express';
import { createProxyMiddleware, Filter, Options, RequestHandler } from 'http-proxy-middleware';

const app = express();

app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);

### What http-proxy-middleware configuration are you using?

```typescript
Default

What OS/version and node/version are you seeing the problem?

Node v18
OS: Node official container.

Additional context (optional)

No response

khoale-groove commented 6 months ago

same issue.

f4z3k4s commented 5 months ago

@LRagji thanks, saved me some time! :)

fetis commented 2 months ago

Any updates on this?

Jokero commented 2 months ago

I got the same error when used a proxy in Parcel

rene-leanix commented 3 weeks ago

We're also getting this warning message, @LRagji, but the code you linked should add the on-close-event-listener only once as this.serverOnCloseSubscribed will be set:

    if (server && !this.serverOnCloseSubscribed) {
      server.on('close', () => {
        debug('server close signal received: closing proxy server');
        this.proxy.close();
      });
      this.serverOnCloseSubscribed = true;
    }

Were you able to debug it?

LRagji commented 3 weeks ago

@rene-leanix If i remember this correctly, Its about adding the event handler every-time a request is made..

rene-leanix commented 3 weeks ago

@LRagji, I just found out that in our code we're calling createProxyMiddleware() not just once but multiple times! That's the reason why we ran into the error message. Maybe its the same for others.

Its about adding the event handler every-time a request is made

But if you look at the code snippet above, I can't see how that could happen, if createProxyMiddleware() is only called once. this.serverOnCloseSubscribed is false to start with and only set in this one place.

LRagji commented 3 weeks ago

Again this was long back but i remember the HttpProxyMiddleware class was getting initialised for every request, which was triggering a lot of handlers.. don't remember how but the default code itself was not working, is that first example working for you? also they released a new release in April so not sure if things in pipeline changed anywhere....

rene-leanix commented 3 weeks ago

I tried to reproduce it as described in your initial post by spinning up that simple app and executing more than 10 curl requests simultaneously against that endpoint, but all requests succeeded and now warning was logged. I also debugged the code you linked to and both the constructor and the server.on('close')-call are executed once. This was confirmed by adding logs to the both places in the http-proxy-middleware dist code.

Could your load-testing setup have triggered a restart of the server between requests?

@Jokero, @fetis, @khoale-groove or @f4z3k4s: can any of you confirm that this issue happens apart from createProxyMiddleware() being called multiple times in your code?

LRagji commented 3 weeks ago

@rene-leanix What version are you testing?, and if your don't mind can you share your code which was calling create method over and over again? I can see a lot of changes that happened for those specific method 'create proxy' in the library for different version

rene-leanix commented 3 weeks ago

@LRagji, I am using the latest versions for http-proxy-middleware (3.0.0) and express (4.19.2), so maybe it was fixed recently.

Regarding the code calling the API, I used the quick & dirty

curl http://localhost:3000/api & curl http://localhost:3000/api & ... & curl http://localhost:3000/api

with more than 10 curl statements.

fetis commented 3 weeks ago

@rene-leanix We call it on application startup to configure and create proxies middleware, smth like this

function proxyHelper() {
  // our custom code and reconfiguration

return createProxyMiddleware();
}

app.use(proxyHelper())