Thursday, June 23, 2011

Express-spdy Has No Style

‹prev | My Chain | next›

Taking a closer look at the express-spdy sample app, I notice a subtle lack of style:



The sample app generated by express.js does not do much, but it does specify a sans-serif font:
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
There are most definitely serifs on the sample page.

Inspecting the SPDY session in Chrome's SPDY tab (under about:net-internals), I notice that no actual data is sent for the stylesheet:
t=1308858355333 [st=171]     SPDY_SESSION_SYN_STREAM  
--> flags = 1
--> accept: text/css,*/*;q=0.1
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
host: localhost:3000
method: GET
referer: https://localhost:3000/
scheme: https
url: /stylesheets/style.css
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.797.0 Safari/535.1
version: HTTP/1.1
--> id = 3
t=1308858355340 [st=178] SPDY_SESSION_SYN_REPLY
--> flags = 0
--> Accept-Ranges: bytes
Cache-Control: public, max-age=0
Content-Length: 128
Content-Type: text/css; charset=UTF-8
ETag: "128-1308857965000"
Last-Modified: Thu, 23 Jun 2011 19:39:25 GMT
X-Powered-By: Express
status: 200 OK
version: HTTP/1.1
--> id = 3
t=1308858355341 [st=179] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 0
--> stream_id = 3
Immediately after the SYN_REPLY for the stylesheet, an empty data frame is sent.

Thinking that Chrome may be ignoring a (possibly malformed) packet, I drop down to Wireshark to inspect the SYN_REPLY and packet immediately following:
              +----------------------------------+
80 02 00 02 |1| 1 | 2 |
+----------------------------------+
00 00 00 8b | Flags (8) | Length (24 bits) |
+----------------------------------+
00 00 00 03 |X| Stream-ID (31bits) |
+----------------------------------+
00 00 62 e0 | Unused | |
c4 65 13 86 +---------------- |
81 86 46 16 | Name/value header block |
0c bc ce e0 | ... |
44 e4 0c 49
44 0c 82 10 0f eb 28 40 bd 6c 6b c0 c0 eb 03 8a
45 5f 68 2c 32 c8 02 e3 49 47 c1 c8 58 01 18 09
0a c0 4c 67 a8 60 68 69 65 6c 69 65 64 aa e0 ee
1b c2 c0 e2 1a 92 98 ce 20 ac 04 34 5b 17 98 f7
80 c1 64 6e 69 06 cc 3b 06 4a 68 3e 15 07 fb 34
b9 b8 d8 5a 01 16 ae a1 21 6e ba 40 17 39 42 f2
55 10 38 5f 31 b0 26 55 96 00 29 c2 1e 07 00 00
00 ff ff


+----------------------------------+
00 00 00 03 |C| Stream-ID (31bits) |
+----------------------------------+
01 00 00 00 | Flags (8) | Length (24 bits) |
+----------------------------------+
| Data |
+----------------------------------+
Nope, Chrome is not missing a thing. The SYN_REPLY is immediately followed by an empty data frame. The flag in the empty data packet, 0x01 is a FLAG_FIN. So express-spdy is definitely not sending out data even though it must be reading the data in order to get the length of the stylesheet.

After a bit of digging, I locate the pipe() in connect's static middleware:
    // stream
var stream = fs.createReadStream(path, opts);
stream.pipe(res);
That ought to be piping data directly back through the SPDY response.

Reading through node's Stream#pipe() source code a bit, I happen across:
Stream.prototype.pipe = function(dest, options) {
var source = this;

function ondata(chunk) {
if (dest.writable) {
if (false === dest.write(chunk)) source.pause();
}
}

source.on('data', ondata);

// ...
}
Hrm... I wonder if writable is set anywhere.

Checking the node-spdy Response class installed into node_modules, I find that writable is, in fact, not set anywhere. So I add it to the constructor:
var Response = exports.Response = function(cframe, c) {
stream.Stream.call(this);
this.streamID = cframe.data.streamID;
this.c = c;

this.statusCode = 200;
this._headers = {};
this._written = false;
this._reasonPhrase = 'OK';

this.writable = true;
};
Reloading the app, I see



Ahhhh. No serifs. That has the stylesheet applied. Checking the SPDY tab in Chrome, I see that data was, indeed, sent:
   t=1308885003974 [st=25673]     SPDY_SESSION_SYN_REPLY  
--> flags = 0
--> Accept-Ranges: bytes
Cache-Control: public, max-age=0
Content-Length: 110
Content-Type: text/css; charset=UTF-8
ETag: "110-1308882421000"
Last-Modified: Fri, 24 Jun 2011 02:27:01 GMT
X-Powered-By: Express
status: 200 OK
version: HTTP/1.1
--> id = 17
t=1308885003975 [st=25674] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 110
--> stream_id = 17

t=1308885003976 [st=25675] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 0
--> stream_id = 17
Looking through the latest node-spdy code, it looks as though this is a solved problem. That explains why I did not notice the lack of a stylesheet until now—I had been using my local node-spdy rather than installing from npm until 2 days ago.

I will ask Fedor Indutny to publish a new version of node-spdy and move on to my next TODO item tomorrow.


Day #57

No comments:

Post a Comment