Skip to content

Commit a949d79

Browse files
committed
fix: should not destroy streams
use black-hole-stream to make sure stream's data has been read
1 parent 9f80296 commit a949d79

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

lib/response.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
var contentDisposition = require('content-disposition');
9+
var BlackHoleStream = require('black-hole-stream');
910
var ensureErrorHandler = require('error-inject');
1011
var getType = require('mime-types').contentType;
1112
var onFinish = require('on-finished');
@@ -15,6 +16,8 @@ var typeis = require('type-is').is;
1516
var statuses = require('statuses');
1617
var destroy = require('destroy');
1718
var assert = require('assert');
19+
var Stream = require('stream');
20+
var http = require('http');
1821
var path = require('path');
1922
var vary = require('vary');
2023
var extname = path.extname;
@@ -161,8 +164,15 @@ module.exports = {
161164
}
162165

163166
// stream
164-
if ('function' == typeof val.pipe) {
165-
onFinish(this.res, destroy.bind(null, val));
167+
if (val instanceof Stream) {
168+
onFinish(this.res, function(){
169+
// don't destroy http IncomingMessage, keep `keep-alive` conncetion alive.
170+
if (val instanceof http.IncomingMessage) {
171+
if (val.readable) val.pipe(new BlackHoleStream());
172+
} else {
173+
destroy(val);
174+
}
175+
});
166176
ensureErrorHandler(val, this.ctx.onerror);
167177

168178
// overwriting

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"license": "MIT",
2020
"dependencies": {
2121
"accepts": "^1.2.2",
22+
"black-hole-stream": "0.0.1",
2223
"co": "^4.4.0",
2324
"composition": "^2.1.1",
2425
"content-disposition": "~0.5.0",
@@ -43,14 +44,18 @@
4344
"vary": "^1.0.0"
4445
},
4546
"devDependencies": {
47+
"agentkeepalive": "~2.0.3",
4648
"babel": "^5.0.0",
49+
"freeport": "~1.0.5",
4750
"istanbul": "^0.4.0",
4851
"make-lint": "^1.0.1",
4952
"mocha": "^2.0.1",
53+
"pedding": "^1.0.0",
5054
"should": "^6.0.3",
5155
"should-http": "0.0.3",
5256
"supertest": "^1.0.1",
53-
"test-console": "^0.7.1"
57+
"test-console": "^0.7.1",
58+
"urllib": "^2.5.0"
5459
},
5560
"engines": {
5661
"node": ">= 0.12.0",

test/application.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
'use strict';
33

44
var stderr = require('test-console').stderr;
5+
var Agent = require('agentkeepalive');
56
var request = require('supertest');
67
var statuses = require('statuses');
8+
var freeport = require('freeport');
9+
var pedding = require('pedding');
710
var assert = require('assert');
11+
var urllib = require('urllib');
812
var http = require('http');
913
var koa = require('..');
1014
var fs = require('fs');
@@ -872,6 +876,69 @@ describe('app.respond', function(){
872876
.get('/')
873877
.expect(404, done);
874878
})
879+
880+
it('should ensure stream do not leak', function(done){
881+
done = pedding(3, done);
882+
var app = koa();
883+
let stream1 = fs.createReadStream(__filename);
884+
let stream2 = fs.createReadStream(__filename);
885+
stream1.once('close', done);
886+
stream2.once('close', done);
887+
888+
app.use(function *(){
889+
this.body = stream1;
890+
this.body = stream2;
891+
});
892+
893+
var server = app.listen();
894+
895+
request(server)
896+
.head('/')
897+
.expect(200, done);
898+
})
899+
})
900+
901+
describe('when .body is a http keepalive IncomingMessage', function(){
902+
var target;
903+
var port;
904+
before(function(done){
905+
var app = koa();
906+
app.use(function *(){
907+
this.body = fs.createReadStream(__filename);
908+
});
909+
910+
freeport(function(err, p){
911+
port = p || 12384;
912+
target = app.listen(port, done);
913+
});
914+
})
915+
916+
after(function(){
917+
target.close();
918+
})
919+
920+
it('should not destroy keepalive connection', function(done){
921+
done = pedding(2, done);
922+
var app = koa();
923+
app.use(function *(){
924+
var remote = yield urllib.request('http://127.0.0.1:' + port, {
925+
streaming: true,
926+
agent: new Agent()
927+
});
928+
var res = remote.res;
929+
this.body = res;
930+
res.once('end', function(){
931+
assert.equal(res.readable, false);
932+
assert.equal(res.socket.destroyed, false);
933+
done();
934+
});
935+
});
936+
937+
var server = app.listen();
938+
request(server)
939+
.head('/')
940+
.expect(200, done);
941+
})
875942
})
876943

877944
describe('when .body is an Object', function(){

0 commit comments

Comments
 (0)