actionpack 6.1.7.5 → 7.1.3.1

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.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -42,11 +42,8 @@ module ActionDispatch
42
42
  HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
43
43
  HTTP_X_FORWARDED_FOR HTTP_ORIGIN HTTP_VERSION
44
44
  HTTP_X_CSRF_TOKEN HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST
45
- SERVER_ADDR
46
45
  ].freeze
47
46
 
48
- # TODO: Remove SERVER_ADDR when we remove support to Rack 2.1.
49
- # See https://github.com/rack/rack/commit/c173b188d81ee437b588c1e046a1c9f031dea550
50
47
  ENV_METHODS.each do |env|
51
48
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
52
49
  # frozen_string_literal: true
@@ -75,7 +72,7 @@ module ActionDispatch
75
72
 
76
73
  PASS_NOT_FOUND = Class.new { # :nodoc:
77
74
  def self.action(_); self; end
78
- def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
75
+ def self.call(_); [404, { Constants::X_CASCADE => "pass" }, []]; end
79
76
  def self.action_encoding_template(action); false; end
80
77
  }
81
78
 
@@ -90,7 +87,7 @@ module ActionDispatch
90
87
  controller_param = name.underscore
91
88
  const_name = controller_param.camelize << "Controller"
92
89
  begin
93
- ActiveSupport::Dependencies.constantize(const_name)
90
+ const_name.constantize
94
91
  rescue NameError => error
95
92
  if error.missing_name == const_name || const_name.start_with?("#{error.missing_name}::")
96
93
  raise MissingController.new(error.message, error.name)
@@ -110,22 +107,21 @@ module ActionDispatch
110
107
  has_header? key
111
108
  end
112
109
 
113
- # List of HTTP request methods from the following RFCs:
114
- # Hypertext Transfer Protocol -- HTTP/1.1 (https://www.ietf.org/rfc/rfc2616.txt)
115
- # HTTP Extensions for Distributed Authoring -- WEBDAV (https://www.ietf.org/rfc/rfc2518.txt)
116
- # Versioning Extensions to WebDAV (https://www.ietf.org/rfc/rfc3253.txt)
117
- # Ordered Collections Protocol (WebDAV) (https://www.ietf.org/rfc/rfc3648.txt)
118
- # Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (https://www.ietf.org/rfc/rfc3744.txt)
119
- # Web Distributed Authoring and Versioning (WebDAV) SEARCH (https://www.ietf.org/rfc/rfc5323.txt)
120
- # Calendar Extensions to WebDAV (https://www.ietf.org/rfc/rfc4791.txt)
121
- # PATCH Method for HTTP (https://www.ietf.org/rfc/rfc5789.txt)
110
+ # HTTP methods from {RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1}[https://www.ietf.org/rfc/rfc2616.txt]
122
111
  RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
112
+ # HTTP methods from {RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV}[https://www.ietf.org/rfc/rfc2518.txt]
123
113
  RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
114
+ # HTTP methods from {RFC 3253: Versioning Extensions to WebDAV}[https://www.ietf.org/rfc/rfc3253.txt]
124
115
  RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
116
+ # HTTP methods from {RFC 3648: WebDAV Ordered Collections Protocol}[https://www.ietf.org/rfc/rfc3648.txt]
125
117
  RFC3648 = %w(ORDERPATCH)
118
+ # HTTP methods from {RFC 3744: WebDAV Access Control Protocol}[https://www.ietf.org/rfc/rfc3744.txt]
126
119
  RFC3744 = %w(ACL)
120
+ # HTTP methods from {RFC 5323: WebDAV SEARCH}[https://www.ietf.org/rfc/rfc5323.txt]
127
121
  RFC5323 = %w(SEARCH)
122
+ # HTTP methods from {RFC 4791: Calendaring Extensions to WebDAV}[https://www.ietf.org/rfc/rfc4791.txt]
128
123
  RFC4791 = %w(MKCALENDAR)
