<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en"><title type="text">igvita.com</title><link rel="alternate" type="text/html" href="http://www.igvita.com" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.igvita.com/igvita" /><subtitle type="text">a goal is a dream with a deadline</subtitle><author><name>Ilya Grigorik</name><email>ilya@igvita.com</email><uri>http://www.igvita.com/</uri></author><updated>2013-06-18T23:33:16+00:00</updated><feedburner:info uri="igvita" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><id>http://www.igvita.com/</id><geo:lat>37.40679</geo:lat><geo:long>-122.074613</geo:long><entry><title type="html">Innovating with HTTP 2.0 Server Push</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/7aGCWUvbF-Q/" /><updated>2013-06-12T00:00:00-07:00</updated><id>http://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/</id><content type="html">&lt;p&gt;&lt;img src='/posts/13/http-push.png' class='left' /&gt;&lt;strong&gt;HTTP 2.0 enables the server to send multiple responses (in parallel) for a single client request - aka, server push&lt;/strong&gt;. Except, why would we ever want to do such a thing? Well, an average page requires dozens of additional assets, such as JavaScript, CSS, and images, and references to all of these assets are embedded in the very HTML that the server is producing! Hence, instead of waiting for the client to discover references to these resources, what if the server just sent all of them immediately? Server push can eliminate entire roundtrips of unnecessary network latency.&lt;/p&gt;

&lt;p&gt;In fact, &lt;strong&gt;if you have ever inlined a resource (CSS, JS, or an image), you've been "simulating" server push&lt;/strong&gt;: an inlined resource is "pushed" as part of the parent document. The only difference is that HTTP 2.0 makes this pattern more efficient and far more powerful!&lt;/p&gt;

&lt;h2&gt;Hands-on with HTTP 2.0 server push&lt;/h2&gt;

&lt;p&gt;An inlined resource, by definition, is part of the parent document. Hence, it cannot be cached independently and may need to be duplicated across many different pages - this is inefficient. By contrast, pushed resources are cached individually by the browser and can be reused across many pages. An example is in order:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;spdy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// push JavaScript asset (/main.js) to the client&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/main.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alert(&amp;quot;hello from push stream!&amp;quot;)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// write main response body and terminate stream&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello World! &amp;lt;script src=&amp;quot;/main.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;What we have above is a minimal SPDY server implemented with the help of the &lt;a href="https://github.com/indutny/node-spdy"&gt;node-spdy module&lt;/a&gt;, which responds to all inbound requests by writing out a "Hello World!" string, followed by script tag. Except, we are also doing something clever: we push the &lt;code&gt;main.js&lt;/code&gt; file to the client, the body of which triggers a JavaScript alert.&lt;/p&gt;

&lt;p&gt;As a result, by the time the browser discovers the script tag in the HTML response the &lt;code&gt;main.js&lt;/code&gt; file is already in cache, and no extra network roundtrips are incurred! &lt;strong&gt;HTTP 2.0 server push &lt;a href="http://chimera.labs.oreilly.com/books/1230000000545/ch12.html#HTTP2_PUSH"&gt;obsoletes inlining&lt;/a&gt;&lt;/strong&gt;. Best of all, server push is already supported by all the SPDY-capable browsers (Firefox, Opera, and Chrome).&lt;/p&gt;

&lt;h2&gt;What else can we push?&lt;/h2&gt;

&lt;p&gt;Replacing inlined resources with server push is the canonical example. However, why stop there, what else could we push? Well, any HTTP response is fair game. &lt;strong&gt;Could we push a redirect? Yep, that's a simple task:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;spdy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//push JavaScript asset (/newasset.js) to the client&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/newasset.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alert(&amp;quot;hello from (redirected) push stream!&amp;quot;)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// push 301 redirect: /asset.js -&amp;gt; /newasset.js&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/asset.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;:status&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Location&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/newasset.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;301 Redirect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// write main response body and terminate stream&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;script src=&amp;quot;/asset.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Same example as above, except we've changed the &lt;code&gt;asset.js&lt;/code&gt; resource to be a 301 redirect to the &lt;code&gt;newasset.js&lt;/code&gt; file. The browser caches both the redirect and the asset and executes the script without any extra network roundtrips. Applications? I'll leave it as an exercise for the reader.&lt;/p&gt;

&lt;p&gt;Could we take this even further? What about pushing cache invalidations, and revalidations, to the client? We can mark any existing resource in client's cache as stale (i.e. push a stale timestamp), or conversely, update its lifetime by pushing a 304 with a future timestamp. &lt;strong&gt;In short, the server could actively manage the client's cache!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the sever is too aggressive or misbehaved, then the client can limit the number of pushed streams, or cancel individual streams as it desires. Finding the right strategy and balance on both sides will be key to getting the best performance out of server push - not an easy challenge, but one that could yield high returns.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: pushing cache revalidations is not supported by current browsers. Should it be?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Client-notifications for server push&lt;/h2&gt;

&lt;p&gt;HTTP 2.0 server push is not a replacement for technologies such as &lt;a href="http://chimera.labs.oreilly.com/books/1230000000545/ch16.html"&gt;Server-Sent Events&lt;/a&gt; (SSE) or WebSocket. Resources delivered via HTTP 2.0 server push are processed by the browser but do not bubble up to the application code - there is no JavaScript API to get notifications for these events. However, the solution to this dilemma is very simple, because we can combine an SSE channel with server push and get the best of both:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;spdy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// set content type for SSE stream&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/event-stream&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;messageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// push a simple JSON message into client&amp;#39;s cache&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;msg&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;resourcePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/resource/&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resourcePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// notify client that resource is available in cache&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data:&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;resourcePath&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\n\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Admittedly, a silly example, but one that illustrates the point: the server generates a message on a periodic two second interval, pushes it into the client's cache, and then sends an SSE notification to the client. In turn, the client can subscribe to these events in application code and execute its own logic to process the event:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;SSE notification: &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;br /&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// fetch resource via XHR... from cache!&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;xhr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Message: &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;br /&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The long-lived SSE stream, the pushed resources, and all other HTTP 2.0 streams are efficiently multiplexed over a single TCP connection - there is no extra connection overhead or unnecessary roundtrips. &lt;strong&gt;By combining SSE and server push we can deliver arbitrary resources (binary or text) to the client, get the benefit of leveraging the native browser cache, and execute the appropriate application logic!&lt;/strong&gt; Best of all, this works today.&lt;/p&gt;

&lt;h2&gt;Innovating with server push&lt;/h2&gt;

&lt;p&gt;Server push opens up an entire world of new optimization opportunities. The examples we have covered above are scratching the surface of what's possible, and there are many additional questions to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which resources should be pushed and when? Are they in cache already?&lt;/li&gt;
&lt;li&gt;Can the server automatically infer which resources should be pushed?&lt;/li&gt;
&lt;li&gt;Should advanced applications such as active cache management be supported?&lt;/li&gt;
&lt;li&gt;How can we design our applications to get the most out of server push?&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;With HTTP 2.0 the servers have the opportunity to become much, much smarter, both in how they optimize the delivery of individual assets, and even more importantly, the delivery of the entire application. Similarly, the browsers could expose additional API's and capabilities to make this process more efficient. In fact, in the long run, server push may well prove to be &lt;em&gt;the killer feature&lt;/em&gt; for HTTP 2.0!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S. curious to learn more about HTTP 2.0? Check out the &lt;a href="http://chimera.labs.oreilly.com/books/1230000000545?utm_source=igvita&amp;amp;utm_medium=referral&amp;amp;utm_campaign=igvita-body"&gt;free preview&lt;/a&gt; of my O'Reilly book!&lt;/em&gt;&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=7aGCWUvbF-Q:dq-tZUgsot8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/7aGCWUvbF-Q" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/</feedburner:origLink></entry><entry><title type="html">Deploying WebP via Accept Content Negotiation</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/lor0GCqlP3E/" /><updated>2013-05-01T00:00:00-07:00</updated><id>http://www.igvita.com/2013/05/01/deploying-webp-via-accept-content-negotiation/</id><content type="html">&lt;p&gt;&lt;img src='/posts/13/webp-nginx.png' class='left' /&gt;&lt;a href="http://www.igvita.com/2012/12/18/deploying-new-image-formats-on-the-web/"&gt;Deploying new image formats on the web&lt;/a&gt; is a challenge, but not an unsolvable one, and one that would pay high dividends in terms of performance. In fact, there are many ways to tackle the problem: client-side logic, server negotiation, and hybrid strategies. There is a time and place for each one - they are not exclusive.&lt;/p&gt;

&lt;p&gt;Having said that, I'm partial to server negotiation as it requires the least amount of work to move the web forward in a significant way: &lt;strong&gt;a single flip of a switch by a CDN or a large hosting provider, instantly enabling significant byte savings for all of their users&lt;/strong&gt;. Computers are great at performing routine negotiation work, &lt;a href="http://blog.yoav.ws/2012/07/Images-Can-we-have-less"&gt;humans are not&lt;/a&gt;: we get bored, we get lazy, we routinely forget to optimize our images.&lt;/p&gt;

&lt;h2&gt;WebP deployment checklist&lt;/h2&gt;

&lt;p&gt;As a hands on example, let's take a look at what we would need to enable Accept negotiation for deploying WebP:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User agents which support WebP should advertise it in the &lt;code&gt;Accept&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;Servers and caches should use the &lt;code&gt;Accept&lt;/code&gt; header to serve appropriate assets&lt;/li&gt;
&lt;li&gt;Origin servers must specify &lt;code&gt;Vary: Accept&lt;/code&gt; in generated responses&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Most importantly, notice what's missing: &lt;strong&gt;there is no mention of having to modify the markup or logic of millions of individual web applications.&lt;/strong&gt; Having said that, the above steps still require some work, let's dig a bit deeper.&lt;/p&gt;

&lt;h2&gt;Configuring Accept negotiation in Nginx&lt;/h2&gt;

&lt;p&gt;The great news is that Chrome Canary is &lt;a href="https://chromiumcodereview.appspot.com/13814024"&gt;now advertising&lt;/a&gt; &lt;code&gt;image/webp&lt;/code&gt; in its Accept header for all image requests - with that in place, we can check off the first item on our checklist above. Now, we need to configure our Nginx server to automatically serve the right file. To start, let's assume we have a pre-generated a WebP asset on disk:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="nginx"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# check Accept header for webp, check if .webp is on disk&lt;/span&gt;
  &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$http_accept&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;webp&amp;quot;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt; &lt;span class="s"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(-f&lt;/span&gt; &lt;span class="nv"&gt;$request_filename.webp&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;${webp}T&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# if WebP is supported, and WebP is on disk, serve it!&lt;/span&gt;
  &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$webp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;TT)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Vary&lt;/span&gt; &lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;(.*)&lt;/span&gt; &lt;span class="nv"&gt;$1.webp&lt;/span&gt; &lt;span class="s"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;First, we check if the Accept header is advertising WebP. Then we check if there is a corresponding file with a &lt;code&gt;.webp&lt;/code&gt; extension on disk. If both conditions match, we serve the WebP asset and add &lt;code&gt;Vary: Accept&lt;/code&gt; - done.&lt;/p&gt;

&lt;p&gt;Now, let's consider the case where Nginx acts as a proxy cache. Here we need to teach Nginx to cache and serve multiple variants of the same image (WebP and non-WebP versions) based on the value of the &lt;code&gt;Accept&lt;/code&gt; header:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="nginx"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# lookup inbound requests with exta WebP flag if advertised in Accept&lt;/span&gt;
    &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$http_accept&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;webp&amp;quot;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$webp&lt;/span&gt; &lt;span class="s"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_cache_key&lt;/span&gt; &lt;span class="nv"&gt;$scheme$proxy_host$request_uri$webp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# proxy requests to remote servers&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_cache&lt;/span&gt; &lt;span class="s"&gt;my-cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Not much different from the previous example! Instead of checking the local disk for the asset, we pass the request through to some set of backends, and append the WebP bit to our cache key - that's all there is to it.&lt;/p&gt;

&lt;h2&gt;Accept fragmentation and Key&lt;/h2&gt;

&lt;p&gt;Wait, why didn't we just append the &lt;code&gt;$http_accept&lt;/code&gt; variable to our proxy cache key above? Unfortunately, the &lt;code&gt;Accept&lt;/code&gt; header varies from browser to browser, which means that using the raw header would unnecessarily fragment the cache. Hence, we scan for WebP support in the header and add an additional bit to the cache key.&lt;/p&gt;

&lt;p&gt;However, wouldn't it be nice if we had a standard mechanism to define a custom cache key? Well, good news, there is work underway on the new &lt;a href="http://tools.ietf.org/html/draft-fielding-http-key-02"&gt;Key response header&lt;/a&gt;, which will act as a smart replacement to &lt;code&gt;Vary&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
The 'Key' header field for HTTP responses allows an origin server to describe the cache key for a negotiated response: a short algorithm that can be used upon later requests to determine if the same response is reusable.
&lt;/blockquote&gt;


&lt;h2&gt;IE is special, as always!&lt;/h2&gt;

&lt;p&gt;Unfortunately, &lt;code&gt;Vary&lt;/code&gt; support in IE, even in the latest versions, remains very poor: IE does not cache outbound headers, which means it cannot perform the appropriate matching algorithm, and is &lt;a href="http://blogs.msdn.com/b/ieinternals/archive/2009/06/17/vary-header-prevents-caching-in-ie.aspx"&gt;forced to issue a revalidation request&lt;/a&gt; on any asset with &lt;code&gt;Vary&lt;/code&gt;. As a result, to avoid the extra requests, we can create a special case for IE:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="nginx"&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$http_user_agent&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;(?i)(MSIE)&amp;quot;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# drop Vary for IE users, mark resource as private&lt;/span&gt;
   &lt;span class="kn"&gt; proxy_hide_header&lt;/span&gt; &lt;span class="s"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt; add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;private&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;With the above configuration in place, IE users won't see &lt;code&gt;Vary&lt;/code&gt;, and resource is marked as private: it can still be cached on the client, but it won't be cached by any other intermediate proxy.&lt;/p&gt;

&lt;h2&gt;The "action plan", continued...&lt;/h2&gt;

&lt;p&gt;Small steps, but we're &lt;a href="http://www.igvita.com/2012/12/18/deploying-new-image-formats-on-the-web/"&gt;heading in the right direction&lt;/a&gt;. We've addressed the issue on the client, we know how to configure our servers, now we need to push for better support by proxies and CDN's - ideally, by deploying support for Key! The good news is, this requires work, but it is not rocket science. As a case in point, CDNConnect recently &lt;a href="http://blog.netdna.com/developer/how-to-reduce-image-size-with-webp-automagically/"&gt;enabled transparent Accept+WebP negotiation for all of their users&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, let's take a look at Facebook's recent experiment with WebP, as &lt;a href="http://arstechnica.com/information-technology/2013/04/chicken-meets-egg-with-facebook-chrome-webp-support/"&gt;summarized by ArsTechnica&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
It turns out that Facebook users routinely do things like copy image URLs and save images locally... As a result, users were left with unusable URLs and files; files they couldn't open locally, and URLs that didn't work in Internet Explorer, Firefox, or Safari.
&lt;/blockquote&gt;


