actionpack 7.1.3 → 7.2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -501
  3. data/lib/abstract_controller/asset_paths.rb +2 -0
  4. data/lib/abstract_controller/base.rb +102 -98
  5. data/lib/abstract_controller/caching/fragments.rb +50 -53
  6. data/lib/abstract_controller/caching.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +66 -64
  8. data/lib/abstract_controller/collector.rb +6 -6
  9. data/lib/abstract_controller/deprecator.rb +2 -0
  10. data/lib/abstract_controller/error.rb +2 -0
  11. data/lib/abstract_controller/helpers.rb +70 -85
  12. data/lib/abstract_controller/logger.rb +2 -0
  13. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  14. data/lib/abstract_controller/rendering.rb +13 -12
  15. data/lib/abstract_controller/translation.rb +15 -7
  16. data/lib/abstract_controller/url_for.rb +8 -6
  17. data/lib/abstract_controller.rb +2 -0
  18. data/lib/action_controller/api/api_rendering.rb +2 -0
  19. data/lib/action_controller/api.rb +74 -72
  20. data/lib/action_controller/base.rb +198 -126
  21. data/lib/action_controller/caching.rb +15 -12
  22. data/lib/action_controller/deprecator.rb +2 -0
  23. data/lib/action_controller/form_builder.rb +20 -17
  24. data/lib/action_controller/log_subscriber.rb +3 -1
  25. data/lib/action_controller/metal/allow_browser.rb +123 -0
  26. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  27. data/lib/action_controller/metal/conditional_get.rb +188 -174
  28. data/lib/action_controller/metal/content_security_policy.rb +25 -24
  29. data/lib/action_controller/metal/cookies.rb +4 -2
  30. data/lib/action_controller/metal/data_streaming.rb +64 -55
  31. data/lib/action_controller/metal/default_headers.rb +5 -3
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
  34. data/lib/action_controller/metal/exceptions.rb +11 -9
  35. data/lib/action_controller/metal/flash.rb +12 -10
  36. data/lib/action_controller/metal/head.rb +12 -10
  37. data/lib/action_controller/metal/helpers.rb +63 -55
  38. data/lib/action_controller/metal/http_authentication.rb +210 -205
  39. data/lib/action_controller/metal/implicit_render.rb +17 -15
  40. data/lib/action_controller/metal/instrumentation.rb +15 -12
  41. data/lib/action_controller/metal/live.rb +113 -107
  42. data/lib/action_controller/metal/logging.rb +6 -4
  43. data/lib/action_controller/metal/mime_responds.rb +151 -142
  44. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  45. data/lib/action_controller/metal/params_wrapper.rb +57 -59
  46. data/lib/action_controller/metal/permissions_policy.rb +13 -12
  47. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  48. data/lib/action_controller/metal/redirecting.rb +108 -82
  49. data/lib/action_controller/metal/renderers.rb +50 -49
  50. data/lib/action_controller/metal/rendering.rb +103 -75
  51. data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
  52. data/lib/action_controller/metal/rescue.rb +11 -9
  53. data/lib/action_controller/metal/streaming.rb +138 -136
  54. data/lib/action_controller/metal/strong_parameters.rb +525 -480
  55. data/lib/action_controller/metal/testing.rb +2 -0
  56. data/lib/action_controller/metal/url_for.rb +17 -15
  57. data/lib/action_controller/metal.rb +86 -60
  58. data/lib/action_controller/railtie.rb +3 -0
  59. data/lib/action_controller/railties/helpers.rb +2 -0
  60. data/lib/action_controller/renderer.rb +42 -36
  61. data/lib/action_controller/template_assertions.rb +4 -2
  62. data/lib/action_controller/test_case.rb +146 -126
  63. data/lib/action_controller.rb +10 -3
  64. data/lib/action_dispatch/constants.rb +2 -0
  65. data/lib/action_dispatch/deprecator.rb +2 -0
  66. data/lib/action_dispatch/http/cache.rb +27 -26
  67. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  68. data/lib/action_dispatch/http/content_security_policy.rb +44 -38
  69. data/lib/action_dispatch/http/filter_parameters.rb +18 -9
  70. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  71. data/lib/action_dispatch/http/headers.rb +22 -22
  72. data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
  73. data/lib/action_dispatch/http/mime_type.rb +31 -24
  74. data/lib/action_dispatch/http/mime_types.rb +2 -0
  75. data/lib/action_dispatch/http/parameters.rb +11 -9
  76. data/lib/action_dispatch/http/permissions_policy.rb +20 -44
  77. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  78. data/lib/action_dispatch/http/request.rb +94 -75
  79. data/lib/action_dispatch/http/response.rb +73 -61
  80. data/lib/action_dispatch/http/upload.rb +18 -16
  81. data/lib/action_dispatch/http/url.rb +75 -73
  82. data/lib/action_dispatch/journey/formatter.rb +13 -6
  83. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  84. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  85. data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
  86. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  88. data/lib/action_dispatch/journey/parser.rb +4 -3
  89. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  90. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  91. data/lib/action_dispatch/journey/route.rb +9 -7
  92. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  93. data/lib/action_dispatch/journey/router.rb +4 -2
  94. data/lib/action_dispatch/journey/routes.rb +4 -2
  95. data/lib/action_dispatch/journey/scanner.rb +4 -2
  96. data/lib/action_dispatch/journey/visitors.rb +2 -0
  97. data/lib/action_dispatch/journey.rb +2 -0
  98. data/lib/action_dispatch/log_subscriber.rb +2 -0
  99. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  100. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  101. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  102. data/lib/action_dispatch/middleware/cookies.rb +119 -104
  103. data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
  104. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  105. data/lib/action_dispatch/middleware/debug_view.rb +2 -0
  106. data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
  107. data/lib/action_dispatch/middleware/executor.rb +8 -0
  108. data/lib/action_dispatch/middleware/flash.rb +63 -51
  109. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  110. data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
  111. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  112. data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
  113. data/lib/action_dispatch/middleware/request_id.rb +14 -9
  114. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
  120. data/lib/action_dispatch/middleware/ssl.rb +43 -40
  121. data/lib/action_dispatch/middleware/stack.rb +11 -10
  122. data/lib/action_dispatch/middleware/static.rb +33 -31
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
  125. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  126. data/lib/action_dispatch/railtie.rb +2 -4
  127. data/lib/action_dispatch/request/session.rb +23 -21
  128. data/lib/action_dispatch/request/utils.rb +2 -0
  129. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  130. data/lib/action_dispatch/routing/inspector.rb +5 -3
  131. data/lib/action_dispatch/routing/mapper.rb +671 -636
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  133. data/lib/action_dispatch/routing/redirection.rb +37 -32
  134. data/lib/action_dispatch/routing/route_set.rb +59 -45
  135. data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
  136. data/lib/action_dispatch/routing/url_for.rb +130 -125
  137. data/lib/action_dispatch/routing.rb +150 -148
  138. data/lib/action_dispatch/system_test_case.rb +91 -81
  139. data/lib/action_dispatch/system_testing/browser.rb +10 -3
  140. data/lib/action_dispatch/system_testing/driver.rb +3 -1
  141. data/lib/action_dispatch/system_testing/server.rb +2 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
  143. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +8 -6
  145. data/lib/action_dispatch/testing/assertions/response.rb +26 -23
  146. data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
  147. data/lib/action_dispatch/testing/assertions.rb +2 -0
  148. data/lib/action_dispatch/testing/integration.rb +223 -222
  149. data/lib/action_dispatch/testing/request_encoder.rb +2 -0
  150. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  151. data/lib/action_dispatch/testing/test_process.rb +12 -8
  152. data/lib/action_dispatch/testing/test_request.rb +3 -1
  153. data/lib/action_dispatch/testing/test_response.rb +27 -26
  154. data/lib/action_dispatch.rb +22 -28
  155. data/lib/action_pack/gem_version.rb +6 -4
  156. data/lib/action_pack/version.rb +3 -1
  157. data/lib/action_pack.rb +17 -16
  158. metadata +39 -16
