Thursday, May 10, 2012

Bug Fix for SPDY/3 Flow Control

‹prev | My Chain | next›

For the past few nights, I have been playing code tag with Tatsuhiro Tsujikawa in an attempt to sort out the finer details of SPDY flow control, which is new in version 3. I have a server implementation in a node-spdy branch that works with Chrome, not Tatsuhiro's spdylay.

Today, Tatsuhiro did me one better than in previous days. Not only does spdylay have additional fixes, but he also fixed my node-spdy code. So first, I update spdylay from the GitHub repository:
git fetch origin
git merge origin/master
make
make install
As for the patch to node-spdy, it is simple enough. As I suspected yesterday, I was doing something wrong and the patch identifies my problem as always sending along data in chunks of 1300—regardless of the current receive window size. If there were 100 bytes left in the receive window, I had simply paused transfer at that point and awaited a WINDOW_UPDATE (Chrome would send one). What the patch does, is send 100bytes, taking the receive window down to exactly zero bytes, and then dumping the remainder of the buffer onto the queue:
Stream.prototype._writeData = function _writeData(fin, buffer) {
  var len;
  if (this.framer.version == 3) {
    if (this._transferWindowSize <= 0) {
      this._transferWindowBuffer.push([fin, buffer]);
      return;
    }
    len = Math.min(this._transferWindowSize, buffer.length);
    if (len < buffer.length) {
      this._transferWindowBuffer.push([fin, buffer.slice(len)]);
      fin = false;
    }
    this._transferWindowSize -= len;
  }

  this.lock(function() {
    var stream = this,
        frame = this.framer.dataFrame(this.id, fin, buffer.slice(0, len));

    // actually write here...
  }
};
That makes complete sense and I have no idea why I did not see that myself. I had a nagging feeling that something was not right, but Chrome's acceptance lulled me into a false sense of security.

Of course, to actually verify that it works, I load the page in Chrome:


So far, so good (no broken images). Checking out the SPDY tab of chrome://net-internals, I see the expected small transfers as the receive window is exhausted followed shortly by a WINDOW_UPDATE from Chrome indicating that more data may be transferred, and finally by more data:
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1300
--> stream_id = 9
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 124
--> stream_id = 9
SPDY_SESSION_SENT_WINDOW_UPDATE
--> delta = 65536
--> stream_id = 9
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1176
--> stream_id = 9
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1300
--> stream_id = 9
This stream ends with a normal DATA FIN frame (shows as zero length):
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1300
--> stream_id = 9
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 28
--> stream_id = 9
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 0
--> stream_id = 9
Ultimately, the conversation ends with a couple of PINGs and all would seem well:
SPDY_SESSION_PING
--> type = "sent"
--> unique_id = 3
SPDY_SESSION_PING
--> type = "received"
--> unique_id = 3
Now for the real moment of truth, does it still work in spdylay? Well, of course it does!
➜  spdylay git:(master) spdycat -v -n https://localhost:3000/images/pipelining.jpg
...
[  0.182] recv DATA frame (stream_id=1, flags=0, length=369)
[  0.182] recv DATA frame (stream_id=1, flags=1, length=0)
It still works when I explicitly supply the initial receive window size (216 = 64kb):
➜  spdylay git:(master) spdycat -v -w16 -n https://localhost:3000/images/pipelining.jpg
...
[  0.178] recv DATA frame (stream_id=1, flags=0, length=369)
[  0.178] recv DATA frame (stream_id=1, flags=1, length=0)
The reason that I started playing with spdylay was to test different initial receive windows, so I specify a window of 16kb:
➜  spdylay git:(master) spdycat -v -w14 -n https://localhost:3000/images/pipelining.jpg
...
[  2.974] recv DATA frame (stream_id=1, flags=0, length=369)
[  2.974] recv DATA frame (stream_id=1, flags=1, length=0)
So yay!

Huge thanks to Tatsuhiro Tsujikawa for not only the tool to help test this, but also for fixing my own code. It is pretty damn cool that the Internet makes this possible, but not nearly as cool that someone on the other side of the planet would help out a stranger like that.

I believe that this leaves the spdy-v3 branch of node-spdy effectively feature complete. There are a few things that I will verify tomorrow, but for now... yay!


Day #381

No comments:

Post a Comment