

Speeding up HTTP with minimal protocol changes - jgrahamc
http://blog.jgc.org/2012/12/speeding-up-http-with-minimal-protocol.html

======
paulsutter
Patrick McManus (a Firefox developer) added the following comment directly on
the blog posting. An underlying/implied point is that SPDY is already shipped
[1].

"

Your premise seems to be that SPDY is a complex binary protocol and we don't
need that achieve mux. I don't see much evidence for your complexity assertion
- especially when compared with HTTP/1.

Consider the difference in finding the message boundaries of a SPDY message
with an HTTP/1.1 one.

To process the SPDY message you

* buffer 8 bytes of data

* determine that it is a data packet by confirming (buf[0] & 0x80 == 0)

* determine the length of the data by ntohl(((uint32_t)buf)[1)& 0x00ffffff

* find out if there are more chunks to come by looking at (buf[4] & 1).

All done. It is very straightforward, efficient, well bounded, and testable.

Contrast that with parsing a HTTP/1 message.

* read "enough" data from the network. You don't know how much that is and if you read too much you have to implement some facility for "putting it back" so the next message can use it. This inevitably leads to streaming parsers and their inherent complexity and lack of efficiency.

* parse the headers so you can determine which message delimiter is being used. To parse the header you have to implement line folding, implement a tokenizer aware of various rules around quotation marks and colons, and adopt to a number of real-world variations in the use of line endings other than just CRLF. To do this you have to run a state machine against every byte of input rather than directly address fixed offsets.

* Implement strategies to deal with conflicts such "Content-Length: 42, 17" and "Content-Length: 95\r\nTransfer-Encoding: chunked\r\n"

* you need to implement an ascii to integer conversion routine to determine the status code because some status codes implicitly impact message delimiters (e.g. 304).

* now you have to implement no less than 5 message delimiting schemes - chunked encodings, EOF, content-length, implicit (304), and everyone's favorite multibyte/ranges.

* If you have content-length or a chunk you'll need to convert a text string to an integer again.. and http/1 doesn't bound the size of the text string so you'll either have a common bug with an overflow or you'll implement an undiscoverable constraint in your implementation leading to cases of failing interop.

* you'll still have exposure to a whole class of CRLF injection attacks inherent in the text format.

The binary framing is so much less complex and significant improvement. Sure,
to the naked eye in a log it may not look that way but that is optimizing for
all the wrong things and can be quite misleading - do you really interpret a
HTTP header with line folding or \n\r instead of \r\n sequences correctly when
eyeballing it?)

Now there certainly is some complexity in SPDY but it doesn't come, in my
opinion, from the binary framing that you're talking about here. That is a a
significant simplification over HTTP/1.

"

[1] [https://hacks.mozilla.org/2012/02/spdy-brings-responsive-
and...](https://hacks.mozilla.org/2012/02/spdy-brings-responsive-and-scalable-
transport-to-firefox-11/)

~~~
dsl
Developers of almost any skill level can write an HTTP/1.1 compatible
implementation (and its fault tolerant enough that compatible-ish is ok). Soon
we will live in a world with 3 or 4 HTTP/2.0 libraries and developers will
have almost no knowledge of how the thing works under the hood.

~~~
agentS
It's not as if it needs to become opaque to developers. If people need a way
to introspect a SPDY connection, someone will write a tool.

This is in the same vein as TCP, TLS, or DNS. Developers use these
technologies all the time, and it exposes a simple interface to them, but all
of them are quite complicated under the covers. I don't believe that Nagle's
algorithm or the UDP framing for DNS is common knowledge amongst the majority
of developers.

Similarly, SPDY or HTTP/2.0 will expose a simple interface to developers, but
internally transport the content in an efficient, if somewhat inscrutable,
manner.

While I appreciate the sentiment that HTTP is easy to understand as it stands,
I'd rather not optimize for "developers of almost any skill level". While it
would be totally OK for you to write a HTTP/1.1 parser as a toy, it would be
somewhat crazy for you to use that implementation in a production scenario.

There, everybody uses a small set of implementations that have been vetted:
Firefox, Chrome, Apache, Nginx, etc. These guys work extremely hard to make
sure that their implementation is solid not merely "compatible-ish". I'd
rather optimize for them, rather than the beginners. I'd rather make it easy
for them to write correct implementations. I'd rather jettison the "fault
tolerant enough that compatible-ish is ok" design of HTTP/1.1 that makes their
life so difficult.

~~~
dsl
I think you made the point for me. TCP, TLS, and DNS (with the exception of
OpenDNS a good 20 years later) see no innovation or hacking from startups.
Compare to plain text protocols like HTTP.