@@ -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, Cookie.
98
- # When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
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
- # # Sets a simple session cookie.
103
- # # This cookie will be deleted when the user's browser is closed.
104
- # cookies[:user_name] = "david"
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
- # # Cookie values are String-based. Other data types need to be serialized.
107
- # cookies[:lat_lon] = JSON.generate([47.68, -122.37])
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
- # # Sets a cookie that expires in 1 hour.
110
- # cookies[:login] = { value: "XJ-122", expires: 1.hour }
112
+ # # Sets a cookie that expires in 1 hour.
113
+ # cookies[:login] = { value: "XJ-122", expires: 1.hour }
111
114
  #
112
- # # Sets a cookie that expires at a specific time.
113
- # cookies[:login] = { value: "XJ-122", expires: Time.utc(2020, 10, 15, 5) }
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
- # # Sets a signed cookie, which prevents users from tampering with its value.
116
- # # It can be read using the signed method `cookies.signed[:name]`
117
- # cookies.signed[:user_id] = current_user.id
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
- # # Sets an encrypted cookie value before sending it to the client which
120
- # # prevent users from reading and tampering with its value.
121
- # # It can be read using the encrypted method `cookies.encrypted[:name]`
122
- # cookies.encrypted[:discount] = 45
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
- # # Sets a "permanent" cookie (which expires in 20 years from now).
125
- # cookies.permanent[:login] = "XJ-122"
127
+ # # Sets a "permanent" cookie (which expires in 20 years from now).
128
+ # cookies.permanent[:login] = "XJ-122"
126
129
  #
