I had thought to mess about with spdy/2 and spdy/3 side-by-side some more, but I seem to have hit a snag. I disable spdy/3 in Chrome's
about:flags
:But, when I reload my express.js site, powered by node-spdy, I am still using spdy/3:
That's what I get for mucking with experimental flags.
No matter how many times I try flipping that bit and restarting the browser, I continue to get spdy/3 connections. Hopefully that will get fixed in some future Chrome update.
Anyhow, flow control.
The SPDY v3 specifiction introduces flow control in the form of a WINDOW_UPDATE control frame.
In node-spdy parlance, this will look something like:
// // ### function windowUpdateFrame (id) // #### @id {Buffer} WindowUpdate ID // Sends WINDOW_UPDATE frame // Framer.prototype.windowUpdateFrame = function windowUpdateFrame(id, delta) { var header = new Buffer(16); header.writeUInt32BE(0x80030009, 0, true); // Version and type header.writeUInt32BE(0x00000008, 4, true); // Length header.writeUInt32BE(id & 0x7fffffff, 8, true); // ID header.writeUInt32BE(delta & 0x7fffffff, 12, true); // delta return header; };The first four octets are version and type, the second 4 are the length (which is always 8 for WINDOW_UPDATE), the next four are for the stream ID, and the last four are the amount of data that can be sent beyond the initial window size (established in a SETTINGS frame). Actually, take my definition of delta with a grain of salt—I really do not have a firm grasp on the concept yet, which is why I am experimenting.
I think my first experiment will cause protocol errors. Even so, it seems the smallest step I can take. I am going to send a WINDOW_UPDATE immediately before my first SYN_REPLY. I think that this is wrong because the recipient of the data (the browser) should be sending WINDOW_UPDATEs per the spec, not the server. If I get a protocol error, then I can hope that I somewhat understand this and can build on it tomorrow. If I do not get a protocol error, then I can go back to the drawing board.
So, back in the server, I manually write a WINDOW_UPDATE after initializing the first SPDY stream:
function Connection(socket, pool, options) { // ... this.parser.on('frame', function (frame) { // ... // Create new stream if (frame.type === 'SYN_STREAM') { // ... if (frame.id == 1) { self.write(self.framer.windowUpdateFrame(frame.id, 8)); } // ... } // ... } // ... }I specify a window size of 8 bytes. That is intentionally small so that it might have a noticeable effect should it be accepted.
So I restart the server, reload the page and...
SPDY_SESSION_SYN_STREAM --> flags = 1 --> :host: localhost:3000 :method: GET :path: / :scheme: https :version: HTTP/1.1 .... --> id = 1 SPDY_SESSION_RECV_SETTING --> flags = 1 --> id = 4 --> value = 100 SPDY_SESSION_RECEIVED_WINDOW_UPDATE --> delta = 8 --> stream_id = 1The browser gets, and seemingly accepts, my WINDOW_UPDATE of a delta of 8 bytes. The rest of the SPDY conversation takes place as if this frame were not present. Specifically, the server issues a SYN_REPLY with more than 8 bytes in the DATA frame and no errors are raised:
SPDY_SESSION_SYN_REPLY --> flags = 0 --> :status: 200 :version: HTTP/1.1 --> id = 1 SPDY_SESSION_RECV_DATA --> flags = 0 --> size = 339 --> stream_id = 1 SPDY_SESSION_RECV_DATA --> flags = 0 --> size = 0 --> stream_id = 1I am not surprised that the SYN_REPLY and subsequent DATA are unchanged—I have not done anything in the server to alter their behavior. I am also not surprised that the browser does not barf on receiving them—the WINDOW_UPDATE is sent by the recipient, but, in this case, the recipient is the browser which sent no WINDOW_UPDATE and should therefore be unaffected.
The only thing that surprises me is that the client accepted the WINDOW_UPDATE in the first place. Then again, SPDY conversations are bi-directional, so maybe that should not surprise me. Technically, the server could be a recipient of DATA (e.g. a POST body).
Satisfied that, if nothing else, my WINDOW_UPDATE is crafted well, I call it a night here. Tomorrow, I plan to start by POSTing data after a WINDOW_UPDATE to see if it has any effect. Then the fun begins...
Day #369
No comments:
Post a Comment