Saturday, April 28, 2012

Chrome and SPDY WINDOW_UPDATE

‹prev | My Chain | next›

Today, I am going to try to get my node-spdy-powered express.js site to force Chrome to honor the SPDY v3 WINDOW_UPDATE frame. I think it more likely that, in real life, the browser would send the server WINDOW_UPDATE packets to indicate that the server should slow down. I am unsure how to coerce Chrome into doing this, but I can send WINDOW_UPDATEs from node-spdy.

In fact, I was able to get node-spdy to send WINDOW_UPDATEs last night, but they had no effect. This was because the browser has no more data to send—it merely sent HTTP GET requests and was then done. The browser can send data—if it is POSTing a form. So I create a jade template to POST a form:
h1 Cool Form
p Enjoy our cool form

form#awesome(method="post")
  p
    input(type="text", name="title", value=title)

  p
    textarea(rows="10", cols="105", name="post")= post

  P
    input(type="submit", name="b", value="Cool!")
I then fill out the form with several hundred-character lines:


When I submit the form, however, I do not get the desired effect. The WINDOW_UPDATE is not being sent until after all of the form data has been sent:
...
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
SPDY_SESSION_SEND_DATA
--> flags = 1
--> size = 2004
--> stream_id = 1
SPDY_SESSION_RECV_SETTING
--> flags = 1
--> id = 4
--> value = 100
SPDY_SESSION_RECEIVED_WINDOW_UPDATE
--> delta = 8
--> stream_id = 1
SPDY_SESSION_SYN_REPLY
--> flags = 0
--> :status: 200
    :version: HTTP/1.1
--> id = 1
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1300
--> stream_id = 1
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1300
--> stream_id = 1
SPDY_SESSION_RECV_DATA
--> flags = 0
--> size = 1300
--> stream_id = 1
...
In fact, Chrome is even getting the SETTINGS frame from node-spdy before it get the WINDOW_UPDATE frame, so I need to rearrange things a little.

This turns out not to be a simple matter of moving the WINDOW_UDPATE write before writing the SETTINGS frame:
this.parser.on('_framer', function(framer, header) {
    self.write(framer.windowUpdateFrame(header.id, 8));

    // Generate custom settings frame and send
    self.write(
      framer.maxStreamsFrame(options.maxStreams)
    );
  });
Well, it is a matter of doing that, but node's socket buffering is preventing that frame from being sent immediately, so I disable buffering with a call to socket.setNoDelay():
Connection.prototype.write = function write(data, encoding) {
  if (this.socket.writable) {
    this.socket.setNoDelay();
    return this.socket.write(data, encoding);
  }
};
That seems to do the trick.

I finally get:
...
SPDY_SESSION_RECEIVED_WINDOW_UPDATE
--> delta = 8
--> stream_id = 0
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
SPDY_SESSION_SEND_DATA
--> flags = 0
--> size = 2852
--> stream_id = 1
...
So Chrome seems to ignore WINDOW_UPDATE completely, unless I am missing something. Which is entirely possible.


Day #370

3 comments:

  1. your server's default rwin is 64KB unless you have changed it in a settings of ID SETTINGS_INITIAL_WINDOW_SIZE.. so chrome can send 64KB of data before waiting for an update. You gave it an update with a delta of 8 - which means "extend the current rwin by 8", it doesn't mean "set the current rwin to 8". and it looks like chrome is sending less than 64KB+8 of post..

    I think to see it in action you can either use settings to change the default rwin or you can send a large negative delta before the syn_reply.

    ReplyDelete
    Replies
    1. actually you can't do negative updates.. that's on my list of features I want and I got it confused for a moment.

      Delete
    2. Thanks. I'm definitely confusing window size with the purpose of WINDOW_UPDATE here. Hopefully I can get it right tonight...

      Delete