├── example ├── skates │ ├── ' │ ├── client.js │ ├── index.html │ ├── server.js │ └── simple.js └── socket.io │ ├── client.js │ ├── index.html │ ├── server.js │ └── simple.js ├── index.js ├── package.json ├── readme.markdown └── test ├── consistent.js ├── disconnections.js └── simple.js /example/skates/': -------------------------------------------------------------------------------- 1 | var skates = require('skates') 2 | var connect = require('connect') 3 | var _bs = require('browser-stream') 4 | var assert = require('assert') 5 | var es = require('event-stream') 6 | 7 | var app = skates() 8 | .use(connect.static(__dirname)) 9 | .on('connection', function (sock) { 10 | sock.on('ping', function () { 11 | console.log('ping') 12 | sock.emit('pong') 13 | }) 14 | var bs = _bs(sock) 15 | bs.on('connection', function (stream) { 16 | console.log('STREAM OPTIONS', stream) 17 | //stream.pipe(es.stringify()).pipe(es.log()) 18 | var i = 0 19 | var t 20 | t = setInterval(function () { 21 | stream.write({item: ++i}) 22 | console.log(i) 23 | /* if(i < 21) 24 | return 25 | stream.end()*/ 26 | // clearInterval(t, 200) 27 | }, 500) 28 | stream.on('end', function (){ 29 | clearInterval(t) 30 | }) 31 | }) 32 | }) 33 | .listen(3000, function () { 34 | console.log('BrowserStream example running on port 3000') 35 | }) 36 | -------------------------------------------------------------------------------- /example/skates/client.js: -------------------------------------------------------------------------------- 1 | //var io = require('socket.io') 2 | skates = require('skates')() 3 | var bs = require('browser-stream')(skates) 4 | 5 | ping = function () { 6 | skates.emit('ping') 7 | } 8 | 9 | skates.on('pong', function () { 10 | console.log('pong') 11 | }) 12 | 13 | skates.on('flow', function (s, r) { 14 | if(s || r) 15 | console.log('flow', s, r) 16 | }) 17 | console.log('MODULE') 18 | require('domready')(function () { 19 | console.log('DOMREADY') 20 | function sync () { 21 | console.log('SYNC!') 22 | //remember, you have to recreate your streams after they have closed 23 | var domnode = require('domnode')('#body', '{{item}} ') 24 | rs = bs.createReadStream('whatever', 25 | {options: true, random: Math.random()}) 26 | 27 | rs.pipe(domnode) 28 | rs.on('end', function () { 29 | rs.removeAllListeners() 30 | sync() 31 | }) 32 | } 33 | sync() 34 | 35 | }) 36 | -------------------------------------------------------------------------------- /example/skates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/skates/server.js: -------------------------------------------------------------------------------- 1 | var skates = require('skates') 2 | var connect = require('connect') 3 | var _bs = require('browser-stream') 4 | var assert = require('assert') 5 | var es = require('event-stream') 6 | 7 | var app = skates() 8 | .use(connect.static(__dirname)) 9 | .on('connection', function (sock) { 10 | sock.on('ping', function () { 11 | console.log('ping') 12 | sock.emit('pong') 13 | }) 14 | var bs = _bs(sock) 15 | bs.on('connection', function (stream) { 16 | console.log('STREAM OPTIONS', stream) 17 | //stream.pipe(es.stringify()).pipe(es.log()) 18 | var i = 0 19 | var t 20 | t = setInterval(function () { 21 | stream.write({item: ++i}) 22 | console.log(i) 23 | /* if(i < 21) 24 | return 25 | stream.end()*/ 26 | // clearInterval(t, 200) 27 | }, 500) 28 | stream.on('close', function (){ 29 | clearInterval(t) 30 | }) 31 | stream.on('error', function () {}) 32 | }) 33 | }) 34 | .listen(3000, function () { 35 | console.log('BrowserStream example running on port 3000') 36 | }) 37 | -------------------------------------------------------------------------------- /example/skates/simple.js: -------------------------------------------------------------------------------- 1 | //am having trouble getting socket.io to work. 2 | 3 | 4 | var sio = require('socket.io') 5 | var http = require('http') 6 | var server = http.createServer(function (req, res) { 7 | 8 | res.end('HELLO') 9 | }) 10 | sio.listen(server) 11 | server.listen(3000) 12 | 13 | -------------------------------------------------------------------------------- /example/socket.io/client.js: -------------------------------------------------------------------------------- 1 | //var io = require('socket.io') 2 | var bs = require('browser-stream')(io.connect('http://localhost:3000')) 3 | 4 | var domnode = require('domnode')('#body', '
{{item}}
') 5 | 6 | rs = bs.createStream('whatever', 7 | {options: true, random: Math.random()}) 8 | 9 | rs.pipe(domnode) 10 | 11 | rs.on('data', function(d) { 12 | console.log('>>>', d) 13 | }) 14 | 15 | rs.write('hello') 16 | rs.write('goodbye') 17 | -------------------------------------------------------------------------------- /example/socket.io/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/socket.io/server.js: -------------------------------------------------------------------------------- 1 | var io = require('socket.io') 2 | var connect = require('connect') 3 | var app = connect.createServer() 4 | var http = require('http') 5 | var io = require('socket.io') 6 | var _bs = require('browser-stream') 7 | var assert = require('assert') 8 | var browserify = require('browserify') 9 | var es = require('event-stream') 10 | var fs = require('fs') 11 | //TODO: allow both _bs(io) and _bs(connection) 12 | 13 | app = app 14 | .use(connect.static(__dirname)) 15 | .use(browserify({entry: __dirname+'/client.js', cache: true})) 16 | 17 | io = io.listen(app.listen(3000, function () { 18 | console.log('BrowserStream example running on port 3000') 19 | })) 20 | 21 | io.on('connection', function (sock) { 22 | var bs = _bs(sock) 23 | bs.on('connection', function (stream) { 24 | stream.pipe(es.stringify()).pipe(es.log()) 25 | var i = 0 26 | var t 27 | t = setInterval(function () { 28 | stream.write({item: ++i}) 29 | if(i < 21) 30 | return 31 | stream.end() 32 | clearInterval(t, 200) 33 | }) 34 | }) 35 | }) 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/socket.io/simple.js: -------------------------------------------------------------------------------- 1 | //am having trouble getting socket.io to work. 2 | 3 | 4 | var sio = require('socket.io') 5 | var http = require('http') 6 | var server = http.createServer(function (req, res) { 7 | 8 | res.end('HELLO') 9 | }) 10 | sio.listen(server) 11 | server.listen(3000) 12 | 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | var Stream = require('stream').Stream 3 | var EventEmitter = require('events').EventEmitter 4 | 5 | /* 6 | Although sockjs and browserchannel are consistent on the client 7 | they are different on the server, while socket.io are consistent on the client and the server... 8 | 9 | I wrote a module that wraps sockjs (could easily adapt to wrap any WebSocket Compatibility Layer) 10 | in an interface that is like an two remote event emitters, 11 | except that A.on('event',listener) is triggered by B.emit('event') 12 | 13 | (see the tests) 14 | 15 | 16 | 17 | */ 18 | 19 | module.exports = function (sock) { 20 | 21 | var streams = {} 22 | /* 23 | socket.io is a bad pattern. it's a single lib, 24 | but it _should_ be a small ecosystem of tools. 25 | 26 | when you call createStream() it should return a stream immediately. 27 | thats how it is everywhere else in node. 28 | 29 | but, if there is a disconnection, all the streams should emit 'end'. 30 | then emit 'error' if you try to write to them after? 31 | 32 | my problem wasn't in socket.io... it was in browser-stream. 33 | */ 34 | 35 | var e = new EventEmitter () 36 | var count = 1 37 | var DATA = '' 38 | , END = '.' 39 | , PAUSE = '?' 40 | , RESUME = '*' 41 | , ERROR = '!' 42 | 43 | function _createStream(opts) { 44 | var s = new Stream(), ended = false 45 | //if either w or r is false, def will be false 46 | var def = !opts.writable && !opts.readable 47 | s.readable = opts.readable || def 48 | s.writable = opts.writable || def 49 | if(opts.opts || opts.options) 50 | s.options = opts.opts || opts.options 51 | s.name = opts.name 52 | //if(streams[opts.name]) 53 | //throw new Error('stream with name "'+opts.name + '" already exists') 54 | streams[opts.name] = true 55 | 56 | s._id = opts._id || count ++ 57 | 58 | function onPause () { 59 | s.paused = true 60 | } 61 | 62 | function onResume () { 63 | s.paused = false 64 | s.emit('drain') 65 | } 66 | 67 | function cleanup () { 68 | s.writable = s.readable = false 69 | delete streams[s.name] 70 | sock.removeListener(PAUSE + id, onPause) 71 | sock.removeListener(RESUME + id, onResume) 72 | sock.removeListener(DATA + id, onData) 73 | sock.removeListener(END + id, onEnd) 74 | sock.removeListener('disconnect', onDisconnect) 75 | } 76 | 77 | function onDisconnect () { 78 | if(!ended) { 79 | s.emit('error', new Error('unexpected disconnection (call end() first)')) 80 | cleanup() 81 | } 82 | ended = true 83 | } 84 | 85 | sock.on('disconnect', onDisconnect) 86 | 87 | function onData(data) { 88 | s.emit('data', data) 89 | } 90 | 91 | function onEnd () { 92 | if(!ended) { 93 | s.emit('end') 94 | cleanup() 95 | } 96 | } 97 | 98 | s.destroy = function () { 99 | ended = true 100 | cleanup() 101 | s.emit('close') 102 | } 103 | 104 | if(s.writable) { 105 | var id = s._id 106 | s.write = function (data) { 107 | if(ended) throw new Error('write to ended stream') 108 | sock.emit(DATA + id, data) 109 | return !s.paused 110 | } 111 | 112 | s.end = function (data) { 113 | if(data != null) this.write(data) 114 | if(!ended) sock.emit(END + id) 115 | s.destroy() 116 | } 117 | 118 | sock.on(PAUSE + id, onPause) 119 | sock.on(RESUME + id, onResume) 120 | 121 | //TODO but remember, some errors are circular. 122 | //sock.on(ERROR + id, function (error) { 123 | // s.emit('error', error) 124 | //} 125 | 126 | } 127 | 128 | if(s.readable) { 129 | var id = s._id 130 | s.readable = true 131 | sock.on(DATA + id, onData) 132 | sock.on(END + id, onEnd) 133 | 134 | s.pause = function () { 135 | s.paused = true 136 | if(ended) return 137 | sock.emit(PAUSE + id) 138 | } 139 | 140 | s.resume = function () { 141 | s.paused = false 142 | if(ended) return 143 | sock.emit(RESUME + id) 144 | } 145 | } //end of setting up readable stream 146 | 147 | return s 148 | } 149 | 150 | e.createWriteStream = function (name, opts) { 151 | return this.createStream(name, opts, {writable: true}) 152 | } 153 | 154 | e.createReadStream = function (name, opts) { 155 | return this.createStream(name, opts, {readable: true}) 156 | } 157 | 158 | e.createStream = function (name, opts, settings) { 159 | settings = settings || {} 160 | settings.name = name 161 | settings.options = opts 162 | 163 | var s = _createStream(settings) //defaults to readable and writable 164 | var _opts = {name: name, _id: s._id} 165 | if(opts) { 166 | _opts.options = opts 167 | s.options = opts 168 | } 169 | 170 | if(s.readable) 171 | _opts.writable = true 172 | if(s.writable) 173 | _opts.readable = true 174 | sock.emit('CREATE_STREAM', _opts) 175 | return s 176 | } 177 | 178 | sock.on('CREATE_STREAM', function (settings) { 179 | var s = _createStream(settings) 180 | e.emit('connection', s, settings.options) 181 | }) 182 | 183 | return e 184 | } 185 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Dominic Tarr (dominictarr.com)", 3 | "name": "browser-stream", 4 | "description": "pipe streams through Socket.io", 5 | "version": "0.1.10", 6 | "homepage": "https://github.com/dominictarr/browser-stream", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/dominictarr/browser-stream.git" 10 | }, 11 | "engines": { 12 | "node": "0.6" 13 | }, 14 | "dependencies": { 15 | "event-stream": "~2.0.0" 16 | }, 17 | "devDependencies": { 18 | "socket.io": "0.9", 19 | "express": "2.5", 20 | "domnode": "0", 21 | "browserify": "~1.13", 22 | "domready": "~0.2.11", 23 | "skates": "0.0.1", 24 | "tap": "~0.2.5", 25 | "remote-events": "0.0.0" 26 | }, 27 | "scripts": { 28 | "test": "tap test/*.js" 29 | }, 30 | "optionalDependencies": {} 31 | } 32 | -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | # BrowserStream 2 | 3 | use `Stream#pipe` to send data to clients or servers. 4 | 5 | wrap `socket.io` (or another web socket abstraction that gives a remote `EventEmitter` api) 6 | with a interface for creating streams. 7 | 8 | ### DEPRECIATED 9 | 10 | use [mux-demux](http://github.com/dominictarr/mux-demux) with [shoe](http://github.com/substack/shoe) 11 | 12 | I'm not gonna use or maintain this any more. If you want to use and maintain this module email me and I'll 13 | add you as owner. 14 | 15 | ## install 16 | 17 | ``` 18 | npm install browser-stream 19 | 20 | ``` 21 | 22 | ## example 23 | 24 | ### client 25 | 26 | ``` js 27 | var bs = require('browser-stream')(io.connect('http://localhost:3000')) 28 | var domnode = require('domnode') //see https://github.com/maxogden/dominode 29 | var opts = {options: 'pass an optional object with the createReadStream message. maybe useful!'}) 30 | //pipe the 'whatever' stream to the dom with dominode. 31 | bs.createReadStream('whatever', options) 32 | .on('error', function (err) { 33 | //unexpected disconnect 34 | console.error(err) 35 | }) 36 | .pipe(domnode('#list', '
  • ')) 37 | 38 | ``` 39 | 40 | ### server 41 | 42 | ``` js 43 | 44 | io = io.listen(app) //see https://github.com/LearnBoost/socket.io 45 | var _bs = require('browser-stream') 46 | 47 | io.sockets.on('connection', function (sock) { 48 | 49 | var bs = _bs(sock) 50 | 51 | var whatever = new Stream() // SOME KIND OF STREAM 52 | bs.on('connection', function (stream, opts) { 53 | if(stream.name == 'whatever') 54 | whatever.pipe(stream) 55 | //stream.options -- will be the same as `opts` from the client side! 56 | stream.on('error', function (err) { 57 | //the client has unexpectedly disconnected. tidy up! 58 | console.error(err) 59 | }) 60 | }) 61 | 62 | } 63 | 64 | ``` 65 | 66 | this will connect `whatever` to the `Dominode` stream, rendering it, in the browser! 67 | 68 | ## API 69 | 70 | ``` js 71 | 72 | var _bs = require('browser-stream') 73 | 74 | io.sockets.on('connection', function (sock) { 75 | var bs = _bs(sock) 76 | bs.on('connection', function (stream) { 77 | //stream is a pipable node.js `Stream` instance 78 | }) 79 | }) 80 | 81 | ``` 82 | 83 | ### createReadStream (name, options) 84 | 85 | 86 | 87 | open a `ReadableStream` from the other side. 88 | returns a `ReadableStream`. 89 | the other side of connection will emit a writable stream that is connected to this stream. 90 | 91 | ### createWriteStream (name, options) 92 | 93 | open a `WritableStream` to the other side. 94 | returns a `WritableStream`, the other side will emit a `ReadableStream` connected to this stream. 95 | 96 | ### createStream (name, options) 97 | 98 | open a `Stream` to the other side which is both readable and writable. 99 | returns a `Stream`, the other side will emit a `Stream` connected to this stream. 100 | 101 | > note to self, references to a class (`Stream`) should be capitalized, and in backticks. 102 | > references to an instance should be lowercase, and not in backticks unless refuring to 103 | > a specific variable in a code example. 104 | -------------------------------------------------------------------------------- /test/consistent.js: -------------------------------------------------------------------------------- 1 | var es = require('event-stream') 2 | var a = require('assertions') 3 | 4 | module.exports = 5 | function consistent(test) { 6 | test = test || a.deepEqual 7 | var stream = es.through() 8 | var chunks = 0 9 | stream.on('data', function () { 10 | chunks ++ 11 | }) 12 | stream.createSlave = function () { 13 | var expected = [], count = 0, ended = false 14 | stream.on('data', function (data) { 15 | expected.push(data) 16 | }) 17 | var slave = es.through() 18 | slave.on('data', function (data) { 19 | a.greaterThan(expected.length, 0, 'slave stream did not expect write') 20 | a.equal(ended, false, 'slave expected stream not to have ended') 21 | var next = expected.shift() 22 | count ++ 23 | test(next, data) 24 | }) 25 | //it's okay to pass data to end(data) 26 | //but never emit('end', data) 27 | slave.on('end', function () { 28 | ended = true 29 | a.equal(expected.length, 0, 'slave stream expected 0 more writes') 30 | }) 31 | slave.validate = function (message) { 32 | a.equal(count, chunks, 'slave must recieve same number of chunks as master') 33 | a.ok(count) 34 | } 35 | return slave 36 | } 37 | return stream 38 | } 39 | -------------------------------------------------------------------------------- /test/disconnections.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | connect two streams. 4 | 5 | on a disconnect, both streams should emit 'close' 6 | 7 | */ 8 | 9 | var a = require('assertions') 10 | var RemoteEventEmitter = require('remote-events') 11 | var consistent = require('./consistent') 12 | var bs = require('..') 13 | var es = require('event-stream') 14 | 15 | function randomNumberStream (max, count) { 16 | count = count || 20 17 | max = max || 10 18 | return es.readable(function (i, cb) { 19 | this.emit('data', Math.random() * max) 20 | if(i > count) 21 | this.emit('end') 22 | cb() 23 | }) 24 | } 25 | 26 | ;(function () { 27 | 28 | var master = consistent() 29 | var slave = master.createSlave() 30 | 31 | var _client = new RemoteEventEmitter() 32 | var _server = new RemoteEventEmitter() 33 | 34 | _client.getStream().pipe(_server.getStream()).pipe(_client.getStream()) 35 | var client = bs(_client) 36 | var server = bs(_server) 37 | var count = 0, dCount = 1 38 | server.on('connection', function (stream) { 39 | a.equal(stream.name, 'disconnect1') 40 | stream 41 | .on('error', function () { 42 | console.log('<< ERROR') 43 | }) 44 | .pipe(slave) 45 | .pipe(es.log('<<'))//.pipe(stream) 46 | .on('data', function () { 47 | dCount ++ 48 | }) 49 | .on('end', function () { 50 | a.equal(count, dCount, 'each stream should see the same items') 51 | console.log('<< END') 52 | }) 53 | }) 54 | var rns = randomNumberStream() 55 | rns 56 | .on('data', function (data) { 57 | if(++ count < 12) return 58 | if(_client.connected) { 59 | _client.disconnect() 60 | console.log('DISCONNECT') 61 | } 62 | console.log('DATA', data, count) 63 | }) 64 | .pipe(master) 65 | .pipe(es.log('>>')) 66 | .pipe(client.createWriteStream('disconnect1') 67 | .on('error', function () {rns.destroy(); console.log('>> ERROR')}) 68 | .on('end', function () { 69 | //END should always be EMITTED 70 | //RIGHT? 71 | a.equal(count, dCount, 'each stream should see the same items') 72 | console.log('>> END') 73 | //not all the events are emitted, 74 | //but since the streams are destroyed, 75 | //and piping stops then they end up with 76 | //the same data through them. 77 | slave.validate() 78 | })) 79 | /* 80 | THERE are some problems with streams that close. 81 | or rather, SHOULD close. 82 | */ 83 | 84 | })(); 85 | 86 | 87 | ;(function simple () { 88 | 89 | var _client = new RemoteEventEmitter() 90 | var _server = new RemoteEventEmitter() 91 | _client.getStream().pipe(_server.getStream()).pipe(_client.getStream()) 92 | 93 | var client = bs(_client) 94 | var server = bs(_server) 95 | 96 | var r1 = Math.random() 97 | server.on('connection', function (stream) { 98 | stream.on('data', function (data) { 99 | a.equal(data, r1) 100 | console.log('data') 101 | }) 102 | stream.on('end', function () { 103 | console.log('end') 104 | }) 105 | }) 106 | 107 | c = client.createWriteStream() 108 | c.write(r1) 109 | c.end() 110 | 111 | })(); 112 | 113 | ;(function disconnect () { 114 | 115 | var _client = new RemoteEventEmitter() 116 | var _server = new RemoteEventEmitter() 117 | _client.getStream().pipe(_server.getStream()).pipe(_client.getStream()) 118 | 119 | _client.on('disconnect', function () { 120 | console.log('CLIENT DISCONNECT') 121 | }) 122 | _server.on('disconnect', function () { 123 | console.log('SERVER DISCONNECT') 124 | }) 125 | var client = bs(_client) 126 | var server = bs(_server) 127 | 128 | var randoms = [] 129 | function rand() { 130 | var r 131 | randoms.push(r = Math.random()) 132 | return r 133 | } 134 | var clientErr = false, serverErr = false 135 | process.on('exit', function () { 136 | a.ok(clientErr, 'expected client to emit an error') 137 | a.ok(serverErr, 'expected server to emit an error') 138 | console.log('end point emitted errors correctly') 139 | }) 140 | 141 | var streams = 0, ended = 0 142 | server.on('connection', function (stream) { 143 | streams ++ 144 | stream 145 | .on('data', function (data) { 146 | var r 147 | a.equal(data, r = randoms.shift()) 148 | console.log('data', r) 149 | }) 150 | .on('error', function () { 151 | //I'm expecting this 152 | serverErr = true 153 | a.equal(streams, 1) 154 | console.log('error!') 155 | }) 156 | var r = Math.random() 157 | var _ended = false 158 | stream.on('end', function () { 159 | a.ok(!_ended, 'end MUST only be emitted once') 160 | _ended = true 161 | a.equal(streams, ++ ended) 162 | console.log('end!') 163 | }) 164 | }) 165 | 166 | var c = client.createWriteStream('A') 167 | c.on('error', function () { 168 | //expecting this! 169 | clientErr = true 170 | console.log('error') 171 | }) 172 | 173 | c.write(rand()) 174 | c.write(rand()) 175 | c.write(rand()) 176 | c.write(rand()) 177 | _client.disconnect() 178 | 179 | if(c.writable) 180 | c.write(rand()) 181 | a.throws(function () { c.write(rand()) }) 182 | 183 | })(); 184 | 185 | 186 | ;(function disconnect2 () { 187 | console.log('disconnect2') 188 | var _client = new RemoteEventEmitter() 189 | var _server = new RemoteEventEmitter() 190 | 191 | var client = bs(_client) 192 | var server = bs(_server) 193 | 194 | var randoms = [] 195 | function rand() { 196 | var r 197 | randoms.push(r = Math.random()) 198 | return r 199 | } 200 | var streams = 0 201 | server.on('connection', function (s) { 202 | s.write(rand()) 203 | s.write(rand()) 204 | s.write(rand()) 205 | s.write(rand()) 206 | s.end() 207 | a.throws(function () { s.write(Math.random()) })//this should be ignored 208 | }) 209 | 210 | c = client.createReadStream() 211 | c.on('data', function (data) { 212 | var r 213 | a.equal(data, r = randoms.shift()) 214 | console.log('data', r) 215 | }) 216 | .on('end', function () { 217 | console.log('end') 218 | }) 219 | _client.getStream().pipe(_server.getStream()).pipe(_client.getStream()) 220 | 221 | })(); 222 | -------------------------------------------------------------------------------- /test/simple.js: -------------------------------------------------------------------------------- 1 | 2 | var a = require('assertions') 3 | var RemoteEventEmitter = require('remote-events') 4 | var consistent = require('./consistent') 5 | var es = require('event-stream') 6 | var _bs = require('..') 7 | 8 | /* 9 | socket.io behaves like two linked EventEmitters. 10 | calling emit on one, triggers listeners on the other. 11 | 12 | (see RemoteEventEmitter) 13 | 14 | test that two streams match. 15 | 16 | create a master stream and slave streams, 17 | assert that every chunk written to the master 18 | is eventually written to the slave. 19 | 20 | OKAY, my disconnection error is in HERE 21 | somewhere in here, things are breaking after reconnecting. 22 | what is it? 23 | 24 | 25 | */ 26 | 27 | 28 | function pair(f) { 29 | var a = new RemoteEventEmitter() 30 | var b = new RemoteEventEmitter() 31 | a.getStream().pipe(b.getStream()).pipe(a.getStream()) 32 | 33 | return [a, b] 34 | } 35 | 36 | function randomNumberStream (max, count) { 37 | count = count || 20 38 | max = max || 10 39 | return es.readable(function (i, cb) { 40 | this.emit('data', Math.random() * max) 41 | if(i > count) 42 | this.emit('end') 43 | cb() 44 | }) 45 | } 46 | 47 | ;(function simple () { 48 | 49 | var p = pair() 50 | var server = _bs(p.pop()) 51 | var client = _bs(p.pop()) 52 | 53 | var master = consistent() 54 | var slave = master.createSlave() 55 | 56 | server.on('connection', function (stream) { 57 | a.equal(stream.name, 'simple') 58 | stream 59 | .pipe(slave) 60 | }) 61 | 62 | randomNumberStream() 63 | .pipe(master) 64 | .pipe(client.createWriteStream('simple')) 65 | 66 | process.on('exit', slave.validate) 67 | 68 | })(); 69 | 70 | //return 71 | ;(function through () { 72 | 73 | var p = pair() 74 | var server = _bs(p.shift()) 75 | var client = _bs(p.shift()) 76 | 77 | var master = consistent() 78 | var slave1 = master.createSlave() 79 | var slave2 = master.createSlave() 80 | 81 | server.on('connection', function (stream) { 82 | a.equal(stream.name, 'through') 83 | stream.pipe(slave1).pipe(stream) //ECHO 84 | }) 85 | 86 | randomNumberStream() 87 | .pipe(master) 88 | .pipe(client.createStream('through')) 89 | .pipe(slave2) 90 | .on('end', function () { 91 | slave1.validate() 92 | slave2.validate() 93 | console.log('slave 1,2 valid') 94 | }) 95 | 96 | })(); 97 | 98 | 99 | // names do not have to be unique. 100 | // should create two seperate streams. 101 | 102 | 103 | ;(function double () { 104 | var p = pair() 105 | var server = _bs(p.shift()) 106 | var client = _bs(p.shift()) 107 | 108 | var master1 = consistent() 109 | var slave1 = master1.createSlave() 110 | var master2 = consistent() 111 | var slave2 = master2.createSlave() 112 | 113 | server.on('connection', function (stream) { 114 | a.equal(stream.name, 'through') 115 | stream.pipe(stream) //ECHO 116 | }) 117 | 118 | randomNumberStream() 119 | .pipe(master1) 120 | .pipe(client.createStream('through')) 121 | .pipe(slave1) 122 | .on('end', function () { 123 | slave1.validate() 124 | console.log('slave1 valid') 125 | }) 126 | 127 | //okay! this is breaking 128 | randomNumberStream() 129 | .pipe(master2) 130 | .pipe(client.createStream('through')) 131 | .pipe(slave2) 132 | .on('end', function () { 133 | slave2.validate() 134 | console.log('slave2 valid') 135 | }) 136 | })(); 137 | 138 | /* 139 | since I'm here, I may as well implement pausing 140 | etc. 141 | 142 | pass through pause, so that it can be used 143 | to control stuff like scrolling. 144 | */ 145 | 146 | /* 147 | if a function respects pause, that means that on pause(), 148 | the next write should return false. 149 | 150 | then, on resume() the next write should return true 151 | 152 | this case is pretty simple. 153 | */ 154 | 155 | ;(function passesPauseThrough(stream) { 156 | var p = pair() 157 | var server = _bs(p.pop()) 158 | var client = _bs(p.pop()) 159 | 160 | server.on('connection', function (stream) { 161 | a.equal(stream.name, 'paused') 162 | stream 163 | var i = 0 164 | stream.on('data', function () { 165 | if(i++ % 2) 166 | stream.pause() 167 | else //if (stream.paused) 168 | stream.resume() 169 | }) 170 | }) 171 | 172 | master = (client.createWriteStream('paused')) 173 | 174 | a.equal(master.write('hello'), true, 'should be free') 175 | a.equal(master.write('paused now'), false, 'should be paused') 176 | a.equal(master.write('hello'), true, 'should be free2') 177 | a.equal(master.write('paused now'), false, 'should be paused') 178 | 179 | master.end() 180 | console.log('pause is correct') 181 | })(); 182 | 183 | --------------------------------------------------------------------------------