actionpack 7.0.4 → 7.1.5.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 +495 -257
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/abstract_controller/base.rb +20 -11
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +75 -28
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +11 -6
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +6 -4
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +121 -123
- data/lib/action_controller/metal/content_security_policy.rb +5 -5
- data/lib/action_controller/metal/data_streaming.rb +20 -18
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +8 -0
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +15 -9
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +25 -1
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +2 -2
- data/lib/action_controller/metal/redirecting.rb +29 -8
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +114 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
- data/lib/action_controller/metal/rescue.rb +6 -3
- data/lib/action_controller/metal/streaming.rb +71 -31
- data/lib/action_controller/metal/strong_parameters.rb +200 -103
- data/lib/action_controller/metal/url_for.rb +9 -4
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +24 -10
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +18 -8
- data/lib/action_controller.rb +13 -3
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +9 -11
- data/lib/action_dispatch/http/content_security_policy.rb +35 -13
- data/lib/action_dispatch/http/filter_parameters.rb +23 -32
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- data/lib/action_dispatch/http/mime_type.rb +37 -11
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +38 -23
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +85 -32
- data/lib/action_dispatch/http/response.rb +80 -63
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +9 -8
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +108 -117
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
- data/lib/action_dispatch/middleware/executor.rb +7 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +21 -20
- data/lib/action_dispatch/middleware/request_id.rb +4 -2
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +39 -22
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +14 -10
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -41
- data/lib/action_dispatch/railtie.rb +13 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +97 -26
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +53 -23
- data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
- data/lib/action_dispatch/routing/url_for.rb +26 -22
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +25 -19
- data/lib/action_dispatch/system_testing/driver.rb +15 -23
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +41 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +68 -32
@@ -3,6 +3,8 @@
|
|
3
3
|
require "active_support/core_ext/object/deep_dup"
|
4
4
|
|
5
5
|
module ActionDispatch # :nodoc:
|
6
|
+
# = Action Dispatch \PermissionsPolicy
|
7
|
+
#
|
6
8
|
# Configures the HTTP
|
7
9
|
# {Feature-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy]
|
8
10
|
# response header to specify which browser features the current document and
|
@@ -19,47 +21,38 @@ module ActionDispatch # :nodoc:
|
|
19
21
|
# policy.payment :self, "https://secure.example.com"
|
20
22
|
# end
|
21
23
|
#
|
24
|
+
# The Feature-Policy header has been renamed to Permissions-Policy.
|
25
|
+
# The Permissions-Policy requires a different implementation and isn't
|
26
|
+
# yet supported by all browsers. To avoid having to rename this
|
27
|
+
# middleware in the future we use the new name for the middleware but
|
28
|
+
# keep the old header name and implementation for now.
|
22
29
|
class PermissionsPolicy
|
23
30
|
class Middleware
|
24
|
-
CONTENT_TYPE = "Content-Type"
|
25
|
-
# The Feature-Policy header has been renamed to Permissions-Policy.
|
26
|
-
# The Permissions-Policy requires a different implementation and isn't
|
27
|
-
# yet supported by all browsers. To avoid having to rename this
|
28
|
-
# middleware in the future we use the new name for the middleware but
|
29
|
-
# keep the old header name and implementation for now.
|
30
|
-
POLICY = "Feature-Policy"
|
31
|
-
|
32
31
|
def initialize(app)
|
33
32
|
@app = app
|
34
33
|
end
|
35
34
|
|
36
35
|
def call(env)
|
37
|
-
request = ActionDispatch::Request.new(env)
|
38
36
|
_, headers, _ = response = @app.call(env)
|
39
37
|
|
40
|
-
return response unless html_response?(headers)
|
41
38
|
return response if policy_present?(headers)
|
42
39
|
|
40
|
+
request = ActionDispatch::Request.new(env)
|
41
|
+
|
43
42
|
if policy = request.permissions_policy
|
44
|
-
headers[
|
43
|
+
headers[ActionDispatch::Constants::FEATURE_POLICY] = policy.build(request.controller_instance)
|
45
44
|
end
|
46
45
|
|
47
46
|
if policy_empty?(policy)
|
48
|
-
headers.delete(
|
47
|
+
headers.delete(ActionDispatch::Constants::FEATURE_POLICY)
|
49
48
|
end
|
50
49
|
|
51
50
|
response
|
52
51
|
end
|
53
52
|
|
54
53
|
private
|
55
|
-
def html_response?(headers)
|
56
|
-
if content_type = headers[CONTENT_TYPE]
|
57
|
-
/html/.match?(content_type)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
54
|
def policy_present?(headers)
|
62
|
-
headers[
|
55
|
+
headers[ActionDispatch::Constants::FEATURE_POLICY]
|
63
56
|
end
|
64
57
|
|
65
58
|
def policy_empty?(policy)
|
@@ -85,7 +78,7 @@ module ActionDispatch # :nodoc:
|
|
85
78
|
}.freeze
|
86
79
|
|
87
80
|
# List of available permissions can be found at
|
88
|
-
# https://github.com/w3c/webappsec-permissions-policy/blob/
|
81
|
+
# https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md#policy-controlled-features
|
89
82
|
DIRECTIVES = {
|
90
83
|
accelerometer: "accelerometer",
|
91
84
|
ambient_light_sensor: "ambient-light-sensor",
|
@@ -95,15 +88,18 @@ module ActionDispatch # :nodoc:
|
|
95
88
|
fullscreen: "fullscreen",
|
96
89
|
geolocation: "geolocation",
|
97
90
|
gyroscope: "gyroscope",
|
91
|
+
hid: "hid",
|
92
|
+
idle_detection: "idle-detection",
|
98
93
|
magnetometer: "magnetometer",
|
99
94
|
microphone: "microphone",
|
100
95
|
midi: "midi",
|
101
96
|
payment: "payment",
|
102
97
|
picture_in_picture: "picture-in-picture",
|
103
|
-
|
98
|
+
screen_wake_lock: "screen-wake-lock",
|
99
|
+
serial: "serial",
|
100
|
+
sync_xhr: "sync-xhr",
|
104
101
|
usb: "usb",
|
105
|
-
|
106
|
-
vr: "vr",
|
102
|
+
web_share: "web-share",
|
107
103
|
}.freeze
|
108
104
|
|
109
105
|
private_constant :MAPPINGS, :DIRECTIVES
|
@@ -129,6 +125,25 @@ module ActionDispatch # :nodoc:
|
|
129
125
|
end
|
130
126
|
end
|
131
127
|
|
128
|
+
%w[speaker vibrate vr].each do |directive|
|
129
|
+
define_method(directive) do |*sources|
|
130
|
+
ActionDispatch.deprecator.warn(<<~MSG)
|
131
|
+
The `#{directive}` permissions policy directive is deprecated
|
132
|
+
and will be removed in Rails 7.2.
|
133
|
+
|
134
|
+
There is no browser support for this directive, and no plan
|
135
|
+
for browser support in the future. You can just remove this
|
136
|
+
directive from your application.
|
137
|
+
MSG
|
138
|
+
|
139
|
+
if sources.first
|
140
|
+
@directives[directive] = apply_mappings(sources)
|
141
|
+
else
|
142
|
+
@directives.delete(directive)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
132
147
|
def build(context = nil)
|
133
148
|
build_directives(context).compact.join("; ")
|
134
149
|
end
|
@@ -72,7 +72,7 @@ module ActionDispatch
|
|
72
72
|
|
73
73
|
PASS_NOT_FOUND = Class.new { # :nodoc:
|
74
74
|
def self.action(_); self; end
|
75
|
-
def self.call(_); [404, {
|
75
|
+
def self.call(_); [404, { Constants::X_CASCADE => "pass" }, []]; end
|
76
76
|
def self.action_encoding_template(action); false; end
|
77
77
|
}
|
78
78
|
|
@@ -107,22 +107,21 @@ module ActionDispatch
|
|
107
107
|
has_header? key
|
108
108
|
end
|
109
109
|
|
110
|
-
#
|
111
|
-
# Hypertext Transfer Protocol -- HTTP/1.1 (https://www.ietf.org/rfc/rfc2616.txt)
|
112
|
-
# HTTP Extensions for Distributed Authoring -- WEBDAV (https://www.ietf.org/rfc/rfc2518.txt)
|
113
|
-
# Versioning Extensions to WebDAV (https://www.ietf.org/rfc/rfc3253.txt)
|
114
|
-
# Ordered Collections Protocol (WebDAV) (https://www.ietf.org/rfc/rfc3648.txt)
|
115
|
-
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (https://www.ietf.org/rfc/rfc3744.txt)
|
116
|
-
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (https://www.ietf.org/rfc/rfc5323.txt)
|
117
|
-
# Calendar Extensions to WebDAV (https://www.ietf.org/rfc/rfc4791.txt)
|
118
|
-
# PATCH Method for HTTP (https://www.ietf.org/rfc/rfc5789.txt)
|
110
|
+
# HTTP methods from {RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1}[https://www.ietf.org/rfc/rfc2616.txt]
|
119
111
|
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
|
112
|
+
# HTTP methods from {RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV}[https://www.ietf.org/rfc/rfc2518.txt]
|
120
113
|
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
|
114
|
+
# HTTP methods from {RFC 3253: Versioning Extensions to WebDAV}[https://www.ietf.org/rfc/rfc3253.txt]
|
121
115
|
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
|
116
|
+
# HTTP methods from {RFC 3648: WebDAV Ordered Collections Protocol}[https://www.ietf.org/rfc/rfc3648.txt]
|
122
117
|
RFC3648 = %w(ORDERPATCH)
|
118
|
+
# HTTP methods from {RFC 3744: WebDAV Access Control Protocol}[https://www.ietf.org/rfc/rfc3744.txt]
|
123
119
|
RFC3744 = %w(ACL)
|
120
|
+
# HTTP methods from {RFC 5323: WebDAV SEARCH}[https://www.ietf.org/rfc/rfc5323.txt]
|
124
121
|
RFC5323 = %w(SEARCH)
|
122
|
+
# HTTP methods from {RFC 4791: Calendaring Extensions to WebDAV}[https://www.ietf.org/rfc/rfc4791.txt]
|
125
123
|
RFC4791 = %w(MKCALENDAR)
|
124
|
+
# HTTP methods from {RFC 5789: PATCH Method for HTTP}[https://www.ietf.org/rfc/rfc5789.txt]
|
126
125
|
RFC5789 = %w(PATCH)
|
127
126
|
|
128
127
|
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
|
@@ -146,6 +145,18 @@ module ActionDispatch
|
|
146
145
|
@request_method ||= check_method(super)
|
147
146
|
end
|
148
147
|
|
148
|
+
# Returns the URI pattern of the matched route for the request,
|
149
|
+
# using the same format as `bin/rails routes`:
|
150
|
+
#
|
151
|
+
# request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
|
152
|
+
def route_uri_pattern
|
153
|
+
get_header("action_dispatch.route_uri_pattern")
|
154
|
+
end
|
155
|
+
|
156
|
+
def route_uri_pattern=(pattern) # :nodoc:
|
157
|
+
set_header("action_dispatch.route_uri_pattern", pattern)
|
158
|
+
end
|
159
|
+
|
149
160
|
def routes # :nodoc:
|
150
161
|
get_header("action_dispatch.routes")
|
151
162
|
end
|
@@ -180,13 +191,6 @@ module ActionDispatch
|
|
180
191
|
get_header "action_dispatch.http_auth_salt"
|
181
192
|
end
|
182
193
|
|
183
|
-
def show_exceptions? # :nodoc:
|
184
|
-
# We're treating `nil` as "unset", and we want the default setting to be
|
185
|
-
# `true`. This logic should be extracted to `env_config` and calculated
|
186
|
-
# once.
|
187
|
-
!(get_header("action_dispatch.show_exceptions") == false)
|
188
|
-
end
|
189
|
-
|
190
194
|
# Returns a symbol form of the #request_method.
|
191
195
|
def request_method_symbol
|
192
196
|
HTTP_METHOD_LOOKUP[request_method]
|
@@ -195,9 +199,20 @@ module ActionDispatch
|
|
195
199
|
# Returns the original value of the environment's REQUEST_METHOD,
|
196
200
|
# even if it was overridden by middleware. See #request_method for
|
197
201
|
# more information.
|
198
|
-
|
199
|
-
|
202
|
+
#
|
203
|
+
# For debugging purposes, when called with arguments this method will
|
204
|
+
# fall back to Object#method
|
205
|
+
def method(*args)
|
206
|
+
if args.empty?
|
207
|
+
@method ||= check_method(
|
208
|
+
get_header("rack.methodoverride.original_method") ||
|
209
|
+
get_header("REQUEST_METHOD")
|
210
|
+
)
|
211
|
+
else
|
212
|
+
super
|
213
|
+
end
|
200
214
|
end
|
215
|
+
ruby2_keywords(:method)
|
201
216
|
|
202
217
|
# Returns a symbol form of the #method.
|
203
218
|
def method_symbol
|
@@ -214,11 +229,12 @@ module ActionDispatch
|
|
214
229
|
# Early Hints is an HTTP/2 status code that indicates hints to help a client start
|
215
230
|
# making preparations for processing the final response.
|
216
231
|
#
|
217
|
-
# If the env contains +rack.early_hints+ then the server accepts HTTP2 push for
|
232
|
+
# If the env contains +rack.early_hints+ then the server accepts HTTP2 push for
|
233
|
+
# link headers.
|
218
234
|
#
|
219
235
|
# The +send_early_hints+ method accepts a hash of links as follows:
|
220
236
|
#
|
221
|
-
# send_early_hints("
|
237
|
+
# send_early_hints("link" => "</style.css>; rel=preload; as=style,</script.js>; rel=preload")
|
222
238
|
#
|
223
239
|
# If you are using +javascript_include_tag+ or +stylesheet_link_tag+ the
|
224
240
|
# Early Hints headers are included by default if supported.
|
@@ -268,10 +284,11 @@ module ActionDispatch
|
|
268
284
|
|
269
285
|
# Returns the content length of the request as an integer.
|
270
286
|
def content_length
|
287
|
+
return raw_post.bytesize if headers.key?("Transfer-Encoding")
|
271
288
|
super.to_i
|
272
289
|
end
|
273
290
|
|
274
|
-
# Returns true if the
|
291
|
+
# Returns true if the +X-Requested-With+ header contains "XMLHttpRequest"
|
275
292
|
# (case-insensitive), which may need to be manually added depending on the
|
276
293
|
# choice of JavaScript libraries and frameworks.
|
277
294
|
def xml_http_request?
|
@@ -297,7 +314,7 @@ module ActionDispatch
|
|
297
314
|
|
298
315
|
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
|
299
316
|
|
300
|
-
# Returns the unique request id, which is based on either the X-Request-Id header that can
|
317
|
+
# Returns the unique request id, which is based on either the +X-Request-Id+ header that can
|
301
318
|
# be generated by a firewall, load balancer, or web server, or by the RequestId middleware
|
302
319
|
# (which sets the +action_dispatch.request_id+ environment variable).
|
303
320
|
#
|
@@ -322,9 +339,7 @@ module ActionDispatch
|
|
322
339
|
# work with raw requests directly.
|
323
340
|
def raw_post
|
324
341
|
unless has_header? "RAW_POST_DATA"
|
325
|
-
|
326
|
-
set_header("RAW_POST_DATA", raw_post_body.read(content_length))
|
327
|
-
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
|
342
|
+
set_header("RAW_POST_DATA", read_body_stream)
|
328
343
|
end
|
329
344
|
get_header "RAW_POST_DATA"
|
330
345
|
end
|
@@ -341,13 +356,13 @@ module ActionDispatch
|
|
341
356
|
end
|
342
357
|
|
343
358
|
# Determine whether the request body contains form-data by checking
|
344
|
-
# the request Content-Type for one of the media-types:
|
345
|
-
#
|
359
|
+
# the request +Content-Type+ for one of the media-types:
|
360
|
+
# +application/x-www-form-urlencoded+ or +multipart/form-data+. The
|
346
361
|
# list of form-data media types can be modified through the
|
347
362
|
# +FORM_DATA_MEDIA_TYPES+ array.
|
348
363
|
#
|
349
364
|
# A request body is not assumed to contain form-data when no
|
350
|
-
# Content-Type header is provided and the request_method is POST.
|
365
|
+
# +Content-Type+ header is provided and the request_method is POST.
|
351
366
|
def form_data?
|
352
367
|
FORM_DATA_MEDIA_TYPES.include?(media_type)
|
353
368
|
end
|
@@ -358,6 +373,7 @@ module ActionDispatch
|
|
358
373
|
|
359
374
|
def reset_session
|
360
375
|
session.destroy
|
376
|
+
reset_csrf_token
|
361
377
|
end
|
362
378
|
|
363
379
|
def session=(session) # :nodoc:
|
@@ -379,7 +395,7 @@ module ActionDispatch
|
|
379
395
|
Request::Utils.check_param_encoding(rack_query_params)
|
380
396
|
set_header k, Request::Utils.normalize_encode_params(rack_query_params)
|
381
397
|
end
|
382
|
-
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
398
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError => e
|
383
399
|
raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
|
384
400
|
end
|
385
401
|
alias :query_parameters :GET
|
@@ -394,7 +410,7 @@ module ActionDispatch
|
|
394
410
|
Request::Utils.check_param_encoding(pr)
|
395
411
|
self.request_parameters = Request::Utils.normalize_encode_params(pr)
|
396
412
|
end
|
397
|
-
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
413
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError, EOFError => e
|
398
414
|
raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
|
399
415
|
end
|
400
416
|
alias :request_parameters :POST
|
@@ -429,15 +445,52 @@ module ActionDispatch
|
|
429
445
|
"#<#{self.class.name} #{method} #{original_url.dump} for #{remote_ip}>"
|
430
446
|
end
|
431
447
|
|
448
|
+
def reset_csrf_token
|
449
|
+
controller_instance.reset_csrf_token(self) if controller_instance.respond_to?(:reset_csrf_token)
|
450
|
+
end
|
451
|
+
|
452
|
+
def commit_csrf_token
|
453
|
+
controller_instance.commit_csrf_token(self) if controller_instance.respond_to?(:commit_csrf_token)
|
454
|
+
end
|
455
|
+
|
432
456
|
private
|
433
457
|
def check_method(name)
|
434
|
-
|
458
|
+
if name
|
459
|
+
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
|
460
|
+
end
|
461
|
+
|
435
462
|
name
|
436
463
|
end
|
437
464
|
|
438
465
|
def default_session
|
439
466
|
Session.disabled(self)
|
440
467
|
end
|
468
|
+
|
469
|
+
def read_body_stream
|
470
|
+
if body_stream
|
471
|
+
reset_stream(body_stream) do
|
472
|
+
if headers.key?("Transfer-Encoding")
|
473
|
+
body_stream.read # Read body stream until EOF if "Transfer-Encoding" is present
|
474
|
+
else
|
475
|
+
body_stream.read(content_length)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
def reset_stream(body_stream)
|
482
|
+
if body_stream.respond_to?(:rewind)
|
483
|
+
body_stream.rewind
|
484
|
+
|
485
|
+
content = yield
|
486
|
+
|
487
|
+
body_stream.rewind
|
488
|
+
|
489
|
+
content
|
490
|
+
else
|
491
|
+
yield
|
492
|
+
end
|
493
|
+
end
|
441
494
|
end
|
442
495
|
end
|
443
496
|
|
@@ -6,23 +6,23 @@ require "action_dispatch/http/cache"
|
|
6
6
|
require "monitor"
|
7
7
|
|
8
8
|
module ActionDispatch # :nodoc:
|
9
|
+
# = Action Dispatch \Response
|
10
|
+
#
|
9
11
|
# Represents an HTTP response generated by a controller action. Use it to
|
10
12
|
# retrieve the current state of the response, or customize the response. It can
|
11
13
|
# either represent a real HTTP response (i.e. one that is meant to be sent
|
12
14
|
# back to the web browser) or a TestResponse (i.e. one that is generated
|
13
15
|
# from integration tests).
|
14
16
|
#
|
15
|
-
# \Response
|
16
|
-
#
|
17
|
-
# methods
|
18
|
-
#
|
19
|
-
# ActionControllerBase#headers instead of Response#headers.
|
17
|
+
# The \Response object for the current request is exposed on controllers as
|
18
|
+
# ActionController::Metal#response. ActionController::Metal also provides a
|
19
|
+
# few additional methods that delegate to attributes of the \Response such as
|
20
|
+
# ActionController::Metal#headers.
|
20
21
|
#
|
21
|
-
#
|
22
|
-
# more detail
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# objects of type TestResponse (which are of course also of type \Response).
|
22
|
+
# Integration tests will likely also want to inspect responses in
|
23
|
+
# more detail. Methods such as Integration::RequestHelpers#get
|
24
|
+
# and Integration::RequestHelpers#post return instances of
|
25
|
+
# TestResponse (which inherits from \Response) for this purpose.
|
26
26
|
#
|
27
27
|
# For example, the following demo integration test prints the body of the
|
28
28
|
# controller response to the console:
|
@@ -34,41 +34,43 @@ module ActionDispatch # :nodoc:
|
|
34
34
|
# end
|
35
35
|
# end
|
36
36
|
class Response
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
raise ActionDispatch::IllegalStateError, "header already sent"
|
46
|
-
end
|
47
|
-
|
48
|
-
super
|
49
|
-
end
|
50
|
-
|
51
|
-
def merge(other)
|
52
|
-
self.class.new @response, __getobj__.merge(other)
|
53
|
-
end
|
54
|
-
|
55
|
-
def to_hash
|
56
|
-
__getobj__.dup
|
57
|
-
end
|
37
|
+
begin
|
38
|
+
# For `Rack::Headers` (Rack 3+):
|
39
|
+
require "rack/headers"
|
40
|
+
Headers = ::Rack::Headers
|
41
|
+
rescue LoadError
|
42
|
+
# For `Rack::Utils::HeaderHash`:
|
43
|
+
require "rack/utils"
|
44
|
+
Headers = ::Rack::Utils::HeaderHash
|
58
45
|
end
|
59
46
|
|
47
|
+
# To be deprecated:
|
48
|
+
Header = Headers
|
49
|
+
|
60
50
|
# The request that the response is responding to.
|
61
51
|
attr_accessor :request
|
62
52
|
|
63
53
|
# The HTTP status code.
|
64
54
|
attr_reader :status
|
65
55
|
|
66
|
-
#
|
67
|
-
|
56
|
+
# The headers for the response.
|
57
|
+
#
|
58
|
+
# header["Content-Type"] # => "text/plain"
|
59
|
+
# header["Content-Type"] = "application/json"
|
60
|
+
# header["Content-Type"] # => "application/json"
|
61
|
+
#
|
62
|
+
# Also aliased as +headers+.
|
63
|
+
#
|
64
|
+
# headers["Content-Type"] # => "text/plain"
|
65
|
+
# headers["Content-Type"] = "application/json"
|
66
|
+
# headers["Content-Type"] # => "application/json"
|
67
|
+
#
|
68
|
+
# Also aliased as +header+ for compatibility.
|
69
|
+
attr_reader :headers
|
68
70
|
|
69
|
-
alias_method :
|
71
|
+
alias_method :header, :headers
|
70
72
|
|
71
|
-
delegate :[], :[]=, to: :@
|
73
|
+
delegate :[], :[]=, to: :@headers
|
72
74
|
|
73
75
|
def each(&block)
|
74
76
|
sending!
|
@@ -79,7 +81,6 @@ module ActionDispatch # :nodoc:
|
|
79
81
|
|
80
82
|
CONTENT_TYPE = "Content-Type"
|
81
83
|
SET_COOKIE = "Set-Cookie"
|
82
|
-
LOCATION = "Location"
|
83
84
|
NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
|
84
85
|
|
85
86
|
cattr_accessor :default_charset, default: "utf-8"
|
@@ -102,6 +103,12 @@ module ActionDispatch # :nodoc:
|
|
102
103
|
@str_body = nil
|
103
104
|
end
|
104
105
|
|
106
|
+
def to_ary
|
107
|
+
@buf.respond_to?(:to_ary) ?
|
108
|
+
@buf.to_ary :
|
109
|
+
@buf.each
|
110
|
+
end
|
111
|
+
|
105
112
|
def body
|
106
113
|
@str_body ||= begin
|
107
114
|
buf = +""
|
@@ -117,6 +124,7 @@ module ActionDispatch # :nodoc:
|
|
117
124
|
@response.commit!
|
118
125
|
@buf.push string
|
119
126
|
end
|
127
|
+
alias_method :<<, :write
|
120
128
|
|
121
129
|
def each(&block)
|
122
130
|
if @str_body
|
@@ -146,9 +154,9 @@ module ActionDispatch # :nodoc:
|
|
146
154
|
end
|
147
155
|
end
|
148
156
|
|
149
|
-
def self.create(status = 200,
|
150
|
-
|
151
|
-
new status,
|
157
|
+
def self.create(status = 200, headers = {}, body = [], default_headers: self.default_headers)
|
158
|
+
headers = merge_default_headers(headers, default_headers)
|
159
|
+
new status, headers, body
|
152
160
|
end
|
153
161
|
|
154
162
|
def self.merge_default_headers(original, default)
|
@@ -158,10 +166,14 @@ module ActionDispatch # :nodoc:
|
|
158
166
|
# The underlying body, as a streamable object.
|
159
167
|
attr_reader :stream
|
160
168
|
|
161
|
-
def initialize(status = 200,
|
169
|
+
def initialize(status = 200, headers = nil, body = [])
|
162
170
|
super()
|
163
171
|
|
164
|
-
@
|
172
|
+
@headers = Headers.new
|
173
|
+
|
174
|
+
headers&.each do |key, value|
|
175
|
+
@headers[key] = value
|
176
|
+
end
|
165
177
|
|
166
178
|
self.body, self.status = body, status
|
167
179
|
|
@@ -175,10 +187,10 @@ module ActionDispatch # :nodoc:
|
|
175
187
|
yield self if block_given?
|
176
188
|
end
|
177
189
|
|
178
|
-
def has_header?(key); headers.key? key; end
|
179
|
-
def get_header(key); headers[key]; end
|
180
|
-
def set_header(key, v); headers[key] = v; end
|
181
|
-
def delete_header(key); headers.delete key; end
|
190
|
+
def has_header?(key); @headers.key? key; end
|
191
|
+
def get_header(key); @headers[key]; end
|
192
|
+
def set_header(key, v); @headers[key] = v; end
|
193
|
+
def delete_header(key); @headers.delete key; end
|
182
194
|
|
183
195
|
def await_commit
|
184
196
|
synchronize do
|
@@ -281,7 +293,7 @@ module ActionDispatch # :nodoc:
|
|
281
293
|
@status
|
282
294
|
end
|
283
295
|
|
284
|
-
# Returns a string to ensure compatibility with
|
296
|
+
# Returns a string to ensure compatibility with +Net::HTTPResponse+.
|
285
297
|
def code
|
286
298
|
@status.to_s
|
287
299
|
end
|
@@ -384,7 +396,7 @@ module ActionDispatch # :nodoc:
|
|
384
396
|
# status, headers, body = *response
|
385
397
|
def to_a
|
386
398
|
commit!
|
387
|
-
rack_response @status, @
|
399
|
+
rack_response @status, @headers.to_hash
|
388
400
|
end
|
389
401
|
alias prepare! to_a
|
390
402
|
|
@@ -451,8 +463,7 @@ module ActionDispatch # :nodoc:
|
|
451
463
|
# our last chance.
|
452
464
|
commit! unless committed?
|
453
465
|
|
454
|
-
|
455
|
-
request.commit_cookie_jar! unless committed?
|
466
|
+
@request.commit_cookie_jar! unless committed?
|
456
467
|
end
|
457
468
|
|
458
469
|
def build_buffer(response, body)
|
@@ -476,10 +487,6 @@ module ActionDispatch # :nodoc:
|
|
476
487
|
@response = response
|
477
488
|
end
|
478
489
|
|
479
|
-
def each(*args, &block)
|
480
|
-
@response.each(*args, &block)
|
481
|
-
end
|
482
|
-
|
483
490
|
def close
|
484
491
|
# Rack "close" maps to Response#abort, and *not* Response#close
|
485
492
|
# (which is used when the controller's finished writing)
|
@@ -490,35 +497,45 @@ module ActionDispatch # :nodoc:
|
|
490
497
|
@response.body
|
491
498
|
end
|
492
499
|
|
500
|
+
BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
|
501
|
+
|
493
502
|
def respond_to?(method, include_private = false)
|
494
|
-
if method
|
503
|
+
if BODY_METHODS.key?(method)
|
495
504
|
@response.stream.respond_to?(method)
|
496
505
|
else
|
497
506
|
super
|
498
507
|
end
|
499
508
|
end
|
500
509
|
|
501
|
-
def
|
502
|
-
@response.stream.
|
510
|
+
def to_ary
|
511
|
+
@response.stream.to_ary
|
503
512
|
end
|
504
513
|
|
505
|
-
def
|
506
|
-
|
514
|
+
def each(*args, &block)
|
515
|
+
@response.each(*args, &block)
|
516
|
+
end
|
517
|
+
|
518
|
+
def call(*arguments, &block)
|
519
|
+
@response.stream.call(*arguments, &block)
|
520
|
+
end
|
521
|
+
|
522
|
+
def to_path
|
523
|
+
@response.stream.to_path
|
507
524
|
end
|
508
525
|
end
|
509
526
|
|
510
527
|
def handle_no_content!
|
511
528
|
if NO_CONTENT_CODES.include?(@status)
|
512
|
-
@
|
513
|
-
@
|
529
|
+
@headers.delete CONTENT_TYPE
|
530
|
+
@headers.delete "Content-Length"
|
514
531
|
end
|
515
532
|
end
|
516
533
|
|
517
|
-
def rack_response(status,
|
534
|
+
def rack_response(status, headers)
|
518
535
|
if NO_CONTENT_CODES.include?(status)
|
519
|
-
[status,
|
536
|
+
[status, headers, []]
|
520
537
|
else
|
521
|
-
[status,
|
538
|
+
[status, headers, RackBody.new(self)]
|
522
539
|
end
|
523
540
|
end
|
524
541
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
4
|
module Http
|
5
|
+
# = Action Dispatch HTTP \UploadedFile
|
6
|
+
#
|
5
7
|
# Models uploaded files.
|
6
8
|
#
|
7
9
|
# The actual file is accessible via the +tempfile+ accessor, though some
|
@@ -28,6 +30,8 @@ module ActionDispatch
|
|
28
30
|
@tempfile = hash[:tempfile]
|
29
31
|
raise(ArgumentError, ":tempfile is required") unless @tempfile
|
30
32
|
|
33
|
+
@content_type = hash[:type]
|
34
|
+
|
31
35
|
if hash[:filename]
|
32
36
|
@original_filename = hash[:filename].dup
|
33
37
|
|
@@ -40,8 +44,17 @@ module ActionDispatch
|
|
40
44
|
@original_filename = nil
|
41
45
|
end
|
42
46
|
|
43
|
-
|
44
|
-
|
47
|
+
if hash[:head]
|
48
|
+
@headers = hash[:head].dup
|
49
|
+
|
50
|
+
begin
|
51
|
+
@headers.encode!(Encoding::UTF_8)
|
52
|
+
rescue EncodingError
|
53
|
+
@headers.force_encoding(Encoding::UTF_8)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
@headers = nil
|
57
|
+
end
|
45
58
|
end
|
46
59
|
|
47
60
|
# Shortcut for +tempfile.read+.
|