mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Enable gzip compression by default
If someone is using ActionDispatch::Static to serve assets and makes it past the `match?` then the file exists on disk and it will be served. This PR adds in logic that checks to see if the file being served is already compressed (via gzip) and on disk, if it is it will be served as long as the client can handle gzip encoding. If not, then a non gzip file will be served. This additional logic slows down an individual asset request but should speed up the consumer experience as compressed files are served and production applications should be delivered with a CDN. This PR allows a CDN to cache a gzip file by setting the `Vary` header appropriately. In net this should speed up a production application that are using Rails as an origin for a CDN. Non-asset request speed is not affected in this PR.
This commit is contained in:
parent
2e355fe0c7
commit
cfaaacd976
7 changed files with 77 additions and 17 deletions
|
@ -1,3 +1,9 @@
|
|||
* Requests that hit `ActionDispatch::Static` can now take advantage
|
||||
of gzipped assets on disk. By default a gzip asset will be served if
|
||||
the client supports gzip and a compressed file is on disk.
|
||||
|
||||
*Richard Schneeman*
|
||||
|
||||
* `ActionController::Parameters` will stop inheriting from `Hash` and
|
||||
`HashWithIndifferentAccess` in the next major release. If you use any method
|
||||
that is not available on `ActionController::Parameters` you should consider
|
||||
|
|
|
@ -16,8 +16,9 @@ module ActionDispatch
|
|||
def initialize(root, cache_control)
|
||||
@root = root.chomp('/')
|
||||
@compiled_root = /^#{Regexp.escape(root)}/
|
||||
headers = cache_control && { 'Cache-Control' => cache_control }
|
||||
@file_server = ::Rack::File.new(@root, headers)
|
||||
headers = {}
|
||||
headers['Cache-Control'] = cache_control if cache_control
|
||||
@file_server = ::Rack::File.new(@root, headers)
|
||||
end
|
||||
|
||||
def match?(path)
|
||||
|
@ -36,23 +37,48 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def call(env)
|
||||
@file_server.call(env)
|
||||
end
|
||||
|
||||
def ext
|
||||
@ext ||= begin
|
||||
ext = ::ActionController::Base.default_static_extension
|
||||
"{,#{ext},/index#{ext}}"
|
||||
path = env['PATH_INFO']
|
||||
gzip_file_exists = gzip_file_exists?(path)
|
||||
if gzip_file_exists && gzip_encoding_accepted?(env)
|
||||
env['PATH_INFO'] = "#{path}.gz"
|
||||
status, headers, body = @file_server.call(env)
|
||||
headers['Content-Encoding'] = 'gzip'
|
||||
headers['Content-Type'] = content_type(path)
|
||||
else
|
||||
status, headers, body = @file_server.call(env)
|
||||
end
|
||||
|
||||
headers['Vary'] = 'Accept-Encoding' if gzip_file_exists
|
||||
return [status, headers, body]
|
||||
end
|
||||
|
||||
def unescape_path(path)
|
||||
URI.parser.unescape(path)
|
||||
end
|
||||
private
|
||||
def ext
|
||||
@ext ||= begin
|
||||
ext = ::ActionController::Base.default_static_extension
|
||||
"{,#{ext},/index#{ext}}"
|
||||
end
|
||||
end
|
||||
|
||||
def escape_glob_chars(path)
|
||||
path.gsub(/[*?{}\[\]]/, "\\\\\\&")
|
||||
end
|
||||
def unescape_path(path)
|
||||
URI.parser.unescape(path)
|
||||
end
|
||||
|
||||
def escape_glob_chars(path)
|
||||
path.gsub(/[*?{}\[\]]/, "\\\\\\&")
|
||||
end
|
||||
|
||||
def content_type(path)
|
||||
::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
||||
end
|
||||
|
||||
def gzip_encoding_accepted?(env)
|
||||
env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
|
||||
end
|
||||
|
||||
def gzip_file_exists?(path)
|
||||
File.exist?(File.join(@root, "#{::Rack::Utils.unescape(path)}.gz"))
|
||||
end
|
||||
end
|
||||
|
||||
# This middleware will attempt to return the contents of a file's body from
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# encoding: utf-8
|
||||
require 'abstract_unit'
|
||||
require 'rbconfig'
|
||||
require 'zlib'
|
||||
|
||||
module StaticTests
|
||||
def test_serves_dynamic_content
|
||||
|
@ -106,6 +107,18 @@ module StaticTests
|
|||
end
|
||||
end
|
||||
|
||||
def test_serves_gzip_files_when_header_set
|
||||
file_name = "/gzip/application-a71b3024f80aea3181c09774ca17e712.js"
|
||||
response = get(file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip')
|
||||
assert_gzip file_name, response
|
||||
assert_equal 'application/javascript', response.headers['Content-Type']
|
||||
assert_equal 'Accept-Encoding', response.headers["Vary"]
|
||||
assert_equal 'gzip', response.headers["Content-Encoding"]
|
||||
|
||||
response = get(file_name, 'HTTP_ACCEPT_ENCODING' => '')
|
||||
refute_equal 'gzip', response.headers["Content-Encoding"]
|
||||
end
|
||||
|
||||
# Windows doesn't allow \ / : * ? " < > | in filenames
|
||||
unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
||||
def test_serves_static_file_with_colon
|
||||
|
@ -125,13 +138,20 @@ module StaticTests
|
|||
|
||||
private
|
||||
|
||||
def assert_gzip(file_name, response)
|
||||
expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name)
|
||||
actual = Zlib::GzipReader.new(StringIO.new(response.body)).read
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def assert_html(body, response)
|
||||
assert_equal body, response.body
|
||||
assert_equal "text/html", response.headers["Content-Type"]
|
||||
refute response.headers.key?("Vary")
|
||||
end
|
||||
|
||||
def get(path)
|
||||
Rack::MockRequest.new(@app).request("GET", path)
|
||||
def get(path, headers = {})
|
||||
Rack::MockRequest.new(@app).request("GET", path, headers)
|
||||
end
|
||||
|
||||
def with_static_file(file)
|
||||
|
|
4
actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js
vendored
Normal file
4
actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz
vendored
Normal file
BIN
actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz
vendored
Normal file
Binary file not shown.
4
actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js
vendored
Normal file
4
actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz
vendored
Normal file
BIN
actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue