actionpack 4.1.7 → 4.2.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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +311 -527
  3. data/README.rdoc +7 -2
  4. data/lib/abstract_controller/base.rb +16 -6
  5. data/lib/abstract_controller/callbacks.rb +28 -51
  6. data/lib/abstract_controller/helpers.rb +11 -4
  7. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  8. data/lib/abstract_controller/url_for.rb +1 -1
  9. data/lib/action_controller/base.rb +2 -1
  10. data/lib/action_controller/caching/fragments.rb +7 -1
  11. data/lib/action_controller/caching.rb +1 -1
  12. data/lib/action_controller/log_subscriber.rb +26 -26
  13. data/lib/action_controller/metal/conditional_get.rb +37 -12
  14. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  15. data/lib/action_controller/metal/exceptions.rb +1 -1
  16. data/lib/action_controller/metal/force_ssl.rb +1 -1
  17. data/lib/action_controller/metal/head.rb +7 -3
  18. data/lib/action_controller/metal/http_authentication.rb +14 -9
  19. data/lib/action_controller/metal/instrumentation.rb +8 -5
  20. data/lib/action_controller/metal/live.rb +57 -6
  21. data/lib/action_controller/metal/mime_responds.rb +23 -246
  22. data/lib/action_controller/metal/params_wrapper.rb +2 -2
  23. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  24. data/lib/action_controller/metal/redirecting.rb +14 -8
  25. data/lib/action_controller/metal/renderers.rb +30 -10
  26. data/lib/action_controller/metal/rendering.rb +2 -6
  27. data/lib/action_controller/metal/request_forgery_protection.rb +78 -7
  28. data/lib/action_controller/metal/streaming.rb +1 -1
  29. data/lib/action_controller/metal/strong_parameters.rb +125 -12
  30. data/lib/action_controller/metal/url_for.rb +11 -12
  31. data/lib/action_controller/metal.rb +12 -11
  32. data/lib/action_controller/model_naming.rb +1 -1
  33. data/lib/action_controller/railtie.rb +4 -0
  34. data/lib/action_controller/test_case.rb +112 -75
  35. data/lib/action_controller.rb +1 -1
  36. data/lib/action_dispatch/http/cache.rb +5 -4
  37. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  38. data/lib/action_dispatch/http/headers.rb +43 -9
  39. data/lib/action_dispatch/http/mime_negotiation.rb +10 -3
  40. data/lib/action_dispatch/http/mime_type.rb +2 -2
  41. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  42. data/lib/action_dispatch/http/parameters.rb +11 -26
  43. data/lib/action_dispatch/http/request.rb +37 -11
  44. data/lib/action_dispatch/http/response.rb +70 -18
  45. data/lib/action_dispatch/http/upload.rb +3 -8
  46. data/lib/action_dispatch/http/url.rb +88 -69
  47. data/lib/action_dispatch/journey/formatter.rb +33 -17
  48. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  49. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  50. data/lib/action_dispatch/journey/gtg/transition_table.rb +20 -28
  51. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  52. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  53. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  54. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  55. data/lib/action_dispatch/journey/parser.rb +52 -60
  56. data/lib/action_dispatch/journey/parser.y +11 -10
  57. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  58. data/lib/action_dispatch/journey/route.rb +3 -18
  59. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  60. data/lib/action_dispatch/journey/router.rb +53 -77
  61. data/lib/action_dispatch/journey/scanner.rb +5 -5
  62. data/lib/action_dispatch/journey/visitors.rb +81 -92
  63. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  64. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  65. data/lib/action_dispatch/middleware/callbacks.rb +1 -1
  66. data/lib/action_dispatch/middleware/cookies.rb +29 -29
  67. data/lib/action_dispatch/middleware/debug_exceptions.rb +15 -4
  68. data/lib/action_dispatch/middleware/exception_wrapper.rb +50 -18
  69. data/lib/action_dispatch/middleware/flash.rb +13 -7
  70. data/lib/action_dispatch/middleware/params_parser.rb +1 -1
  71. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  72. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  73. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  74. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  75. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
  76. data/lib/action_dispatch/middleware/static.rb +66 -37
  77. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  78. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +37 -9
  79. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
  80. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
  81. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  83. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -0
  84. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +2 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
  86. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
  87. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  88. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  89. data/lib/action_dispatch/routing/inspector.rb +5 -12
  90. data/lib/action_dispatch/routing/mapper.rb +410 -281
  91. data/lib/action_dispatch/routing/polymorphic_routes.rb +191 -79
  92. data/lib/action_dispatch/routing/redirection.rb +10 -12
  93. data/lib/action_dispatch/routing/route_set.rb +297 -168
  94. data/lib/action_dispatch/routing/url_for.rb +15 -4
  95. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  96. data/lib/action_dispatch/testing/assertions/response.rb +2 -7
  97. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  98. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  99. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  100. data/lib/action_dispatch/testing/assertions.rb +11 -7
  101. data/lib/action_dispatch/testing/integration.rb +24 -19
  102. data/lib/action_dispatch/testing/test_request.rb +1 -1
  103. data/lib/action_dispatch/testing/test_response.rb +7 -0
  104. data/lib/action_pack/gem_version.rb +3 -3
  105. metadata +55 -13
  106. data/lib/action_controller/metal/responder.rb +0 -297
