one request per page

node streams, gzip, http connections, and you.

I'm JAKE SCOTT





Software engineer at




(we're hiring!)

make the web faster?

  1. increase conversions
  2. decrease server load
  3. increase revenue

Make the web faster.

  1. decrease payload size
  2. reduce request overhead
  3. make the load feel shorter

gzip - decrease payload size

  1. replaces duplicated symbols with pointers
  2. maximum window of 32kB
  3. maximum symbol size of 258B

Reduce request overhead

Every* request will have these:

  1. referral URL
  2. resource URL 
  3. cookies


These headers add up.

The less requests you make, the better.

How do I reduce requests?

  1. concatenate js files
  2. concatenate css files
  3. concatenate image files? 

Apparent load times

What the user doesn't know won't bug them.


Avoid the white screen of death.

decrease (apparent) load time

above the fold (ATF) matters

  1. inline ATF CSS
  2. ATF HTML should come first
  3. what about images?

Images, images, images

Current solutions:

  • CSS sprite sheets
    • refactor your whole page
    • maintaining sucks
  • Data URIs
    • 33% bloat (binary -> base64 utf-8)

A new solution for images

  • easy to implement
  • easy to maintain
  • stream everything
  • respond instantly
  • reduce requests
  • load ATF first

Finally, some node


this is wan.js

Wan.js

Turns all your <img> tags into one
 streaming, compressed, prioritized,
progressive, cached request.

  • koa middleware
  • express middleware
  • small client side file

How its used 

  1. replace <img src="...">
     with <img data-src="...">
  2. optionally set priority attribute
  3. call  Wan.getImages();

How it works (server)

  1. request all image URLs at once
  2. stream images 1 by 1, base64 encoded
  3. pipe stream to gzip
  4. pipe stream to http.serverResponse


res.setHeader('Transfer-Encoding', 'chunked');
res.setHeader('Content-type', 'text/html; charset=utf-8'); 

HOW IT WORKS (SERVER)

Request is processed as a stream

HOW IT WORKS (SERVER)

  1. stream file names from request
  2. write cached content to response
    OR
    pipe file stream to response
  3. cache contents of file after stream ends
  4. take the next file name from request stream


...probably should have been written as a transform stream

file names → cache/read stream response

STream file names from req

/imgs/lolcat.jpg&/imgs/memes/crazy-guy.jpg&/omg.png&/icons.png&/icons2.png&/assets/other/mountain.jpg&

/some/partially/transferred/filena

Process file names as they come in...

...buffer the ones that are not complete

TRANSFORM FILE NAME TO CONTENT

  • internal cache returns file content as string, read
    stream if not cached, 
    or undefined if ENOENT
  • cache fills itself from read stream
  • partial caching if stream isn't done
if(!asset) {  done();}else if(typeof asset === 'string') {
  res.write(asset);  done();}else {  asset.on('error', function () {...}).on('end', done);  res.pipe(asset, {end: false})}

HOW IT WORKS (CLIENT)

  1. get all <img> tags with data-src  attributes
  2. remove data-src attribute
  3. sort unique URLs by  priority attribute
  4. make GET if possible, POST otherwise
  5. stream in response, setting data URI

 if(xhr.readyState == 3) {  var relevant = xhr.responseText.substring(parserIndex)  , parts = relevant.split('\n')  , part;  //...}
Currently, it's a bit GC hungry.

HOW IT WORKS (CLIENT)

  • cache in memory
  • cache in local storage
  • GETs use normal cache
  • uses cache control header to dictate behavior
  • each file cached separately

Gzip... image streams?

  1. redundant headers for small images
  2. base64 + gzip < binary (usually)

charts from http://davidbcalhoun.com/

RESULT (SMITHSONIAN.COM)


Before
 
After:

38 images
 0.1MB smaller payload*


*...Not sure if chrome is reporting header content here

ADvantages over Css Sprites

  1. full control over image priority
  2. easy to convert src to data-src
  3. no build step
  4. no maintenance
  5. possibly smaller! (but not likely)

DISadvantages

  1. Can't use progressive JPGs
  2. POSTs won't cache, relies on local storage
  3. Slower on lossy connections 
  4. Does not yet work in IE (go figure)

QUESTIONS?


one request per page

By Jake Scott

one request per page

  • 1,141