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