@@ -17,6 +17,7 @@ module ActionController
17
17
  autoload :ConditionalGet
18
18
  autoload :Cookies
19
19
  autoload :DataStreaming
20
+ autoload :EtagWithTemplateDigest
20
21
  autoload :Flash
21
22
  autoload :ForceSSL
22
23
  autoload :Head
@@ -33,7 +34,6 @@ module ActionController
33
34
  autoload :Rendering
34
35
  autoload :RequestForgeryProtection
35
36
  autoload :Rescue
36
- autoload :Responder
37
37
  autoload :Streaming
38
38
  autoload :StrongParameters
39
39
  autoload :Testing
@@ -69,17 +69,17 @@ module ActionDispatch
69
69
  end
70
70
 
71
71
  def date
72
- if date_header = headers['Date']
72
+ if date_header = headers[DATE]
73
73
  Time.httpdate(date_header)
74
74
  end
75
75
  end
76
76
 
77
77
  def date?
78
- headers.include?('Date')
78
+ headers.include?(DATE)
79
79
  end
80
80
 
81
81
  def date=(utc_time)
82
- headers['Date'] = utc_time.httpdate
82
+ headers[DATE] = utc_time.httpdate
83
83
  end
84
84
 
85
85
  def etag=(etag)
@@ -89,10 +89,11 @@ module ActionDispatch
89
89
 
90
90
  private
91
91
 
92
+ DATE = 'Date'.freeze
92
93
  LAST_MODIFIED = "Last-Modified".freeze
93
94
  ETAG = "ETag".freeze
94
95
  CACHE_CONTROL = "Cache-Control".freeze
95
- SPECIAL_KEYS = %w[extras no-cache max-age public must-revalidate]
96
+ SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public must-revalidate])
96
97
 
97
98
  def cache_control_segments
98
99
  if cache_control = self[CACHE_CONTROL]
@@ -6,8 +6,8 @@ module ActionDispatch
6
6
  module Http
7
7
  # Allows you to specify sensitive parameters which will be replaced from
8
8
  # the request log by looking in the query string of the request and all
9
- # subhashes of the params hash to filter. If a block is given, each key and
10
- # value of the params hash and all subhashes is passed to it, the value
9
+ # sub-hashes of the params hash to filter. If a block is given, each key and
10
+ # value of the params hash and all sub-hashes is passed to it, the value
11
11
  # or key can be replaced using String#replace or similar method.
12
12
  #
13
13
  # env["action_dispatch.parameter_filter"] = [:password]
@@ -1,27 +1,47 @@
1
1
  module ActionDispatch
2
2
  module Http
3
+ # Provides access to the request's HTTP headers from the environment.
4
+ #
5
+ # env = { "CONTENT_TYPE" => "text/plain" }
6
+ # headers = ActionDispatch::Http::Headers.new(env)
7
+ # headers["Content-Type"] # => "text/plain"
3
8
  class Headers