124
+ # HTTP methods from {RFC 5789: PATCH Method for HTTP}[https://www.ietf.org/rfc/rfc5789.txt]
129
125
  RFC5789 = %w(PATCH)
130
126
 
131
127
  HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
@@ -149,6 +145,18 @@ module ActionDispatch
149
145
  @request_method ||= check_method(super)
150
146
  end
151
147
 
148
+ # Returns the URI pattern of the matched route for the request,
149
+ # using the same format as `bin/rails routes`:
150
+ #
151
+ # request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
152
+ def route_uri_pattern
153
+ get_header("action_dispatch.route_uri_pattern")
154
+ end
155
+
156
+ def route_uri_pattern=(pattern) # :nodoc:
157
+ set_header("action_dispatch.route_uri_pattern", pattern)
158
+ end
159
+
152
160
  def routes # :nodoc:
153
161
  get_header("action_dispatch.routes")
154
162
  end
@@ -165,7 +173,7 @@ module ActionDispatch
165
173
  set_header(routes.env_key, name.dup)
166
174
  end
167
175
 
168
- def request_method=(request_method) #:nodoc:
176
+ def request_method=(request_method) # :nodoc:
169
177
  if check_method(request_method)
170
178
  @request_method = set_header("REQUEST_METHOD", request_method)
171
179
  end
@@ -183,13 +191,6 @@ module ActionDispatch
183
191
  get_header "action_dispatch.http_auth_salt"
184
192
  end
185
193
 
186
- def show_exceptions? # :nodoc:
187
- # We're treating `nil` as "unset", and we want the default setting to be
188
- # `true`. This logic should be extracted to `env_config` and calculated
189
- # once.
190
- !(get_header("action_dispatch.show_exceptions") == false)
191
- end
192
-
193
194
  # Returns a symbol form of the #request_method.
194
195
  def request_method_symbol
195
196
  HTTP_METHOD_LOOKUP[request_method]
@@ -198,9 +199,20 @@ module ActionDispatch
198
199
  # Returns the original value of the environment's REQUEST_METHOD,
199
200
  # even if it was overridden by middleware. See #request_method for
200
201
  # more information.
201
- def method
202
- @method ||= check_method(get_header("rack.methodoverride.original_method") || get_header("REQUEST_METHOD"))
202
+ #
203
+ # For debugging purposes, when called with arguments this method will
204
+ # fall back to Object#method
205
+ def method(*args)
206
+ if args.empty?
207
+ @method ||= check_method(
208
+ get_header("rack.methodoverride.original_method") ||
209
+ get_header("REQUEST_METHOD")
210
+ )
211
+ else
212
+ super
213
+ end
203
214
  end
215
+ ruby2_keywords(:method)
204
216
 
205
217
  # Returns a symbol form of the #method.
206
218
  def method_symbol
@@ -266,15 +278,16 @@ module ActionDispatch
266
278
  # # get "/articles"
267
279
  # request.media_type # => "application/x-www-form-urlencoded"
268
280
  def media_type
269
- content_mime_type.to_s
281
+ content_mime_type&.to_s
270
282
  end
271
283
 
272
284
  # Returns the content length of the request as an integer.
273
285
  def content_length
286
+ return raw_post.bytesize if headers.key?("Transfer-Encoding")
274
287
  super.to_i
275
288
  end
276
289
 
277
- # Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
290
+ # Returns true if the +X-Requested-With+ header contains "XMLHttpRequest"
278
291
  # (case-insensitive), which may need to be manually added depending on the
279
292
  # choice of JavaScript libraries and frameworks.
280
293
  def xml_http_request?
@@ -300,9 +313,9 @@ module ActionDispatch
300
313
 
301
314
  ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
302
315
 
303
- # Returns the unique request id, which is based on either the X-Request-Id header that can
304
- # be generated by a firewall, load balancer, or web server or by the RequestId middleware
305
- # (which sets the action_dispatch.request_id environment variable).
316
+ # Returns the unique request id, which is based on either the +X-Request-Id+ header that can
317
+ # be generated by a firewall, load balancer, or web server, or by the RequestId middleware
318
+ # (which sets the +action_dispatch.request_id+ environment variable).
306
319
  #