127
- # # You can also chain these methods:
128
- # cookies.signed.permanent[:login] = "XJ-122"
130
+ # # You can also chain these methods:
131
+ # cookies.signed.permanent[:login] = "XJ-122"
129
132
  #
130
133
  # Examples of reading:
131
134
  #
132
- # cookies[:user_name] # => "david"
133
- # cookies.size # => 2
134
- # JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
135
- # cookies.signed[:login] # => "XJ-122"
136
- # cookies.encrypted[:discount] # => 45
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
- # cookies.delete :user_name
143
+ # cookies.delete :user_name
141
144
  #
142
- # Please note that if you specify a +:domain+ when setting a cookie, you must also specify the domain when deleting the cookie:
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
- # cookies[:name] = {
145
- # value: 'a yummy cookie',
146
- # expires: 1.year,
147
- # domain: 'domain.com'
148
- # }
148
+ # cookies[:name] = {
149
+ # value: 'a yummy cookie',
150
+ # expires: 1.year,
151
+ # domain: 'domain.com'
152
+ # }
149
153
  #
150
- # cookies.delete(:name, domain: 'domain.com')
154
+ # cookies.delete(:name, domain: 'domain.com')
151
155
  #
152
156
  # The option symbols for setting cookies are:
153
157
  #
154
- # * <tt>:value</tt> - The cookie's value.
155
- # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
156
- # of the application.
157
- # * <tt>:domain</tt> - The domain for which this cookie applies so you can
158
- # restrict to the domain level. If you use a schema like www.example.com
159
- # and want to share session with user.example.com set <tt>:domain</tt>
160
- # to <tt>:all</tt>. To support multiple domains, provide an array, and
161
- # the first domain matching <tt>request.host</tt> will be used. Make
162
- # sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
163
- # <tt>Array</tt> again when deleting cookies. For more flexibility you
164
- # can set the domain on a per-request basis by specifying <tt>:domain</tt>
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
- # domain: nil # Does not set cookie domain. (default)
168
- # domain: :all # Allow the cookie for the top most level
169
- # # domain and subdomains.
170
- # domain: %w(.example.com .example.org) # Allow the cookie
171
- # # for concrete domain names.
172
- # domain: proc { Tenant.current.cookie_domain } # Set cookie domain dynamically
173
- # domain: proc { |req| ".sub.#{req.host}" } # Set cookie domain dynamically based on request
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. +cookies.permanent.signed+.
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 expiration date 20 years from now. Example:
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
- # cookies.permanent[:prefers_open_id] = true
216
- # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
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 regular accessor.
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 permanent, signed cookies. Examples:
227
+ # This jar allows chaining with the signed jar as well, so you can set
228
+ # permanent, signed cookies. Examples:
221
229
  #
222
- # cookies.permanent.signed[:remember_me] = current_user.id
223
- # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
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 value and verify it when reading from
229
- # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
230
- # cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
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 app's +secret_key_base+.
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
- # cookies.signed[:discount] = 45
237
- # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
247
+ # cookies.signed[:discount] = 45
248
+ # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
238
249
  #