4
- CGI_VARIABLES = %w(
5
- CONTENT_TYPE CONTENT_LENGTH
6
- HTTPS AUTH_TYPE GATEWAY_INTERFACE
7
- PATH_INFO PATH_TRANSLATED QUERY_STRING
8
- REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER
9
- REQUEST_METHOD SCRIPT_NAME
10
- SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
11
- )
9
+ CGI_VARIABLES = Set.new(%W[
10
+ AUTH_TYPE
11
+ CONTENT_LENGTH
12
+ CONTENT_TYPE
13
+ GATEWAY_INTERFACE
14
+ HTTPS
15
+ PATH_INFO
16
+ PATH_TRANSLATED
17
+ QUERY_STRING
18
+ REMOTE_ADDR
19
+ REMOTE_HOST
20
+ REMOTE_IDENT
21
+ REMOTE_USER
22
+ REQUEST_METHOD
23
+ SCRIPT_NAME
24
+ SERVER_NAME
25
+ SERVER_PORT
26
+ SERVER_PROTOCOL
27
+ SERVER_SOFTWARE
28
+ ]).freeze
29
+
12
30
  HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
13
31
 
14
32
  include Enumerable
15
33
  attr_reader :env
16
34
 
17
- def initialize(env = {})
35
+ def initialize(env = {}) # :nodoc:
18
36
  @env = env
19
37
  end
20
38
 
39
+ # Returns the value for the given key mapped to @env.
21
40
  def [](key)
22
41
  @env[env_name(key)]
23
42
  end
24
43
 
44
+ # Sets the given value for the key mapped to @env.
25
45
  def []=(key, value)
26
46
  @env[env_name(key)] = value
27
47
  end
@@ -31,6 +51,13 @@ module ActionDispatch
31
51
  end
32
52
  alias :include? :key?
33
53
 
54
+ # Returns the value for the given key mapped to @env.
55
+ #
56
+ # If the key is not found and an optional code block is not provided,
57
+ # raises a <tt>KeyError</tt> exception.
58
+ #
59
+ # If the code block is provided, then it will be run and
60
+ # its result returned.
34
61
  def fetch(key, *args, &block)
35
62
  @env.fetch env_name(key), *args, &block
36
63
  end
@@ -39,12 +66,17 @@ module ActionDispatch
39
66
  @env.each(&block)
40
67
  end
41
68
 
69
+ # Returns a new Http::Headers instance containing the contents of
70
+ # <tt>headers_or_env</tt> and the original instance.
42
71
  def merge(headers_or_env)
43
72
  headers = Http::Headers.new(env.dup)
44
73
  headers.merge!(headers_or_env)
45
74
  headers
46
75
  end
47
76
 
77
+ # Adds the contents of <tt>headers_or_env</tt> to original instance
78
+ # entries; duplicate keys are overwritten with the values from
79
+ # <tt>headers_or_env</tt>.
48
80
  def merge!(headers_or_env)
49
81
  headers_or_env.each do |key, value|
50
82
  self[env_name(key)] = value
@@ -52,6 +84,8 @@ module ActionDispatch
52
84
  end
53
85
 
54
86
  private
87
+ # Converts a HTTP header name to an environment variable name if it is
88
+ # not contained within the headers hash.
55
89
  def env_name(key)
56
90
  key = key.to_s
57
91
  if key =~ HTTP_HEADER
@@ -54,8 +54,14 @@ module ActionDispatch
54
54
  end
55
55
 
56
56
  def formats
57
- @env["action_dispatch.request.formats"] ||=
58
- if parameters[:format]
57
+ @env["action_dispatch.request.formats"] ||= begin
58
+ params_readable = begin
59
+ parameters[:format]
60
+ rescue ActionController::BadRequest
61
+ false
62
+ end
63
+
64
+ if params_readable
59
65
  Array(Mime[parameters[:format]])
60
66
  elsif use_accept_header && valid_accept_header
61
67
  accepts
@@ -64,13 +70,14 @@ module ActionDispatch
64
70
  else
65
71
  [Mime::HTML]
66
72
  end
73
+ end
67
74
  end
68
75
 
69
76
  # Sets the \variant for template.
70
77
  def variant=(variant)
71
78
  if variant.is_a?(Symbol)
72
79
  @variant = [variant]
73
- elsif variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
80
+ elsif variant.nil? || variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
74
81
  @variant = variant
75
82
  else
76
83
  raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \
@@ -45,8 +45,8 @@ module Mime
45
45
  #
46
46
  # respond_to do |format|
47
47
  # format.html
48
- # format.ics { render text: post.to_ics, mime_type: Mime::Type["text/calendar"] }
49
- # format.xml { render xml: @people }
48
+ # format.ics { render text: @post.to_ics, mime_type: Mime::Type["text/calendar"] }
49
+ # format.xml { render xml: @post }
50
50
  # end
51
51
  # end
52
52
  # end
