Wednesday, May 25, 2011

SPDY Push: A First Look

‹prev | My Chain | next›

Up tonight, I would like to start investigating server push with SPDY.

Server push in SPDY does not involve all of what web developers tend to include in the concept of server push. SPDY does not establish a mechanism to replace COMET / web sockets / long polling, etc. Some (all?) of those would be more efficient over SPDY, but SPDY more or less sticks to a replacement for HTTP in this respect.

The "less" close to HTTP refers push, which allows the server to reply multiple times to a single reply. For instance if the client request the homepage, the server can reply "sure, and while I'm at it, here are a couple of images and a few stylesheets that you'll be wanting as well". There is some potential for abuse there, which the SPDY draft spec either addresses or offers up suggestions for implementers on how to mitigate.

I will defer abuse questions to another day, but it sure would be cool to push out the image and stylesheet for the node-spdy homepage:



I am unsure exactly where to hook this into node-spdy. It is currently not easy (possible?) at the server creation level because access to the compression stream is not exposed:
  if (req.method == 'POST') {
res.writeHead(200);
req.pipe(res);
return;
}
static(req, res, function() {
res.writeHead(404);
res.end();
});
I might be able to slide in an uncompressed push here.

But first, I do a quick check of the specs written for node-spdy. First I install vows.js (globally so that the executable is in my PATH) and run the specs:
➜  node-spdy git:(master) ✗ npm install -g vows
/home/cstrom/local/node-v0.5.0-pre/bin/vows -> /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/vows/bin/vows
eyes@0.1.6 /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/vows/node_modules/eyes
vows@0.5.8 /home/cstrom/local/node-v0.5.0-pre/lib/node_modules/vows

➜ node-spdy git:(master) ✗ vows --spec

♢ SPDY/basic test

spdy.createServer
✓ should return spdy.Server instance
Listening on this server instance
✓ should be successfull
Calling spdy.createZLib
✓ should return instance of spdy.ZLib
Creating new connection to this server
✓ should receive connect event
Creating parser and waiting for SYN_REPLY
✓ should end up w/ that frame
Sending control SYN_STREAM frame should be successfull and sending request body
✓ should emit data and end events on request
Creating parser and waiting for Data packet
✓ should end up w/ that frame
When calling server.close
✓ all connections will be dropped

✓ OK » 8 honored (0.026s)
The vow for sending a request body when the client has sent a SYN_STREAM looks promising. I might ultimately write something similar, but include additional resources when specified requests are made. But for now, I spike.

But where to spike? Whilst digging around, I come across the following in lib/spdy/response.js:
/**
* Server push
*/
Response.prototype.createPushStream = function() {
// stub
};
Ah, that's the stuff. Just begging to be implemented.

For now, I start a new class named PushStream (Stream might have been more appropriate, but is already taken in node land):
/**
* PushStream class
*/

var Buffer = require('buffer').Buffer,
//... more modules requires...

/**
* Class constructor
*/
var PushStream = exports.PushStream = function(cframe, c) {
stream.Stream.call(this);
this.associatedStreamId = cframe.data.streamID;
this.c = c;

this._headers = {
};
this._written = false;

// For stream.pipe and others
this.writable = true;
};
util.inherits(PushStream, stream.Stream);
Unlike the response class, the push will not go out over the same stream. This will be a server initiated stream, but it does need to be associated with the original request (to pick up some headers and help the client identify the stream). Hence, the associated stream ID needs to be set in response to the original SYN_STREAM request.

I would have preferred to have gotten a bit further on this, but that will suffice as a stopping point for tonight. Now that I have a clear understanding of what needs to be done, I hope to make better progress on this tomorrow.


Day #31

No comments:

Post a Comment