239
- # cookies.signed[:discount] # => 45
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 to the client and will decrypt them for read.
245
- # If the cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
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 +config.action_dispatch.encrypted_cookie_salt+ and +config.action_dispatch.encrypted_signed_cookie_salt+
248
- # are both set, legacy cookies encrypted with HMAC AES-256-CBC will be transparently upgraded.
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 app's +secret_key_base+.
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
- # cookies.encrypted[:discount] = 45
255
- # # => Set-Cookie: discount=DIQ7fw==--K3n//8vvnSbGq9dA--7Xh91HfLpwzbj1czhBiwOg==; path=/
268
+ # cookies.encrypted[:discount] = 45
269
+ # # => Set-Cookie: discount=DIQ7fw==--K3n//8vvnSbGq9dA--7Xh91HfLpwzbj1czhBiwOg==; path=/
256
270
  #
257
- # cookies.encrypted[:discount] # => 45
271
+ # cookies.encrypted[:discount] # => 45
258
272
  def encrypted
259
273
  @encrypted ||= EncryptedKeyRotatingCookieJar.new(self)
260
274
  end
261
275
 
262
- # Returns the +signed+ or +encrypted+ jar, preferring +encrypted+ if +secret_key_base+ is set.
263
- # Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
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 +name+, or +nil+ if no such cookie exists.
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 +name+. The second argument may be the cookie's
361
- # value or a hash of options as documented above.
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 string
383
- # and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
384
- # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
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 +nil+ if the cookie does not exist.
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
- # Like <tt>[]=</tt>, you can pass in an options hash to test if a
400
- # deletion applies to a specific <tt>:path</tt>, <tt>:domain</tt> etc.
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 <tt>delete</tt> for each cookie.
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
- # (ip confirms to the domain structure we expect so we explicitly check for ip)
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
- # = Action Dispatch \DebugExceptions
11
+ # # Action Dispatch DebugExceptions
10
12
  #
11
- # This middleware is responsible for logging exceptions and
12
- # showing a debugging page in case the request is local.
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
- # `exception` for legacy code.
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
- # = Action Dispatch \DebugLocks
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
- # <tt>config/application.rb</tt>:
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
- # the route <tt>/rails/locks</tt> will show a summary of all threads currently
15
- # known to the interlock, which lock level they are holding or awaiting, and
16
- # their current backtrace.
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
- # is executing. That gives us a more consistent picture of everything,
51
- # but creates a pretty strong Observer Effect.
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
- # this block. More widely, it means this middleware should remain a
55
- # strictly diagnostic tool (to be used when something has gone wrong),
56
- # and not for any sort of general monitoring.
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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "pp"
4
6
 
5
7
  require "action_view"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/core_ext/module/attribute_accessors"
4
6
  require "active_support/syntax_error_proxy"
5
7
  require "active_support/core_ext/thread/backtrace/location"
@@ -175,23 +177,15 @@ module ActionDispatch
175
177
  end
176
178
 
177
179
  def show?(request)
178
- # We're treating `nil` as "unset", and we want the default setting to be
179
- # `:all`. This logic should be extracted to `env_config` and calculated
180
- # once.
180
+ # We're treating `nil` as "unset", and we want the default setting to be `:all`.
181
+ # This logic should be extracted to `env_config` and calculated once.
181
182
  config = request.get_header("action_dispatch.show_exceptions")
182
183
 
183
- # Include true and false for backwards compatibility.
184
184
  case config
185
185
  when :none
186
186
  false
187
187
  when :rescuable
188
188
  rescue_response?
189
- when true
190
- ActionDispatch.deprecator.warn("Setting action_dispatch.show_exceptions to true is deprecated. Set to :all instead.")
191
- true
192
- when false
193
- ActionDispatch.deprecator.warn("Setting action_dispatch.show_exceptions to false is deprecated. Set to :none instead.")
194
- false
195
189
  else
196
190
  true
197
191
  end
@@ -208,7 +202,8 @@ module ActionDispatch
208
202
  end
209
203
 
210
204
  def error_highlight_available?
211
- # ErrorHighlight.spot with backtrace_location keyword is available since error_highlight 0.4.0
205
+ # ErrorHighlight.spot with backtrace_location keyword is available since
206
+ # error_highlight 0.4.0
212
207
  defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
213
208
  end
214
209
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "rack/body_proxy"
4
6
 
5
7
  module ActionDispatch
@@ -12,6 +14,12 @@ module ActionDispatch
12
14
  state = @executor.run!(reset: true)
13
15
  begin
14
16
  response = @app.call(env)
17
+
18
+ if env["action_dispatch.report_exception"]
19
+ error = env["action_dispatch.exception"]
20
+ @executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
21
+ end
22
+
15
23
  returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
16
24
  rescue => error
17
25
  @executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")