307
320
  # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
308
321
  # This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
@@ -325,9 +338,8 @@ module ActionDispatch
325
338
  # work with raw requests directly.
326
339
  def raw_post
327
340
  unless has_header? "RAW_POST_DATA"
328
- raw_post_body = body
329
- set_header("RAW_POST_DATA", raw_post_body.read(content_length))
330
- raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
341
+ set_header("RAW_POST_DATA", read_body_stream)
342
+ body_stream.rewind if body_stream.respond_to?(:rewind)
331
343
  end
332
344
  get_header "RAW_POST_DATA"
333
345
  end
@@ -344,32 +356,27 @@ module ActionDispatch
344
356
  end
345
357
 
346
358
  # Determine whether the request body contains form-data by checking
347
- # the request Content-Type for one of the media-types:
348
- # "application/x-www-form-urlencoded" or "multipart/form-data". The
359
+ # the request +Content-Type+ for one of the media-types:
360
+ # +application/x-www-form-urlencoded+ or +multipart/form-data+. The
349
361
  # list of form-data media types can be modified through the
350
362
  # +FORM_DATA_MEDIA_TYPES+ array.
351
363
  #
352
364
  # A request body is not assumed to contain form-data when no
353
- # Content-Type header is provided and the request_method is POST.
365
+ # +Content-Type+ header is provided and the request_method is POST.
354
366
  def form_data?
355
367
  FORM_DATA_MEDIA_TYPES.include?(media_type)
356
368
  end
357
369
 
358
- def body_stream #:nodoc:
370
+ def body_stream # :nodoc:
359
371
  get_header("rack.input")
360
372
  end
361
373
 
362
- # TODO This should be broken apart into AD::Request::Session and probably
363
- # be included by the session middleware.
364
374
  def reset_session
365
- if session && session.respond_to?(:destroy)
366
- session.destroy
367
- else
368
- self.session = {}
369
- end
375
+ session.destroy
376
+ reset_csrf_token
370
377
  end
371
378
 
372
- def session=(session) #:nodoc:
379
+ def session=(session) # :nodoc:
373
380
  Session.set self, session
374
381
  end
375
382
 
@@ -388,7 +395,7 @@ module ActionDispatch
388
395
  Request::Utils.check_param_encoding(rack_query_params)
389
396
  set_header k, Request::Utils.normalize_encode_params(rack_query_params)
390
397
  end
391
- rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
398
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError => e
392
399
  raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
393
400
  end
394
401
  alias :query_parameters :GET
@@ -403,7 +410,7 @@ module ActionDispatch
403
410
  Request::Utils.check_param_encoding(pr)
404
411
  self.request_parameters = Request::Utils.normalize_encode_params(pr)
405
412
  end
406
- rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
413
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError, EOFError => e
407
414
  raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
408
415
  end
409
416
  alias :request_parameters :POST
@@ -434,19 +441,36 @@ module ActionDispatch
434
441
  def commit_flash
435
442
  end
436
443
 
437
- def ssl?
438
- super || scheme == "wss"
439
- end
440
-
441
444
  def inspect # :nodoc:
442
445
  "#<#{self.class.name} #{method} #{original_url.dump} for #{remote_ip}>"
443
446
  end
444
447
 
448
+ def reset_csrf_token
449
+ controller_instance.reset_csrf_token(self) if controller_instance.respond_to?(:reset_csrf_token)
450
+ end
451
+
452
+ def commit_csrf_token
453
+ controller_instance.commit_csrf_token(self) if controller_instance.respond_to?(:commit_csrf_token)
454
+ end
455
+
445
456
  private
446
457
  def check_method(name)
447
- HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
458
+ if name
459
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
460
+ end
461
+
448
462
  name
449
463
  end
