Skip to content

Commit 3d7ab70

Browse files
committed
feat(node-request): close connections after request
This automatically closes server connections after making a request; so that test runners (like mocha 4) aren't left hanging after the test execution. If someone really needs to keep the server open, `.keepOpen()` can be used. Fixes #178 BREAKING CHANGE: This change closes servers down after every request. If you want to use the server for reasons at the same time as your test suite or for some other reason you dont want the server to automatically be shut-down, then you'll need to change any `chai.request` callsites to also use the `keepOpen` comand, like so: ```js chai.request(server).keepOpen() ```
1 parent d3d9ec6 commit 3d7ab70

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@ chai.request(app)
6161
.get('/')
6262
```
6363

64+
When passing an `app` to `request`; it will automatically open the server for
65+
incoming requests (by calling `listen()`) and, once a request has been made
66+
the server will automatically shut down (by calling `.close()`). If you want to
67+
keep the server open, perhaps if you're making multiple requests, you must call
68+
`.keepOpen()` after `.request()`, and manually close the server down:
69+
70+
```js
71+
var requester = chai.request(app).keepOpen()
72+
73+
Promise.all([
74+
requester.get('/a'),
75+
requester.get('/b'),
76+
])
77+
.then(responses => ....)
78+
.then(() => requester.close())
79+
```
80+
81+
6482
#### URL
6583

6684
You may also use a base url as the foundation of your request.

lib/request.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,31 @@ module.exports = function (app) {
214214
: app
215215
, obj = {};
216216

217+
var keepOpen = false
218+
if (typeof server !== 'string' && server && server.listen && server.address) {
219+
if (!server.address()) {
220+
server = server.listen(0)
221+
}
222+
}
223+
obj.keepOpen = function() {
224+
keepOpen = true
225+
return this
226+
}
227+
obj.close = function(callback) {
228+
if (server && server.close && keepOpen === false) {
229+
server.close(callback);
230+
}
231+
return this
232+
}
217233
methods.forEach(function (method) {
218234
obj[method] = function (path) {
219-
return new Test(server, method, path);
235+
return new Test(server, method, path)
236+
.on('end', function() {
237+
obj.close();
238+
});
220239
};
221240
});
222241
obj.del = obj.delete;
223-
224242
return obj;
225243
};
226244

@@ -257,8 +275,7 @@ function serverAddress (app, path) {
257275
}
258276
var addr = app.address();
259277
if (!addr) {
260-
app.listen(0);
261-
addr = app.address();
278+
throw new Error('Server is not listening')
262279
}
263280
var protocol = (app instanceof https.Server) ? 'https' : 'http';
264281
// If address is "unroutable" IPv4/6 address, then set to localhost
@@ -286,9 +303,22 @@ function TestAgent(app) {
286303
if (typeof app === 'function') app = http.createServer(app);
287304
(Agent || Request).call(this);
288305
this.app = app;
306+
if (typeof app !== 'string' && app && app.listen && app.address && !app.address()) {
307+
this.app = app.listen(0)
308+
}
289309
}
290310
util.inherits(TestAgent, Agent || Request);
291311

312+
TestAgent.prototype.close = function close(callback) {
313+
if (this.app && this.app.close) {
314+
this.app.close(callback)
315+
}
316+
return this
317+
}
318+
TestAgent.prototype.keepOpen = function keepOpen() {
319+
return this
320+
}
321+
292322
// override HTTP verb methods
293323
methods.forEach(function(method){
294324
TestAgent.prototype[method] = function(url){

test/request.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,10 @@ describe('request', function () {
123123
request(app).get('/')
124124
.set('X-API-Key', 'testing')
125125
.end(function (err, res) {
126+
if (err) return done(err)
126127
res.should.have.status(200);
127128
res.text.should.equal('hello universe');
128-
done(err);
129+
done();
129130
});
130131
});
131132

@@ -169,9 +170,41 @@ describe('request', function () {
169170
})
170171
.then(function (res) {
171172
res.text.should.equal('your cookie: mycookie=test');
173+
agent.close()
172174
})
173175
.then(done, done);
174176
});
177+
178+
it('automatically closes the server down once done with it', function (done) {
179+
var server = require('http').createServer(function (req, res) {
180+
res.writeHeader(200, { 'content-type' : 'text/plain' });
181+
res.end('hello world');
182+
});
183+
184+
request(server)
185+
.get('/')
186+
.end(function (err, res) {
187+
res.should.have.status(200);
188+
res.text.should.equal('hello world');
189+
should.not.exist(server.address())
190+
done(err)
191+
});
192+
});
193+
194+
it('can use keepOpen() to not close the server', function (done) {
195+
var server = require('http').createServer(function (req, res) {
196+
res.writeHeader(200, { 'content-type' : 'text/plain' });
197+
res.end('hello world');
198+
});
199+
var cachedRequest = request(server).keepOpen();
200+
server.listen = function () { throw new Error('listen was called when it shouldnt have been') }
201+
cachedRequest.get('/') .end(function (err, res) {
202+
cachedRequest.get('/').end(function (err2, res) {
203+
server.close(function () { done(err || err2) })
204+
})
205+
});
206+
});
207+
175208
});
176209

177210
isBrowser && describe('Browser', function () {

0 commit comments

Comments
 (0)