actionpack 4.2.11.1 → 6.1.3.2
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 +291 -489
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +102 -120
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +83 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +296 -422
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +107 -56
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +90 -43
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- 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 +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
- 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 +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +78 -48
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,30 +1,31 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
require "active_support/core_ext/integer/time"
|
2
5
|
|
3
6
|
module ActionController
|
4
7
|
module ConditionalGet
|
5
8
|
extend ActiveSupport::Concern
|
6
9
|
|
7
|
-
include RackDelegation
|
8
10
|
include Head
|
9
11
|
|
10
12
|
included do
|
11
|
-
class_attribute :etaggers
|
12
|
-
self.etaggers = []
|
13
|
+
class_attribute :etaggers, default: []
|
13
14
|
end
|
14
15
|
|
15
16
|
module ClassMethods
|
16
17
|
# Allows you to consider additional controller-wide information when generating an ETag.
|
17
18
|
# For example, if you serve pages tailored depending on who's logged in at the moment, you
|
18
|
-
# may want to add the current user id to be part of the ETag to prevent
|
19
|
+
# may want to add the current user id to be part of the ETag to prevent unauthorized displaying
|
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)
|
@@ -37,10 +38,25 @@ module ActionController
|
|
37
38
|
#
|
38
39
|
# === Parameters:
|
39
40
|
#
|
40
|
-
# * <tt>:etag</tt
|
41
|
-
#
|
41
|
+
# * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
|
42
|
+
# +:weak_etag+ option.
|
43
|
+
# * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
|
44
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
45
|
+
# response if it matches the ETag exactly. A weak ETag indicates semantic
|
46
|
+
# equivalence, not byte-for-byte equality, so they're good for caching
|
47
|
+
# HTML pages in browser caches. They can't be used for responses that
|
48
|
+
# must be byte-identical, like serving Range requests within a PDF file.
|
49
|
+
# * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
|
50
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
51
|
+
# response if it matches the ETag exactly. A strong ETag implies exact
|
52
|
+
# equality: the response must match byte for byte. This is necessary for
|
53
|
+
# doing Range requests within a large video or PDF file, for example, or
|
54
|
+
# for compatibility with some CDNs that don't support weak ETags.
|
55
|
+
# * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
|
56
|
+
# response. Subsequent requests that set If-Modified-Since may return a
|
57
|
+
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
42
58
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
43
|
-
# +true+ if you want your application to be
|
59
|
+
# +true+ if you want your application to be cacheable by other devices (proxy caches).
|
44
60
|
# * <tt>:template</tt> By default, the template digest for the current
|
45
61
|
# controller/action is included in ETags. If the action renders a
|
46
62
|
# different template, you can include its digest instead. If the action
|
@@ -51,21 +67,31 @@ module ActionController
|
|
51
67
|
#
|
52
68
|
# def show
|
53
69
|
# @article = Article.find(params[:id])
|
54
|
-
# fresh_when(etag: @article, last_modified: @article.
|
70
|
+
# fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
|
55
71
|
# end
|
56
72
|
#
|
57
73
|
# This will render the show template if the request isn't sending a matching ETag or
|
58
74
|
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
59
75
|
#
|
60
|
-
# You can also just pass a record
|
61
|
-
# +updated_at+ and
|
76
|
+
# You can also just pass a record. In this case +last_modified+ will be set
|
77
|
+
# by calling +updated_at+ and +etag+ by passing the object itself.
|
62
78
|
#
|
63
79
|
# def show
|
64
80
|
# @article = Article.find(params[:id])
|
65
81
|
# fresh_when(@article)
|
66
82
|
# end
|
67
83
|
#
|
68
|
-
#
|
84
|
+
# You can also pass an object that responds to +maximum+, such as a
|
85
|
+
# collection of active records. In this case +last_modified+ will be set by
|
86
|
+
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
87
|
+
# most recently updated record) and the +etag+ by passing the object itself.
|
88
|
+
#
|
89
|
+
# def index
|
90
|
+
# @articles = Article.all
|
91
|
+
# fresh_when(@articles)
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# When passing a record or a collection, you can still set the public header:
|
69
95
|
#
|
70
96
|
# def show
|
71
97
|
# @article = Article.find(params[:id])
|
@@ -77,18 +103,20 @@ module ActionController
|
|
77
103
|
#
|
78
104
|
# before_action { fresh_when @article, template: 'widgets/show' }
|
79
105
|
#
|
80
|
-
def fresh_when(
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
106
|
+
def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
|
107
|
+
weak_etag ||= etag || object unless strong_etag
|
108
|
+
last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
|
109
|
+
|
110
|
+
if strong_etag
|
111
|
+
response.strong_etag = combine_etags strong_etag,
|
112
|
+
last_modified: last_modified, public: public, template: template
|
113
|
+
elsif weak_etag || template
|
114
|
+
response.weak_etag = combine_etags weak_etag,
|
115
|
+
last_modified: last_modified, public: public, template: template
|
87
116
|
end
|
88
117
|
|
89
|
-
response.
|
90
|
-
response.
|
91
|
-
response.cache_control[:public] = true if options[:public]
|
118
|
+
response.last_modified = last_modified if last_modified
|
119
|
+
response.cache_control[:public] = true if public
|
92
120
|
|
93
121
|
head :not_modified if request.fresh?(response)
|
94
122
|
end
|
@@ -100,10 +128,25 @@ module ActionController
|
|
100
128
|
#
|
101
129
|
# === Parameters:
|
102
130
|
#
|
103
|
-
# * <tt>:etag</tt
|
104
|
-
#
|
131
|
+
# * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
|
132
|
+
# +:weak_etag+ option.
|
133
|
+
# * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
|
134
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
135
|
+
# response if it matches the ETag exactly. A weak ETag indicates semantic
|
136
|
+
# equivalence, not byte-for-byte equality, so they're good for caching
|
137
|
+
# HTML pages in browser caches. They can't be used for responses that
|
138
|
+
# must be byte-identical, like serving Range requests within a PDF file.
|
139
|
+
# * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
|
140
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
141
|
+
# response if it matches the ETag exactly. A strong ETag implies exact
|
142
|
+
# equality: the response must match byte for byte. This is necessary for
|
143
|
+
# doing Range requests within a large video or PDF file, for example, or
|
144
|
+
# for compatibility with some CDNs that don't support weak ETags.
|
145
|
+
# * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
|
146
|
+
# response. Subsequent requests that set If-Modified-Since may return a
|
147
|
+
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
105
148
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
106
|
-
# +true+ if you want your application to be
|
149
|
+
# +true+ if you want your application to be cacheable by other devices (proxy caches).
|
107
150
|
# * <tt>:template</tt> By default, the template digest for the current
|
108
151
|
# controller/action is included in ETags. If the action renders a
|
109
152
|
# different template, you can include its digest instead. If the action
|
@@ -115,7 +158,7 @@ module ActionController
|
|
115
158
|
# def show
|
116
159
|
# @article = Article.find(params[:id])
|
117
160
|
#
|
118
|
-
# if stale?(etag: @article, last_modified: @article.
|
161
|
+
# if stale?(etag: @article, last_modified: @article.updated_at)
|
119
162
|
# @statistics = @article.really_expensive_call
|
120
163
|
# respond_to do |format|
|
121
164
|
# # all the supported formats
|
@@ -123,8 +166,8 @@ module ActionController
|
|
123
166
|
# end
|
124
167
|
# end
|
125
168
|
#
|
126
|
-
# You can also just pass a record
|
127
|
-
# +updated_at+ and
|
169
|
+
# You can also just pass a record. In this case +last_modified+ will be set
|
170
|
+
# by calling +updated_at+ and +etag+ by passing the object itself.
|
128
171
|
#
|
129
172
|
# def show
|
130
173
|
# @article = Article.find(params[:id])
|
@@ -137,7 +180,23 @@ module ActionController
|
|
137
180
|
# end
|
138
181
|
# end
|
139
182
|
#
|
140
|
-
#
|
183
|
+
# You can also pass an object that responds to +maximum+, such as a
|
184
|
+
# collection of active records. In this case +last_modified+ will be set by
|
185
|
+
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
186
|
+
# most recently updated record) and the +etag+ by passing the object itself.
|
187
|
+
#
|
188
|
+
# def index
|
189
|
+
# @articles = Article.all
|
190
|
+
#
|
191
|
+
# if stale?(@articles)
|
192
|
+
# @statistics = @articles.really_expensive_call
|
193
|
+
# respond_to do |format|
|
194
|
+
# # all the supported formats
|
195
|
+
# end
|
196
|
+
# end
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# When passing a record or a collection, you can still set the public header:
|
141
200
|
#
|
142
201
|
# def show
|
143
202
|
# @article = Article.find(params[:id])
|
@@ -157,12 +216,12 @@ module ActionController
|
|
157
216
|
# super if stale? @article, template: 'widgets/show'
|
158
217
|
# end
|
159
218
|
#
|
160
|
-
def stale?(
|
161
|
-
fresh_when(
|
219
|
+
def stale?(object = nil, **freshness_kwargs)
|
220
|
+
fresh_when(object, **freshness_kwargs)
|
162
221
|
!request.fresh?(response)
|
163
222
|
end
|
164
223
|
|
165
|
-
# Sets
|
224
|
+
# Sets an HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
|
166
225
|
# instruction, so that intermediate caches must not cache the response.
|
167
226
|
#
|
168
227
|
# expires_in 20.minutes
|
@@ -170,31 +229,60 @@ module ActionController
|
|
170
229
|
# expires_in 3.hours, public: true, must_revalidate: true
|
171
230
|
#
|
172
231
|
# This method will overwrite an existing Cache-Control header.
|
173
|
-
# See
|
232
|
+
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
233
|
+
#
|
234
|
+
# HTTP Cache-Control Extensions for Stale Content. See https://tools.ietf.org/html/rfc5861
|
235
|
+
# It helps to cache an asset and serve it while is being revalidated and/or returning with an error.
|
236
|
+
#
|
237
|
+
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds
|
238
|
+
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
|
239
|
+
#
|
240
|
+
# 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 `Cache-Control` header in the response:
|
242
|
+
#
|
243
|
+
# expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true
|
174
244
|
#
|
175
|
-
# The method will also ensure
|
245
|
+
# The method will also ensure an HTTP Date header for client compatibility.
|
176
246
|
def expires_in(seconds, options = {})
|
177
247
|
response.cache_control.merge!(
|
178
|
-
:
|
179
|
-
:
|
180
|
-
:
|
248
|
+
max_age: seconds,
|
249
|
+
public: options.delete(:public),
|
250
|
+
must_revalidate: options.delete(:must_revalidate),
|
251
|
+
stale_while_revalidate: options.delete(:stale_while_revalidate),
|
252
|
+
stale_if_error: options.delete(:stale_if_error),
|
181
253
|
)
|
182
254
|
options.delete(:private)
|
183
255
|
|
184
|
-
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
256
|
+
response.cache_control[:extras] = options.map { |k, v| "#{k}=#{v}" }
|
185
257
|
response.date = Time.now unless response.date?
|
186
258
|
end
|
187
259
|
|
188
|
-
# Sets
|
189
|
-
#
|
260
|
+
# Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt>. This means the
|
261
|
+
# resource will be marked as stale, so clients must always revalidate.
|
262
|
+
# Intermediate/browser caches may still store the asset.
|
190
263
|
def expires_now
|
191
|
-
response.cache_control.replace(:
|
264
|
+
response.cache_control.replace(no_cache: true)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Cache or yield the block. The cache is supposed to never expire.
|
268
|
+
#
|
269
|
+
# You can use this method when you have an HTTP response that never changes,
|
270
|
+
# and the browser and proxies should cache it indefinitely.
|
271
|
+
#
|
272
|
+
# * +public+: By default, HTTP responses are private, cached only on the
|
273
|
+
# user's web browser. To allow proxies to cache the response, set +true+ to
|
274
|
+
# indicate that they can serve the cached response to all users.
|
275
|
+
def http_cache_forever(public: false)
|
276
|
+
expires_in 100.years, public: public
|
277
|
+
|
278
|
+
yield if stale?(etag: request.fullpath,
|
279
|
+
last_modified: Time.new(2011, 1, 1).utc,
|
280
|
+
public: public)
|
192
281
|
end
|
193
282
|
|
194
283
|
private
|
195
|
-
def combine_etags(options)
|
196
|
-
|
197
|
-
etags.unshift options[:etag]
|
284
|
+
def combine_etags(validator, options)
|
285
|
+
[validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
|
198
286
|
end
|
199
287
|
end
|
200
288
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController #:nodoc:
|
4
|
+
module ContentSecurityPolicy
|
5
|
+
# TODO: Documentation
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
include AbstractController::Helpers
|
9
|
+
include AbstractController::Callbacks
|
10
|
+
|
11
|
+
included do
|
12
|
+
helper_method :content_security_policy?
|
13
|
+
helper_method :content_security_policy_nonce
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def content_security_policy(enabled = true, **options, &block)
|
18
|
+
before_action(options) do
|
19
|
+
if block_given?
|
20
|
+
policy = current_content_security_policy
|
21
|
+
yield policy
|
22
|
+
request.content_security_policy = policy
|
23
|
+
end
|
24
|
+
|
25
|
+
unless enabled
|
26
|
+
request.content_security_policy = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def content_security_policy_report_only(report_only = true, **options)
|
32
|
+
before_action(options) do
|
33
|
+
request.content_security_policy_report_only = report_only
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def content_security_policy?
|
40
|
+
request.content_security_policy
|
41
|
+
end
|
42
|
+
|
43
|
+
def content_security_policy_nonce
|
44
|
+
request.content_security_policy_nonce
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_content_security_policy
|
48
|
+
request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionController #:nodoc:
|
2
4
|
module Cookies
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
5
|
-
include RackDelegation
|
6
|
-
|
7
7
|
included do
|
8
|
-
helper_method :cookies
|
8
|
+
helper_method :cookies if defined?(helper_method)
|
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,4 +1,7 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_controller/metal/exceptions"
|
4
|
+
require "action_dispatch/http/content_disposition"
|
2
5
|
|
3
6
|
module ActionController #:nodoc:
|
4
7
|
# Methods for sending arbitrary data and for streaming files to the browser,
|
@@ -8,10 +11,10 @@ module ActionController #:nodoc:
|
|
8
11
|
|
9
12
|
include ActionController::Rendering
|
10
13
|
|
11
|
-
DEFAULT_SEND_FILE_TYPE =
|
12
|
-
DEFAULT_SEND_FILE_DISPOSITION =
|
14
|
+
DEFAULT_SEND_FILE_TYPE = "application/octet-stream" #:nodoc:
|
15
|
+
DEFAULT_SEND_FILE_DISPOSITION = "attachment" #:nodoc:
|
13
16
|
|
14
|
-
|
17
|
+
private
|
15
18
|
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
16
19
|
# via the Rack::Sendfile middleware. The header to use is set via
|
17
20
|
# +config.action_dispatch.x_sendfile_header+.
|
@@ -25,14 +28,13 @@ module ActionController #:nodoc:
|
|
25
28
|
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
26
29
|
# Defaults to <tt>File.basename(path)</tt>.
|
27
30
|
# * <tt>:type</tt> - specifies an HTTP content type.
|
28
|
-
# You can specify either a string or a symbol for a registered type register
|
29
|
-
# <tt
|
30
|
-
# If
|
31
|
-
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
|
31
|
+
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
|
32
|
+
# 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 'application/octet-stream' will be used.
|
32
34
|
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
33
35
|
# Valid values are 'inline' and 'attachment' (default).
|
34
36
|
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
|
35
|
-
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
|
37
|
+
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser to guess the filename from
|
36
38
|
# the URL, which is necessary for i18n filenames on certain browsers
|
37
39
|
# (setting <tt>:filename</tt> overrides this option).
|
38
40
|
#
|
@@ -51,62 +53,42 @@ module ActionController #:nodoc:
|
|
51
53
|
#
|
52
54
|
# Show a 404 page in the browser:
|
53
55
|
#
|
54
|
-
# 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
|
55
57
|
#
|
56
58
|
# Read about the other Content-* HTTP headers if you'd like to
|
57
59
|
# provide the user with more information (such as Content-Description) in
|
58
|
-
#
|
60
|
+
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
|
59
61
|
#
|
60
62
|
# Also be aware that the document may be cached by proxies and browsers.
|
61
63
|
# The Pragma and Cache-Control headers declare how the file may be cached
|
62
64
|
# by intermediaries. They default to require clients to validate with
|
63
65
|
# the server before releasing cached responses. See
|
64
|
-
#
|
65
|
-
#
|
66
|
+
# https://www.mnot.net/cache_docs/ for an overview of web caching and
|
67
|
+
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
66
68
|
# for the Cache-Control header spec.
|
67
69
|
def send_file(path, options = {}) #:doc:
|
68
|
-
raise MissingFile, "Cannot read file #{path}" unless File.file?(path)
|
70
|
+
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
|
69
71
|
|
70
72
|
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
71
73
|
send_file_headers! options
|
72
74
|
|
73
75
|
self.status = options[:status] || 200
|
74
76
|
self.content_type = options[:content_type] if options.key?(:content_type)
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
# Avoid having to pass an open file handle as the response body.
|
79
|
-
# Rack::Sendfile will usually intercept the response and uses
|
80
|
-
# the path directly, so there is no reason to open the file.
|
81
|
-
class FileBody #:nodoc:
|
82
|
-
attr_reader :to_path
|
83
|
-
|
84
|
-
def initialize(path)
|
85
|
-
@to_path = path
|
86
|
-
end
|
87
|
-
|
88
|
-
# Stream the file's contents if Rack::Sendfile isn't present.
|
89
|
-
def each
|
90
|
-
File.open(to_path, 'rb') do |file|
|
91
|
-
while chunk = file.read(16384)
|
92
|
-
yield chunk
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
77
|
+
response.send_file path
|
96
78
|
end
|
97
79
|
|
98
80
|
# Sends the given binary data to the browser. This method is similar to
|
99
81
|
# <tt>render plain: data</tt>, but also allows you to specify whether
|
100
82
|
# the browser should display the response as a file attachment (i.e. in a
|
101
83
|
# download dialog) or as inline data. You may also set the content type,
|
102
|
-
# the
|
84
|
+
# the file name, and other things.
|
103
85
|
#
|
104
86
|
# Options:
|
105
87
|
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
106
|
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
|
107
|
-
# either a string or a symbol for a registered type
|
108
|
-
# If omitted, type will be
|
109
|
-
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
|
88
|
+
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
|
89
|
+
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
|
90
|
+
# 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 'application/octet-stream' will be used.
|
110
92
|
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
111
93
|
# Valid values are 'inline' and 'attachment' (default).
|
112
94
|
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
|
@@ -126,14 +108,16 @@ module ActionController #:nodoc:
|
|
126
108
|
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
127
109
|
def send_data(data, options = {}) #:doc:
|
128
110
|
send_file_headers! options
|
129
|
-
render options.slice(:status, :content_type).merge(:
|
111
|
+
render options.slice(:status, :content_type).merge(body: data)
|
130
112
|
end
|
131
113
|
|
132
|
-
private
|
133
114
|
def send_file_headers!(options)
|
134
115
|
type_provided = options.has_key?(:type)
|
135
116
|
|
136
117
|
content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
|
118
|
+
self.content_type = content_type
|
119
|
+
response.sending_file = true
|
120
|
+
|
137
121
|
raise ArgumentError, ":type option required" if content_type.nil?
|
138
122
|
|
139
123
|
if content_type.is_a?(Symbol)
|
@@ -143,21 +127,17 @@ module ActionController #:nodoc:
|
|
143
127
|
else
|
144
128
|
if !type_provided && options[:filename]
|
145
129
|
# If type wasn't provided, try guessing from file extension.
|
146
|
-
content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete(
|
130
|
+
content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete(".")) || content_type
|
147
131
|
end
|
148
132
|
self.content_type = content_type
|
149
133
|
end
|
150
134
|
|
151
135
|
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
|
152
|
-
|
153
|
-
|
154
|
-
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
155
|
-
headers['Content-Disposition'] = disposition
|
136
|
+
if disposition
|
137
|
+
headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: options[:filename])
|
156
138
|
end
|
157
139
|
|
158
|
-
headers[
|
159
|
-
|
160
|
-
response.sending_file = true
|
140
|
+
headers["Content-Transfer-Encoding"] = "binary"
|
161
141
|
|
162
142
|
# Fix a problem with IE 6.0 on opening downloaded files:
|
163
143
|
# If Cache-Control: no-cache is set (which Rails does by default),
|
@@ -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
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
# When you're using the flash, it's generally used as a conditional on the view.
|
5
|
+
# This means the content of the view depends on the flash. Which in turn means
|
6
|
+
# that the ETag for a response should be computed with the content of the flash
|
7
|
+
# in mind. This does that by including the content of the flash as a component
|
8
|
+
# in the ETag that's generated for a response.
|
9
|
+
module EtagWithFlash
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
include ActionController::ConditionalGet
|
13
|
+
|
14
|
+
included do
|
15
|
+
etag { flash unless flash.empty? }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionController
|
2
4
|
# When our views change, they should bubble up into HTTP cache freshness
|
3
5
|
# and bust browser caches. So the template digest for the current action
|
@@ -22,29 +24,32 @@ module ActionController
|
|
22
24
|
include ActionController::ConditionalGet
|
23
25
|
|
24
26
|
included do
|
25
|
-
class_attribute :etag_with_template_digest
|
26
|
-
self.etag_with_template_digest = true
|
27
|
+
class_attribute :etag_with_template_digest, default: true
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
determine_template_etag(options) if etag_with_template_digest
|
31
|
-
end
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
def determine_template_etag(options)
|
36
|
+
if template = pick_template_for_etag(options)
|
37
|
+
lookup_and_digest_template(template)
|
38
|
+
end
|
39
39
|
end
|
40
|
-
end
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
# Pick the template digest to include in the ETag. If the +:template+ option
|
42
|
+
# is present, use the named template. If +:template+ is +nil+ or absent, use
|
43
|
+
# the default controller/action template. If +:template+ is false, omit the
|
44
|
+
# template digest from the ETag.
|
45
|
+
def pick_template_for_etag(options)
|
46
|
+
unless options[:template] == false
|
47
|
+
options[:template] || "#{controller_path}/#{action_name}"
|
48
|
+
end
|
49
|
+
end
|
45
50
|
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
def lookup_and_digest_template(template)
|
52
|
+
ActionView::Digestor.digest name: template, format: nil, finder: lookup_context
|
53
|
+
end
|
49
54
|
end
|
50
55
|
end
|