464
+
465
+ def default_session
466
+ Session.disabled(self)
467
+ end
468
+
469
+ def read_body_stream
470
+ body_stream.rewind if body_stream.respond_to?(:rewind)
471
+ return body_stream.read if headers.key?("Transfer-Encoding") # Read body stream until EOF if "Transfer-Encoding" is present
472
+ body_stream.read(content_length)
473
+ end
450
474
  end
451
475
  end
452
476
 
@@ -6,24 +6,23 @@ require "action_dispatch/http/cache"
6
6
  require "monitor"
7
7
 
8
8
  module ActionDispatch # :nodoc:
9
+ # = Action Dispatch \Response
10
+ #
9
11
  # Represents an HTTP response generated by a controller action. Use it to
10
12
  # retrieve the current state of the response, or customize the response. It can
11
13
  # either represent a real HTTP response (i.e. one that is meant to be sent
12
14
  # back to the web browser) or a TestResponse (i.e. one that is generated
13
15
  # from integration tests).
14
16
  #
15
- # \Response is mostly a Ruby on \Rails framework implementation detail, and
16
- # should never be used directly in controllers. Controllers should use the
17
- # methods defined in ActionController::Base instead. For example, if you want
18
- # to set the HTTP response's content MIME type, then use
19
- # ActionControllerBase#headers instead of Response#headers.
17
+ # The \Response object for the current request is exposed on controllers as
18
+ # ActionController::Metal#response. ActionController::Metal also provides a
19
+ # few additional methods that delegate to attributes of the \Response such as
20
+ # ActionController::Metal#headers.
20
21
  #
21
- # Nevertheless, integration tests may want to inspect controller responses in
22
- # more detail, and that's when \Response can be useful for application
23
- # developers. Integration test methods such as
24
- # ActionDispatch::Integration::Session#get and
25
- # ActionDispatch::Integration::Session#post return objects of type
26
- # TestResponse (which are of course also of type \Response).
22
+ # Integration tests will likely also want to inspect responses in
23
+ # more detail. Methods such as Integration::RequestHelpers#get
24
+ # and Integration::RequestHelpers#post return instances of
25
+ # TestResponse (which inherits from \Response) for this purpose.
27
26
  #
28
27
  # For example, the following demo integration test prints the body of the
29
28
  # controller response to the console:
@@ -35,41 +34,43 @@ module ActionDispatch # :nodoc:
35
34
  # end
36
35
  # end
37
36
  class Response
38
- class Header < DelegateClass(Hash) # :nodoc:
39
- def initialize(response, header)
40
- @response = response
41
- super(header)
42
- end
43
-
44
- def []=(k, v)
45
- if @response.sending? || @response.sent?
46
- raise ActionDispatch::IllegalStateError, "header already sent"
47
- end
48
-
49
- super
50
- end
51
-
52
- def merge(other)
53
- self.class.new @response, __getobj__.merge(other)
54
- end
55
-
56
- def to_hash
57
- __getobj__.dup
58
- end
37
+ begin
38
+ # For `Rack::Headers` (Rack 3+):
39
+ require "rack/headers"
40
+ Headers = ::Rack::Headers
41
+ rescue LoadError
42
+ # For `Rack::Utils::HeaderHash`:
43
+ require "rack/utils"
44
+ Headers = ::Rack::Utils::HeaderHash
59
45
  end
60
46
 
47
+ # To be deprecated:
48
+ Header = Headers
49
+
61
50
  # The request that the response is responding to.
62
51
  attr_accessor :request
63
52
 
64
53
  # The HTTP status code.
65
54
  attr_reader :status
66
55
 
67
- # Get headers for this response.
68
- attr_reader :header
56
+ # The headers for the response.
57
+ #
58
+ # header["Content-Type"] # => "text/plain"
59
+ # header["Content-Type"] = "application/json"
60
+ # header["Content-Type"] # => "application/json"
61
+ #
62
+ # Also aliased as +headers+.
63
+ #
64
+ # headers["Content-Type"] # => "text/plain"
65
+ # headers["Content-Type"] = "application/json"
66
+ # headers["Content-Type"] # => "application/json"
67
+ #
68
+ # Also aliased as +header+ for compatibility.
69
+ attr_reader :headers
69
70
 
