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