While testing with supertest I have noticed that after a test run netstat reports a connection in TIME_WAIT state for every call (get, post, etc.) made with a supertest agent. This can become a problem for me doing frequent and larger test runs - my system actually runs out of either file handles or ports (not sure which.)
My app binds to port 3000 and it's my understanding that if the app is bound to a port supertest won't bind to an ephemeral port.
Here's an abridged version of how my app and test suite are written:
app.js
const express = require('express');
...
let app = express();
...
app.listen(process.env.PORT, function () {
logger.info('App listening on port ' + process.env.PORT);
});
module.exports = app;
test.js
let app = require('../app.js'),
agent = require('supertest').agent(app),
assert = require('chai').assert;
describe('/api/auth/ typical sign up and login', function () {
let email = '[email protected]',
password = 'foobar',
path = '/api';
it('gets no results', async () => {
let res = await agent
.get(path + '/credentials/')
.expect(200);
assert.doesNotHaveAnyKeys(res.body, ['email']);
});
...
}
For every await agent.(get|post|put)... there seems to be a connection on a different port. Is there some way to reduce the number of connections opened in my test suite? Is it possible to reuse a connection, or even just have connections close so they're not stuck in TIME_WAIT taking up resources?
Below is a sample output from netstat after a suite that made 11 "agent" calls - ports 6379 and 5432 are connections to redis and postgres respectively, all the rest I think are between supertest and my app:
tcp4 0 0 127.0.0.1.62021 127.0.0.1.6379 TIME_WAIT
tcp4 0 0 127.0.0.1.62023 127.0.0.1.62024 TIME_WAIT
tcp4 0 0 127.0.0.1.62026 127.0.0.1.62027 TIME_WAIT
tcp4 0 0 127.0.0.1.62028 127.0.0.1.62029 TIME_WAIT
tcp4 0 0 127.0.0.1.62030 127.0.0.1.62031 TIME_WAIT
tcp4 0 0 127.0.0.1.62032 127.0.0.1.62033 TIME_WAIT
tcp4 0 0 127.0.0.1.62034 127.0.0.1.62035 TIME_WAIT
tcp4 0 0 127.0.0.1.62036 127.0.0.1.62037 TIME_WAIT
tcp4 0 0 127.0.0.1.62038 127.0.0.1.62039 TIME_WAIT
tcp4 0 0 127.0.0.1.62040 127.0.0.1.62041 TIME_WAIT
tcp4 0 0 127.0.0.1.62042 127.0.0.1.62043 TIME_WAIT
tcp4 0 0 127.0.0.1.62044 127.0.0.1.62045 TIME_WAIT
tcp4 0 0 127.0.0.1.62022 127.0.0.1.6379 TIME_WAIT
tcp4 0 0 127.0.0.1.62025 127.0.0.1.5432 TIME_WAIT
I figured out that passing a function to supertest.agent() will always cause supertest to bind to an ephemeral port on every request - the express app being a function.
Instead, I had to export the result of app.listen() (a NodeJS http.Server object) and pass that to supertest.agent():
app.js
...
let server = app.listen(process.env.PORT, function () {
logger.info('App listening on port ' + process.env.PORT);
});
module.exports = server;
test.js
let server = require('../app.js'),
agent = require('supertest').agent(server),
...
Now supertest opens a new socket for every request, but they all go to the same port on the server side.
Is it possible to reuse the sockets? Or have them close cleanly so they're not hanging around?
I was able to set the http agent in superagent by wrapping the various HTTP verb method calls and then setting the agent. Combined with passing the http server returned from express app.listen this should give you a configurable number of sockets in a pool that make requests.
Here's the code I used to set the HTTP socket pool to 10 as well as reversing the cookie saving behavior of supertest when you set an agent.
`
const http = require("http");
const keepAliveAgent = new http.Agent({ keepAlive: true, keepAliveMsecs: 90000, timeout: 1200000, maxSockets:10, maxFreeSockets:10 });
const _request = request.agent(app.app);
_request._saveCookies = _.noop;
['get', 'post', 'put', 'del', 'delete', 'head'].forEach((method)=>{
let fx = _request[method];
_request[method] = (...args)=>{
let result = fx.call(_request, ...args);
result.agent(keepAliveAgent);
return result;
};
});
`
Most helpful comment
I figured out that passing a function to
supertest.agent()will always cause supertest to bind to an ephemeral port on every request - the expressappbeing a function.Instead, I had to export the result of
app.listen()(a NodeJShttp.Serverobject) and pass that tosupertest.agent():app.js
test.js
Now supertest opens a new socket for every request, but they all go to the same port on the server side.
Is it possible to reuse the sockets? Or have them close cleanly so they're not hanging around?