70
- alias_method :headers, :header
71
+ alias_method :header, :headers
71
72
 
72
- delegate :[], :[]=, to: :@header
73
+ delegate :[], :[]=, to: :@headers
73
74
 
74
75
  def each(&block)
75
76
  sending!
@@ -80,24 +81,11 @@ module ActionDispatch # :nodoc:
80
81
 
81
82
  CONTENT_TYPE = "Content-Type"
82
83
  SET_COOKIE = "Set-Cookie"
83
- LOCATION = "Location"
84
84
  NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
85
85
 
86
86
  cattr_accessor :default_charset, default: "utf-8"
87
87
  cattr_accessor :default_headers
88
88
 
89
- def self.return_only_media_type_on_content_type=(*)
90
- ActiveSupport::Deprecation.warn(
91
- ".return_only_media_type_on_content_type= is dreprecated with no replacement and will be removed in 7.0."
92
- )
93
- end
94
-
95
- def self.return_only_media_type_on_content_type
96
- ActiveSupport::Deprecation.warn(
97
- ".return_only_media_type_on_content_type is dreprecated with no replacement and will be removed in 7.0."
98
- )
99
- end
100
-
101
89
  include Rack::Response::Helpers
102
90
  # Aliasing these off because AD::Http::Cache::Response defines them.
103
91
  alias :_cache_control :cache_control
@@ -115,6 +103,12 @@ module ActionDispatch # :nodoc:
115
103
  @str_body = nil
116
104
  end
117
105
 
106
+ def to_ary
107
+ @buf.respond_to?(:to_ary) ?
108
+ @buf.to_ary :
109
+ @buf.each
110
+ end
111
+
118
112
  def body
119
113
  @str_body ||= begin
120
114
  buf = +""
@@ -130,6 +124,7 @@ module ActionDispatch # :nodoc:
130
124
  @response.commit!
131
125
  @buf.push string
132
126
  end
127
+ alias_method :<<, :write
133
128
 
134
129
  def each(&block)
135
130
  if @str_body
@@ -159,9 +154,9 @@ module ActionDispatch # :nodoc:
159
154
  end
160
155
  end
161
156
 
162
- def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
163
- header = merge_default_headers(header, default_headers)
164
- new status, header, body
157
+ def self.create(status = 200, headers = {}, body = [], default_headers: self.default_headers)
158
+ headers = merge_default_headers(headers, default_headers)
159
+ new status, headers, body
165
160
  end
166
161
 
167
162
  def self.merge_default_headers(original, default)
@@ -171,10 +166,14 @@ module ActionDispatch # :nodoc:
171
166
  # The underlying body, as a streamable object.
172
167
  attr_reader :stream
173
168
 
174
- def initialize(status = 200, header = {}, body = [])
169
+ def initialize(status = 200, headers = nil, body = [])
175
170
  super()
176
171
 
177
- @header = Header.new(self, header)
172
+ @headers = Headers.new
173
+
174
+ headers&.each do |key, value|
175
+ @headers[key] = value
176
+ end
178
177
 
179
178
  self.body, self.status = body, status
180
179
 
@@ -188,10 +187,10 @@ module ActionDispatch # :nodoc:
188
187
  yield self if block_given?
189
188
  end
190
189
 
191
- def has_header?(key); headers.key? key; end
192
- def get_header(key); headers[key]; end
193
- def set_header(key, v); headers[key] = v; end
194
- def delete_header(key); headers.delete key; end
190
+ def has_header?(key); @headers.key? key; end
191
+ def get_header(key); @headers[key]; end
192
+ def set_header(key, v); @headers[key] = v; end
193
+ def delete_header(key); @headers.delete key; end
195
194
 
196
195
  def await_commit
197
196
  synchronize do
@@ -294,7 +293,7 @@ module ActionDispatch # :nodoc:
294
293
  @status
295
294
  end
296
295
 
297
- # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
296
+ # Returns a string to ensure compatibility with +Net::HTTPResponse+.
298
297
  def code
