Hopefully everyone who builds stuff for the web knows about gzip compression available in HTTP. Here's a quick intro if you don't: https://developers.google.com/speed/articles/gzip
I use connect or express when building web servers in node, and you can use the compress middleware to have your content gzip'd (or deflate'd), like in this little snippet.
However ...
Let's think about what's going on here. When you use the compress middleware like this, for every response that sends compressable content, your content will be compressed. Every time. Of course, for your "static" resources, the result of that compression is the same every time, and so for those resources, it's really kind of pointless to run the compression for each request. You should do it once, and then reuse that compressed result for future requests.
Here are some tests using the play Waste, by Harley Granville-Barker. I pulled the HTML version of the file, and then also gzip'd the file manually from the command-line for one of tests.
The HTML file is ~300 KB. The gzip'd version is ~90 KB.
And here's a server I built to serve the files:
The server runs on 3 different HTTP ports, each one serving the file, but in a different way.
Port 4000 serves the HTML file with no compression.
Port 4001 serves the HTML file with the compress middleware
Port 4002 serves the the pre-gzip'd version of the file that I stored in
a separate directory; the original file was waste.html
, but the gzip'd
version is in gz/waste.html
. It checks the incoming request to see
if a gzip'd version of the file exists (caching that result), and
internally redirects the server to that file by resetting request.url
.
Setting the appropriate Content-Encoding
, etc headers.
What a hack! Not
quite sure that "fixing" request.url
is kosher, but, worked great for
this test.
Here's some curl
invocations.
$ curl --compressed --output /dev/null --dump-header - --write-out "%{size_download} bytes" http://localhost:4000/waste.html X-Powered-By: Express Accept-Ranges: bytes ETag: "305826-1384296482000" Date: Wed, 13 Nov 2013 01:21:13 GMT Cache-Control: public, max-age=0 Last-Modified: Tue, 12 Nov 2013 22:48:02 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 305826 Connection: keep-alive 305826 bytes
Looks normal.
$ curl --compressed --output /dev/null --dump-header - --write-out "%{size_download} bytes" http://localhost:4001/waste.html X-Powered-By: Express Accept-Ranges: bytes ETag: "305826-1384296482000" Date: Wed, 13 Nov 2013 01:21:13 GMT Cache-Control: public, max-age=0 Last-Modified: Tue, 12 Nov 2013 22:48:02 GMT Content-Type: text/html; charset=UTF-8 Vary: Accept-Encoding Content-Encoding: gzip Connection: keep-alive Transfer-Encoding: chunked 91071 bytes
Nice seeing the Content-Encoding
and Vary
headers, along with the reduced
download size. But look ma, no Content-Length
header; instead the content
comes down chunked, as you would expect with a server-processed output stream.
$ curl --compressed --output /dev/null --dump-header - --write-out "%{size_download} bytes" http://localhost:4002/waste.html X-Powered-By: Express Content-Encoding: gzip Vary: Accept-Encoding Accept-Ranges: bytes ETag: "90711-1384297654000" Date: Wed, 13 Nov 2013 01:21:13 GMT Cache-Control: public, max-age=0 Last-Modified: Tue, 12 Nov 2013 23:07:34 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 90711 Connection: keep-alive 90711 bytes
Like the gzip'd version above, but this one has a Content-Length
!
Here are some contrived, useless benches using wrk, that confirm my fears.
$ wrk --connections 100 --duration 10s --threads 10 --header "Accept-Encoding: gzip" http://localhost:4000/waste.html Running 10s test @ http://localhost:4000/waste.html 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 71.72ms 15.64ms 101.74ms 69.82% Req/Sec 139.91 10.52 187.00 87.24% 13810 requests in 10.00s, 3.95GB read Requests/sec: 1380.67 Transfer/sec: 404.87MB $ wrk --connections 100 --duration 10s --threads 10 --header "Accept-Encoding: gzip" http://localhost:4001/waste.html Running 10s test @ http://localhost:4001/waste.html 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 431.76ms 20.27ms 493.16ms 63.89% Req/Sec 22.47 3.80 30.00 80.00% 2248 requests in 10.00s, 199.27MB read Requests/sec: 224.70 Transfer/sec: 19.92MB $ wrk --connections 100 --duration 10s --threads 10 --header "Accept-Encoding: gzip" http://localhost:4002/waste.html Running 10s test @ http://localhost:4002/waste.html 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 48.11ms 10.66ms 72.33ms 67.39% Req/Sec 209.46 24.30 264.00 81.07% 20795 requests in 10.01s, 1.76GB read Requests/sec: 2078.08 Transfer/sec: 180.47MB
Funny to note that the server using compress middleware actually handles less requests/sec than the one that doesn't compress at all. But this is a localhost test so the network bandwidth/throughput isn't realistic. Still, makes ya think.