&lt;p&gt;URL sharing is not an issue if &lt;code&gt;Accept&lt;/code&gt; negotiation is configured correctly - see instructions above. Next, once a file is saved locally, you need a local WebP codec or viewer. The good news is, Chrome just &lt;a href="https://chromiumcodereview.appspot.com/14021010"&gt;landed a patch&lt;/a&gt; to register itself as a default WebP handler across all platforms - if you have Chrome, you can view local WebP files, and there is a growing list of &lt;a href="http://en.wikipedia.org/wiki/WebP#Support"&gt;editors, viewers, and plugins&lt;/a&gt;. Also, there are ongoing discussions about enabling the browser to save a "safe copy" of the asset to mitigate the problem entirely. One step at a time, we're getting there!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: &lt;a href="https://gist.github.com/sergejmueller/5500879"&gt;configuring Apache to serve WebP&lt;/a&gt; with Accept negotiation, courtesy of Sergej Müller.&lt;/em&gt;&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=lor0GCqlP3E:6RIC-luZ0Sg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/lor0GCqlP3E" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2013/05/01/deploying-webp-via-accept-content-negotiation/</feedburner:origLink></entry><entry><title type="html">Faster, smaller and more beautiful web with WebP</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/0pbefuN7FIQ/" /><updated>2013-03-07T00:00:00-08:00</updated><id>http://www.igvita.com/2013/03/07/faster-smaller-and-more-beautiful-web-with-webp/</id><content type="html">&lt;p&gt;&lt;img src='/posts/13/webp-small.png' class='left' /&gt;At the risk of sounding repetitive... An average page is now &lt;a href="http://httparchive.org/interesting.php#bytesperpage"&gt;over 1300 kB&lt;/a&gt; in size and over 60% of that is in images. Hence, if you have limited time, then &lt;strong&gt;inspecting and optimizing your image assets will likely yield the highest rate of return&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Case in point, the new &lt;a href="https://developers.google.com/chrome/mobile/docs/data-compression"&gt;data compression proxy for Chrome&lt;/a&gt; applies dozens of different content optimizations, but image optimization almost invariably comes out at the top. End result? On average, data usage is reduced by 50%! The strategy? Simple, transcode all images to &lt;a href="https://developers.google.com/speed/webp/"&gt;WebP&lt;/a&gt;!&lt;/p&gt;

&lt;div class='ytvideo' id='4tu2SJfSalA'&gt;&lt;/div&gt;


&lt;p&gt;With that in mind, I had a chance to sit down with Stephen Konig (Product Manager on the WebP team), to chat about the latest news, team progress over last two years, and where WebP is heading. You can scan through the &lt;a href="https://docs.google.com/presentation/d/1NidHQ-HAWpgQiYJ44gOfgcp_FQ2u59WE4eHuSvwddXY/present"&gt;slides&lt;/a&gt;, or watch the &lt;a href="http://www.youtube.com/watch?v=4tu2SJfSalA"&gt;GDL on YouTube&lt;/a&gt;, but below are a few highlights and resources to help you get started.&lt;/p&gt;

&lt;h2&gt;Focus on adoption in 2013&lt;/h2&gt;

&lt;p&gt;The primary focus and goal for the WebP team over the course of the past two years has been on developing the necessary features of the format itself - i.e., the engineering part. The good news is, end of 2012 marked an important milestone: support for lossy and lossless compression, alpha channel, animation, metadata, color profiles, and more. With all of these features in place, &lt;strong&gt;the focus is shifting towards tooling and driving adoption&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/chrome-webstore.png' class='left' /&gt;In fact, in the time-honored tradition of dogfooding own products, there is already a large and growing list of Google properties (Gmail, Drive, Picasa, Instant Previews, Play Magazines, Image Search, YouTube, ...) with WebP support. Most recently, Chrome Web Store &lt;a href="http://blog.chromium.org/2013/02/using-webp-to-improve-speed.html"&gt;switched to WebP&lt;/a&gt;, saw ~30% byte reduction on average, and is now saving &lt;strong&gt;several terabytes of bandwidth per day!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In parallel, there are now over 300,000 sites using the open-source &lt;a href="https://developers.google.com/speed/pagespeed/"&gt;PageSpeed&lt;/a&gt; libraries, which enable transparent WebP transcoding on Apache (&lt;a href="https://developers.google.com/speed/pagespeed/mod"&gt;mod_pagespeed&lt;/a&gt;) and Nginx (&lt;a href="https://github.com/pagespeed/ngx_pagespeed"&gt;ngx_pagespeed&lt;/a&gt;), and there is a growing list of commercial products (Torbit, EdgeCast) which can do similar optimizations - the bandwidth savings are hard to argue against!&lt;/p&gt;

&lt;h2&gt;"Time to Glass" vs. Bandwidth&lt;/h2&gt;

&lt;p&gt;WebP achieves better compression by spending more CPU cycles - that's an inherent tradeoff of any compression algorithm. Today, when compared to JPEG, the encoding speed for WebP is &lt;em&gt;~10x&lt;/em&gt; slower, and decoding is &lt;em&gt;~1.4x&lt;/em&gt; slower when done on the CPU. Is that big deal? The answer depends on your application: if you are generating unique and dynamic images on every request, then the extra CPU overhead is something you'll notice. But if the files are (mostly) static, then the encoding time is a non-issue.&lt;/p&gt;

&lt;p&gt;More likely, the concern is not over encoding, but over decoding speeds. Is &lt;em&gt;1.4x&lt;/em&gt; going to hurt your performance? Once again, it depends on your application - as with any performance metric, &lt;em&gt;measure it.&lt;/em&gt; The Ebay tech team recently published a &lt;a href="http://www.ebaytechblog.com/2013/02/22/a-picture-is-worth-a-thousand-words/"&gt;great overview of various image optimization techniques&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/webp-jpeg.png' class='center' style='max-width:760px;width:100%;' /&gt;&lt;/p&gt;

&lt;blockquote&gt;&lt;a href="http://www.webpagetest.org/video/compare.php?tests=130125_6N_KZA%2C130125_NH_KZ8&amp;amp;thumbSize=200&amp;amp;ival=100&amp;amp;end=full"&gt;This test from webpagetest.org&lt;/a&gt; compares the page load time of WebP vs. JPEG. The test has one page with 50 images in the WebP format, and another page with the same 50 images in the JPEG format. Because the WebP page had to download fewer bytes (474484 vs. 757228), it completes loading much earlier compared to the JPEG page... &lt;b&gt;If you track your web site's browser usage stats and find that Chrome/Opera users are a sizable chunk, using WebP images will improve the page load time for these users&lt;/b&gt;.&lt;/blockquote&gt;


&lt;p&gt;Despite the extra decoding time, the visual rendering time is much faster due to fewer bytes shipped. Further, for a significant segment of the population, bytes are (literally) expensive: &lt;strong&gt;bandwidth caps are a real constraint for many users, especially on mobile devices and in the developing world.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;Deploying WebP on Native and Web platforms&lt;/h2&gt;

&lt;p&gt;&lt;img src='/posts/13/android-ios.png' class='left' /&gt;Deploying WebP in a native app, iOS or Android, is actually very straightforward: Android 4.x.x+ has &lt;a href="https://docs.google.com/presentation/d/1NidHQ-HAWpgQiYJ44gOfgcp_FQ2u59WE4eHuSvwddXY/present#slide=id.gaf279013_033"&gt;native support&lt;/a&gt; for WebP, and there is a &lt;a href="https://github.com/alexey-pelykh/webp-android-backport"&gt;backport&lt;/a&gt; for earlier versions; on iOS you can use the official libraries provided by the WebP team (&lt;a href="http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/"&gt;tutorial&lt;/a&gt;, &lt;a href="https://github.com/carsonmcdonald/WebP-iOS-example"&gt;demo app&lt;/a&gt;). Since you control the display logic and the platform, you can safely convert all assets to WebP and save on data transfers both from and to the device - WebP helps for upload cases equally well!&lt;/p&gt;

&lt;p&gt;On the web, things get a bit more difficult, but still manageable. Chrome and Opera have native support for WebP, and there is an &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=600919"&gt;open discussion&lt;/a&gt; with the Firefox team. There are also third party plugins for &lt;a href="http://seiryu.home.comcast.net/~seiryu/weppy.html"&gt;Safari&lt;/a&gt; and &lt;a href="https://developers.google.com/chrome/chrome-frame/"&gt;Chrome Frame&lt;/a&gt; provides support for IE - however, you can't rely on these plugins being present. Instead, for time being you have to fall back to &lt;code&gt;User-Agent&lt;/code&gt; detection on the server, or use a &lt;a href="https://github.com/Modernizr/Modernizr/tree/master/feature-detects/img"&gt;JavaScript check&lt;/a&gt; - in fact, there is even a &lt;a href="http://webpjs.appspot.com/"&gt;JS polyfill&lt;/a&gt;! Finally, there is &lt;a href="https://code.google.com/p/chromium/issues/detail?id=169182"&gt;work in progress&lt;/a&gt; to fix &lt;code&gt;Accept&lt;/code&gt; negotiation to make this entire process &lt;a href="http://www.igvita.com/2012/12/18/deploying-new-image-formats-on-the-web/"&gt;much easier&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/webp-serverside.png' class='center' style='max-width:568px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;The most popular technique today and one that is used by PageSpeed and other optimization products, is to rely on server detection: run a &lt;code&gt;User-Agent&lt;/code&gt; check, and serve the HTML with WebP image links, or non-WebP links. What are the &lt;code&gt;User-Agent&lt;/code&gt; rules? PageSpeed is open-source, so the answer is &lt;a href="https://code.google.com/p/modpagespeed/source/browse/trunk/src/net/instaweb/http/user_agent_matcher.cc#86"&gt;right in the code&lt;/a&gt;. Further, to simplify this process, I've translated the rules into &lt;strong&gt;sample configuration files for Varnish and Nginx&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="download"&gt;&lt;a href="http://www.github.com/igrigorik/webp-detect/"&gt;webp-detect.git&lt;/a&gt; - User-Agent detection rules for WebP&lt;/div&gt;


&lt;p&gt;With above detection in place, Varnish and Nginx will report an extra &lt;code&gt;WebP: lossy, lossless&lt;/code&gt; header to the app server, allowing your application to generate customized HTML based on available WebP support. In turn, the static image assets can be safely cached either on the local file system or on any CDN service. The only other caveat is that the HTML must be marked with &lt;code&gt;Cache-Control: private&lt;/code&gt; to ensure that an intermediate cache does not accidentally serve the wrong file to the wrong browser.&lt;/p&gt;

&lt;p&gt;For more details on WebP, scan through the &lt;a href="https://docs.google.com/presentation/d/1NidHQ-HAWpgQiYJ44gOfgcp_FQ2u59WE4eHuSvwddXY/present"&gt;slides&lt;/a&gt;, and check out the &lt;a href="http://www.youtube.com/watch?v=4tu2SJfSalA"&gt;GDL on YouTube&lt;/a&gt; for additional context.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=0pbefuN7FIQ:hGNGBkhoAE4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/0pbefuN7FIQ" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2013/03/07/faster-smaller-and-more-beautiful-web-with-webp/</feedburner:origLink></entry><entry><title type="html">High Performance Networking in Google Chrome</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/1bH6oXMqciU/high-performance-networking-in-google-chrome" /><updated>2013-01-31T00:00:00-08:00</updated><id>http://www.igvita.com/posa/high-performance-networking-in-google-chrome</id><content type="html">&lt;div class="posa"&gt;
The following is a &lt;strong&gt;draft chapter&lt;/strong&gt; for the upcoming "The Performance of Open Source Applications" (POSA), a sequel to &lt;a href="http://www.aosabook.org/en/index.html"&gt;The Architecture of Open Source Applications&lt;/a&gt;. POSA is a collection of essays about performance optimization, designing for performance, managing performance as part of a development process, and more. &lt;strong&gt;The book will be published in Spring 2013 under a Creative Commons license with royalties going to Amnesty International.&lt;/strong&gt;&lt;/div&gt;




&lt;hr/&gt;




&lt;ul class="post-toc" id="toc"&gt;
  &lt;li&gt;&lt;a href="#history"&gt;History and guiding principles of Google Chrome&lt;/a&gt;
  &lt;li&gt;&lt;a href="#perf-facets"&gt;The many facets of performance&lt;/a&gt;
  &lt;li&gt;&lt;a href="#modern-application"&gt;What is a modern web application?&lt;/a&gt;
  &lt;li&gt;&lt;a href="#resource-request"&gt;The life of a resource request on the wire&lt;/a&gt;
  &lt;li&gt;&lt;a href="#fast-enough"&gt;What is "fast enough"?&lt;/a&gt;
  &lt;li&gt;&lt;a href="#network-stack"&gt;Chrome's network stack from 10,000 feet&lt;/a&gt;
    &lt;ul style="margin:0em"&gt;
      &lt;li&gt;&lt;a href="#multi-process"&gt;Multi-process architecture&lt;/a&gt;
      &lt;li&gt;&lt;a href="#ipc"&gt;Inter-process communication (IPC) and Multi-process resource loading&lt;/a&gt;
      &lt;li&gt;&lt;a href="#cross-platform"&gt;Cross-platform resource fetching&lt;/a&gt;
      &lt;li&gt;&lt;a href="#mobile"&gt;Architecture and performance on mobile platforms&lt;/a&gt;
      &lt;li&gt;&lt;a href="#predictor"&gt;Speculative optimization with Chrome’s Predictor&lt;/a&gt;
      &lt;li&gt;&lt;a href="#nutshell"&gt;Chrome network architecture in a nutshell&lt;/a&gt;
    &lt;/ul&gt;
  &lt;li&gt;&lt;a href="#lifetime"&gt;Lifetime of your browser session...&lt;/a&gt;
    &lt;ul style="margin:0em"&gt;
      &lt;li&gt;&lt;a href="#cold-boot"&gt;Optimizing the cold-boot experience&lt;/a&gt;
      &lt;li&gt;&lt;a href="#omnibox"&gt;Optimizing interactions with the Omnibox&lt;/a&gt;
      &lt;li&gt;&lt;a href="#dns-prefetching"&gt;Optimizing DNS with prefetching&lt;/a&gt;
      &lt;li&gt;&lt;a href="#tcp-pre-connect"&gt;Optimizing TCP connection management with pre-connect&lt;/a&gt;
      &lt;li&gt;&lt;a href="#prefetching"&gt;Optimizing resource loading with prefetch hints&lt;/a&gt;
      &lt;li&gt;&lt;a href="#prefreshing"&gt;Optimizing resource loading with browser prefreshing&lt;/a&gt;
      &lt;li&gt;&lt;a href="#prerendering"&gt;Optimizing navigation with prerendering&lt;/a&gt;
    &lt;/ul&gt;
  &lt;li&gt;&lt;a href="#faster"&gt;Chrome gets faster as you use it&lt;/a&gt;
&lt;/ul&gt;




&lt;hr /&gt;




&lt;h2 id="history" style="margin-top:1em;"&gt;History and guiding principles of Google Chrome &lt;a href="#history"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;Google Chrome was first released in the second half of 2008, as a beta version for the Windows platform. The Google-authored code powering Chrome was also made available under a permissive BSD license - aka, the Chromium project. To many observers, this turn of events came as a surprise: &lt;strong&gt;the return of the browser wars?&lt;/strong&gt; Could Google really do much better?&lt;/p&gt;

&lt;blockquote&gt;"It was so good that it essentially forced me to change my mind..." - Eric Schmidt, on his &lt;a href="http://blogs.wsj.com/digits/2009/07/09/sun-valley-schmidt-didnt-want-to-build-chrome-initially-he-says/"&gt;initial reluctance&lt;/a&gt; to the idea of developing Google Chrome.&lt;/blockquote&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/comic-chrome.png' class='left' /&gt;Turns out, they could. Today Chrome is one of the most widely used browsers on the web (&lt;a href="http://gs.statcounter.com/?PHPSESSID=oc1i9oue7por39rmhqq2eouoh0"&gt;35%+&lt;/a&gt; of the market share according to StatCounter) and is now available on Windows, Linux, OS X, Chrome OS, as well as Android and iOS platforms. Clearly, the features and the functionality resonated with the users, and many innovations of Chrome have also found their way into other popular browsers.&lt;/p&gt;