@@ -56,7 +56,7 @@ module ActionDispatch
56
56
  elsif value.is_a?(Array)
57
57
  value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
58
58
  elsif blocks.any?
59
- key = key.dup
59
+ key = key.dup if key.duplicable?
60
60
  value = value.dup if value.duplicable?
61
61
  blocks.each { |b| b.call(key, value) }
62
62
  end
@@ -1,13 +1,11 @@
1
1
  require 'active_support/core_ext/hash/keys'
2
2
  require 'active_support/core_ext/hash/indifferent_access'
3
+ require 'active_support/deprecation'
3
4
 
4
5
  module ActionDispatch
5
6
  module Http
6
7
  module Parameters
7
- def initialize(env)
8
- super
9
- @symbolized_path_params = nil
10
- end
8
+ PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
11
9
 
12
10
  # Returns both GET and POST \parameters in a single hash.
13
11
  def parameters
@@ -18,55 +16,42 @@ module ActionDispatch
18
16
  query_parameters.dup
19
17
  end
20
18
  params.merge!(path_parameters)
21
- params.with_indifferent_access
22
19
  end
23
20
  end
24
21
  alias :params :parameters
25
22
 
26
23
  def path_parameters=(parameters) #:nodoc:
27
- @symbolized_path_params = nil
28
- @env.delete("action_dispatch.request.parameters")
29
- @env["action_dispatch.request.path_parameters"] = parameters
24
+ @env.delete('action_dispatch.request.parameters')
25
+ @env[PARAMETERS_KEY] = parameters
30
26
  end
31
27
 
32
- # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
33
28
  def symbolized_path_parameters
34
- @symbolized_path_params ||= path_parameters.symbolize_keys
29
+ ActiveSupport::Deprecation.warn(
30
+ '`symbolized_path_parameters` is deprecated. Please use `path_parameters`.'
31
+ )
32
+ path_parameters
35
33
  end
36
34
 
37
35
  # Returns a hash with the \parameters used to form the \path of the request.
38
36
  # Returned hash keys are strings:
39
37
  #
40
38
  # {'action' => 'my_action', 'controller' => 'my_controller'}
41
- #
42
- # See <tt>symbolized_path_parameters</tt> for symbolized keys.
43
39
  def path_parameters
44
- @env["action_dispatch.request.path_parameters"] ||= {}
45
- end
46
-
47
- def reset_parameters #:nodoc:
48
- @env.delete("action_dispatch.request.parameters")
40
+ @env[PARAMETERS_KEY] ||= {}
49
41
  end
50
42
 
51
43
  private
52
44
 
53
- # Convert nested Hash to HashWithIndifferentAccess
54
- # and UTF-8 encode both keys and values in nested Hash.
45
+ # Convert nested Hash to HashWithIndifferentAccess.
55
46
  #
56
- # TODO: Validate that the characters are UTF-8. If they aren't,
57
- # you'll get a weird error down the road, but our form handling
58
- # should really prevent that from happening
59
47
  def normalize_encode_params(params)
60
48
  case params
61
- when String
62
- params.force_encoding(Encoding::UTF_8).encode!
63
49
  when Hash
64
50
  if params.has_key?(:tempfile)
65
51
  UploadedFile.new(params)
66
52
  else
67
53
  params.each_with_object({}) do |(key, val), new_hash|
68
- new_key = key.is_a?(String) ? key.dup.force_encoding(Encoding::UTF_8).encode! : key
69
- new_hash[new_key] = if val.is_a?(Array)
54
+ new_hash[key] = if val.is_a?(Array)
70
55
  val.map! { |el| normalize_encode_params(el) }
71
56
  else
72
57
  normalize_encode_params(val)
@@ -23,7 +23,7 @@ module ActionDispatch
23
23
  autoload :Session, 'action_dispatch/request/session'
24
24
  autoload :Utils, 'action_dispatch/request/utils'
25
25
 
