actionpack 6.1.7.5 → 7.1.3.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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +355 -435
- data/MIT-LICENSE +2 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +33 -37
- data/lib/abstract_controller/caching/fragments.rb +4 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +50 -11
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +78 -30
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +26 -7
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +12 -10
- data/lib/action_controller/base.rb +8 -21
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +4 -2
- data/lib/action_controller/log_subscriber.rb +20 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +37 -3
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +25 -31
- 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 +27 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +5 -16
- data/lib/action_controller/metal/http_authentication.rb +78 -42
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +62 -50
- data/lib/action_controller/metal/live.rb +67 -2
- data/lib/action_controller/metal/mime_responds.rb +5 -5
- data/lib/action_controller/metal/params_wrapper.rb +24 -13
- data/lib/action_controller/metal/permissions_policy.rb +20 -29
- data/lib/action_controller/metal/redirecting.rb +96 -23
- data/lib/action_controller/metal/renderers.rb +14 -15
- data/lib/action_controller/metal/rendering.rb +121 -16
- data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
- data/lib/action_controller/metal/rescue.rb +7 -4
- data/lib/action_controller/metal/streaming.rb +74 -36
- data/lib/action_controller/metal/strong_parameters.rb +254 -151
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +10 -5
- data/lib/action_controller/metal.rb +89 -34
- data/lib/action_controller/railtie.rb +66 -9
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +42 -11
- data/lib/action_controller.rb +10 -6
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +21 -16
- data/lib/action_dispatch/http/content_security_policy.rb +122 -44
- data/lib/action_dispatch/http/filter_parameters.rb +14 -23
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
- data/lib/action_dispatch/http/mime_type.rb +43 -22
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +6 -6
- data/lib/action_dispatch/http/permissions_policy.rb +57 -19
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -51
- data/lib/action_dispatch/http/response.rb +81 -77
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +36 -27
- data/lib/action_dispatch/journey/route.rb +8 -14
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +10 -9
- data/lib/action_dispatch/journey/routes.rb +5 -5
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
- 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 +97 -107
- data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
- data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +24 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
- 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 +32 -19
- data/lib/action_dispatch/middleware/request_id.rb +5 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +34 -11
- data/lib/action_dispatch/middleware/static.rb +16 -16
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- 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 +64 -55
- data/lib/action_dispatch/railtie.rb +20 -4
- data/lib/action_dispatch/request/session.rb +59 -19
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +55 -7
- data/lib/action_dispatch/routing/mapper.rb +117 -107
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +20 -8
- data/lib/action_dispatch/routing/route_set.rb +67 -27
- data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
- data/lib/action_dispatch/routing/url_for.rb +29 -26
- data/lib/action_dispatch/routing.rb +12 -13
- data/lib/action_dispatch/system_test_case.rb +8 -8
- data/lib/action_dispatch/system_testing/browser.rb +20 -29
- data/lib/action_dispatch/system_testing/driver.rb +34 -18
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- 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 +70 -30
- data/lib/action_dispatch/testing/assertions.rb +3 -4
- data/lib/action_dispatch/testing/integration.rb +33 -25
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +5 -30
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +34 -2
- data/lib/action_dispatch.rb +38 -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 +67 -30
@@ -42,11 +42,8 @@ module ActionDispatch
|
|
42
42
|
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
|
43
43
|
HTTP_X_FORWARDED_FOR HTTP_ORIGIN HTTP_VERSION
|
44
44
|
HTTP_X_CSRF_TOKEN HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST
|
45
|
-
SERVER_ADDR
|
46
45
|
].freeze
|
47
46
|
|
48
|
-
# TODO: Remove SERVER_ADDR when we remove support to Rack 2.1.
|
49
|
-
# See https://github.com/rack/rack/commit/c173b188d81ee437b588c1e046a1c9f031dea550
|
50
47
|
ENV_METHODS.each do |env|
|
51
48
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
52
49
|
# frozen_string_literal: true
|
@@ -75,7 +72,7 @@ module ActionDispatch
|
|
75
72
|
|
76
73
|
PASS_NOT_FOUND = Class.new { # :nodoc:
|
77
74
|
def self.action(_); self; end
|
78
|
-
def self.call(_); [404, {
|
75
|
+
def self.call(_); [404, { Constants::X_CASCADE => "pass" }, []]; end
|
79
76
|
def self.action_encoding_template(action); false; end
|
80
77
|
}
|
81
78
|
|
@@ -90,7 +87,7 @@ module ActionDispatch
|
|
90
87
|
controller_param = name.underscore
|
91
88
|
const_name = controller_param.camelize << "Controller"
|
92
89
|
begin
|
93
|
-
|
90
|
+
const_name.constantize
|
94
91
|
rescue NameError => error
|
95
92
|
if error.missing_name == const_name || const_name.start_with?("#{error.missing_name}::")
|
96
93
|
raise MissingController.new(error.message, error.name)
|
@@ -110,22 +107,21 @@ module ActionDispatch
|
|
110
107
|
has_header? key
|
111
108
|
end
|
112
109
|
|
113
|
-
#
|
114
|
-
# Hypertext Transfer Protocol -- HTTP/1.1 (https://www.ietf.org/rfc/rfc2616.txt)
|
115
|
-
# HTTP Extensions for Distributed Authoring -- WEBDAV (https://www.ietf.org/rfc/rfc2518.txt)
|
116
|
-
# Versioning Extensions to WebDAV (https://www.ietf.org/rfc/rfc3253.txt)
|
117
|
-
# Ordered Collections Protocol (WebDAV) (https://www.ietf.org/rfc/rfc3648.txt)
|
118
|
-
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (https://www.ietf.org/rfc/rfc3744.txt)
|
119
|
-
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (https://www.ietf.org/rfc/rfc5323.txt)
|
120
|
-
# Calendar Extensions to WebDAV (https://www.ietf.org/rfc/rfc4791.txt)
|
121
|
-
# 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]
|
122
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]
|
123
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]
|
124
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]
|
125
117
|
RFC3648 = %w(ORDERPATCH)
|
118
|
+
# HTTP methods from {RFC 3744: WebDAV Access Control Protocol}[https://www.ietf.org/rfc/rfc3744.txt]
|
126
119
|
RFC3744 = %w(ACL)
|
120
|
+
# HTTP methods from {RFC 5323: WebDAV SEARCH}[https://www.ietf.org/rfc/rfc5323.txt]
|
127
121
|
RFC5323 = %w(SEARCH)
|
122
|
+
# HTTP methods from {RFC 4791: Calendaring Extensions to WebDAV}[https://www.ietf.org/rfc/rfc4791.txt]
|
128
123
|
RFC4791 = %w(MKCALENDAR)
|
124
|
+
# HTTP methods from {RFC 5789: PATCH Method for HTTP}[https://www.ietf.org/rfc/rfc5789.txt]
|
129
125
|
RFC5789 = %w(PATCH)
|
130
126
|
|
131
127
|
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
|
@@ -149,6 +145,18 @@ module ActionDispatch
|
|
149
145
|
@request_method ||= check_method(super)
|
150
146
|
end
|
151
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
|
+
|
152
160
|
def routes # :nodoc:
|
153
161
|
get_header("action_dispatch.routes")
|
154
162
|
end
|
@@ -165,7 +173,7 @@ module ActionDispatch
|
|
165
173
|
set_header(routes.env_key, name.dup)
|
166
174
|
end
|
167
175
|
|
168
|
-
def request_method=(request_method)
|
176
|
+
def request_method=(request_method) # :nodoc:
|
169
177
|
if check_method(request_method)
|
170
178
|
@request_method = set_header("REQUEST_METHOD", request_method)
|
171
179
|
end
|
@@ -183,13 +191,6 @@ module ActionDispatch
|
|
183
191
|
get_header "action_dispatch.http_auth_salt"
|
184
192
|
end
|
185
193
|
|
186
|
-
def show_exceptions? # :nodoc:
|
187
|
-
# We're treating `nil` as "unset", and we want the default setting to be
|
188
|
-
# `true`. This logic should be extracted to `env_config` and calculated
|
189
|
-
# once.
|
190
|
-
!(get_header("action_dispatch.show_exceptions") == false)
|
191
|
-
end
|
192
|
-
|
193
194
|
# Returns a symbol form of the #request_method.
|
194
195
|
def request_method_symbol
|
195
196
|
HTTP_METHOD_LOOKUP[request_method]
|
@@ -198,9 +199,20 @@ module ActionDispatch
|
|
198
199
|
# Returns the original value of the environment's REQUEST_METHOD,
|
199
200
|
# even if it was overridden by middleware. See #request_method for
|
200
201
|
# more information.
|
201
|
-
|
202
|
-
|
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
|
203
214
|
end
|
215
|
+
ruby2_keywords(:method)
|
204
216
|
|
205
217
|
# Returns a symbol form of the #method.
|
206
218
|
def method_symbol
|
@@ -266,15 +278,16 @@ module ActionDispatch
|
|
266
278
|
# # get "/articles"
|
267
279
|
# request.media_type # => "application/x-www-form-urlencoded"
|
268
280
|
def media_type
|
269
|
-
content_mime_type
|
281
|
+
content_mime_type&.to_s
|
270
282
|
end
|
271
283
|
|
272
284
|
# Returns the content length of the request as an integer.
|
273
285
|
def content_length
|
286
|
+
return raw_post.bytesize if headers.key?("Transfer-Encoding")
|
274
287
|
super.to_i
|
275
288
|
end
|
276
289
|
|
277
|
-
# Returns true if the
|
290
|
+
# Returns true if the +X-Requested-With+ header contains "XMLHttpRequest"
|
278
291
|
# (case-insensitive), which may need to be manually added depending on the
|
279
292
|
# choice of JavaScript libraries and frameworks.
|
280
293
|
def xml_http_request?
|
@@ -300,9 +313,9 @@ module ActionDispatch
|
|
300
313
|
|
301
314
|
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
|
302
315
|
|
303
|
-
# Returns the unique request id, which is based on either the X-Request-Id header that can
|
304
|
-
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
|
305
|
-
# (which sets the action_dispatch.request_id environment variable).
|
316
|
+
# Returns the unique request id, which is based on either the +X-Request-Id+ header that can
|
317
|
+
# be generated by a firewall, load balancer, or web server, or by the RequestId middleware
|
318
|
+
# (which sets the +action_dispatch.request_id+ environment variable).
|
306
319
|
#
|
307
320
|
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
|
308
321
|
# This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
|
@@ -325,9 +338,8 @@ module ActionDispatch
|
|
325
338
|
# work with raw requests directly.
|
326
339
|
def raw_post
|
327
340
|
unless has_header? "RAW_POST_DATA"
|
328
|
-
|
329
|
-
|
330
|
-
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
|
341
|
+
set_header("RAW_POST_DATA", read_body_stream)
|
342
|
+
body_stream.rewind if body_stream.respond_to?(:rewind)
|
331
343
|
end
|
332
344
|
get_header "RAW_POST_DATA"
|
333
345
|
end
|
@@ -344,32 +356,27 @@ module ActionDispatch
|
|
344
356
|
end
|
345
357
|
|
346
358
|
# Determine whether the request body contains form-data by checking
|
347
|
-
# the request Content-Type for one of the media-types:
|
348
|
-
#
|
359
|
+
# the request +Content-Type+ for one of the media-types:
|
360
|
+
# +application/x-www-form-urlencoded+ or +multipart/form-data+. The
|
349
361
|
# list of form-data media types can be modified through the
|
350
362
|
# +FORM_DATA_MEDIA_TYPES+ array.
|
351
363
|
#
|
352
364
|
# A request body is not assumed to contain form-data when no
|
353
|
-
# Content-Type header is provided and the request_method is POST.
|
365
|
+
# +Content-Type+ header is provided and the request_method is POST.
|
354
366
|
def form_data?
|
355
367
|
FORM_DATA_MEDIA_TYPES.include?(media_type)
|
356
368
|
end
|
357
369
|
|
358
|
-
def body_stream
|
370
|
+
def body_stream # :nodoc:
|
359
371
|
get_header("rack.input")
|
360
372
|
end
|
361
373
|
|
362
|
-
# TODO This should be broken apart into AD::Request::Session and probably
|
363
|
-
# be included by the session middleware.
|
364
374
|
def reset_session
|
365
|
-
|
366
|
-
|
367
|
-
else
|
368
|
-
self.session = {}
|
369
|
-
end
|
375
|
+
session.destroy
|
376
|
+
reset_csrf_token
|
370
377
|
end
|
371
378
|
|
372
|
-
def session=(session)
|
379
|
+
def session=(session) # :nodoc:
|
373
380
|
Session.set self, session
|
374
381
|
end
|
375
382
|
|
@@ -388,7 +395,7 @@ module ActionDispatch
|
|
388
395
|
Request::Utils.check_param_encoding(rack_query_params)
|
389
396
|
set_header k, Request::Utils.normalize_encode_params(rack_query_params)
|
390
397
|
end
|
391
|
-
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
398
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError => e
|
392
399
|
raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
|
393
400
|
end
|
394
401
|
alias :query_parameters :GET
|
@@ -403,7 +410,7 @@ module ActionDispatch
|
|
403
410
|
Request::Utils.check_param_encoding(pr)
|
404
411
|
self.request_parameters = Request::Utils.normalize_encode_params(pr)
|
405
412
|
end
|
406
|
-
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
413
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError, EOFError => e
|
407
414
|
raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
|
408
415
|
end
|
409
416
|
alias :request_parameters :POST
|
@@ -434,19 +441,36 @@ module ActionDispatch
|
|
434
441
|
def commit_flash
|
435
442
|
end
|
436
443
|
|
437
|
-
def ssl?
|
438
|
-
super || scheme == "wss"
|
439
|
-
end
|
440
|
-
|
441
444
|
def inspect # :nodoc:
|
442
445
|
"#<#{self.class.name} #{method} #{original_url.dump} for #{remote_ip}>"
|
443
446
|
end
|
444
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
|
+
|
445
456
|
private
|
446
457
|
def check_method(name)
|
447
|
-
|
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
|
+
|
448
462
|
name
|
449
463
|
end
|
464
|
+
|
465
|
+
def default_session
|
466
|
+
Session.disabled(self)
|
467
|
+
end
|
468
|
+
|
469
|
+
def read_body_stream
|
470
|
+
body_stream.rewind if body_stream.respond_to?(:rewind)
|
471
|
+
return body_stream.read if headers.key?("Transfer-Encoding") # Read body stream until EOF if "Transfer-Encoding" is present
|
472
|
+
body_stream.read(content_length)
|
473
|
+
end
|
450
474
|
end
|
451
475
|
end
|
452
476
|
|
@@ -6,24 +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
|
-
# ActionDispatch::Integration::Session#post return objects of type
|
26
|
-
# 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.
|
27
26
|
#
|
28
27
|
# For example, the following demo integration test prints the body of the
|
29
28
|
# controller response to the console:
|
@@ -35,41 +34,43 @@ module ActionDispatch # :nodoc:
|
|
35
34
|
# end
|
36
35
|
# end
|
37
36
|
class Response
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise ActionDispatch::IllegalStateError, "header already sent"
|
47
|
-
end
|
48
|
-
|
49
|
-
super
|
50
|
-
end
|
51
|
-
|
52
|
-
def merge(other)
|
53
|
-
self.class.new @response, __getobj__.merge(other)
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_hash
|
57
|
-
__getobj__.dup
|
58
|
-
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
|
59
45
|
end
|
60
46
|
|
47
|
+
# To be deprecated:
|
48
|
+
Header = Headers
|
49
|
+
|
61
50
|
# The request that the response is responding to.
|
62
51
|
attr_accessor :request
|
63
52
|
|
64
53
|
# The HTTP status code.
|
65
54
|
attr_reader :status
|
66
55
|
|
67
|
-
#
|
68
|
-
|
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
|
69
70
|
|
70
|
-
alias_method :
|
71
|
+
alias_method :header, :headers
|
71
72
|
|
72
|
-
delegate :[], :[]=, to: :@
|
73
|
+
delegate :[], :[]=, to: :@headers
|
73
74
|
|
74
75
|
def each(&block)
|
75
76
|
sending!
|
@@ -80,24 +81,11 @@ module ActionDispatch # :nodoc:
|
|
80
81
|
|
81
82
|
CONTENT_TYPE = "Content-Type"
|
82
83
|
SET_COOKIE = "Set-Cookie"
|
83
|
-
LOCATION = "Location"
|
84
84
|
NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
|
85
85
|
|
86
86
|
cattr_accessor :default_charset, default: "utf-8"
|
87
87
|
cattr_accessor :default_headers
|
88
88
|
|
89
|
-
def self.return_only_media_type_on_content_type=(*)
|
90
|
-
ActiveSupport::Deprecation.warn(
|
91
|
-
".return_only_media_type_on_content_type= is dreprecated with no replacement and will be removed in 7.0."
|
92
|
-
)
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.return_only_media_type_on_content_type
|
96
|
-
ActiveSupport::Deprecation.warn(
|
97
|
-
".return_only_media_type_on_content_type is dreprecated with no replacement and will be removed in 7.0."
|
98
|
-
)
|
99
|
-
end
|
100
|
-
|
101
89
|
include Rack::Response::Helpers
|
102
90
|
# Aliasing these off because AD::Http::Cache::Response defines them.
|
103
91
|
alias :_cache_control :cache_control
|
@@ -115,6 +103,12 @@ module ActionDispatch # :nodoc:
|
|
115
103
|
@str_body = nil
|
116
104
|
end
|
117
105
|
|
106
|
+
def to_ary
|
107
|
+
@buf.respond_to?(:to_ary) ?
|
108
|
+
@buf.to_ary :
|
109
|
+
@buf.each
|
110
|
+
end
|
111
|
+
|
118
112
|
def body
|
119
113
|
@str_body ||= begin
|
120
114
|
buf = +""
|
@@ -130,6 +124,7 @@ module ActionDispatch # :nodoc:
|
|
130
124
|
@response.commit!
|
131
125
|
@buf.push string
|
132
126
|
end
|
127
|
+
alias_method :<<, :write
|
133
128
|
|
134
129
|
def each(&block)
|
135
130
|
if @str_body
|
@@ -159,9 +154,9 @@ module ActionDispatch # :nodoc:
|
|
159
154
|
end
|
160
155
|
end
|
161
156
|
|
162
|
-
def self.create(status = 200,
|
163
|
-
|
164
|
-
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
|
165
160
|
end
|
166
161
|
|
167
162
|
def self.merge_default_headers(original, default)
|
@@ -171,10 +166,14 @@ module ActionDispatch # :nodoc:
|
|
171
166
|
# The underlying body, as a streamable object.
|
172
167
|
attr_reader :stream
|
173
168
|
|
174
|
-
def initialize(status = 200,
|
169
|
+
def initialize(status = 200, headers = nil, body = [])
|
175
170
|
super()
|
176
171
|
|
177
|
-
@
|
172
|
+
@headers = Headers.new
|
173
|
+
|
174
|
+
headers&.each do |key, value|
|
175
|
+
@headers[key] = value
|
176
|
+
end
|
178
177
|
|
179
178
|
self.body, self.status = body, status
|
180
179
|
|
@@ -188,10 +187,10 @@ module ActionDispatch # :nodoc:
|
|
188
187
|
yield self if block_given?
|
189
188
|
end
|
190
189
|
|
191
|
-
def has_header?(key); headers.key? key; end
|
192
|
-
def get_header(key); headers[key]; end
|
193
|
-
def set_header(key, v); headers[key] = v; end
|
194
|
-
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
|
195
194
|
|
196
195
|
def await_commit
|
197
196
|
synchronize do
|
@@ -294,7 +293,7 @@ module ActionDispatch # :nodoc:
|
|
294
293
|
@status
|
295
294
|
end
|
296
295
|
|
297
|
-
# Returns a string to ensure compatibility with
|
296
|
+
# Returns a string to ensure compatibility with +Net::HTTPResponse+.
|
298
297
|
def code
|
299
298
|
@status.to_s
|
300
299
|
end
|
@@ -336,7 +335,7 @@ module ActionDispatch # :nodoc:
|
|
336
335
|
# Avoid having to pass an open file handle as the response body.
|
337
336
|
# Rack::Sendfile will usually intercept the response and uses
|
338
337
|
# the path directly, so there is no reason to open the file.
|
339
|
-
class FileBody
|
338
|
+
class FileBody # :nodoc:
|
340
339
|
attr_reader :to_path
|
341
340
|
|
342
341
|
def initialize(path)
|
@@ -397,7 +396,7 @@ module ActionDispatch # :nodoc:
|
|
397
396
|
# status, headers, body = *response
|
398
397
|
def to_a
|
399
398
|
commit!
|
400
|
-
rack_response @status, @
|
399
|
+
rack_response @status, @headers.to_hash
|
401
400
|
end
|
402
401
|
alias prepare! to_a
|
403
402
|
|
@@ -464,8 +463,7 @@ module ActionDispatch # :nodoc:
|
|
464
463
|
# our last chance.
|
465
464
|
commit! unless committed?
|
466
465
|
|
467
|
-
|
468
|
-
request.commit_cookie_jar! unless committed?
|
466
|
+
@request.commit_cookie_jar! unless committed?
|
469
467
|
end
|
470
468
|
|
471
469
|
def build_buffer(response, body)
|
@@ -489,10 +487,6 @@ module ActionDispatch # :nodoc:
|
|
489
487
|
@response = response
|
490
488
|
end
|
491
489
|
|
492
|
-
def each(*args, &block)
|
493
|
-
@response.each(*args, &block)
|
494
|
-
end
|
495
|
-
|
496
490
|
def close
|
497
491
|
# Rack "close" maps to Response#abort, and *not* Response#close
|
498
492
|
# (which is used when the controller's finished writing)
|
@@ -503,35 +497,45 @@ module ActionDispatch # :nodoc:
|
|
503
497
|
@response.body
|
504
498
|
end
|
505
499
|
|
500
|
+
BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
|
501
|
+
|
506
502
|
def respond_to?(method, include_private = false)
|
507
|
-
if method
|
503
|
+
if BODY_METHODS.key?(method)
|
508
504
|
@response.stream.respond_to?(method)
|
509
505
|
else
|
510
506
|
super
|
511
507
|
end
|
512
508
|
end
|
513
509
|
|
514
|
-
def
|
515
|
-
@response.stream.
|
510
|
+
def to_ary
|
511
|
+
@response.stream.to_ary
|
516
512
|
end
|
517
513
|
|
518
|
-
def
|
519
|
-
|
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
|
520
524
|
end
|
521
525
|
end
|
522
526
|
|
523
527
|
def handle_no_content!
|
524
528
|
if NO_CONTENT_CODES.include?(@status)
|
525
|
-
@
|
526
|
-
@
|
529
|
+
@headers.delete CONTENT_TYPE
|
530
|
+
@headers.delete "Content-Length"
|
527
531
|
end
|
528
532
|
end
|
529
533
|
|
530
|
-
def rack_response(status,
|
534
|
+
def rack_response(status, headers)
|
531
535
|
if NO_CONTENT_CODES.include?(status)
|
532
|
-
[status,
|
536
|
+
[status, headers, []]
|
533
537
|
else
|
534
|
-
[status,
|
538
|
+
[status, headers, RackBody.new(self)]
|
535
539
|
end
|
536
540
|
end
|
537
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+.
|
@@ -71,7 +71,8 @@ module ActionDispatch
|
|
71
71
|
path = options[:script_name].to_s.chomp("/")
|
72
72
|
path << options[:path] if options.key?(:path)
|
73
73
|
|
74
|
-
|
74
|
+
path = "/" if options[:trailing_slash] && path.blank?
|
75
|
+
|
75
76
|
add_params(path, options[:params]) if options.key?(:params)
|
76
77
|
add_anchor(path, options[:anchor]) if options.key?(:anchor)
|
77
78
|
|
@@ -101,14 +102,6 @@ module ActionDispatch
|
|
101
102
|
parts[0..-(tld_length + 2)]
|
102
103
|
end
|
103
104
|
|
104
|
-
def add_trailing_slash(path)
|
105
|
-
if path.include?("?")
|
106
|
-
path.sub!(/\?/, '/\&')
|
107
|
-
elsif !path.include?(".")
|
108
|
-
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
105
|
def build_host_url(host, port, protocol, options, path)
|
113
106
|
if match = host.match(HOST_REGEXP)
|
114
107
|
protocol ||= match[1] unless protocol == false
|
@@ -222,7 +215,7 @@ module ActionDispatch
|
|
222
215
|
if forwarded = x_forwarded_host.presence
|
223
216
|
forwarded.split(/,\s?/).last
|
224
217
|
else
|
225
|
-
get_header("HTTP_HOST") || "#{server_name
|
218
|
+
get_header("HTTP_HOST") || "#{server_name}:#{get_header('SERVER_PORT')}"
|
226
219
|
end
|
227
220
|
end
|
228
221
|
|
@@ -258,12 +251,10 @@ module ActionDispatch
|
|
258
251
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
259
252
|
# req.port # => 8080
|
260
253
|
def port
|
261
|
-
@port ||=
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
standard_port
|
266
|
-
end
|
254
|
+
@port ||= if raw_host_with_port =~ /:(\d+)$/
|
255
|
+
$1.to_i
|
256
|
+
else
|
257
|
+
standard_port
|
267
258
|
end
|
268
259
|
end
|
269
260
|
|
@@ -272,9 +263,10 @@ module ActionDispatch
|
|
272
263
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
273
264
|
# req.standard_port # => 80
|
274
265
|
def standard_port
|
275
|
-
|
276
|
-
|
277
|
-
else
|
266
|
+
if "https://" == protocol
|
267
|
+
443
|
268
|
+
else
|
269
|
+
80
|
278
270
|
end
|
279
271
|
end
|
280
272
|
|
@@ -57,6 +57,9 @@ module ActionDispatch
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def generate(name, options, path_parameters)
|
60
|
+
original_options = options.dup
|
61
|
+
path_params = options.delete(:path_params) || {}
|
62
|
+
options = path_params.merge(options)
|
60
63
|
constraints = path_parameters.merge(options)
|
61
64
|
missing_keys = nil
|
62
65
|
|
@@ -70,8 +73,11 @@ module ActionDispatch
|
|
70
73
|
|
71
74
|
missing_keys = missing_keys(route, parameterized_parts)
|
72
75
|
next if missing_keys && !missing_keys.empty?
|
73
|
-
params = options.
|
74
|
-
|
76
|
+
params = options.delete_if do |key, _|
|
77
|
+
# top-level params' normal behavior of generating query_params
|
78
|
+
# should be preserved even if the same key is also a bind_param
|
79
|
+
parameterized_parts.key?(key) || route.defaults.key?(key) ||
|
80
|
+
(path_params.key?(key) && !original_options.key?(key))
|
75
81
|
end
|
76
82
|
|
77
83
|
defaults = route.defaults
|