actionpack 7.0.4 → 7.1.3.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 +397 -269
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/abstract_controller/base.rb +20 -11
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +75 -28
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +9 -6
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +6 -4
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +121 -123
- data/lib/action_controller/metal/content_security_policy.rb +5 -5
- data/lib/action_controller/metal/data_streaming.rb +20 -18
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +8 -0
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +17 -8
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +25 -1
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +2 -2
- data/lib/action_controller/metal/redirecting.rb +29 -8
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +114 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
- data/lib/action_controller/metal/rescue.rb +6 -3
- data/lib/action_controller/metal/streaming.rb +71 -31
- data/lib/action_controller/metal/strong_parameters.rb +158 -101
- data/lib/action_controller/metal/url_for.rb +9 -4
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +24 -10
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +15 -5
- data/lib/action_controller.rb +8 -1
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +9 -11
- data/lib/action_dispatch/http/content_security_policy.rb +14 -9
- data/lib/action_dispatch/http/filter_parameters.rb +14 -28
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- data/lib/action_dispatch/http/mime_type.rb +35 -12
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +38 -23
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +63 -30
- data/lib/action_dispatch/http/response.rb +80 -63
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +9 -8
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +108 -117
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +21 -20
- data/lib/action_dispatch/middleware/request_id.rb +4 -2
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +25 -18
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +14 -10
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -41
- data/lib/action_dispatch/railtie.rb +14 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +58 -24
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +52 -22
- data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
- data/lib/action_dispatch/routing/url_for.rb +26 -22
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +20 -19
- data/lib/action_dispatch/system_testing/driver.rb +14 -22
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +37 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +65 -29
@@ -8,7 +8,10 @@ module ActionController
|
|
8
8
|
return unless logger.info?
|
9
9
|
|
10
10
|
payload = event.payload
|
11
|
-
params
|
11
|
+
params = {}
|
12
|
+
payload[:params].each_pair do |k, v|
|
13
|
+
params[k] = v unless INTERNAL_PARAMS.include?(k)
|
14
|
+
end
|
12
15
|
format = payload[:format]
|
13
16
|
format = format.to_s.upcase if format.is_a?(Symbol)
|
14
17
|
format = "*/*" if format.nil?
|
@@ -16,6 +19,7 @@ module ActionController
|
|
16
19
|
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
|
17
20
|
info " Parameters: #{params.inspect}" unless params.empty?
|
18
21
|
end
|
22
|
+
subscribe_log_level :start_processing, :info
|
19
23
|
|
20
24
|
def process_action(event)
|
21
25
|
info do
|
@@ -29,29 +33,34 @@ module ActionController
|
|
29
33
|
|
30
34
|
additions << "Allocations: #{event.allocations}"
|
31
35
|
|
32
|
-
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
33
|
-
|
36
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms" \
|
37
|
+
" (#{additions.join(" | ")})"
|
34
38
|
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
35
39
|
|
36
40
|
message
|
37
41
|
end
|
38
42
|
end
|
43
|
+
subscribe_log_level :process_action, :info
|
39
44
|
|
40
45
|
def halted_callback(event)
|
41
46
|
info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
|
42
47
|
end
|
48
|
+
subscribe_log_level :halted_callback, :info
|
43
49
|
|
44
50
|
def send_file(event)
|
45
51
|
info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
|
46
52
|
end
|
53
|
+
subscribe_log_level :send_file, :info
|
47
54
|
|
48
55
|
def redirect_to(event)
|
49
56
|
info { "Redirected to #{event.payload[:location]}" }
|
50
57
|
end
|
58
|
+
subscribe_log_level :redirect_to, :info
|
51
59
|
|
52
60
|
def send_data(event)
|
53
61
|
info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
|
54
62
|
end
|
63
|
+
subscribe_log_level :send_data, :info
|
55
64
|
|
56
65
|
def unpermitted_parameters(event)
|
57
66
|
debug do
|
@@ -61,15 +70,18 @@ module ActionController
|
|
61
70
|
color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{display_unpermitted_keys}. Context: { #{context} }", RED)
|
62
71
|
end
|
63
72
|
end
|
73
|
+
subscribe_log_level :unpermitted_parameters, :debug
|
64
74
|
|
65
75
|
%w(write_fragment read_fragment exist_fragment? expire_fragment).each do |method|
|
66
76
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
77
|
+
# frozen_string_literal: true
|
67
78
|
def #{method}(event)
|
68
|
-
return unless
|
79
|
+
return unless ActionController::Base.enable_fragment_cache_logging
|
69
80
|
key = ActiveSupport::Cache.expand_cache_key(event.payload[:key] || event.payload[:path])
|
70
81
|
human_name = #{method.to_s.humanize.inspect}
|
71
82
|
info("\#{human_name} \#{key} (\#{event.duration.round(1)}ms)")
|
72
83
|
end
|
84
|
+
subscribe_log_level :#{method}, :info
|
73
85
|
METHOD
|
74
86
|
end
|
75
87
|
|
@@ -33,86 +33,97 @@ module ActionController
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
# Sets the +etag+, +last_modified+, or both on the response and renders a
|
36
|
+
# Sets the +etag+, +last_modified+, or both on the response, and renders a
|
37
37
|
# <tt>304 Not Modified</tt> response if the request is already fresh.
|
38
38
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# +:weak_etag+ option.
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
39
|
+
# ==== Options
|
40
|
+
#
|
41
|
+
# [+:etag+]
|
42
|
+
# Sets a "weak" ETag validator on the response. See the +:weak_etag+ option.
|
43
|
+
# [+:weak_etag+]
|
44
|
+
# Sets a "weak" ETag validator on the response. Requests that specify an
|
45
|
+
# +If-None-Match+ header may receive a <tt>304 Not Modified</tt> response
|
46
|
+
# if the ETag matches exactly.
|
47
|
+
#
|
48
|
+
# A weak ETag indicates semantic equivalence, not byte-for-byte equality,
|
49
|
+
# so they're good for caching HTML pages in browser caches. They can't be
|
50
|
+
# used for responses that must be byte-identical, like serving +Range+
|
51
|
+
# requests within a PDF file.
|
52
|
+
# [+:strong_etag+]
|
53
|
+
# Sets a "strong" ETag validator on the response. Requests that specify an
|
54
|
+
# +If-None-Match+ header may receive a <tt>304 Not Modified</tt> response
|
55
|
+
# if the ETag matches exactly.
|
56
|
+
#
|
57
|
+
# A strong ETag implies exact equality -- the response must match byte for
|
58
|
+
# byte. This is necessary for serving +Range+ requests within a large
|
59
|
+
# video or PDF file, for example, or for compatibility with some CDNs that
|
60
|
+
# don't support weak ETags.
|
61
|
+
# [+:last_modified+]
|
62
|
+
# Sets a "weak" last-update validator on the response. Subsequent requests
|
63
|
+
# that specify an +If-Modified-Since+ header may receive a <tt>304 Not Modified</tt>
|
64
|
+
# response if +last_modified+ <= +If-Modified-Since+.
|
65
|
+
# [+:public+]
|
66
|
+
# By default the +Cache-Control+ header is private. Set this option to
|
67
|
+
# +true+ if you want your application to be cacheable by other devices,
|
68
|
+
# such as proxy caches.
|
69
|
+
# [+:cache_control+]
|
70
|
+
# When given, will overwrite an existing +Cache-Control+ header. For a
|
71
|
+
# list of +Cache-Control+ directives, see the {article on
|
72
|
+
# MDN}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control].
|
73
|
+
# [+:template+]
|
74
|
+
# By default, the template digest for the current controller/action is
|
75
|
+
# included in ETags. If the action renders a different template, you can
|
76
|
+
# include its digest instead. If the action doesn't render a template at
|
77
|
+
# all, you can pass <tt>template: false</tt> to skip any attempt to check
|
78
|
+
# for a template digest.
|
79
|
+
#
|
80
|
+
# ==== Examples
|
69
81
|
#
|
70
82
|
# def show
|
71
83
|
# @article = Article.find(params[:id])
|
72
84
|
# fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
|
73
85
|
# end
|
74
86
|
#
|
75
|
-
# This will
|
76
|
-
# If-Modified-Since header
|
87
|
+
# This will send a <tt>304 Not Modified</tt> response if the request
|
88
|
+
# specifies a matching ETag and +If-Modified-Since+ header. Otherwise, it
|
89
|
+
# will render the +show+ template.
|
77
90
|
#
|
78
|
-
# You can also just pass a record
|
79
|
-
# by calling +updated_at+ and +etag+ by passing the object itself.
|
91
|
+
# You can also just pass a record:
|
80
92
|
#
|
81
93
|
# def show
|
82
94
|
# @article = Article.find(params[:id])
|
83
95
|
# fresh_when(@article)
|
84
96
|
# end
|
85
97
|
#
|
98
|
+
# +etag+ will be set to the record, and +last_modified+ will be set to the
|
99
|
+
# record's +updated_at+.
|
100
|
+
#
|
86
101
|
# You can also pass an object that responds to +maximum+, such as a
|
87
|
-
# collection of
|
88
|
-
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
89
|
-
# most recently updated record) and the +etag+ by passing the object itself.
|
102
|
+
# collection of records:
|
90
103
|
#
|
91
104
|
# def index
|
92
105
|
# @articles = Article.all
|
93
106
|
# fresh_when(@articles)
|
94
107
|
# end
|
95
108
|
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
# @article = Article.find(params[:id])
|
100
|
-
# fresh_when(@article, public: true)
|
101
|
-
# end
|
109
|
+
# In this case, +etag+ will be set to the collection, and +last_modified+
|
110
|
+
# will be set to <tt>maximum(:updated_at)</tt> (the timestamp of the most
|
111
|
+
# recently updated record).
|
102
112
|
#
|
103
|
-
# When
|
113
|
+
# When passing a record or a collection, you can still specify other
|
114
|
+
# options, such as +:public+ and +:cache_control+:
|
104
115
|
#
|
105
116
|
# def show
|
106
117
|
# @article = Article.find(params[:id])
|
107
118
|
# fresh_when(@article, public: true, cache_control: { no_cache: true })
|
108
119
|
# end
|
109
120
|
#
|
110
|
-
#
|
121
|
+
# The above will set <tt>Cache-Control: public, no-cache</tt> in the response.
|
111
122
|
#
|
112
|
-
# When rendering a different template than the
|
113
|
-
#
|
123
|
+
# When rendering a different template than the controller/action's default
|
124
|
+
# template, you can indicate which digest to include in the ETag:
|
114
125
|
#
|
115
|
-
# before_action { fresh_when @article, template:
|
126
|
+
# before_action { fresh_when @article, template: "widgets/show" }
|
116
127
|
#
|
117
128
|
def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, cache_control: {}, template: nil)
|
118
129
|
response.cache_control.delete(:no_store)
|
@@ -134,41 +145,16 @@ module ActionController
|
|
134
145
|
head :not_modified if request.fresh?(response)
|
135
146
|
end
|
136
147
|
|
137
|
-
# Sets the +etag+ and/or +last_modified+ on the response and checks
|
138
|
-
# the
|
139
|
-
#
|
140
|
-
# it
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
# Requests that set If-None-Match header may return a 304 Not Modified
|
148
|
-
# response if it matches the ETag exactly. A weak ETag indicates semantic
|
149
|
-
# equivalence, not byte-for-byte equality, so they're good for caching
|
150
|
-
# HTML pages in browser caches. They can't be used for responses that
|
151
|
-
# must be byte-identical, like serving Range requests within a PDF file.
|
152
|
-
# * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
|
153
|
-
# Requests that set If-None-Match header may return a 304 Not Modified
|
154
|
-
# response if it matches the ETag exactly. A strong ETag implies exact
|
155
|
-
# equality: the response must match byte for byte. This is necessary for
|
156
|
-
# doing Range requests within a large video or PDF file, for example, or
|
157
|
-
# for compatibility with some CDNs that don't support weak ETags.
|
158
|
-
# * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
|
159
|
-
# response. Subsequent requests that set If-Modified-Since may return a
|
160
|
-
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
161
|
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
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.
|
165
|
-
# * <tt>:template</tt> By default, the template digest for the current
|
166
|
-
# controller/action is included in ETags. If the action renders a
|
167
|
-
# different template, you can include its digest instead. If the action
|
168
|
-
# doesn't render a template at all, you can pass <tt>template: false</tt>
|
169
|
-
# to skip any attempt to check for a template digest.
|
170
|
-
#
|
171
|
-
# === Example:
|
148
|
+
# Sets the +etag+ and/or +last_modified+ on the response and checks them
|
149
|
+
# against the request. If the request doesn't match the provided options, it
|
150
|
+
# is considered stale, and the response should be rendered from scratch.
|
151
|
+
# Otherwise, it is fresh, and a <tt>304 Not Modified</tt> is sent.
|
152
|
+
#
|
153
|
+
# ==== Options
|
154
|
+
#
|
155
|
+
# See #fresh_when for supported options.
|
156
|
+
#
|
157
|
+
# ==== Examples
|
172
158
|
#
|
173
159
|
# def show
|
174
160
|
# @article = Article.find(params[:id])
|
@@ -181,8 +167,7 @@ module ActionController
|
|
181
167
|
# end
|
182
168
|
# end
|
183
169
|
#
|
184
|
-
# You can also just pass a record
|
185
|
-
# by calling +updated_at+ and +etag+ by passing the object itself.
|
170
|
+
# You can also just pass a record:
|
186
171
|
#
|
187
172
|
# def show
|
188
173
|
# @article = Article.find(params[:id])
|
@@ -195,10 +180,11 @@ module ActionController
|
|
195
180
|
# end
|
196
181
|
# end
|
197
182
|
#
|
183
|
+
# +etag+ will be set to the record, and +last_modified+ will be set to the
|
184
|
+
# record's +updated_at+.
|
185
|
+
#
|
198
186
|
# You can also pass an object that responds to +maximum+, such as a
|
199
|
-
# collection of
|
200
|
-
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
201
|
-
# most recently updated record) and the +etag+ by passing the object itself.
|
187
|
+
# collection of records:
|
202
188
|
#
|
203
189
|
# def index
|
204
190
|
# @articles = Article.all
|
@@ -211,20 +197,12 @@ module ActionController
|
|
211
197
|
# end
|
212
198
|
# end
|
213
199
|
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
# @article = Article.find(params[:id])
|
218
|
-
#
|
219
|
-
# if stale?(@article, public: true)
|
220
|
-
# @statistics = @article.really_expensive_call
|
221
|
-
# respond_to do |format|
|
222
|
-
# # all the supported formats
|
223
|
-
# end
|
224
|
-
# end
|
225
|
-
# end
|
200
|
+
# In this case, +etag+ will be set to the collection, and +last_modified+
|
201
|
+
# will be set to <tt>maximum(:updated_at)</tt> (the timestamp of the most
|
202
|
+
# recently updated record).
|
226
203
|
#
|
227
|
-
# When
|
204
|
+
# When passing a record or a collection, you can still specify other
|
205
|
+
# options, such as +:public+ and +:cache_control+:
|
228
206
|
#
|
229
207
|
# def show
|
230
208
|
# @article = Article.find(params[:id])
|
@@ -237,13 +215,13 @@ module ActionController
|
|
237
215
|
# end
|
238
216
|
# end
|
239
217
|
#
|
240
|
-
#
|
218
|
+
# The above will set <tt>Cache-Control: public, no-cache</tt> in the response.
|
241
219
|
#
|
242
|
-
# When rendering a different template than the
|
243
|
-
#
|
220
|
+
# When rendering a different template than the controller/action's default
|
221
|
+
# template, you can indicate which digest to include in the ETag:
|
244
222
|
#
|
245
223
|
# def show
|
246
|
-
# super if stale?
|
224
|
+
# super if stale?(@article, template: "widgets/show")
|
247
225
|
# end
|
248
226
|
#
|
249
227
|
def stale?(object = nil, **freshness_kwargs)
|
@@ -251,28 +229,48 @@ module ActionController
|
|
251
229
|
!request.fresh?(response)
|
252
230
|
end
|
253
231
|
|
254
|
-
# Sets
|
255
|
-
#
|
232
|
+
# Sets the +Cache-Control+ header, overwriting existing directives. This
|
233
|
+
# method will also ensure an HTTP +Date+ header for client compatibility.
|
234
|
+
#
|
235
|
+
# Defaults to issuing the +private+ directive, so that intermediate caches
|
236
|
+
# must not cache the response.
|
237
|
+
#
|
238
|
+
# ==== Options
|
239
|
+
#
|
240
|
+
# [+:public+]
|
241
|
+
# If true, replaces the default +private+ directive with the +public+
|
242
|
+
# directive.
|
243
|
+
# [+:must_revalidate+]
|
244
|
+
# If true, adds the +must-revalidate+ directive.
|
245
|
+
# [+:stale_while_revalidate+]
|
246
|
+
# Sets the value of the +stale-while-revalidate+ directive.
|
247
|
+
# [+:stale_if_error+]
|
248
|
+
# Sets the value of the +stale-if-error+ directive.
|
249
|
+
#
|
250
|
+
# Any additional key-value pairs are concatenated as directives. For a list
|
251
|
+
# of supported +Cache-Control+ directives, see the {article on
|
252
|
+
# MDN}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control].
|
253
|
+
#
|
254
|
+
# ==== Examples
|
256
255
|
#
|
257
|
-
# expires_in
|
258
|
-
#
|
259
|
-
# expires_in 3.hours, public: true, must_revalidate: true
|
256
|
+
# expires_in 10.minutes
|
257
|
+
# # => Cache-Control: max-age=600, private
|
260
258
|
#
|
261
|
-
#
|
262
|
-
#
|
259
|
+
# expires_in 10.minutes, public: true
|
260
|
+
# # => Cache-Control: max-age=600, public
|
263
261
|
#
|
264
|
-
#
|
265
|
-
#
|
262
|
+
# expires_in 10.minutes, public: true, must_revalidate: true
|
263
|
+
# # => Cache-Control: max-age=600, public, must-revalidate
|
266
264
|
#
|
267
|
-
# expires_in
|
268
|
-
#
|
265
|
+
# expires_in 1.hour, stale_while_revalidate: 60.seconds
|
266
|
+
# # => Cache-Control: max-age=3600, private, stale-while-revalidate=60
|
269
267
|
#
|
270
|
-
#
|
271
|
-
#
|
268
|
+
# expires_in 1.hour, stale_if_error: 5.minutes
|
269
|
+
# # => Cache-Control: max-age=3600, private, stale-if-error=300
|
272
270
|
#
|
273
|
-
# expires_in
|
271
|
+
# expires_in 1.hour, public: true, "s-maxage": 3.hours, "no-transform": true
|
272
|
+
# # => Cache-Control: max-age=3600, public, s-maxage=10800, no-transform=true
|
274
273
|
#
|
275
|
-
# The method will also ensure an HTTP Date header for client compatibility.
|
276
274
|
def expires_in(seconds, options = {})
|
277
275
|
response.cache_control.delete(:no_store)
|
278
276
|
response.cache_control.merge!(
|
@@ -288,7 +286,7 @@ module ActionController
|
|
288
286
|
response.date = Time.now unless response.date?
|
289
287
|
end
|
290
288
|
|
291
|
-
# Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt>. This means the
|
289
|
+
# Sets an HTTP 1.1 +Cache-Control+ header of <tt>no-cache</tt>. This means the
|
292
290
|
# resource will be marked as stale, so clients must always revalidate.
|
293
291
|
# Intermediate/browser caches may still store the asset.
|
294
292
|
def expires_now
|
@@ -311,7 +309,7 @@ module ActionController
|
|
311
309
|
public: public)
|
312
310
|
end
|
313
311
|
|
314
|
-
# Sets an HTTP 1.1 Cache-Control header of <tt>no-store</tt>. This means the
|
312
|
+
# Sets an HTTP 1.1 +Cache-Control+ header of <tt>no-store</tt>. This means the
|
315
313
|
# resource may not be stored in any cache.
|
316
314
|
def no_store
|
317
315
|
response.cache_control.replace(no_store: true)
|
@@ -13,7 +13,7 @@ module ActionController # :nodoc:
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module ClassMethods
|
16
|
-
# Overrides parts of the globally configured Content-Security-Policy
|
16
|
+
# Overrides parts of the globally configured +Content-Security-Policy+
|
17
17
|
# header:
|
18
18
|
#
|
19
19
|
# class PostsController < ApplicationController
|
@@ -31,7 +31,7 @@ module ActionController # :nodoc:
|
|
31
31
|
# end
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
# Pass +false+ to remove the Content-Security-Policy header:
|
34
|
+
# Pass +false+ to remove the +Content-Security-Policy+ header:
|
35
35
|
#
|
36
36
|
# class PostsController < ApplicationController
|
37
37
|
# content_security_policy false, only: :index
|
@@ -40,7 +40,7 @@ module ActionController # :nodoc:
|
|
40
40
|
before_action(options) do
|
41
41
|
if block_given?
|
42
42
|
policy = current_content_security_policy
|
43
|
-
|
43
|
+
instance_exec(policy, &block)
|
44
44
|
request.content_security_policy = policy
|
45
45
|
end
|
46
46
|
|
@@ -50,14 +50,14 @@ module ActionController # :nodoc:
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
# Overrides the globally configured Content-Security-Policy-Report-Only
|
53
|
+
# Overrides the globally configured +Content-Security-Policy-Report-Only+
|
54
54
|
# header:
|
55
55
|
#
|
56
56
|
# class PostsController < ApplicationController
|
57
57
|
# content_security_policy_report_only only: :index
|
58
58
|
# end
|
59
59
|
#
|
60
|
-
# Pass +false+ to remove the Content-Security-Policy-Report-Only header:
|
60
|
+
# Pass +false+ to remove the +Content-Security-Policy-Report-Only+ header:
|
61
61
|
#
|
62
62
|
# class PostsController < ApplicationController
|
63
63
|
# content_security_policy_report_only false, only: :index
|
@@ -4,6 +4,8 @@ require "action_controller/metal/exceptions"
|
|
4
4
|
require "action_dispatch/http/content_disposition"
|
5
5
|
|
6
6
|
module ActionController # :nodoc:
|
7
|
+
# = Action Controller Data \Streaming
|
8
|
+
#
|
7
9
|
# Methods for sending arbitrary data and for streaming files to the browser,
|
8
10
|
# instead of rendering.
|
9
11
|
module DataStreaming
|
@@ -15,10 +17,10 @@ module ActionController # :nodoc:
|
|
15
17
|
DEFAULT_SEND_FILE_DISPOSITION = "attachment" # :nodoc:
|
16
18
|
|
17
19
|
private
|
18
|
-
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
19
|
-
# via the Rack::Sendfile middleware. The header to use is set via
|
20
|
+
# Sends the file. This uses a server-appropriate method (such as +X-Sendfile+)
|
21
|
+
# via the +Rack::Sendfile+ middleware. The header to use is set via
|
20
22
|
# +config.action_dispatch.x_sendfile_header+.
|
21
|
-
# Your server can also configure this for you by setting the X-Sendfile-Type header.
|
23
|
+
# Your server can also configure this for you by setting the +X-Sendfile-Type+ header.
|
22
24
|
#
|
23
25
|
# Be careful to sanitize the path parameter if it is coming from a web
|
24
26
|
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
|
@@ -28,17 +30,17 @@ module ActionController # :nodoc:
|
|
28
30
|
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
29
31
|
# Defaults to <tt>File.basename(path)</tt>.
|
30
32
|
# * <tt>:type</tt> - specifies an HTTP content type.
|
31
|
-
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example
|
33
|
+
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example +:json+.
|
32
34
|
# If omitted, the type will be inferred from the file extension specified in <tt>:filename</tt>.
|
33
|
-
# If no content type is registered for the extension, the default type
|
35
|
+
# If no content type is registered for the extension, the default type +application/octet-stream+ will be used.
|
34
36
|
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
35
|
-
# Valid values are
|
37
|
+
# Valid values are <tt>"inline"</tt> and <tt>"attachment"</tt> (default).
|
36
38
|
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
|
37
39
|
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser to guess the filename from
|
38
40
|
# the URL, which is necessary for i18n filenames on certain browsers
|
39
41
|
# (setting <tt>:filename</tt> overrides this option).
|
40
42
|
#
|
41
|
-
# The default Content-Type and Content-Disposition headers are
|
43
|
+
# The default +Content-Type+ and +Content-Disposition+ headers are
|
42
44
|
# set to download arbitrary binary files in as many browsers as
|
43
45
|
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
|
44
46
|
# a variety of quirks (especially when downloading over SSL).
|
@@ -55,17 +57,17 @@ module ActionController # :nodoc:
|
|
55
57
|
#
|
56
58
|
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
|
57
59
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# https://
|
60
|
+
# You can use other <tt>Content-*</tt> HTTP headers to provide additional
|
61
|
+
# information to the client. See MDN for a
|
62
|
+
# {list of HTTP headers}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers].
|
61
63
|
#
|
62
64
|
# Also be aware that the document may be cached by proxies and browsers.
|
63
|
-
# The Pragma and Cache-Control headers declare how the file may be cached
|
65
|
+
# The +Pragma+ and +Cache-Control+ headers declare how the file may be cached
|
64
66
|
# by intermediaries. They default to require clients to validate with
|
65
67
|
# the server before releasing cached responses. See
|
66
68
|
# https://www.mnot.net/cache_docs/ for an overview of web caching and
|
67
|
-
# https://www.
|
68
|
-
# for the Cache-Control header spec.
|
69
|
+
# {RFC 9111}[https://www.rfc-editor.org/rfc/rfc9111.html#name-cache-control]
|
70
|
+
# for the +Cache-Control+ header spec.
|
69
71
|
def send_file(path, options = {}) # :doc:
|
70
72
|
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
|
71
73
|
|
@@ -85,12 +87,12 @@ module ActionController # :nodoc:
|
|
85
87
|
#
|
86
88
|
# Options:
|
87
89
|
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
88
|
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to
|
89
|
-
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example
|
90
|
+
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to +application/octet-stream+.
|
91
|
+
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example +:json+.
|
90
92
|
# If omitted, type will be inferred from the file extension specified in <tt>:filename</tt>.
|
91
|
-
# If no content type is registered for the extension, the default type
|
93
|
+
# If no content type is registered for the extension, the default type +application/octet-stream+ will be used.
|
92
94
|
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
93
|
-
# Valid values are
|
95
|
+
# Valid values are <tt>"inline"</tt> and <tt>"attachment"</tt> (default).
|
94
96
|
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
|
95
97
|
#
|
96
98
|
# Generic data download:
|
@@ -105,7 +107,7 @@ module ActionController # :nodoc:
|
|
105
107
|
#
|
106
108
|
# send_data image.data, type: image.content_type, disposition: 'inline'
|
107
109
|
#
|
108
|
-
# See +send_file+ for more information on HTTP Content
|
110
|
+
# See +send_file+ for more information on HTTP <tt>Content-*</tt> headers and caching.
|
109
111
|
def send_data(data, options = {}) # :doc:
|
110
112
|
send_file_headers! options
|
111
113
|
render options.slice(:status, :content_type).merge(body: data)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionController
|
4
|
+
# = Action Controller Etag With \Flash
|
5
|
+
#
|
4
6
|
# When you're using the flash, it's generally used as a conditional on the view.
|
5
7
|
# This means the content of the view depends on the flash. Which in turn means
|
6
8
|
# that the ETag for a response should be computed with the content of the flash
|
@@ -12,7 +14,7 @@ module ActionController
|
|
12
14
|
include ActionController::ConditionalGet
|
13
15
|
|
14
16
|
included do
|
15
|
-
etag { flash
|
17
|
+
etag { flash if request.respond_to?(:flash) && !flash.empty? }
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionController
|
4
|
+
# = Action Controller Etag With Template \Digest
|
5
|
+
#
|
4
6
|
# When our views change, they should bubble up into HTTP cache freshness
|
5
7
|
# and bust browser caches. So the template digest for the current action
|
6
8
|
# is automatically included in the ETag.
|
@@ -92,5 +92,13 @@ module ActionController
|
|
92
92
|
end
|
93
93
|
|
94
94
|
class MissingExactTemplate < UnknownFormat # :nodoc:
|
95
|
+
attr_reader :controller, :action_name
|
96
|
+
|
97
|
+
def initialize(message, controller, action_name)
|
98
|
+
@controller = controller
|
99
|
+
@action_name = action_name
|
100
|
+
|
101
|
+
super(message)
|
102
|
+
end
|
95
103
|
end
|
96
104
|
end
|