Skip to content

Commit 5c7eb95

Browse files
authoredFeb 9, 2022
fix: use close event on response instead of socket (#1892)
1 parent 4d404d4 commit 5c7eb95

File tree

2 files changed

+123
-15
lines changed

2 files changed

+123
-15
lines changed
 

‎lib/plugins/bodyReader.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ function bodyReader(options) {
186186
// add 'close and 'aborted' event handlers so that requests (and their
187187
// corresponding memory) don't leak if client stops sending data half
188188
// way through a POST request
189-
req.socket.once('close', next);
189+
res.once('close', next);
190190
req.once('aborted', next);
191191
req.resume();
192192
}

‎test/plugins/bodyReader.test.js

+122-14
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@ var CLIENT;
1818
var PORT;
1919

2020
describe('body reader', function() {
21-
describe('gzip content encoding', function() {
22-
beforeEach(function(done) {
23-
SERVER = restify.createServer({
24-
dtrace: helper.dtrace,
25-
log: helper.getLog('server')
26-
});
21+
beforeEach(function(done) {
22+
SERVER = restify.createServer({
23+
dtrace: helper.dtrace,
24+
log: helper.getLog('server')
25+
});
2726

28-
SERVER.listen(0, '127.0.0.1', function() {
29-
PORT = SERVER.address().port;
27+
SERVER.listen(0, '127.0.0.1', function() {
28+
PORT = SERVER.address().port;
3029

31-
done();
32-
});
30+
done();
3331
});
32+
});
3433

35-
afterEach(function(done) {
36-
CLIENT.close();
37-
SERVER.close(done);
38-
});
34+
afterEach(function(done) {
35+
CLIENT.close();
36+
SERVER.close(done);
37+
});
3938

39+
describe('gzip content encoding', function() {
4040
it('should parse gzip encoded content', function(done) {
4141
SERVER.use(restify.plugins.bodyParser());
4242

@@ -187,4 +187,112 @@ describe('body reader', function() {
187187
req.write(postData);
188188
});
189189
});
190+
191+
it('should not add a listener for each call on same socket', done => {
192+
SERVER.use(restify.plugins.bodyParser());
193+
194+
let serverReq, serverRes, serverReqSocket;
195+
SERVER.post('/meals', function(req, res, next) {
196+
serverReq = req;
197+
serverRes = res;
198+
serverReqSocket = req.socket;
199+
res.send();
200+
next();
201+
});
202+
203+
CLIENT = restifyClients.createJsonClient({
204+
url: 'http://127.0.0.1:' + PORT,
205+
retry: false,
206+
agent: new http.Agent({ keepAlive: true })
207+
});
208+
209+
CLIENT.post('/meals', { breakfast: 'pancakes' }, (err, _, res) => {
210+
assert.ifError(err);
211+
assert.equal(res.statusCode, 200);
212+
213+
const firstReqSocket = serverReqSocket;
214+
const numReqListeners = listenerCount(serverReq);
215+
const numResListeners = listenerCount(serverRes);
216+
const numReqSocketListeners = listenerCount(serverReq.socket);
217+
218+
// Without setImmediate, the second request will not reuse the socket.
219+
setImmediate(() => {
220+
CLIENT.post('/meals', { lunch: 'salad' }, (err2, __, res2) => {
221+
assert.ifError(err2);
222+
assert.equal(res2.statusCode, 200);
223+
assert.equal(
224+
serverReqSocket,
225+
firstReqSocket,
226+
'This test should issue two requests that share the ' +
227+
'same socket.'
228+
);
229+
// The number of listeners on each emitter should not have
230+
// increased since the first request.
231+
assert.equal(listenerCount(serverReq), numReqListeners);
232+
assert.equal(listenerCount(serverRes), numResListeners);
233+
assert.equal(
234+
listenerCount(serverReq.socket),
235+
numReqSocketListeners
236+
);
237+
done();
238+
});
239+
});
240+
});
241+
});
242+
243+
it('should call next for each successful request on same socket', done => {
244+
let nextCallCount = 0;
245+
SERVER.use(restify.plugins.bodyParser());
246+
SERVER.use((req, res, next) => {
247+
nextCallCount += 1;
248+
next();
249+
});
250+
251+
let serverReqSocket;
252+
SERVER.post('/meals', function(req, res, next) {
253+
res.send();
254+
next();
255+
});
256+
257+
CLIENT = restifyClients.createJsonClient({
258+
url: 'http://127.0.0.1:' + PORT,
259+
retry: false,
260+
agent: new http.Agent({ keepAlive: true })
261+
});
262+
263+
CLIENT.post('/meals', { breakfast: 'waffles' }, (err, _, res) => {
264+
assert.ifError(err);
265+
assert.equal(res.statusCode, 200);
266+
const firstReqSocket = serverReqSocket;
267+
assert.equal(nextCallCount, 1);
268+
269+
// Without setImmediate, the second request will not reuse the socket.
270+
setImmediate(() => {
271+
CLIENT.post('/meals', { lunch: 'candy' }, (err2, __, res2) => {
272+
assert.ifError(err2);
273+
assert.equal(res2.statusCode, 200);
274+
assert.equal(
275+
serverReqSocket,
276+
firstReqSocket,
277+
'This test should issue two requests that share the ' +
278+
'same socket.'
279+
);
280+
assert.equal(nextCallCount, 2);
281+
done();
282+
});
283+
});
284+
});
285+
});
190286
});
287+
288+
/**
289+
* @param {EventEmitter} emitter - An emitter
290+
* @returns {number} - The total number of listeners across all events
291+
*/
292+
function listenerCount(emitter) {
293+
let numListeners = 0;
294+
for (const eventName of emitter.eventNames()) {
295+
numListeners += emitter.listenerCount(eventName);
296+
}
297+
return numListeners;
298+
}

0 commit comments

Comments
 (0)
Please sign in to comment.