Class: ActionDispatch::FileHandler
Relationships & Source Files | |
Inherits: | Object |
Defined in: | actionpack/lib/action_dispatch/middleware/static.rb |
Overview
This endpoint serves static files from disk using Rack::Files
.
URL paths are matched with static files according to expected conventions: path
, path
.html, path
/index.html.
Precompressed versions of these files are checked first. Brotli (.br) and gzip (.gz) files are supported. If path
.br exists, this endpoint returns that file with a content-encoding: br
header.
If no matching file is found, this endpoint responds 404 Not Found
.
Pass the ::
directory to search for matching files, an optional ‘index: “index”` to change the default path
/index.html, and optional additional response headers.
Constant Summary
-
PRECOMPRESSED =
Accept-Encoding
value -> file extension{ "br" => ".br", "gzip" => ".gz", "identity" => nil }
Class Method Summary
Instance Method Summary
- #attempt(env)
- #call(env)
- #clean_path(path_info) private
- #compressible?(content_type) ⇒ Boolean private
- #each_candidate_filepath(path_info) {|path, content_type || "text/plain"| ... } private
- #each_precompressed_filepath(filepath) private
- #file_readable?(path) ⇒ Boolean private
-
#find_file(path_info, accept_encoding:)
private
Match a URI path to a static file to be served.
- #serve(request, filepath, content_headers) private
- #try_files(filepath, content_type, accept_encoding:) private
- #try_precompressed_files(filepath, headers, accept_encoding:) private
Constructor Details
.new(root, index: "index", headers: {}, precompressed: %i[ br gzip ],, compressible_content_types: /\A(?:text\/|application\/javascript|image\/svg\+xml)/) ⇒ FileHandler
# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 55
def initialize(root, index: "index", headers: {}, precompressed: %i[ br gzip ], compressible_content_types: /\A(?:text\/|application\/javascript|image\/svgxml)/) @root = root.chomp("/").b @index = index @precompressed = Array(precompressed).map(&:to_s) | %w[ identity ] @compressible_content_types = compressible_content_types @file_server = ::Rack::Files.new(@root, headers) end
Instance Method Details
#attempt(env)
[ GitHub ]#call(env)
[ GitHub ]# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 65
def call(env) attempt(env) || @file_server.call(env) end
#clean_path(path_info) (private)
[ GitHub ]
#compressible?(content_type) ⇒ Boolean
(private)
# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 149
def compressible?(content_type) @compressible_content_types.match?(content_type) end
#each_candidate_filepath(path_info) {|path, content_type || "text/plain"| ... } (private)
# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 162
def each_candidate_filepath(path_info) return unless path = clean_path(path_info) ext = ::File.extname(path) content_type = ::Rack::Mime.mime_type(ext, nil) yield path, content_type || "text/plain" # Tack on .html and /index.html only for paths that don't have an explicit, # resolvable file extension. No need to check for foo.js.html and # foo.js/index.html. unless content_type default_ext = ::ActionController::Base.default_static_extension if ext != default_ext default_content_type = ::Rack::Mime.mime_type(default_ext, "text/plain") yield "#{path}#{default_ext}", default_content_type yield "#{path}/#{@index}#{default_ext}", default_content_type end end nil end
#each_precompressed_filepath(filepath) (private)
[ GitHub ]# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 153
def each_precompressed_filepath(filepath) @precompressed.each do |content_encoding| precompressed_ext = PRECOMPRESSED.fetch(content_encoding) yield content_encoding, "#{filepath}#{precompressed_ext}" end nil end
#file_readable?(path) ⇒ Boolean
(private)
#find_file(path_info, accept_encoding:) (private)
Match a URI path to a static file to be served.
Used by the Static
class to negotiate a servable file in the public/
directory (see Static#call).
Checks for path
, path
.html, and path
/index.html files, in that order, including .br
and .gzip
compressed extensions.
If a matching file is found, the path and necessary response headers (Content-Type, Content-Encoding) are returned.
# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 104
def find_file(path_info, accept_encoding:) each_candidate_filepath(path_info) do |filepath, content_type| if response = try_files(filepath, content_type, accept_encoding: accept_encoding) return response end end end
#serve(request, filepath, content_headers) (private)
[ GitHub ]# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 80
def serve(request, filepath, content_headers) original, request.path_info = request.path_info, ::Rack::Utils.escape_path(filepath).b @file_server.call(request.env).tap do |status, headers, body| # Omit content-encoding/type/etc headers for 304 Not Modified if status != 304 headers.update(content_headers) end end ensure request.path_info = original end
#try_files(filepath, content_type, accept_encoding:) (private)
[ GitHub ]# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 112
def try_files(filepath, content_type, accept_encoding:) headers = { Rack::CONTENT_TYPE => content_type } if compressible? content_type try_precompressed_files filepath, headers, accept_encoding: accept_encoding elsif file_readable? filepath [ filepath, headers ] end end
#try_precompressed_files(filepath, headers, accept_encoding:) (private)
[ GitHub ]# File 'actionpack/lib/action_dispatch/middleware/static.rb', line 122
def try_precompressed_files(filepath, headers, accept_encoding:) each_precompressed_filepath(filepath) do |content_encoding, precompressed_filepath| if file_readable? precompressed_filepath # Identity encoding is default, so we skip Accept-Encoding negotiation and # needn't set Content-Encoding. # # Vary header is expected when we've found other available encodings that # Accept-Encoding ruled out. if content_encoding == "identity" return precompressed_filepath, headers else headers[ActionDispatch::Constants::VARY] = "accept-encoding" if accept_encoding.any? { |enc, _| /\b#{content_encoding}\b/i.match?(enc) } headers[ActionDispatch::Constants::CONTENT_ENCODING] = content_encoding return precompressed_filepath, headers end end end end end