On second thought, don't use 'enumerator' types in 'initiateStream'.

I could be wrong, but I don't think it's possible to get just the next
element from an enumerator, leaving it in a state where it can be
resumed. This means that the third argument of 'initiateStream' shouldn't
be an Enumerator.

It should be possible to just submit the next element to an iteratee,
but there is no guarantee that the resulting iteratee will be able to
consume more input. While we could retain the Iteratee types in the
last two arguments of 'intiateStream', I've decided not to.

Besides, now the data source and the header and data consumers can
simply be stored in the newly-created Stream. This should be enough
to get something that works; if the types turn out not to be ideal,
it should be relatively easy to change them later.

Since 'intiateStream' was the only thing that used Iteratee or Enumerator,
I've removed the dependency on the 'enumerator' package in the cabal file.
Rename 'ping.hs' to 'SPing.hs', and the module from 'Main' to 'SPing'.

Reading through the GHC flags, I see that with '-main-is' it is possible
to name the module containing the 'main' function. This will come in
handy when there is more than one little test program, since we won't
have several files containing 'Main' modules.
Implement just the basics of down-loading a data stream from the server.

This commit fills out enough of 'initiateStream', and adds enough
frame handlers in 'readFrames', that we can can open a half-open
stream in order to download the contents of a url from the 'flip'
server. I've tried this out and confirmed that it works with a small
test program, which I'll add in the next commit.

There is still a fair bit of stuff that is missing here. In particular:

- other than the first chunk of data, there is no way to submit further
  further data chunks to the remote endpoint

- if the list of headers is too big, we have no way to detect it, and
  have no way to continue the headers in additional HEADERS frames

- we do not respond to RST_STREAM frames, so if the server signals an
  error we will not properly clean up the stream

- we have no way to cancel open streams when there is a connection
  error, or when we receive a GOAWAY

- we do not defensively handle exceptions that can occur when calling
  into the application-supplied data source, header consumer, or
  data consumer

But, despite the short-comings, it seems we can correctly receive
SYN_REPLY and DATA frames.

In the process of making these changes, I realized that the proper
application-facing interface for 'initiateStream' should use the
client and the connection key. However, now there is an
inconsistency between 'ping' and 'initiateStream', in two respects:

- 'ping' puts the client and connection key arguments last, but
  'initiateStream' puts them first

- 'ping' collects all the ping-specific arguments in a value of type
  PingOptions, so there can be some reasonable defaults, but
  'initiateStream' does not. We should pick a style and stick with it.

I'll resolve these issues in future commits.

With respect to the issue of not sending all the data that might be
produced by the data-producer action, there isn't a lot of testing
that we can easily do here with the flip server -- or at least I don't
think there is. Maybe if we can arrange for some responses to HTTP
POSTS we can try the uploading functionality, but I'm not sure how
much we can really exercise it.

However, were we to start implementing a server, we could write some
unit tests that exercise the client and server talking to one
another. There is quite a bit of code that could be factored out into
a common 'EndPoint' module, so perhaps we should spend some time on
that. I've also noticed that it takes quite a while to download a
large file like '/usr/share/dict/words', so it would also be a good
idea to do a little profiling and see where the performance problem
lies.
Add SGet.hs, a small program for fetching a resource from the flip server.

SGet.hs is slightly more complicated than SPing, and downloads a resource
from the flip server. Currently, it prints the headers and data on stdout,
and logs all frames on stderr, so to make the output useful it's best
to separate the two streams with redirection, e.g.

 ./sget host port file > file 2> file.log

will put the output in <file> and the logged frames in <file.log>.

I've tried it out with a handful of files I've got loaded up in the
modified 'flip' server, including the contents of '/usr/share/dict/words'
on my system, and it can recover the data faithfully.

I've also added a Makefile that can build the 'sping' and 'sget' programs,
and I've updated '.gitignore' to ignore them and the temporary directory
used for the build output.
Produce a version of 'sget' with profiling enabled.

This adds a target for an 'sget-prof' binary for SGet, with GHC time and
space profiling enabled.

The profile for 'sget-prof' when transferring the 'words' file isn't
terribly elluminating; it says that almost all the time is spent in
'readFrames', which is to be expected. However, none of the functions
called by 'readFrames' appears to be an obvious culprit.

Using the unix 'time' program to time the execution of 'sget' for this
file indicates that more than half the time is spent doing system calls:

  $ time ./sget 127.0.0.1 10041 words > words 2> words.log

  real                      0m6.137s
  user                      0m2.540s
  sys                       0m3.572s

which suggests that block-buffering stdout and stderr might solve the
problem. I'll try that separately.
Block buffer 'stdout' and 'stderr' in SGet.

Block buffering 'stdout' and 'stderr' turns out to make a huge
difference. Here are a couple of runs of the 'time' command:

  $ time ./sget 127.0.0.1 10041 words > words 2> words.log

  real	0m0.218s
  user	0m0.072s
  sys	0m0.008s

  $ time ./sget 127.0.0.1 10041 words > words 2> words.log

  real	0m0.161s
  user	0m0.064s
  sys	0m0.020s

For comparison, that was taking well over 6 seconds before, so that's
a solid 25 times improvement.
Block-buffer 'stdout' and 'stderr' in SPing too.

