Thursday, April 19, 2012

Edge SPDY, Node.js, and Express.js

‹prev | My Chain | next›

I am slowly coming to the conclusion that this may not be the best time to issue a 1.1 edition of SPDY Book. Much of the new hotness that is node-spdy is in limbo until the unstable 0.7 node.js becomes stable 0.8. I almost have a handle on that except for express.js, the pleasant HTTP resource-oriented application server. The current 2.0 series will work with edge node-spdy, but not 0.7/0.8 node.js. For 0.8 node.js, the 3.0 alpha of express.js is imminent—only it does not play well with node-spdy.

All of these moving parts have me thinking that a more modest SPDY Book update might be the better part of valor. But I am not quite ready to give up. First, I would like to have a go at getting edge node-spdy and edge express.js working together.

The latest node-spdy does a rather elegant thing when creating SPDY-sized servers—it accepts an optional class argument that can be used to decorate server-like objects. This is specifically used to create express.js instances as I found last night:
var app = spdy.createServer(express.HTTPSServer, options);
The problem presented by edge express.js is that there is no express.HTTPSServer class anymore. In particular, the new express.application is more of a mixin than a proper class.

The answer (hopefully) is that node-spdy also works with HTTP server objects, which is what the express() function ought to return in 3.0. I have my forks of express.js and connect.js (for middleware) checked out locally. I have to modify the package.json for each to be compatible with 0.7:
  "name": "express",
  "description": "Sinatra inspired web development framework",
  // ...
  "engines": { "node":">= 0.5.0 < 0.9.0" }
With that, I can install globally and use the resultant generator to get started on an express3 application:
➜  tmp  npm install -g ~/repos/express/ ~/repos/connect
➜  tmp  express express3-spdy-test
➜  tmp  cd express3-spdy-test
➜  express3-spdy-test  npm install ~/repos/express ~/repos/connect ~/repos/node-spdy
➜  express3-spdy-test  npm install jade
I also copy the SSL keys from my 2.0 test application:
➜  express3-spdy-test  mkdir keys
➜  express3-spdy-test  cp ../express-spdy-test/keys/* keys 
➜  express3-spdy-test  ls keys 
spdy-cert.pem  spdy-csr.pem  spdy-key.pem
I can start up the generated express 3.0 app:
➜  express3-spdy-test  node app
Express server listening on port 3000
Pointing the browser at that site, I get the expected default homepage:

Next, I need an SSL version of the site. For that I change the http.createServer() to the https equivalent in the application app.js file:
// ...
var options = {
  key: fs.readFileSync(__dirname + '/keys/spdy-key.pem'),
  cert: fs.readFileSync(__dirname + '/keys/spdy-cert.pem'),
  ca: fs.readFileSync(__dirname + '/keys/spdy-csr.pem')

https.createServer(options, app).listen(3000);
After restarting, I can now access the default express.js site over SSL:

Now for the moment of truth: switching to node-spdy. I add spdy to the list of required packages and replace the https.createServer with the node-spdy equivalent:
var express = require('express')
  , routes = require('./routes')
  , http = require('http')
  , https = require('https')
  , spdy = require('spdy')
  , fs = require('fs');

var app = express();

app.configure(function(){ /* ... */ });

app.get('/', routes.index);

var options = {
  key: fs.readFileSync(__dirname + '/keys/spdy-key.pem'),
  cert: fs.readFileSync(__dirname + '/keys/spdy-cert.pem'),
  ca: fs.readFileSync(__dirname + '/keys/spdy-csr.pem')

//https.createServer(options, app).listen(3000);
spdy.createServer(https.Server, options, app).listen(3000);
After starting the server, I can again access the homepage over SSL, but this time, when I check the SPDY tab in Chrome's chrome://net-internals, I have SPDY!
--> flags = 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
    cookie: [value was stripped]
    host: localhost:3000
    method: GET
    pragma: no-cache
    scheme: https
    url: /
    user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.8 (KHTML, like Gecko) Chrome/20.0.1105.0 Safari/536.8
    version: HTTP/1.1
--> id = 1
--> flags = 1
--> id = 4
--> value = 100
--> flags = 0
--> content-length: 277
    content-type: text/html; charset=utf-8
    status: 200 OK
    version: HTTP/1.1
    x-powered-by: Express
--> id = 1
Aw, man! That is nice. I was really worried that the new express.js would be incompatible, but clearly my worries were completely unfounded. Not only does it work, but it is really easy to configure and get running. Once again, kudos to Fedor Indunty for the very slick implementation.

Despite the ease with which I got all of this up and running, I am still in a bit of a bind with regards to including edge stuff like this in the updated book or sticking with express-spdy, which is clearly no longer needed. The former is a moving target and the latter is likely to be obsolete days after I put out the next edition of The SPDY Book. I think this suggests that minimal changes for now might be the best course of action, but I will sleep on it before making a decision.

If nothing else, the future of SPDY on node.js looks quite nice.

Day #361


  1. I followed your same steps, except in Windows, with node v0.7.8, but when I visit the URL in Chrome, net-internals doesn't list it as SPDY. What steps can I do to debug why Chrome isn't detecting it as SPDY session? Or isn't utilizing SPDY...

    1. Hrm... I suppose the next step is snooping the SSL handshake to see if the server really is advertising SPDY. Something like wireshark should do:

    2. Since I couldn't wireshark local, I installed node on my mac and worked between it and vm of windows on it. Only this time SPDY worked, so I'll have to do the same against my other physical machine to see deviations. Do you know with node-spdy, is it possible to run the server with out SSL? I would like to be able to capture the traffic and analyze with Wireshark without dealing with SSL encryption...

      Thanks for the suggestions!

    3. It is not possible to run node-spdy without SSL. There was a fork a while back that had a go at enabling this, but it was never merged in and would not work well with all of the changes in version 1.0 and later.

      I wonder if it's possible that node-spdy does not work under windows...?

  2. Can we use SPDY with HTTP ?? and not HTTPS ?