actionpack 4.2.10 → 7.2.0.rc1
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 +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- 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 +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -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 +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- 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/middleware/templates/rescues/_source.erb +0 -27
- 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,200 +1,337 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "active_support/core_ext/object/try"
|
6
|
+
require "active_support/core_ext/integer/time"
|
2
7
|
|
3
8
|
module ActionController
|
4
9
|
module ConditionalGet
|
5
10
|
extend ActiveSupport::Concern
|
6
11
|
|
7
|
-
include RackDelegation
|
8
12
|
include Head
|
9
13
|
|
10
14
|
included do
|
11
|
-
class_attribute :etaggers
|
12
|
-
self.etaggers = []
|
15
|
+
class_attribute :etaggers, default: []
|
13
16
|
end
|
14
17
|
|
15
18
|
module ClassMethods
|
16
|
-
# Allows you to consider additional controller-wide information when generating
|
17
|
-
# For example, if you serve pages tailored depending on who's logged in
|
18
|
-
# may want to add the current user id to be part of the ETag
|
19
|
-
# of cached pages.
|
19
|
+
# Allows you to consider additional controller-wide information when generating
|
20
|
+
# an ETag. For example, if you serve pages tailored depending on who's logged in
|
21
|
+
# at the moment, you may want to add the current user id to be part of the ETag
|
22
|
+
# to prevent unauthorized displaying of cached pages.
|
20
23
|
#
|
21
|
-
#
|
22
|
-
#
|
24
|
+
# class InvoicesController < ApplicationController
|
25
|
+
# etag { current_user&.id }
|
23
26
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
27
|
+
# def show
|
28
|
+
# # Etag will differ even for the same invoice when it's viewed by a different current_user
|
29
|
+
# @invoice = Invoice.find(params[:id])
|
30
|
+
# fresh_when etag: @invoice
|
31
|
+
# end
|
28
32
|
# end
|
29
|
-
# end
|
30
33
|
def etag(&etagger)
|
31
34
|
self.etaggers += [etagger]
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
35
|
-
# Sets the
|
36
|
-
#
|
38
|
+
# Sets the `etag`, `last_modified`, or both on the response, and renders a `304
|
39
|
+
# Not Modified` response if the request is already fresh.
|
40
|
+
#
|
41
|
+
# #### Options
|
42
|
+
#
|
43
|
+
# `:etag`
|
44
|
+
# : Sets a "weak" ETag validator on the response. See the `:weak_etag` option.
|
37
45
|
#
|
38
|
-
#
|
46
|
+
# `:weak_etag`
|
47
|
+
# : Sets a "weak" ETag validator on the response. Requests that specify an
|
48
|
+
# `If-None-Match` header may receive a `304 Not Modified` response if the
|
49
|
+
# ETag matches exactly.
|
39
50
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# * <tt>:template</tt> By default, the template digest for the current
|
45
|
-
# controller/action is included in ETags. If the action renders a
|
46
|
-
# different template, you can include its digest instead. If the action
|
47
|
-
# doesn't render a template at all, you can pass <tt>template: false</tt>
|
48
|
-
# to skip any attempt to check for a template digest.
|
51
|
+
# : A weak ETag indicates semantic equivalence, not byte-for-byte equality, so
|
52
|
+
# they're good for caching HTML pages in browser caches. They can't be used
|
53
|
+
# for responses that must be byte-identical, like serving `Range` requests
|
54
|
+
# within a PDF file.
|
49
55
|
#
|
50
|
-
#
|
56
|
+
# `:strong_etag`
|
57
|
+
# : Sets a "strong" ETag validator on the response. Requests that specify an
|
58
|
+
# `If-None-Match` header may receive a `304 Not Modified` response if the
|
59
|
+
# ETag matches exactly.
|
51
60
|
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
61
|
+
# : A strong ETag implies exact equality -- the response must match byte for
|
62
|
+
# byte. This is necessary for serving `Range` requests within a large video
|
63
|
+
# or PDF file, for example, or for compatibility with some CDNs that don't
|
64
|
+
# support weak ETags.
|
56
65
|
#
|
57
|
-
#
|
58
|
-
#
|
66
|
+
# `:last_modified`
|
67
|
+
# : Sets a "weak" last-update validator on the response. Subsequent requests
|
68
|
+
# that specify an `If-Modified-Since` header may receive a `304 Not
|
69
|
+
# Modified` response if `last_modified` <= `If-Modified-Since`.
|
59
70
|
#
|
60
|
-
#
|
61
|
-
#
|
71
|
+
# `:public`
|
72
|
+
# : By default the `Cache-Control` header is private. Set this option to
|
73
|
+
# `true` if you want your application to be cacheable by other devices, such
|
74
|
+
# as proxy caches.
|
62
75
|
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
76
|
+
# `:cache_control`
|
77
|
+
# : When given, will overwrite an existing `Cache-Control` header. For a list
|
78
|
+
# of `Cache-Control` directives, see the [article on
|
79
|
+
# MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Contr
|
80
|
+
# ol).
|
67
81
|
#
|
68
|
-
#
|
82
|
+
# `:template`
|
83
|
+
# : By default, the template digest for the current controller/action is
|
84
|
+
# included in ETags. If the action renders a different template, you can
|
85
|
+
# include its digest instead. If the action doesn't render a template at
|
86
|
+
# all, you can pass `template: false` to skip any attempt to check for a
|
87
|
+
# template digest.
|
69
88
|
#
|
70
|
-
# def show
|
71
|
-
# @article = Article.find(params[:id])
|
72
|
-
# fresh_when(@article, public: true)
|
73
|
-
# end
|
74
89
|
#
|
75
|
-
#
|
76
|
-
#
|
90
|
+
# #### Examples
|
91
|
+
#
|
92
|
+
# def show
|
93
|
+
# @article = Article.find(params[:id])
|
94
|
+
# fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# This will send a `304 Not Modified` response if the request specifies a
|
98
|
+
# matching ETag and `If-Modified-Since` header. Otherwise, it will render the
|
99
|
+
# `show` template.
|
77
100
|
#
|
78
|
-
#
|
101
|
+
# You can also just pass a record:
|
102
|
+
#
|
103
|
+
# def show
|
104
|
+
# @article = Article.find(params[:id])
|
105
|
+
# fresh_when(@article)
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# `etag` will be set to the record, and `last_modified` will be set to the
|
109
|
+
# record's `updated_at`.
|
110
|
+
#
|
111
|
+
# You can also pass an object that responds to `maximum`, such as a collection
|
112
|
+
# of records:
|
113
|
+
#
|
114
|
+
# def index
|
115
|
+
# @articles = Article.all
|
116
|
+
# fresh_when(@articles)
|
117
|
+
# end
|
79
118
|
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
119
|
+
# In this case, `etag` will be set to the collection, and `last_modified` will
|
120
|
+
# be set to `maximum(:updated_at)` (the timestamp of the most recently updated
|
121
|
+
# record).
|
122
|
+
#
|
123
|
+
# When passing a record or a collection, you can still specify other options,
|
124
|
+
# such as `:public` and `:cache_control`:
|
125
|
+
#
|
126
|
+
# def show
|
127
|
+
# @article = Article.find(params[:id])
|
128
|
+
# fresh_when(@article, public: true, cache_control: { no_cache: true })
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# The above will set `Cache-Control: public, no-cache` in the response.
|
132
|
+
#
|
133
|
+
# When rendering a different template than the controller/action's default
|
134
|
+
# template, you can indicate which digest to include in the ETag:
|
135
|
+
#
|
136
|
+
# before_action { fresh_when @article, template: "widgets/show" }
|
137
|
+
#
|
138
|
+
def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, cache_control: {}, template: nil)
|
139
|
+
response.cache_control.delete(:no_store)
|
140
|
+
weak_etag ||= etag || object unless strong_etag
|
141
|
+
last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
|
142
|
+
|
143
|
+
if strong_etag
|
144
|
+
response.strong_etag = combine_etags strong_etag,
|
145
|
+
last_modified: last_modified, public: public, template: template
|
146
|
+
elsif weak_etag || template
|
147
|
+
response.weak_etag = combine_etags weak_etag,
|
148
|
+
last_modified: last_modified, public: public, template: template
|
87
149
|
end
|
88
150
|
|
89
|
-
response.
|
90
|
-
response.
|
91
|
-
response.cache_control
|
151
|
+
response.last_modified = last_modified if last_modified
|
152
|
+
response.cache_control[:public] = true if public
|
153
|
+
response.cache_control.merge!(cache_control)
|
92
154
|
|
93
155
|
head :not_modified if request.fresh?(response)
|
94
156
|
end
|
95
157
|
|
96
|
-
# Sets the
|
97
|
-
# the
|
98
|
-
#
|
99
|
-
# it
|
158
|
+
# Sets the `etag` and/or `last_modified` on the response and checks them against
|
159
|
+
# the request. If the request doesn't match the provided options, it is
|
160
|
+
# considered stale, and the response should be rendered from scratch. Otherwise,
|
161
|
+
# it is fresh, and a `304 Not Modified` is sent.
|
162
|
+
#
|
163
|
+
# #### Options
|
100
164
|
#
|
101
|
-
#
|
165
|
+
# See #fresh_when for supported options.
|
102
166
|
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
167
|
+
# #### Examples
|
168
|
+
#
|
169
|
+
# def show
|
170
|
+
# @article = Article.find(params[:id])
|
171
|
+
#
|
172
|
+
# if stale?(etag: @article, last_modified: @article.updated_at)
|
173
|
+
# @statistics = @article.really_expensive_call
|
174
|
+
# respond_to do |format|
|
175
|
+
# # all the supported formats
|
176
|
+
# end
|
177
|
+
# end
|
178
|
+
# end
|
112
179
|
#
|
113
|
-
#
|
180
|
+
# You can also just pass a record:
|
114
181
|
#
|
115
|
-
#
|
116
|
-
#
|
182
|
+
# def show
|
183
|
+
# @article = Article.find(params[:id])
|
117
184
|
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
185
|
+
# if stale?(@article)
|
186
|
+
# @statistics = @article.really_expensive_call
|
187
|
+
# respond_to do |format|
|
188
|
+
# # all the supported formats
|
189
|
+
# end
|
122
190
|
# end
|
123
191
|
# end
|
124
|
-
# end
|
125
192
|
#
|
126
|
-
#
|
127
|
-
#
|
193
|
+
# `etag` will be set to the record, and `last_modified` will be set to the
|
194
|
+
# record's `updated_at`.
|
195
|
+
#
|
196
|
+
# You can also pass an object that responds to `maximum`, such as a collection
|
197
|
+
# of records:
|
128
198
|
#
|
129
|
-
#
|
130
|
-
#
|
199
|
+
# def index
|
200
|
+
# @articles = Article.all
|
131
201
|
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
202
|
+
# if stale?(@articles)
|
203
|
+
# @statistics = @articles.really_expensive_call
|
204
|
+
# respond_to do |format|
|
205
|
+
# # all the supported formats
|
206
|
+
# end
|
136
207
|
# end
|
137
208
|
# end
|
138
|
-
# end
|
139
209
|
#
|
140
|
-
#
|
210
|
+
# In this case, `etag` will be set to the collection, and `last_modified` will
|
211
|
+
# be set to `maximum(:updated_at)` (the timestamp of the most recently updated
|
212
|
+
# record).
|
141
213
|
#
|
142
|
-
#
|
143
|
-
#
|
214
|
+
# When passing a record or a collection, you can still specify other options,
|
215
|
+
# such as `:public` and `:cache_control`:
|
144
216
|
#
|
145
|
-
#
|
146
|
-
# @
|
147
|
-
#
|
148
|
-
#
|
217
|
+
# def show
|
218
|
+
# @article = Article.find(params[:id])
|
219
|
+
#
|
220
|
+
# if stale?(@article, public: true, cache_control: { no_cache: true })
|
221
|
+
# @statistics = @articles.really_expensive_call
|
222
|
+
# respond_to do |format|
|
223
|
+
# # all the supported formats
|
224
|
+
# end
|
149
225
|
# end
|
150
226
|
# end
|
151
|
-
# end
|
152
227
|
#
|
153
|
-
#
|
154
|
-
# style, you can indicate which digest to include in the ETag:
|
228
|
+
# The above will set `Cache-Control: public, no-cache` in the response.
|
155
229
|
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
# end
|
230
|
+
# When rendering a different template than the controller/action's default
|
231
|
+
# template, you can indicate which digest to include in the ETag:
|
159
232
|
#
|
160
|
-
def
|
161
|
-
|
233
|
+
# def show
|
234
|
+
# super if stale?(@article, template: "widgets/show")
|
235
|
+
# end
|
236
|
+
#
|
237
|
+
def stale?(object = nil, **freshness_kwargs)
|
238
|
+
fresh_when(object, **freshness_kwargs)
|
162
239
|
!request.fresh?(response)
|
163
240
|
end
|
164
241
|
|
165
|
-
# Sets
|
166
|
-
#
|
242
|
+
# Sets the `Cache-Control` header, overwriting existing directives. This method
|
243
|
+
# will also ensure an HTTP `Date` header for client compatibility.
|
244
|
+
#
|
245
|
+
# Defaults to issuing the `private` directive, so that intermediate caches must
|
246
|
+
# not cache the response.
|
247
|
+
#
|
248
|
+
# #### Options
|
249
|
+
#
|
250
|
+
# `:public`
|
251
|
+
# : If true, replaces the default `private` directive with the `public`
|
252
|
+
# directive.
|
253
|
+
#
|
254
|
+
# `:must_revalidate`
|
255
|
+
# : If true, adds the `must-revalidate` directive.
|
256
|
+
#
|
257
|
+
# `:stale_while_revalidate`
|
258
|
+
# : Sets the value of the `stale-while-revalidate` directive.
|
259
|
+
#
|
260
|
+
# `:stale_if_error`
|
261
|
+
# : Sets the value of the `stale-if-error` directive.
|
262
|
+
#
|
263
|
+
#
|
264
|
+
# Any additional key-value pairs are concatenated as directives. For a list of
|
265
|
+
# supported `Cache-Control` directives, see the [article on
|
266
|
+
# MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control).
|
167
267
|
#
|
168
|
-
#
|
169
|
-
# expires_in 3.hours, public: true
|
170
|
-
# expires_in 3.hours, public: true, must_revalidate: true
|
268
|
+
# #### Examples
|
171
269
|
#
|
172
|
-
#
|
173
|
-
#
|
270
|
+
# expires_in 10.minutes
|
271
|
+
# # => Cache-Control: max-age=600, private
|
272
|
+
#
|
273
|
+
# expires_in 10.minutes, public: true
|
274
|
+
# # => Cache-Control: max-age=600, public
|
275
|
+
#
|
276
|
+
# expires_in 10.minutes, public: true, must_revalidate: true
|
277
|
+
# # => Cache-Control: max-age=600, public, must-revalidate
|
278
|
+
#
|
279
|
+
# expires_in 1.hour, stale_while_revalidate: 60.seconds
|
280
|
+
# # => Cache-Control: max-age=3600, private, stale-while-revalidate=60
|
281
|
+
#
|
282
|
+
# expires_in 1.hour, stale_if_error: 5.minutes
|
283
|
+
# # => Cache-Control: max-age=3600, private, stale-if-error=300
|
284
|
+
#
|
285
|
+
# expires_in 1.hour, public: true, "s-maxage": 3.hours, "no-transform": true
|
286
|
+
# # => Cache-Control: max-age=3600, public, s-maxage=10800, no-transform=true
|
174
287
|
#
|
175
|
-
# The method will also ensure a HTTP Date header for client compatibility.
|
176
288
|
def expires_in(seconds, options = {})
|
289
|
+
response.cache_control.delete(:no_store)
|
177
290
|
response.cache_control.merge!(
|
178
|
-
:
|
179
|
-
:
|
180
|
-
:
|
291
|
+
max_age: seconds,
|
292
|
+
public: options.delete(:public),
|
293
|
+
must_revalidate: options.delete(:must_revalidate),
|
294
|
+
stale_while_revalidate: options.delete(:stale_while_revalidate),
|
295
|
+
stale_if_error: options.delete(:stale_if_error),
|
181
296
|
)
|
182
297
|
options.delete(:private)
|
183
298
|
|
184
|
-
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
299
|
+
response.cache_control[:extras] = options.map { |k, v| "#{k}=#{v}" }
|
185
300
|
response.date = Time.now unless response.date?
|
186
301
|
end
|
187
302
|
|
188
|
-
# Sets
|
189
|
-
#
|
303
|
+
# Sets an HTTP 1.1 `Cache-Control` header of `no-cache`. This means the resource
|
304
|
+
# will be marked as stale, so clients must always revalidate.
|
305
|
+
# Intermediate/browser caches may still store the asset.
|
190
306
|
def expires_now
|
191
|
-
response.cache_control.replace(:
|
307
|
+
response.cache_control.replace(no_cache: true)
|
308
|
+
end
|
309
|
+
|
310
|
+
# Cache or yield the block. The cache is supposed to never expire.
|
311
|
+
#
|
312
|
+
# You can use this method when you have an HTTP response that never changes, and
|
313
|
+
# the browser and proxies should cache it indefinitely.
|
314
|
+
#
|
315
|
+
# * `public`: By default, HTTP responses are private, cached only on the
|
316
|
+
# user's web browser. To allow proxies to cache the response, set `true` to
|
317
|
+
# indicate that they can serve the cached response to all users.
|
318
|
+
def http_cache_forever(public: false)
|
319
|
+
expires_in 100.years, public: public
|
320
|
+
|
321
|
+
yield if stale?(etag: request.fullpath,
|
322
|
+
last_modified: Time.new(2011, 1, 1).utc,
|
323
|
+
public: public)
|
324
|
+
end
|
325
|
+
|
326
|
+
# Sets an HTTP 1.1 `Cache-Control` header of `no-store`. This means the resource
|
327
|
+
# may not be stored in any cache.
|
328
|
+
def no_store
|
329
|
+
response.cache_control.replace(no_store: true)
|
192
330
|
end
|
193
331
|
|
194
332
|
private
|
195
|
-
def combine_etags(options)
|
196
|
-
|
197
|
-
etags.unshift options[:etag]
|
333
|
+
def combine_etags(validator, options)
|
334
|
+
[validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
|
198
335
|
end
|
199
336
|
end
|
200
337
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController # :nodoc:
|
6
|
+
module ContentSecurityPolicy
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
include AbstractController::Helpers
|
10
|
+
include AbstractController::Callbacks
|
11
|
+
|
12
|
+
included do
|
13
|
+
helper_method :content_security_policy?
|
14
|
+
helper_method :content_security_policy_nonce
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Overrides parts of the globally configured `Content-Security-Policy` header:
|
19
|
+
#
|
20
|
+
# class PostsController < ApplicationController
|
21
|
+
# content_security_policy do |policy|
|
22
|
+
# policy.base_uri "https://www.example.com"
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# Options can be passed similar to `before_action`. For example, pass `only:
|
27
|
+
# :index` to override the header on the index action only:
|
28
|
+
#
|
29
|
+
# class PostsController < ApplicationController
|
30
|
+
# content_security_policy(only: :index) do |policy|
|
31
|
+
# policy.default_src :self, :https
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Pass `false` to remove the `Content-Security-Policy` header:
|
36
|
+
#
|
37
|
+
# class PostsController < ApplicationController
|
38
|
+
# content_security_policy false, only: :index
|
39
|
+
# end
|
40
|
+
def content_security_policy(enabled = true, **options, &block)
|
41
|
+
before_action(options) do
|
42
|
+
if block_given?
|
43
|
+
policy = current_content_security_policy
|
44
|
+
instance_exec(policy, &block)
|
45
|
+
request.content_security_policy = policy
|
46
|
+
end
|
47
|
+
|
48
|
+
unless enabled
|
49
|
+
request.content_security_policy = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Overrides the globally configured `Content-Security-Policy-Report-Only`
|
55
|
+
# header:
|
56
|
+
#
|
57
|
+
# class PostsController < ApplicationController
|
58
|
+
# content_security_policy_report_only only: :index
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# Pass `false` to remove the `Content-Security-Policy-Report-Only` header:
|
62
|
+
#
|
63
|
+
# class PostsController < ApplicationController
|
64
|
+
# content_security_policy_report_only false, only: :index
|
65
|
+
# end
|
66
|
+
def content_security_policy_report_only(report_only = true, **options)
|
67
|
+
before_action(options) do
|
68
|
+
request.content_security_policy_report_only = report_only
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def content_security_policy?
|
75
|
+
request.content_security_policy
|
76
|
+
end
|
77
|
+
|
78
|
+
def content_security_policy_nonce
|
79
|
+
request.content_security_policy_nonce
|
80
|
+
end
|
81
|
+
|
82
|
+
def current_content_security_policy
|
83
|
+
request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -1,15 +1,19 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController # :nodoc:
|
2
6
|
module Cookies
|
3
7
|
extend ActiveSupport::Concern
|
4
8
|
|
5
|
-
include RackDelegation
|
6
|
-
|
7
9
|
included do
|
8
|
-
helper_method :cookies
|
10
|
+
helper_method :cookies if defined?(helper_method)
|
9
11
|
end
|
10
12
|
|
11
13
|
private
|
12
|
-
|
14
|
+
# The cookies for the current request. See ActionDispatch::Cookies for more
|
15
|
+
# information.
|
16
|
+
def cookies # :doc:
|
13
17
|
request.cookie_jar
|
14
18
|
end
|
15
19
|
end
|