299
298
  @status.to_s
300
299
  end
@@ -336,7 +335,7 @@ module ActionDispatch # :nodoc:
336
335
  # Avoid having to pass an open file handle as the response body.
337
336
  # Rack::Sendfile will usually intercept the response and uses
338
337
  # the path directly, so there is no reason to open the file.
339
- class FileBody #:nodoc:
338
+ class FileBody # :nodoc:
340
339
  attr_reader :to_path
341
340
 
342
341
  def initialize(path)
@@ -397,7 +396,7 @@ module ActionDispatch # :nodoc:
397
396
  # status, headers, body = *response
398
397
  def to_a
399
398
  commit!
400
- rack_response @status, @header.to_hash
399
+ rack_response @status, @headers.to_hash
401
400
  end
402
401
  alias prepare! to_a
403
402
 
@@ -464,8 +463,7 @@ module ActionDispatch # :nodoc:
464
463
  # our last chance.
465
464
  commit! unless committed?
466
465
 
467
- headers.freeze
468
- request.commit_cookie_jar! unless committed?
466
+ @request.commit_cookie_jar! unless committed?
469
467
  end
470
468
 
471
469
  def build_buffer(response, body)
@@ -489,10 +487,6 @@ module ActionDispatch # :nodoc:
489
487
  @response = response
490
488
  end
491
489
 
492
- def each(*args, &block)
493
- @response.each(*args, &block)
494
- end
495
-
496
490
  def close
497
491
  # Rack "close" maps to Response#abort, and *not* Response#close
498
492
  # (which is used when the controller's finished writing)
@@ -503,35 +497,45 @@ module ActionDispatch # :nodoc:
503
497
  @response.body
504
498
  end
505
499
 
500
+ BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
501
+
506
502
  def respond_to?(method, include_private = false)
507
- if method.to_sym == :to_path
503
+ if BODY_METHODS.key?(method)
508
504
  @response.stream.respond_to?(method)
509
505
  else
510
506
  super
511
507
  end
512
508
  end
513
509
 
514
- def to_path
515
- @response.stream.to_path
510
+ def to_ary
511
+ @response.stream.to_ary
516
512
  end
517
513
 
518
- def to_ary
519
- nil
514
+ def each(*args, &block)
515
+ @response.each(*args, &block)
516
+ end
517
+
518
+ def call(*arguments, &block)
519
+ @response.stream.call(*arguments, &block)
520
+ end
521
+
522
+ def to_path
523
+ @response.stream.to_path
520
524
  end
521
525
  end
522
526
 
523
527
  def handle_no_content!
524
528
  if NO_CONTENT_CODES.include?(@status)
525
- @header.delete CONTENT_TYPE
526
- @header.delete "Content-Length"
529
+ @headers.delete CONTENT_TYPE
530
+ @headers.delete "Content-Length"
527
531
  end
528
532
  end
529
533
 
530
- def rack_response(status, header)
534
+ def rack_response(status, headers)
531
535
  if NO_CONTENT_CODES.include?(status)
532
- [status, header, []]
536
+ [status, headers, []]
533
537
  else
534
- [status, header, RackBody.new(self)]
538
+ [status, headers, RackBody.new(self)]
535
539
  end
536
540
  end
537
541
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActionDispatch
4
4
  module Http
5
+ # = Action Dispatch HTTP \UploadedFile
6
+ #
5
7
  # Models uploaded files.
6
8
  #
7
9
  # The actual file is accessible via the +tempfile+ accessor, though some
@@ -28,6 +30,8 @@ module ActionDispatch
28
30
  @tempfile = hash[:tempfile]
29
31
  raise(ArgumentError, ":tempfile is required") unless @tempfile
30
32
 
33
+ @content_type = hash[:type]
34
+
31
35
  if hash[:filename]
32
36
  @original_filename = hash[:filename].dup
33
37
 
@@ -40,8 +44,17 @@ module ActionDispatch
40
44
  @original_filename = nil
41
45
  end
42
46
 
