actionpack 7.1.3.2 → 7.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +70 -530
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +102 -98
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +66 -64
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +70 -85
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -12
- data/lib/abstract_controller/translation.rb +11 -10
- data/lib/abstract_controller/url_for.rb +8 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +74 -72
- data/lib/action_controller/base.rb +155 -117
- data/lib/action_controller/caching.rb +15 -12
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +20 -17
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +188 -174
- data/lib/action_controller/metal/content_security_policy.rb +25 -24
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +64 -55
- data/lib/action_controller/metal/default_headers.rb +5 -3
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
- data/lib/action_controller/metal/exceptions.rb +11 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +12 -10
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +209 -201
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +15 -12
- data/lib/action_controller/metal/live.rb +113 -107
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +57 -59
- data/lib/action_controller/metal/permissions_policy.rb +13 -12
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +108 -82
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -75
- data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
- data/lib/action_controller/metal/rescue.rb +11 -9
- data/lib/action_controller/metal/streaming.rb +138 -136
- data/lib/action_controller/metal/strong_parameters.rb +525 -480
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +17 -15
- data/lib/action_controller/metal.rb +58 -57
- data/lib/action_controller/railtie.rb +3 -0
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +42 -36
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +146 -126
- data/lib/action_controller.rb +10 -3
- data/lib/action_dispatch/constants.rb +2 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +27 -26
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +44 -38
- data/lib/action_dispatch/http/filter_parameters.rb +9 -5
- data/lib/action_dispatch/http/filter_redirect.rb +15 -1
- data/lib/action_dispatch/http/headers.rb +22 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
- data/lib/action_dispatch/http/mime_type.rb +29 -22
- data/lib/action_dispatch/http/mime_types.rb +2 -0
- data/lib/action_dispatch/http/parameters.rb +11 -9
- data/lib/action_dispatch/http/permissions_policy.rb +20 -37
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +71 -71
- data/lib/action_dispatch/http/response.rb +61 -61
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +13 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +9 -7
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +4 -2
- data/lib/action_dispatch/journey/routes.rb +4 -2
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +2 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
- data/lib/action_dispatch/middleware/callbacks.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +119 -104
- data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +2 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
- data/lib/action_dispatch/middleware/executor.rb +8 -0
- data/lib/action_dispatch/middleware/flash.rb +63 -51
- data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
- data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
- data/lib/action_dispatch/middleware/request_id.rb +14 -9
- data/lib/action_dispatch/middleware/server_timing.rb +4 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
- data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
- data/lib/action_dispatch/middleware/ssl.rb +43 -40
- data/lib/action_dispatch/middleware/stack.rb +11 -10
- data/lib/action_dispatch/middleware/static.rb +33 -31
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +2 -4
- data/lib/action_dispatch/request/session.rb +23 -21
- data/lib/action_dispatch/request/utils.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -3
- data/lib/action_dispatch/routing/mapper.rb +670 -635
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +37 -32
- data/lib/action_dispatch/routing/route_set.rb +59 -45
- data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +10 -3
- data/lib/action_dispatch/system_testing/driver.rb +3 -1
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +8 -6
- data/lib/action_dispatch/testing/assertions/response.rb +26 -23
- data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +223 -222
- data/lib/action_dispatch/testing/request_encoder.rb +2 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +12 -8
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +27 -26
- data/lib/action_dispatch.rb +22 -28
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +30 -13
@@ -1,19 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
6
|
module Journey # :nodoc:
|
5
7
|
class Router # :nodoc:
|
6
8
|
class Utils # :nodoc:
|
7
9
|
# Normalizes URI path.
|
8
10
|
#
|
9
|
-
# Strips off trailing slash and ensures there is a leading slash.
|
10
|
-
#
|
11
|
+
# Strips off trailing slash and ensures there is a leading slash. Also converts
|
12
|
+
# downcase URL encoded string to uppercase.
|
11
13
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
14
|
+
# normalize_path("/foo") # => "/foo"
|
15
|
+
# normalize_path("/foo/") # => "/foo"
|
16
|
+
# normalize_path("foo") # => "/foo"
|
17
|
+
# normalize_path("") # => "/"
|
18
|
+
# normalize_path("/%ab") # => "/%AB"
|
17
19
|
def self.normalize_path(path)
|
18
20
|
path ||= ""
|
19
21
|
encoding = path.encoding
|
@@ -28,8 +30,7 @@ module ActionDispatch
|
|
28
30
|
path.force_encoding(encoding)
|
29
31
|
end
|
30
32
|
|
31
|
-
# URI path and fragment escaping
|
32
|
-
# https://tools.ietf.org/html/rfc3986
|
33
|
+
# URI path and fragment escaping https://tools.ietf.org/html/rfc3986
|
33
34
|
class UriEncoder # :nodoc:
|
34
35
|
ENCODE = "%%%02X"
|
35
36
|
US_ASCII = Encoding::US_ASCII
|
@@ -42,11 +43,11 @@ module ActionDispatch
|
|
42
43
|
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
43
44
|
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
44
45
|
|
45
|
-
ESCAPED = /%[a-zA-Z0-9]{2}
|
46
|
+
ESCAPED = /%[a-zA-Z0-9]{2}/
|
46
47
|
|
47
|
-
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]
|
48
|
-
SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]
|
49
|
-
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]
|
48
|
+
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/
|
49
|
+
SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/
|
50
|
+
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/
|
50
51
|
|
51
52
|
def escape_fragment(fragment)
|
52
53
|
escape(fragment, FRAGMENT)
|
@@ -93,8 +94,8 @@ module ActionDispatch
|
|
93
94
|
|
94
95
|
# Replaces any escaped sequences with their unescaped representations.
|
95
96
|
#
|
96
|
-
#
|
97
|
-
#
|
97
|
+
# uri = "/topics?title=Ruby%20on%20Rails"
|
98
|
+
# unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
|
98
99
|
def self.unescape_uri(uri)
|
99
100
|
ENCODER.unescape_uri(uri)
|
100
101
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "action_dispatch/journey/router/utils"
|
4
6
|
require "action_dispatch/journey/routes"
|
5
7
|
require "action_dispatch/journey/formatter"
|
@@ -22,8 +24,8 @@ module ActionDispatch
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def eager_load!
|
25
|
-
# Eagerly trigger the simulator's initialization so
|
26
|
-
#
|
27
|
+
# Eagerly trigger the simulator's initialization so it doesn't happen during a
|
28
|
+
# request cycle.
|
27
29
|
simulator
|
28
30
|
nil
|
29
31
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
6
|
module Journey # :nodoc:
|
5
|
-
# The Routing table. Contains all routes for a system. Routes can be
|
6
|
-
#
|
7
|
+
# The Routing table. Contains all routes for a system. Routes can be added to
|
8
|
+
# the table by calling Routes#add_route.
|
7
9
|
class Routes # :nodoc:
|
8
10
|
include Enumerable
|
9
11
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "strscan"
|
4
6
|
|
5
7
|
module ActionDispatch
|
@@ -33,8 +35,8 @@ module ActionDispatch
|
|
33
35
|
end
|
34
36
|
|
35
37
|
private
|
36
|
-
# takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
|
37
|
-
#
|
38
|
+
# takes advantage of String @- deduping capabilities in Ruby 2.5 upwards see:
|
39
|
+
# https://bugs.ruby-lang.org/issues/13077
|
38
40
|
def dedup_scan(regex)
|
39
41
|
r = @ss.scan(regex)
|
40
42
|
r ? -r : nil
|
@@ -1,12 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
|
-
#
|
6
|
+
# # Action Dispatch AssumeSSL
|
5
7
|
#
|
6
|
-
# When proxying through a load balancer that terminates SSL, the forwarded
|
7
|
-
# as though it's HTTP instead of HTTPS to the application.
|
8
|
-
# security target HTTP instead of HTTPS. This
|
9
|
-
#
|
8
|
+
# When proxying through a load balancer that terminates SSL, the forwarded
|
9
|
+
# request will appear as though it's HTTP instead of HTTPS to the application.
|
10
|
+
# This makes redirects and cookie security target HTTP instead of HTTPS. This
|
11
|
+
# middleware makes the server assume that the proxy already terminated SSL, and
|
12
|
+
# that the request really is HTTPS.
|
10
13
|
class AssumeSSL
|
11
14
|
def initialize(app)
|
12
15
|
@app = app
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/hash/keys"
|
4
6
|
require "active_support/key_generator"
|
5
7
|
require "active_support/message_verifier"
|
@@ -94,97 +96,100 @@ module ActionDispatch
|
|
94
96
|
|
95
97
|
# Read and write data to cookies through ActionController::Cookies#cookies.
|
96
98
|
#
|
97
|
-
# When reading cookie data, the data is read from the HTTP request header,
|
98
|
-
# When writing cookie data, the data is sent out in the HTTP response
|
99
|
+
# When reading cookie data, the data is read from the HTTP request header,
|
100
|
+
# Cookie. When writing cookie data, the data is sent out in the HTTP response
|
101
|
+
# header, `Set-Cookie`.
|
99
102
|
#
|
100
103
|
# Examples of writing:
|
101
104
|
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
+
# # Sets a simple session cookie.
|
106
|
+
# # This cookie will be deleted when the user's browser is closed.
|
107
|
+
# cookies[:user_name] = "david"
|
105
108
|
#
|
106
|
-
#
|
107
|
-
#
|
109
|
+
# # Cookie values are String-based. Other data types need to be serialized.
|
110
|
+
# cookies[:lat_lon] = JSON.generate([47.68, -122.37])
|
108
111
|
#
|
109
|
-
#
|
110
|
-
#
|
112
|
+
# # Sets a cookie that expires in 1 hour.
|
113
|
+
# cookies[:login] = { value: "XJ-122", expires: 1.hour }
|
111
114
|
#
|
112
|
-
#
|
113
|
-
#
|
115
|
+
# # Sets a cookie that expires at a specific time.
|
116
|
+
# cookies[:login] = { value: "XJ-122", expires: Time.utc(2020, 10, 15, 5) }
|
114
117
|
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
+
# # Sets a signed cookie, which prevents users from tampering with its value.
|
119
|
+
# # It can be read using the signed method `cookies.signed[:name]`
|
120
|
+
# cookies.signed[:user_id] = current_user.id
|
118
121
|
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
122
|
+
# # Sets an encrypted cookie value before sending it to the client which
|
123
|
+
# # prevent users from reading and tampering with its value.
|
124
|
+
# # It can be read using the encrypted method `cookies.encrypted[:name]`
|
125
|
+
# cookies.encrypted[:discount] = 45
|
123
126
|
#
|
124
|
-
#
|
125
|
-
#
|
127
|
+
# # Sets a "permanent" cookie (which expires in 20 years from now).
|
128
|
+
# cookies.permanent[:login] = "XJ-122"
|
126
129
|
#
|
127
|
-
#
|
128
|
-
#
|
130
|
+
# # You can also chain these methods:
|
131
|
+
# cookies.signed.permanent[:login] = "XJ-122"
|
129
132
|
#
|
130
133
|
# Examples of reading:
|
131
134
|
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
136
|
-
#
|
135
|
+
# cookies[:user_name] # => "david"
|
136
|
+
# cookies.size # => 2
|
137
|
+
# JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
|
138
|
+
# cookies.signed[:login] # => "XJ-122"
|
139
|
+
# cookies.encrypted[:discount] # => 45
|
137
140
|
#
|
138
141
|
# Example for deleting:
|
139
142
|
#
|
140
|
-
#
|
143
|
+
# cookies.delete :user_name
|
141
144
|
#
|
142
|
-
# Please note that if you specify a
|
145
|
+
# Please note that if you specify a `:domain` when setting a cookie, you must
|
146
|
+
# also specify the domain when deleting the cookie:
|
143
147
|
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
148
|
+
# cookies[:name] = {
|
149
|
+
# value: 'a yummy cookie',
|
150
|
+
# expires: 1.year,
|
151
|
+
# domain: 'domain.com'
|
152
|
+
# }
|
149
153
|
#
|
150
|
-
#
|
154
|
+
# cookies.delete(:name, domain: 'domain.com')
|
151
155
|
#
|
152
156
|
# The option symbols for setting cookies are:
|
153
157
|
#
|
154
|
-
# *
|
155
|
-
# *
|
156
|
-
#
|
157
|
-
# *
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
# with a proc.
|
158
|
+
# * `:value` - The cookie's value.
|
159
|
+
# * `:path` - The path for which this cookie applies. Defaults to the root of
|
160
|
+
# the application.
|
161
|
+
# * `:domain` - The domain for which this cookie applies so you can restrict
|
162
|
+
# to the domain level. If you use a schema like www.example.com and want to
|
163
|
+
# share session with user.example.com set `:domain` to `:all`. To support
|
164
|
+
# multiple domains, provide an array, and the first domain matching
|
165
|
+
# `request.host` will be used. Make sure to specify the `:domain` option
|
166
|
+
# with `:all` or `Array` again when deleting cookies. For more flexibility
|
167
|
+
# you can set the domain on a per-request basis by specifying `:domain` with
|
168
|
+
# a proc.
|
166
169
|
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
170
|
+
# domain: nil # Does not set cookie domain. (default)
|
171
|
+
# domain: :all # Allow the cookie for the top most level
|
172
|
+
# # domain and subdomains.
|
173
|
+
# domain: %w(.example.com .example.org) # Allow the cookie
|
174
|
+
# # for concrete domain names.
|
175
|
+
# domain: proc { Tenant.current.cookie_domain } # Set cookie domain dynamically
|
176
|
+
# domain: proc { |req| ".sub.#{req.host}" } # Set cookie domain dynamically based on request
|
174
177
|
#
|
178
|
+
# * `:tld_length` - When using `:domain => :all`, this option can be used to
|
179
|
+
# explicitly set the TLD length when using a short (<= 3 character) domain
|
180
|
+
# that is being interpreted as part of a TLD. For example, to share cookies
|
181
|
+
# between user1.lvh.me and user2.lvh.me, set `:tld_length` to 2.
|
182
|
+
# * `:expires` - The time at which this cookie expires, as a Time or
|
183
|
+
# ActiveSupport::Duration object.
|
184
|
+
# * `:secure` - Whether this cookie is only transmitted to HTTPS servers.
|
185
|
+
# Default is `false`.
|
186
|
+
# * `:httponly` - Whether this cookie is accessible via scripting or only
|
187
|
+
# HTTP. Defaults to `false`.
|
188
|
+
# * `:same_site` - The value of the `SameSite` cookie attribute, which
|
189
|
+
# determines how this cookie should be restricted in cross-site contexts.
|
190
|
+
# Possible values are `nil`, `:none`, `:lax`, and `:strict`. Defaults to
|
191
|
+
# `:lax`.
|
175
192
|
#
|
176
|
-
# * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
|
177
|
-
# set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
|
178
|
-
# For example, to share cookies between user1.lvh.me and user2.lvh.me, set <tt>:tld_length</tt> to 2.
|
179
|
-
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time or ActiveSupport::Duration object.
|
180
|
-
# * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
|
181
|
-
# Default is +false+.
|
182
|
-
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
183
|
-
# only HTTP. Defaults to +false+.
|
184
|
-
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
185
|
-
# determines how this cookie should be restricted in cross-site contexts.
|
186
|
-
# Possible values are +nil+, +:none+, +:lax+, and +:strict+. Defaults to
|
187
|
-
# +:lax+.
|
188
193
|
class Cookies
|
189
194
|
HTTP_HEADER = "Set-Cookie"
|
190
195
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -208,59 +213,69 @@ module ActionDispatch
|
|
208
213
|
# Raised when storing more than 4K of session data.
|
209
214
|
CookieOverflow = Class.new StandardError
|
210
215
|
|
211
|
-
# Include in a cookie jar to allow chaining, e.g.
|
216
|
+
# Include in a cookie jar to allow chaining, e.g. `cookies.permanent.signed`.
|
212
217
|
module ChainedCookieJars
|
213
|
-
# Returns a jar that'll automatically set the assigned cookies to have an
|
218
|
+
# Returns a jar that'll automatically set the assigned cookies to have an
|
219
|
+
# expiration date 20 years from now. Example:
|
214
220
|
#
|
215
|
-
#
|
216
|
-
#
|
221
|
+
# cookies.permanent[:prefers_open_id] = true
|
222
|
+
# # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
|
217
223
|
#
|
218
|
-
# This jar is only meant for writing. You'll read permanent cookies through the
|
224
|
+
# This jar is only meant for writing. You'll read permanent cookies through the
|
225
|
+
# regular accessor.
|
219
226
|
#
|
220
|
-
# This jar allows chaining with the signed jar as well, so you can set
|
227
|
+
# This jar allows chaining with the signed jar as well, so you can set
|
228
|
+
# permanent, signed cookies. Examples:
|
221
229
|
#
|
222
|
-
#
|
223
|
-
#
|
230
|
+
# cookies.permanent.signed[:remember_me] = current_user.id
|
231
|
+
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
|
224
232
|
def permanent
|
225
233
|
@permanent ||= PermanentCookieJar.new(self)
|
226
234
|
end
|
227
235
|
|
228
|
-
# Returns a jar that'll automatically generate a signed representation of cookie
|
229
|
-
# the cookie again. This is useful for
|
230
|
-
#
|
236
|
+
# Returns a jar that'll automatically generate a signed representation of cookie
|
237
|
+
# value and verify it when reading from the cookie again. This is useful for
|
238
|
+
# creating cookies with values that the user is not supposed to change. If a
|
239
|
+
# signed cookie was tampered with by the user (or a 3rd party), `nil` will be
|
240
|
+
# returned.
|
231
241
|
#
|
232
|
-
# This jar requires that you set a suitable secret for the verification on your
|
242
|
+
# This jar requires that you set a suitable secret for the verification on your
|
243
|
+
# app's `secret_key_base`.
|
233
244
|
#
|
234
245
|
# Example:
|
235
246
|
#
|
236
|
-
#
|
237
|
-
#
|
247
|
+
# cookies.signed[:discount] = 45
|
248
|
+
# # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
|
238
249
|
#
|
239
|
-
#
|
250
|
+
# cookies.signed[:discount] # => 45
|
240
251
|
def signed
|
241
252
|
@signed ||= SignedKeyRotatingCookieJar.new(self)
|
242
253
|
end
|
243
254
|
|
244
|
-
# Returns a jar that'll automatically encrypt cookie values before sending them
|
245
|
-
#
|
255
|
+
# Returns a jar that'll automatically encrypt cookie values before sending them
|
256
|
+
# to the client and will decrypt them for read. If the cookie was tampered with
|
257
|
+
# by the user (or a 3rd party), `nil` will be returned.
|
246
258
|
#
|
247
|
-
# If
|
248
|
-
# are both set, legacy
|
259
|
+
# If `config.action_dispatch.encrypted_cookie_salt` and
|
260
|
+
# `config.action_dispatch.encrypted_signed_cookie_salt` are both set, legacy
|
261
|
+
# cookies encrypted with HMAC AES-256-CBC will be transparently upgraded.
|
249
262
|
#
|
250
|
-
# This jar requires that you set a suitable secret for the verification on your
|
263
|
+
# This jar requires that you set a suitable secret for the verification on your
|
264
|
+
# app's `secret_key_base`.
|
251
265
|
#
|
252
266
|
# Example:
|
253
267
|
#
|
254
|
-
#
|
255
|
-
#
|
268
|
+
# cookies.encrypted[:discount] = 45
|
269
|
+
# # => Set-Cookie: discount=DIQ7fw==--K3n//8vvnSbGq9dA--7Xh91HfLpwzbj1czhBiwOg==; path=/
|
256
270
|
#
|
257
|
-
#
|
271
|
+
# cookies.encrypted[:discount] # => 45
|
258
272
|
def encrypted
|
259
273
|
@encrypted ||= EncryptedKeyRotatingCookieJar.new(self)
|
260
274
|
end
|
261
275
|
|
262
|
-
# Returns the
|
263
|
-
# Used by ActionDispatch::Session::CookieStore to
|
276
|
+
# Returns the `signed` or `encrypted` jar, preferring `encrypted` if
|
277
|
+
# `secret_key_base` is set. Used by ActionDispatch::Session::CookieStore to
|
278
|
+
# avoid the need to introduce new cookie stores.
|
264
279
|
def signed_or_encrypted
|
265
280
|
@signed_or_encrypted ||=
|
266
281
|
if request.secret_key_base.present?
|
@@ -324,7 +339,7 @@ module ActionDispatch
|
|
324
339
|
@cookies.each(&block)
|
325
340
|
end
|
326
341
|
|
327
|
-
# Returns the value of the cookie by
|
342
|
+
# Returns the value of the cookie by `name`, or `nil` if no such cookie exists.
|
328
343
|
def [](name)
|
329
344
|
@cookies[name.to_s]
|
330
345
|
end
|
@@ -357,8 +372,8 @@ module ActionDispatch
|
|
357
372
|
@cookies.map { |k, v| "#{escape(k)}=#{escape(v)}" }.join "; "
|
358
373
|
end
|
359
374
|
|
360
|
-
# Sets the cookie named
|
361
|
-
#
|
375
|
+
# Sets the cookie named `name`. The second argument may be the cookie's value or
|
376
|
+
# a hash of options as documented above.
|
362
377
|
def []=(name, options)
|
363
378
|
if options.is_a?(Hash)
|
364
379
|
options.symbolize_keys!
|
@@ -379,11 +394,11 @@ module ActionDispatch
|
|
379
394
|
value
|
380
395
|
end
|
381
396
|
|
382
|
-
# Removes the cookie on the client machine by setting the value to an empty
|
383
|
-
# and the expiration date in the past. Like
|
384
|
-
#
|
397
|
+
# Removes the cookie on the client machine by setting the value to an empty
|
398
|
+
# string and the expiration date in the past. Like `[]=`, you can pass in an
|
399
|
+
# options hash to delete cookies with extra data such as a `:path`.
|
385
400
|
#
|
386
|
-
# Returns the value of the cookie, or
|
401
|
+
# Returns the value of the cookie, or `nil` if the cookie does not exist.
|
387
402
|
def delete(name, options = {})
|
388
403
|
return unless @cookies.has_key? name.to_s
|
389
404
|
|
@@ -395,16 +410,16 @@ module ActionDispatch
|
|
395
410
|
value
|
396
411
|
end
|
397
412
|
|
398
|
-
# Whether the given cookie is to be deleted by this CookieJar.
|
399
|
-
#
|
400
|
-
#
|
413
|
+
# Whether the given cookie is to be deleted by this CookieJar. Like `[]=`, you
|
414
|
+
# can pass in an options hash to test if a deletion applies to a specific
|
415
|
+
# `:path`, `:domain` etc.
|
401
416
|
def deleted?(name, options = {})
|
402
417
|
options.symbolize_keys!
|
403
418
|
handle_options(options)
|
404
419
|
@delete_cookies[name.to_s] == options
|
405
420
|
end
|
406
421
|
|
407
|
-
# Removes all cookies on the client machine by calling
|
422
|
+
# Removes all cookies on the client machine by calling `delete` for each cookie.
|
408
423
|
def clear(options = {})
|
409
424
|
@cookies.each_key { |k| delete(k, options) }
|
410
425
|
end
|
@@ -447,8 +462,8 @@ module ActionDispatch
|
|
447
462
|
cookie_domain = ""
|
448
463
|
dot_splitted_host = request.host.split(".", -1)
|
449
464
|
|
450
|
-
# Case where request.host is not an IP address or it's an invalid domain
|
451
|
-
#
|
465
|
+
# Case where request.host is not an IP address or it's an invalid domain (ip
|
466
|
+
# confirms to the domain structure we expect so we explicitly check for ip)
|
452
467
|
if request.host.match?(/^[\d.]+$/) || dot_splitted_host.include?("") || dot_splitted_host.length == 1
|
453
468
|
options[:domain] = nil
|
454
469
|
return
|
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "action_dispatch/middleware/exception_wrapper"
|
4
6
|
require "action_dispatch/routing/inspector"
|
5
7
|
|
6
8
|
require "action_view"
|
7
9
|
|
8
10
|
module ActionDispatch
|
9
|
-
#
|
11
|
+
# # Action Dispatch DebugExceptions
|
10
12
|
#
|
11
|
-
# This middleware is responsible for logging exceptions and
|
12
|
-
#
|
13
|
+
# This middleware is responsible for logging exceptions and showing a debugging
|
14
|
+
# page in case the request is local.
|
13
15
|
class DebugExceptions
|
14
16
|
cattr_reader :interceptors, instance_accessor: false, default: []
|
15
17
|
|
@@ -115,8 +117,8 @@ module ActionDispatch
|
|
115
117
|
DebugView.new(
|
116
118
|
request: request,
|
117
119
|
exception_wrapper: wrapper,
|
118
|
-
# Everything should use the wrapper, but we need to pass
|
119
|
-
#
|
120
|
+
# Everything should use the wrapper, but we need to pass `exception` for legacy
|
121
|
+
# code.
|
120
122
|
exception: wrapper.exception,
|
121
123
|
traces: wrapper.traces,
|
122
124
|
show_source_idx: wrapper.source_to_show_id,
|
@@ -141,6 +143,12 @@ module ActionDispatch
|
|
141
143
|
message = []
|
142
144
|
message << " "
|
143
145
|
message << "#{wrapper.exception_class_name} (#{wrapper.message}):"
|
146
|
+
if wrapper.has_cause?
|
147
|
+
message << "\nCauses:"
|
148
|
+
wrapper.wrapped_causes.each do |wrapped_cause|
|
149
|
+
message << "#{wrapped_cause.exception_class_name} (#{wrapped_cause.message})"
|
150
|
+
end
|
151
|
+
end
|
144
152
|
message.concat(wrapper.annotated_source_code)
|
145
153
|
message << " "
|
146
154
|
message.concat(trace)
|
@@ -1,19 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
|
-
#
|
6
|
+
# # Action Dispatch DebugLocks
|
5
7
|
#
|
6
8
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
7
9
|
#
|
8
10
|
# To use it, insert it near the top of the middleware stack, using
|
9
|
-
#
|
11
|
+
# `config/application.rb`:
|
10
12
|
#
|
11
13
|
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
12
14
|
#
|
13
|
-
# After restarting the application and re-triggering the deadlock condition,
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
15
|
+
# After restarting the application and re-triggering the deadlock condition, the
|
16
|
+
# route `/rails/locks` will show a summary of all threads currently known to the
|
17
|
+
# interlock, which lock level they are holding or awaiting, and their current
|
18
|
+
# backtrace.
|
17
19
|
#
|
18
20
|
# Generally a deadlock will be caused by the interlock conflicting with some
|
19
21
|
# other external lock or blocking I/O call. These cannot be automatically
|
@@ -46,14 +48,14 @@ module ActionDispatch
|
|
46
48
|
private
|
47
49
|
def render_details(req)
|
48
50
|
threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
|
49
|
-
# The Interlock itself comes to a complete halt as long as this block
|
50
|
-
#
|
51
|
-
#
|
51
|
+
# The Interlock itself comes to a complete halt as long as this block is
|
52
|
+
# executing. That gives us a more consistent picture of everything, but creates
|
53
|
+
# a pretty strong Observer Effect.
|
52
54
|
#
|
53
|
-
# Most directly, that means we need to do as little as possible in
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
55
|
+
# Most directly, that means we need to do as little as possible in this block.
|
56
|
+
# More widely, it means this middleware should remain a strictly diagnostic tool
|
57
|
+
# (to be used when something has gone wrong), and not for any sort of general
|
58
|
+
# monitoring.
|
57
59
|
|
58
60
|
raw_threads.each.with_index do |(thread, info), idx|
|
59
61
|
info[:index] = idx
|