Using Deflate Compression in Ruby
Posted on April 19th, 2006
When I realized that my homemade Ruby-based blogging script wasn't compressing pages, I wanted to do something about it. Normally this is something that could be handled at the web server level-- under Apache 1.3 that would mean mod_gzip, under Apache 2 it would probably mean mod_deflate. In a shared hosting setup, neither of those may be available. It falls to your application to handle the details by itself.
The Accept-Encoding HTTP header reveals which compression schemes a particular browser supports. Opera, Internet Explorer, Firefox, and Safari all know how to handle gzip compression and deflate compression. People have tried to compare the two to answer seemingly weighty questions like, "Which is better, gzip or deflate?" but the results seem pretty close so a better question for my purposes is, "Which is easier to implement?"
Ruby's zlib library supports both types of compression, and deflate ended up being the winner. Provide a string, call the deflate method on it, get back the compressed version. Just like this:
unless @cgi.accept_encoding.nil? or
@cgi.accept_encoding.index("deflate") == nil then
deflated = Zlib::Deflate.deflate(content,9)
@cgi.out("Content-Encoding"=>"deflate",
"Vary"=>"Accept-Encoding",
"Content-type"=>"#{content_type}; charset=utf-8")
{deflated[2..-5]}
else
@cgi.out("Content-type"=>"#{content_type}; charset=utf-8")
{content}
end
So why the [2..-5] there? Initially I didn't have
that, and discovered that Firefox and Opera could understand the
output but Internet Explorer and Safari could not. After a lot of
thrashing around, I ended up writing a test script in PHP and then
comparing its output to an equivalent script in Ruby:
header('Content-Encoding: deflate');
$mystring = "testing testing";
$compressed = gzdeflate($mystring, 9);
print $compressed;
The output of the PHP script and the Ruby script didn't match, but once I trimmed off a bit from the front and the end of the Ruby scripts output, everything was wine and roses.
It seems that there's a header and a footer to the deflated output that needed to get stripped away. Not that there was any mention of this in the Ruby zlib documentation. Weak.