Thursday, April 26, 2012

Branch for SPDY/3

‹prev | My Chain | next›

Yesterday I finally got a version 3 flavored SPDY conversation to take place. I had enabled spdy/3 in Chrome's about:flags the other day, but the struggled through conversion of node-spdy's version 2 implementation to account for incompatibilities between the two versions and then a dumb mistake in assembling the headers.

Today, I clean up.

First, I remove every console.log() statement. There were a lot.

Then I make sure that everything is still working. Stranger things have happened than everything breaking after removing console.log() statements. Thankfully that is not the case this time. I still have my spdy/3 session:


And, better still, no errors in the SPDY tab of about:net-internals:
....
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 = 1
SPDY_SESSION_SYN_STREAM
--> flags = 1
--> :host: localhost:3000
    :method: GET
    :path: /stylesheets/style.css
    :scheme: https
    :version: HTTP/1.1
    accept: text/css,*/*;q=0.1
    accept-charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
    accept-encoding: gzip,deflate,sdch
    accept-language: en-US,en;q=0.8
    cache-control: no-cache
    cookie: [value was stripped]
    pragma: no-cache
    referer: https://localhost:3000/
    user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.9 (KHTML, like Gecko) Chrome/20.0.1115.1 Safari/536.9
--> id = 3
...
There is still much work to go, but this good enough to create a branch on the node-spdy repository.

The whole reason that I started down the spdy/3 road was to experiment with flow control. But there is at least one thing that I have to do before I move onto that. I need to re-enable spdy/2. Nearly all clients still use spdy/2, but right now I only have the spdy/3 zlib dictionary (used for blindingly fast header compression) hard-coded in lib/spdy/utils.js. I need to move that out of utils.js and into the protocol version definitions. I also need to dynamically select the appropriate dictionary at connection time.

Thankfully, the node-spdy module makes the value of the Next Protocol Negotation (an extension of SSL) available on the socket. So, when the zlib compression contexts are established on the connection, I can supply the protocol version:
function Connection(socket, pool, options) {
  process.EventEmitter.call(this);

  var self = this;

  this._closed = false;

  this.pool = pool;
  var pair = pool.get(socket.npnProtocol);

  this.deflate = pair.deflate;
  this.inflate = pair.inflate;
  // ...
The pool can then look up the dictionary using the appropriate SPDY version number:
Pool.prototype.get = function get(version, callback) {
  var version_number = version.split(/\//)[1],
      dictionary = spdy.protocol[version_number].dictionary;
  // ...
The dictionary itself is assigned to the protocol in the protocol's index.js (e.g. lib/spdy/protocol/v3/index.js):
lib/spdy/protocol/v3/index.jsvar v3;

// ...
module.exports = v3;

v3.dictionary = require('./dictionary').dictionary;
And the dictionary can be defined in dictionary.js as:
exports.dictionary = new Buffer([
 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,
 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,
 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,
        // ...
]);
(that is lifted directly from the spec)

With that, I have my SPDY version three connection working again. This time, however, the dictionary comes from the correct protocol version library. After doing the same for spdy/2, node-spdy is now capable of serving up both spdy/3 and spdy2 from the same server:


That is a good stopping point for now. Tomorrow, I will hopefully pick back up with flow control.

Day #368

No comments:

Post a Comment