&lt;p&gt;The original &lt;a href="http://www.google.com/googlebooks/chrome/"&gt;38-page comic book&lt;/a&gt; explanation of the ideas and innovations of Google Chrome offers a great overview of the thinking and design process behind the popular browser.
However, this was only the beginning. The core principles that motivated the original development of the browser continue to be the guiding principles for ongoing improvements in Chrome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong style="color:green"&gt;Speed:&lt;/strong&gt; the objective is to make the &lt;strong&gt;fastest&lt;/strong&gt; browser&lt;/li&gt;
&lt;li&gt;&lt;strong style="color:green"&gt;Security:&lt;/strong&gt; provide the &lt;strong&gt;most secure&lt;/strong&gt; environment to the user&lt;/li&gt;
&lt;li&gt;&lt;strong style="color:green"&gt;Stability:&lt;/strong&gt; provide a &lt;strong&gt;resilient and stable&lt;/strong&gt; web application platform&lt;/li&gt;
&lt;li&gt;&lt;strong style="color:green"&gt;Simplicity:&lt;/strong&gt; sophisticated technology, wrapped in a &lt;strong&gt;simple user experience&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;As the team observed, many of the sites we use today aren't just web pages, they are applications. In turn, the ever more ambitious applications require speed, security, and stability. Each of these deserves its own dedicated chapter, but since our subject is performance, our focus will be primarily on speed.&lt;/p&gt;

&lt;h2 id="perf-facets"&gt;The many facets of performance &lt;a href="#perf-facets"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;A modern browser is a platform, just like your operating system, and Google Chrome is designed as such. Prior to Google Chrome, all major browsers were built as a monolithic, single process applications. All open pages shared the same address space and contended for the same resources. A bug in any page, or the browser, ran the risk of compromising the entire experience.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/comic-multi-process.png' class='left' /&gt;By contrast, &lt;strong&gt;Chrome works on a multi-process model&lt;/strong&gt;, which provides process and memory isolation, and a tight &lt;a href="http://dev.chromium.org/developers/design-documents/sandbox"&gt;security sandbox&lt;/a&gt; for each tab. In an increasingly multi-core world, the ability to isolate the processes as well as shield each open tab from other misbehaving pages has by itself proven to give Chrome a significant performance edge over the competition. In fact, it is important to note that most other browsers have followed suit, or are in the process of migrating to similar architecture.&lt;/p&gt;

&lt;p&gt;With an allocated process in place, the execution of a web program primarily involves three tasks: fetching resources, page layout and rendering, and JavaScript execution. The rendering and script steps follow a single-threaded, interleaved model of execution - it is not possible to perform concurrent modifications of the resulting Document Object Model (DOM). This is in part due to the fact that JavaScript itself is a single threaded language. Hence, optimizing how the rendering and script execution runtimes work together is of critical importance, both to the web developers building the applications as well as the developers working on the browser.&lt;/p&gt;

&lt;p&gt;For rendering, Chrome uses &lt;a href="http://en.wikipedia.org/wiki/WebKit"&gt;WebKit&lt;/a&gt;, which is a fast, open-source, and standards compliant layout engine. For JavaScript, Chrome ships with its own, heavily optimized &lt;a href="http://en.wikipedia.org/wiki/V8_(JavaScript_engine)"&gt;"V8" JavaScript runtime&lt;/a&gt;, which was also released as a standalone open-source project and has found its way into many other popular projects - e.g., runtime for node.js. &lt;strong&gt;However, optimizing V8 JavaScript execution, or the WebKit parsing and rendering pipelines won't do much good if the browser is blocked on the network, waiting for the resources to arrive!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ability of the browser to optimize the order, priority, and latency of each network resource is one of the most critical contributors to the overall user experience. You may not be aware of it, but Chrome's network stack is, quite literally, getting smarter every day, trying to hide or decrease the latency cost of each resource: it learns likely DNS lookups, it remembers the topology of the web, it preconnects to likely destination targets, and more. From the outside, it presents itself as a simple resource fetching mechanism, but from the inside it is an elaborate and a fascinating case study for how to optimize web performance and deliver the best experience to the user.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let's dive in...&lt;/em&gt;&lt;/p&gt;

&lt;h2 id="modern-application"&gt;What is a modern web application? &lt;a href="#modern-application"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;Before we get to the tactical details of how to optimize our interaction with the network, it helps to understand the trends and the landscape of the problem we are up against. In other words, &lt;strong&gt;what does a modern web page, or application look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://httparchive.org/"&gt;HTTP Archive&lt;/a&gt; project tracks how the web is built, and it can help us answer this question. Instead of crawling the web for the content, it periodically crawls the most popular sites to record and aggregate analytics on the number of used resources, content types, headers, and other metadata for each individual destination. The stats, as of January 2013, may surprise you. An average page, amongst the top 300,000 destinations on the web is:&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/httparchive-jan2013.png' class='center' style='max-width:700px;width:100%;' /&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1280 KB&lt;/strong&gt; in size&lt;/li&gt;
&lt;li&gt;composed of &lt;strong&gt;88 resources&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;connects to &lt;strong&gt;15+ distinct hosts&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let that sink in. Over 1MB in size on average, composed of 88 resources such as images, JavaScript, and CSS, and delivered from 15 different own and third-party hosts! Further, each of these numbers have been &lt;a href="http://httparchive.org/trends.php"&gt;steadily increasing&lt;/a&gt; over the past few years, and there are no signs of stopping. We are increasingly building larger and more ambitious web applications.&lt;/p&gt;

&lt;p&gt;Applying basic math to the HTTP Archive numbers reveals that an average resource is about 12KB in size (1045 KB / 84 resources), which means that &lt;strong&gt;most network transfers in the browser are short and bursty&lt;/strong&gt;. This presents its own set of complications because the underlying transport (TCP) is optimized for large, streaming downloads. Let's peel back the onion and inspect one of these network requests...&lt;/p&gt;

&lt;h2 id="resource-request"&gt;The life of a resource request on the wire &lt;a href="#resource-request"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;The W3C &lt;a href="http://www.w3.org/TR/navigation-timing/"&gt;Navigation Timing specification&lt;/a&gt; provides a browser API and visibility into the timing and performance data behind the life of every request in the browser. Let's inspect the components, as each is a critical piece of delivering the optimal user experience:&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/navtiming.png' class='center' style='max-width:635px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;Given the URL of a resource on the web, the browser starts by checking its local and application caches. If you have previously fetched the resource and the &lt;a href="https://developers.google.com/speed/docs/best-practices/caching"&gt;appropriate cache headers&lt;/a&gt; were provided (&lt;em&gt;Expires&lt;/em&gt;, &lt;em&gt;Cache-Control&lt;/em&gt;, etc.), then it is possible that we may be allowed to use the local copy to fulfill the request - &lt;strong&gt;the fastest request is a request not made&lt;/strong&gt;. Alternatively, if we have to revalidate the resource, if it expired, or if we simply haven't seen it before, then a costly network request must be dispatched.&lt;/p&gt;

&lt;p&gt;Given a hostname and resource path, Chrome first checks for existing open connections it is allowed to reuse - sockets are pooled by &lt;code&gt;{scheme, host, port}&lt;/code&gt;. Alternatively, if you have configured a proxy, or specified a &lt;a href="http://en.wikipedia.org/wiki/Proxy_auto-config"&gt;proxy auto-config&lt;/a&gt; (PAC) script, then Chrome checks for connections through the appropriate proxy. PAC scripts allow for different proxies based on URL, or other specified rules, each of which can have its own socket pool. Finally, if neither of the above conditions is matched, then the &lt;strong&gt;request must begin by resolving the hostname to its IP address - aka, a DNS lookup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If we are lucky, the hostname may already be cached in which case the response is usually just one quick system call away. If not, then a DNS query must be dispatched before any other work can happen. The time taken to do the DNS lookup will vary based on your internet provider, the popularity of the site and the likelihood of the hostname to be in intermediate caches, as well as the response time of the authoritative servers for that domain. In other words, there are a lot of variables at play, but it's not unusual for a DNS lookup to take up to &lt;em&gt;several hundred milliseconds&lt;/em&gt; - ouch.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/tcp-handshake.png' class='left' /&gt;With the resolved IP address in hand, Chrome can now open a new TCP connection to the destination, which means that we must perform the "&lt;a href="http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_establishment"&gt;three-way handshake&lt;/a&gt;": &lt;code&gt;SYN &gt; SYN-ACK &gt; ACK&lt;/code&gt;. This exchange adds a &lt;strong&gt;full roundtrip of latency delay to each and every new TCP connection - no shortcuts&lt;/strong&gt;. Depending on the distance between the client and the server, as well as the chosen routing path, this can yield from tens to hundreds, or even thousands, of milliseconds of delay. All of this work and latency is before even a single byte of application data has hit the wire!&lt;/p&gt;

&lt;p&gt;Once the TCP handshake is complete, and if we're connecting to a secure destination (HTTPS), then the SSL handshake must take place. This can add &lt;strong&gt;up to two additional roundtrips of latency delay&lt;/strong&gt; between client and server. If the SSL session is cached, then we can "escape" with just one additional roundtrip.&lt;/p&gt;

&lt;p&gt;Finally, Chrome is able to dispatch the HTTP request (&lt;code&gt;requestStart&lt;/code&gt; in the Nav Timing figure above). Once received, the server can process the request and then stream the response data back to the client. This incurs a minimum of a network roundtrip, plus the processing time on the server. Following that, we're done. Well, that is unless the actual response is an HTTP redirect! In which case, we may have to repeat the entire cycle once over. Have a few gratuitous redirects on your pages? You may want to revisit that decision!&lt;/p&gt;

