actionpack 6.1.7.5 → 7.1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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