Thursday, June 16, 2011

SPDY Express: A Spike

‹prev | My Chain | next›

I am closing in on the deadline for the beta copy of SPDY Book, which got me to thinking of things that I absolutely need for a proper book. The first thing that popped into my mind was an easy Express.js server.

Currently in my node-spdy working repo, I have Connect.js installed:
➜  node-spdy git:(data-compression) npm ls
spdy@0.0.1 /home/cstrom/repos/node-spdy
├─┬ connect@1.4.1 extraneous
│ ├── mime@1.2.2
│ └── qs@0.1.0
└── zlibcontext@1.0.7
That is a requirement for express,js, but not sufficient. So I install express as well:
➜  node-spdy git:(data-compression) npm install express
mime@1.2.2 ./node_modules/express/node_modules/mime
qs@0.1.0 ./node_modules/express/node_modules/qs
express@2.3.11 ./node_modules/express
➜ node-spdy git:(data-compression) npm ls
spdy@0.0.1 /home/cstrom/repos/node-spdy
├─┬ connect@1.4.1 extraneous
│ ├── mime@1.2.2
│ └── qs@0.1.0
├─┬ express@2.3.11 extraneous
│ ├── mime@1.2.2
│ └── qs@0.1.0
└── zlibcontext@1.0.7
Ooh! It seems that Express.js now comes with a generator. So I install express globally (to be able to run the binary) and execute the generator:
➜  node-spdy git:(express) npm install -g express
/home/cstrom/local/node-v0.5.0-pre/bin/express -> /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/express/bin/express
mime@1.2.2 /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/express/node_modules/mime
connect@1.4.4 /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/express/node_modules/connect
qs@0.1.0 /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/express/node_modules/qs
express@2.3.11 /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/express
➜ node-spdy git:(express) express test/express
create : test/express
create : test/express/app.js
create : test/express/public/stylesheets
create : test/express/public/stylesheets/style.css
create : test/express/public/images
create : test/express/public/javascripts
create : test/express/logs
create : test/express/pids
create : test/express/test
create : test/express/test/app.test.js
create : test/express/views
create : test/express/views/layout.jade
create : test/express/views/index.jade
- make sure you have installed jade: $ npm install jade
I have not installed jade, so I do so now:
➜  node-spdy git:(express) ✗ npm install jade
jade@0.12.2 ./node_modules/jade
With that, I can fire up the skeleton app:
➜  node-spdy git:(express) ✗ node test/express/app.js 
Express server listening on port 3000
Checking it out in the browser, I see that it works:

The next step is to run this as an https server. Running an https server in express is identical to running an http server except that I need to pass in SSL related options to the createServer() call. So, I fire up Emacs and add those options to the express created app.js:
var express = require('express')
, fs = require('fs');

var app = module.exports = express.createServer({
key: fs.readFileSync(__dirname + '/../../keys/spdy-key.pem'),
cert: fs.readFileSync(__dirname + '/../../keys/spdy-cert.pem'),
ca: fs.readFileSync(__dirname + '/../../keys/spdy-csr.pem'),

npnProtocols: ['spdy/2']
And, sure enough, I now have a https version of the skeleton app:

Now, I do something terrible. I start editing the express code directly in node_modules. This is almost certainly going to come back to bite me, but I am embracing this as a pure spike to gain a first understanding of what is required.

In there, I copy the https.js modules in both express and connect to equivalent spdy.js files. After replacing all instances of https with SPDY, I fire up the browser, sniff the network to find that I do not have a SPDY session.

It looks as though my first attempt at a SPDY / Express server is not sending out SPDY as a possible protocol. In a legitimate SPDY session, one of the first things to occur is a SSL Server Hello that includes spdy/2 as a possible protocol:

In my SPDY / express server, I am not seeing any NPN protocols:

I drop down into the debugger to find that I am passing the required NPNProtocols options all the way to the TLS server instance. Furthermore, I trace the NPNProtocol all the way to convertNPNProtocols:
debug> { '0': 6, '1': 115, '2': 112, '3': 100, '4': 121, '5': 47, '6': 50, length: 7, offset: 4048, parent: '#<SlowBuffer>' }
That is a buffer containing the ascii representations for spdy/2 (115 == s, 112 = p, etc). Ugh. So why is that not getting sent out to the browser?

I quit and restart Chrome a couple of times and eventually get a legit SPDY NPN session:

I cannot quite figure out why the server kept sending out an empty NPN list previously. I could see Chrome caching the empty NPN cert, but the server was clearly actively sending an empty NPN session over the wire. For now I will have to chalk that up to PEBKAC. Still, I tuck this away in the back of my mind for future reference. It seems like something is going wrong (or significantly unexpected).

Getting back to a working express / SPDY session, I check things out in the SPDY tab of Chrome's about:net-internals to find:
t=1308281848286 [st= 11]     SPDY_SESSION_SYN_REPLY  
--> flags = 0
--> connection: keep-alive
content-type: text/html
status: 500 OK
version: HTTP/1.1
x-powered-by: Express
--> id = 1
Yay! A SPDY session served up by express!

Oh wait. The response status is 500 / internal server error. So, just... yay.

I will call it a night at this point. Hopefully the internal server error will be relatively easy to track down. Assuming that is the case, I will get started on implementing a real SPDY express middleware module tomorrow.

Day #52

No comments:

Post a Comment