I have SPDY flow control from version 3 of the protocol working in node-spdy. Tonight, I aim to make sure that I have not introduced unnecessary complexity to node-spdy in my implementation.
Flow control in SPDY is per-stream, so I added the internal data transfer window calculation and backlog to the
Stream constructor:function Stream(connection, frame) {
// ...
// Lock data
this.locked = false;
this.lockBuffer = [];
// ...
this._paused = false;
this._buffer = [];
this.internalWindowSize = Math.pow(2,16);
this._backlog = [];
// ...
}There is a lot going in the Stream constructor, but the properties that concerned me last night were the lockBuffer and the _buffer—both sound similar to the _backlog that I added.The
lockBuffer turns out to be a buffer of callbacks, not bytes, so that won't help me. The _paused / _buffer properties, on the other hand, may prove useful. The associated resume() method even does similar work of draining the buffer after some event triggers the resumption of the stream:Stream.prototype.resume = function resume() {
if (!this._paused) return;
this._paused = false;
var self = this,
buffer = this._buffer;
this._buffer = [];
process.nextTick(function() {
buffer.forEach(function(chunk) {
self._read(chunk);
});
});
};Ah, but wait, that is a read-operation. Dang it, I ran into this yesterday as well. Node.js allows it readable streams to pause, but not the writable streams. The _paused and _buffer properties are simply supporting node-spdy's attempt to expose a complete Stream interface.So it looks as though I need a separate transfer window buffer after all. I do think that a rename is order, however. The name
backlog made a tiny bit of sense when I was mucking with global variables, but now I think a rename is in order:// ... this._transferWindowSize = Math.pow(2,16); this._transferWindowBuffer = []; // ...That should make life easier.
Before calling it a night, I double check that my connections are being closed. When upstream signals the
end() of a connection, I had to conditionally close the stream. It turns out the _write() method already has a built-in call to close()—separate from the call in end()—when a DATA FIN is seen:Stream.prototype._writeData = function _writeData(fin, buffer) {
// guard against data transfer window exhaustion...
if (fin) this.close();
// ...
};
A quick check verifies that the stream really is being closed properly. The following console.log() in close():Stream.prototype.close = function close() {
console.log(this.id + " is closed");
this.destroy();
};Results in all of the streams getting closed:➜ express-spdy-test node app Express server listening on port 3000 in development mode 1 is closed 1 is closed 3 is closed 3 is closed 5 is closed 5 is closed 7 is closed 9 is closed 11 is closed 11 is closedI am not fussed over the duplicate closes for the non-flow controlled (non-buffered) output streams. Closing a second time has no effect. As long as no resources remain open and allocated, everything should be fine.
Day #376
No comments:
Post a Comment