aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/middleware/static.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/middleware/static.rb')
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb73
1 files changed, 59 insertions, 14 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 2764584fe9..a3e8126ace 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -2,12 +2,23 @@ require 'rack/utils'
require 'active_support/core_ext/uri'
module ActionDispatch
+ # This middleware returns a file's contents from disk in the body response.
+ # When initialized it can accept an optional 'Cache-Control' header which
+ # will be set when a response containing a file's contents is delivered.
+ #
+ # This middleware will render the file specified in `env["PATH_INFO"]`
+ # where the base path is in the +root+ directory. For example if the +root+
+ # is set to `public/` then a request with `env["PATH_INFO"]` of
+ # `assets/application.js` will return a response with contents of a file
+ # located at `public/assets/application.js` if the file exists. If the file
+ # does not exist a 404 "File not Found" response will be returned.
class FileHandler
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)
@@ -26,25 +37,59 @@ module ActionDispatch
end
def call(env)
- @file_server.call(env)
+ 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 ext
- @ext ||= begin
- ext = ::ActionController::Base.default_static_extension
- "{,#{ext},/index#{ext}}"
+ private
+ def ext
+ @ext ||= begin
+ ext = ::ActionController::Base.default_static_extension
+ "{,#{ext},/index#{ext}}"
+ end
end
- end
- def unescape_path(path)
- URI.parser.unescape(path)
- end
+ def unescape_path(path)
+ URI.parser.unescape(path)
+ end
- def escape_glob_chars(path)
- path.gsub(/[*?{}\[\]]/, "\\\\\\&")
- 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
+ # disk in the response. If a file is not found on disk, the request will be
+ # delegated to the application stack. This middleware is commonly initialized
+ # to serve assets from a server's `public/` directory.
+ #
+ # This middleware verifies the path to ensure that only files
+ # living in the root directory can be rendered. A request cannot
+ # produce a directory traversal using this middleware. Only 'GET' and 'HEAD'
+ # requests will result in a file being returned.
class Static
def initialize(app, path, cache_control=nil)
@app = app