actionpack 7.2.2.1 → 8.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +123 -109
- data/lib/abstract_controller/rendering.rb +0 -1
- data/lib/action_controller/base.rb +1 -1
- data/lib/action_controller/form_builder.rb +3 -3
- data/lib/action_controller/metal/allow_browser.rb +11 -1
- data/lib/action_controller/metal/conditional_get.rb +5 -1
- data/lib/action_controller/metal/data_streaming.rb +4 -2
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +13 -4
- data/lib/action_controller/metal/rate_limiting.rb +13 -4
- data/lib/action_controller/metal/redirecting.rb +2 -1
- data/lib/action_controller/metal/renderers.rb +2 -3
- data/lib/action_controller/metal/streaming.rb +5 -84
- data/lib/action_controller/metal/strong_parameters.rb +277 -89
- data/lib/action_controller/railtie.rb +1 -7
- data/lib/action_controller/test_case.rb +4 -2
- data/lib/action_dispatch/http/cache.rb +27 -10
- data/lib/action_dispatch/http/content_security_policy.rb +1 -0
- data/lib/action_dispatch/http/param_builder.rb +186 -0
- data/lib/action_dispatch/http/param_error.rb +26 -0
- data/lib/action_dispatch/http/permissions_policy.rb +2 -0
- data/lib/action_dispatch/http/query_parser.rb +53 -0
- data/lib/action_dispatch/http/request.rb +60 -16
- data/lib/action_dispatch/journey/parser.rb +99 -196
- data/lib/action_dispatch/journey/scanner.rb +44 -42
- data/lib/action_dispatch/middleware/cookies.rb +4 -2
- data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -3
- data/lib/action_dispatch/middleware/debug_view.rb +0 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +0 -6
- data/lib/action_dispatch/middleware/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/ssl.rb +13 -3
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +0 -3
- data/lib/action_dispatch/railtie.rb +8 -0
- data/lib/action_dispatch/request/session.rb +1 -0
- data/lib/action_dispatch/request/utils.rb +9 -3
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +90 -62
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
- data/lib/action_dispatch/routing/route_set.rb +20 -8
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_dispatch/testing/assertions/response.rb +12 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +4 -4
- data/lib/action_dispatch/testing/integration.rb +11 -1
- data/lib/action_dispatch/testing/test_process.rb +1 -2
- data/lib/action_dispatch.rb +6 -4
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +15 -34
- data/lib/action_dispatch/journey/parser.y +0 -50
- data/lib/action_dispatch/journey/parser_extras.rb +0 -33
@@ -48,11 +48,6 @@ module ActionController
|
|
48
48
|
end
|
49
49
|
|
50
50
|
ActionController::Parameters.action_on_unpermitted_parameters = action_on_unpermitted_parameters
|
51
|
-
|
52
|
-
unless options.allow_deprecated_parameters_hash_equality.nil?
|
53
|
-
ActionController::Parameters.allow_deprecated_parameters_hash_equality =
|
54
|
-
options.allow_deprecated_parameters_hash_equality
|
55
|
-
end
|
56
51
|
end
|
57
52
|
end
|
58
53
|
|
@@ -85,7 +80,6 @@ module ActionController
|
|
85
80
|
:action_on_unpermitted_parameters,
|
86
81
|
:always_permitted_parameters,
|
87
82
|
:wrap_parameters_by_default,
|
88
|
-
:allow_deprecated_parameters_hash_equality
|
89
83
|
)
|
90
84
|
|
91
85
|
filtered_options.each do |k, v|
|
@@ -123,7 +117,7 @@ module ActionController
|
|
123
117
|
app.config.active_record.query_log_tags |= [:action]
|
124
118
|
|
125
119
|
ActiveSupport.on_load(:active_record) do
|
126
|
-
ActiveRecord::QueryLogs.taggings.merge
|
120
|
+
ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
|
127
121
|
controller: ->(context) { context[:controller]&.controller_name },
|
128
122
|
action: ->(context) { context[:controller]&.action_name },
|
129
123
|
namespaced_controller: ->(context) {
|
@@ -22,6 +22,8 @@ module ActionController
|
|
22
22
|
# database on the main thread, so they could open a txn, then the controller
|
23
23
|
# thread will open a new connection and try to access data that's only visible
|
24
24
|
# to the main thread's txn. This is the problem in #23483.
|
25
|
+
alias_method :original_new_controller_thread, :new_controller_thread
|
26
|
+
|
25
27
|
silence_redefinition_of_method :new_controller_thread
|
26
28
|
def new_controller_thread # :nodoc:
|
27
29
|
yield
|
@@ -106,7 +108,7 @@ module ActionController
|
|
106
108
|
set_header k, "application/x-www-form-urlencoded"
|
107
109
|
end
|
108
110
|
|
109
|
-
case content_mime_type
|
111
|
+
case content_mime_type&.to_sym
|
110
112
|
when nil
|
111
113
|
raise "Unknown Content-Type: #{content_type}"
|
112
114
|
when :json
|
@@ -121,7 +123,7 @@ module ActionController
|
|
121
123
|
end
|
122
124
|
end
|
123
125
|
|
124
|
-
data_stream = StringIO.new(data)
|
126
|
+
data_stream = StringIO.new(data.b)
|
125
127
|
set_header "CONTENT_LENGTH", data_stream.length.to_s
|
126
128
|
set_header "rack.input", data_stream
|
127
129
|
end
|
@@ -9,6 +9,8 @@ module ActionDispatch
|
|
9
9
|
HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE"
|
10
10
|
HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH"
|
11
11
|
|
12
|
+
mattr_accessor :strict_freshness, default: false
|
13
|
+
|
12
14
|
def if_modified_since
|
13
15
|
if since = get_header(HTTP_IF_MODIFIED_SINCE)
|
14
16
|
Time.rfc2822(since) rescue nil
|
@@ -34,19 +36,32 @@ module ActionDispatch
|
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
# Check response freshness (`Last-Modified` and ETag) against request
|
38
|
-
# `If-Modified-Since` and `If-None-Match` conditions.
|
39
|
-
# supplied,
|
39
|
+
# Check response freshness (`Last-Modified` and `ETag`) against request
|
40
|
+
# `If-Modified-Since` and `If-None-Match` conditions.
|
41
|
+
# If both headers are supplied, based on configuration, either `ETag` is preferred over `Last-Modified`
|
42
|
+
# or both are considered equally. You can adjust the preference with
|
43
|
+
# `config.action_dispatch.strict_freshness`.
|
44
|
+
# Reference: http://tools.ietf.org/html/rfc7232#section-6
|
40
45
|
def fresh?(response)
|
41
|
-
|
42
|
-
|
46
|
+
if Request.strict_freshness
|
47
|
+
if if_none_match
|
48
|
+
etag_matches?(response.etag)
|
49
|
+
elsif if_modified_since
|
50
|
+
not_modified?(response.last_modified)
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
else
|
55
|
+
last_modified = if_modified_since
|
56
|
+
etag = if_none_match
|
43
57
|
|
44
|
-
|
58
|
+
return false unless last_modified || etag
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
60
|
+
success = true
|
61
|
+
success &&= not_modified?(response.last_modified) if last_modified
|
62
|
+
success &&= etag_matches?(response.etag) if etag
|
63
|
+
success
|
64
|
+
end
|
50
65
|
end
|
51
66
|
end
|
52
67
|
|
@@ -171,6 +186,7 @@ module ActionDispatch
|
|
171
186
|
PUBLIC = "public"
|
172
187
|
PRIVATE = "private"
|
173
188
|
MUST_REVALIDATE = "must-revalidate"
|
189
|
+
IMMUTABLE = "immutable"
|
174
190
|
|
175
191
|
def handle_conditional_get!
|
176
192
|
# Normally default cache control setting is handled by ETag middleware. But, if
|
@@ -221,6 +237,7 @@ module ActionDispatch
|
|
221
237
|
options << MUST_REVALIDATE if control[:must_revalidate]
|
222
238
|
options << "stale-while-revalidate=#{stale_while_revalidate.to_i}" if stale_while_revalidate
|
223
239
|
options << "stale-if-error=#{stale_if_error.to_i}" if stale_if_error
|
240
|
+
options << IMMUTABLE if control[:immutable]
|
224
241
|
options.concat(extras) if extras
|
225
242
|
end
|
226
243
|
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class ParamBuilder
|
5
|
+
# --
|
6
|
+
# This implementation is based on Rack::QueryParser,
|
7
|
+
# Copyright (C) 2007-2021 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
|
8
|
+
|
9
|
+
def self.make_default(param_depth_limit)
|
10
|
+
new param_depth_limit
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :param_depth_limit
|
14
|
+
|
15
|
+
def initialize(param_depth_limit)
|
16
|
+
@param_depth_limit = param_depth_limit
|
17
|
+
end
|
18
|
+
|
19
|
+
cattr_accessor :ignore_leading_brackets
|
20
|
+
|
21
|
+
LEADING_BRACKETS_COMPAT = defined?(::Rack::RELEASE) && ::Rack::RELEASE.to_s.start_with?("2.")
|
22
|
+
|
23
|
+
cattr_accessor :default
|
24
|
+
self.default = make_default(100)
|
25
|
+
|
26
|
+
class << self
|
27
|
+
delegate :from_query_string, :from_pairs, :from_hash, to: :default
|
28
|
+
end
|
29
|
+
|
30
|
+
def from_query_string(qs, separator: nil, encoding_template: nil)
|
31
|
+
from_pairs QueryParser.each_pair(qs, separator), encoding_template: encoding_template
|
32
|
+
end
|
33
|
+
|
34
|
+
def from_pairs(pairs, encoding_template: nil)
|
35
|
+
params = make_params
|
36
|
+
|
37
|
+
pairs.each do |k, v|
|
38
|
+
if Hash === v
|
39
|
+
v = ActionDispatch::Http::UploadedFile.new(v)
|
40
|
+
end
|
41
|
+
|
42
|
+
store_nested_param(params, k, v, 0, encoding_template)
|
43
|
+
end
|
44
|
+
|
45
|
+
params
|
46
|
+
rescue ArgumentError => e
|
47
|
+
raise InvalidParameterError, e.message, e.backtrace
|
48
|
+
end
|
49
|
+
|
50
|
+
def from_hash(hash, encoding_template: nil)
|
51
|
+
# Force encodings from encoding template
|
52
|
+
hash = Request::Utils::CustomParamEncoder.encode_for_template(hash, encoding_template)
|
53
|
+
|
54
|
+
# Assert valid encoding
|
55
|
+
Request::Utils.check_param_encoding(hash)
|
56
|
+
|
57
|
+
# Convert hashes to HWIA (or UploadedFile), and deep-munge nils
|
58
|
+
# out of arrays
|
59
|
+
hash = Request::Utils.normalize_encode_params(hash)
|
60
|
+
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def store_nested_param(params, name, v, depth, encoding_template = nil)
|
66
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
67
|
+
|
68
|
+
if !name
|
69
|
+
# nil name, treat same as empty string (required by tests)
|
70
|
+
k = after = ""
|
71
|
+
elsif depth == 0
|
72
|
+
if ignore_leading_brackets || (ignore_leading_brackets.nil? && LEADING_BRACKETS_COMPAT)
|
73
|
+
# Rack 2 compatible behavior, ignore leading brackets
|
74
|
+
if name =~ /\A[\[\]]*([^\[\]]+)\]*/
|
75
|
+
k = $1
|
76
|
+
after = $' || ""
|
77
|
+
|
78
|
+
if !ignore_leading_brackets && (k != $& || !after.empty? && !after.start_with?("["))
|
79
|
+
ActionDispatch.deprecator.warn("Skipping over leading brackets in parameter name #{name.inspect} is deprecated and will parse differently in Rails 8.1 or Rack 3.0.")
|
80
|
+
end
|
81
|
+
else
|
82
|
+
k = name
|
83
|
+
after = ""
|
84
|
+
end
|
85
|
+
else
|
86
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
87
|
+
if start = name.index("[", 1)
|
88
|
+
# Start of parameter nesting, use part before brackets as key
|
89
|
+
k = name[0, start]
|
90
|
+
after = name[start, name.length]
|
91
|
+
else
|
92
|
+
# Plain parameter with no nesting
|
93
|
+
k = name
|
94
|
+
after = ""
|
95
|
+
end
|
96
|
+
end
|
97
|
+
elsif name.start_with?("[]")
|
98
|
+
# Array nesting
|
99
|
+
k = "[]"
|
100
|
+
after = name[2, name.length]
|
101
|
+
elsif name.start_with?("[") && (start = name.index("]", 1))
|
102
|
+
# Hash nesting, use the part inside brackets as the key
|
103
|
+
k = name[1, start - 1]
|
104
|
+
after = name[start + 1, name.length]
|
105
|
+
else
|
106
|
+
# Probably malformed input, nested but not starting with [
|
107
|
+
# treat full name as key for backwards compatibility.
|
108
|
+
k = name
|
109
|
+
after = ""
|
110
|
+
end
|
111
|
+
|
112
|
+
return if k.empty?
|
113
|
+
|
114
|
+
if depth == 0 && String === v
|
115
|
+
# We have to wait until we've found the top part of the name,
|
116
|
+
# because that's what the encoding template is configured with
|
117
|
+
if encoding_template && (designated_encoding = encoding_template[k]) && !v.frozen?
|
118
|
+
v.force_encoding(designated_encoding)
|
119
|
+
end
|
120
|
+
|
121
|
+
# ... and we can't validate the encoding until after we've
|
122
|
+
# applied any template override
|
123
|
+
unless v.valid_encoding?
|
124
|
+
raise InvalidParameterError, "Invalid encoding for parameter: #{v.scrub}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
if after == ""
|
129
|
+
if k == "[]" && depth != 0
|
130
|
+
return (v || !ActionDispatch::Request::Utils.perform_deep_munge) ? [v] : []
|
131
|
+
else
|
132
|
+
params[k] = v
|
133
|
+
end
|
134
|
+
elsif after == "["
|
135
|
+
params[name] = v
|
136
|
+
elsif after == "[]"
|
137
|
+
params[k] ||= []
|
138
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
139
|
+
params[k] << v if v || !ActionDispatch::Request::Utils.perform_deep_munge
|
140
|
+
elsif after.start_with?("[]")
|
141
|
+
# Recognize x[][y] (hash inside array) parameters
|
142
|
+
unless after[2] == "[" && after.end_with?("]") && (child_key = after[3, after.length - 4]) && !child_key.empty? && !child_key.index("[") && !child_key.index("]")
|
143
|
+
# Handle other nested array parameters
|
144
|
+
child_key = after[2, after.length]
|
145
|
+
end
|
146
|
+
params[k] ||= []
|
147
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
148
|
+
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
149
|
+
store_nested_param(params[k].last, child_key, v, depth + 1)
|
150
|
+
else
|
151
|
+
params[k] << store_nested_param(make_params, child_key, v, depth + 1)
|
152
|
+
end
|
153
|
+
else
|
154
|
+
params[k] ||= make_params
|
155
|
+
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
156
|
+
params[k] = store_nested_param(params[k], after, v, depth + 1)
|
157
|
+
end
|
158
|
+
|
159
|
+
params
|
160
|
+
end
|
161
|
+
|
162
|
+
def make_params
|
163
|
+
ActiveSupport::HashWithIndifferentAccess.new
|
164
|
+
end
|
165
|
+
|
166
|
+
def new_depth_limit(param_depth_limit)
|
167
|
+
self.class.new @params_class, param_depth_limit
|
168
|
+
end
|
169
|
+
|
170
|
+
def params_hash_type?(obj)
|
171
|
+
Hash === obj
|
172
|
+
end
|
173
|
+
|
174
|
+
def params_hash_has_key?(hash, key)
|
175
|
+
return false if key.include?("[]")
|
176
|
+
|
177
|
+
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
178
|
+
next h if part == ""
|
179
|
+
return false unless params_hash_type?(h) && h.key?(part)
|
180
|
+
h[part]
|
181
|
+
end
|
182
|
+
|
183
|
+
true
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class ParamError < ActionDispatch::Http::Parameters::ParseError
|
5
|
+
def initialize(message = nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.===(other)
|
10
|
+
super || (
|
11
|
+
defined?(Rack::Utils::ParameterTypeError) && Rack::Utils::ParameterTypeError === other ||
|
12
|
+
defined?(Rack::Utils::InvalidParameterError) && Rack::Utils::InvalidParameterError === other ||
|
13
|
+
defined?(Rack::QueryParser::ParamsTooDeepError) && Rack::QueryParser::ParamsTooDeepError === other
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ParameterTypeError < ParamError
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidParameterError < ParamError
|
22
|
+
end
|
23
|
+
|
24
|
+
class ParamsTooDeepError < ParamError
|
25
|
+
end
|
26
|
+
end
|
@@ -86,12 +86,14 @@ module ActionDispatch # :nodoc:
|
|
86
86
|
ambient_light_sensor: "ambient-light-sensor",
|
87
87
|
autoplay: "autoplay",
|
88
88
|
camera: "camera",
|
89
|
+
display_capture: "display-capture",
|
89
90
|
encrypted_media: "encrypted-media",
|
90
91
|
fullscreen: "fullscreen",
|
91
92
|
geolocation: "geolocation",
|
92
93
|
gyroscope: "gyroscope",
|
93
94
|
hid: "hid",
|
94
95
|
idle_detection: "idle-detection",
|
96
|
+
keyboard_map: "keyboard-map",
|
95
97
|
magnetometer: "magnetometer",
|
96
98
|
microphone: "microphone",
|
97
99
|
midi: "midi",
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "rack"
|
5
|
+
|
6
|
+
module ActionDispatch
|
7
|
+
class QueryParser
|
8
|
+
DEFAULT_SEP = /& */n
|
9
|
+
COMPAT_SEP = /[&;] */n
|
10
|
+
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n, "&;" => /[&;] */n }
|
11
|
+
|
12
|
+
cattr_accessor :strict_query_string_separator
|
13
|
+
|
14
|
+
SEMICOLON_COMPAT = defined?(::Rack::QueryParser::DEFAULT_SEP) && ::Rack::QueryParser::DEFAULT_SEP.to_s.include?(";")
|
15
|
+
|
16
|
+
#--
|
17
|
+
# Note this departs from WHATWG's specified parsing algorithm by
|
18
|
+
# giving a nil value for keys that do not use '='. Callers that need
|
19
|
+
# the standard's interpretation can use `v.to_s`.
|
20
|
+
def self.each_pair(s, separator = nil)
|
21
|
+
return enum_for(:each_pair, s, separator) unless block_given?
|
22
|
+
|
23
|
+
s ||= ""
|
24
|
+
|
25
|
+
splitter =
|
26
|
+
if separator
|
27
|
+
COMMON_SEP[separator] || /[#{separator}] */n
|
28
|
+
elsif strict_query_string_separator
|
29
|
+
DEFAULT_SEP
|
30
|
+
elsif SEMICOLON_COMPAT && s.include?(";")
|
31
|
+
if strict_query_string_separator.nil?
|
32
|
+
ActionDispatch.deprecator.warn("Using semicolon as a query string separator is deprecated and will not be supported in Rails 8.1 or Rack 3.0. Use `&` instead.")
|
33
|
+
end
|
34
|
+
COMPAT_SEP
|
35
|
+
else
|
36
|
+
DEFAULT_SEP
|
37
|
+
end
|
38
|
+
|
39
|
+
s.split(splitter).each do |part|
|
40
|
+
next if part.empty?
|
41
|
+
|
42
|
+
k, v = part.split("=", 2)
|
43
|
+
|
44
|
+
k = URI.decode_www_form_component(k)
|
45
|
+
v &&= URI.decode_www_form_component(v)
|
46
|
+
|
47
|
+
yield k, v
|
48
|
+
end
|
49
|
+
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -55,12 +55,17 @@ module ActionDispatch
|
|
55
55
|
METHOD
|
56
56
|
end
|
57
57
|
|
58
|
+
TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING" # :nodoc:
|
59
|
+
|
58
60
|
def self.empty
|
59
61
|
new({})
|
60
62
|
end
|
61
63
|
|
62
64
|
def initialize(env)
|
63
65
|
super
|
66
|
+
|
67
|
+
@rack_request = Rack::Request.new(env)
|
68
|
+
|
64
69
|
@method = nil
|
65
70
|
@request_method = nil
|
66
71
|
@remote_ip = nil
|
@@ -69,6 +74,8 @@ module ActionDispatch
|
|
69
74
|
@ip = nil
|
70
75
|
end
|
71
76
|
|
77
|
+
attr_reader :rack_request
|
78
|
+
|
72
79
|
def commit_cookie_jar! # :nodoc:
|
73
80
|
end
|
74
81
|
|
@@ -282,7 +289,7 @@ module ActionDispatch
|
|
282
289
|
|
283
290
|
# Returns the content length of the request as an integer.
|
284
291
|
def content_length
|
285
|
-
return raw_post.bytesize if
|
292
|
+
return raw_post.bytesize if has_header?(TRANSFER_ENCODING)
|
286
293
|
super.to_i
|
287
294
|
end
|
288
295
|
|
@@ -386,15 +393,12 @@ module ActionDispatch
|
|
386
393
|
# Override Rack's GET method to support indifferent access.
|
387
394
|
def GET
|
388
395
|
fetch_header("action_dispatch.request.query_parameters") do |k|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
# Check for non UTF-8 parameter values, which would cause errors later
|
394
|
-
Request::Utils.check_param_encoding(rack_query_params)
|
395
|
-
set_header k, Request::Utils.normalize_encode_params(rack_query_params)
|
396
|
+
encoding_template = Request::Utils::CustomParamEncoder.action_encoding_template(self, path_parameters[:controller], path_parameters[:action])
|
397
|
+
rack_query_params = ActionDispatch::ParamBuilder.from_query_string(rack_request.query_string, encoding_template: encoding_template)
|
398
|
+
|
399
|
+
set_header k, rack_query_params
|
396
400
|
end
|
397
|
-
rescue
|
401
|
+
rescue ActionDispatch::ParamError => e
|
398
402
|
raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
|
399
403
|
end
|
400
404
|
alias :query_parameters :GET
|
@@ -402,18 +406,54 @@ module ActionDispatch
|
|
402
406
|
# Override Rack's POST method to support indifferent access.
|
403
407
|
def POST
|
404
408
|
fetch_header("action_dispatch.request.request_parameters") do
|
405
|
-
|
406
|
-
|
409
|
+
encoding_template = Request::Utils::CustomParamEncoder.action_encoding_template(self, path_parameters[:controller], path_parameters[:action])
|
410
|
+
|
411
|
+
param_list = nil
|
412
|
+
pr = parse_formatted_parameters(params_parsers) do
|
413
|
+
if param_list = request_parameters_list
|
414
|
+
ActionDispatch::ParamBuilder.from_pairs(param_list, encoding_template: encoding_template)
|
415
|
+
else
|
416
|
+
# We're not using a version of Rack that provides raw form
|
417
|
+
# pairs; we must use its hash (and thus post-process it below).
|
418
|
+
fallback_request_parameters
|
419
|
+
end
|
407
420
|
end
|
408
|
-
|
409
|
-
|
410
|
-
|
421
|
+
|
422
|
+
# If the request body was parsed by a custom parser like JSON
|
423
|
+
# (and thus the above block was not run), we need to
|
424
|
+
# post-process the result hash.
|
425
|
+
if param_list.nil?
|
426
|
+
pr = ActionDispatch::ParamBuilder.from_hash(pr, encoding_template: encoding_template)
|
427
|
+
end
|
428
|
+
|
429
|
+
self.request_parameters = pr
|
411
430
|
end
|
412
|
-
rescue
|
431
|
+
rescue ActionDispatch::ParamError, EOFError => e
|
413
432
|
raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
|
414
433
|
end
|
415
434
|
alias :request_parameters :POST
|
416
435
|
|
436
|
+
def request_parameters_list
|
437
|
+
# We don't use Rack's parse result, but we must call it so Rack
|
438
|
+
# can populate the rack.request.* keys we need.
|
439
|
+
rack_post = rack_request.POST
|
440
|
+
|
441
|
+
if form_pairs = get_header("rack.request.form_pairs")
|
442
|
+
# Multipart
|
443
|
+
form_pairs
|
444
|
+
elsif form_vars = get_header("rack.request.form_vars")
|
445
|
+
# URL-encoded
|
446
|
+
ActionDispatch::QueryParser.each_pair(form_vars)
|
447
|
+
elsif rack_post && !rack_post.empty?
|
448
|
+
# It was multipart, but Rack did not preserve a pair list
|
449
|
+
# (probably too old). Flat parameter list is not available.
|
450
|
+
nil
|
451
|
+
else
|
452
|
+
# No request body, or not a format Rack knows
|
453
|
+
[]
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
417
457
|
# Returns the authorization header regardless of whether it was specified
|
418
458
|
# directly or through one of the proxy alternatives.
|
419
459
|
def authorization
|
@@ -468,7 +508,7 @@ module ActionDispatch
|
|
468
508
|
def read_body_stream
|
469
509
|
if body_stream
|
470
510
|
reset_stream(body_stream) do
|
471
|
-
if
|
511
|
+
if has_header?(TRANSFER_ENCODING)
|
472
512
|
body_stream.read # Read body stream until EOF if "Transfer-Encoding" is present
|
473
513
|
else
|
474
514
|
body_stream.read(content_length)
|
@@ -490,6 +530,10 @@ module ActionDispatch
|
|
490
530
|
yield
|
491
531
|
end
|
492
532
|
end
|
533
|
+
|
534
|
+
def fallback_request_parameters
|
535
|
+
rack_request.POST
|
536
|
+
end
|
493
537
|
end
|
494
538
|
end
|
495
539
|
|