Add the ability of returning arbitrary headers to ActionDispatch::Static
Now ActionDispatch::Static can accept HTTP headers so that developers will have control of returning arbitrary headers like 'Access-Control-Allow-Origin' when a response is delivered. They can be configured through `#config.public_file_server.headers`: config.public_file_server.headers = { "Cache-Control" => "public, max-age=60", "Access-Control-Allow-Origin" => "http://rubyonrails.org" } Also deprecate `config.static_cache_control` in favor of `config.public_file_server.headers`.
This commit is contained in:
parent
dfc9269517
commit
5226058163
@ -1,3 +1,22 @@
|
||||
* Deprecate `config.static_cache_control` in favor of
|
||||
`config.public_file_server.headers`
|
||||
|
||||
*Yuki Nishijima*
|
||||
|
||||
* Add the ability of returning arbitrary headers to ActionDispatch::Static
|
||||
|
||||
Now ActionDispatch::Static can accept HTTP headers so that developers
|
||||
will have control of returning arbitrary headers like
|
||||
'Access-Control-Allow-Origin' when a response is delivered. They can be
|
||||
configured with `#config`:
|
||||
|
||||
config.public_file_server.headers = {
|
||||
"Cache-Control" => "public, max-age=60",
|
||||
"Access-Control-Allow-Origin" => "http://rubyonrails.org"
|
||||
}
|
||||
|
||||
*Yuki Nishijima*
|
||||
|
||||
* Adds`Rack::Utils::ParameterTypeError` and `Rack::Utils::InvalidParameterError`
|
||||
to the rescue_responses hash in `ExceptionWrapper` (Rack recommends
|
||||
integrators serve 400s for both of these).
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
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.
|
||||
# When initialized, it can accept optional HTTP headers, 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+
|
||||
@ -13,12 +13,11 @@ module ActionDispatch
|
||||
# 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, index: 'index')
|
||||
def initialize(root, index: 'index', headers: {})
|
||||
@root = root.chomp('/')
|
||||
@compiled_root = /^#{Regexp.escape(root)}/
|
||||
headers = cache_control && { 'Cache-Control' => cache_control }
|
||||
@file_server = ::Rack::File.new(@root, headers)
|
||||
@index = index
|
||||
@file_server = ::Rack::File.new(@root, headers)
|
||||
@index = index
|
||||
end
|
||||
|
||||
# Takes a path to a file. If the file is found, has valid encoding, and has
|
||||
@ -104,9 +103,16 @@ def gzip_file_path(path)
|
||||
# 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, index: 'index')
|
||||
def initialize(app, path, deprecated_cache_control = :not_set, index: 'index', headers: {})
|
||||
if deprecated_cache_control != :not_set
|
||||
ActiveSupport::Deprecation.warn("The `cache_control` argument is deprecated," \
|
||||
"replaced by `headers: { 'Cache-Control' => #{deprecated_cache_control} }`, " \
|
||||
" and will be removed in Rails 5.1.")
|
||||
headers['Cache-Control'.freeze] = deprecated_cache_control
|
||||
end
|
||||
|
||||
@app = app
|
||||
@file_handler = FileHandler.new(path, cache_control, index: index)
|
||||
@file_handler = FileHandler.new(path, index: index, headers: headers)
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
@ -2,6 +2,10 @@
|
||||
require 'zlib'
|
||||
|
||||
module StaticTests
|
||||
DummyApp = lambda { |env|
|
||||
[200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
|
||||
}
|
||||
|
||||
def setup
|
||||
silence_warnings do
|
||||
@default_internal_encoding = Encoding.default_internal
|
||||
@ -37,7 +41,11 @@ def test_handles_urls_with_ascii_8bit_on_win_31j
|
||||
end
|
||||
|
||||
def test_sets_cache_control
|
||||
response = get("/index.html")
|
||||
app = assert_deprecated do
|
||||
ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60")
|
||||
end
|
||||
response = Rack::MockRequest.new(app).request("GET", "/index.html")
|
||||
|
||||
assert_html "/index.html", response
|
||||
assert_equal "public, max-age=60", response.headers["Cache-Control"]
|
||||
end
|
||||
@ -180,6 +188,21 @@ def test_serves_gzip_files_with_not_modified
|
||||
assert_equal nil, response.headers['Vary']
|
||||
end
|
||||
|
||||
def test_serves_files_with_headers
|
||||
headers = {
|
||||
"Access-Control-Allow-Origin" => 'http://rubyonrails.org',
|
||||
"Cache-Control" => 'public, max-age=60',
|
||||
"X-Custom-Header" => "I'm a teapot"
|
||||
}
|
||||
|
||||
app = ActionDispatch::Static.new(DummyApp, @root, headers: headers)
|
||||
response = Rack::MockRequest.new(app).request("GET", "/foo/bar.html")
|
||||
|
||||
assert_equal 'http://rubyonrails.org', response.headers["Access-Control-Allow-Origin"]
|
||||
assert_equal 'public, max-age=60', response.headers["Cache-Control"]
|
||||
assert_equal "I'm a teapot", response.headers["X-Custom-Header"]
|
||||
end
|
||||
|
||||
# Windows doesn't allow \ / : * ? " < > | in filenames
|
||||
unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
||||
def test_serves_static_file_with_colon
|
||||
@ -230,14 +253,10 @@ def with_static_file(file)
|
||||
end
|
||||
|
||||
class StaticTest < ActiveSupport::TestCase
|
||||
DummyApp = lambda { |env|
|
||||
[200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
|
||||
}
|
||||
|
||||
def setup
|
||||
super
|
||||
@root = "#{FIXTURE_LOAD_PATH}/public"
|
||||
@app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60")
|
||||
@app = ActionDispatch::Static.new(DummyApp, @root, headers: {'Cache-Control' => "public, max-age=60"})
|
||||
end
|
||||
|
||||
def public_path
|
||||
@ -263,7 +282,7 @@ def test_custom_handler_called_when_file_is_outside_root
|
||||
end
|
||||
|
||||
def test_non_default_static_index
|
||||
@app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60", index: "other-index")
|
||||
@app = ActionDispatch::Static.new(DummyApp, @root, index: "other-index")
|
||||
assert_html "/other-index.html", get("/other-index.html")
|
||||
assert_html "/other-index.html", get("/other-index")
|
||||
assert_html "/other-index.html", get("/")
|
||||
@ -280,7 +299,7 @@ class StaticEncodingTest < StaticTest
|
||||
def setup
|
||||
super
|
||||
@root = "#{FIXTURE_LOAD_PATH}/公共"
|
||||
@app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60")
|
||||
@app = ActionDispatch::Static.new(DummyApp, @root, headers: {'Cache-Control' => "public, max-age=60"})
|
||||
end
|
||||
|
||||
def public_path
|
||||
|
@ -11,12 +11,12 @@ class Configuration < ::Rails::Engine::Configuration
|
||||
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
|
||||
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
|
||||
:railties_order, :relative_url_root, :secret_key_base, :secret_token,
|
||||
:serve_static_files, :ssl_options, :static_cache_control, :static_index,
|
||||
:serve_static_files, :ssl_options, :static_index, :public_file_server,
|
||||
:session_options, :time_zone, :reload_classes_only_on_change,
|
||||
:beginning_of_week, :filter_redirect, :x
|
||||
|
||||
attr_writer :log_level
|
||||
attr_reader :encoding, :api_only
|
||||
attr_reader :encoding, :api_only, :static_cache_control
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
@ -27,8 +27,8 @@ def initialize(*)
|
||||
@filter_redirect = []
|
||||
@helpers_paths = []
|
||||
@serve_static_files = true
|
||||
@static_cache_control = nil
|
||||
@static_index = "index"
|
||||
@public_file_server = ActiveSupport::OrderedOptions.new
|
||||
@force_ssl = false
|
||||
@ssl_options = {}
|
||||
@session_store = :cookie_store
|
||||
@ -53,6 +53,14 @@ def initialize(*)
|
||||
@x = Custom.new
|
||||
end
|
||||
|
||||
def static_cache_control=(value)
|
||||
ActiveSupport::Deprecation.warn("static_cache_control is deprecated and will be removed in Rails 5.1. " \
|
||||
"Please use `config.public_file_server.headers = {'Cache-Control' => #{value}} " \
|
||||
"instead.")
|
||||
|
||||
@static_cache_control = value
|
||||
end
|
||||
|
||||
def encoding=(value)
|
||||
@encoding = value
|
||||
silence_warnings do
|
||||
|
@ -18,7 +18,10 @@ def build_stack
|
||||
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
|
||||
|
||||
if config.serve_static_files
|
||||
middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control, index: config.static_index
|
||||
headers = config.public_file_server.headers || {}
|
||||
headers['Cache-Control'.freeze] = config.static_cache_control if config.static_cache_control
|
||||
|
||||
middleware.use ::ActionDispatch::Static, paths["public"].first, index: config.static_index, headers: headers
|
||||
end
|
||||
|
||||
if rack_cache = load_rack_cache
|
||||
|
@ -13,8 +13,10 @@ Rails.application.configure do
|
||||
config.eager_load = false
|
||||
|
||||
# Configure static file server for tests with Cache-Control for performance.
|
||||
config.serve_static_files = true
|
||||
config.static_cache_control = 'public, max-age=3600'
|
||||
config.serve_static_files = true
|
||||
config.public_file_server.headers = {
|
||||
'Cache-Control' => 'public, max-age=3600'
|
||||
}
|
||||
|
||||
# Show full error reports and disable caching.
|
||||
config.consider_all_requests_local = true
|
||||
|
@ -320,6 +320,16 @@ def assert_utf8
|
||||
end
|
||||
end
|
||||
|
||||
test "config.static_cache_control is deprecated" do
|
||||
make_basic_app do |application|
|
||||
assert_deprecated do
|
||||
application.config.static_cache_control = "public, max-age=60"
|
||||
end
|
||||
|
||||
assert_equal application.config.static_cache_control, "public, max-age=60"
|
||||
end
|
||||
end
|
||||
|
||||
test "Use key_generator when secret_key_base is set" do
|
||||
make_basic_app do |application|
|
||||
application.secrets.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
|
||||
|
@ -27,6 +27,23 @@ def teardown
|
||||
assert_not last_response.headers.has_key?('Cache-Control'), "Cache-Control should not be set"
|
||||
end
|
||||
|
||||
test "headers for static files are configurable" do
|
||||
app_file "public/about.html", 'static'
|
||||
add_to_config <<-CONFIG
|
||||
config.public_file_server.headers = {
|
||||
"Access-Control-Allow-Origin" => "http://rubyonrails.org",
|
||||
"Cache-Control" => "public, max-age=60"
|
||||
}
|
||||
CONFIG
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
get '/about.html'
|
||||
|
||||
assert_equal 'http://rubyonrails.org', last_response.headers["Access-Control-Allow-Origin"]
|
||||
assert_equal 'public, max-age=60', last_response.headers["Cache-Control"]
|
||||
end
|
||||
|
||||
test "static_index defaults to 'index'" do
|
||||
app_file "public/index.html", "/index.html"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user