&lt;p&gt;Have you been counting all the delays? To illustrate the problem, let's assume the worst case scenario for a typical broadband connection: local cache miss, followed by a relatively fast DNS lookup (50 ms), TCP handshake, SSL negotiation, and a relatively fast (100 ms) server response time, with a round-trip time of 80 ms (an average round-trip across continental USA):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50ms for DNS&lt;/li&gt;
&lt;li&gt;80ms for TCP handshake (one RTT)&lt;/li&gt;
&lt;li&gt;160ms for SSL handshake (two RTT's)&lt;/li&gt;
&lt;li&gt;40ms for request to server&lt;/li&gt;
&lt;li&gt;100ms for server processing&lt;/li&gt;
&lt;li&gt;40ms for response from the server&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;That's 470 milliseconds for a single request, which translates to &lt;strong&gt;over 80% of network latency overhead as compared to the actual server processing time to fulfill the request&lt;/strong&gt; - we have some work to do here! In fact, even 470 milliseconds may be an optimistic estimate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the server response does not fit into the initial TCP &lt;a href="http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Congestion_control"&gt;congestion window&lt;/a&gt; (4-15 KB), then one or more additional roundtrips of latency is introduced&lt;/li&gt;
&lt;li&gt;SSL delays could get even worse if we need to fetch a missing certificate or perform an &lt;a href="http://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol"&gt;online certificate status check&lt;/a&gt; (OCSP), both of which will require an entirely new TCP connection, which can add hundreds and even thousands of milliseconds of additional latency&lt;/li&gt;
&lt;/ul&gt;


&lt;h2 id="fast-enough"&gt;What is "fast enough"? &lt;a href="#fast-enough"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;The network overhead of DNS, handshakes, and the roundtrip times is what dominates the total time in our earlier case - the server response time accounts for only 20% of the total latency! But, &lt;strong&gt;in the grand scheme of things, do these delays even matter?&lt;/strong&gt; If you are reading this, then you probably already know the answer: yes, very much so.&lt;/p&gt;

&lt;p&gt;Past &lt;a href="http://www.useit.com/papers/responsetime.html"&gt;user experience research&lt;/a&gt; paints a consistent picture in what we, as users, expect in terms of responsiveness of any applications, both offline and online:&lt;/p&gt;

&lt;table border="1" class="delay" width="100%"&gt;
  &lt;tr&gt;
    &lt;td class="header"&gt;Delay&lt;/td&gt;
    &lt;td class="header"&gt;User Reaction&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td class="green"&gt;0 - 100ms&lt;/td&gt;
    &lt;td&gt;Instant&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;100 - 300ms&lt;/td&gt;
    &lt;td&gt;Small perceptible delay&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;300 - 1000ms&lt;/td&gt;
    &lt;td&gt;Machine is working&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td class="red"&gt;1s+&lt;/td&gt;
    &lt;td&gt;Mental context switch&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td class="red"&gt;10s+&lt;/td&gt;
    &lt;td&gt;I'll come back later...&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;The table above also explains the unofficial rule of thumb in the web performance community: render your pages, or at the very least, provide visual feedback in under 250 ms to keep the user engaged. This is not speed simply for speed's sake. Studies at Google, Amazon, Microsoft, as well as thousands of other sites show that additional latency has a direct impact on the bottom line of your site: &lt;strong&gt;faster sites yield more pageviews, higher engagement from the users, and see higher conversion rates.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, there you have it, our optimal latency budget is 250 ms, and yet as we saw in the example above, the combination of a DNS lookup, the TCP and SSL handshakes, and propagation times for the request add up to 370 ms. We're 50% over budget, and we still haven't factored in the server processing time!&lt;/p&gt;

&lt;p&gt;To most users and even web-developers, the DNS, TCP, and SSL delays are entirely transparent and are negotiated at network layers to which few of us descend or think about. And yet, each of these steps is critical to the overall user experience, since each extra network request can add tens or hundreds of milliseconds of latency. &lt;strong&gt;This is the reason why Chrome's network stack is much, much more than a simple socket handler.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now that we've identified the problem, let's dive into the implementation details...&lt;/em&gt;&lt;/p&gt;

&lt;h2 id="network-stack"&gt;Chrome's network stack from 10,000 feet &lt;a href="#network-stack"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;




&lt;h3 id="multi-process" style="margin-top:1em;"&gt;Multi-process architecture &lt;a href="#multi-process"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;Chrome's multi-process architecture carries important implications for how each network request is handled within the browser. Under the hood, Chrome actually supports &lt;a href="http://www.chromium.org/developers/design-documents/process-models"&gt;four different execution models&lt;/a&gt; that determine how the process allocation is performed.&lt;/p&gt;

&lt;p&gt;By default, desktop Chrome browsers use the process-per-site model, that isolates different sites from each other, but groups all instances of the same site into the same process. However, to keep things simple, let's assume one of the simplest cases: one distinct process for each open tab. From the network performance perspective, the differences here are not substantial, but the process-per-tab model is much easier to understand.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/kernel-renderer.png' class='center' style='max-width:445px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;The architecture dedicates one &lt;em&gt;render process&lt;/em&gt; to each tab, which itself contains an instance of the WebKit open-source layout engine for interpreting and layout out the HTML (aka, "HTML Renderer" in the diagram), an instance of the V8 JavaScript engine, and the glue code to bridge these and a few other components. If you are curious, the Chromium wiki contains a &lt;a href="http://www.chromium.org/developers/design-documents/multi-process-architecture"&gt;great introduction to the plumbing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each of these "render" processes is executed within a sandboxed environment that has limited access to the user's computer - including the network. To gain access to these resources, each render process communicates with the browser (kernel) process, which is able to impose security and access policies on each renderer.&lt;/p&gt;

&lt;h3 id="ipc"&gt;Inter-process communication (IPC) and Multi-process resource loading &lt;a href="#ipc"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;All communication between the renderer and the kernel process in Chrome is done via IPC. On Linux and OSX, a &lt;code&gt;socketpair()&lt;/code&gt; is used, which provides a named pipe transport for asynchronous communication. Each message from the renderer is serialized and passed to a dedicated I/O thread, which dispatches it to the kernel process. On the receiving end, the kernel process provides a filter interface, which allows Chrome to intercept resource IPC requests (see &lt;a href="http://code.google.com/p/chromium/source/search?q=resourcemessagefilter&amp;amp;origq=resourcemessagefilter&amp;amp;btnG=Search+Trunk"&gt;ResourceMessageFilter&lt;/a&gt;) which should be handled by the network stack.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/chrome-ipc.png' class='center' style='max-width:445px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;One of the advantages of this architecture is that &lt;strong&gt;all resource requests are handled entirely on the I/O threads and neither any UI generated activity, or network events interfere with each other&lt;/strong&gt;. The resource filter runs in the I/O thread in the browser process, intercepts the resource request messages, and forwards them to a &lt;a href="http://code.google.com/searchframe#OAMlx_jo-ck/src/content/public/browser/resource_dispatcher_host.h&amp;amp;exact_package=chromium&amp;amp;q=ResourceDispatcherHost"&gt;ResourceDispatcherHost&lt;/a&gt; singleton in the browser process.&lt;/p&gt;

&lt;p&gt;The singleton interface allows the browser to control each renderer's access to the network, but it also enables efficient, and consistent resource sharing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Socket pool and connection limits:&lt;/strong&gt; the browser is able to enforce limits on the number of open sockets per profile (256), proxy (32), and &lt;code&gt;{scheme, host, port}&lt;/code&gt; (6) groups. Note that this allows up to six HTTP and six HTTPS connections to the same &lt;code&gt;{host, port}&lt;/code&gt;!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Socket reuse:&lt;/strong&gt; persistent TCP connections are retained in the socket pool for some time after servicing the request to enable connection reuse, which avoids the extra DNS, TCP, and SSL (if required) setup overhead imposed on each new connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Socket late-binding:&lt;/strong&gt; requests are associated with an underlying TCP connection only once the socket is ready to dispatch the application request, allowing better request prioritization (e.g., arrival of a higher priority request while the socket was connecting), better throughput (e.g., re-use of a "warm" TCP connection in cases where an existing socket becomes available while a new connection is being opened), as well as a general-purpose mechanism for TCP pre-connect, and a number of other optimizations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consistent session state:&lt;/strong&gt; authentication, cookies, and cached data is shared between all render processes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global resource and network optimizations:&lt;/strong&gt; the browser is able to make decisions across all render processes and outstanding requests. For example, giving network priority to the requests initiated by the foreground tab.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predictive optimizations:&lt;/strong&gt; by observing all network traffic, Chrome is able to build and refine predictive models to improve performance.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;... and the list goes on.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;As far as the render process is concerned, it is simply sending a resource request message over IPC, which is tagged with a unique request ID to the browser process, and the browser kernel process handles the rest.&lt;/p&gt;

&lt;h3 id="cross-platform"&gt;Cross-platform resource fetching &lt;a href="#cross-platform"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-platforms.png' class='left' /&gt;One of the chief concerns in the implementation of Chrome's network stack is portability across many different platforms: Linux, Windows, OS X, Chrome OS, Android, and iOS. To address this challenge, &lt;strong&gt;the network stack is implemented as a mostly single-threaded (there are separate cache and proxy threads), cross-platform library&lt;/strong&gt;, which allows Chrome to reuse the same infrastructure and provide the same performance optimizations, as well as a greater opportunity for optimization across all platforms.&lt;/p&gt;

&lt;p&gt;All of the network code is, of course, open source and can be found in &lt;a href="https://code.google.com/p/chromium/codesearch#chromium/src/net/&amp;ct=rc&amp;cd=1&amp;q=src.net&amp;sq=package:chromium"&gt;the "src/net" subdirectory&lt;/a&gt;. We won't examine each component in detail, but the layout of the code itself tells you a lot about its capabilities and structure. A few examples:&lt;/p&gt;

&lt;table border="1" class="source" width="100%"&gt;
  &lt;tr&gt;
    &lt;td&gt;net/android&lt;/td&gt;
    &lt;td&gt;Bindings to the Android runtime&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/base&lt;/td&gt;
    &lt;td&gt;Common net utilities, such as host resolution, cookies, network change detection, and SSL certificate management&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/cookies&lt;/td&gt;
    &lt;td&gt;Implementation of storage, management, and retrieval of HTTP cookies&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/disk_cache&lt;/td&gt;
    &lt;td&gt;Disk and memory cache implementation for web resources&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/dns&lt;/td&gt;
    &lt;td&gt;Implementation of an asynchronous DNS resolver&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/http&lt;/td&gt;
    &lt;td&gt;HTTP protocol implementation&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/proxy&lt;/td&gt;
    &lt;td&gt;Proxy (SOCKS and HTTP) configuration, resolution, script fetching, ...&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/socket&lt;/td&gt;
    &lt;td&gt;Cross-platform implementations of TCP sockets, SSL streams, and socket pools&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/spdy&lt;/td&gt;
    &lt;td&gt;SPDY protocol implementation&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/url_request&lt;/td&gt;
    &lt;td&gt;URLRequest, URLRequestContext, and URLRequestJob implementations&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;net/websockets&lt;/td&gt;
    &lt;td&gt;WebSockets protocol implementation&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;Each of the above makes for a great read for the curious - the code is well documented, and you'll find plenty of unit tests for every component.&lt;/p&gt;

&lt;h3 id="mobile"&gt;Architecture and performance on mobile platforms &lt;a href="#mobile"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-android.png' class='left' /&gt;Mobile browser usage is growing at an exponential rate and even by modest projections, it will eclipse desktop browsing in the not so distant future. Needless to say, delivering an optimized mobile experience has been a top priority for the Chrome team. In early 2012, &lt;a href="http://www.google.com/intl/en/chrome/browser/mobile/android.html"&gt;Chrome for Android&lt;/a&gt; was announced, and a few months later, &lt;a href="http://www.google.com/intl/en/chrome/browser/mobile/ios.html"&gt;Chrome for iOS&lt;/a&gt; followed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first thing to note about the mobile version of Chrome, is that it's not simply a direct adaptation of the desktop browser&lt;/strong&gt; - that would not deliver the best user experience. By its very nature, the mobile environment is both much more resource constrained, and has many fundamentally different operating parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Desktop users navigate with the mouse, may have overlapping windows, have a large screen, are mostly not power constrained, usually have a much more stable network connection, and have access to much larger pools of storage and memory.&lt;/li&gt;
&lt;li&gt;Mobile users use touch and gesture navigation, have a much smaller screen, are battery and power constrained, are often on metered connections, and have limited local storage and memory.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Further, there is no such thing as a &lt;em&gt;"typical mobile device"&lt;/em&gt;. Instead there is a wide range of devices with varying hardware capabilities, and to deliver the best performance, Chrome must to adapt to the operating constraints of each and every device. Thankfully, the various execution models allow Chrome to do exactly that!&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/android-chrome.png' class='left' /&gt;&lt;strong&gt;On Android devices, Chrome leverages the same multi-process architecture as the desktop version&lt;/strong&gt; - there is a browser process, and one or more renderer processes. The one difference is that due to memory constraints of the mobile device, Chrome may not be able to run a dedicated renderer for each open tab. Instead, Chrome determines the optimal number of renderer processes based on available memory, and other constraints of the device, and shares the renderer process between the multiple tabs.&lt;/p&gt;

&lt;p&gt;In cases where only minimal resources are available, or if Chrome is unable to run multiple processes, it can also switch to use a single-process, multi-threaded processing model. In fact, &lt;strong&gt;on iOS devices, due to sandboxing restrictions of the underlying platform, it does exactly that - it runs a single, but multi-threaded process.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What about network performance? First off, &lt;strong&gt;Chrome uses the same network stack on Android and iOS, as it does on all other versions&lt;/strong&gt;. This enables all of the same network optimizations across all platforms, which gives Chrome a significant performance advantage. However, what is different, and is often adjusted based on the capabilities of the device and the network in use, are variables such as priority of speculative optimization techniques, socket timeouts and management logic, cache sizes, and more.&lt;/p&gt;

&lt;p&gt;For example, to preserve battery, mobile Chrome may opt-in to use lazy closing of idle sockets - sockets are closed only when opening new ones to minimize radio use. Similarly, since prerendering (which we will discuss below), may require significant network and processing resources, it is often only enabled when the user is on Wi-Fi.&lt;/p&gt;

&lt;p&gt;Optimizing the mobile browsing experience is one of the highest priority items for the Chrome development team, and we can expect to see &lt;em&gt;a lot of new improvements&lt;/em&gt; in the months and years to come. In fact, it is a topic that deserves its own separate chapter - perhaps in the next installment of the POSA series!&lt;/p&gt;

&lt;h3 id="predictor"&gt;Speculative optimization with Chrome's Predictor &lt;a href="#predictor"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-wings.png' class='left' /&gt;&lt;strong&gt;Chrome gets faster as you use it.&lt;/strong&gt; This feat is accomplished with the help of a singleton &lt;code&gt;Predictor&lt;/code&gt; object, which is instantiated within the browser kernel process, and whose sole responsibility is to observe network patterns and to learn and anticipate likely user actions in the future. A few example signals processed by the &lt;code&gt;Predictor&lt;/code&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users hovering their mouse over a link is a good indicator of a likely, upcoming navigation event, which Chrome can help accelerate by dispatching a speculative DNS lookup of the target hostname, as well as potentially starting the TCP handshake. By the time the user clicks, which takes ~200 ms on average, there is a good chance that we have already completed the DNS and TCP steps, allowing us to eliminate hundreds of milliseconds of extra latency for the navigation event.&lt;/li&gt;
&lt;li&gt;Typing in the Omnibox (URL) bar triggers high-likelihood suggestions, which may similarly kick off a DNS lookup, TCP pre-connect, and can even pre-render the page in a hidden tab!&lt;/li&gt;
&lt;li&gt;Each one of us has a list of favorite sites that we visit every day. Chrome can learn the subresources on these sites and speculatively pre-resolve and perhaps even pre-fetch them to accelerate the browsing experience.
And the list goes on...&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Chrome learns the topology of the web, as well as your own browsing patterns, as you use it. If it does the job well, it can eliminate hundreds of milliseconds of latency from each navigation and get the user closer to the holy grail of the "instant page load". To achieve this goal, Chrome leverages four core optimization techniques:&lt;/p&gt;

&lt;table border="1" class="source" width="100%"&gt;
  &lt;tr&gt;
    &lt;td width="25%"&gt;DNS pre-resolve&lt;/td&gt;
    &lt;td&gt;resolve hostnames ahead of time, to avoid DNS latency&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;TCP pre-connect&lt;/td&gt;
    &lt;td&gt;connect to destination server ahead of time, to avoid TCP handshake latency&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Resource prefetching&lt;/td&gt;
    &lt;td&gt;fetch critical resources on the page ahead of time, to accelerate rendering of the page&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Page prerendering&lt;/td&gt;
    &lt;td&gt;fetch the entire page with all of its resources ahead of time, to enable instant navigation when triggered by the user&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;Each decision to invoke one or several of these techniques is optimized against a large number of constraints. After all, each is a speculative optimization, which means that if done poorly, it might trigger unnecessary work and network traffic, or even worse, have a negative effect on the loading time for an actual navigation triggered by the user.&lt;/p&gt;

&lt;p&gt;How does Chrome address this problem? &lt;strong&gt;The predictor consumes as many signals as it can, which include user generated actions, historical browsing data, as well as signals from the renderer and the network stack itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not unlike the &lt;code&gt;ResourceDispatcherHost&lt;/code&gt;, which is responsible for coordinating all of the network activity within Chrome, the &lt;code&gt;Predictor&lt;/code&gt; object creates a number of filters on user and network generated activity within Chrome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IPC channel filter to monitor for signals from the render processes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ConnectInterceptor&lt;/code&gt; object is added to each request, such that it can observe the traffic patterns and record success metrics for each request&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;As a hands on example, the render process can trigger a message to the browser process with any of the following hints, which are conveniently defined in &lt;code&gt;ResolutionMotivation&lt;/code&gt; (&lt;a href="http://code.google.com/searchframe#OAMlx_jo-ck/src/chrome/browser/net/url_info.h&amp;amp;l=35"&gt;url_info.h&lt;/a&gt;):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="c++"&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ResolutionMotivation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;MOUSE_OVER_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Mouse-over initiated by the user.&lt;/span&gt;
  &lt;span class="n"&gt;OMNIBOX_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// Omni-box suggested resolving this.&lt;/span&gt;
  &lt;span class="n"&gt;STARTUP_LIST_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// This resource is on the top 10 startup list.&lt;/span&gt;
  &lt;span class="n"&gt;EARLY_LOAD_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// In some cases we use the prefetcher to warm up&lt;/span&gt;
                            &lt;span class="c1"&gt;// the connection in advance of issuing the real&lt;/span&gt;
                            &lt;span class="c1"&gt;// request.&lt;/span&gt;

  &lt;span class="c1"&gt;// The following involve predictive prefetching, triggered by a navigation.&lt;/span&gt;
  &lt;span class="c1"&gt;// The referring_url_ is also set when these are used.&lt;/span&gt;
  &lt;span class="n"&gt;STATIC_REFERAL_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// External database suggested this resolution.&lt;/span&gt;
  &lt;span class="n"&gt;LEARNED_REFERAL_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Prior navigation taught us this resolution.&lt;/span&gt;
  &lt;span class="n"&gt;SELF_REFERAL_MOTIVATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// Guess about need for a second connection.&lt;/span&gt;

  &lt;span class="c1"&gt;// &amp;lt;snip&amp;gt; ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Given such a signal, the goal of the predictor is to evaluate the likelihood of its success, and then to trigger the activity if resources are available. Every hint may have a likelihood of success, a priority, and an expiration timestamp, the combination of which can be used to maintain an internal priority queue of speculative optimizations. Finally, for every dispatched request from within this queue, the predictor is also able to track its success rate, which allows it to further optimize its future decisions.&lt;/p&gt;

&lt;h3 id="nutshell" style="color:red"&gt;Chrome network architecture in a nutshell &lt;a href="#nutshell"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;ul&gt;
&lt;li&gt;Chrome uses a &lt;strong&gt;multi-process architecture&lt;/strong&gt;, which isolates render processes from the browser process&lt;/li&gt;
&lt;li&gt;Chrome maintains a &lt;strong&gt;single instance of the resource dispatcher&lt;/strong&gt;, which is shared across all render processes, and runs within the browser kernel process&lt;/li&gt;
&lt;li&gt;The network stack is a &lt;strong&gt;cross-platform&lt;/strong&gt;, (mostly) single-threaded library&lt;/li&gt;
&lt;li&gt;The network stack uses &lt;strong&gt;non-blocking&lt;/strong&gt; operations to manage all network operations&lt;/li&gt;
&lt;li&gt;Shared network stack allows efficient resource prioritization, reuse, and provides the browser with ability to perform global optimization across all running processes&lt;/li&gt;
&lt;li&gt;Each render process communicates with the resource dispatcher via IPC&lt;/li&gt;
&lt;li&gt;Resource dispatcher intercepts resource requests via a custom IPC filter&lt;/li&gt;
&lt;li&gt;Predictor intercepts resources request and response traffic to learn and optimize future network requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predictor may speculatively schedule DNS, TCP, and even resource requests&lt;/strong&gt; based on learned traffic patterns, saving hundreds of milliseconds when the navigation is triggered by the user&lt;/li&gt;
&lt;/ul&gt;


&lt;h2 id="lifetime"&gt;Lifetime of your browser session... &lt;a href="#lifetime"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;With the 10,000 foot architecture view of the Chrome network stack in mind, let's now take a closer look at the kinds of user-facing optimizations enabled within the browser. Specifically, let's imagine we have just created a new Chrome profile and are ready to start our day.&lt;/p&gt;

&lt;h3 id="cold-boot"&gt;Optimizing the cold-boot experience &lt;a href="#cold-boot"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-user.png' class='left' /&gt;The first time you load your browser, it of course knows little about your favorite sites or navigation patterns. But, as it turns out, &lt;strong&gt;many of us follow the same routine after a cold-boot of the browser&lt;/strong&gt;, where we may navigate to our email inbox, favorite news site, a social site, an internal portal, and so on. The specific sites will, of course, vary, but the similarity of all these sessions allows the Chrome predictor to accelerate your cold-boot experience!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome remembers the top ten likely hostnames accessed by the user following the browser start&lt;/strong&gt; - note that this is not the top ten global destinations, but specifically the destinations following a fresh browser start. As the browser loads, Chrome can trigger a DNS pre-fetch for the likely destinations! If you are curious, you can inspect your own startup hostname list by opening a new tab and navigating to &lt;code&gt;chrome://dns&lt;/code&gt;. At the top of the page, you will find the list of the top ten likely startup candidates for your profile.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/startup-dns.png' class='center' style='max-width:421px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;Above screenshot is an example from my own Chrome profile. How do I usually begin my browsing? Frequently by navigating to Google Docs if I'm working on an article such at this one. Not surprisingly, we see a lot of Google hostnames in the list!&lt;/p&gt;

&lt;h3 id="omnibox"&gt;Optimizing interactions with the Omnibox &lt;a href="#omnibox"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-omnibox.png' class='left' /&gt;One of the innovations of Chrome was the introduction of the Omnibox, which unlike its predecessors handles much more than just destination URLs. Besides remembering the URLs of pages that the user visited in the past, it also offers full text search over your history &lt;em&gt;(tip: instead of the URL, try typing the name of the page you've recently visited)&lt;/em&gt;, as well as a tight integration with the search engine of your choice.&lt;/p&gt;

