I had all but given up trying to test out my flow control implementation in node-spdy. Flow control is new in version 3 of the SPDY protocol. I think my implementation—even the race condition handling—is sound. But, since Chrome currently has no way to adjust its own data transfer window, I cannot use it to test my solution.
While chatting on the spdy-dev mailing list, however, Tatsuhiro Tsujikawa, the maintainer of spdylay said that he had added support for different receive window sizes. That sounds perfect, so I grab a copy of the spdylay repository and run the configure steps:
git clone https://github.com/tatsuhiro-t/spdylay.git autoreconf -i automake # there was no visible output from this command autoconf # there was no visible output from this command ./configure --prefix=$HOME/localThe
--prefix=$HOME/local
option is my preference. I do not like installing unsigned code with root privileges, so I use the standard --prefix
switch to signify that the resultant executable and shared libraries will be installed in $HOME/local
(instead of the default /usr/local
).I do not have to worry about the prerequisites. I have been playing with SPDY for so long that I already have all the important development libraries needed.
The result of the
configure
command confirms that everything is in order:configure: summary of build options: version: 0.1.0 shared 0:0:0 Host type: x86_64-unknown-linux-gnu Install prefix: /home/chris/local C compiler: gcc CFlags: -g -O2 Library types: Shared=yes, Static=yes CUnit: no OpenSSL: yes Examples: yesSo I build and install spdylay:
make make installWith that, I have the desired
spdycat
command installed:➜ spdylay git:(master) which spdycat /home/chris/local/bin/spdycatMore importantly, it is not crashy:
➜ spdylay git:(master) spdycat --help Usage: spdycat [-Onv3] [-t=seconds] [-w=window_bits] [--cert=CERT] [--key=KEY] URI... OPTIONS: -v, --verbose Print debug information such as reception/ transmission of frames and name/value pairs. -n, --null-out Discard downloaded data. -O, --remote-name Save download data in the current directory. The filename is dereived from URI. If URI ends with '/', 'index.html' is used as a filename. Not implemented yet. -3, --spdy3 Only use SPDY/3. -t, --timeout=N Timeout each request after N seconds. -w, --window-bits=N Sets the initial window size to 2**N. --cert=CERT Use the specified client certificate file. The file must be in PEM format. --key=KEY Use the client private key file. The file must be in PEM format.So, it is time to fire it up against my spdy/3 express.js site:
➜ spdylay git:(master) spdycat -v3 https://localhost:3000 [ 0.004] NPN select next protocol: the remote server offers: * spdy/3 * spdy/2 * http/1.1 * http/1.0 NPN selected the protocol: spdy/3 [ 0.013] send SYN_STREAM frame <version=3, flags=1, length=102> (stream_id=1, assoc_stream_id=0, pri=3) :host: localhost:3000 :method: GET :path: / :scheme: https :version: HTTP/1.1 accept: */* user-agent: spdylay/0.1.0 [ 0.021] send GOAWAY frame <version=3, flags=0, length=8> (last_good_stream_id=0)Hrm... that does not seem right. It looks to have successfully connected and determined that the server was spdy/3 capable. But immediately after sending the usual SYN_STREAM, it told the server to GOAWAY. There is no explanation that I can see for this seemingly rude behavior.
So I downgrade my SPDY server to spdy/2 and try again:
➜ spdylay git:(master) spdycat -v https://localhost:3000 [ 0.003] NPN select next protocol: the remote server offers: * spdy/2 * http/1.1 * http/1.0 NPN selected the protocol: spdy/2 [ 0.009] send SYN_STREAM frame <version=2, flags=1, length=111> (stream_id=1, assoc_stream_id=0, pri=3) :host: localhost:3000 :method: GET :path: / :scheme: https :version: HTTP/1.1 accept: */* user-agent: spdylay/0.1.0 [ 0.011] recv SETTINGS frame <version=2, flags=0, length=12> (niv=1) [4(1):100] [ 0.047] recv SYN_REPLY frame <version=2, flags=0, length=80> (stream_id=1) x-powered-by: Express content-type: text/html content-length: 629 :status: 200 OK :version: HTTP/1.1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><title>Express w/ SPDY 3</title><link rel="stylesheet" href="/stylesheets/style.css"/><script src="/main.js"></script></head><body><h1>Express w/ SPDY 3</h1><p>Welcome to Express w/ SPDY 3</p><p><Here>is a huge image (830kb)</Here><br/><jammed>into a height=200</jammed><br/><img> tag: </p><img src="images/pipelining.jpg" height="200"/><p><And>another image (956kb)</And><br/><Also>jammed into a small</Also><br/><img> tag: </p><img src="images/tube.jpg" height="200"/></body></html>[ 0.047] recv DATA frame (stream_id=1, flags=0, length=629) [ 0.047] recv DATA frame (stream_id=1, flags=1, length=0) [ 0.047] send GOAWAY frame <version=2, flags=0, length=4> (last_good_stream_id=0)Hrm... it would be nice if
spdycat
followed the Javascript and image links in there in order to get a better feel for real SPDY conversations. Still, it works so my problems is not spdycat
but something in the spdy/3 conversation.Since I
spdycat
is being unhelpful here, I am forced to inspect the SPDY conversation with Wireshark. What I find is that node-spdy is sending back a SETTINGS frame, which ought to be perfectly OK:It ought to be OK because both Chrome and Firefox not only accept this frame, but seem to process it.
spdycat
on the other hand, not so much.Update: Figured out my problem. I had encoded the wrong number of entries in the SETTINGS frame (I advertised 2 but only sent 1). I could ask for a more helpful error message, but the problem is solved and I have my spdy/3 conversation:
➜ spdylay git:(master) spdycat -vvv https://localhost:3000 [ 0.007] NPN select next protocol: the remote server offers: * spdy/3 * spdy/2 * http/1.1 * http/1.0 NPN selected the protocol: spdy/3 [ 0.017] send SYN_STREAM frame <version=3, flags=1, length=102> (stream_id=1, assoc_stream_id=0, pri=3) :host: localhost:3000 :method: GET :path: / :scheme: https :version: HTTP/1.1 accept: */* user-agent: spdylay/0.1.0 [ 0.019] recv SETTINGS frame <version=3, flags=0, length=12> (niv=1) [4(1):100] [ 0.093] recv SYN_REPLY frame <version=3, flags=0, length=34> (stream_id=1) :status: 200 :version: HTTP/1.1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><title>Express w/ SPDY 3</title><link rel="stylesheet" href="/stylesheets/style.css"/><script src="/main.js"></script></head><body><h1>Express w/ SPDY 3</h1><p>Welcome to Express w/ SPDY 3</p><p><Here>is a huge image (830kb)</Here><br/><jammed>into a height=200</jammed><br/><img> tag: </p><img src="images/pipelining.jpg" height="200"/><p><And>another image (956kb)</And><br/><Also>jammed into a small</Also><br/><img> tag: </p><img src="images/tube.jpg" height="200"/></body></html>[ 0.093] recv DATA frame (stream_id=1, flags=0, length=629) [ 0.094] recv DATA frame (stream_id=1, flags=1, length=0) [ 0.094] send GOAWAY frame <version=3, flags=0, length=8> (last_good_stream_id=0)With that, I can try out my update receive window code by way of the
-w
switch. I use that switch to specify a 16kb receive window (214 == 16kb):➜ spdylay git:(master) spdycat -v -w14 -n https://localhost:3000/images/pipelining.jpg [ 0.003] NPN select next protocol: the remote server offers: * spdy/3 * spdy/2 * http/1.1 * http/1.0 NPN selected the protocol: spdy/3 [ 0.008] send SETTINGS frame <version=3, flags=0, length=12> (niv=1) [7(0):16384] [ 0.008] send SYN_STREAM frame <version=3, flags=1, length=118> (stream_id=1, assoc_stream_id=0, pri=3) :host: localhost:3000 :method: GET :path: /images/pipelining.jpg :scheme: https :version: HTTP/1.1 accept: */* user-agent: spdylay/0.1.0 [ 0.009] recv SETTINGS frame <version=3, flags=0, length=12> (niv=1) [4(1):100] [ 0.017] recv SYN_REPLY frame <version=3, flags=0, length=34> (stream_id=1) :status: 200 :version: HTTP/1.1 [ 0.017] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.017] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.017] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.017] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.017] recv DATA frame (stream_id=1, flags=0, length=1300) .... (stream_id=1, delta_window_size=9100) [ 0.227] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.265] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.265] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.265] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.265] recv DATA frame (stream_id=1, flags=0, length=1300) [ 0.265] recv DATA frame (stream_id=1, flags=0, length=1300)It actually seems to work right up until the end when the server seemingly fails to send more data. At first glance, my implementation in node-spdy seems to be at fault, but I will investigate further tomorrow.
Day #380
No comments:
Post a Comment