26
- LOCALHOST = Regexp.union [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
26
+ LOCALHOST = Regexp.union [/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
27
27
 
28
28
  ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
29
29
  PATH_TRANSLATED REMOTE_HOST
@@ -53,6 +53,17 @@ module ActionDispatch
53
53
  @uuid = nil
54
54
  end
55
55
 
56
+ def check_path_parameters!
57
+ # If any of the path parameters has an invalid encoding then
58
+ # raise since it's likely to trigger errors further on.
59
+ path_parameters.each do |key, value|
60
+ next unless value.respond_to?(:valid_encoding?)
61
+ unless value.valid_encoding?
62
+ raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
63
+ end
64
+ end
65
+ end
66
+
56
67
  def key?(key)
57
68
  @env.key?(key)
58
69
  end
@@ -64,6 +75,7 @@ module ActionDispatch
64
75
  # Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
65
76
  # Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
66
77
  # Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
78
+ # Calendar Extensions to WebDAV (http://www.ietf.org/rfc/rfc4791.txt)
67
79
  # PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
68
80
  RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
69
81
  RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
@@ -71,9 +83,10 @@ module ActionDispatch
71
83
  RFC3648 = %w(ORDERPATCH)
72
84
  RFC3744 = %w(ACL)
73
85
  RFC5323 = %w(SEARCH)
86
+ RFC4791 = %w(MKCALENDAR)
74
87
  RFC5789 = %w(PATCH)
75
88
 
76
- HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
89
+ HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
77
90
 
78
91
  HTTP_METHOD_LOOKUP = {}
79
92
 
@@ -92,6 +105,12 @@ module ActionDispatch
92
105
  @request_method ||= check_method(env["REQUEST_METHOD"])
93
106
  end
94
107
 
108
+ def request_method=(request_method) #:nodoc:
109
+ if check_method(request_method)
110
+ @request_method = env["REQUEST_METHOD"] = request_method
111
+ end
112
+ end
113
+
95
114
  # Returns a symbol form of the #request_method
96
115
  def request_method_symbol
97
116
  HTTP_METHOD_LOOKUP[request_method]
@@ -152,6 +171,13 @@ module ActionDispatch
152
171
  Http::Headers.new(@env)
153
172
  end
154
173
 
174
+ # Returns a +String+ with the last requested path including their params.
175
+ #
176
+ # # get '/foo'
177
+ # request.original_fullpath # => '/foo'
178
+ #
179
+ # # get '/foo?bar'
180
+ # request.original_fullpath # => '/foo?bar'
155
181
  def original_fullpath
156
182
  @original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
157
183
  end
@@ -189,8 +215,8 @@ module ActionDispatch
189
215
  end
190
216
 
191
217
  # Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
192
- # (case-insensitive). All major JavaScript libraries send this header with
193
- # every Ajax request.
218
+ # (case-insensitive), which may need to be manually added depending on the
219
+ # choice of JavaScript libraries and frameworks.
194
220
  def xml_http_request?
195
221
  @env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
196
222
  end
@@ -205,7 +231,7 @@ module ActionDispatch
205
231
  @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
206
232
  end
207
233
 
208
- # Returns the unique request id, which is based off either the X-Request-Id header that can
234
+ # Returns the unique request id, which is based on either the X-Request-Id header that can
209
235
  # be generated by a firewall, load balancer, or web server or by the RequestId middleware
210
236
  # (which sets the action_dispatch.request_id environment variable).
211
237
  #
@@ -271,16 +297,16 @@ module ActionDispatch
271
297
 
272
298
  # Override Rack's GET method to support indifferent access
273
299
  def GET
274
- @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
275
- rescue TypeError => e
300
+ @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
301
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
276
302
  raise ActionController::BadRequest.new(:query, e)
277
303
  end
278
304
  alias :query_parameters :GET
279
305
 
280
306
  # Override Rack's POST method to support indifferent access
281
307
  def POST
282
- @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
283
- rescue TypeError => e
308
+ @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
309
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
284
310
  raise ActionController::BadRequest.new(:request, e)
285
311
  end
286
312
  alias :request_parameters :POST
@@ -302,7 +328,7 @@ module ActionDispatch
302
328
  # Extracted into ActionDispatch::Request::Utils.deep_munge, but kept here for backwards compatibility.
303
329
  def deep_munge(hash)
304
330
  ActiveSupport::Deprecation.warn(
305
- "This method has been extracted into ActionDispatch::Request::Utils.deep_munge. Please start using that instead."
331
+ 'This method has been extracted into `ActionDispatch::Request::Utils.deep_munge`. Please start using that instead.'
306
332
  )
307
333
 
308
334
  Utils.deep_munge(hash)
@@ -315,7 +341,7 @@ module ActionDispatch
315
341
 
316
342
  private
317
343
  def check_method(name)
318
- HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
344
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
319
345
  name
320
346
  end
321
347
  end
@@ -1,4 +1,6 @@
1
1
  require 'active_support/core_ext/module/attribute_accessors'
2
+ require 'active_support/core_ext/string/filters'
3
+ require 'active_support/deprecation'
2
4
  require 'action_dispatch/http/filter_redirect'
3
5
  require 'monitor'
4
6
 
@@ -97,6 +99,9 @@ module ActionDispatch # :nodoc:
97
99
  x
98
100
  end
99
101
 
102
+ def abort
103
+ end
104
+
100
105
  def close
101
106
  @response.commit!
102
107
  @closed = true
@@ -207,18 +212,6 @@ module ActionDispatch # :nodoc:
207
212
  end
208
213
  alias_method :status_message, :message
209
214
 
210
- def respond_to?(method, include_private = false)
211
- if method.to_s == 'to_path'
212
- stream.respond_to?(method)
213
- else
214
- super
215
- end
216
- end
217
-
218
- def to_path
219
- stream.to_path
220
- end
221
-
222
215
  # Returns the content of the response as a string. This contains the contents
223
216
  # of any calls to <tt>render</tt>.
224
217
  def body
@@ -271,13 +264,39 @@ module ActionDispatch # :nodoc:
271
264
  stream.close if stream.respond_to?(:close)
272
265
  end
273
266
 
267
+ def abort
268
+ if stream.respond_to?(:abort)
269
+ stream.abort
270
+ elsif stream.respond_to?(:close)
271
+ # `stream.close` should really be reserved for a close from the
272
+ # other direction, but we must fall back to it for
273
+ # compatibility.
274
+ stream.close
275
+ end
276
+ end
277
+
274
278
  # Turns the Response into a Rack-compatible array of the status, headers,
275
- # and body.
279
+ # and body. Allows explict splatting:
280
+ #
281
+ # status, headers, body = *response
276
282
  def to_a
277
283
  rack_response @status, @header.to_hash
278
284
  end
279
285
  alias prepare! to_a
280
- alias to_ary to_a
286
+
287
+ # Be super clear that a response object is not an Array. Defining this
288
+ # would make implicit splatting work, but it also makes adding responses
289
+ # as arrays work, and "flattening" responses, cascading to the rack body!
290
+ # Not sensible behavior.
291
+ def to_ary
292
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
293
+ `ActionDispatch::Response#to_ary` no longer performs implicit conversion
294
+ to an array. Please use `response.to_a` instead, or a splat like `status,
295
+ headers, body = *response`.
296
+ MSG
297
+
298
+ to_a
299
+ end
281
300
 
282
301
  # Returns the response cookies, converted to a Hash of (name => value) pairs
283
302
  #
@@ -296,9 +315,6 @@ module ActionDispatch # :nodoc:
296
315
  cookies
297
316
  end
298
317
 
299
- def _status_code
300
- @status
301
- end
302
318
  private
303
319
 
304
320
  def before_committed
@@ -337,6 +353,42 @@ module ActionDispatch # :nodoc:
337
353
  !@sending_file && @charset != false
338
354
  end
339
355
 
356
+ class RackBody
357
+ def initialize(response)
358
+ @response = response
359
+ end
360
+
361
+ def each(*args, &block)
362
+ @response.each(*args, &block)
363
+ end
364
+
365
+ def close
366
+ # Rack "close" maps to Response#abort, and *not* Response#close
367
+ # (which is used when the controller's finished writing)
368
+ @response.abort
369
+ end
370
+
371
+ def body
372
+ @response.body
373
+ end
374
+
375
+ def respond_to?(method, include_private = false)
376
+ if method.to_s == 'to_path'
377
+ @response.stream.respond_to?(method)
378
+ else
379
+ super
380
+ end
381
+ end
382
+
383
+ def to_path
384
+ @response.stream.to_path
385
+ end
386
+
387
+ def to_ary
388
+ nil
389
+ end
390
+ end
391
+
340
392
  def rack_response(status, header)
341
393
  assign_default_content_type_and_charset!(header)
342
394
  handle_conditional_get!
@@ -347,7 +399,7 @@ module ActionDispatch # :nodoc:
347
399
  header.delete CONTENT_TYPE
348
400
  [status, header, []]
349
401
  else
350
- [status, header, Rack::BodyProxy.new(self){}]
402
+ [status, header, RackBody.new(self)]
351
403
  end
352
404
  end
353
405
  end