Detect whether a http.ServerResponse / Writable Stream has an empty write queue

For my first node.js project ever I'm creating a proxy for some mjpeg output. Now, some clients can not read the stream as fast as the server is getting it, so I'm dropping frames to let them catch up. I'm currently using the drain event of an http.ServerResponse object, more or less like below, but because the drain gets called an awful lot, I'm wondering whether there is a better / more lightweight approach (I could not find on in the Writeable Stream docs)

httpServer.on('request',function(request,response){
    //skipped setting some headers etc.
    res.socket.on('drain', function(){
        res.drained = true;
        res.draincounter = 0;
    });
    //followed by some unconditional first write (set drained to false first)
    res.drained = false;
    res.write(someData);
};

Now, writing frames to the client happens like this:

writeFrame = function(res,frame){
   if(res.drained){
       //nothing in queue, ready for new data

       //first set drained to false
       res.drained = false;
       //and feed it more data
       res.write(frame, 'binary');
   } else if(res.draincounter > maxDroppedFrames){
       //to many dropped frames in a row, disconnect
       res.end();
   } else {
       res.draincounter++;
   }
}

But this uses custom properties, and a lot of calls to the drain event (which may be OK but with my limited experience doesn't sit well with me). In a nutshell, my question is this:

Is there a more effective way to detect a Writable Stream's write buffers are empty?


Edit: After restarting this project with node 0.10.18 after being shut down & dormant for a while, there's something terribly wrong, as I no longer get drain events (at least, not in the way as described above). For now, I check if res.socket.bufferSize is above a certain mark to decide write a mjpeg frame or not, the draincounter still works. The advocated pipe() from the stream docs in the comment or not a solution in my case, as the problem is not writing all data on the clients speed, but drop data when the client's speed is lower then the source stream.

Can anyone enlighten me as to what is the proper way to handle dropping data on backpressure (if this is not it)?

I personally would check res._writableState directly, but at this point you're diving into node source which is subject to change, but i doubt much of this will change in the future.

but then this is when things become an art. there are so many different flags you can check: https://github.com/joyent/node/blob/master/lib/_stream_writable.js#L40

if you want to check the current size of the buffer, you can check res._writableState.length - https://github.com/joyent/node/blob/master/lib/_stream_writable.js#L79.

this would only write if the buffer is 0.

function writeFrame(res, frame) {
  if (res._writableState.length) {
    if (++res.droppedFrames > maxDroppedFrames) res.end()
  } else {
    res.write(frame)
  }
}

if you want to write only when the buffer is lower than the high water mark (ie it doesn't need a drain), you can just check res._writableState.needDrain - https://github.com/joyent/node/blob/master/lib/_stream_writable.js#L57

this would only write if the buffer is below the high water mark:

function writeFrame(res, frame) {
  if (res._writableState.needDrain) {
    if (++res.droppedFrames > maxDroppedFrames) res.end()
  } else {
    res.write(frame)
  }
}

there are other flags you may be interested. the main point is that you don't have to listen to any drain event or do any internal state flagging.

a side note, you should probably only care about maxDroppedFrames / second, not the absolute total number. you also shouldn't write with binary encoding, just write the raw buffer instead.

also, this probably only works in node 0.10+