forked from mafintosh/pump
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
79 lines (61 loc) · 1.96 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
var once = require('once');
var eos = require('end-of-stream');
var fs = require('fs'); // we only need fs to get the ReadStream and WriteStream prototypes
var noop = function() {};
var isFn = function(fn) {
return typeof fn === 'function';
};
var isFS = function(stream) {
return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close);
};
var isRequest = function(stream) {
return stream.setHeader && isFn(stream.abort);
};
var destroyer = function(stream, reading, writing, callback) {
callback = once(callback);
var closed = false;
stream.on('close', function() {
closed = true;
});
eos(stream, {readable:reading, writable:writing}, function(err) {
if (err) return callback(err);
closed = true;
callback();
});
var destroyed = false;
return function(err) {
if (closed) return;
if (destroyed) return;
destroyed = true;
if (isFS(stream)) return stream.close(); // use close for fs streams to avoid fd leaks
if (isRequest(stream)) return stream.abort(); // request.destroy just do .end - .abort is what we want
if (isFn(stream.destroy)) return stream.destroy();
callback(err || new Error('stream was destroyed'));
};
};
var call = function(fn) {
fn();
};
var pipe = function(from, to) {
return from.pipe(to);
};
var pump = function() {
var streams = Array.prototype.slice.call(arguments);
var callback = isFn(streams[streams.length-1] || noop) && streams.pop() || noop;
if (Array.isArray(streams[0])) streams = streams[0];
if (streams.length < 2) throw new Error('pump requires two streams per minimum');
var error;
var destroys = streams.map(function(stream, i) {
var reading = i < streams.length-1;
var writing = i > 0;
return destroyer(stream, reading, writing, function(err) {
if (!error) error = err;
if (err) destroys.forEach(call);
if (reading) return;
destroys.forEach(call);
callback(error);
});
});
return streams.reduce(pipe);
};
module.exports = pump;