------
samwillis
I like this, it seems so much more clean than SPDY, HTTP really should stay a
text based protocol. My understanding for the design of SPDY was that by
forcing TLS it ensured that any proxy server in the middle wouldn't interfere
or be "confused" with the connection.

What would an existing proxy server (i.e. doesn't understand HMURR) do with a
connection that looked like this?

~~~
CJefferson
That is I think one of the big problems with this suggestion.

The world is full of proxies and html-consuming tools that do not bother to
check the version number, which would get horribly confused by this, and
probably mangle the stream.

I was under the impression one of the reasons SPDY is so different is
precisely to try to avoid problems with bad proxies. I'm not an expert on this
unfortunately, but I'm sure that quite a few bits of the web-proxy standard
involved getting around badly behaving proxies, and they had to do a few
strange things.

~~~
0x0
Even if the tool does not check the version number in the request, wouldn't
any proxied response contain a HTTP/1.0 or HTTP/1.1 header? In that case, the
client will see all but the first request in a connection stands no chance of
being fulfilled, and fall back to HTTP/1.1 behaviour (perhaps caching a
NO_HTTP_1_2 flag per hostname for a while)

Edit: Or also add an "Upgrade: slices" to the initial request, and if the
expected response to the Upgrade: request is missing, continue with HTTP 1.1.

~~~
jgrahamc
Correct. Since each connection in my proposal starts with a non-pipelined
request the HTTP version number of the response could be examined.

------
hobohacker
It's unclear to me what advantages this proposal has over a HTTP/2.0 proposal
using HTTP Upgrade. AFAICT, the client is unable to rely on HTTP/1.2 support
in the first roundtrip. Therefore, it cannot begin utilizing the new proposed
features. Relying on the HTTP/1.2 HTTP-Version within the Request-Line and
Status-Line strikes me as less safe than trying to use the Upgrade header to
attempt to upgrade to HTTP/2.0, as it seems much more likely for
intermediaries to have broken HTTP-Version parsing than broken Upgrade
support. In contrast with this proposal, when using the Upgrade header, there
is much more freedom to change the wire level representation of the protocol.
And if you're going to do HTTPS anyway, using TLS-NPN to negotiate a
completely wire level representation (without an additional roundtrip over the
TLS handshake roundtrip[s]) likewise enables more freedom to add features.

It seems like the main reason to prefer this proposal is if you do not want to
write another parser for HTTP/2.0 and think that new features afforded by a
different wire level representation are not worthwhile.

Another thing to note is that this proposal effectively adds on multiplexing
without prioritization. Prioritization is fairly important, otherwise the
client application has to do application layer throttling in order to reduce
contention. Adding prioritization would help obviate the need to make the link
utilization vs contention tradeoff.

~~~
jgrahamc
You could easily modify this proposal to use the Upgrade header for greater
safety in interacting with older systems or use TLS-NPN.

Can you give an example of prioritization in practice?

~~~
hobohacker
I see, so your main point really is that you don't think the new features
proposed in HTTP/2.0 are worth the change in wire representation.

Prioritization: Generally speaking, documents (HTML) > script/stylesheets
(JS&CSS) > subresources (e.g. images). [https://insouciant.org/tech/resource-
prioritization-in-chrom...](https://insouciant.org/tech/resource-
prioritization-in-chromium/) [https://insouciant.org/tech/throttling-
subresources-before-f...](https://insouciant.org/tech/throttling-subresources-
before-first-paint/)

~~~
jgrahamc
I'm mostly disappointed to see a protocol that has thrived as textual (as have
so many others over the years) get a binary layer added.

Some binary layers are helpful: TLS, for example, adds a nice generic
encryption layer to connections. SPDY, on the other hand, has intimate
knowledge of the operation of HTTP (which it needs to do header compression,
for example). For example, the header compression dictionary is based on
today's usage of HTTP with no provision for it to change over time.

It is a shame that features that are important to HTTP (multiplexing and
priority) are not being added within HTTP itself.

~~~
hobohacker
I believe Patrick already well explained why this the binary representation is
useful, so I won't bother addressing that.

I'd like to comment on your statement on the header compression dictionary.
Firstly, your statement is only true for the _initial_ header compression
dictionary. Most of the improvement is achieved strictly through the use of
compression at all, with very marginal gains from different compression
algorithms or better initial dictionaries. If your implication is that
intimate knowledge of HTTP is required for SPDY compression to work well, that
is false. Please note the research at
<http://www.eecis.udel.edu/~amer/PEL/poc/pdf/SPDY-Fan.pdf> with the following
conclusion: "This result suggests that zlib’s adaptive dictionary evolves to
roughly an equivalent state after compressing the first header regardless of
the content of the initial dictionary."