&lt;p&gt;As the user types, the Omnibox automatically proposes an action, which is either a URL based on your navigation history, or a search query. Under the hood, each proposed action is scored with respect to the query, as well as its past performance. In fact, Chrome allows us to inspect this data by visiting &lt;code&gt;chrome://predictors&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/omnibox.png' class='center' style='max-width:744px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome maintains a history of the user entered prefixes, the actions it has proposed, as well as the hit rate for each one.&lt;/strong&gt; For my own profile, you can see that whenever I enter "g" in the Omnibox, there is a 76% chance that I'm heading to Gmail. Once I add an "m" (for "gm"), then the confidence rises to 99.8% - in fact, out of the 412 recorded visits, I didn't end up going to Gmail, after entering "gm" only once!&lt;/p&gt;

&lt;p&gt;But, you're thinking, what does this have to do with the network stack? Well, the yellow and green colors for the likely candidates are also important signals for the &lt;code&gt;ResourceDispatcher&lt;/code&gt;! If we have a likely candidate (yellow), Chrome may trigger a DNS pre-fetch for the target host. If we have a high confidence candidate (green), then Chrome may also trigger a TCP pre-connect once the hostname has been resolved. And finally, if both complete while the user is still deliberating, then Chrome may even pre-render the entire page in a hidden tab.&lt;/p&gt;

&lt;p&gt;Alternatively, if there is no good match for the entered prefix based on past navigation history, then Chrome may issue a DNS pre-fetch and TCP pre-connect to your search provider, in anticipation of a likely search request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An average user takes hundreds of milliseconds to fill in their query and to evaluate the proposed autocomplete suggestions.&lt;/strong&gt; In the background, Chrome is able to pre-fetch, pre-connect, and in certain cases even pre-render the page, such that by the time the user is ready to hit the "enter" key, much of the network latency has already been eliminated!&lt;/p&gt;

&lt;h3 id="cache"&gt;Optimizing cache performance &lt;a href="#cache"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;The best, and the fastest request, is a request not made. Whenever we talk about performance, we would be amiss if we didn't talk about the cache -- you are providing &lt;em&gt;Expires&lt;/em&gt;, &lt;em&gt;ETag&lt;/em&gt;, &lt;em&gt;Last-Modified&lt;/em&gt;, and &lt;em&gt;Cache-Control&lt;/em&gt; &lt;a href="https://developers.google.com/speed/docs/best-practices/caching"&gt;response headers&lt;/a&gt; for all the resources on your pages, right? If not, stop, go fix it, we'll wait.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/chrome-incognito.png' class='left' /&gt;Chrome has two different implementations of the internal cache: one backed by local disk, and second which stores everything in memory. &lt;strong&gt;The in-memory implementation is used for the &lt;a href="http://support.google.com/chrome/bin/answer.py?hl=en&amp;amp;answer=95464"&gt;Incognito browsing mode&lt;/a&gt; and is wiped clean whenever you close the window&lt;/strong&gt;. Both implement the same internal interface (&lt;code&gt;disk_cache::Backend&lt;/code&gt;, and &lt;code&gt;disk_cache::Entry&lt;/code&gt;), which greatly simplifies the architecture - and if you are so inclined, allows you to easily experiment with your own, experimental cache implementations.&lt;/p&gt;

&lt;p&gt;Internally, the disk cache implements its own set of data structures, all of which are stored within a single cache folder for your profile. Inside this folder, there are index files, which are memmapped when the browser starts, and data files which store the actual data, alongside the HTTP headers and other bookkeeping information. As an interesting sidenote, resources up to 16KB in size are stored in shared data block-files, and larger files get their own dedicated files on disk. Finally, for eviction, the disk cache maintains an LRU which uses ranking metrics such as frequency of access and age of resource into account.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/internals-cache.png' class='center' style='max-width:760px;width:100%;border:1px solid #ccc;' /&gt;&lt;/p&gt;

&lt;p&gt;If you are ever curious about the state of the Chrome cache, open a new tab and navigate to &lt;code&gt;chrome://net-internals/#httpCache&lt;/code&gt;. Alternatively, if you want to see the actual HTTP metadata and the cached response, you can also visit &lt;code&gt;chrome://cache&lt;/code&gt;, which will enumerate all of the resources currently available in the cache. From that page, search for a resource you're looking for and click on the URL to see the exact, cached headers and response bytes.&lt;/p&gt;

&lt;h3 id="dns-prefetching"&gt;Optimizing DNS with prefetching &lt;a href="#dns-prefetching"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;We have already mentioned DNS pre-resolution on several occasions, so before we dive into the implementation, let's review the cases in which it may be triggered, and why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The WebKit document parser, which runs in the render process, may provide a list of hostnames for all the links on the current page, which Chrome may, or may not choose to pre-resolve ahead of time.&lt;/li&gt;
&lt;li&gt;Renderer process may trigger a mouse hover or "button down" event as an early signal of user's intent to perform a navigation.&lt;/li&gt;
&lt;li&gt;The Omnibox may trigger a resolve request based on a high likelihood suggestion.&lt;/li&gt;
&lt;li&gt;Chrome predictor may request hostname resolution based on past navigation and resource request data - more on this below.&lt;/li&gt;
&lt;li&gt;The owner of the page may explicitly indicate to Chrome which hostnames it should pre-resolve.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;In all cases, DNS pre-resolution is treated as a hint&lt;/strong&gt;. Chrome does not guarantee that the pre-resolution will occur, rather it uses each signal in combination with its own predictor to assess the hint and decide on a course of action. In the "worst case", if we weren't able to pre-resolve the hostname in time, the user would have to wait for an explicit DNS resolution, followed by TCP connection time, and finally the actual resource fetch. However, when this occurs, the predictor can take note and adjust its future decisions accordingly - it gets faster, and smarter, as you use it.&lt;/p&gt;

&lt;p&gt;One of the optimizations we have not covered previously is the &lt;strong&gt;ability of Chrome to learn the topology of each site and then use this information to accelerate future visits&lt;/strong&gt;. Specifically, recall that an average page consists of 88 resources, which are delivered from 30+ distinct hosts. Well, each time you perform a navigation, Chrome may record the hostnames for the popular resources on the page, and during a future visit, it may choose to trigger a DNS pre-resolve and even a TCP pre-connect for some, or all of them!&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/subresource-stats.png' class='center' style='max-width:790px;width:100%;border:1px solid #ccc;' /&gt;&lt;/p&gt;

&lt;p&gt;To inspect the subresource hostnames stored by Chrome, navigate to &lt;code&gt;chrome://dns&lt;/code&gt; and search for any popular destination hostname for your profile. In the example above, you can see the six subresource hostnames that Chrome remembered for Google+, as well as stats for the number of cases when a DNS pre-resolution was triggered, or a TCP pre-connect was performed, as well as an expected number of requests that will be served by each. This internal accounting is what enables the Chrome predictor to perform its optimizations.&lt;/p&gt;

&lt;p&gt;In addition to all of the internal signals, the owner of the site is also able to embed additional markup on their pages to request the browser to pre-resolve a hostname:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dns-prefetch&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;//host_name_to_prefetch.com&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Why not simply rely on the automated machinery in the browser? &lt;strong&gt;In some cases, you may want to pre-resolve a hostname which is not mentioned anywhere on the page&lt;/strong&gt;. The canonical example is, of course, redirects: a link may point to a host, like an analytics tracking service, which then redirects the user to the actual destination. By itself, Chrome cannot infer this pattern, but you can help it by providing a manual hint and get the browser to resolve the hostname of the actual destination ahead of time.&lt;/p&gt;

&lt;p&gt;So, how is this all implemented under the hood? The answer to this question, just like all other optimizations we have covered, depends on the version of Chrome, since the team is &lt;em&gt;always&lt;/em&gt; experimenting with new and better ways to improve performance. However, broadly speaking, the DNS infrastructure within Chrome has two major implementations: historically, Chrome has relied on the platform-independent &lt;code&gt;getaddrinfo()&lt;/code&gt; system call, and delegated the actual responsibility for the lookups to the operating system, however this approach is in the process of being replaced with &lt;strong&gt;Chrome's own implementation of an asynchronous DNS resolver&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The original implementation, which relied on the operating system, has its benefits: less and simpler code, and the ability to leverage the operating system's DNS cache. However, &lt;code&gt;getaddrinfo()&lt;/code&gt; is also a blocking system call, which meant that Chrome had to create and maintain a dedicated worker thread-pool to allow it to perform multiple lookups in parallel. &lt;strong&gt;This unjoined pool was capped at six worker threads&lt;/strong&gt;, which is an empirical number based on lowest common denominator of hardware - turns out, higher numbers of parallel requests can overload some users' routers!&lt;/p&gt;

&lt;p&gt;For pre-resolution with the worker-pool, Chrome simply dispatches the &lt;code&gt;getaddrinfo()&lt;/code&gt; call, which blocks the worker thread until the response is ready, at which point it just discards the returned result and begins processing the next prefetch request. &lt;em&gt;Discards it?&lt;/em&gt; The result is cached by the OS DNS cache, which returns an immediate response to future, actual &lt;code&gt;getaddrinfo()&lt;/code&gt; lookups. Simple, effective, works well enough in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well, effective, but not good enough!&lt;/strong&gt; The &lt;code&gt;getaddrinfo()&lt;/code&gt; call hides a lot of useful information from Chrome, such as the time-to-live (TTL) timestamps for each record, as well as the state of the DNS cache itself. To improve performance, Chrome team decided to implement their own, cross-platform, asynchronous DNS resolver.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/async-dns.png' class='center' style='max-width:590px;width:100%;border:1px solid #ccc;' /&gt;&lt;/p&gt;

&lt;p&gt;By moving DNS resolution into Chrome the new async resolver enables a number of new optimizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better control of retransmission timers, and ability to execute multiple queries in parallel&lt;/li&gt;
&lt;li&gt;visibility into record TTLs, which allows Chrome to refresh popular records ahead of time&lt;/li&gt;
&lt;li&gt;better behavior for dual stack implementations (IPv4 and IPv6)&lt;/li&gt;
&lt;li&gt;failovers to different servers, based on RTT or other signals&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;All of the above, and more, are ideas for continuous experimentation and improvement within Chrome. Which brings us to the obvious question: &lt;em&gt;how do we know and measure the impact of these ideas?&lt;/em&gt; Simple, &lt;strong&gt;Chrome tracks detailed network performance stats and histograms for each individual profile&lt;/strong&gt;. To inspect the collected DNS metrics, open a new tab, and head to &lt;code&gt;chrome://histograms/DNS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/dns-prefetch.png' class='center' style='max-width:790px;width:100%;border:1px solid #ccc;' /&gt;&lt;/p&gt;

&lt;p&gt;The above histogram shows the distribution of latencies for DNS prefetch requests: roughly 50% (rightmost column) of the prefetch queries were finished within 20ms (leftmost column). Note that this is data based on a recent browsing session (9869 samples), and is private to the user. If the user has opted in to report their usage stats in Chrome, then the summary of this data is anonymized and periodically beaconed back to the engineering team, which is then able to see the impact of their experiments and adjust accordingly. &lt;em&gt;Rinse, lather, repeat.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="tcp-pre-connect"&gt;Optimizing TCP connection management with pre-connect &lt;a href="#tcp-pre-connect"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;We have pre-resolved the hostname and we have a high likelihood navigation event that's about to happen, as estimated by the Omnibox, or the Chrome predictor. &lt;strong&gt;Why not go one step further, and also speculatively pre-connect to the destination host and complete the TCP handshake before the user dispatches the request?&lt;/strong&gt; By doing so, we can eliminate another full round-trip of latency delay, which can easily save hundreds of milliseconds for the user. Well, that's exactly what TCP-preconnect is and how it works!&lt;/p&gt;

&lt;p&gt;To see the hosts for which a TCP preconnect has been triggered, open a new tab and visit &lt;code&gt;chrome://dns&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/preconnect.png' class='center' style='max-width:764px;width:100%;' /&gt;&lt;/p&gt;

&lt;p&gt;First, Chrome checks its socket pools to see if there is an available socket for the hostname, which it may be able to reuse - keep-alive sockets are kept in the pool for some period of time, to avoid the TCP handshake and slow-start penalties. If no socket is available, then it can initiate the TCP handshake, and place it in the pool. Then, when the user initiates the navigation, the HTTP request can be dispatched immediately.&lt;/p&gt;

&lt;p&gt;Curious to see the state of all the open sockets in Chrome? Simple, head to: &lt;code&gt;chrome://net-internals#sockets&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/netinternals-sockets.png' class='center' style='max-width:770px;width:100%;border:1px solid #ccc;' /&gt;&lt;/p&gt;

&lt;p&gt;Note that you can also drill into each socket and inspect the timeline: connect and proxy times, arrival times for each packet, and more. Last but not least, you can also export this data for further analysis or a bug report. Having &lt;strong&gt;good instrumentation is key to any performance optimization, and &lt;code&gt;chrome://net-internals&lt;/code&gt; is the nexus of all things networking in Chrome&lt;/strong&gt; - if you haven't explored it yet, you should!&lt;/p&gt;

&lt;h3 id="prefetching"&gt;Optimizing resource loading with prefetch hints &lt;a href="#prefetching"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;Sometimes, the author of a page is able to provide additional navigation, or page context, based on the structure or the layout of their site, and help the browser optimize the experience for the user. Chrome supports two such hints, which can be embedded in the markup of the page:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;subresource&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/javascript/myapp.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;prefetch&amp;quot;&lt;/span&gt;    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/images/big.jpeg&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Subresource and prefetch look very similar, but have very different semantics&lt;/strong&gt;. When a link resource specifies its relationship as "prefetch", it is an indication to the browser that this resource might be needed in a future navigation. In other words, effectively it is a cross-page hint. By contrast, when a resource specifies the relationship as a "subresource", it is an early indication to the browser that the resource will be used on a current page, and that it may want to dispatch the request before it encounters it later in the document.&lt;/p&gt;

