actionpack 5.2.1 → 7.0.2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +6 -11
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -28,6 +28,7 @@ module AbstractController
|
|
28
28
|
else
|
29
29
|
_set_rendered_content_type rendered_format
|
30
30
|
end
|
31
|
+
_set_vary_header
|
31
32
|
self.response_body = rendered_body
|
32
33
|
end
|
33
34
|
|
@@ -55,20 +56,16 @@ module AbstractController
|
|
55
56
|
Mime[:text]
|
56
57
|
end
|
57
58
|
|
58
|
-
DEFAULT_PROTECTED_INSTANCE_VARIABLES =
|
59
|
-
@_action_name @_response_body @_formats @_prefixes
|
60
|
-
)
|
59
|
+
DEFAULT_PROTECTED_INSTANCE_VARIABLES = %i(@_action_name @_response_body @_formats @_prefixes)
|
61
60
|
|
62
61
|
# This method should return a hash with assigns.
|
63
62
|
# You can overwrite this configuration per controller.
|
64
63
|
def view_assigns
|
65
|
-
|
66
|
-
variables = instance_variables
|
64
|
+
variables = instance_variables - _protected_ivars
|
67
65
|
|
68
|
-
variables.
|
69
|
-
variables.each_with_object({}) { |name, hash|
|
66
|
+
variables.each_with_object({}) do |name, hash|
|
70
67
|
hash[name.slice(1, name.length)] = instance_variable_get(name)
|
71
|
-
|
68
|
+
end
|
72
69
|
end
|
73
70
|
|
74
71
|
private
|
@@ -109,6 +106,9 @@ module AbstractController
|
|
109
106
|
def _set_html_content_type # :nodoc:
|
110
107
|
end
|
111
108
|
|
109
|
+
def _set_vary_header # :nodoc:
|
110
|
+
end
|
111
|
+
|
112
112
|
def _set_rendered_content_type(format) # :nodoc:
|
113
113
|
end
|
114
114
|
|
@@ -120,7 +120,7 @@ module AbstractController
|
|
120
120
|
options
|
121
121
|
end
|
122
122
|
|
123
|
-
def _protected_ivars
|
123
|
+
def _protected_ivars
|
124
124
|
DEFAULT_PROTECTED_INSTANCE_VARIABLES
|
125
125
|
end
|
126
126
|
end
|
@@ -1,7 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/html_safe_translation"
|
4
|
+
|
3
5
|
module AbstractController
|
4
6
|
module Translation
|
7
|
+
mattr_accessor :raise_on_missing_translations, default: false
|
8
|
+
|
5
9
|
# Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
|
6
10
|
#
|
7
11
|
# When the given key starts with a period, it will be scoped by the current
|
@@ -10,21 +14,24 @@ module AbstractController
|
|
10
14
|
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
|
11
15
|
# to translate many keys within the same controller / action and gives you a
|
12
16
|
# simple framework for scoping them consistently.
|
13
|
-
def translate(key, options
|
14
|
-
if key
|
17
|
+
def translate(key, **options)
|
18
|
+
if key&.start_with?(".")
|
15
19
|
path = controller_path.tr("/", ".")
|
16
20
|
defaults = [:"#{path}#{key}"]
|
17
21
|
defaults << options[:default] if options[:default]
|
18
22
|
options[:default] = defaults.flatten
|
19
23
|
key = "#{path}.#{action_name}#{key}"
|
20
24
|
end
|
21
|
-
|
25
|
+
|
26
|
+
i18n_raise = options.fetch(:raise, self.raise_on_missing_translations)
|
27
|
+
|
28
|
+
ActiveSupport::HtmlSafeTranslation.translate(key, **options, raise: i18n_raise)
|
22
29
|
end
|
23
30
|
alias :t :translate
|
24
31
|
|
25
32
|
# Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
|
26
|
-
def localize(
|
27
|
-
I18n.localize(
|
33
|
+
def localize(object, **options)
|
34
|
+
I18n.localize(object, **options)
|
28
35
|
end
|
29
36
|
alias :l :localize
|
30
37
|
end
|
@@ -22,12 +22,10 @@ module AbstractController
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def action_methods
|
25
|
-
@action_methods ||=
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
super
|
30
|
-
end
|
25
|
+
@action_methods ||= if _routes
|
26
|
+
super - _routes.named_routes.helper_names
|
27
|
+
else
|
28
|
+
super
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
data/lib/abstract_controller.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "action_pack"
|
4
|
+
require "active_support"
|
4
5
|
require "active_support/rails"
|
5
6
|
require "active_support/i18n"
|
6
7
|
|
7
8
|
module AbstractController
|
8
9
|
extend ActiveSupport::Autoload
|
9
10
|
|
11
|
+
autoload :ActionNotFound, "abstract_controller/base"
|
10
12
|
autoload :Base
|
11
13
|
autoload :Caching
|
12
14
|
autoload :Callbacks
|
@@ -12,7 +12,7 @@ module ActionController
|
|
12
12
|
#
|
13
13
|
# An API Controller is different from a normal controller in the sense that
|
14
14
|
# by default it doesn't include a number of features that are usually required
|
15
|
-
# by browser access only: layouts and templates rendering,
|
15
|
+
# by browser access only: layouts and templates rendering,
|
16
16
|
# flash, assets, and so on. This makes the entire controller stack thinner,
|
17
17
|
# suitable for API applications. It doesn't mean you won't have such
|
18
18
|
# features if you need them: they're all available for you to include in
|
@@ -37,7 +37,7 @@ module ActionController
|
|
37
37
|
# == Renders
|
38
38
|
#
|
39
39
|
# The default API Controller stack includes all renderers, which means you
|
40
|
-
# can use <tt>render :json</tt> and
|
40
|
+
# can use <tt>render :json</tt> and siblings freely in your controllers. Keep
|
41
41
|
# in mind that templates are not going to be rendered, so you need to ensure
|
42
42
|
# your controller is calling either <tt>render</tt> or <tt>redirect_to</tt> in
|
43
43
|
# all actions, otherwise it will return 204 No Content.
|
@@ -93,7 +93,7 @@ module ActionController
|
|
93
93
|
# the ones passed as arguments:
|
94
94
|
#
|
95
95
|
# class MyAPIBaseController < ActionController::Metal
|
96
|
-
# ActionController::API.without_modules(:
|
96
|
+
# ActionController::API.without_modules(:UrlFor).each do |left|
|
97
97
|
# include left
|
98
98
|
# end
|
99
99
|
# end
|
@@ -120,8 +120,9 @@ module ActionController
|
|
120
120
|
BasicImplicitRender,
|
121
121
|
StrongParameters,
|
122
122
|
|
123
|
-
ForceSSL,
|
124
123
|
DataStreaming,
|
124
|
+
DefaultHeaders,
|
125
|
+
Logging,
|
125
126
|
|
126
127
|
# Before callbacks should also be executed as early as possible, so
|
127
128
|
# also include them at the bottom.
|
@@ -78,7 +78,7 @@ module ActionController
|
|
78
78
|
#
|
79
79
|
# You can retrieve it again through the same hash:
|
80
80
|
#
|
81
|
-
# Hello #{session[:person]}
|
81
|
+
# "Hello #{session[:person]}"
|
82
82
|
#
|
83
83
|
# For removing objects from the session, you can either assign a single key to +nil+:
|
84
84
|
#
|
@@ -226,12 +226,14 @@ module ActionController
|
|
226
226
|
FormBuilder,
|
227
227
|
RequestForgeryProtection,
|
228
228
|
ContentSecurityPolicy,
|
229
|
-
|
229
|
+
PermissionsPolicy,
|
230
230
|
Streaming,
|
231
231
|
DataStreaming,
|
232
232
|
HttpAuthentication::Basic::ControllerMethods,
|
233
233
|
HttpAuthentication::Digest::ControllerMethods,
|
234
234
|
HttpAuthentication::Token::ControllerMethods,
|
235
|
+
DefaultHeaders,
|
236
|
+
Logging,
|
235
237
|
|
236
238
|
# Before callbacks should also be executed as early as possible, so
|
237
239
|
# also include them at the bottom.
|
@@ -260,15 +262,10 @@ module ActionController
|
|
260
262
|
@_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
|
261
263
|
)
|
262
264
|
|
263
|
-
def _protected_ivars
|
265
|
+
def _protected_ivars
|
264
266
|
PROTECTED_IVARS
|
265
267
|
end
|
266
|
-
|
267
|
-
def self.make_response!(request)
|
268
|
-
ActionDispatch::Response.create.tap do |res|
|
269
|
-
res.request = request
|
270
|
-
end
|
271
|
-
end
|
268
|
+
private :_protected_ivars
|
272
269
|
|
273
270
|
ActiveSupport.run_load_hooks(:action_controller_base, self)
|
274
271
|
ActiveSupport.run_load_hooks(:action_controller, self)
|
@@ -22,7 +22,6 @@ module ActionController
|
|
22
22
|
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
|
23
23
|
# config.action_controller.cache_store = MyOwnStore.new('parameter')
|
24
24
|
module Caching
|
25
|
-
extend ActiveSupport::Autoload
|
26
25
|
extend ActiveSupport::Concern
|
27
26
|
|
28
27
|
included do
|
@@ -30,7 +29,6 @@ module ActionController
|
|
30
29
|
end
|
31
30
|
|
32
31
|
private
|
33
|
-
|
34
32
|
def instrument_payload(key)
|
35
33
|
{
|
36
34
|
controller: controller_name,
|
@@ -40,7 +38,7 @@ module ActionController
|
|
40
38
|
end
|
41
39
|
|
42
40
|
def instrument_name
|
43
|
-
"action_controller"
|
41
|
+
"action_controller"
|
44
42
|
end
|
45
43
|
end
|
46
44
|
end
|
@@ -11,6 +11,7 @@ module ActionController
|
|
11
11
|
params = payload[:params].except(*INTERNAL_PARAMS)
|
12
12
|
format = payload[:format]
|
13
13
|
format = format.to_s.upcase if format.is_a?(Symbol)
|
14
|
+
format = "*/*" if format.nil?
|
14
15
|
|
15
16
|
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
|
16
17
|
info " Parameters: #{params.inspect}" unless params.empty?
|
@@ -18,16 +19,18 @@ module ActionController
|
|
18
19
|
|
19
20
|
def process_action(event)
|
20
21
|
info do
|
21
|
-
payload
|
22
|
+
payload = event.payload
|
22
23
|
additions = ActionController::Base.log_process_action(payload)
|
23
|
-
|
24
24
|
status = payload[:status]
|
25
|
-
|
26
|
-
|
25
|
+
|
26
|
+
if status.nil? && (exception_class_name = payload[:exception]&.first)
|
27
27
|
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
28
28
|
end
|
29
|
-
|
30
|
-
|
29
|
+
|
30
|
+
additions << "Allocations: #{event.allocations}"
|
31
|
+
|
32
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
33
|
+
message << " (#{additions.join(" | ")})"
|
31
34
|
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
32
35
|
|
33
36
|
message
|
@@ -53,12 +56,13 @@ module ActionController
|
|
53
56
|
def unpermitted_parameters(event)
|
54
57
|
debug do
|
55
58
|
unpermitted_keys = event.payload[:keys]
|
56
|
-
|
59
|
+
display_unpermitted_keys = unpermitted_keys.map { |e| ":#{e}" }.join(", ")
|
60
|
+
context = event.payload[:context].map { |k, v| "#{k}: #{v}" }.join(", ")
|
61
|
+
color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{display_unpermitted_keys}. Context: { #{context} }", RED)
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
60
|
-
%w(write_fragment read_fragment exist_fragment?
|
61
|
-
expire_fragment expire_page write_page).each do |method|
|
65
|
+
%w(write_fragment read_fragment exist_fragment? expire_fragment).each do |method|
|
62
66
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
63
67
|
def #{method}(event)
|
64
68
|
return unless logger.info? && ActionController::Base.enable_fragment_cache_logging
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
require "active_support/core_ext/integer/time"
|
4
5
|
|
5
6
|
module ActionController
|
6
7
|
module ConditionalGet
|
@@ -19,12 +20,12 @@ module ActionController
|
|
19
20
|
# of cached pages.
|
20
21
|
#
|
21
22
|
# class InvoicesController < ApplicationController
|
22
|
-
# etag { current_user
|
23
|
+
# etag { current_user&.id }
|
23
24
|
#
|
24
25
|
# def show
|
25
26
|
# # Etag will differ even for the same invoice when it's viewed by a different current_user
|
26
27
|
# @invoice = Invoice.find(params[:id])
|
27
|
-
# fresh_when
|
28
|
+
# fresh_when etag: @invoice
|
28
29
|
# end
|
29
30
|
# end
|
30
31
|
def etag(&etagger)
|
@@ -56,6 +57,8 @@ module ActionController
|
|
56
57
|
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
57
58
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
58
59
|
# +true+ if you want your application to be cacheable by other devices (proxy caches).
|
60
|
+
# * <tt>:cache_control</tt> When given will overwrite an existing Cache-Control header.
|
61
|
+
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
59
62
|
# * <tt>:template</tt> By default, the template digest for the current
|
60
63
|
# controller/action is included in ETags. If the action renders a
|
61
64
|
# different template, you can include its digest instead. If the action
|
@@ -97,12 +100,22 @@ module ActionController
|
|
97
100
|
# fresh_when(@article, public: true)
|
98
101
|
# end
|
99
102
|
#
|
103
|
+
# When overwriting Cache-Control header:
|
104
|
+
#
|
105
|
+
# def show
|
106
|
+
# @article = Article.find(params[:id])
|
107
|
+
# fresh_when(@article, public: true, cache_control: { no_cache: true })
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# This will set in the response Cache-Control = public, no-cache.
|
111
|
+
#
|
100
112
|
# When rendering a different template than the default controller/action
|
101
113
|
# style, you can indicate which digest to include in the ETag:
|
102
114
|
#
|
103
115
|
# before_action { fresh_when @article, template: 'widgets/show' }
|
104
116
|
#
|
105
|
-
def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
|
117
|
+
def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, cache_control: {}, template: nil)
|
118
|
+
response.cache_control.delete(:no_store)
|
106
119
|
weak_etag ||= etag || object unless strong_etag
|
107
120
|
last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
|
108
121
|
|
@@ -116,6 +129,7 @@ module ActionController
|
|
116
129
|
|
117
130
|
response.last_modified = last_modified if last_modified
|
118
131
|
response.cache_control[:public] = true if public
|
132
|
+
response.cache_control.merge!(cache_control)
|
119
133
|
|
120
134
|
head :not_modified if request.fresh?(response)
|
121
135
|
end
|
@@ -146,6 +160,8 @@ module ActionController
|
|
146
160
|
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
147
161
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
148
162
|
# +true+ if you want your application to be cacheable by other devices (proxy caches).
|
163
|
+
# * <tt>:cache_control</tt> When given will overwrite an existing Cache-Control header.
|
164
|
+
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
149
165
|
# * <tt>:template</tt> By default, the template digest for the current
|
150
166
|
# controller/action is included in ETags. If the action renders a
|
151
167
|
# different template, you can include its digest instead. If the action
|
@@ -181,7 +197,7 @@ module ActionController
|
|
181
197
|
#
|
182
198
|
# You can also pass an object that responds to +maximum+, such as a
|
183
199
|
# collection of active records. In this case +last_modified+ will be set by
|
184
|
-
# calling
|
200
|
+
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
185
201
|
# most recently updated record) and the +etag+ by passing the object itself.
|
186
202
|
#
|
187
203
|
# def index
|
@@ -208,6 +224,21 @@ module ActionController
|
|
208
224
|
# end
|
209
225
|
# end
|
210
226
|
#
|
227
|
+
# When overwriting Cache-Control header:
|
228
|
+
#
|
229
|
+
# def show
|
230
|
+
# @article = Article.find(params[:id])
|
231
|
+
#
|
232
|
+
# if stale?(@article, public: true, cache_control: { no_cache: true })
|
233
|
+
# @statistics = @articles.really_expensive_call
|
234
|
+
# respond_to do |format|
|
235
|
+
# # all the supported formats
|
236
|
+
# end
|
237
|
+
# end
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# This will set in the response Cache-Control = public, no-cache.
|
241
|
+
#
|
211
242
|
# When rendering a different template than the default controller/action
|
212
243
|
# style, you can indicate which digest to include in the ETag:
|
213
244
|
#
|
@@ -230,12 +261,26 @@ module ActionController
|
|
230
261
|
# This method will overwrite an existing Cache-Control header.
|
231
262
|
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
232
263
|
#
|
264
|
+
# HTTP Cache-Control Extensions for Stale Content. See https://tools.ietf.org/html/rfc5861
|
265
|
+
# It helps to cache an asset and serve it while is being revalidated and/or returning with an error.
|
266
|
+
#
|
267
|
+
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds
|
268
|
+
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
|
269
|
+
#
|
270
|
+
# HTTP Cache-Control Extensions other values: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
271
|
+
# Any additional key-value pairs are concatenated onto the `Cache-Control` header in the response:
|
272
|
+
#
|
273
|
+
# expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true
|
274
|
+
#
|
233
275
|
# The method will also ensure an HTTP Date header for client compatibility.
|
234
276
|
def expires_in(seconds, options = {})
|
277
|
+
response.cache_control.delete(:no_store)
|
235
278
|
response.cache_control.merge!(
|
236
279
|
max_age: seconds,
|
237
280
|
public: options.delete(:public),
|
238
|
-
must_revalidate: options.delete(:must_revalidate)
|
281
|
+
must_revalidate: options.delete(:must_revalidate),
|
282
|
+
stale_while_revalidate: options.delete(:stale_while_revalidate),
|
283
|
+
stale_if_error: options.delete(:stale_if_error),
|
239
284
|
)
|
240
285
|
options.delete(:private)
|
241
286
|
|
@@ -266,6 +311,12 @@ module ActionController
|
|
266
311
|
public: public)
|
267
312
|
end
|
268
313
|
|
314
|
+
# Sets an HTTP 1.1 Cache-Control header of <tt>no-store</tt>. This means the
|
315
|
+
# resource may not be stored in any cache.
|
316
|
+
def no_store
|
317
|
+
response.cache_control.replace(no_store: true)
|
318
|
+
end
|
319
|
+
|
269
320
|
private
|
270
321
|
def combine_etags(validator, options)
|
271
322
|
[validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionController
|
3
|
+
module ActionController # :nodoc:
|
4
4
|
module ContentSecurityPolicy
|
5
5
|
# TODO: Documentation
|
6
6
|
extend ActiveSupport::Concern
|
@@ -36,7 +36,6 @@ module ActionController #:nodoc:
|
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
|
-
|
40
39
|
def content_security_policy?
|
41
40
|
request.content_security_policy
|
42
41
|
end
|
@@ -46,7 +45,7 @@ module ActionController #:nodoc:
|
|
46
45
|
end
|
47
46
|
|
48
47
|
def current_content_security_policy
|
49
|
-
request.content_security_policy
|
48
|
+
request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new
|
50
49
|
end
|
51
50
|
end
|
52
51
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionController
|
3
|
+
module ActionController # :nodoc:
|
4
4
|
module Cookies
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
@@ -9,7 +9,9 @@ module ActionController #:nodoc:
|
|
9
9
|
end
|
10
10
|
|
11
11
|
private
|
12
|
-
|
12
|
+
# The cookies for the current request. See ActionDispatch::Cookies for
|
13
|
+
# more information.
|
14
|
+
def cookies # :doc:
|
13
15
|
request.cookie_jar
|
14
16
|
end
|
15
17
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "action_controller/metal/exceptions"
|
4
|
+
require "action_dispatch/http/content_disposition"
|
4
5
|
|
5
|
-
module ActionController
|
6
|
+
module ActionController # :nodoc:
|
6
7
|
# Methods for sending arbitrary data and for streaming files to the browser,
|
7
8
|
# instead of rendering.
|
8
9
|
module DataStreaming
|
@@ -10,8 +11,8 @@ module ActionController #:nodoc:
|
|
10
11
|
|
11
12
|
include ActionController::Rendering
|
12
13
|
|
13
|
-
DEFAULT_SEND_FILE_TYPE = "application/octet-stream"
|
14
|
-
DEFAULT_SEND_FILE_DISPOSITION = "attachment"
|
14
|
+
DEFAULT_SEND_FILE_TYPE = "application/octet-stream" # :nodoc:
|
15
|
+
DEFAULT_SEND_FILE_DISPOSITION = "attachment" # :nodoc:
|
15
16
|
|
16
17
|
private
|
17
18
|
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
@@ -52,7 +53,7 @@ module ActionController #:nodoc:
|
|
52
53
|
#
|
53
54
|
# Show a 404 page in the browser:
|
54
55
|
#
|
55
|
-
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
|
56
|
+
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
|
56
57
|
#
|
57
58
|
# Read about the other Content-* HTTP headers if you'd like to
|
58
59
|
# provide the user with more information (such as Content-Description) in
|
@@ -65,7 +66,7 @@ module ActionController #:nodoc:
|
|
65
66
|
# https://www.mnot.net/cache_docs/ for an overview of web caching and
|
66
67
|
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
67
68
|
# for the Cache-Control header spec.
|
68
|
-
def send_file(path, options = {})
|
69
|
+
def send_file(path, options = {}) # :doc:
|
69
70
|
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
|
70
71
|
|
71
72
|
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
@@ -105,7 +106,7 @@ module ActionController #:nodoc:
|
|
105
106
|
# send_data image.data, type: image.content_type, disposition: 'inline'
|
106
107
|
#
|
107
108
|
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
108
|
-
def send_data(data, options = {})
|
109
|
+
def send_data(data, options = {}) # :doc:
|
109
110
|
send_file_headers! options
|
110
111
|
render options.slice(:status, :content_type).merge(body: data)
|
111
112
|
end
|
@@ -132,21 +133,11 @@ module ActionController #:nodoc:
|
|
132
133
|
end
|
133
134
|
|
134
135
|
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
|
135
|
-
|
136
|
-
|
137
|
-
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
138
|
-
headers["Content-Disposition"] = disposition
|
136
|
+
if disposition
|
137
|
+
headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: options[:filename])
|
139
138
|
end
|
140
139
|
|
141
140
|
headers["Content-Transfer-Encoding"] = "binary"
|
142
|
-
|
143
|
-
# Fix a problem with IE 6.0 on opening downloaded files:
|
144
|
-
# If Cache-Control: no-cache is set (which Rails does by default),
|
145
|
-
# IE removes the file it just downloaded from its cache immediately
|
146
|
-
# after it displays the "open/save" dialog, which means that if you
|
147
|
-
# hit "open" the file isn't there anymore when the application that
|
148
|
-
# is called for handling the download is run, so let's workaround that
|
149
|
-
response.cache_control[:public] ||= false
|
150
141
|
end
|
151
142
|
end
|
152
143
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
# Allows configuring default headers that will be automatically merged into
|
5
|
+
# each response.
|
6
|
+
module DefaultHeaders
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def make_response!(request)
|
11
|
+
ActionDispatch::Response.create.tap do |res|
|
12
|
+
res.request = request
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -26,10 +26,8 @@ module ActionController
|
|
26
26
|
included do
|
27
27
|
class_attribute :etag_with_template_digest, default: true
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
determine_template_etag(options) if etag_with_template_digest
|
32
|
-
end
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -46,12 +44,12 @@ module ActionController
|
|
46
44
|
# template digest from the ETag.
|
47
45
|
def pick_template_for_etag(options)
|
48
46
|
unless options[:template] == false
|
49
|
-
options[:template] ||
|
47
|
+
options[:template] || lookup_context.find_all(action_name, _prefixes).first&.virtual_path
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
53
51
|
def lookup_and_digest_template(template)
|
54
|
-
ActionView::Digestor.digest name: template, finder: lookup_context
|
52
|
+
ActionView::Digestor.digest name: template, format: nil, finder: lookup_context
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|