Skip to content

Commit

Permalink
Added GzFile for serving gzipped static files
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaudanza committed Feb 27, 2013
1 parent 7b535cd commit 6b5dcfc
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/rack.rb
Expand Up @@ -34,6 +34,7 @@ def self.release
autoload :ContentType, "rack/content_type"
autoload :ETag, "rack/etag"
autoload :File, "rack/file"
autoload :GzFile, "rack/gz_file"
autoload :Deflater, "rack/deflater"
autoload :Directory, "rack/directory"
autoload :ForwardRequest, "rack/recursive"
Expand Down
41 changes: 41 additions & 0 deletions lib/rack/gz_file.rb
@@ -0,0 +1,41 @@
module Rack
# Rack::GzFile behaves exactly the same as Rack::File, except that it will
# also serve up a gzip encoding of a file, if one is available on the
# filesystem.
#
# For each request, Rack::GzFile first checks the filesystem for a file with a
# .gz extension. If one is found, the appropriate encoding headers are added
# to the response and the gzip file is served.
#
# If no .gz file is found, Rack::GzFile will behave exactly like Rack::File.
class GzFile
def initialize(root, headers={}, default_mime = 'text/plain')
@file_server = Rack::File.new(root, headers, default_mime)
@default_mime = default_mime
end

def call(env)
path_info = env['PATH_INFO']

status, headers, body = @file_server.call(
env.merge('PATH_INFO' => path_info + '.gz')
)

gzip_available = status != 404

if !gzip_available || env['HTTP_ACCEPT_ENCODING'] !~ /\bgzip\b/
status, headers, body = @file_server.call(env)
else
headers['Content-Type'] = Mime.mime_type(::File.extname(path_info),
@default_mime)
headers['Content-Encoding'] = 'gzip'
end

if gzip_available
headers['Vary'] = 'Accept-Encoding'
end

[status, headers, body]
end
end
end
1 change: 1 addition & 0 deletions test/cgi/assets/compress_me.html
@@ -0,0 +1 @@
Hello, Rack!
Binary file added test/cgi/assets/compress_me.html.gz
Binary file not shown.
43 changes: 43 additions & 0 deletions test/spec_gz_file.rb
@@ -0,0 +1,43 @@
require 'rack/gz_file'
require 'rack/lint'
require 'rack/mock'
require 'zlib'

describe Rack::GzFile do
DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT

def request
Rack::MockRequest.new(Rack::Lint.new(Rack::GzFile.new(DOCROOT)))
end

should "serve an uncompressed file when gzip is not supported by the server" do
res = request.get('/cgi/assets/index.html')
res.body.should.equal "### TestFile ###\n"
res.headers.should.not.include 'Vary'
res.headers.should.not.include('Content-Encoding')
res.headers['Content-Length'].should.equal(
File.size(DOCROOT + '/cgi/assets/index.html').to_s)
end

should "serve an uncompressed file when gzip is not supported by the client" do
res = request.get('/cgi/assets/compress_me.html')
res.body.should.equal 'Hello, Rack!'
res.headers['Vary'].should.equal 'Accept-Encoding'
res.headers.should.not.include('Content-Encoding')
res.headers['Content-Length'].should.equal(
File.size(DOCROOT + '/cgi/assets/compress_me.html').to_s)
end

should "serve a compressed file" do
res = request.get('/cgi/assets/compress_me.html',
'HTTP_ACCEPT_ENCODING' => 'gzip')

gz = Zlib::GzipReader.new(StringIO.new(res.body))
gz.read.should.equal "Hello, Rack!"
res.headers['Vary'].should.equal 'Accept-Encoding'
res.headers['Content-Encoding'].should.equal 'gzip'
res.headers['Content-Type'].should.equal 'text/html'
res.headers['Content-Length'].should.equal(
File.size(DOCROOT + '/cgi/assets/compress_me.html.gz').to_s)
end
end

0 comments on commit 6b5dcfc

Please sign in to comment.