Saturday, May 28, 2011

SPDY Associated Stream ID

‹prev | My Chain | next›

Today, I continue working through the SPDY server push issue in my node-spdy fork that is resulting in:



Today I am going to focus on the SYN_STREAM frames (identified by an ID of 2):
...
t=1306553787485 [st=45] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 20
--> stream_id = 2
t=1306553787485 [st=45] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 0
--> stream_id = 2
t=1306553787485 [st=45] SPDY_SESSION_SEND_RST_STREAM
--> status = 1
--> stream_id = 1
...
In the non-push conversations, the packet look like:
t=1306632639034 [st= 46]     SPDY_SESSION_SYN_REPLY  
--> flags = 0
--> accept-ranges: bytes
cache-control: public, max-age=0
connection: keep-alive
content-length: 698
content-type: text/html; charset=UTF-8
etag: "698-1306200491000"
last-modified: Tue, 24 May 2011 01:28:11 GMT
status: 200 OK
version: HTTP/1.1
--> id = 1
t=1306632639035 [st= 47] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 698
--> stream_id = 1
t=1306632639035 [st= 47] SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 0
--> stream_id = 1
I start by comparing the data seen in Wireshark against the spec:
                   +----------------------------------+
80 02 00 01 |1| 1 | 1 |
+----------------------------------+
02 00 00 3b | Flags (8) | Length (24 bits) |
+----------------------------------+
00 00 00 02 |X| Stream-ID (31bits) |
+----------------------------------+
00 00 00 00 |X|Associated-To-Stream-ID (31bits)|
+----------------------------------+
00 01 62 60 | Pri | Unused | |
+------------------ |
64 60 06 05 | Name/value header block |
81 42 46 49 | ... |
49 41 b1 95
be 3e 28 31
e5 80 8a 0c 2b 0b 03 0b 43 fd e2 92
ca 9c 54 bd e4 e2 62 00 00 00 00 ff ff 02 00 00
00 ff ff
Ah, well that would explain it. There is no associated stream ID.

Digging through the code a bit, I find that the associated stream ID is written by the very long
function nvsToBuffer(zlib, headers, nvs) {
//...

// Insert assocStreamID for SYN_STREAM
console.log(buff);
buff.writeUInt32(assocStreamID & 0x7fffffff, 6, 'big');

console.log(buff);

//...

console.log(buff);
return buff;
};
When I reload, I find:
<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
<Buffer 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
<Buffer 00 00 00 02 00 00 00 00 00 01 62 60 64 60 06 05 81 42 46 49 49 41 b1 95 be 3e 28 31 e5 80 8a 0c 2b 0b 03 0b 43 fd e2 92 ca 9c 54 bd e4 e2 62 00 00 00 00 ff ff 02 00 00 00 ff ff>
All right. It seems to be set as expected. So where do things go wrong?

It turns out that it is set as expected, but in the wrong place. The assembled packet looks very familiar:
<Buffer 80 02 00 01 02 00 00 3b 00 00 00 02 00 00 00 00 00 01 62 60 64 60 06 05 81 42 46 49 49 41 b1 95 be 3e 28 31 e5 80 8a 0c 2b 0b 03 0b 43 fd e2 92 ca 9c 54 bd e4 e2 62 00 00 00 00 ff ff 02 00 00 00 ff ff>
That is the same packet that I saw in Wireshark... Ah, the associated stream ID is being set, just in the wrong place:
                   +----------------------------------+
80 02 00 01 |1| 1 | 1 |
+----------------------------------+
02 00 00 3b | Flags (8) | Length (24 bits) |
+----------------------------------+
00 00 00 02 |X| Stream-ID (31bits) |
+----------------------------------+
00 00 00 00 |X|Associated-To-Stream-ID (31bits)|
+----------------------------------+
00 01 62 60 | Pri | Unused | |
+------------------ |
64 60 06 05 | Name/value header block |
81 42 46 49 | ... |
49 41 b1 95
be 3e 28 31
e5 80 8a 0c 2b 0b 03 0b 43 fd e2 92
ca 9c 54 bd e4 e2 62 00 00 00 00 ff ff 02 00 00
00 ff ff
As-is, it is not the associated stream ID, but the Primary flag.

I cannot quite follow the bit flipping that goes on in the nvsToBuffer method, but I can easily move the associated stream ID location back two bytes by switching this:
    buff.writeUInt32(assocStreamID & 0x7fffffff, 6, 'big');
To this:
    buff.writeUInt32(assocStreamID & 0x7fffffff, 4, 'big');
With that, I get my packet printing out as desired:
                   +----------------------------------+
80 02 00 01 |1| 1 | 1 |
+----------------------------------+
02 00 00 3b | Flags (8) | Length (24 bits) |
+----------------------------------+
00 00 00 02 |X| Stream-ID (31bits) |
+----------------------------------+
00 00 00 01 |X|Associated-To-Stream-ID (31bits)|
+----------------------------------+
00 00 62 60 | Pri | Unused | |
+------------------ |
64 60 06 05 | Name/value header block |
81 42 46 49 | ... |
49 41 b1 95
be 3e 28 31
e5 80 8a 0c 2b 0b 03 0b 43 fd e2 92 ca 9c 54 bd e4 e2 62 00 00 00 00 ff ff 02 00 00 00 ff ff
But the real test is the browser, does it work any better now?

Sadly, no:



Grrr... it seems that the problem is elsewhere. I will pick back up with that tomorrow, but first I will cherry-pick the associated stream ID fix back into master so that I can submit a pull request.


Day #33

No comments:

Post a Comment