&lt;p&gt;As you would expect, the different semantics of the hints lead to very different behavior by the resource loader. Resources marked with prefetch are considered low priority and might be downloaded by the browser only once the current page has finished loading. Whereas subresource resources are fetched with high priority as soon as they are encountered and will compete with the rest of the resources on the current page.&lt;/p&gt;

&lt;p&gt;Both hints, when used well and in the right context, can help significantly with optimizing the user experience on your site. Finally, it is also important to note that prefetch is &lt;a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#link-type-prefetch"&gt;part of the HTML5 spec&lt;/a&gt;, and as of today supported by Firefox and Chrome, whereas subresource is currently &lt;a href="http://www.chromium.org/spdy/link-headers-and-server-hint/link-rel-subresource"&gt;only available in Chrome&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="prefreshing"&gt;Optimizing resource loading with browser prefreshing &lt;a href="#prefreshing"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;Unfortunately, not all site owners are able or willing to provide the browser with subresource hints in their markup. Further, even if they do, we must wait for the HTML document to arrive from the server before we are able to parse the hints and begin fetching the necessary subresources - depending on the server response time, as well as the latency between the client and the server, this could take hundreds and even thousands of milliseconds.&lt;/p&gt;

&lt;p&gt;However, as we saw earlier, Chrome is already learning the hostnames of the popular resources to perform DNS pre-fetching. So, why couldn't it do the same, but go one step further and perform the DNS lookup, use TCP preconnect, and then also speculatively prefetch the resource? Well, that's exactly what "prefreshing" could do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User initiates a request to a target URL&lt;/li&gt;
&lt;li&gt;Chrome queries its Predictor for learned subresources associated with target URL and initiates the sequence of DNS prefetch, TCP preconnect, and resource prefreshing&lt;/li&gt;
&lt;li&gt;If the learned subresource is in the cache, then its loaded from disk and into memory&lt;/li&gt;
&lt;li&gt;If the learned subresource is missing, or has expired, then a network request is made&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-experiment.png' class='left' /&gt;Resource prefreshing is a great example of the workflow of every experimental optimization in Chrome - in theory, it should enable better performance, but there are many tradeoffs as well. There is only way to reliably determine if it will make the cut and make it into Chrome: implement it and run it as A/B experiment in some of the pre-release channels with real users, on real networks, with real browsing patterns.&lt;/p&gt;

&lt;p&gt;As of early 2013, the Chrome team is in the early stages of discussing the implementation. If it makes the cut based on gathered results, we may see prefreshing in Chrome sometime later in the year. &lt;strong&gt;The process of improving Chrome network performance never stops, the team is always experimenting with new approaches, ideas, and techniques.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id="prerendering"&gt;Optimizing navigation with prerendering &lt;a href="#prerendering"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h3&gt;


&lt;p&gt;Each and every optimization we have covered up to now helps reduce the latency between the user's direct request for a navigation and the resulting page rendering in their tab. However, &lt;strong&gt;what would it take to have a truly instant experience?&lt;/strong&gt; Based on the UX data we saw earlier, this interaction would have to happen in less than 100 milliseconds, which doesn't leave much room for network latency at all. What could we do to deliver a rendered page in sub 100 milliseconds?&lt;/p&gt;

&lt;p&gt;Of course, you already know the answer, since this is a common pattern employed by many users: if you open multiple tabs then switching between tabs is instant and is definitely much faster than waiting for the navigation between the same resources in a single foreground tab. Well, what if the browser provided an API to do this?&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;prerender&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://example.org/index.html&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You guessed it, that's &lt;a href="https://developers.google.com/chrome/whitepapers/prerender"&gt;prerendering in Chrome&lt;/a&gt;! Instead of just downloading a single resource, as the "prefetch" hint would have done, the "prerender" attribute indicates to Chrome that it should, well, prerender the page in a hidden tab, along with all of its subresources. &lt;strong&gt;The hidden tab itself is invisible to the user, but when the user triggers the navigation, the tab is swapped in from the background for an "instant experience".&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Curious to try it out? You can visit &lt;a href="http://prerender-test.appspot.com"&gt;prerender-test.appspot.com&lt;/a&gt; for a hands on demo, and see the history and status of the prerendered pages for your profile by visiting: &lt;code&gt;chrome://net-internals/#prerender&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src='/posts/13/posa/netinternals-prerender.png' class='center' style='max-width:770px;width:100%;border:1px solid #ccc;' /&gt;&lt;/p&gt;

&lt;p&gt;As you would expect, rendering an entire page in a hidden tab can consume a lot of resources, both CPU and network, and hence should only be used in cases where we have high confidence that the hidden tab will be used! For example, when you are using the Omnibox, a prerender may be triggered for the a high confidence suggestion. Similarly, Google Search sometimes adds the prerender hint to its markup if it estimates that the first search result is a highly confidence destination (aka, Google Instant Pages):&lt;/p&gt;

&lt;div class='ytvideo' id='_Jn93FDx9oI'&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note that you can also add prerender hints to your own site!&lt;/strong&gt; However, before you do, note that prerendering has a number of restrictions and limitations, which you should keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At most one prerender tab is allowed across all processes&lt;/li&gt;
&lt;li&gt;HTTPS and pages with HTTP authentication are not allowed&lt;/li&gt;
&lt;li&gt;Prerendering is abandoned if the requested resource, or any of its subresources need to make a non-idempotent request (only GET requests allowed)&lt;/li&gt;
&lt;li&gt;All resources are fetched with lowest network priority&lt;/li&gt;
&lt;li&gt;The page is rendered with lowest CPU priority&lt;/li&gt;
&lt;li&gt;The page is abandoned if memory requirements exceed 100MB&lt;/li&gt;
&lt;li&gt;Plugin initialization is deferred, and pre-rendering is abandoned if an HTML5 media element is present&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In other words, prerendering is not guaranteed to happen and only applies to pages where it is safe. Additionally, since JavaScript and other logic may be executed within the hidden page, it is best practice to leverage the &lt;a href="https://developers.google.com/chrome/whitepapers/pagevisibility"&gt;Page Visibility API&lt;/a&gt; to detect if the page is visible - which is something you &lt;a href="http://www.html5rocks.com/en/tutorials/pagevisibility/intro/"&gt;should be doing anyway&lt;/a&gt;!&lt;/p&gt;

&lt;h2 id="faster"&gt;Chrome gets faster as you use it &lt;a href="#faster"&gt;#&lt;/a&gt; &lt;a class="toc-arrow" href="#toc"&gt;&amp;uarr;&lt;/a&gt;&lt;/h2&gt;


