actionpack 7.2.1.1 → 8.0.0.rc1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -100
- data/lib/abstract_controller/helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +0 -1
- data/lib/action_controller/api.rb +1 -0
- data/lib/action_controller/form_builder.rb +3 -3
- data/lib/action_controller/metal/allow_browser.rb +12 -2
- data/lib/action_controller/metal/conditional_get.rb +6 -3
- data/lib/action_controller/metal/http_authentication.rb +6 -3
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +19 -8
- data/lib/action_controller/metal/rate_limiting.rb +13 -4
- 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 +274 -88
- data/lib/action_controller/railtie.rb +1 -7
- data/lib/action_controller/test_case.rb +6 -5
- data/lib/action_dispatch/http/cache.rb +27 -10
- data/lib/action_dispatch/http/content_security_policy.rb +5 -8
- data/lib/action_dispatch/http/filter_parameters.rb +4 -9
- data/lib/action_dispatch/http/filter_redirect.rb +2 -9
- data/lib/action_dispatch/http/param_builder.rb +163 -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 +31 -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 +40 -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/remote_ip.rb +5 -6
- data/lib/action_dispatch/middleware/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/ssl.rb +14 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +0 -3
- data/lib/action_dispatch/railtie.rb +2 -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 +91 -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.rb +6 -0
- 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
|
@@ -427,9 +429,7 @@ module ActionController
|
|
427
429
|
# Note that the request method is not verified. The different methods are
|
428
430
|
# available to make the tests more expressive.
|
429
431
|
def get(action, **args)
|
430
|
-
|
431
|
-
cookies.update res.cookies
|
432
|
-
res
|
432
|
+
process(action, method: "GET", **args)
|
433
433
|
end
|
434
434
|
|
435
435
|
# Simulate a POST request with the given parameters and set/volley the response.
|
@@ -637,6 +637,7 @@ module ActionController
|
|
637
637
|
unless @request.cookie_jar.committed?
|
638
638
|
@request.cookie_jar.write(@response)
|
639
639
|
cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
|
640
|
+
cookies.update(@response.cookies)
|
640
641
|
end
|
641
642
|
end
|
642
643
|
@response.prepare!
|
@@ -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
|
|
@@ -8,8 +8,7 @@ require "active_support/core_ext/array/wrap"
|
|
8
8
|
module ActionDispatch # :nodoc:
|
9
9
|
# # Action Dispatch Content Security Policy
|
10
10
|
#
|
11
|
-
# Configures the HTTP [Content-Security-Policy]
|
12
|
-
# (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
|
11
|
+
# Configures the HTTP [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
|
13
12
|
# response header to help protect against XSS and
|
14
13
|
# injection attacks.
|
15
14
|
#
|
@@ -126,6 +125,7 @@ module ActionDispatch # :nodoc:
|
|
126
125
|
MAPPINGS = {
|
127
126
|
self: "'self'",
|
128
127
|
unsafe_eval: "'unsafe-eval'",
|
128
|
+
wasm_unsafe_eval: "'wasm-unsafe-eval'",
|
129
129
|
unsafe_hashes: "'unsafe-hashes'",
|
130
130
|
unsafe_inline: "'unsafe-inline'",
|
131
131
|
none: "'none'",
|
@@ -226,8 +226,7 @@ module ActionDispatch # :nodoc:
|
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
229
|
-
# Enable the [report-uri]
|
230
|
-
# (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri)
|
229
|
+
# Enable the [report-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri)
|
231
230
|
# directive. Violation reports will be sent to the
|
232
231
|
# specified URI:
|
233
232
|
#
|
@@ -237,8 +236,7 @@ module ActionDispatch # :nodoc:
|
|
237
236
|
@directives["report-uri"] = [uri]
|
238
237
|
end
|
239
238
|
|
240
|
-
# Specify asset types for which [Subresource Integrity]
|
241
|
-
# (https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) is required:
|
239
|
+
# Specify asset types for which [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) is required:
|
242
240
|
#
|
243
241
|
# policy.require_sri_for :script, :style
|
244
242
|
#
|
@@ -254,8 +252,7 @@ module ActionDispatch # :nodoc:
|
|
254
252
|
end
|
255
253
|
end
|
256
254
|
|
257
|
-
# Specify whether a [sandbox]
|
258
|
-
# (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox)
|
255
|
+
# Specify whether a [sandbox](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox)
|
259
256
|
# should be enabled for the requested resource:
|
260
257
|
#
|
261
258
|
# policy.sandbox
|
@@ -68,17 +68,12 @@ module ActionDispatch
|
|
68
68
|
ActiveSupport::ParameterFilter.new(filters)
|
69
69
|
end
|
70
70
|
|
71
|
+
KV_RE = "[^&;=]+"
|
72
|
+
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
|
71
73
|
def filtered_query_string # :doc:
|
72
|
-
|
73
|
-
|
74
|
-
if part.include?("=")
|
75
|
-
key, value = part.split("=", 2)
|
76
|
-
parameter_filter.filter(key => value).first.join("=")
|
77
|
-
else
|
78
|
-
part
|
79
|
-
end
|
74
|
+
query_string.gsub(PAIR_RE) do |_|
|
75
|
+
parameter_filter.filter($1 => $2).first.join("=")
|
80
76
|
end
|
81
|
-
filtered_parts.join("")
|
82
77
|
end
|
83
78
|
end
|
84
79
|
end
|
@@ -37,16 +37,9 @@ module ActionDispatch
|
|
37
37
|
def parameter_filtered_location
|
38
38
|
uri = URI.parse(location)
|
39
39
|
unless uri.query.nil? || uri.query.empty?
|
40
|
-
|
41
|
-
|
42
|
-
if part.include?("=")
|
43
|
-
key, value = part.split("=", 2)
|
44
|
-
request.parameter_filter.filter(key => value).first.join("=")
|
45
|
-
else
|
46
|
-
part
|
47
|
-
end
|
40
|
+
uri.query.gsub!(FilterParameters::PAIR_RE) do
|
41
|
+
request.parameter_filter.filter($1 => $2).first.join("=")
|
48
42
|
end
|
49
|
-
uri.query = filtered_parts.join("")
|
50
43
|
end
|
51
44
|
uri.to_s
|
52
45
|
rescue URI::Error
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class ParamBuilder
|
5
|
+
def self.make_default(param_depth_limit)
|
6
|
+
new param_depth_limit
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :param_depth_limit
|
10
|
+
|
11
|
+
def initialize(param_depth_limit)
|
12
|
+
@param_depth_limit = param_depth_limit
|
13
|
+
end
|
14
|
+
|
15
|
+
cattr_accessor :default
|
16
|
+
self.default = make_default(100)
|
17
|
+
|
18
|
+
class << self
|
19
|
+
delegate :from_query_string, :from_pairs, :from_hash, to: :default
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_query_string(qs, separator: nil, encoding_template: nil)
|
23
|
+
from_pairs QueryParser.each_pair(qs, separator), encoding_template: encoding_template
|
24
|
+
end
|
25
|
+
|
26
|
+
def from_pairs(pairs, encoding_template: nil)
|
27
|
+
params = make_params
|
28
|
+
|
29
|
+
pairs.each do |k, v|
|
30
|
+
if Hash === v
|
31
|
+
v = ActionDispatch::Http::UploadedFile.new(v)
|
32
|
+
end
|
33
|
+
|
34
|
+
store_nested_param(params, k, v, 0, encoding_template)
|
35
|
+
end
|
36
|
+
|
37
|
+
params
|
38
|
+
rescue ArgumentError => e
|
39
|
+
raise InvalidParameterError, e.message, e.backtrace
|
40
|
+
end
|
41
|
+
|
42
|
+
def from_hash(hash, encoding_template: nil)
|
43
|
+
# Force encodings from encoding template
|
44
|
+
hash = Request::Utils::CustomParamEncoder.encode_for_template(hash, encoding_template)
|
45
|
+
|
46
|
+
# Assert valid encoding
|
47
|
+
Request::Utils.check_param_encoding(hash)
|
48
|
+
|
49
|
+
# Convert hashes to HWIA (or UploadedFile), and deep-munge nils
|
50
|
+
# out of arrays
|
51
|
+
hash = Request::Utils.normalize_encode_params(hash)
|
52
|
+
|
53
|
+
hash
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def store_nested_param(params, name, v, depth, encoding_template = nil)
|
58
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
59
|
+
|
60
|
+
if !name
|
61
|
+
# nil name, treat same as empty string (required by tests)
|
62
|
+
k = after = ""
|
63
|
+
elsif depth == 0
|
64
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
65
|
+
if start = name.index("[", 1)
|
66
|
+
# Start of parameter nesting, use part before brackets as key
|
67
|
+
k = name[0, start]
|
68
|
+
after = name[start, name.length]
|
69
|
+
else
|
70
|
+
# Plain parameter with no nesting
|
71
|
+
k = name
|
72
|
+
after = ""
|
73
|
+
end
|
74
|
+
elsif name.start_with?("[]")
|
75
|
+
# Array nesting
|
76
|
+
k = "[]"
|
77
|
+
after = name[2, name.length]
|
78
|
+
elsif name.start_with?("[") && (start = name.index("]", 1))
|
79
|
+
# Hash nesting, use the part inside brackets as the key
|
80
|
+
k = name[1, start - 1]
|
81
|
+
after = name[start + 1, name.length]
|
82
|
+
else
|
83
|
+
# Probably malformed input, nested but not starting with [
|
84
|
+
# treat full name as key for backwards compatibility.
|
85
|
+
k = name
|
86
|
+
after = ""
|
87
|
+
end
|
88
|
+
|
89
|
+
return if k.empty?
|
90
|
+
|
91
|
+
if depth == 0 && String === v
|
92
|
+
# We have to wait until we've found the top part of the name,
|
93
|
+
# because that's what the encoding template is configured with
|
94
|
+
if encoding_template && (designated_encoding = encoding_template[k]) && !v.frozen?
|
95
|
+
v.force_encoding(designated_encoding)
|
96
|
+
end
|
97
|
+
|
98
|
+
# ... and we can't validate the encoding until after we've
|
99
|
+
# applied any template override
|
100
|
+
unless v.valid_encoding?
|
101
|
+
raise InvalidParameterError, "Invalid encoding for parameter: #{v.scrub}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if after == ""
|
106
|
+
if k == "[]" && depth != 0
|
107
|
+
return (v || !ActionDispatch::Request::Utils.perform_deep_munge) ? [v] : []
|
108
|
+
else
|
109
|
+
params[k] = v
|
110
|
+
end
|
111
|
+
elsif after == "["
|
112
|
+
params[name] = v
|
113
|
+
elsif after == "[]"
|
114
|
+
params[k] ||= []
|
115
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
116
|
+
params[k] << v if v || !ActionDispatch::Request::Utils.perform_deep_munge
|
117
|
+
elsif after.start_with?("[]")
|
118
|
+
# Recognize x[][y] (hash inside array) parameters
|
119
|
+
unless after[2] == "[" && after.end_with?("]") && (child_key = after[3, after.length - 4]) && !child_key.empty? && !child_key.index("[") && !child_key.index("]")
|
120
|
+
# Handle other nested array parameters
|
121
|
+
child_key = after[2, after.length]
|
122
|
+
end
|
123
|
+
params[k] ||= []
|
124
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
125
|
+
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
126
|
+
store_nested_param(params[k].last, child_key, v, depth + 1)
|
127
|
+
else
|
128
|
+
params[k] << store_nested_param(make_params, child_key, v, depth + 1)
|
129
|
+
end
|
130
|
+
else
|
131
|
+
params[k] ||= make_params
|
132
|
+
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
133
|
+
params[k] = store_nested_param(params[k], after, v, depth + 1)
|
134
|
+
end
|
135
|
+
|
136
|
+
params
|
137
|
+
end
|
138
|
+
|
139
|
+
def make_params
|
140
|
+
ActiveSupport::HashWithIndifferentAccess.new
|
141
|
+
end
|
142
|
+
|
143
|
+
def new_depth_limit(param_depth_limit)
|
144
|
+
self.class.new @params_class, param_depth_limit
|
145
|
+
end
|
146
|
+
|
147
|
+
def params_hash_type?(obj)
|
148
|
+
Hash === obj
|
149
|
+
end
|
150
|
+
|
151
|
+
def params_hash_has_key?(hash, key)
|
152
|
+
return false if key.include?("[]")
|
153
|
+
|
154
|
+
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
155
|
+
next h if part == ""
|
156
|
+
return false unless params_hash_type?(h) && h.key?(part)
|
157
|
+
h[part]
|
158
|
+
end
|
159
|
+
|
160
|
+
true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
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,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
class QueryParser
|
7
|
+
DEFAULT_SEP = /& */n
|
8
|
+
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n }
|
9
|
+
|
10
|
+
#--
|
11
|
+
# Note this departs from WHATWG's specified parsing algorithm by
|
12
|
+
# giving a nil value for keys that do not use '='. Callers that need
|
13
|
+
# the standard's interpretation can use `v.to_s`.
|
14
|
+
def self.each_pair(s, separator = nil)
|
15
|
+
return enum_for(:each_pair, s, separator) unless block_given?
|
16
|
+
|
17
|
+
(s || "").split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |part|
|
18
|
+
next if part.empty?
|
19
|
+
|
20
|
+
k, v = part.split("=", 2)
|
21
|
+
|
22
|
+
k = URI.decode_www_form_component(k)
|
23
|
+
v &&= URI.decode_www_form_component(v)
|
24
|
+
|
25
|
+
yield k, v
|
26
|
+
end
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
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
|
|