actionpack 7.2.2.1 → 8.1.2
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 +408 -95
- data/README.rdoc +1 -1
- data/lib/abstract_controller/asset_paths.rb +4 -2
- data/lib/abstract_controller/base.rb +12 -17
- data/lib/abstract_controller/caching.rb +6 -3
- data/lib/abstract_controller/callbacks.rb +6 -0
- data/lib/abstract_controller/collector.rb +1 -1
- data/lib/abstract_controller/helpers.rb +1 -1
- data/lib/abstract_controller/logger.rb +2 -1
- data/lib/abstract_controller/rendering.rb +0 -1
- data/lib/action_controller/api.rb +1 -0
- data/lib/action_controller/base.rb +3 -2
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/form_builder.rb +4 -4
- data/lib/action_controller/log_subscriber.rb +22 -3
- data/lib/action_controller/metal/allow_browser.rb +12 -2
- data/lib/action_controller/metal/conditional_get.rb +30 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -5
- data/lib/action_controller/metal/exceptions.rb +5 -0
- data/lib/action_controller/metal/flash.rb +1 -4
- data/lib/action_controller/metal/head.rb +3 -1
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +66 -26
- data/lib/action_controller/metal/params_wrapper.rb +3 -3
- data/lib/action_controller/metal/permissions_policy.rb +9 -0
- data/lib/action_controller/metal/rate_limiting.rb +39 -9
- data/lib/action_controller/metal/redirecting.rb +109 -16
- data/lib/action_controller/metal/renderers.rb +29 -9
- data/lib/action_controller/metal/rendering.rb +8 -2
- data/lib/action_controller/metal/request_forgery_protection.rb +21 -11
- data/lib/action_controller/metal/rescue.rb +9 -0
- data/lib/action_controller/metal/streaming.rb +5 -84
- data/lib/action_controller/metal/strong_parameters.rb +277 -92
- data/lib/action_controller/railtie.rb +33 -15
- data/lib/action_controller/renderer.rb +0 -1
- data/lib/action_controller/structured_event_subscriber.rb +116 -0
- data/lib/action_controller/test_case.rb +12 -2
- data/lib/action_dispatch/constants.rb +6 -0
- data/lib/action_dispatch/http/cache.rb +138 -11
- data/lib/action_dispatch/http/content_security_policy.rb +14 -1
- data/lib/action_dispatch/http/filter_parameters.rb +5 -3
- data/lib/action_dispatch/http/mime_negotiation.rb +63 -4
- data/lib/action_dispatch/http/mime_types.rb +1 -0
- data/lib/action_dispatch/http/param_builder.rb +187 -0
- data/lib/action_dispatch/http/param_error.rb +26 -0
- data/lib/action_dispatch/http/parameters.rb +3 -3
- data/lib/action_dispatch/http/permissions_policy.rb +6 -0
- data/lib/action_dispatch/http/query_parser.rb +55 -0
- data/lib/action_dispatch/http/request.rb +73 -23
- data/lib/action_dispatch/http/response.rb +65 -17
- data/lib/action_dispatch/http/url.rb +112 -16
- data/lib/action_dispatch/journey/formatter.rb +8 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +33 -12
- data/lib/action_dispatch/journey/gtg/transition_table.rb +37 -45
- data/lib/action_dispatch/journey/nodes/node.rb +2 -1
- data/lib/action_dispatch/journey/parser.rb +99 -196
- data/lib/action_dispatch/journey/route.rb +45 -31
- data/lib/action_dispatch/journey/router/utils.rb +8 -14
- data/lib/action_dispatch/journey/router.rb +59 -81
- data/lib/action_dispatch/journey/routes.rb +7 -0
- data/lib/action_dispatch/journey/scanner.rb +44 -42
- data/lib/action_dispatch/journey/visitors.rb +55 -23
- data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
- data/lib/action_dispatch/log_subscriber.rb +7 -3
- data/lib/action_dispatch/middleware/cookies.rb +8 -4
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -5
- data/lib/action_dispatch/middleware/debug_view.rb +11 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +14 -14
- data/lib/action_dispatch/middleware/executor.rb +17 -4
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -6
- data/lib/action_dispatch/middleware/remote_ip.rb +11 -5
- data/lib/action_dispatch/middleware/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +17 -0
- data/lib/action_dispatch/middleware/ssl.rb +13 -3
- data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -5
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
- data/lib/action_dispatch/railtie.rb +21 -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 +80 -57
- data/lib/action_dispatch/routing/mapper.rb +409 -228
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
- data/lib/action_dispatch/routing/redirection.rb +10 -7
- data/lib/action_dispatch/routing/route_set.rb +21 -12
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -0
- data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +2 -2
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +26 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +27 -15
- data/lib/action_dispatch/testing/integration.rb +16 -7
- data/lib/action_dispatch/testing/request_encoder.rb +9 -9
- data/lib/action_dispatch/testing/test_process.rb +1 -2
- data/lib/action_dispatch.rb +14 -4
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +19 -38
- data/lib/action_dispatch/journey/parser.y +0 -50
- data/lib/action_dispatch/journey/parser_extras.rb +0 -33
|
@@ -0,0 +1,187 @@
|
|
|
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 :default
|
|
20
|
+
self.default = make_default(100)
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
delegate :from_query_string, :from_pairs, :from_hash, to: :default
|
|
24
|
+
|
|
25
|
+
def ignore_leading_brackets
|
|
26
|
+
ActionDispatch.deprecator.warn <<~MSG
|
|
27
|
+
ActionDispatch::ParamBuilder.ignore_leading_brackets is deprecated and have no effect and will be removed in Rails 8.2.
|
|
28
|
+
MSG
|
|
29
|
+
|
|
30
|
+
@ignore_leading_brackets
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ignore_leading_brackets=(value)
|
|
34
|
+
ActionDispatch.deprecator.warn <<~MSG
|
|
35
|
+
ActionDispatch::ParamBuilder.ignore_leading_brackets is deprecated and have no effect and will be removed in Rails 8.2.
|
|
36
|
+
MSG
|
|
37
|
+
|
|
38
|
+
@ignore_leading_brackets = value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def from_query_string(qs, separator: nil, encoding_template: nil)
|
|
43
|
+
from_pairs QueryParser.each_pair(qs, separator), encoding_template: encoding_template
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def from_pairs(pairs, encoding_template: nil)
|
|
47
|
+
params = make_params
|
|
48
|
+
|
|
49
|
+
pairs.each do |k, v|
|
|
50
|
+
if Hash === v
|
|
51
|
+
v = ActionDispatch::Http::UploadedFile.new(v)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
store_nested_param(params, k, v, 0, encoding_template)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
params
|
|
58
|
+
rescue ArgumentError => e
|
|
59
|
+
raise InvalidParameterError, e.message, e.backtrace
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def from_hash(hash, encoding_template: nil)
|
|
63
|
+
# Force encodings from encoding template
|
|
64
|
+
hash = Request::Utils::CustomParamEncoder.encode_for_template(hash, encoding_template)
|
|
65
|
+
|
|
66
|
+
# Assert valid encoding
|
|
67
|
+
Request::Utils.check_param_encoding(hash)
|
|
68
|
+
|
|
69
|
+
# Convert hashes to HWIA (or UploadedFile), and deep-munge nils
|
|
70
|
+
# out of arrays
|
|
71
|
+
hash = Request::Utils.normalize_encode_params(hash)
|
|
72
|
+
|
|
73
|
+
hash
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
def store_nested_param(params, name, v, depth, encoding_template = nil)
|
|
78
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
|
79
|
+
|
|
80
|
+
if !name
|
|
81
|
+
# nil name, treat same as empty string (required by tests)
|
|
82
|
+
k = after = ""
|
|
83
|
+
elsif depth == 0
|
|
84
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
|
85
|
+
if start = name.index("[", 1)
|
|
86
|
+
# Start of parameter nesting, use part before brackets as key
|
|
87
|
+
k = name[0, start]
|
|
88
|
+
after = name[start, name.length]
|
|
89
|
+
else
|
|
90
|
+
# Plain parameter with no nesting
|
|
91
|
+
k = name
|
|
92
|
+
after = ""
|
|
93
|
+
end
|
|
94
|
+
elsif name.start_with?("[]")
|
|
95
|
+
# Array nesting
|
|
96
|
+
k = "[]"
|
|
97
|
+
after = name[2, name.length]
|
|
98
|
+
elsif name.start_with?("[") && (start = name.index("]", 1))
|
|
99
|
+
# Hash nesting, use the part inside brackets as the key
|
|
100
|
+
k = name[1, start - 1]
|
|
101
|
+
after = name[start + 1, name.length]
|
|
102
|
+
else
|
|
103
|
+
# Probably malformed input, nested but not starting with [
|
|
104
|
+
# treat full name as key for backwards compatibility.
|
|
105
|
+
k = name
|
|
106
|
+
after = ""
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
return if k.empty?
|
|
110
|
+
|
|
111
|
+
unless k.valid_encoding?
|
|
112
|
+
raise InvalidParameterError, "Invalid encoding for parameter: #{k}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if depth == 0 && String === v
|
|
116
|
+
# We have to wait until we've found the top part of the name,
|
|
117
|
+
# because that's what the encoding template is configured with
|
|
118
|
+
if encoding_template && (designated_encoding = encoding_template[k]) && !v.frozen?
|
|
119
|
+
v.force_encoding(designated_encoding)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# ... and we can't validate the encoding until after we've
|
|
123
|
+
# applied any template override
|
|
124
|
+
unless v.valid_encoding?
|
|
125
|
+
raise InvalidParameterError, "Invalid encoding for parameter: #{v.scrub}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if after == ""
|
|
130
|
+
if k == "[]" && depth != 0
|
|
131
|
+
return (v || !ActionDispatch::Request::Utils.perform_deep_munge) ? [v] : []
|
|
132
|
+
else
|
|
133
|
+
params[k] = v
|
|
134
|
+
end
|
|
135
|
+
elsif after == "["
|
|
136
|
+
params[name] = v
|
|
137
|
+
elsif after == "[]"
|
|
138
|
+
params[k] ||= []
|
|
139
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
|
140
|
+
params[k] << v if v || !ActionDispatch::Request::Utils.perform_deep_munge
|
|
141
|
+
elsif after.start_with?("[]")
|
|
142
|
+
# Recognize x[][y] (hash inside array) parameters
|
|
143
|
+
unless after[2] == "[" && after.end_with?("]") && (child_key = after[3, after.length - 4]) && !child_key.empty? && !child_key.index("[") && !child_key.index("]")
|
|
144
|
+
# Handle other nested array parameters
|
|
145
|
+
child_key = after[2, after.length]
|
|
146
|
+
end
|
|
147
|
+
params[k] ||= []
|
|
148
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
|
149
|
+
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
|
150
|
+
store_nested_param(params[k].last, child_key, v, depth + 1)
|
|
151
|
+
else
|
|
152
|
+
params[k] << store_nested_param(make_params, child_key, v, depth + 1)
|
|
153
|
+
end
|
|
154
|
+
else
|
|
155
|
+
params[k] ||= make_params
|
|
156
|
+
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
|
157
|
+
params[k] = store_nested_param(params[k], after, v, depth + 1)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
params
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def make_params
|
|
164
|
+
ActiveSupport::HashWithIndifferentAccess.new
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def new_depth_limit(param_depth_limit)
|
|
168
|
+
self.class.new @params_class, param_depth_limit
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def params_hash_type?(obj)
|
|
172
|
+
Hash === obj
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def params_hash_has_key?(hash, key)
|
|
176
|
+
return false if key.include?("[]")
|
|
177
|
+
|
|
178
|
+
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
|
179
|
+
next h if part == ""
|
|
180
|
+
return false unless params_hash_type?(h) && h.key?(part)
|
|
181
|
+
h[part]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
true
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
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
|
|
@@ -65,14 +65,14 @@ module ActionDispatch
|
|
|
65
65
|
alias :params :parameters
|
|
66
66
|
|
|
67
67
|
def path_parameters=(parameters) # :nodoc:
|
|
68
|
-
|
|
68
|
+
@env.delete("action_dispatch.request.parameters")
|
|
69
69
|
|
|
70
70
|
parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
|
|
71
71
|
# If any of the path parameters has an invalid encoding then raise since it's
|
|
72
72
|
# likely to trigger errors further on.
|
|
73
73
|
Request::Utils.check_param_encoding(parameters)
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
@env[PARAMETERS_KEY] = parameters
|
|
76
76
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
|
77
77
|
raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}")
|
|
78
78
|
end
|
|
@@ -82,7 +82,7 @@ module ActionDispatch
|
|
|
82
82
|
#
|
|
83
83
|
# { action: "my_action", controller: "my_controller" }
|
|
84
84
|
def path_parameters
|
|
85
|
-
|
|
85
|
+
@env[PARAMETERS_KEY] ||= {}
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
private
|
|
@@ -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",
|
|
@@ -184,4 +186,8 @@ module ActionDispatch # :nodoc:
|
|
|
184
186
|
end
|
|
185
187
|
end
|
|
186
188
|
end
|
|
189
|
+
|
|
190
|
+
ActiveSupport.on_load(:action_dispatch_request) do
|
|
191
|
+
include ActionDispatch::PermissionsPolicy::Request
|
|
192
|
+
end
|
|
187
193
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
require "rack"
|
|
5
|
+
|
|
6
|
+
module ActionDispatch
|
|
7
|
+
class QueryParser
|
|
8
|
+
DEFAULT_SEP = /& */n
|
|
9
|
+
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n, "&;" => /[&;] */n }
|
|
10
|
+
|
|
11
|
+
def self.strict_query_string_separator
|
|
12
|
+
ActionDispatch.deprecator.warn <<~MSG
|
|
13
|
+
The `strict_query_string_separator` configuration is deprecated have no effect and will be removed in Rails 8.2.
|
|
14
|
+
MSG
|
|
15
|
+
@strict_query_string_separator
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.strict_query_string_separator=(value)
|
|
19
|
+
ActionDispatch.deprecator.warn <<~MSG
|
|
20
|
+
The `strict_query_string_separator` configuration is deprecated have no effect and will be removed in Rails 8.2.
|
|
21
|
+
MSG
|
|
22
|
+
@strict_query_string_separator = value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#--
|
|
26
|
+
# Note this departs from WHATWG's specified parsing algorithm by
|
|
27
|
+
# giving a nil value for keys that do not use '='. Callers that need
|
|
28
|
+
# the standard's interpretation can use `v.to_s`.
|
|
29
|
+
def self.each_pair(s, separator = nil)
|
|
30
|
+
return enum_for(:each_pair, s, separator) unless block_given?
|
|
31
|
+
|
|
32
|
+
s ||= ""
|
|
33
|
+
|
|
34
|
+
splitter =
|
|
35
|
+
if separator
|
|
36
|
+
COMMON_SEP[separator] || /[#{separator}] */n
|
|
37
|
+
else
|
|
38
|
+
DEFAULT_SEP
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
s.split(splitter).each do |part|
|
|
42
|
+
next if part.empty?
|
|
43
|
+
|
|
44
|
+
k, v = part.split("=", 2)
|
|
45
|
+
|
|
46
|
+
k = URI.decode_www_form_component(k)
|
|
47
|
+
v &&= URI.decode_www_form_component(v)
|
|
48
|
+
|
|
49
|
+
yield k, v
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -25,7 +25,6 @@ module ActionDispatch
|
|
|
25
25
|
include ActionDispatch::Http::FilterParameters
|
|
26
26
|
include ActionDispatch::Http::URL
|
|
27
27
|
include ActionDispatch::ContentSecurityPolicy::Request
|
|
28
|
-
include ActionDispatch::PermissionsPolicy::Request
|
|
29
28
|
include Rack::Request::Env
|
|
30
29
|
|
|
31
30
|
autoload :Session, "action_dispatch/request/session"
|
|
@@ -55,12 +54,17 @@ module ActionDispatch
|
|
|
55
54
|
METHOD
|
|
56
55
|
end
|
|
57
56
|
|
|
57
|
+
TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING" # :nodoc:
|
|
58
|
+
|
|
58
59
|
def self.empty
|
|
59
60
|
new({})
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
def initialize(env)
|
|
63
64
|
super
|
|
65
|
+
|
|
66
|
+
@rack_request = Rack::Request.new(env)
|
|
67
|
+
|
|
64
68
|
@method = nil
|
|
65
69
|
@request_method = nil
|
|
66
70
|
@remote_ip = nil
|
|
@@ -69,6 +73,8 @@ module ActionDispatch
|
|
|
69
73
|
@ip = nil
|
|
70
74
|
end
|
|
71
75
|
|
|
76
|
+
attr_reader :rack_request
|
|
77
|
+
|
|
72
78
|
def commit_cookie_jar! # :nodoc:
|
|
73
79
|
end
|
|
74
80
|
|
|
@@ -132,7 +138,7 @@ module ActionDispatch
|
|
|
132
138
|
|
|
133
139
|
# Populate the HTTP method lookup cache.
|
|
134
140
|
HTTP_METHODS.each { |method|
|
|
135
|
-
HTTP_METHOD_LOOKUP[method] = method.
|
|
141
|
+
HTTP_METHOD_LOOKUP[method] = method.downcase.tap { |m| m.tr!("-", "_") }.to_sym
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
alias raw_request_method request_method # :nodoc:
|
|
@@ -151,11 +157,17 @@ module ActionDispatch
|
|
|
151
157
|
#
|
|
152
158
|
# request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
|
|
153
159
|
def route_uri_pattern
|
|
154
|
-
get_header("action_dispatch.route_uri_pattern")
|
|
160
|
+
unless pattern = get_header("action_dispatch.route_uri_pattern")
|
|
161
|
+
route = get_header("action_dispatch.route")
|
|
162
|
+
return if route.nil?
|
|
163
|
+
pattern = route.path.spec.to_s
|
|
164
|
+
set_header("action_dispatch.route_uri_pattern", pattern)
|
|
165
|
+
end
|
|
166
|
+
pattern
|
|
155
167
|
end
|
|
156
168
|
|
|
157
|
-
def
|
|
158
|
-
|
|
169
|
+
def route=(route) # :nodoc:
|
|
170
|
+
@env["action_dispatch.route"] = route
|
|
159
171
|
end
|
|
160
172
|
|
|
161
173
|
def routes # :nodoc:
|
|
@@ -236,8 +248,9 @@ module ActionDispatch
|
|
|
236
248
|
#
|
|
237
249
|
# send_early_hints("link" => "</style.css>; rel=preload; as=style,</script.js>; rel=preload")
|
|
238
250
|
#
|
|
239
|
-
# If you are using
|
|
240
|
-
#
|
|
251
|
+
# If you are using {javascript_include_tag}[rdoc-ref:ActionView::Helpers::AssetTagHelper#javascript_include_tag]
|
|
252
|
+
# or {stylesheet_link_tag}[rdoc-ref:ActionView::Helpers::AssetTagHelper#stylesheet_link_tag]
|
|
253
|
+
# the Early Hints headers are included by default if supported.
|
|
241
254
|
def send_early_hints(links)
|
|
242
255
|
env["rack.early_hints"]&.call(links)
|
|
243
256
|
end
|
|
@@ -282,7 +295,7 @@ module ActionDispatch
|
|
|
282
295
|
|
|
283
296
|
# Returns the content length of the request as an integer.
|
|
284
297
|
def content_length
|
|
285
|
-
return raw_post.bytesize if
|
|
298
|
+
return raw_post.bytesize if has_header?(TRANSFER_ENCODING)
|
|
286
299
|
super.to_i
|
|
287
300
|
end
|
|
288
301
|
|
|
@@ -386,15 +399,12 @@ module ActionDispatch
|
|
|
386
399
|
# Override Rack's GET method to support indifferent access.
|
|
387
400
|
def GET
|
|
388
401
|
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)
|
|
402
|
+
encoding_template = Request::Utils::CustomParamEncoder.action_encoding_template(self, path_parameters[:controller], path_parameters[:action])
|
|
403
|
+
rack_query_params = ActionDispatch::ParamBuilder.from_query_string(rack_request.query_string, encoding_template: encoding_template)
|
|
404
|
+
|
|
405
|
+
set_header k, rack_query_params
|
|
396
406
|
end
|
|
397
|
-
rescue
|
|
407
|
+
rescue ActionDispatch::ParamError => e
|
|
398
408
|
raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
|
|
399
409
|
end
|
|
400
410
|
alias :query_parameters :GET
|
|
@@ -402,18 +412,54 @@ module ActionDispatch
|
|
|
402
412
|
# Override Rack's POST method to support indifferent access.
|
|
403
413
|
def POST
|
|
404
414
|
fetch_header("action_dispatch.request.request_parameters") do
|
|
405
|
-
|
|
406
|
-
|
|
415
|
+
encoding_template = Request::Utils::CustomParamEncoder.action_encoding_template(self, path_parameters[:controller], path_parameters[:action])
|
|
416
|
+
|
|
417
|
+
param_list = nil
|
|
418
|
+
pr = parse_formatted_parameters(params_parsers) do
|
|
419
|
+
if param_list = request_parameters_list
|
|
420
|
+
ActionDispatch::ParamBuilder.from_pairs(param_list, encoding_template: encoding_template)
|
|
421
|
+
else
|
|
422
|
+
# We're not using a version of Rack that provides raw form
|
|
423
|
+
# pairs; we must use its hash (and thus post-process it below).
|
|
424
|
+
fallback_request_parameters
|
|
425
|
+
end
|
|
407
426
|
end
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
427
|
+
|
|
428
|
+
# If the request body was parsed by a custom parser like JSON
|
|
429
|
+
# (and thus the above block was not run), we need to
|
|
430
|
+
# post-process the result hash.
|
|
431
|
+
if param_list.nil?
|
|
432
|
+
pr = ActionDispatch::ParamBuilder.from_hash(pr, encoding_template: encoding_template)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
self.request_parameters = pr
|
|
411
436
|
end
|
|
412
|
-
rescue
|
|
437
|
+
rescue ActionDispatch::ParamError, EOFError => e
|
|
413
438
|
raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
|
|
414
439
|
end
|
|
415
440
|
alias :request_parameters :POST
|
|
416
441
|
|
|
442
|
+
def request_parameters_list
|
|
443
|
+
# We don't use Rack's parse result, but we must call it so Rack
|
|
444
|
+
# can populate the rack.request.* keys we need.
|
|
445
|
+
rack_post = rack_request.POST
|
|
446
|
+
|
|
447
|
+
if form_pairs = get_header("rack.request.form_pairs")
|
|
448
|
+
# Multipart
|
|
449
|
+
form_pairs
|
|
450
|
+
elsif form_vars = get_header("rack.request.form_vars")
|
|
451
|
+
# URL-encoded
|
|
452
|
+
ActionDispatch::QueryParser.each_pair(form_vars)
|
|
453
|
+
elsif rack_post && !rack_post.empty?
|
|
454
|
+
# It was multipart, but Rack did not preserve a pair list
|
|
455
|
+
# (probably too old). Flat parameter list is not available.
|
|
456
|
+
nil
|
|
457
|
+
else
|
|
458
|
+
# No request body, or not a format Rack knows
|
|
459
|
+
[]
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
417
463
|
# Returns the authorization header regardless of whether it was specified
|
|
418
464
|
# directly or through one of the proxy alternatives.
|
|
419
465
|
def authorization
|
|
@@ -468,7 +514,7 @@ module ActionDispatch
|
|
|
468
514
|
def read_body_stream
|
|
469
515
|
if body_stream
|
|
470
516
|
reset_stream(body_stream) do
|
|
471
|
-
if
|
|
517
|
+
if has_header?(TRANSFER_ENCODING)
|
|
472
518
|
body_stream.read # Read body stream until EOF if "Transfer-Encoding" is present
|
|
473
519
|
else
|
|
474
520
|
body_stream.read(content_length)
|
|
@@ -490,6 +536,10 @@ module ActionDispatch
|
|
|
490
536
|
yield
|
|
491
537
|
end
|
|
492
538
|
end
|
|
539
|
+
|
|
540
|
+
def fallback_request_parameters
|
|
541
|
+
rack_request.POST
|
|
542
|
+
end
|
|
493
543
|
end
|
|
494
544
|
end
|
|
495
545
|
|
|
@@ -46,6 +46,20 @@ module ActionDispatch # :nodoc:
|
|
|
46
46
|
Headers = ::Rack::Utils::HeaderHash
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
class << self
|
|
50
|
+
if ActionDispatch::Constants::UNPROCESSABLE_CONTENT == :unprocessable_content
|
|
51
|
+
def rack_status_code(status) # :nodoc:
|
|
52
|
+
status = :unprocessable_content if status == :unprocessable_entity
|
|
53
|
+
Rack::Utils.status_code(status)
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
def rack_status_code(status) # :nodoc:
|
|
57
|
+
status = :unprocessable_entity if status == :unprocessable_content
|
|
58
|
+
Rack::Utils.status_code(status)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
49
63
|
# To be deprecated:
|
|
50
64
|
Header = Headers
|
|
51
65
|
|
|
@@ -105,10 +119,22 @@ module ActionDispatch # :nodoc:
|
|
|
105
119
|
@str_body = nil
|
|
106
120
|
end
|
|
107
121
|
|
|
122
|
+
BODY_METHODS = { to_ary: true }
|
|
123
|
+
|
|
124
|
+
def respond_to?(method, include_private = false)
|
|
125
|
+
if BODY_METHODS.key?(method)
|
|
126
|
+
@buf.respond_to?(method)
|
|
127
|
+
else
|
|
128
|
+
super
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
108
132
|
def to_ary
|
|
109
|
-
@
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
if @str_body
|
|
134
|
+
[body]
|
|
135
|
+
else
|
|
136
|
+
@buf = @buf.to_ary
|
|
137
|
+
end
|
|
112
138
|
end
|
|
113
139
|
|
|
114
140
|
def body
|
|
@@ -245,20 +271,33 @@ module ActionDispatch # :nodoc:
|
|
|
245
271
|
|
|
246
272
|
# Sets the HTTP status code.
|
|
247
273
|
def status=(status)
|
|
248
|
-
@status =
|
|
274
|
+
@status = Response.rack_status_code(status)
|
|
249
275
|
end
|
|
250
276
|
|
|
251
277
|
# Sets the HTTP response's content MIME type. For example, in the controller you
|
|
252
278
|
# could write this:
|
|
253
279
|
#
|
|
254
|
-
# response.content_type = "text/
|
|
280
|
+
# response.content_type = "text/html"
|
|
281
|
+
#
|
|
282
|
+
# This method also accepts a symbol with the extension of the MIME type:
|
|
283
|
+
#
|
|
284
|
+
# response.content_type = :html
|
|
255
285
|
#
|
|
256
286
|
# If a character set has been defined for this response (see #charset=) then the
|
|
257
287
|
# character set information will also be included in the content type
|
|
258
288
|
# information.
|
|
259
289
|
def content_type=(content_type)
|
|
260
|
-
|
|
261
|
-
|
|
290
|
+
case content_type
|
|
291
|
+
when NilClass
|
|
292
|
+
return
|
|
293
|
+
when Symbol
|
|
294
|
+
mime_type = Mime[content_type]
|
|
295
|
+
raise ArgumentError, "Unknown MIME type #{content_type}" unless mime_type
|
|
296
|
+
new_header_info = ContentTypeHeader.new(mime_type.to_s)
|
|
297
|
+
else
|
|
298
|
+
new_header_info = parse_content_type(content_type.to_s)
|
|
299
|
+
end
|
|
300
|
+
|
|
262
301
|
prev_header_info = parsed_content_type_header
|
|
263
302
|
charset = new_header_info.charset || prev_header_info.charset
|
|
264
303
|
charset ||= self.class.default_charset unless prev_header_info.mime_type
|
|
@@ -328,7 +367,13 @@ module ActionDispatch # :nodoc:
|
|
|
328
367
|
# Returns the content of the response as a string. This contains the contents of
|
|
329
368
|
# any calls to `render`.
|
|
330
369
|
def body
|
|
331
|
-
@stream.
|
|
370
|
+
if @stream.respond_to?(:to_ary)
|
|
371
|
+
@stream.to_ary.join
|
|
372
|
+
elsif @stream.respond_to?(:body)
|
|
373
|
+
@stream.body
|
|
374
|
+
else
|
|
375
|
+
@stream
|
|
376
|
+
end
|
|
332
377
|
end
|
|
333
378
|
|
|
334
379
|
def write(string)
|
|
@@ -337,11 +382,16 @@ module ActionDispatch # :nodoc:
|
|
|
337
382
|
|
|
338
383
|
# Allows you to manually set or override the response body.
|
|
339
384
|
def body=(body)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
385
|
+
# Prevent ActionController::Metal::Live::Response from committing the response prematurely.
|
|
386
|
+
synchronize do
|
|
387
|
+
if body.respond_to?(:to_str)
|
|
388
|
+
@stream = build_buffer(self, [body])
|
|
389
|
+
elsif body.respond_to?(:to_path)
|
|
390
|
+
@stream = body
|
|
391
|
+
elsif body.respond_to?(:to_ary)
|
|
392
|
+
@stream = build_buffer(self, body)
|
|
393
|
+
else
|
|
394
|
+
@stream = body
|
|
345
395
|
end
|
|
346
396
|
end
|
|
347
397
|
end
|
|
@@ -482,10 +532,6 @@ module ActionDispatch # :nodoc:
|
|
|
482
532
|
Buffer.new response, body
|
|
483
533
|
end
|
|
484
534
|
|
|
485
|
-
def munge_body_object(body)
|
|
486
|
-
body.respond_to?(:each) ? body : [body]
|
|
487
|
-
end
|
|
488
|
-
|
|
489
535
|
def assign_default_content_type_and_charset!
|
|
490
536
|
return if media_type
|
|
491
537
|
|
|
@@ -499,6 +545,8 @@ module ActionDispatch # :nodoc:
|
|
|
499
545
|
@response = response
|
|
500
546
|
end
|
|
501
547
|
|
|
548
|
+
attr :response
|
|
549
|
+
|
|
502
550
|
def close
|
|
503
551
|
# Rack "close" maps to Response#abort, and **not** Response#close (which is used
|
|
504
552
|
# when the controller's finished writing)
|