&lt;p&gt;&lt;img src='/posts/13/posa/chrome-speed.png' class='left' /&gt;Needless to say, Chrome's network stack is much more than a simple socket manager. Our whirlwind tour covered the many levels of potential optimizations that are performed transparently in the background, as you navigate the web. The more Chrome learns about the topology of the web and your browsing patterns, the better it can do its job. &lt;strong&gt;Almost like magic, Chrome gets faster as you use it. Except, it's not magic, because now you know how it works!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, it is important to note that the Chrome team continues to iterate and experiment with new ideas to improve performance - this process never stops. By the time you read this, chances are there will be new experiments and optimizations being developed, tested, or deployed. Perhaps once we reach our target destination of instant page loads (&amp;#060;100 ms), for each and every page, then we can take a break. Until then, there is always more work to do!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=1bH6oXMqciU:Mt5rTWczfos:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/1bH6oXMqciU" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/posa/high-performance-networking-in-google-chrome</feedburner:origLink></entry><entry><title type="html">Faster Websites: Crash Course on Web Performance</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/nE1CsV7y8Qc/" /><updated>2013-01-15T00:00:00-08:00</updated><id>http://www.igvita.com/2013/01/15/faster-websites-crash-course-on-web-performance/</id><content type="html">&lt;p&gt;&lt;img src='/posts/12/perf-waterfall.png' class='left' /&gt;Delivering a fast and optimized user experience in the browser requires careful thinking across many layers of the stack - TCP and up. In a rather ambitious undertaking, when I got the chance to run a three hour (marathon) workshop at Devoxx 2012, I tried to do exactly that: a crash course on web performance. Even with that much time, much was left unsaid, but I'm happy with how it went - it turned out to be one of the most popular workshops.&lt;/p&gt;

&lt;p&gt;The best part is, the video is now available online for free! The Devoxx team did an amazing job of post-processing the recording, with inline slides, full agenda navigation, and more. Check it out below. Hope you like it, and let me know if you have any feedback, comments or questions.&lt;/p&gt;

&lt;div class="download"&gt;&lt;a href="http://bit.ly/webperf-crash-course"&gt;Web Performance Crash Course&lt;/a&gt; - PDF slides (9.5MB)&lt;/div&gt;


&lt;h2&gt;Life of a web request: TCP, HTTP, SPDY, Mobile and Navigation Timing&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://parleys.com/#st=5&amp;amp;id=3648" target="_blank"&gt;&lt;img src='/posts/13/devoxx-netstack.png' class='center' style='max-width:770px;width:100%;border:1px solid #999' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;a href="http://parleys.com/#st=5&amp;amp;id=3648" target="_blank"&gt;Part one&lt;/a&gt;&lt;/b&gt; covers much of the underlying networking infrastructure, which you may not think about day-to-day, but which often dictates the delivery and consequent performance of your application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the impact of slow sites?&lt;/li&gt;
&lt;li&gt;What is "fast" and how are we doing today?&lt;/li&gt;
&lt;li&gt;Life of an HTTP request, TCP-up&lt;/li&gt;
&lt;li&gt;Faster networks will save us, right? (Mostly) Wrong!&lt;/li&gt;
&lt;li&gt;Bandwidth doesn't matter (much)&lt;/li&gt;
&lt;li&gt;HTTP 1.0/1.1 and TCP performance&lt;/li&gt;
&lt;li&gt;SPDY in 10 slides, or less&lt;/li&gt;
&lt;li&gt;Performance of Mobile networks&lt;/li&gt;
&lt;li&gt;Under the hood of Chrome's network stack&lt;/li&gt;
&lt;li&gt;Navigation Timing &amp;amp; Real user measurement (RUM)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Life of a webpage: DOM, CSSOM, rendering, acceleration&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://parleys.com/#st=5&amp;amp;id=3649" target="_blank"&gt;&lt;img src='/posts/13/devoxx-browser.png' class='center' style='max-width:770px;width:100%;border:1px solid #999' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;a href="http://parleys.com/#st=5&amp;amp;id=3649" target="_blank"&gt;Part two&lt;/a&gt;&lt;/b&gt; picks up from &lt;a href="http://www.igvita.com/slides/2012/webperf-crash-course.pdf#page=79" target="_blank"&gt;slide 79&lt;/a&gt; and is focused on the architecture and the execution model of the browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokenizing, parsing, and construction of the DOM&lt;/li&gt;
&lt;li&gt;Building the Render Tree (DOM and CSSOM)&lt;/li&gt;
&lt;li&gt;Measuring visual &amp;amp; rendering performance&lt;/li&gt;
&lt;li&gt;Hardware acceleration 101&lt;/li&gt;
&lt;li&gt;Putting it all into practice on a synthetic example&lt;/li&gt;
&lt;li&gt;Critical Path analysis for guardian.co.uk&lt;/li&gt;
&lt;li&gt;Performance rules, tips and tricks to apply on your site&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=nE1CsV7y8Qc:vs0L2G0lcCM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/nE1CsV7y8Qc" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2013/01/15/faster-websites-crash-course-on-web-performance/</feedburner:origLink></entry><entry><title type="html">Deploying New Image Formats on the Web</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/pgpxmCzLQk0/" /><updated>2012-12-18T00:00:00-08:00</updated><id>http://www.igvita.com/2012/12/18/deploying-new-image-formats-on-the-web/</id><content type="html">&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/image-formats.png' class='left' /&gt;An average page is now over 1200kB in size and &lt;a href="http://httparchive.org/interesting.php#bytesperpage"&gt;60% of that&lt;/a&gt; is in images. With all the focus on performance and speed across the web performance industry, you would think that innovating on better image formats would be a top agenda item. Not so. Instead, we are living in a self-imposed world of &lt;a href="http://en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support"&gt;scarcity of formats&lt;/a&gt;, effectively limiting ourselves to gif’s, png’s and jpeg’s.&lt;/p&gt;

&lt;p&gt;In practice, deploying new image formats has been painful - just think back to the saga of png. But one would also hope that png was not the last. In fact, &lt;strong&gt;if we really want to make an impact on web performance, then image formats is the place to do it. There is absolutely no reason why we shouldn't have dozens of specialized formats, each tailored for a specific case and type of image.&lt;/strong&gt; But before we get there, we need to iron out some kinks...&lt;/p&gt;

&lt;h2&gt;Deploying new "Magic Image Format"&lt;/h2&gt;

&lt;p&gt;As a practical example, let's imagine we have just invented a new &lt;em&gt;magic image format (mif)&lt;/em&gt;. How do we deploy it? As a &lt;a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=20214"&gt;recent bug on W3C points out&lt;/a&gt;, our markup provides no facility to specify different formats for a single image. Let's assume we add such a mechanism. The syntax does not matter, I'll just make it up for the sake of an example:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;awesome.jpeg 1x, awesome.mif 2x&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Use awesome MIF for retina screens!&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;So far, so good. The browser reads the page and decides to load the &lt;code&gt;.mif&lt;/code&gt; file while rendering the page. The user loves our awesome image and decides to share it on their favorite social network: right click, copy URL, or simply drags the image into a bookmarklet or extension. At this point, we have a problem. Our user's friends may use a different browser, which may not support &lt;code&gt;.mif&lt;/code&gt; files. Instead of an awesome image, they see a broken asset.&lt;/p&gt;

&lt;p&gt;Why did this occur? When presented with all the available image options, the browser was able to negotiate the format for the user, but then the user made the singular choice for all of his friends, and in the process broke it for them. &lt;strong&gt;Client-driven negotiation breaks down the moment the resource leaves the page where all of the representations are available.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, in theory, the browsers could address this by ensuring that every time you grab the asset URL, either via drag and drop, right click, or even JavaScript interaction, then a "safe" URL is returned. However, in the long run, I don't think this is the right solution.&lt;/p&gt;

&lt;h2&gt;Humans don't scale&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/ig/8084362969/in/photostream"&gt;&lt;img src='//www.igvita.com/posts/12/london-photo.png' class='left' /&gt;&lt;/a&gt;Quick, what is the format for the image on the left? Trick question. The file is saved as a &lt;code&gt;.png&lt;/code&gt; on my &lt;a href="http://origin.igvita.com/posts/12/london-photo.png"&gt;origin&lt;/a&gt; server, but depending on your browser, it's either coming up as a &lt;code&gt;.jpeg&lt;/code&gt;, or a &lt;code&gt;.webp&lt;/code&gt; as you're viewing this page. &lt;a href="https://developers.google.com/speed/pagespeed/service"&gt;PageSpeed Service&lt;/a&gt; proxy tested the various formats, and decided to re-encode the image to achieve better compression (&lt;a href="http://www.igvita.com/posts/12/pss-img.png"&gt;see settings&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In fact, we already know, based on &lt;a href="http://blog.yoav.ws/2012/07/Images-Can-we-have-less"&gt;empirical results&lt;/a&gt;, that we as humans are terrible at optimizing images: we forget to resize them, we pick the wrong formats, and its tedious work. You would think that with three image formats to choose from, such problems would not exist. Not so. &lt;strong&gt;I've long stopped worrying about hand-optimizing images. Computers are much better at this task than we are, and they also don't mind the boring work.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;The format doesn't actually matter&lt;/h2&gt;

&lt;p&gt;For the sake of an argument, let's say we do hand-optimize each image asset. Next, enumerate each variant:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;path/awesome.jpeg, path/awesome.png, path/awesome.webp,&lt;/span&gt;
&lt;span class="s"&gt;                      path/awesome.svg, path/awesome.mif, path/awesome.mif2&amp;quot;&lt;/span&gt;
          &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;How many times do I need to repeat myself?&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That is silly, boring, and a waste of bytes in the markup. We'll need automation tools just to simplify the task of generating the repeated boilerplate - not unlike the existing CSS prefix woes. So, with that, the extension of the image file doesn't actually matter - we can do without it.&lt;/p&gt;

&lt;p&gt;Let's imagine that we instead invent an abstract &lt;code&gt;.img&lt;/code&gt; format, which acts as a stand-in for the optimal format. &lt;strong&gt;What is optimal? It is a function of the image contents and the user agent preference and capabilities at the time of the request.&lt;/strong&gt; Who performs the optimization? The server, of course. Given a single source image, it is able to re-encode, recompress, resize, strip unnecessary metadata, ..., and deliver the optimal format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In a world with dozens of image formats, the human solution does not scale - read, markup does not scale.&lt;/strong&gt; Whereas computers are fantastic at doing exactly the kind of optimization work required to solve the problem.&lt;/p&gt;

&lt;h2&gt;Content-type negotiation&lt;/h2&gt;

&lt;p&gt;Good news, HTTP 1.1 already anticipated all of the mechanics to make this work with &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html"&gt;server-driven negotiation&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User agent indicates which file types it supports or is willing to accept through &lt;code&gt;Accept&lt;/code&gt; request header&lt;/li&gt;
&lt;li&gt;Server selects the format and indicates the returned type through &lt;code&gt;Content-Type&lt;/code&gt; response header&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Note that the extension on the image file in the URL does not matter. &lt;strong&gt;The same URL can return a different representation based on the negotiated client-server preferences, and the browser then uses the Content-Type to properly interpret the response.&lt;/strong&gt; No need for extra markup, no need to hand-tune each image. Further, users don't care which format is negotiated, as long as the image works, and as long as it is delivered quickly.&lt;/p&gt;

&lt;h2&gt;Outlines of the solution&lt;/h2&gt;

&lt;p&gt;If content negotiation is already here, then why are we even having this discussion? On paper we have all we need, in practice, there are a few implementation issues to resolve. First, let's look at the &lt;code&gt;Accept&lt;/code&gt; headers sent by modern browsers when making a request for an image resource:&lt;/p&gt;

&lt;table border="1" class="browsers"&gt;
  &lt;tr&gt;
    &lt;td&gt;Chrome&lt;/td&gt;
    &lt;td class="header"&gt;*/*&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Safari&lt;/td&gt;
    &lt;td class="header"&gt;*/*&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Firefox&lt;/td&gt;
    &lt;td class="header"&gt;image/png,image/*;q=0.8,*/*;q=0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style="width:9em"&gt;Internet Explorer&lt;/td&gt;
    &lt;td class="header"&gt;image/png,image/svg+xml,image/*;q=0.8, */*;q=0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Opera&lt;/td&gt;
    &lt;td class="header"&gt;text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;Chrome and Safari headers are effectively useless - we accept everything! Firefox and IE aren't doing much better. Opera is the only one explicitly enumerating the supported filetypes, which is the behavior we want, albeit it also adds some unnecessary types at the front. &lt;strong&gt;If we want server-driven negotiation to work, then the first task is to get the browsers to send a useful Accept header - a header which actually enumerates the supported types.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, fixing the &lt;code&gt;Accept&lt;/code&gt; header is only half the problem. The fact that the same URL may have multiple representations means that all the intermediate caches must have a way to differentiate the various responses. Thankfully, HTTP 1.1 has a &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44"&gt;mechanism for that&lt;/a&gt; as well, the &lt;code&gt;Vary&lt;/code&gt; header.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;  &lt;span class="o"&gt;(&lt;/span&gt;client&lt;span class="o"&gt;)&lt;/span&gt;  &amp;gt;  Accept: image/jpeg, image/png, image/mif
  &lt;span class="o"&gt;(&lt;/span&gt;server&lt;span class="o"&gt;)&lt;/span&gt; &amp;gt;  Content-Type: image/mif
              &amp;gt;  Vary: Accept
              &amp;gt;  &lt;span class="o"&gt;(&lt;/span&gt;object&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The server indicates to upstream clients that the resource should be varied based on the value of client's &lt;code&gt;Accept&lt;/code&gt; header by returning &lt;code&gt;Vary: Accept&lt;/code&gt;. In the example above, given the choice of three formats, the server chose mif as the optimal one. Any upstream cache can safely cache and serve the mif object to any user agent which provides the the same &lt;code&gt;Accept&lt;/code&gt; header. If another user agent sends a different header value, for example without &lt;code&gt;image/mif&lt;/code&gt;, then a different format will be served and cached. To the user, this negotiation is transparent.&lt;/p&gt;

&lt;h2&gt;But, but, but...&lt;/h2&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/picket.png' class='left' /&gt;&lt;span class="red"&gt;&lt;strong&gt;But this puts more header bytes on the wire!&lt;/strong&gt;&lt;/span&gt; Yes, it does. If we explicitly enumerate all image types, then the header is 50-100 bytes in the upstream. Half of the requests (~40) on an average page are image requests, which means ~2~4kB in total. In the downlink, these images cost us ~600kB. Assuming we can get 10-30% better compression - a realistic number for WebP, as we'll see below - this translates to &lt;strong&gt;60-180kB in savings, and a 30-45x return on investment&lt;/strong&gt;. And &lt;del&gt;I hope&lt;/del&gt; I know we can do even better in the future.&lt;/p&gt;

&lt;p&gt;Further, for HTTP 2.0, we will have header compression, which will amortize the cost of sending the image header down to a single transfer of 50-100 bytes, instead of the ~2~4kB in current overhead. As for HTTP 1.1, we can be  smart and provide a site controlled opt-in mechanism, such that only sites which support the new negotiation will get the updated header from the browser - the mechanics of this deserves its own separate discussion.&lt;/p&gt;

&lt;p&gt;&lt;span class="red"&gt;&lt;strong&gt;But this would fragment the cache!&lt;/strong&gt;&lt;/span&gt; Small changes in the Accept header could potentially create duplicate entries in the cache. This is &lt;a href="https://www.varnish-cache.org/docs/3.0/tutorial/vary.html"&gt;nothing new&lt;/a&gt;, and the spec documents ways to normalize the headers: downcase, order does not matter, etc. Also, "&lt;a href="http://tools.ietf.org/html/draft-fielding-http-key-01"&gt;Key&lt;/a&gt;" proposal is specifically designed to resolve this issue in a generic, cache-friendly way.&lt;/p&gt;

&lt;p&gt;&lt;span class="red"&gt;&lt;strong&gt;But cache support for Vary is missing!&lt;/strong&gt;&lt;/span&gt; Turns out, most CDN's will simply not cache anything with &lt;code&gt;Vary: Accept&lt;/code&gt;. That's a sad state of affairs and should be considered a bug. The good news is, &lt;strong&gt;this is not rocket science, rather it is a question of business incentives&lt;/strong&gt;. Better image optimization translates to fewer bytes on the wire and better performance - this is aligned with what every CDN is trying to sell you. If the client support is there, then support for &lt;code&gt;Vary: Accept&lt;/code&gt; is a competitive edge for every cache and CDN provider.&lt;/p&gt;

&lt;p&gt;&lt;span class="red"&gt;&lt;strong&gt;But old clients will break with Vary: Accept!&lt;/strong&gt;&lt;/span&gt; Support for Vary has been spotty in older clients: &lt;a href="http://blogs.msdn.com/b/ieinternals/archive/2009/06/17/vary-header-prevents-caching-in-ie.aspx"&gt;IE6 won't cache&lt;/a&gt; any asset with Vary, IE7 will cache but makes a &lt;a href="http://blogs.msdn.com/b/ieinternals/archive/2009/06/17/vary-header-prevents-caching-in-ie.aspx"&gt;conditional request&lt;/a&gt;, and so on. One practical approach is to make this a forward looking optimization where only newer clients would trigger the new behavior.&lt;/p&gt;

&lt;p&gt;&lt;span class="red"&gt;&lt;strong&gt;But now I need new server software to optimize the images!&lt;/strong&gt;&lt;/span&gt; Yes, you will. This is once again a question of incentives for hosting providers and CDN's. In fact, many CDN's already perform image optimization at the edge. Similarly, open-source projects like &lt;a href="https://developers.google.com/speed/docs/mod_pagespeed/filter-image-optimize"&gt;mod_pagespeed&lt;/a&gt; and &lt;a href="https://github.com/pagespeed/ngx_pagespeed"&gt;ngx_pagespeed&lt;/a&gt; are drop-in modules, which will do all the work required to make this work.&lt;/p&gt;

&lt;p&gt;&lt;span class="red"&gt;&lt;strong&gt;But this means more load on the server!&lt;/strong&gt;&lt;/span&gt; Dynamic image optimization doesn't come for free, but &lt;strong&gt;your time is more valuable&lt;/strong&gt;. The server can optimize the asset, cache it, and be done with it. There is no global shortage of CPU cycles, and once there is an incentive, these image optimization workflows will be tuned into oblivion.&lt;/p&gt;

&lt;h2&gt;Hands on example with WebP&lt;/h2&gt;

&lt;blockquote&gt;WebP is a new image format that provides lossless and lossy compression for images on the web. WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller in size compared to JPEG images at equivalent SSIM index. WebP supports lossless transparency (also known as alpha channel) with just 22% additional bytes. Transparency is also supported with lossy compression and typically provides 3x smaller file sizes compared to PNG when lossy compression is acceptable for the red/green/blue color channels.&lt;/blockquote&gt;


&lt;p&gt;&lt;strong&gt;25-35% savings over PNG and JPEG, and up to 60%+ for PNG’s with an alpha (transparency) channel.&lt;/strong&gt; That's hundreds of kilobytes of savings on most every page. Something &lt;a href="https://developers.google.com/speed/webp/"&gt;worth fighting for&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/webp-logo.png' class='left' /&gt;Both Chrome and Opera support WebP, as do some optimization proxies such as mod_pagespeed, PageSpeed Service, Torbit, and few others. However, because of the lacking context in existing &lt;code&gt;Accept&lt;/code&gt; headers, each is forced to mark &lt;code&gt;.webp&lt;/code&gt; resources with a &lt;code&gt;Cache-Control: private&lt;/code&gt; header. This effectively forces every request to be routed to the optimizing proxy, which then performs user agent detection and serves the appropriate content type.&lt;/p&gt;

&lt;p&gt;As many have pointed out, this method does not scale: every request is routed to the origin server, and marking the resource as private bypasses all the intermediate caches. This alone is enough of a reason for why we haven’t seen WebP, and other experimental formats, get any significant adoption on the modern web.&lt;/p&gt;

&lt;h2&gt;The "action plan"&lt;/h2&gt;

&lt;p&gt;Step one, we need to fix and normalize the &lt;code&gt;Accept&lt;/code&gt; headers. Opera's header is the closest to what we want, albeit we can restrict the content-types for image assets when requesting an image resource. Perhaps something like:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;  Accept: image/webp, image/png, image/jpeg, image/gif, image/svg+xml, image/bitmap
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;With that in place, there is an incentive for the CDNs, proxies, and servers to perform the negotiation to deliver the optimal image format. Finally, once the server can choose the format, the upstream caches have an incentive to make &lt;code&gt;Vary&lt;/code&gt; work. &lt;strong&gt;In the end, we shave off hundreds of kilobytes of image data, we automate the menial task of selecting the optimal image formats, and we have a future-proof negotiation mechanism which can scale to dozens of image formats.&lt;/strong&gt; Everyone wins... &lt;em&gt;until we offset the win by embedding more cat pictures on our pages&lt;/em&gt;...&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=pgpxmCzLQk0:5X-EVCVnPjo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/pgpxmCzLQk0" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2012/12/18/deploying-new-image-formats-on-the-web/</feedburner:origLink></entry><entry><title type="html">Web Performance Anomaly Detection with Google Analytics</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/YGE3LYsTprI/" /><updated>2012-11-30T00:00:00-08:00</updated><id>http://www.igvita.com/2012/11/30/web-performance-anomaly-detection-with-google-analytics/</id><content type="html">&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/ga-alert.png' class='left' /&gt;Step one, monitor all the things. Step two, dedicate 90% of your analytics time and resources to analyzing data, deriving insights, and &lt;strong&gt;iterating&lt;/strong&gt; on what metrics are being monitored and are being optimized. However, there is one small problem. &lt;strong&gt;Chances are, the amount of data produced by the instrumentation outpaces your ability to analyze, monitor, and correlate all the variations of the variables at play.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where an &lt;a href="http://en.wikipedia.org/wiki/Anomaly_detection"&gt;anomaly detection algorithm&lt;/a&gt;, backed by a good statistical engine with access to the data can prove invaluable: it does not need to be perfect, but it should be able to alert you to significant outliers in the data. With the alert in hand, or in your mailbox, you can dig in and determine if more investigation is required.&lt;/p&gt;

&lt;div class='ytvideo' id='PulNjqfToAo'&gt;&lt;/div&gt;


&lt;h2&gt;Google Analytics Intelligence Events&lt;/h2&gt;

&lt;p&gt;Good news, if you are using Google Analytics, then you already have a powerful anomaly detection engine at your disposal: &lt;a href="http://support.google.com/analytics/bin/answer.py?hl=en&amp;amp;answer=1320491&amp;amp;topic=1032994&amp;amp;ctx=topic"&gt;Intelligence Events&lt;/a&gt;. Best of all, it can leverage all of your existing data, configured segments, and other customizations. And the price is right as well - it's free.&lt;/p&gt;

&lt;blockquote&gt;Analytics monitors your website's traffic to detect significant statistical variations, and then automatically generates alerts, or Intelligence Events, when those variations occur. Taking a closer look at these anomalies can provide insights you might have otherwise missed, for example, a spike in traffic from a particular city or referring site.&lt;/blockquote&gt;


&lt;p&gt;In fact, with a little bit of work and customization, &lt;strong&gt;Intelligence Events can be easily configured to help you monitor the performance of your site!&lt;/strong&gt; Visitors from India seeing a sudden spike in page load times? Now you have an automated tool which will help you spot the problem.&lt;/p&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/plt-alert.png' class='center' style='max-width:691px;width:100%' /&gt;&lt;/p&gt;

&lt;p&gt;Even better, the sample report above correlated the likely contributors to the generated alert and identified visitors from Chennai, India as experiencing a significant increase in their page loading times. With this information in hand, you can dig deeper to identify root cause.&lt;/p&gt;

&lt;h2&gt;Web performance anomaly detection&lt;/h2&gt;

&lt;p&gt;Google Analytics &lt;a href="http://www.igvita.com/2012/04/04/measuring-site-speed-with-navigation-timing/"&gt;samples page load time performance data&lt;/a&gt; for browsers that support the &lt;a href="http://w3c-test.org/webperf/specs/NavigationTiming/"&gt;W3C Navigation Timing&lt;/a&gt; API's, which includes: redirect and DNS times, TCP establishment, server response times, as well as DOM-level metrics such as the onload time. There are over half a dozen metrics in total, each recorded from a real user accessing your site - in other words, this is Real User Measurement (RUM), not synthetic data.&lt;/p&gt;

&lt;p&gt;If you're not familiar with the &lt;a href="http://support.google.com/analytics/bin/answer.py?hl=en&amp;amp;answer=1205784"&gt;Site Speed reports&lt;/a&gt;, then that's a good place to start - check out &lt;a href="http://www.youtube.com/watch?v=NCFVEuKQgBM&amp;amp;list=PL1B4F4863AEE2B122&amp;amp;index=1"&gt;this GDL episode&lt;/a&gt; for an in-depth look. However, we're going to go a level deeper: &lt;strong&gt;each of the Navigation Timing metrics can be monitored with Intelligence Events!&lt;/strong&gt; All you need to do is create a custom alert and define a few threshold criteria which will trigger it in the future.&lt;/p&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/alert-segment.png' class='center' style='max-width:638px;width:100%' /&gt;&lt;/p&gt;

&lt;p&gt;A few performance alert ideas for your site:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Track DNS resolve times&lt;/strong&gt; across the world, or in specific regions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Track server response times&lt;/strong&gt; across all visitors, or customize for different versions of the site&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Track onload time&lt;/strong&gt; to detect misbehaving CSS, scripts, and other resources&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Powertip: Use Advanced Segments!&lt;/h2&gt;

&lt;p&gt;Setting up an alert for a specific variable, within a global context, is a good place to start. However, if you have not created a &lt;a href="http://support.google.com/analytics/bin/answer.py?hl=en&amp;amp;answer=1033017"&gt;custom advanced segment&lt;/a&gt; in Google Analytics, then you've only scratched the surface of what's possible. Need to monitor the page load time (PLT), or DNS times for all mobile visitors in Asia, or perhaps Tokyo specifically? No problem, just create a new custom segment:&lt;/p&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/mobile-asia-segment.png' class='center' style='max-width:684px;width:100%' /&gt;&lt;/p&gt;

&lt;p&gt;You can apply the segment on any report in Google Analytics, and once created, you can also select it when setting up the Intelligence Alert! If there are specific markets, or types of users or traffic that you're concerned about, then creating an advanced segment will allow you to tailor the alerts as well.&lt;/p&gt;

&lt;h2&gt;Measure, Optimize, Iterate&lt;/h2&gt;

&lt;p&gt;Anomaly detection is a tool, and a powerful one at that. However, &lt;strong&gt;it is still up to you to define, and iteratively improve the segments and the thresholds to tailor the alerts to your application&lt;/strong&gt;. Expect false positives, but also expect to be alerted to issues that you would never otherwise have caught in the torrent of the monitoring data.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=YGE3LYsTprI:mVKk3zmdKVs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/YGE3LYsTprI" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2012/11/30/web-performance-anomaly-detection-with-google-analytics/</feedburner:origLink></entry><entry><title type="html">Wait, Chrome DevTools could do THAT?</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/ILRhEEyzD0o/" /><updated>2012-11-14T00:00:00-08:00</updated><id>http://www.igvita.com/2012/11/14/wait-chrome-devtools-could-do-that/</id><content type="html">&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/chrome-devtools.png' class='left' /&gt;&lt;strong&gt;Your browser is one of the most and best instrumented development platforms - you may just not realize it yet.&lt;/strong&gt; Of course, you can inspect the source, walk the DOM, fiddle with the CSS, and evaluate and debug your JavaScript, but there is so much more! Granted, there is always room for improvement, but the exciting part is that much of this functionality is also built with the same technology we build our sites: HTML, CSS, JavaScript, and WebSockets. This means we can extend it, leverage it for use cases outside the browser, and much more.&lt;/p&gt;

&lt;p&gt;To highlight some of these features, I gave a presentation at Velocity EU a few weeks back: "&lt;a href="http://www.igvita.com/slides/2012/devtools-tips-and-tricks/"&gt;Wait, Chrome DevTools could do THAT?&lt;/a&gt;". To my surprise, it became a runaway hit, aggregating over 100K unique visitors since published, and generating a lot of requests for the video of the talk... See below!&lt;/p&gt;

&lt;h2&gt;Wait, Chrome Dev Tools could do THAT?&lt;/h2&gt;

&lt;div class='ytvideo' id='BaneWEqNcpE'&gt;&lt;/div&gt;


&lt;p&gt;This episode provides a whirlwind tour of how to analyze network performance, rendering and layout pipeline, as well as detecting memory leaks in your JavaScript, and using audits and extensions to build faster apps!&lt;/p&gt;

&lt;h2&gt;Extending Chrome DevTools for fun and profit...&lt;/h2&gt;

&lt;div class='ytvideo' id='yO-TfKT2O_4'&gt;&lt;/div&gt;


&lt;p&gt;This episode takes a deep(er) dive into the inner workings of Chrome itself: the debugging protocol, and Audit and Panel extension API's in DevTools, a quick tour of the Chrome's network stack, and chrome://tracing features!&lt;/p&gt;

&lt;p&gt;I encourage you to try these tools for yourself by following the links and examples in the presentation: &lt;a href="http://www.igvita.com/slides/2012/devtools-tips-and-tricks/"&gt;see the slides&lt;/a&gt;. Finaly, if you have any other tips, suggestions, or favorite features I've omitted - let me know!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=ILRhEEyzD0o:SQ5-GTazR1U:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/ILRhEEyzD0o" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2012/11/14/wait-chrome-devtools-could-do-that/</feedburner:origLink></entry><entry><title type="html">Simple SPDY and NPN Negotiation with HAProxy</title><link rel="alternate" type="text/html" href="http://feeds.igvita.com/~r/igvita/~3/6gB5v_IaBVk/" /><updated>2012-10-31T00:00:00-07:00</updated><id>http://www.igvita.com/2012/10/31/simple-spdy-and-npn-negotiation-with-haproxy/</id><content type="html">&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/spdy-stack.png' class='left' /&gt;SPDY is an experimental protocol developed at Google, designed to reduce the latency of web pages. Specifically, its goal is to address the &lt;a href="http://www.igvita.com/2011/04/07/life-beyond-http-11-googles-spdy/"&gt;limitations of HTTP 1.1&lt;/a&gt; and to remove existing bottlenecks: head of line blocking, inefficient use of underlying TCP connections, and header bloat amongst others. However, while all of this sounds great in writing, deploying a new protocol on the web, in practice, is fraught with difficulty.&lt;/p&gt;

&lt;p&gt;Specifically, the many layers of HTTP routers, caches, and custom appliances will often behave in unpredictable ways when presented with an alternate protocol, leading to randomly dropped connections and frustrated users. To address this, SPDY is delivered via SSL: the end-to-end encrypted tunnel allows the client and the server to exchange SPDY frames without intervention by intermediate nodes. It's important to note that SPDY &lt;strong&gt;does not&lt;/strong&gt; require SSL, but in practice, SSL is a pragmatic choice to get to a working solution.&lt;/p&gt;

&lt;h2&gt;Next Protocol Negotiation (NPN)&lt;/h2&gt;

&lt;p&gt;But how does the client and server know to use SPDY once the SSL tunnel is opened? This is where &lt;a href="https://technotes.googlecode.com/git/nextprotoneg.html"&gt;Next Protocol Negotiation&lt;/a&gt; (NPN) enters the picture. &lt;strong&gt;NPN is an SSL extension which allows the client and the server to negotiate the application protocol as part of the SSL handshake&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/npn-negotiation.png' class='center' style='max-width:609px;width:100%' /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NPN eliminates the extra roundtrip to negotiate the application protocol&lt;/strong&gt; - contrast this to the &lt;a href="http://en.wikipedia.org/wiki/WebSocket#WebSocket_protocol_handshake"&gt;current WebSocket  handshake&lt;/a&gt;, which imposes another roundtrip of latency on top of the SSL negotiation. If you're counting, that is one RTT for the TCP handshake, two for SSL negotiation, and one for WebSocket upgrade - four RTT's before any application data can be exchanged!&lt;/p&gt;

&lt;p&gt;Support for NPN was added in OpenSSL (1.0.0d+), NSS, and TLSLite. Chrome, Firefox, and Opera support SPDY (and hence NPN also), as well as many of the popular servers: &lt;a href="http://code.google.com/p/mod-spdy/"&gt;Apache&lt;/a&gt;, &lt;a href="http://mailman.nginx.org/pipermail/nginx-devel/2012-June/002343.html"&gt;nginx&lt;/a&gt;, &lt;a href="http://wiki.eclipse.org/Jetty/Feature/SPDY"&gt;Jetty&lt;/a&gt;, and &lt;a href="https://github.com/indutny/node-spdy"&gt;node.js&lt;/a&gt; amongst others. That said, the requirement for NPN support has nonetheless been a barrier to wider adoption. That is, until HAProxy decided to make everyone's life a lot easier! Let's take a look at a hands on example.&lt;/p&gt;

&lt;h2&gt;Building HAProxy with NPN support&lt;/h2&gt;

&lt;p&gt;Early &lt;a href="http://comments.gmane.org/gmane.comp.web.haproxy/9381"&gt;SSL support landed in HAProxy&lt;/a&gt; development builds in early September and has been rapidly improving ever since. A notable and a recent addition is, you guessed it, support for NPN negotiation! To make it work, download the latest &lt;a href="http://haproxy.1wt.eu/download/1.5/src/snapshot/"&gt;development tarball&lt;/a&gt;, or do a checkout from git and build the binary against a recent version of OpenSSL:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; wget http://www.openssl.org/source/openssl-1.0.1c.tar.gz
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; tar -zxvf openssl* &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;openssl*
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; ./configure --prefix&lt;span class="o"&gt;=&lt;/span&gt;/tmp/openssl --openssldir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/openssl darwin64-x86_64-cc
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; make &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make install

&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; git clone http://git.1wt.eu/git/haproxy.git/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;haproxy
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; make &lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;generic &lt;span class="nv"&gt;USE_OPENSSL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ADDINC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-I/tmp/openssl/include &lt;span class="nv"&gt;ADDLIB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-L/tmp/openssl/lib -lssl
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; ./haproxy -vv
  &lt;span class="c"&gt;# Built with OpenSSL version : OpenSSL 1.0.1c 10 May 2012&lt;/span&gt;
  &lt;span class="c"&gt;# OpenSSL library supports TLS extensions : yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The above instructions should build the latest version of HAProxy on OSX. If you already have OpenSSL 1.0.0d+, then you can skip the first step, as well as the extra &lt;code&gt;ADDINC&lt;/code&gt; and &lt;code&gt;ADDLIB&lt;/code&gt; directives. With that, we're almost ready to handle SPDY alongside our regular HTTP and HTTPS traffic!&lt;/p&gt;

&lt;h2&gt;Configuring HAProxy for HTTP, HTTPS, and SPDY&lt;/h2&gt;

&lt;p&gt;What we want to do is to configure our HAProxy as an &lt;a href="http://en.wikipedia.org/wiki/SSL_termination_proxy"&gt;SSL termination proxy&lt;/a&gt;. Meaning, &lt;strong&gt;HAProxy will be the one serving our SSL certificate back to the client, and all traffic forwarded to our internal servers will flow unencrypted&lt;/strong&gt;. This also means that HAProxy will need to handle the NPN handshake. In fact, ideally, it should handle and route all types of traffic: HTTP, HTTPS, and SPDY.&lt;/p&gt;

&lt;p&gt;&lt;img src='//www.igvita.com/posts/12/haproxy-npn.png' class='center' style='max-width:525px;width:100%' /&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="nginx"&gt;&lt;span class="k"&gt;defaults&lt;/span&gt;
  &lt;span class="s"&gt;log&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="s"&gt;.0.0.1&lt;/span&gt; &lt;span class="s"&gt;local0&lt;/span&gt;

&lt;span class="c1"&gt;# accept connections on port 80, forward requests to &amp;quot;http_cluster&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;frontend&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
  &lt;span class="s"&gt;mode&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
  &lt;span class="s"&gt;bind&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="s"&gt;default_backend&lt;/span&gt; &lt;span class="s"&gt;http_cluster&lt;/span&gt;

&lt;span class="c1"&gt;# accept connections on port 443 (SSL)&lt;/span&gt;
&lt;span class="c1"&gt;# - forward SPDY connections to spdy_cluster&lt;/span&gt;
&lt;span class="c1"&gt;# - forward regular HTTPS connections to http_cluster&lt;/span&gt;
&lt;span class="c1"&gt;# - ha.pem should be a concatenated file: certificate first then the key&lt;/span&gt;
&lt;span class="s"&gt;frontend&lt;/span&gt; &lt;span class="s"&gt;secure&lt;/span&gt;
  &lt;span class="s"&gt;mode&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;

  &lt;span class="c1"&gt;# advertise spdy/2 support via NPN&lt;/span&gt;
  &lt;span class="s"&gt;bind&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;crt&lt;/span&gt; &lt;span class="s"&gt;./certs/ha.pem&lt;/span&gt; &lt;span class="s"&gt;npn&lt;/span&gt; &lt;span class="s"&gt;spdy/2&lt;/span&gt;
  &lt;span class="s"&gt;use_backend&lt;/span&gt; &lt;span class="s"&gt;spdy_cluster&lt;/span&gt; &lt;span class="s"&gt;if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;ssl_npn&lt;/span&gt; &lt;span class="s"&gt;-i&lt;/span&gt; &lt;span class="s"&gt;spdy/2&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;

  &lt;span class="s"&gt;default_backend&lt;/span&gt; &lt;span class="s"&gt;http_cluster&lt;/span&gt;

&lt;span class="c1"&gt;# SPDY server running on port 10000&lt;/span&gt;
&lt;span class="s"&gt;backend&lt;/span&gt; &lt;span class="s"&gt;spdy_cluster&lt;/span&gt;
  &lt;span class="s"&gt;server&lt;/span&gt; &lt;span class="s"&gt;srv01&lt;/span&gt; &lt;span class="n"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;

&lt;span class="c1"&gt;# HTTP server running on port 8000&lt;/span&gt;
&lt;span class="s"&gt;backend&lt;/span&gt; &lt;span class="s"&gt;http_cluster&lt;/span&gt;
  &lt;span class="s"&gt;mode&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
  &lt;span class="s"&gt;server&lt;/span&gt; &lt;span class="s"&gt;srv01&lt;/span&gt; &lt;span class="n"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Simple as that. HAProxy accepts the SSL connection, performs the SSL handshake and advertises &lt;code&gt;spdy/2&lt;/code&gt; support if client supports NPN. If the client selects &lt;code&gt;spdy/2&lt;/code&gt; then the request is routed to the SPDY server. Otherwise, the HTTPS request is forwarded to the HTTP cluster. The important bit is that in both cases, the forwarded traffic is no longer encrypted - &lt;strong&gt;the SPDY server does not need to perform the SSL handshake or worry about NPN, instead it simply has to parse and return the raw SPDY frames back to the proxy&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;NPN is not just for SPDY&lt;/h2&gt;

&lt;p&gt;To see SPDY in action, you can checkout and &lt;a href="https://github.com/igrigorik/spdy/blob/master/examples/spdy_server.rb"&gt;run the demo&lt;/a&gt; SPDY server &lt;a href="https://github.com/igrigorik/spdy"&gt;written in Ruby&lt;/a&gt; behind HAProxy. With native NPN support in HAProxy, adding SPDY support has never been easier - you are literally just a few rules away from intelligently routing incoming SPDY requests alongside the rest of your traffic. &lt;strong&gt;No need to downgrade your SPDY connection to HTTP, punch holes in your infrastructure to expose NPN capable serves, or deploy parallel infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Best of all, while the above example is specific to SPDY, the &lt;strong&gt;NPN mechanism works for any new, or existing protocol&lt;/strong&gt;. As an example, we can eliminate the extra roundtrip in WebSocket negotiation with NPN, or we can deploy other, new and experimental protocols without imposing any additional latency penalties.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.igvita.com/~ff/igvita?a=6gB5v_IaBVk:PKvQF8uWBmk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/igvita/~4/6gB5v_IaBVk" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.igvita.com/2012/10/31/simple-spdy-and-npn-negotiation-with-haproxy/</feedburner:origLink></entry></feed>