43
- @content_type = hash[:type]
44
- @headers = hash[:head]
47
+ if hash[:head]
48
+ @headers = hash[:head].dup
49
+
50
+ begin
51
+ @headers.encode!(Encoding::UTF_8)
52
+ rescue EncodingError
53
+ @headers.force_encoding(Encoding::UTF_8)
54
+ end
55
+ else
56
+ @headers = nil
57
+ end
45
58
  end
46
59
 
47
60
  # Shortcut for +tempfile.read+.
@@ -71,7 +71,8 @@ module ActionDispatch
71
71
  path = options[:script_name].to_s.chomp("/")
72
72
  path << options[:path] if options.key?(:path)
73
73
 
74
- add_trailing_slash(path) if options[:trailing_slash]
74
+ path = "/" if options[:trailing_slash] && path.blank?
75
+
75
76
  add_params(path, options[:params]) if options.key?(:params)
76
77
  add_anchor(path, options[:anchor]) if options.key?(:anchor)
77
78
 
@@ -101,14 +102,6 @@ module ActionDispatch
101
102
  parts[0..-(tld_length + 2)]
102
103
  end
103
104
 
104
- def add_trailing_slash(path)
105
- if path.include?("?")
106
- path.sub!(/\?/, '/\&')
107
- elsif !path.include?(".")
108
- path.sub!(/[^\/]\z|\A\z/, '\&/')
109
- end
110
- end
111
-
112
105
  def build_host_url(host, port, protocol, options, path)
113
106
  if match = host.match(HOST_REGEXP)
114
107
  protocol ||= match[1] unless protocol == false
@@ -222,7 +215,7 @@ module ActionDispatch
222
215
  if forwarded = x_forwarded_host.presence
223
216
  forwarded.split(/,\s?/).last
224
217
  else
225
- get_header("HTTP_HOST") || "#{server_name || server_addr}:#{get_header('SERVER_PORT')}"
218
+ get_header("HTTP_HOST") || "#{server_name}:#{get_header('SERVER_PORT')}"
226
219
  end
227
220
  end
228
221
 
@@ -258,12 +251,10 @@ module ActionDispatch
258
251
  # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
259
252
  # req.port # => 8080
260
253
  def port
261
- @port ||= begin
262
- if raw_host_with_port =~ /:(\d+)$/
263
- $1.to_i
264
- else
265
- standard_port
266
- end
254
+ @port ||= if raw_host_with_port =~ /:(\d+)$/
255
+ $1.to_i
256
+ else
257
+ standard_port
267
258
  end
268
259
  end
269
260
 
@@ -272,9 +263,10 @@ module ActionDispatch
272
263
  # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
273
264
  # req.standard_port # => 80
274
265
  def standard_port
275
- case protocol
276
- when "https://" then 443
277
- else 80
266
+ if "https://" == protocol
267
+ 443
268
+ else
269
+ 80
278
270
  end
279
271
  end
280
272
 
@@ -57,6 +57,9 @@ module ActionDispatch
57
57
  end
58
58
 
59
59
  def generate(name, options, path_parameters)
60
+ original_options = options.dup
61
+ path_params = options.delete(:path_params) || {}
62
+ options = path_params.merge(options)
60
63
  constraints = path_parameters.merge(options)
61
64
  missing_keys = nil
62
65
 
@@ -70,8 +73,11 @@ module ActionDispatch
70
73
 
71
74
  missing_keys = missing_keys(route, parameterized_parts)
72
75
  next if missing_keys && !missing_keys.empty?
73
- params = options.dup.delete_if do |key, _|
74
- parameterized_parts.key?(key) || route.defaults.key?(key)
76
+ params = options.delete_if do |key, _|
77
+ # top-level params' normal behavior of generating query_params
78
+ # should be preserved even if the same key is also a bind_param
79
+ parameterized_parts.key?(key) || route.defaults.key?(key) ||
80
+ (path_params.key?(key) && !original_options.key?(key))
75
81
  end
76
82
 
77
83
  defaults = route.defaults