Can you clarify what you mean about features not being added within HTTP
itself? Where are you drawing the line for "HTTP itself"? I'm curious, since
the ideas behind SPDY have already been proposed for HTTP/2.0, and indeed the
starting point for the HTTP/2.0 proposal used the SPDY draft, so I don't know
what it means that important features aren't being added to HTTP itself.

~~~
jgrahamc
Yes, that gzip result is unsurprising.

When I think of HTTP I think of a text-based protocol not binary. I fully
understand that SPDY has essentially been ratified as HTTP 2.0 given that the
charter has been updated to be so close to SPDY itself. Thus at some point
what I think if as HTTP will need updating to simply include SPDY and what I
currently think of HTTP will be moot.

------
lucian1900
Transparent proxies, stupid antivirus software ... There's no end to the
amount of crap that interferes with HTTP. These days, the only way one can get
anything through is over SSL.

------
asdfaoeu
I don't really see the point. Spdy is already supported by major browsers more
than this would ever have. Why have half the features when you can have them
all? Also browsers would need new APIs to deal with getting the data from
slicing as it doesn't seem to properly support something like web sockets (no
multiplexing of the input, does spdy support web sockets?)

~~~
samwillis
True, it doesn't help with web sockets but I think it could work very nicely
with "Server-sent events"[1]. I believe that in almost all cases (Except
gaming) where people are trying to use web sockets you can achieve the same
result with SSE.

1: <http://en.wikipedia.org/wiki/Server-sent_events>

------
pibi
Maybe better without Slice-Length header for chunked encoding to avoid
buffering on web server.

~~~
matthavener
Doesn't the slice length help the server avoid buffering? You can send a
incomplete amount of data out as soon as you've computed it.

~~~
pibi
I think of dynamic pages. How could the webserver compute the slice length
without known chunked length and buffering?

------
vaxdigitalnh
I'd like to be able to use pipelining without having to de-chunk. That is,
thanks to the the HTTP/1.1 protocol I have never found a server that accepts
pipelined requests and delivers unchunked output. Is there a reason they must
be linked together? Maybe you could compare this to how SPDY requires header
compression; you can't get the other features without accepting the
compression feature... the single feature which was the source of the security
problems.

In sum, why can't we uncouple features that each may have merit on their own?

~~~
patrickmcmanus
Optional features tend to be broken in practice. So HTTP/2 is trying to have
far fewer of them so that implementations will be more robust.

Chunked requests and pipelines are good examples. They are rarely used but
absolutely supported by the spec. But because they are rarely used they break
a lot in practice and are not realistically deployable on Internet scale.

------
rwj
I wonder why no one has defined a multi-part message format. The server would
deliver a single file, which would start with the HTML and be followed by
images and other assets. This approach should play nice with all the proxies
(which would see a single octet stream), and, since the pipelining multiplexes
everything on a single connection anyway, deliver similar performance.

I understand that no one wants to define a new format, but I don't see how it
is more difficult than defining a new protocol.

~~~
hobohacker
While intriguing, this suggestion has a number of issues. For one, it implies
coalescing a number of resources into a single resource named by a single URL.
This means that all resources would have to share the same caching properties.
Since it's generally desirable to keep the main document (the HTML)
uncacheable to allow site updates, this would imply eliminating the vast
majority of web caching, which sounds undesirable.

~~~
rwj
I may be mistaken, but since everything in SPDY happens over a secure
connection, isn't caching already broken? It seams to me that any approach to
combining resources to minimize round-tripping will involve a trade-off
against the granularity of caching properties.

~~~
hobohacker
I think the source of the confusion is that you are conflating sharing of a
[SPDY] connection with sharing of a resource identifier (a URI/URL). This is a
misunderstanding. SPDY multiplexes multiple streams over a single connection
(or more accurately, a session). Each stream is used in the HTTP layering case
to correspond to a request/response pair for fetching a resource at a URL. So,
even though a single connection is shared, different resources at different
URLs can be simultaneously requested over the same SPDY connection.

In short, SPDY does not imply combining resources. Application level combining
of resources is a common hack to work around lack of parallelization at the
HTTP/1 protocol level, but as noted, it has deficiencies.

~~~
rwj
I'm aware of the distinction. However, part of the goal of SPDY is to avoid
round-trip by pre-emptively sending resources. So the question still remains,
why not use some type of archive format to send the HTML along with the images
and other assets that would have been pushed down the pipeline? Clients could
negotiate for the bundle using Content-accept, and proxies would not be
broken.

------
FooBarWidget
Not sufficient. If you look at the SPDY spec you'll see commands for window
control, which is necessary in a serious multiplexing protocol. This spec
doesn't have it.

