dominictarr/mux-demux

only write on .pipe

juliangruber opened this issue · 4 comments

Me and @ralphtheninja had this problem that MuxDemux wrote its meta infos before mux-demux was piped anywhere, like:

// Server
var mdm = require('mux-demux')()
net.createServer(function (c) {
  c.pipe(mdm).pipe(c)
}).listen(3000)

// Client
var mdm = require('mux-demux')()
mdm.pipe(net.connect(3000)).pipe(mdm)

If you inspect the traffic you see that the server-mdm wrote its meta before the connection to the client was up. What I saw you doing to avoid this is you create the mdm object inside the net.createServer-handler.

That works for this case, but libraries using mux-demux (like multilevel) have the problem that they can't expose a simple stream interface anymore.

// Breaks
var server = multilevel.server('/db')
net.createServer(function (c) {
  c.pipe(server).pipe(c)
})

// Works
net.createServer(function (c) {
  c.pipe(multilevel.server('/db')).pipe(c)
})

multilevel directly exports its underlying MuxDemux-instance. What I could do is wrap that in a paused duplex-stream and listen for mdm.on('pipe', fn) but I think that this should happen inside mux-demux, in order not to leak its implementation details.

If you want I can hack up a PR that for every pipe event emits its stream meta.

You should create a mux-demux immediately before you pipe it, or explicitly pause it.

This will work:

var server = multilevel.server('/db').pause()
net.createServer(function (c) {
  c.pipe(server.resume()).pipe(c)
})

Just tried it in https://github.com/juliangruber/multilevel/blob/master/test/util.js

This doesn't work:

    var server = multilevel.server(__dirname + '/db').pause()
    net.createServer(function (con) {
      con.pipe(server.resume()).pipe(con)
    }).listen(port)

oh, oops

just realized, resume() resumes immediately, so you need to

var server = multilevel.server(__dirname + '/db').pause()
    net.createServer(function (con) {
      con.pipe(server).pipe(con)
      server.resume()
    }).listen(port)

sorry.

returning it prepaused is unusual. If really want to do that make sure you document it CLEARLY.

Generally, you shouldn't need to create a stream before you pipe it, so this is best:

net.createServer(function (con) {
   con.pipe(multilevel.server(__dirname + '/db')).pipe(con)
}).listen(port)

actually, this is wrong:

var server = multilevel.server(__dirname + '/db').pause()
    net.createServer(function (con) {
      con.pipe(server).pipe(con)
      server.resume()
    }).listen(port)

because when multiple connections join, they will all be piped onto the same stream.
instead, you want a stream from multilevel for each connection.

Also, can you alias multilevel.server and multilevel.client to multilevel.createServerStream and multilevel.createClientStream as it will make their behaviour more obvious?

ok. @substack I think that "you shouldn't need to create a stream before you pipe it" pattern should go in the stream handbook?