actionpack 4.2.10 → 5.2.4.6
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 +300 -466
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +45 -49
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +47 -31
- data/lib/abstract_controller/collector.rb +8 -11
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +25 -25
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
- data/lib/abstract_controller/rendering.rb +42 -41
- data/lib/abstract_controller/translation.rb +10 -7
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +12 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/base.rb +27 -19
- data/lib/action_controller/caching.rb +14 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +10 -15
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +118 -44
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +3 -3
- data/lib/action_controller/metal/data_streaming.rb +27 -46
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
- data/lib/action_controller/metal/exceptions.rb +8 -14
- data/lib/action_controller/metal/flash.rb +4 -3
- data/lib/action_controller/metal/force_ssl.rb +23 -21
- data/lib/action_controller/metal/head.rb +21 -19
- data/lib/action_controller/metal/helpers.rb +24 -14
- data/lib/action_controller/metal/http_authentication.rb +65 -58
- data/lib/action_controller/metal/implicit_render.rb +62 -8
- data/lib/action_controller/metal/instrumentation.rb +19 -21
- data/lib/action_controller/metal/live.rb +90 -106
- data/lib/action_controller/metal/mime_responds.rb +33 -46
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +61 -53
- data/lib/action_controller/metal/redirecting.rb +49 -28
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +72 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +229 -93
- data/lib/action_controller/metal/rescue.rb +9 -16
- data/lib/action_controller/metal/streaming.rb +12 -10
- data/lib/action_controller/metal/strong_parameters.rb +583 -164
- 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 +98 -83
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +280 -411
- data/lib/action_controller.rb +29 -21
- data/lib/action_dispatch/http/cache.rb +93 -47
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +26 -20
- data/lib/action_dispatch/http/filter_redirect.rb +10 -11
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
- data/lib/action_dispatch/http/mime_type.rb +134 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameter_filter.rb +25 -11
- data/lib/action_dispatch/http/parameters.rb +98 -39
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +200 -118
- data/lib/action_dispatch/http/response.rb +225 -110
- data/lib/action_dispatch/http/upload.rb +12 -6
- data/lib/action_dispatch/http/url.rb +110 -28
- data/lib/action_dispatch/journey/formatter.rb +55 -32
- data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
- data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
- data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
- data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
- data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
- data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
- data/lib/action_dispatch/journey/nodes/node.rb +18 -6
- data/lib/action_dispatch/journey/parser.rb +23 -22
- data/lib/action_dispatch/journey/parser.y +3 -2
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +50 -44
- data/lib/action_dispatch/journey/route.rb +106 -28
- data/lib/action_dispatch/journey/router/utils.rb +20 -11
- data/lib/action_dispatch/journey/router.rb +35 -23
- data/lib/action_dispatch/journey/routes.rb +18 -16
- data/lib/action_dispatch/journey/scanner.rb +18 -15
- data/lib/action_dispatch/journey/visitors.rb +99 -52
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/middleware/callbacks.rb +1 -2
- data/lib/action_dispatch/middleware/cookies.rb +304 -193
- data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
- data/lib/action_dispatch/middleware/request_id.rb +17 -9
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
- data/lib/action_dispatch/middleware/ssl.rb +114 -36
- data/lib/action_dispatch/middleware/stack.rb +31 -44
- data/lib/action_dispatch/middleware/static.rb +57 -50
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.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 +64 -64
- data/lib/action_dispatch/railtie.rb +19 -11
- data/lib/action_dispatch/request/session.rb +106 -59
- data/lib/action_dispatch/request/utils.rb +67 -24
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +58 -67
- data/lib/action_dispatch/routing/mapper.rb +733 -447
- data/lib/action_dispatch/routing/polymorphic_routes.rb +166 -140
- data/lib/action_dispatch/routing/redirection.rb +36 -26
- data/lib/action_dispatch/routing/route_set.rb +321 -291
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +65 -25
- data/lib/action_dispatch/routing.rb +17 -18
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions/response.rb +45 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +347 -209
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +28 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +35 -7
- data/lib/action_dispatch.rb +27 -19
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +59 -42
- 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/journey/backwards.rb +0 -5
- 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,6 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'uri'
|
3
|
-
require 'set'
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
3
|
module ActionController
|
6
4
|
# \Caching is a cheap way of speeding up slow applications by keeping the result of
|
@@ -8,7 +6,7 @@ module ActionController
|
|
8
6
|
#
|
9
7
|
# You can read more about each approach by clicking the modules below.
|
10
8
|
#
|
11
|
-
# Note: To turn off all caching, set
|
9
|
+
# Note: To turn off all caching provided by Action Controller, set
|
12
10
|
# config.action_controller.perform_caching = false
|
13
11
|
#
|
14
12
|
# == \Caching stores
|
@@ -24,66 +22,25 @@ module ActionController
|
|
24
22
|
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
|
25
23
|
# config.action_controller.cache_store = MyOwnStore.new('parameter')
|
26
24
|
module Caching
|
27
|
-
extend ActiveSupport::Concern
|
28
25
|
extend ActiveSupport::Autoload
|
29
|
-
|
30
|
-
eager_autoload do
|
31
|
-
autoload :Fragments
|
32
|
-
end
|
33
|
-
|
34
|
-
module ConfigMethods
|
35
|
-
def cache_store
|
36
|
-
config.cache_store
|
37
|
-
end
|
38
|
-
|
39
|
-
def cache_store=(store)
|
40
|
-
config.cache_store = ActiveSupport::Cache.lookup_store(store)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
def cache_configured?
|
45
|
-
perform_caching && cache_store
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
include RackDelegation
|
50
|
-
include AbstractController::Callbacks
|
51
|
-
|
52
|
-
include ConfigMethods
|
53
|
-
include Fragments
|
26
|
+
extend ActiveSupport::Concern
|
54
27
|
|
55
28
|
included do
|
56
|
-
|
57
|
-
|
58
|
-
config_accessor :default_static_extension
|
59
|
-
self.default_static_extension ||= '.html'
|
60
|
-
|
61
|
-
config_accessor :perform_caching
|
62
|
-
self.perform_caching = true if perform_caching.nil?
|
63
|
-
|
64
|
-
class_attribute :_view_cache_dependencies
|
65
|
-
self._view_cache_dependencies = []
|
66
|
-
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
29
|
+
include AbstractController::Caching
|
67
30
|
end
|
68
31
|
|
69
|
-
|
70
|
-
def view_cache_dependency(&dependency)
|
71
|
-
self._view_cache_dependencies += [dependency]
|
72
|
-
end
|
73
|
-
end
|
32
|
+
private
|
74
33
|
|
75
|
-
|
76
|
-
|
77
|
-
|
34
|
+
def instrument_payload(key)
|
35
|
+
{
|
36
|
+
controller: controller_name,
|
37
|
+
action: action_name,
|
38
|
+
key: key
|
39
|
+
}
|
40
|
+
end
|
78
41
|
|
79
|
-
|
80
|
-
|
81
|
-
def cache(key, options = {}, &block)
|
82
|
-
if cache_configured?
|
83
|
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
84
|
-
else
|
85
|
-
yield
|
86
|
-
end
|
42
|
+
def instrument_name
|
43
|
+
"action_controller".freeze
|
87
44
|
end
|
88
45
|
end
|
89
46
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
# Override the default form builder for all views rendered by this
|
5
|
+
# controller and any of its descendants. Accepts a subclass of
|
6
|
+
# +ActionView::Helpers::FormBuilder+.
|
7
|
+
#
|
8
|
+
# For example, given a form builder:
|
9
|
+
#
|
10
|
+
# class AdminFormBuilder < ActionView::Helpers::FormBuilder
|
11
|
+
# def special_field(name)
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# The controller specifies a form builder as its default:
|
16
|
+
#
|
17
|
+
# class AdminAreaController < ApplicationController
|
18
|
+
# default_form_builder AdminFormBuilder
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Then in the view any form using +form_for+ will be an instance of the
|
22
|
+
# specified form builder:
|
23
|
+
#
|
24
|
+
# <%= form_for(@instance) do |builder| %>
|
25
|
+
# <%= builder.special_field(:name) %>
|
26
|
+
# <% end %>
|
27
|
+
module FormBuilder
|
28
|
+
extend ActiveSupport::Concern
|
29
|
+
|
30
|
+
included do
|
31
|
+
class_attribute :_default_form_builder, instance_accessor: false
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
# Set the form builder to be used as the default for all forms
|
36
|
+
# in the views rendered by this controller and its subclasses.
|
37
|
+
#
|
38
|
+
# ==== Parameters
|
39
|
+
# * <tt>builder</tt> - Default form builder, an instance of +ActionView::Helpers::FormBuilder+
|
40
|
+
def default_form_builder(builder)
|
41
|
+
self._default_form_builder = builder
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Default form builder for the controller
|
46
|
+
def default_form_builder
|
47
|
+
self.class._default_form_builder
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionController
|
2
4
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
3
5
|
INTERNAL_PARAMS = %w(controller action format _method only_path)
|
@@ -24,8 +26,10 @@ module ActionController
|
|
24
26
|
exception_class_name = payload[:exception].first
|
25
27
|
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
26
28
|
end
|
27
|
-
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
28
|
-
message << " (#{additions.join(" | ")})" unless additions.
|
29
|
+
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms".dup
|
30
|
+
message << " (#{additions.join(" | ".freeze)})" unless additions.empty?
|
31
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
32
|
+
|
29
33
|
message
|
30
34
|
end
|
31
35
|
end
|
@@ -49,16 +53,7 @@ module ActionController
|
|
49
53
|
def unpermitted_parameters(event)
|
50
54
|
debug do
|
51
55
|
unpermitted_keys = event.payload[:keys]
|
52
|
-
"Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def deep_munge(event)
|
57
|
-
debug do
|
58
|
-
"Value for params[:#{event.payload[:keys].join('][:')}] was set "\
|
59
|
-
"to nil, because it was one of [], [null] or [null, null, ...]. "\
|
60
|
-
"Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
|
61
|
-
"for more information."\
|
56
|
+
"Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}"
|
62
57
|
end
|
63
58
|
end
|
64
59
|
|
@@ -66,10 +61,10 @@ module ActionController
|
|
66
61
|
expire_fragment expire_page write_page).each do |method|
|
67
62
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
68
63
|
def #{method}(event)
|
69
|
-
return unless logger.info?
|
70
|
-
|
64
|
+
return unless logger.info? && ActionController::Base.enable_fragment_cache_logging
|
65
|
+
key = ActiveSupport::Cache.expand_cache_key(event.payload[:key] || event.payload[:path])
|
71
66
|
human_name = #{method.to_s.humanize.inspect}
|
72
|
-
info("\#{human_name} \#{
|
67
|
+
info("\#{human_name} \#{key} (\#{event.duration.round(1)}ms)")
|
73
68
|
end
|
74
69
|
METHOD
|
75
70
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
module BasicImplicitRender # :nodoc:
|
5
|
+
def send_action(method, *args)
|
6
|
+
super.tap { default_render unless performed? }
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_render(*args)
|
10
|
+
head :no_content
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,21 +1,21 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/keys"
|
2
4
|
|
3
5
|
module ActionController
|
4
6
|
module ConditionalGet
|
5
7
|
extend ActiveSupport::Concern
|
6
8
|
|
7
|
-
include RackDelegation
|
8
9
|
include Head
|
9
10
|
|
10
11
|
included do
|
11
|
-
class_attribute :etaggers
|
12
|
-
self.etaggers = []
|
12
|
+
class_attribute :etaggers, default: []
|
13
13
|
end
|
14
14
|
|
15
15
|
module ClassMethods
|
16
16
|
# Allows you to consider additional controller-wide information when generating an ETag.
|
17
17
|
# 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
|
18
|
+
# may want to add the current user id to be part of the ETag to prevent unauthorized displaying
|
19
19
|
# of cached pages.
|
20
20
|
#
|
21
21
|
# class InvoicesController < ApplicationController
|
@@ -37,10 +37,25 @@ module ActionController
|
|
37
37
|
#
|
38
38
|
# === Parameters:
|
39
39
|
#
|
40
|
-
# * <tt>:etag</tt
|
41
|
-
#
|
40
|
+
# * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
|
41
|
+
# +:weak_etag+ option.
|
42
|
+
# * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
|
43
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
44
|
+
# response if it matches the ETag exactly. A weak ETag indicates semantic
|
45
|
+
# equivalence, not byte-for-byte equality, so they're good for caching
|
46
|
+
# HTML pages in browser caches. They can't be used for responses that
|
47
|
+
# must be byte-identical, like serving Range requests within a PDF file.
|
48
|
+
# * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
|
49
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
50
|
+
# response if it matches the ETag exactly. A strong ETag implies exact
|
51
|
+
# equality: the response must match byte for byte. This is necessary for
|
52
|
+
# doing Range requests within a large video or PDF file, for example, or
|
53
|
+
# for compatibility with some CDNs that don't support weak ETags.
|
54
|
+
# * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
|
55
|
+
# response. Subsequent requests that set If-Modified-Since may return a
|
56
|
+
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
42
57
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
43
|
-
# +true+ if you want your application to be
|
58
|
+
# +true+ if you want your application to be cacheable by other devices (proxy caches).
|
44
59
|
# * <tt>:template</tt> By default, the template digest for the current
|
45
60
|
# controller/action is included in ETags. If the action renders a
|
46
61
|
# different template, you can include its digest instead. If the action
|
@@ -51,21 +66,31 @@ module ActionController
|
|
51
66
|
#
|
52
67
|
# def show
|
53
68
|
# @article = Article.find(params[:id])
|
54
|
-
# fresh_when(etag: @article, last_modified: @article.
|
69
|
+
# fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
|
55
70
|
# end
|
56
71
|
#
|
57
72
|
# This will render the show template if the request isn't sending a matching ETag or
|
58
73
|
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
59
74
|
#
|
60
|
-
# You can also just pass a record
|
61
|
-
# +updated_at+ and
|
75
|
+
# You can also just pass a record. In this case +last_modified+ will be set
|
76
|
+
# by calling +updated_at+ and +etag+ by passing the object itself.
|
62
77
|
#
|
63
78
|
# def show
|
64
79
|
# @article = Article.find(params[:id])
|
65
80
|
# fresh_when(@article)
|
66
81
|
# end
|
67
82
|
#
|
68
|
-
#
|
83
|
+
# You can also pass an object that responds to +maximum+, such as a
|
84
|
+
# collection of active records. In this case +last_modified+ will be set by
|
85
|
+
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
86
|
+
# most recently updated record) and the +etag+ by passing the object itself.
|
87
|
+
#
|
88
|
+
# def index
|
89
|
+
# @articles = Article.all
|
90
|
+
# fresh_when(@articles)
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# When passing a record or a collection, you can still set the public header:
|
69
94
|
#
|
70
95
|
# def show
|
71
96
|
# @article = Article.find(params[:id])
|
@@ -77,18 +102,20 @@ module ActionController
|
|
77
102
|
#
|
78
103
|
# before_action { fresh_when @article, template: 'widgets/show' }
|
79
104
|
#
|
80
|
-
def fresh_when(
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
105
|
+
def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
|
106
|
+
weak_etag ||= etag || object unless strong_etag
|
107
|
+
last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
|
108
|
+
|
109
|
+
if strong_etag
|
110
|
+
response.strong_etag = combine_etags strong_etag,
|
111
|
+
last_modified: last_modified, public: public, template: template
|
112
|
+
elsif weak_etag || template
|
113
|
+
response.weak_etag = combine_etags weak_etag,
|
114
|
+
last_modified: last_modified, public: public, template: template
|
87
115
|
end
|
88
116
|
|
89
|
-
response.
|
90
|
-
response.
|
91
|
-
response.cache_control[:public] = true if options[:public]
|
117
|
+
response.last_modified = last_modified if last_modified
|
118
|
+
response.cache_control[:public] = true if public
|
92
119
|
|
93
120
|
head :not_modified if request.fresh?(response)
|
94
121
|
end
|
@@ -100,10 +127,25 @@ module ActionController
|
|
100
127
|
#
|
101
128
|
# === Parameters:
|
102
129
|
#
|
103
|
-
# * <tt>:etag</tt
|
104
|
-
#
|
130
|
+
# * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
|
131
|
+
# +:weak_etag+ option.
|
132
|
+
# * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
|
133
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
134
|
+
# response if it matches the ETag exactly. A weak ETag indicates semantic
|
135
|
+
# equivalence, not byte-for-byte equality, so they're good for caching
|
136
|
+
# HTML pages in browser caches. They can't be used for responses that
|
137
|
+
# must be byte-identical, like serving Range requests within a PDF file.
|
138
|
+
# * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
|
139
|
+
# Requests that set If-None-Match header may return a 304 Not Modified
|
140
|
+
# response if it matches the ETag exactly. A strong ETag implies exact
|
141
|
+
# equality: the response must match byte for byte. This is necessary for
|
142
|
+
# doing Range requests within a large video or PDF file, for example, or
|
143
|
+
# for compatibility with some CDNs that don't support weak ETags.
|
144
|
+
# * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
|
145
|
+
# response. Subsequent requests that set If-Modified-Since may return a
|
146
|
+
# 304 Not Modified response if last_modified <= If-Modified-Since.
|
105
147
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
106
|
-
# +true+ if you want your application to be
|
148
|
+
# +true+ if you want your application to be cacheable by other devices (proxy caches).
|
107
149
|
# * <tt>:template</tt> By default, the template digest for the current
|
108
150
|
# controller/action is included in ETags. If the action renders a
|
109
151
|
# different template, you can include its digest instead. If the action
|
@@ -115,7 +157,7 @@ module ActionController
|
|
115
157
|
# def show
|
116
158
|
# @article = Article.find(params[:id])
|
117
159
|
#
|
118
|
-
# if stale?(etag: @article, last_modified: @article.
|
160
|
+
# if stale?(etag: @article, last_modified: @article.updated_at)
|
119
161
|
# @statistics = @article.really_expensive_call
|
120
162
|
# respond_to do |format|
|
121
163
|
# # all the supported formats
|
@@ -123,8 +165,8 @@ module ActionController
|
|
123
165
|
# end
|
124
166
|
# end
|
125
167
|
#
|
126
|
-
# You can also just pass a record
|
127
|
-
# +updated_at+ and
|
168
|
+
# You can also just pass a record. In this case +last_modified+ will be set
|
169
|
+
# by calling +updated_at+ and +etag+ by passing the object itself.
|
128
170
|
#
|
129
171
|
# def show
|
130
172
|
# @article = Article.find(params[:id])
|
@@ -137,7 +179,23 @@ module ActionController
|
|
137
179
|
# end
|
138
180
|
# end
|
139
181
|
#
|
140
|
-
#
|
182
|
+
# You can also pass an object that responds to +maximum+, such as a
|
183
|
+
# collection of active records. In this case +last_modified+ will be set by
|
184
|
+
# calling +maximum(:updated_at)+ on the collection (the timestamp of the
|
185
|
+
# most recently updated record) and the +etag+ by passing the object itself.
|
186
|
+
#
|
187
|
+
# def index
|
188
|
+
# @articles = Article.all
|
189
|
+
#
|
190
|
+
# if stale?(@articles)
|
191
|
+
# @statistics = @articles.really_expensive_call
|
192
|
+
# respond_to do |format|
|
193
|
+
# # all the supported formats
|
194
|
+
# end
|
195
|
+
# end
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# When passing a record or a collection, you can still set the public header:
|
141
199
|
#
|
142
200
|
# def show
|
143
201
|
# @article = Article.find(params[:id])
|
@@ -157,12 +215,12 @@ module ActionController
|
|
157
215
|
# super if stale? @article, template: 'widgets/show'
|
158
216
|
# end
|
159
217
|
#
|
160
|
-
def stale?(
|
161
|
-
fresh_when(
|
218
|
+
def stale?(object = nil, **freshness_kwargs)
|
219
|
+
fresh_when(object, **freshness_kwargs)
|
162
220
|
!request.fresh?(response)
|
163
221
|
end
|
164
222
|
|
165
|
-
# Sets
|
223
|
+
# Sets an HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
|
166
224
|
# instruction, so that intermediate caches must not cache the response.
|
167
225
|
#
|
168
226
|
# expires_in 20.minutes
|
@@ -170,31 +228,47 @@ module ActionController
|
|
170
228
|
# expires_in 3.hours, public: true, must_revalidate: true
|
171
229
|
#
|
172
230
|
# This method will overwrite an existing Cache-Control header.
|
173
|
-
# See
|
231
|
+
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
174
232
|
#
|
175
|
-
# The method will also ensure
|
233
|
+
# The method will also ensure an HTTP Date header for client compatibility.
|
176
234
|
def expires_in(seconds, options = {})
|
177
235
|
response.cache_control.merge!(
|
178
|
-
:
|
179
|
-
:
|
180
|
-
:
|
236
|
+
max_age: seconds,
|
237
|
+
public: options.delete(:public),
|
238
|
+
must_revalidate: options.delete(:must_revalidate)
|
181
239
|
)
|
182
240
|
options.delete(:private)
|
183
241
|
|
184
|
-
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
242
|
+
response.cache_control[:extras] = options.map { |k, v| "#{k}=#{v}" }
|
185
243
|
response.date = Time.now unless response.date?
|
186
244
|
end
|
187
245
|
|
188
|
-
# Sets
|
189
|
-
#
|
246
|
+
# Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt>. This means the
|
247
|
+
# resource will be marked as stale, so clients must always revalidate.
|
248
|
+
# Intermediate/browser caches may still store the asset.
|
190
249
|
def expires_now
|
191
|
-
response.cache_control.replace(:
|
250
|
+
response.cache_control.replace(no_cache: true)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Cache or yield the block. The cache is supposed to never expire.
|
254
|
+
#
|
255
|
+
# You can use this method when you have an HTTP response that never changes,
|
256
|
+
# and the browser and proxies should cache it indefinitely.
|
257
|
+
#
|
258
|
+
# * +public+: By default, HTTP responses are private, cached only on the
|
259
|
+
# user's web browser. To allow proxies to cache the response, set +true+ to
|
260
|
+
# indicate that they can serve the cached response to all users.
|
261
|
+
def http_cache_forever(public: false)
|
262
|
+
expires_in 100.years, public: public
|
263
|
+
|
264
|
+
yield if stale?(etag: request.fullpath,
|
265
|
+
last_modified: Time.new(2011, 1, 1).utc,
|
266
|
+
public: public)
|
192
267
|
end
|
193
268
|
|
194
269
|
private
|
195
|
-
def combine_etags(options)
|
196
|
-
|
197
|
-
etags.unshift options[:etag]
|
270
|
+
def combine_etags(validator, options)
|
271
|
+
[validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
|
198
272
|
end
|
199
273
|
end
|
200
274
|
end
|
@@ -0,0 +1,52 @@
|
|
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
|
+
|
40
|
+
def content_security_policy?
|
41
|
+
request.content_security_policy
|
42
|
+
end
|
43
|
+
|
44
|
+
def content_security_policy_nonce
|
45
|
+
request.content_security_policy_nonce
|
46
|
+
end
|
47
|
+
|
48
|
+
def current_content_security_policy
|
49
|
+
request.content_security_policy.try(:clone) || ActionDispatch::ContentSecurityPolicy.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,11 +1,11 @@
|
|
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
|