Sunday, June 12, 2011

NPN in Detail (node-spdy)

‹prev | My Chain | next›

Up tonight, I would like to explore NPN (Next Protocol Negotiation) in node-spdy. NPN is a new SSL extension that is required to perform proper SPDY. I have already looked at this in the SPDY gem, but believe that I may have to add this to node-spdy.

To test this out, I load up the sample node-spdy server in Firefox. Amazingly it just works. Looking at the packets in Wireshark, I find:



There is no NPN negotiation taking place in there. The next packet after the SSL Server Hello is a normal HTTP GET request for the homepage. Nice.

I already know that node-spdy works perfectly well with Chrome when explicitly specifying spdy/2 as the NPN protocol. But how does Chrome deal with an NPN protocol of http/1.1? More to the point, how does node-spdy handle it?

I change the test server to set http/1.1:
var options = {
// ...
NPNProtocols: ['http/1.1']
};

var server = spdy.createServer(options, function(req, res) {
// ...
});
Again, it turns out to handle it just fine. The Wireshark capture shows that Chrome triggers a NPN session, which only specifies http/1.1:



So, wow. There does not seem to be anything needed to get node-spdy working with NPN while still being backward compatible. But how is it managing that? I have not come across any code that I recall...

Digging into it some, I find that indeed node-spdy does nothing to perform NPN. In fact, it is node.js itself that does the negotiation. The options from the node-spdy test server:
var options = {
//...
NPNProtocols: ['http/1.1']
};

var server = spdy.createServer(options, function(req, res) {
//...
});
These are passed to the node-spdy createServer() function, which, in turn, passes them along to the TLS create server:
var Server = core.Server = function(options, requestListener) {
//...
tls.Server.call(this, options, function(c) {
// ...
});
//...
};
Following this through to the TLS module in node.js, I find that these options are passed along to setOptions:
function Server(/* [options], listener */) {
var options, listener;
//...

this.setOptions(options);
});
And, finally, those options are converted into a Buffer to be used in NPN by the convertNPNProtocols() function:
// Convert protocols array into valid OpenSSL protocols list
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
function convertNPNProtocols(NPNProtocols, out) {
//..
};
Pretty darn impressive that this just works in node.js. Huge kudos to Fedor Indutny who not only wrote node-spdy but also wrote the node.js code that does NPN. As a Rubyist, I am dissappointed that the same feature languishes in the pull request purgatory of eventmachine.


Day #49

No comments:

Post a Comment