actionpack 3.2.22.5 → 5.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +279 -603
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -297
- data/lib/abstract_controller/asset_paths.rb +4 -2
- data/lib/abstract_controller/base.rb +82 -52
- data/lib/abstract_controller/caching/fragments.rb +166 -0
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +117 -103
- data/lib/abstract_controller/collector.rb +18 -7
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +65 -38
- data/lib/abstract_controller/logger.rb +3 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
- data/lib/abstract_controller/rendering.rb +77 -129
- data/lib/abstract_controller/translation.rb +21 -3
- data/lib/abstract_controller/url_for.rb +9 -7
- data/lib/abstract_controller.rb +12 -13
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/base.rb +81 -40
- data/lib/action_controller/caching.rb +22 -62
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +30 -18
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +190 -47
- 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 +40 -65
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
- data/lib/action_controller/metal/exceptions.rb +19 -12
- data/lib/action_controller/metal/flash.rb +42 -9
- data/lib/action_controller/metal/force_ssl.rb +79 -19
- data/lib/action_controller/metal/head.rb +35 -10
- data/lib/action_controller/metal/helpers.rb +31 -21
- data/lib/action_controller/metal/http_authentication.rb +182 -134
- data/lib/action_controller/metal/implicit_render.rb +62 -8
- data/lib/action_controller/metal/instrumentation.rb +28 -26
- data/lib/action_controller/metal/live.rb +312 -0
- data/lib/action_controller/metal/mime_responds.rb +159 -163
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +146 -93
- data/lib/action_controller/metal/redirecting.rb +80 -56
- data/lib/action_controller/metal/renderers.rb +119 -47
- data/lib/action_controller/metal/rendering.rb +89 -32
- data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
- data/lib/action_controller/metal/rescue.rb +9 -16
- data/lib/action_controller/metal/streaming.rb +39 -45
- data/lib/action_controller/metal/strong_parameters.rb +1086 -0
- data/lib/action_controller/metal/testing.rb +8 -29
- data/lib/action_controller/metal/url_for.rb +43 -32
- data/lib/action_controller/metal.rb +112 -106
- data/lib/action_controller/railtie.rb +56 -18
- data/lib/action_controller/railties/helpers.rb +24 -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 +402 -347
- data/lib/action_controller.rb +31 -30
- data/lib/action_dispatch/http/cache.rb +133 -34
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +40 -24
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +117 -16
- data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
- data/lib/action_dispatch/http/mime_type.rb +198 -146
- data/lib/action_dispatch/http/mime_types.rb +22 -7
- data/lib/action_dispatch/http/parameter_filter.rb +61 -49
- data/lib/action_dispatch/http/parameters.rb +94 -51
- data/lib/action_dispatch/http/rack_cache.rb +4 -3
- data/lib/action_dispatch/http/request.rb +262 -117
- data/lib/action_dispatch/http/response.rb +400 -86
- data/lib/action_dispatch/http/upload.rb +66 -29
- data/lib/action_dispatch/http/url.rb +232 -60
- data/lib/action_dispatch/journey/formatter.rb +189 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
- data/lib/action_dispatch/journey/nodes/node.rb +140 -0
- data/lib/action_dispatch/journey/parser.rb +199 -0
- data/lib/action_dispatch/journey/parser.y +50 -0
- data/lib/action_dispatch/journey/parser_extras.rb +31 -0
- data/lib/action_dispatch/journey/path/pattern.rb +199 -0
- data/lib/action_dispatch/journey/route.rb +203 -0
- data/lib/action_dispatch/journey/router/utils.rb +102 -0
- data/lib/action_dispatch/journey/router.rb +156 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +64 -0
- data/lib/action_dispatch/journey/visitors.rb +268 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/middleware/callbacks.rb +17 -13
- data/lib/action_dispatch/middleware/cookies.rb +494 -162
- data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +128 -91
- data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
- data/lib/action_dispatch/middleware/reloader.rb +6 -83
- data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
- data/lib/action_dispatch/middleware/request_id.rb +19 -15
- data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
- data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
- data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
- data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
- data/lib/action_dispatch/middleware/ssl.rb +150 -0
- data/lib/action_dispatch/middleware/stack.rb +33 -41
- data/lib/action_dispatch/middleware/static.rb +92 -48
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -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 +134 -5
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +29 -8
- data/lib/action_dispatch/request/session.rb +234 -0
- data/lib/action_dispatch/request/utils.rb +78 -0
- data/lib/action_dispatch/routing/endpoint.rb +17 -0
- data/lib/action_dispatch/routing/inspector.rb +225 -0
- data/lib/action_dispatch/routing/mapper.rb +1329 -582
- data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
- data/lib/action_dispatch/routing/redirection.rb +120 -50
- data/lib/action_dispatch/routing/route_set.rb +545 -322
- data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
- data/lib/action_dispatch/routing/url_for.rb +103 -34
- data/lib/action_dispatch/routing.rb +66 -99
- 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 +53 -42
- data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
- data/lib/action_dispatch/testing/assertions.rb +15 -9
- data/lib/action_dispatch/testing/integration.rb +361 -207
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +28 -19
- data/lib/action_dispatch/testing/test_request.rb +30 -33
- data/lib/action_dispatch/testing/test_response.rb +35 -11
- data/lib/action_dispatch.rb +42 -32
- data/lib/action_pack/gem_version.rb +17 -0
- data/lib/action_pack/version.rb +7 -7
- data/lib/action_pack.rb +4 -2
- metadata +116 -175
- data/lib/abstract_controller/layouts.rb +0 -423
- data/lib/abstract_controller/view_paths.rb +0 -96
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/fragments.rb +0 -127
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/integration_test.rb +0 -2
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/deprecated.rb +0 -3
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/hide_actions.rb +0 -41
- data/lib/action_controller/metal/rack_delegation.rb +0 -26
- data/lib/action_controller/metal/responder.rb +0 -286
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/railties/paths.rb +0 -25
- data/lib/action_controller/record_identifier.rb +0 -85
- data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
- data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
- data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
- data/lib/action_controller/vendor/html-scanner.rb +0 -20
- data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
- data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
- data/lib/action_dispatch/middleware/head.rb +0 -18
- data/lib/action_dispatch/middleware/params_parser.rb +0 -75
- data/lib/action_dispatch/middleware/rescue.rb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/base.rb +0 -220
- data/lib/action_view/buffers.rb +0 -43
- data/lib/action_view/context.rb +0 -36
- data/lib/action_view/flows.rb +0 -79
- data/lib/action_view/helpers/active_model_helper.rb +0 -50
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
- data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
- data/lib/action_view/helpers/cache_helper.rb +0 -64
- data/lib/action_view/helpers/capture_helper.rb +0 -203
- data/lib/action_view/helpers/controller_helper.rb +0 -25
- data/lib/action_view/helpers/csrf_helper.rb +0 -32
- data/lib/action_view/helpers/date_helper.rb +0 -1062
- data/lib/action_view/helpers/debug_helper.rb +0 -40
- data/lib/action_view/helpers/form_helper.rb +0 -1486
- data/lib/action_view/helpers/form_options_helper.rb +0 -658
- data/lib/action_view/helpers/form_tag_helper.rb +0 -685
- data/lib/action_view/helpers/javascript_helper.rb +0 -110
- data/lib/action_view/helpers/number_helper.rb +0 -622
- data/lib/action_view/helpers/output_safety_helper.rb +0 -38
- data/lib/action_view/helpers/record_tag_helper.rb +0 -111
- data/lib/action_view/helpers/rendering_helper.rb +0 -92
- data/lib/action_view/helpers/sanitize_helper.rb +0 -259
- data/lib/action_view/helpers/tag_helper.rb +0 -167
- data/lib/action_view/helpers/text_helper.rb +0 -426
- data/lib/action_view/helpers/translation_helper.rb +0 -91
- data/lib/action_view/helpers/url_helper.rb +0 -693
- data/lib/action_view/helpers.rb +0 -60
- data/lib/action_view/locale/en.yml +0 -160
- data/lib/action_view/log_subscriber.rb +0 -28
- data/lib/action_view/lookup_context.rb +0 -258
- data/lib/action_view/path_set.rb +0 -101
- data/lib/action_view/railtie.rb +0 -55
- data/lib/action_view/renderer/abstract_renderer.rb +0 -41
- data/lib/action_view/renderer/partial_renderer.rb +0 -415
- data/lib/action_view/renderer/renderer.rb +0 -61
- data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
- data/lib/action_view/renderer/template_renderer.rb +0 -95
- data/lib/action_view/template/error.rb +0 -128
- data/lib/action_view/template/handlers/builder.rb +0 -26
- data/lib/action_view/template/handlers/erb.rb +0 -125
- data/lib/action_view/template/handlers.rb +0 -50
- data/lib/action_view/template/resolver.rb +0 -298
- data/lib/action_view/template/text.rb +0 -30
- data/lib/action_view/template.rb +0 -337
- data/lib/action_view/test_case.rb +0 -246
- data/lib/action_view/testing/resolvers.rb +0 -49
- data/lib/action_view.rb +0 -84
- data/lib/sprockets/assets.rake +0 -99
- data/lib/sprockets/bootstrap.rb +0 -37
- data/lib/sprockets/compressors.rb +0 -83
- data/lib/sprockets/helpers/isolated_helper.rb +0 -13
- data/lib/sprockets/helpers/rails_helper.rb +0 -182
- data/lib/sprockets/helpers.rb +0 -6
- data/lib/sprockets/railtie.rb +0 -62
- data/lib/sprockets/static_compiler.rb +0 -56
data/lib/action_controller.rb
CHANGED
|
@@ -1,65 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/rails"
|
|
4
|
+
require "abstract_controller"
|
|
5
|
+
require "action_dispatch"
|
|
6
|
+
require "action_controller/metal/live"
|
|
7
|
+
require "action_controller/metal/strong_parameters"
|
|
3
8
|
|
|
4
9
|
module ActionController
|
|
5
10
|
extend ActiveSupport::Autoload
|
|
6
11
|
|
|
12
|
+
autoload :API
|
|
7
13
|
autoload :Base
|
|
8
|
-
autoload :Caching
|
|
9
14
|
autoload :Metal
|
|
10
15
|
autoload :Middleware
|
|
16
|
+
autoload :Renderer
|
|
17
|
+
autoload :FormBuilder
|
|
18
|
+
|
|
19
|
+
eager_autoload do
|
|
20
|
+
autoload :Caching
|
|
21
|
+
end
|
|
11
22
|
|
|
12
23
|
autoload_under "metal" do
|
|
13
|
-
autoload :Compatibility
|
|
14
24
|
autoload :ConditionalGet
|
|
25
|
+
autoload :ContentSecurityPolicy
|
|
15
26
|
autoload :Cookies
|
|
16
27
|
autoload :DataStreaming
|
|
28
|
+
autoload :EtagWithTemplateDigest
|
|
29
|
+
autoload :EtagWithFlash
|
|
17
30
|
autoload :Flash
|
|
18
31
|
autoload :ForceSSL
|
|
19
32
|
autoload :Head
|
|
20
33
|
autoload :Helpers
|
|
21
|
-
autoload :HideActions
|
|
22
34
|
autoload :HttpAuthentication
|
|
35
|
+
autoload :BasicImplicitRender
|
|
23
36
|
autoload :ImplicitRender
|
|
24
37
|
autoload :Instrumentation
|
|
25
38
|
autoload :MimeResponds
|
|
26
39
|
autoload :ParamsWrapper
|
|
27
|
-
autoload :RackDelegation
|
|
28
40
|
autoload :Redirecting
|
|
29
41
|
autoload :Renderers
|
|
30
42
|
autoload :Rendering
|
|
31
43
|
autoload :RequestForgeryProtection
|
|
32
44
|
autoload :Rescue
|
|
33
|
-
autoload :Responder
|
|
34
|
-
autoload :SessionManagement
|
|
35
45
|
autoload :Streaming
|
|
46
|
+
autoload :StrongParameters
|
|
47
|
+
autoload :ParameterEncoding
|
|
36
48
|
autoload :Testing
|
|
37
49
|
autoload :UrlFor
|
|
38
50
|
end
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
|
|
43
|
-
autoload :UrlWriter, 'action_controller/deprecated'
|
|
44
|
-
autoload :Routing, 'action_controller/deprecated'
|
|
45
|
-
autoload :TestCase, 'action_controller/test_case'
|
|
46
|
-
autoload :TemplateAssertions, 'action_controller/test_case'
|
|
47
|
-
|
|
48
|
-
eager_autoload do
|
|
49
|
-
autoload :RecordIdentifier
|
|
52
|
+
autoload_under "api" do
|
|
53
|
+
autoload :ApiRendering
|
|
50
54
|
end
|
|
51
|
-
end
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
autoload :TestCase, "action_controller/test_case"
|
|
57
|
+
autoload :TemplateAssertions, "action_controller/test_case"
|
|
58
|
+
end
|
|
56
59
|
|
|
57
60
|
# Common Active Support usage in Action Controller
|
|
58
|
-
require
|
|
59
|
-
require
|
|
60
|
-
require
|
|
61
|
-
require
|
|
62
|
-
require
|
|
63
|
-
require
|
|
64
|
-
require 'active_support/core_ext/uri'
|
|
65
|
-
require 'active_support/inflector'
|
|
61
|
+
require "active_support/core_ext/module/attribute_accessors"
|
|
62
|
+
require "active_support/core_ext/load_error"
|
|
63
|
+
require "active_support/core_ext/module/attr_internal"
|
|
64
|
+
require "active_support/core_ext/name_error"
|
|
65
|
+
require "active_support/core_ext/uri"
|
|
66
|
+
require "active_support/inflector"
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ActionDispatch
|
|
4
4
|
module Http
|
|
5
5
|
module Cache
|
|
6
6
|
module Request
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
|
|
7
|
+
HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE".freeze
|
|
8
|
+
HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH".freeze
|
|
10
9
|
|
|
11
10
|
def if_modified_since
|
|
12
|
-
if since =
|
|
11
|
+
if since = get_header(HTTP_IF_MODIFIED_SINCE)
|
|
13
12
|
Time.rfc2822(since) rescue nil
|
|
14
13
|
end
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def if_none_match
|
|
18
|
-
|
|
17
|
+
get_header HTTP_IF_NONE_MATCH
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def if_none_match_etags
|
|
21
|
+
if_none_match ? if_none_match.split(/\s*,\s*/) : []
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def not_modified?(modified_at)
|
|
@@ -23,7 +26,10 @@ module ActionDispatch
|
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
def etag_matches?(etag)
|
|
26
|
-
|
|
29
|
+
if etag
|
|
30
|
+
validators = if_none_match_etags
|
|
31
|
+
validators.include?(etag) || validators.include?("*")
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
# Check response freshness (Last-Modified and ETag) against request
|
|
@@ -43,50 +49,121 @@ module ActionDispatch
|
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
module Response
|
|
46
|
-
attr_reader :cache_control
|
|
47
|
-
alias :etag? :etag
|
|
52
|
+
attr_reader :cache_control
|
|
48
53
|
|
|
49
54
|
def last_modified
|
|
50
|
-
if last =
|
|
55
|
+
if last = get_header(LAST_MODIFIED)
|
|
51
56
|
Time.httpdate(last)
|
|
52
57
|
end
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
def last_modified?
|
|
56
|
-
|
|
61
|
+
has_header? LAST_MODIFIED
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
def last_modified=(utc_time)
|
|
60
|
-
|
|
65
|
+
set_header LAST_MODIFIED, utc_time.httpdate
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def date
|
|
69
|
+
if date_header = get_header(DATE)
|
|
70
|
+
Time.httpdate(date_header)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def date?
|
|
75
|
+
has_header? DATE
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def date=(utc_time)
|
|
79
|
+
set_header DATE, utc_time.httpdate
|
|
61
80
|
end
|
|
62
81
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
# This method sets a weak ETag validator on the response so browsers
|
|
83
|
+
# and proxies may cache the response, keyed on the ETag. On subsequent
|
|
84
|
+
# requests, the If-None-Match header is set to the cached ETag. If it
|
|
85
|
+
# matches the current ETag, we can return a 304 Not Modified response
|
|
86
|
+
# with no body, letting the browser or proxy know that their cache is
|
|
87
|
+
# current. Big savings in request time and network bandwidth.
|
|
88
|
+
#
|
|
89
|
+
# Weak ETags are considered to be semantically equivalent but not
|
|
90
|
+
# byte-for-byte identical. This is perfect for browser caching of HTML
|
|
91
|
+
# pages where we don't care about exact equality, just what the user
|
|
92
|
+
# is viewing.
|
|
93
|
+
#
|
|
94
|
+
# Strong ETags are considered byte-for-byte identical. They allow a
|
|
95
|
+
# browser or proxy cache to support Range requests, useful for paging
|
|
96
|
+
# through a PDF file or scrubbing through a video. Some CDNs only
|
|
97
|
+
# support strong ETags and will ignore weak ETags entirely.
|
|
98
|
+
#
|
|
99
|
+
# Weak ETags are what we almost always need, so they're the default.
|
|
100
|
+
# Check out #strong_etag= to provide a strong ETag validator.
|
|
101
|
+
def etag=(weak_validators)
|
|
102
|
+
self.weak_etag = weak_validators
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def weak_etag=(weak_validators)
|
|
106
|
+
set_header "ETag", generate_weak_etag(weak_validators)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def strong_etag=(strong_validators)
|
|
110
|
+
set_header "ETag", generate_strong_etag(strong_validators)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def etag?; etag; end
|
|
114
|
+
|
|
115
|
+
# True if an ETag is set and it's a weak validator (preceded with W/)
|
|
116
|
+
def weak_etag?
|
|
117
|
+
etag? && etag.starts_with?('W/"')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# True if an ETag is set and it isn't a weak validator (not preceded with W/)
|
|
121
|
+
def strong_etag?
|
|
122
|
+
etag? && !weak_etag?
|
|
66
123
|
end
|
|
67
124
|
|
|
68
125
|
private
|
|
69
126
|
|
|
127
|
+
DATE = "Date".freeze
|
|
70
128
|
LAST_MODIFIED = "Last-Modified".freeze
|
|
71
|
-
|
|
72
|
-
CACHE_CONTROL = "Cache-Control".freeze
|
|
129
|
+
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
|
|
73
130
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
131
|
+
def generate_weak_etag(validators)
|
|
132
|
+
"W/#{generate_strong_etag(validators)}"
|
|
133
|
+
end
|
|
77
134
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
135
|
+
def generate_strong_etag(validators)
|
|
136
|
+
%("#{ActiveSupport::Digest.hexdigest(ActiveSupport::Cache.expand_cache_key(validators))}")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def cache_control_segments
|
|
140
|
+
if cache_control = _cache_control
|
|
141
|
+
cache_control.delete(" ").split(",")
|
|
142
|
+
else
|
|
143
|
+
[]
|
|
83
144
|
end
|
|
84
145
|
end
|
|
85
146
|
|
|
86
|
-
def
|
|
87
|
-
|
|
88
|
-
|
|
147
|
+
def cache_control_headers
|
|
148
|
+
cache_control = {}
|
|
149
|
+
|
|
150
|
+
cache_control_segments.each do |segment|
|
|
151
|
+
directive, argument = segment.split("=", 2)
|
|
152
|
+
|
|
153
|
+
if SPECIAL_KEYS.include? directive
|
|
154
|
+
key = directive.tr("-", "_")
|
|
155
|
+
cache_control[key.to_sym] = argument || true
|
|
156
|
+
else
|
|
157
|
+
cache_control[:extras] ||= []
|
|
158
|
+
cache_control[:extras] << segment
|
|
159
|
+
end
|
|
89
160
|
end
|
|
161
|
+
|
|
162
|
+
cache_control
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def prepare_cache_control!
|
|
166
|
+
@cache_control = cache_control_headers
|
|
90
167
|
end
|
|
91
168
|
|
|
92
169
|
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
|
|
@@ -95,15 +172,37 @@ module ActionDispatch
|
|
|
95
172
|
PRIVATE = "private".freeze
|
|
96
173
|
MUST_REVALIDATE = "must-revalidate".freeze
|
|
97
174
|
|
|
98
|
-
def
|
|
99
|
-
|
|
175
|
+
def handle_conditional_get!
|
|
176
|
+
# Normally default cache control setting is handled by ETag
|
|
177
|
+
# middleware. But, if an etag is already set, the middleware
|
|
178
|
+
# defaults to `no-cache` unless a default `Cache-Control` value is
|
|
179
|
+
# previously set. So, set a default one here.
|
|
180
|
+
if (etag? || last_modified?) && !self._cache_control
|
|
181
|
+
self._cache_control = DEFAULT_CACHE_CONTROL
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def merge_and_normalize_cache_control!(cache_control)
|
|
186
|
+
control = {}
|
|
187
|
+
cc_headers = cache_control_headers
|
|
188
|
+
if extras = cc_headers.delete(:extras)
|
|
189
|
+
cache_control[:extras] ||= []
|
|
190
|
+
cache_control[:extras] += extras
|
|
191
|
+
cache_control[:extras].uniq!
|
|
192
|
+
end
|
|
100
193
|
|
|
101
|
-
control
|
|
194
|
+
control.merge! cc_headers
|
|
195
|
+
control.merge! cache_control
|
|
102
196
|
|
|
103
197
|
if control.empty?
|
|
104
|
-
|
|
198
|
+
# Let middleware handle default behavior
|
|
105
199
|
elsif control[:no_cache]
|
|
106
|
-
|
|
200
|
+
options = []
|
|
201
|
+
options << PUBLIC if control[:public]
|
|
202
|
+
options << NO_CACHE
|
|
203
|
+
options.concat(control[:extras]) if control[:extras]
|
|
204
|
+
|
|
205
|
+
self._cache_control = options.join(", ")
|
|
107
206
|
else
|
|
108
207
|
extras = control[:extras]
|
|
109
208
|
max_age = control[:max_age]
|
|
@@ -114,7 +213,7 @@ module ActionDispatch
|
|
|
114
213
|
options << MUST_REVALIDATE if control[:must_revalidate]
|
|
115
214
|
options.concat(extras) if extras
|
|
116
215
|
|
|
117
|
-
|
|
216
|
+
self._cache_control = options.join(", ")
|
|
118
217
|
end
|
|
119
218
|
end
|
|
120
219
|
end
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/object/deep_dup"
|
|
4
|
+
|
|
5
|
+
module ActionDispatch #:nodoc:
|
|
6
|
+
class ContentSecurityPolicy
|
|
7
|
+
class Middleware
|
|
8
|
+
CONTENT_TYPE = "Content-Type".freeze
|
|
9
|
+
POLICY = "Content-Security-Policy".freeze
|
|
10
|
+
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only".freeze
|
|
11
|
+
|
|
12
|
+
def initialize(app)
|
|
13
|
+
@app = app
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(env)
|
|
17
|
+
request = ActionDispatch::Request.new env
|
|
18
|
+
_, headers, _ = response = @app.call(env)
|
|
19
|
+
|
|
20
|
+
return response unless html_response?(headers)
|
|
21
|
+
return response if policy_present?(headers)
|
|
22
|
+
|
|
23
|
+
if policy = request.content_security_policy
|
|
24
|
+
nonce = request.content_security_policy_nonce
|
|
25
|
+
context = request.controller_instance || request
|
|
26
|
+
headers[header_name(request)] = policy.build(context, nonce)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
response
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def html_response?(headers)
|
|
35
|
+
if content_type = headers[CONTENT_TYPE]
|
|
36
|
+
content_type =~ /html/
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def header_name(request)
|
|
41
|
+
if request.content_security_policy_report_only
|
|
42
|
+
POLICY_REPORT_ONLY
|
|
43
|
+
else
|
|
44
|
+
POLICY
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def policy_present?(headers)
|
|
49
|
+
headers[POLICY] || headers[POLICY_REPORT_ONLY]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module Request
|
|
54
|
+
POLICY = "action_dispatch.content_security_policy".freeze
|
|
55
|
+
POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only".freeze
|
|
56
|
+
NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator".freeze
|
|
57
|
+
NONCE = "action_dispatch.content_security_policy_nonce".freeze
|
|
58
|
+
|
|
59
|
+
def content_security_policy
|
|
60
|
+
get_header(POLICY)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def content_security_policy=(policy)
|
|
64
|
+
set_header(POLICY, policy)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def content_security_policy_report_only
|
|
68
|
+
get_header(POLICY_REPORT_ONLY)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def content_security_policy_report_only=(value)
|
|
72
|
+
set_header(POLICY_REPORT_ONLY, value)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def content_security_policy_nonce_generator
|
|
76
|
+
get_header(NONCE_GENERATOR)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def content_security_policy_nonce_generator=(generator)
|
|
80
|
+
set_header(NONCE_GENERATOR, generator)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def content_security_policy_nonce
|
|
84
|
+
if content_security_policy_nonce_generator
|
|
85
|
+
if nonce = get_header(NONCE)
|
|
86
|
+
nonce
|
|
87
|
+
else
|
|
88
|
+
set_header(NONCE, generate_content_security_policy_nonce)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def generate_content_security_policy_nonce
|
|
96
|
+
content_security_policy_nonce_generator.call(self)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
MAPPINGS = {
|
|
101
|
+
self: "'self'",
|
|
102
|
+
unsafe_eval: "'unsafe-eval'",
|
|
103
|
+
unsafe_inline: "'unsafe-inline'",
|
|
104
|
+
none: "'none'",
|
|
105
|
+
http: "http:",
|
|
106
|
+
https: "https:",
|
|
107
|
+
data: "data:",
|
|
108
|
+
mediastream: "mediastream:",
|
|
109
|
+
blob: "blob:",
|
|
110
|
+
filesystem: "filesystem:",
|
|
111
|
+
report_sample: "'report-sample'",
|
|
112
|
+
strict_dynamic: "'strict-dynamic'",
|
|
113
|
+
ws: "ws:",
|
|
114
|
+
wss: "wss:"
|
|
115
|
+
}.freeze
|
|
116
|
+
|
|
117
|
+
DIRECTIVES = {
|
|
118
|
+
base_uri: "base-uri",
|
|
119
|
+
child_src: "child-src",
|
|
120
|
+
connect_src: "connect-src",
|
|
121
|
+
default_src: "default-src",
|
|
122
|
+
font_src: "font-src",
|
|
123
|
+
form_action: "form-action",
|
|
124
|
+
frame_ancestors: "frame-ancestors",
|
|
125
|
+
frame_src: "frame-src",
|
|
126
|
+
img_src: "img-src",
|
|
127
|
+
manifest_src: "manifest-src",
|
|
128
|
+
media_src: "media-src",
|
|
129
|
+
object_src: "object-src",
|
|
130
|
+
script_src: "script-src",
|
|
131
|
+
style_src: "style-src",
|
|
132
|
+
worker_src: "worker-src"
|
|
133
|
+
}.freeze
|
|
134
|
+
|
|
135
|
+
NONCE_DIRECTIVES = %w[script-src].freeze
|
|
136
|
+
|
|
137
|
+
private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
|
|
138
|
+
|
|
139
|
+
attr_reader :directives
|
|
140
|
+
|
|
141
|
+
def initialize
|
|
142
|
+
@directives = {}
|
|
143
|
+
yield self if block_given?
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def initialize_copy(other)
|
|
147
|
+
@directives = other.directives.deep_dup
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
DIRECTIVES.each do |name, directive|
|
|
151
|
+
define_method(name) do |*sources|
|
|
152
|
+
if sources.first
|
|
153
|
+
@directives[directive] = apply_mappings(sources)
|
|
154
|
+
else
|
|
155
|
+
@directives.delete(directive)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def block_all_mixed_content(enabled = true)
|
|
161
|
+
if enabled
|
|
162
|
+
@directives["block-all-mixed-content"] = true
|
|
163
|
+
else
|
|
164
|
+
@directives.delete("block-all-mixed-content")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def plugin_types(*types)
|
|
169
|
+
if types.first
|
|
170
|
+
@directives["plugin-types"] = types
|
|
171
|
+
else
|
|
172
|
+
@directives.delete("plugin-types")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def report_uri(uri)
|
|
177
|
+
@directives["report-uri"] = [uri]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def require_sri_for(*types)
|
|
181
|
+
if types.first
|
|
182
|
+
@directives["require-sri-for"] = types
|
|
183
|
+
else
|
|
184
|
+
@directives.delete("require-sri-for")
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def sandbox(*values)
|
|
189
|
+
if values.empty?
|
|
190
|
+
@directives["sandbox"] = true
|
|
191
|
+
elsif values.first
|
|
192
|
+
@directives["sandbox"] = values
|
|
193
|
+
else
|
|
194
|
+
@directives.delete("sandbox")
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def upgrade_insecure_requests(enabled = true)
|
|
199
|
+
if enabled
|
|
200
|
+
@directives["upgrade-insecure-requests"] = true
|
|
201
|
+
else
|
|
202
|
+
@directives.delete("upgrade-insecure-requests")
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def build(context = nil, nonce = nil)
|
|
207
|
+
build_directives(context, nonce).compact.join("; ")
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
private
|
|
211
|
+
def apply_mappings(sources)
|
|
212
|
+
sources.map do |source|
|
|
213
|
+
case source
|
|
214
|
+
when Symbol
|
|
215
|
+
apply_mapping(source)
|
|
216
|
+
when String, Proc
|
|
217
|
+
source
|
|
218
|
+
else
|
|
219
|
+
raise ArgumentError, "Invalid content security policy source: #{source.inspect}"
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def apply_mapping(source)
|
|
225
|
+
MAPPINGS.fetch(source) do
|
|
226
|
+
raise ArgumentError, "Unknown content security policy source mapping: #{source.inspect}"
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def build_directives(context, nonce)
|
|
231
|
+
@directives.map do |directive, sources|
|
|
232
|
+
if sources.is_a?(Array)
|
|
233
|
+
if nonce && nonce_directive?(directive)
|
|
234
|
+
"#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
|
|
235
|
+
else
|
|
236
|
+
"#{directive} #{build_directive(sources, context).join(' ')}"
|
|
237
|
+
end
|
|
238
|
+
elsif sources
|
|
239
|
+
directive
|
|
240
|
+
else
|
|
241
|
+
nil
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def build_directive(sources, context)
|
|
247
|
+
sources.map { |source| resolve_source(source, context) }
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def resolve_source(source, context)
|
|
251
|
+
case source
|
|
252
|
+
when String
|
|
253
|
+
source
|
|
254
|
+
when Symbol
|
|
255
|
+
source.to_s
|
|
256
|
+
when Proc
|
|
257
|
+
if context.nil?
|
|
258
|
+
raise RuntimeError, "Missing context for the dynamic content security policy source: #{source.inspect}"
|
|
259
|
+
else
|
|
260
|
+
resolved = context.instance_exec(&source)
|
|
261
|
+
resolved.is_a?(Symbol) ? apply_mapping(resolved) : resolved
|
|
262
|
+
end
|
|
263
|
+
else
|
|
264
|
+
raise RuntimeError, "Unexpected content security policy source: #{source.inspect}"
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def nonce_directive?(directive)
|
|
269
|
+
NONCE_DIRECTIVES.include?(directive)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|