It turns out that a lot of the time that we're measuring in SPing is
often the time to log frames to 'stderr'. Block-buffering 'stderr' and,
for good measure, 'stdout' improves the performance of SPing too. Now
many of the measured round-trip times round to zero milliseconds when
we're talking to the flip server on the loopback interface, which seems
reasonable.
Rearrange imports slightly in SPing.

Put the Network.SPDY imports last and in their own section, to be
consistent with the other modules.
Remove the unused 'receiveHeaders' and 'receiveData' functions in Client.

It turns out that these functions aren't really necessary, since it's
convenient and a bit more flexible to put the functionality in
'readFrames'. I've removed them.
Send WINDOW_UPDATE frames from the client.

SPDY flow control requires that the client send WINDOW_UPDATE frames
when it consumes data sent by the server and frees up buffer space.
Once a DATA frame arrives with the FIN flag set, the client sends no
further WINDOW_UPDATE frames.

Since the client immediately passes received data chunks to the
data consumer associated with the stream, we need to change the type
of the consumer so it tells the client how much buffer space is
released. The consumer now has the type

  Maybe ByteString -> IO DeltaWindowSize

so it can say how many bytes have been released each time it is fed
more data.

It's possible that in some circumstances the application may take a
while to release buffer space, and so this interface may not be
sufficient. We should really add a function to the API specifically
for reporting changes in the window size when the data consumer
doesn't report window size changes. However, for many circumstances,
this interface may be enough. I'll make a separate commit to add
another API function.

These changes required a modest compensating change in the SGet test
program. There, the data consumer immediately writes bytes out to
stdout, so it always reports that the entire byte string is consumed.

The 'flip' server does not implement flow control, and in fact it has
absolutely no response to WINDOW_UPDATE frames. I've modified my version
of the server so that it logs WINDOW_UPDATE frames, but I currently
have no third-party server against which I can test my understanding of
flow control. That may need to wait until we can use TLS + NPN.
Derive Integral and supporting type class instances for DeltaWindowSize.

It will be convenient if DeltaWindowSize is an instance of Integral.
I've derived the default instance of Integral, and of the required
super classes.

This change enables some minor simplifications in the SGet test program,
which I've included here.
Don't send WINDOW_UPDATE frames if there is no change in window size.

It's entirely possible that the data consumer for a stream will report
a zero change in the window size. When this happens, there is no point
in sending a WINDOW_UPDATE frame to the sending endpoint. It's probably
harmless to do so, but why incur an unnecessary flush on the outgoing
network connection? I've guarded against this in 'readFrames'.
Add a public 'updateWindow' API function to the Client module.

There is a chance that application code will need to explicitly
participate in the SPDY flow control by signalling when there is
extra space in the window for data received from the remote endpoint.
I've added an 'updateWindow' function to do this, and factored out
the common code between it and 'readFrames' into "updateWindow'".

This isn't used yet, but I think it is necessary in the API.
Add a StreamOptions type and use it in 'initiateStream'.

The signature of 'initiateStream' is very long, and there may well be
some other arguments we'll need to add in the future. It is also
inconsistent with the signature for 'ping', which collects the ping-
specific arguments in a PingOptions type. So it seems reasonable to
add a StreamOptions type and a default 'defaultStreamOptions' value.

The 'defaultStreamOptions' value lets us get rid of 'defaultPriority',
and use simply Priority instead of (Maybe Priority) without burdening
application code. I've made the necessary adjustments in the SGet
module, and confirmed that I can still download files from the flip
server.

I've kept the list of headers out of StreamOptions, since the only
essential argument when creating a stream is the list of initial
headers for inclusion in the SYN_STREAM frame.

There is still one minor inconsistency between 'ping' and
'initiateStream'.  In 'ping', the client and connection key arguments
come after the options, whereas in 'initiateStream' they come before
the headers and options.  I'll pick one style in a subsequent commit.
Improve some of the API comments regarding flow control.

I've moved around and reworded some documentation so that it's a bit
clearer what the DeltaWindowSize is for in 'streamOptsDataConsumer'
and 'windowUpdate'. I'm still not very satisfied with it, but it will
do for now.
Add some notes on common behaviours of clients and servers.

A lot of the code in the Client module could be moved into a common
'EndPoint' module, and then be specialized for building a Client and
a Server. I've jotted down a few notes on what I think is needed to
allow extracting the common code.

The most pressing thing I think is to change the frame types so we
can deal with different kinds of frames as different types. This
can make it easier to plug in responses for different kinds of frames
that must be treated a bit differently by clients and servers, and
also make it impossible to accidentally use the wrong field selector
with the wrong variant of a multi-variant type.

That rework isn't difficult, but it will be messy, so I will perform
it in several small commits, starting next.
Move the DataFrame fields into a Data type.

This is the first step in a large reworking of the frame types, aimed
at allowing client and server code to treat individual frames in a
more abstract way. It will also make it impossible to use a field
selector for one variant of frame with another.

The unit tests still pass, and the test programs still build and work.
Move the SynStream fields into a separate SynStream type.

This is step 2 in the big frame type refactoring, and is the first of
the control frames. The tests still pass and the test programs still
run.
Move the SynReply fields into a SynReply type.

Step 3 in the big refactoring. The tests still pass and the test
programs still build and run.
Move the RstStream fields into an RstStream type.

Part 4 of the big refactoring. The tests still pass and the test programs
still compile and run.