Saturday, June 18, 2011

SPDY Express: A Spike 3

‹prev | My Chain | next›

For the last two days, I have be spiking in an attempt to get SPDY and express.js playing nicely. Day 3 picks up where I left off yesterday, with an exception being generated from within the http.js module of connect:
Server.prototype.handle = function(req, res, out) {
// ...

// Call the layer handler
// Trim off the part of the url that matches the route
removed = layer.route;
req.url = req.url.substr(removed.length);

// Ensure leading slash
if ('/' != req.url[0]) req.url = '/' + req.url;

// ...
After some rooting around in node debug, I come across a seemingly unhelpful exception:
 RangeError undefined.
It is an odd little error because it seemingly comes from nowhere (there is no associated stacktrace). Eventually, I step through enough code to come across:
debug> break in #<Object>.render(view=index, opts=#<Object>, fn=undefined, parent=undefined, sub=undefined), /home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:308
if ('function' == typeof opts) {
^
debug>
break in #<Object>.render(view=index, opts=#<Object>, fn=undefined, parent=undefined, sub=undefined), /home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:313
return this._render(view, opts, fn, parent, sub);
^
debug>
break in #<Object>.render(view=index, opts=#<Object>, fn=undefined, parent=undefined, sub=undefined), /home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:308
if ('function' == typeof opts) {
^
debug>
break in #<Object>.render(view=index, opts=#<Object>, fn=undefined, parent=undefined, sub=undefined), /home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:313
return this._render(view, opts, fn, parent, sub);
^
debug>
break in #<Object>.render(view=index, opts=#<Object>, fn=undefined, parent=undefined, sub=undefined), /home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:308
if ('function' == typeof opts) {
^
I actually go through many more iterations than shown above before I realize that it is repeating. I think the endless repeating must eventually result in the RangeError. Regardless, I need to figure out why it is repeating. More to the point, why is a call to the _render() method stepping right back into render() on line 308?

It turns out that there is a perfectly reasonable answer to that question. In my sleep deprived delirium yesterday, I defined _render and render in the SPDY response object to be the same thing:
spdy_res.partial = res.partial;
spdy_res.render = res.render;
spdy_res._render = res.render;
Both are pointing to the render() method from Response in express.js. The solution is easy enough, point spdy_res._render to res._render:
spdy_res.partial = res.partial;
spdy_res.render = res.render;
spdy_res._render = res._render;
That gets me a little farther. But, when I load the express sample app now, I get:
TypeError: Object [object Object] has no method 'send'
at [object Object]._render (/home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:439:10)
at [object Object].render (/home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:313:17)
at [object Object]._render (/home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:429:10)
at [object Object].render (/home/cstrom/repos/node-spdy/node_modules/express/lib/view.js:313:17)
at Router. (/home/cstrom/repos/node-spdy/test/express/app.js:39:7)
at done (/home/cstrom/repos/node-spdy/node_modules/express/lib/router/index.js:250:22)
at middleware (/home/cstrom/repos/node-spdy/node_modules/express/lib/router/index.js:244:9)
at param (/home/cstrom/repos/node-spdy/node_modules/express/lib/router/index.js:227:11)
at pass (/home/cstrom/repos/node-spdy/node_modules/express/lib/router/index.js:232:6)
at Router._dispatch (/home/cstrom/repos/node-spdy/node_modules/express/lib/router/index.js:255:4)
That is a similar class of error to what I was seeing last night. Specifically, express decorates connect and node.js objects with new methods (like render() and send()). I already figured out how to add render() (it did take a few tries to get _render() working). Unfortunately, that same technique won't quite work with send().

For render() I was able to decorate directly in my newly added express/lib/spdy.js. Unfortunately, the send() method is added to Response.prototype after lib/spdy.js is loaded. So I have to add code directly to lib/express.js:
var http = require('http')
, res = http.ServerResponse.prototype
, spdy = require('../../../lib/spdy')
, spdy_res = spdy.Response.prototype;

spdy_res.send = res.send;
After doing the same for contentType() and header(), I am actually able to load up the app!

Even more impressive, when I check out the SPDY tab in Chrome's about:net-internals, I find:
    t=1308442868018 [st=24936]     SPDY_SESSION_SYN_STREAM  
--> 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: max-age=0
host: localhost:3000
method: GET
scheme: https
url: /
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.794.0 Safari/535.1
version: HTTP/1.1
--> id = 7
t=1308442868031 [st=24949] SPDY_SESSION_SYN_REPLY
--> flags = 0
--> connection: keep-alive
content-length: 277
content-type: text/html
status: 200 OK
version: HTTP/1.1
x-powered-by: Express
--> id = 7
Holy wow! That's a real SPDY session. And it is powered by express.js! And no errors!

Yay! It actually works!

Er... I mean of course it works. I'm brilliant! And tomorrow I'll see if I can figure out how it works. And maybe even implement it well.


Day #53

No comments:

Post a Comment