actionpack 6.1.7 → 7.0.4.1
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 +269 -406
- data/MIT-LICENSE +1 -0
- data/README.rdoc +2 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +13 -26
- data/lib/abstract_controller/caching/fragments.rb +2 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +21 -7
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/translation.rb +3 -2
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +6 -6
- data/lib/action_controller/base.rb +5 -4
- data/lib/action_controller/form_builder.rb +2 -2
- data/lib/action_controller/log_subscriber.rb +4 -3
- data/lib/action_controller/metal/conditional_get.rb +39 -2
- data/lib/action_controller/metal/content_security_policy.rb +36 -2
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -13
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/helpers.rb +2 -2
- data/lib/action_controller/metal/http_authentication.rb +66 -39
- data/lib/action_controller/metal/instrumentation.rb +57 -52
- data/lib/action_controller/metal/live.rb +43 -2
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +20 -11
- data/lib/action_controller/metal/permissions_policy.rb +19 -28
- data/lib/action_controller/metal/redirecting.rb +93 -18
- data/lib/action_controller/metal/renderers.rb +10 -11
- data/lib/action_controller/metal/rendering.rb +8 -8
- data/lib/action_controller/metal/request_forgery_protection.rb +78 -29
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +6 -8
- data/lib/action_controller/metal/strong_parameters.rb +100 -54
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +3 -3
- data/lib/action_controller/metal.rb +10 -13
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/renderer.rb +1 -1
- data/lib/action_controller/test_case.rb +28 -7
- data/lib/action_controller.rb +2 -5
- data/lib/action_dispatch/http/cache.rb +14 -7
- data/lib/action_dispatch/http/content_security_policy.rb +108 -35
- data/lib/action_dispatch/http/filter_parameters.rb +5 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +5 -5
- data/lib/action_dispatch/http/permissions_policy.rb +17 -1
- data/lib/action_dispatch/http/request.rb +12 -21
- data/lib/action_dispatch/http/response.rb +3 -16
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +22 -13
- data/lib/action_dispatch/journey/route.rb +6 -13
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +1 -1
- data/lib/action_dispatch/journey/routes.rb +3 -3
- 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/middleware/actionable_exceptions.rb +0 -1
- data/lib/action_dispatch/middleware/cookies.rb +42 -27
- data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +17 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +1 -12
- data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +7 -9
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +2 -6
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +3 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +2 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +43 -13
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +59 -83
- data/lib/action_dispatch/routing/redirection.rb +5 -2
- data/lib/action_dispatch/routing/route_set.rb +17 -7
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +4 -5
- data/lib/action_dispatch/routing.rb +5 -6
- data/lib/action_dispatch/system_test_case.rb +5 -5
- data/lib/action_dispatch/system_testing/browser.rb +2 -12
- data/lib/action_dispatch/system_testing/driver.rb +35 -11
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
- data/lib/action_dispatch/testing/assertions.rb +2 -5
- data/lib/action_dispatch/testing/integration.rb +6 -8
- data/lib/action_dispatch/testing/test_process.rb +3 -29
- data/lib/action_dispatch/testing/test_response.rb +20 -2
- data/lib/action_dispatch.rb +1 -0
- data/lib/action_pack/gem_version.rb +5 -5
- data/lib/action_pack/version.rb +1 -1
- metadata +16 -15
data/MIT-LICENSE
CHANGED
@@ -18,3 +18,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.rdoc
CHANGED
@@ -2,9 +2,8 @@
|
|
2
2
|
|
3
3
|
Action Pack is a framework for handling and responding to web requests. It
|
4
4
|
provides mechanisms for *routing* (mapping request URLs to actions), defining
|
5
|
-
*controllers* that implement actions, and generating responses
|
6
|
-
|
7
|
-
provides the view and controller layers in the MVC paradigm.
|
5
|
+
*controllers* that implement actions, and generating responses. In short, Action Pack
|
6
|
+
provides the controller layer in the MVC paradigm.
|
8
7
|
|
9
8
|
It consists of several modules:
|
10
9
|
|
@@ -9,35 +9,21 @@ require "active_support/core_ext/module/attr_internal"
|
|
9
9
|
module AbstractController
|
10
10
|
# Raised when a non-existing controller action is triggered.
|
11
11
|
class ActionNotFound < StandardError
|
12
|
-
attr_reader :controller, :action
|
13
|
-
|
12
|
+
attr_reader :controller, :action # :nodoc:
|
13
|
+
|
14
|
+
def initialize(message = nil, controller = nil, action = nil) # :nodoc:
|
14
15
|
@controller = controller
|
15
16
|
@action = action
|
16
17
|
super(message)
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
@error = error
|
22
|
-
end
|
23
|
-
|
24
|
-
def corrections
|
25
|
-
if @error.action
|
26
|
-
maybe_these = @error.controller.class.action_methods
|
20
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
21
|
+
include DidYouMean::Correctable # :nodoc:
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
}.reverse.first(4)
|
31
|
-
else
|
32
|
-
[]
|
33
|
-
end
|
23
|
+
def corrections # :nodoc:
|
24
|
+
@corrections ||= DidYouMean::SpellChecker.new(dictionary: controller.class.action_methods).correct(action)
|
34
25
|
end
|
35
26
|
end
|
36
|
-
|
37
|
-
# We may not have DYM, and DYM might not let us register error handlers
|
38
|
-
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
39
|
-
DidYouMean.correct_error(self, Correction)
|
40
|
-
end
|
41
27
|
end
|
42
28
|
|
43
29
|
# AbstractController::Base is a low-level API. Nobody should be
|
@@ -164,13 +150,14 @@ module AbstractController
|
|
164
150
|
|
165
151
|
process_action(action_name, *args)
|
166
152
|
end
|
153
|
+
ruby2_keywords(:process)
|
167
154
|
|
168
|
-
# Delegates to the class' ::controller_path
|
155
|
+
# Delegates to the class's ::controller_path.
|
169
156
|
def controller_path
|
170
157
|
self.class.controller_path
|
171
158
|
end
|
172
159
|
|
173
|
-
# Delegates to the class' ::action_methods
|
160
|
+
# Delegates to the class's ::action_methods.
|
174
161
|
def action_methods
|
175
162
|
self.class.action_methods
|
176
163
|
end
|
@@ -191,7 +178,7 @@ module AbstractController
|
|
191
178
|
|
192
179
|
# Tests if a response body is set. Used to determine if the
|
193
180
|
# +process_action+ callback needs to be terminated in
|
194
|
-
#
|
181
|
+
# AbstractController::Callbacks.
|
195
182
|
def performed?
|
196
183
|
response_body
|
197
184
|
end
|
@@ -224,8 +211,8 @@ module AbstractController
|
|
224
211
|
#
|
225
212
|
# Notice that the first argument is the method to be dispatched
|
226
213
|
# which is *not* necessarily the same as the action name.
|
227
|
-
def process_action(
|
228
|
-
send_action(
|
214
|
+
def process_action(...)
|
215
|
+
send_action(...)
|
229
216
|
end
|
230
217
|
|
231
218
|
# Actually call the method associated with the action. Override
|
@@ -142,8 +142,8 @@ module AbstractController
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
-
def instrument_fragment_cache(name, key) # :nodoc:
|
146
|
-
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key))
|
145
|
+
def instrument_fragment_cache(name, key, &block) # :nodoc:
|
146
|
+
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key), &block)
|
147
147
|
end
|
148
148
|
end
|
149
149
|
end
|
@@ -35,12 +35,18 @@ module AbstractController
|
|
35
35
|
skip_after_callbacks_if_terminated: true
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
run_callbacks(:process_action) do
|
42
|
-
super
|
38
|
+
class ActionFilter # :nodoc:
|
39
|
+
def initialize(actions)
|
40
|
+
@actions = Array(actions).map(&:to_s).to_set
|
43
41
|
end
|
42
|
+
|
43
|
+
def match?(controller)
|
44
|
+
@actions.include?(controller.action_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
alias after match?
|
48
|
+
alias before match?
|
49
|
+
alias around match?
|
44
50
|
end
|
45
51
|
|
46
52
|
module ClassMethods
|
@@ -70,8 +76,7 @@ module AbstractController
|
|
70
76
|
|
71
77
|
def _normalize_callback_option(options, from, to) # :nodoc:
|
72
78
|
if from = options.delete(from)
|
73
|
-
|
74
|
-
from = proc { |c| _from.include? c.action_name }
|
79
|
+
from = ActionFilter.new(from)
|
75
80
|
options[to] = Array(options[to]).unshift(from)
|
76
81
|
end
|
77
82
|
end
|
@@ -220,5 +225,14 @@ module AbstractController
|
|
220
225
|
alias_method :"append_#{callback}_action", :"#{callback}_action"
|
221
226
|
end
|
222
227
|
end
|
228
|
+
|
229
|
+
private
|
230
|
+
# Override <tt>AbstractController::Base#process_action</tt> to run the
|
231
|
+
# <tt>process_action</tt> callbacks around the normal behavior.
|
232
|
+
def process_action(...)
|
233
|
+
run_callbacks(:process_action) do
|
234
|
+
super
|
235
|
+
end
|
236
|
+
end
|
223
237
|
end
|
224
238
|
end
|
@@ -10,7 +10,7 @@ module AbstractController
|
|
10
10
|
def #{sym}(*args, &block)
|
11
11
|
custom(Mime[:#{sym}], *args, &block)
|
12
12
|
end
|
13
|
-
ruby2_keywords(:#{sym})
|
13
|
+
ruby2_keywords(:#{sym})
|
14
14
|
RUBY
|
15
15
|
end
|
16
16
|
|
@@ -39,6 +39,6 @@ module AbstractController
|
|
39
39
|
super
|
40
40
|
end
|
41
41
|
end
|
42
|
-
ruby2_keywords(:method_missing)
|
42
|
+
ruby2_keywords(:method_missing)
|
43
43
|
end
|
44
44
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/dependencies"
|
4
|
+
require "active_support/core_ext/name_error"
|
4
5
|
|
5
6
|
module AbstractController
|
6
7
|
module Helpers
|
@@ -83,11 +84,11 @@ module AbstractController
|
|
83
84
|
file, line = location.path, location.lineno
|
84
85
|
|
85
86
|
methods.each do |method|
|
86
|
-
_helpers_for_modification.class_eval
|
87
|
+
_helpers_for_modification.class_eval <<~ruby_eval, file, line
|
87
88
|
def #{method}(*args, &block) # def current_user(*args, &block)
|
88
89
|
controller.send(:'#{method}', *args, &block) # controller.send(:'current_user', *args, &block)
|
89
90
|
end # end
|
90
|
-
ruby2_keywords(:'#{method}')
|
91
|
+
ruby2_keywords(:'#{method}')
|
91
92
|
ruby_eval
|
92
93
|
end
|
93
94
|
end
|
@@ -109,7 +110,7 @@ module AbstractController
|
|
109
110
|
# The last two assume that <tt>"foo".camelize</tt> returns "Foo".
|
110
111
|
#
|
111
112
|
# When strings or symbols are passed, the method finds the actual module
|
112
|
-
# object using
|
113
|
+
# object using String#constantize. Therefore, if the module has not been
|
113
114
|
# yet loaded, it has to be autoloadable, which is normally the case.
|
114
115
|
#
|
115
116
|
# Namespaces are supported. The following calls include +Foo::BarHelper+:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/
|
3
|
+
require "active_support/html_safe_translation"
|
4
4
|
|
5
5
|
module AbstractController
|
6
6
|
module Translation
|
@@ -24,7 +24,8 @@ module AbstractController
|
|
24
24
|
end
|
25
25
|
|
26
26
|
i18n_raise = options.fetch(:raise, self.raise_on_missing_translations)
|
27
|
-
|
27
|
+
|
28
|
+
ActiveSupport::HtmlSafeTranslation.translate(key, **options, raise: i18n_raise)
|
28
29
|
end
|
29
30
|
alias :t :translate
|
30
31
|
|
@@ -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
|
@@ -5,7 +5,7 @@ require "action_controller"
|
|
5
5
|
require "action_controller/log_subscriber"
|
6
6
|
|
7
7
|
module ActionController
|
8
|
-
# API Controller is a lightweight version of
|
8
|
+
# API Controller is a lightweight version of ActionController::Base,
|
9
9
|
# created for applications that don't require all functionalities that a complete
|
10
10
|
# \Rails controller provides, allowing you to create controllers with just the
|
11
11
|
# features that you need for API only applications.
|
@@ -32,12 +32,12 @@ module ActionController
|
|
32
32
|
# end
|
33
33
|
#
|
34
34
|
# Request, response, and parameters objects all work the exact same way as
|
35
|
-
#
|
35
|
+
# ActionController::Base.
|
36
36
|
#
|
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.
|
@@ -51,7 +51,7 @@ module ActionController
|
|
51
51
|
#
|
52
52
|
# Redirects are used to move from one action to another. You can use the
|
53
53
|
# <tt>redirect_to</tt> method in your controllers in the same way as in
|
54
|
-
#
|
54
|
+
# ActionController::Base. For example:
|
55
55
|
#
|
56
56
|
# def create
|
57
57
|
# redirect_to root_url and return if not_authorized?
|
@@ -61,7 +61,7 @@ module ActionController
|
|
61
61
|
# == Adding New Behavior
|
62
62
|
#
|
63
63
|
# In some scenarios you may want to add back some functionality provided by
|
64
|
-
#
|
64
|
+
# ActionController::Base that is not present by default in
|
65
65
|
# <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
|
66
66
|
# module gives you the <tt>respond_to</tt> method. Adding it is quite simple,
|
67
67
|
# you just need to include the module in a specific controller or in
|
@@ -83,7 +83,7 @@ module ActionController
|
|
83
83
|
# end
|
84
84
|
# end
|
85
85
|
#
|
86
|
-
# Make sure to check the modules included in
|
86
|
+
# Make sure to check the modules included in ActionController::Base
|
87
87
|
# if you want to use any other functionality that is not provided
|
88
88
|
# by <tt>ActionController::API</tt> out of the box.
|
89
89
|
class API < Metal
|
@@ -87,10 +87,11 @@ module ActionController
|
|
87
87
|
#
|
88
88
|
# or you can remove the entire session with +reset_session+.
|
89
89
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
90
|
+
# By default, sessions are stored in an encrypted browser cookie (see
|
91
|
+
# ActionDispatch::Session::CookieStore). Thus the user will not be able to
|
92
|
+
# read or edit the session data. However, the user can keep a copy of the
|
93
|
+
# cookie even after it has expired, so you should avoid storing sensitive
|
94
|
+
# information in cookie-based sessions.
|
94
95
|
#
|
95
96
|
# == Responses
|
96
97
|
#
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActionController
|
4
4
|
# Override the default form builder for all views rendered by this
|
5
5
|
# controller and any of its descendants. Accepts a subclass of
|
6
|
-
#
|
6
|
+
# ActionView::Helpers::FormBuilder.
|
7
7
|
#
|
8
8
|
# For example, given a form builder:
|
9
9
|
#
|
@@ -36,7 +36,7 @@ module ActionController
|
|
36
36
|
# in the views rendered by this controller and its subclasses.
|
37
37
|
#
|
38
38
|
# ==== Parameters
|
39
|
-
# * <tt>builder</tt> - Default form builder, an instance of
|
39
|
+
# * <tt>builder</tt> - Default form builder, an instance of ActionView::Helpers::FormBuilder
|
40
40
|
def default_form_builder(builder)
|
41
41
|
self._default_form_builder = builder
|
42
42
|
end
|
@@ -56,12 +56,13 @@ module ActionController
|
|
56
56
|
def unpermitted_parameters(event)
|
57
57
|
debug do
|
58
58
|
unpermitted_keys = event.payload[:keys]
|
59
|
-
|
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)
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
|
-
%w(write_fragment read_fragment exist_fragment?
|
64
|
-
expire_fragment expire_page write_page).each do |method|
|
65
|
+
%w(write_fragment read_fragment exist_fragment? expire_fragment).each do |method|
|
65
66
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
66
67
|
def #{method}(event)
|
67
68
|
return unless logger.info? && ActionController::Base.enable_fragment_cache_logging
|
@@ -57,6 +57,8 @@ module ActionController
|
|
57
57
|
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
58
58
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
59
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.
|
60
62
|
# * <tt>:template</tt> By default, the template digest for the current
|
61
63
|
# controller/action is included in ETags. If the action renders a
|
62
64
|
# different template, you can include its digest instead. If the action
|
@@ -98,12 +100,22 @@ module ActionController
|
|
98
100
|
# fresh_when(@article, public: true)
|
99
101
|
# end
|
100
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
|
+
#
|
101
112
|
# When rendering a different template than the default controller/action
|
102
113
|
# style, you can indicate which digest to include in the ETag:
|
103
114
|
#
|
104
115
|
# before_action { fresh_when @article, template: 'widgets/show' }
|
105
116
|
#
|
106
|
-
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)
|
107
119
|
weak_etag ||= etag || object unless strong_etag
|
108
120
|
last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
|
109
121
|
|
@@ -117,6 +129,7 @@ module ActionController
|
|
117
129
|
|
118
130
|
response.last_modified = last_modified if last_modified
|
119
131
|
response.cache_control[:public] = true if public
|
132
|
+
response.cache_control.merge!(cache_control)
|
120
133
|
|
121
134
|
head :not_modified if request.fresh?(response)
|
122
135
|
end
|
@@ -147,6 +160,8 @@ module ActionController
|
|
147
160
|
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
148
161
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
149
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.
|
150
165
|
# * <tt>:template</tt> By default, the template digest for the current
|
151
166
|
# controller/action is included in ETags. If the action renders a
|
152
167
|
# different template, you can include its digest instead. If the action
|
@@ -209,6 +224,21 @@ module ActionController
|
|
209
224
|
# end
|
210
225
|
# end
|
211
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
|
+
#
|
212
242
|
# When rendering a different template than the default controller/action
|
213
243
|
# style, you can indicate which digest to include in the ETag:
|
214
244
|
#
|
@@ -238,12 +268,13 @@ module ActionController
|
|
238
268
|
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
|
239
269
|
#
|
240
270
|
# HTTP Cache-Control Extensions other values: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
241
|
-
# Any additional key-value pairs are concatenated onto the
|
271
|
+
# Any additional key-value pairs are concatenated onto the Cache-Control header in the response:
|
242
272
|
#
|
243
273
|
# expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true
|
244
274
|
#
|
245
275
|
# The method will also ensure an HTTP Date header for client compatibility.
|
246
276
|
def expires_in(seconds, options = {})
|
277
|
+
response.cache_control.delete(:no_store)
|
247
278
|
response.cache_control.merge!(
|
248
279
|
max_age: seconds,
|
249
280
|
public: options.delete(:public),
|
@@ -280,6 +311,12 @@ module ActionController
|
|
280
311
|
public: public)
|
281
312
|
end
|
282
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
|
+
|
283
320
|
private
|
284
321
|
def combine_etags(validator, options)
|
285
322
|
[validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionController
|
3
|
+
module ActionController # :nodoc:
|
4
4
|
module ContentSecurityPolicy
|
5
|
-
# TODO: Documentation
|
6
5
|
extend ActiveSupport::Concern
|
7
6
|
|
8
7
|
include AbstractController::Helpers
|
@@ -14,6 +13,29 @@ module ActionController #:nodoc:
|
|
14
13
|
end
|
15
14
|
|
16
15
|
module ClassMethods
|
16
|
+
# Overrides parts of the globally configured Content-Security-Policy
|
17
|
+
# header:
|
18
|
+
#
|
19
|
+
# class PostsController < ApplicationController
|
20
|
+
# content_security_policy do |policy|
|
21
|
+
# policy.base_uri "https://www.example.com"
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Options can be passed similar to +before_action+. For example, pass
|
26
|
+
# <tt>only: :index</tt> to override the header on the index action only:
|
27
|
+
#
|
28
|
+
# class PostsController < ApplicationController
|
29
|
+
# content_security_policy(only: :index) do |policy|
|
30
|
+
# policy.default_src :self, :https
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Pass +false+ to remove the Content-Security-Policy header:
|
35
|
+
#
|
36
|
+
# class PostsController < ApplicationController
|
37
|
+
# content_security_policy false, only: :index
|
38
|
+
# end
|
17
39
|
def content_security_policy(enabled = true, **options, &block)
|
18
40
|
before_action(options) do
|
19
41
|
if block_given?
|
@@ -28,6 +50,18 @@ module ActionController #:nodoc:
|
|
28
50
|
end
|
29
51
|
end
|
30
52
|
|
53
|
+
# Overrides the globally configured Content-Security-Policy-Report-Only
|
54
|
+
# header:
|
55
|
+
#
|
56
|
+
# class PostsController < ApplicationController
|
57
|
+
# content_security_policy_report_only only: :index
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# Pass +false+ to remove the Content-Security-Policy-Report-Only header:
|
61
|
+
#
|
62
|
+
# class PostsController < ApplicationController
|
63
|
+
# content_security_policy_report_only false, only: :index
|
64
|
+
# end
|
31
65
|
def content_security_policy_report_only(report_only = true, **options)
|
32
66
|
before_action(options) do
|
33
67
|
request.content_security_policy_report_only = report_only
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "action_controller/metal/exceptions"
|
4
4
|
require "action_dispatch/http/content_disposition"
|
5
5
|
|
6
|
-
module ActionController
|
6
|
+
module ActionController # :nodoc:
|
7
7
|
# Methods for sending arbitrary data and for streaming files to the browser,
|
8
8
|
# instead of rendering.
|
9
9
|
module DataStreaming
|
@@ -11,8 +11,8 @@ module ActionController #:nodoc:
|
|
11
11
|
|
12
12
|
include ActionController::Rendering
|
13
13
|
|
14
|
-
DEFAULT_SEND_FILE_TYPE = "application/octet-stream"
|
15
|
-
DEFAULT_SEND_FILE_DISPOSITION = "attachment"
|
14
|
+
DEFAULT_SEND_FILE_TYPE = "application/octet-stream" # :nodoc:
|
15
|
+
DEFAULT_SEND_FILE_DISPOSITION = "attachment" # :nodoc:
|
16
16
|
|
17
17
|
private
|
18
18
|
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
@@ -66,7 +66,7 @@ module ActionController #:nodoc:
|
|
66
66
|
# https://www.mnot.net/cache_docs/ for an overview of web caching and
|
67
67
|
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
68
68
|
# for the Cache-Control header spec.
|
69
|
-
def send_file(path, options = {})
|
69
|
+
def send_file(path, options = {}) # :doc:
|
70
70
|
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
|
71
71
|
|
72
72
|
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
@@ -106,7 +106,7 @@ module ActionController #:nodoc:
|
|
106
106
|
# send_data image.data, type: image.content_type, disposition: 'inline'
|
107
107
|
#
|
108
108
|
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
109
|
-
def send_data(data, options = {})
|
109
|
+
def send_data(data, options = {}) # :doc:
|
110
110
|
send_file_headers! options
|
111
111
|
render options.slice(:status, :content_type).merge(body: data)
|
112
112
|
end
|
@@ -138,14 +138,6 @@ module ActionController #:nodoc:
|
|
138
138
|
end
|
139
139
|
|
140
140
|
headers["Content-Transfer-Encoding"] = "binary"
|
141
|
-
|
142
|
-
# Fix a problem with IE 6.0 on opening downloaded files:
|
143
|
-
# If Cache-Control: no-cache is set (which Rails does by default),
|
144
|
-
# IE removes the file it just downloaded from its cache immediately
|
145
|
-
# after it displays the "open/save" dialog, which means that if you
|
146
|
-
# hit "open" the file isn't there anymore when the application that
|
147
|
-
# is called for handling the download is run, so let's workaround that
|
148
|
-
response.cache_control[:public] ||= false
|
149
141
|
end
|
150
142
|
end
|
151
143
|
end
|