Monday, April 23, 2012

Not-Actual SPDY v3 Conversation

‹prev | My Chain | next›

Yesterday, I was able to get my simple express.js prepped and ready for SPDY v3 with an assist from the latest and greatest node-spdy. I even have Chrome speaking SPDY v3 thanks to an about:flag setting. Only the SPDY conversation quickly dies.

This is almost certainly because I do not actually have v3 well defined in node-spdy (in fact, I only have an exact copy of v2). I begin my investigation with Wireshark.... only everything looks OK. I already know that the browser is SPDY v3 capable and Wireshark confirms that the server also thinks it is capable of talking SPDY v3:

Next stop: print-STDERR in the code!

But, before I can even write my first console.log(), I spy:
      // Fallback to HTTPS if needed
      if (socket.npnProtocol !== 'spdy/2' && !options.debug) {
        return, socket);
So, even though both client and server believe that SPDY is about to ensue, the server begins talking plain-old HTTP over SSL. No wonder things are going badly.

I replace the SPDY guard with:
      // Fallback to HTTPS if needed
      if (!socket.npnProtocol.match(/spdy/) && !options.debug) {
        return, socket);
After restarting and reloading the page, I am greeted with my old favorite, Error 337 (net::ERR_SPDY_PROTOCOL_ERROR): Unknown error.:

Good times.

But really, I did not expect that simply copying version 3 directly from the node-spdy version 2 implementation would work. Mercifully, the server error makes plan what the problem is:
➜  express-spdy-test  node app
Express server listening on port 3000 in development mode
node: ../src/ static void node::ZCtx::Process(uv_work_t*): Assertion `err == 0 && "Failed to set dictionary"' failed.
[1]    31988 abort (core dumped)  node app
The zlib compression dictionary did not work, nor should it—the dictionary was one of the many things that changed between SPDY v2 and v3.

Unfortunately, node-spdy hard-codes the dictionary in lib/utils.js rather than in a protocol version location. For now, I simply aim to get this working, not architected well, so I copy the dictionary from the spec into utils.js:
var 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,
Now, when I reload everything... I still get the same net::ERR_SPDY_PROTOCOL_ERROR error in the browser. On the plus side, the server is no longer crashing, so there's that!

The next bit of cleanup that I perform is to convert the header parsing from 16 bit to 32 bit. In SPDY version 2, the key value pairs (think HTTP headers) had been marked by 16 bit integers. Version 3 ups that to 32bit integers, mostly for consistency. At any rate, this involves replacing things like readUInt16BE with readUInt32BE and offsets of 2 with 4. Eventually, I am able to parse the incoming headers again:
   { ':host': 'localhost:3000',
     ':method': 'GET',
     ':path': '/',
     ':scheme': 'https',
     ':version': 'HTTP/1.1',
     accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
     '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',
     pragma: 'no-cache',
     'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.8 (KHTML, like Gecko) Chrome/20.0.1105.0 Safari/536.8' } }
I still am not sending a response back, however. I call it a night here and hope that I can get everything working tomorrow.

Day #365

No comments:

Post a Comment