Skip to content

Commit 24aded0

Browse files
committed
Add optional filters to stream.pipe()
1 parent a8f666e commit 24aded0

2 files changed

Lines changed: 84 additions & 3 deletions

File tree

doc/api/streams.markdown

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Resumes the incoming `'data'` events after a `pause()`.
6565

6666
Closes the underlying file descriptor. Stream will not emit any more events.
6767

68-
### stream.pipe(destination, [options])
68+
### stream.pipe(destination, [options], [filter])
6969

7070
This is a `Stream.prototype` method available on all `Stream`s.
7171

@@ -92,6 +92,48 @@ NOTE: If the source stream does not support `pause()` and `resume()`, this funct
9292
adds simple definitions which simply emit `'pause'` and `'resume'` events on
9393
the source stream.
9494

95+
96+
The `filter` argument is an optional callback which can be used to filter all
97+
data passing through the pipe. This makes it easy to do arbitrary transforms
98+
(like gzip) while still maintaining the proper throttling. `filter` gets
99+
three arguments: a buffer, a write function, and a done function. Here is an
100+
example of a chat which uses a `filter` to append each message with the
101+
address of the sender.
102+
103+
var net = require('net');
104+
var people = [];
105+
106+
function address(socket) {
107+
return '<' + socket.remoteAddress + ':' + socket.remotePort + '> ';
108+
}
109+
110+
net.Server(function (socket) {
111+
socket.write("hello!\r\n");
112+
113+
people.forEach(function (p) {
114+
socket.pipe(p, { end: false }, function (d, write, done) {
115+
write(address(socket));
116+
write(d);
117+
done();
118+
});
119+
120+
p.pipe(socket, { end: false }, function (d, write, done) {
121+
write(address(p));
122+
write(d);
123+
done();
124+
});
125+
});
126+
127+
people.push(socket);
128+
129+
socket.on('end', function () {
130+
people.splice(people.indexOf(socket), 1);
131+
});
132+
}).listen(8000);
133+
134+
135+
136+
95137
## Writable Stream
96138

97139
A `Writable Stream` has the following methods, members, and events.

lib/stream.js

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,55 @@ function Stream() {
77
util.inherits(Stream, events.EventEmitter);
88
exports.Stream = Stream;
99

10-
Stream.prototype.pipe = function(dest, options) {
10+
Stream.prototype.pipe = function(dest /* options, filter */) {
1111
var source = this;
1212

13+
// parse arguments
14+
var options, filter;
15+
if (typeof arguments[1] == 'object') {
16+
options = arguments[1];
17+
filter = arguments[2];
18+
} else {
19+
filter = arguments[1];
20+
}
21+
1322
function ondata(chunk) {
23+
// FIXME shouldn't need to test writable - this is working around bug.
24+
// .writable should not change before a 'end' event is fired.
1425
if (dest.writable) {
1526
if (false === dest.write(chunk)) source.pause();
1627
}
1728
}
1829

19-
source.on('data', ondata);
30+
if (!filter) {
31+
source.on('data', ondata);
32+
} else {
33+
//
34+
// TODO: needs tests
35+
//
36+
var wait = false;
37+
var waitQueue = [];
38+
39+
function done () {
40+
wait = false;
41+
// Drain the waitQueue
42+
if (dest.writable && waitQueue.length) {
43+
wait = true;
44+
filter(waitQueue.shift(), ondata, done);
45+
}
46+
}
47+
48+
source.on('data', function (d) {
49+
if (wait) {
50+
waitQueue.push(d);
51+
source.pause();
52+
} else {
53+
wait = true;
54+
filter(d, ondata, done);
55+
}
56+
});
57+
}
58+
2059

2160
function ondrain() {
2261
if (source.readable) source.resume();

0 commit comments

Comments
 (0)