actionpack 5.2.8.1 → 6.1.6.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +383 -346
- data/MIT-LICENSE +1 -2
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +4 -3
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +10 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +19 -5
- data/lib/action_controller/metal/content_security_policy.rb +1 -2
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +6 -7
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +56 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +14 -5
- data/lib/action_controller/metal/http_authentication.rb +25 -23
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +13 -14
- data/lib/action_controller/metal/live.rb +39 -32
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +19 -4
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +32 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +6 -6
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +8 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +168 -59
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +10 -8
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +71 -63
- data/lib/action_controller.rb +7 -4
- data/lib/action_dispatch/http/cache.rb +31 -27
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +34 -18
- data/lib/action_dispatch/http/filter_parameters.rb +9 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
- data/lib/action_dispatch/http/mime_type.rb +43 -24
- data/lib/action_dispatch/http/parameters.rb +14 -23
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +45 -22
- data/lib/action_dispatch/http/response.rb +45 -25
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +82 -82
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +13 -11
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +19 -21
- data/lib/action_dispatch/journey/route.rb +10 -20
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/router.rb +26 -34
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +128 -109
- data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +170 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +13 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +56 -2
- data/lib/action_dispatch/middleware/static.rb +153 -93
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +11 -10
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +100 -52
- data/lib/action_dispatch/routing/mapper.rb +155 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
- data/lib/action_dispatch/routing/redirection.rb +4 -4
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +60 -11
- data/lib/action_dispatch/system_testing/browser.rb +53 -16
- data/lib/action_dispatch/system_testing/driver.rb +11 -3
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +60 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +32 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +9 -3
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +34 -21
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -23,6 +23,7 @@ module ActionDispatch
|
|
23
23
|
include ActionDispatch::Http::FilterParameters
|
24
24
|
include ActionDispatch::Http::URL
|
25
25
|
include ActionDispatch::ContentSecurityPolicy::Request
|
26
|
+
include ActionDispatch::PermissionsPolicy::Request
|
26
27
|
include Rack::Request::Env
|
27
28
|
|
28
29
|
autoload :Session, "action_dispatch/request/session"
|
@@ -44,11 +45,14 @@ module ActionDispatch
|
|
44
45
|
SERVER_ADDR
|
45
46
|
].freeze
|
46
47
|
|
48
|
+
# TODO: Remove SERVER_ADDR when we remove support to Rack 2.1.
|
49
|
+
# See https://github.com/rack/rack/commit/c173b188d81ee437b588c1e046a1c9f031dea550
|
47
50
|
ENV_METHODS.each do |env|
|
48
51
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
# frozen_string_literal: true
|
53
|
+
def #{env.delete_prefix("HTTP_").downcase} # def accept_charset
|
54
|
+
get_header "#{env}" # get_header "HTTP_ACCEPT_CHARSET"
|
55
|
+
end # end
|
52
56
|
METHOD
|
53
57
|
end
|
54
58
|
|
@@ -72,7 +76,7 @@ module ActionDispatch
|
|
72
76
|
PASS_NOT_FOUND = Class.new { # :nodoc:
|
73
77
|
def self.action(_); self; end
|
74
78
|
def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
|
75
|
-
def self.
|
79
|
+
def self.action_encoding_template(action); false; end
|
76
80
|
}
|
77
81
|
|
78
82
|
def controller_class
|
@@ -84,8 +88,16 @@ module ActionDispatch
|
|
84
88
|
def controller_class_for(name)
|
85
89
|
if name
|
86
90
|
controller_param = name.underscore
|
87
|
-
const_name =
|
88
|
-
|
91
|
+
const_name = controller_param.camelize << "Controller"
|
92
|
+
begin
|
93
|
+
ActiveSupport::Dependencies.constantize(const_name)
|
94
|
+
rescue NameError => error
|
95
|
+
if error.missing_name == const_name || const_name.start_with?("#{error.missing_name}::")
|
96
|
+
raise MissingController.new(error.message, error.name)
|
97
|
+
else
|
98
|
+
raise
|
99
|
+
end
|
100
|
+
end
|
89
101
|
else
|
90
102
|
PASS_NOT_FOUND
|
91
103
|
end
|
@@ -125,6 +137,8 @@ module ActionDispatch
|
|
125
137
|
HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
|
126
138
|
}
|
127
139
|
|
140
|
+
alias raw_request_method request_method # :nodoc:
|
141
|
+
|
128
142
|
# Returns the HTTP \method that the application should see.
|
129
143
|
# In the case where the \method was overridden by a middleware
|
130
144
|
# (for instance, if a HEAD request was converted to a GET,
|
@@ -136,11 +150,11 @@ module ActionDispatch
|
|
136
150
|
end
|
137
151
|
|
138
152
|
def routes # :nodoc:
|
139
|
-
get_header("action_dispatch.routes"
|
153
|
+
get_header("action_dispatch.routes")
|
140
154
|
end
|
141
155
|
|
142
156
|
def routes=(routes) # :nodoc:
|
143
|
-
set_header("action_dispatch.routes"
|
157
|
+
set_header("action_dispatch.routes", routes)
|
144
158
|
end
|
145
159
|
|
146
160
|
def engine_script_name(_routes) # :nodoc:
|
@@ -158,11 +172,11 @@ module ActionDispatch
|
|
158
172
|
end
|
159
173
|
|
160
174
|
def controller_instance # :nodoc:
|
161
|
-
get_header("action_controller.instance"
|
175
|
+
get_header("action_controller.instance")
|
162
176
|
end
|
163
177
|
|
164
178
|
def controller_instance=(controller) # :nodoc:
|
165
|
-
set_header("action_controller.instance"
|
179
|
+
set_header("action_controller.instance", controller)
|
166
180
|
end
|
167
181
|
|
168
182
|
def http_auth_salt
|
@@ -173,7 +187,7 @@ module ActionDispatch
|
|
173
187
|
# We're treating `nil` as "unset", and we want the default setting to be
|
174
188
|
# `true`. This logic should be extracted to `env_config` and calculated
|
175
189
|
# once.
|
176
|
-
!(get_header("action_dispatch.show_exceptions"
|
190
|
+
!(get_header("action_dispatch.show_exceptions") == false)
|
177
191
|
end
|
178
192
|
|
179
193
|
# Returns a symbol form of the #request_method.
|
@@ -264,7 +278,7 @@ module ActionDispatch
|
|
264
278
|
# (case-insensitive), which may need to be manually added depending on the
|
265
279
|
# choice of JavaScript libraries and frameworks.
|
266
280
|
def xml_http_request?
|
267
|
-
get_header("HTTP_X_REQUESTED_WITH")
|
281
|
+
/XMLHttpRequest/i.match?(get_header("HTTP_X_REQUESTED_WITH"))
|
268
282
|
end
|
269
283
|
alias :xhr? :xml_http_request?
|
270
284
|
|
@@ -280,10 +294,11 @@ module ActionDispatch
|
|
280
294
|
end
|
281
295
|
|
282
296
|
def remote_ip=(remote_ip)
|
283
|
-
|
297
|
+
@remote_ip = nil
|
298
|
+
set_header "action_dispatch.remote_ip", remote_ip
|
284
299
|
end
|
285
300
|
|
286
|
-
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id"
|
301
|
+
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
|
287
302
|
|
288
303
|
# Returns the unique request id, which is based on either the X-Request-Id header that can
|
289
304
|
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
|
@@ -321,7 +336,7 @@ module ActionDispatch
|
|
321
336
|
# variable is already set, wrap it in a StringIO.
|
322
337
|
def body
|
323
338
|
if raw_post = get_header("RAW_POST_DATA")
|
324
|
-
raw_post = raw_post.
|
339
|
+
raw_post = (+raw_post).force_encoding(Encoding::BINARY)
|
325
340
|
StringIO.new(raw_post)
|
326
341
|
else
|
327
342
|
body_stream
|
@@ -366,6 +381,9 @@ module ActionDispatch
|
|
366
381
|
def GET
|
367
382
|
fetch_header("action_dispatch.request.query_parameters") do |k|
|
368
383
|
rack_query_params = super || {}
|
384
|
+
controller = path_parameters[:controller]
|
385
|
+
action = path_parameters[:action]
|
386
|
+
rack_query_params = Request::Utils.set_binary_encoding(self, rack_query_params, controller, action)
|
369
387
|
# Check for non UTF-8 parameter values, which would cause errors later
|
370
388
|
Request::Utils.check_param_encoding(rack_query_params)
|
371
389
|
set_header k, Request::Utils.normalize_encode_params(rack_query_params)
|
@@ -381,11 +399,10 @@ module ActionDispatch
|
|
381
399
|
pr = parse_formatted_parameters(params_parsers) do |params|
|
382
400
|
super || {}
|
383
401
|
end
|
402
|
+
pr = Request::Utils.set_binary_encoding(self, pr, path_parameters[:controller], path_parameters[:action])
|
403
|
+
Request::Utils.check_param_encoding(pr)
|
384
404
|
self.request_parameters = Request::Utils.normalize_encode_params(pr)
|
385
405
|
end
|
386
|
-
rescue Http::Parameters::ParseError # one of the parse strategies blew up
|
387
|
-
self.request_parameters = Request::Utils.normalize_encode_params(super || {})
|
388
|
-
raise
|
389
406
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
390
407
|
raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
|
391
408
|
end
|
@@ -402,23 +419,27 @@ module ActionDispatch
|
|
402
419
|
|
403
420
|
# True if the request came from localhost, 127.0.0.1, or ::1.
|
404
421
|
def local?
|
405
|
-
LOCALHOST
|
422
|
+
LOCALHOST.match?(remote_addr) && LOCALHOST.match?(remote_ip)
|
406
423
|
end
|
407
424
|
|
408
425
|
def request_parameters=(params)
|
409
426
|
raise if params.nil?
|
410
|
-
set_header("action_dispatch.request.request_parameters"
|
427
|
+
set_header("action_dispatch.request.request_parameters", params)
|
411
428
|
end
|
412
429
|
|
413
430
|
def logger
|
414
|
-
get_header("action_dispatch.logger"
|
431
|
+
get_header("action_dispatch.logger")
|
415
432
|
end
|
416
433
|
|
417
434
|
def commit_flash
|
418
435
|
end
|
419
436
|
|
420
437
|
def ssl?
|
421
|
-
super || scheme == "wss"
|
438
|
+
super || scheme == "wss"
|
439
|
+
end
|
440
|
+
|
441
|
+
def inspect # :nodoc:
|
442
|
+
"#<#{self.class.name} #{method} #{original_url.dump} for #{remote_ip}>"
|
422
443
|
end
|
423
444
|
|
424
445
|
private
|
@@ -428,3 +449,5 @@ module ActionDispatch
|
|
428
449
|
end
|
429
450
|
end
|
430
451
|
end
|
452
|
+
|
453
|
+
ActiveSupport.run_load_hooks :action_dispatch_request, ActionDispatch::Request
|
@@ -78,14 +78,26 @@ module ActionDispatch # :nodoc:
|
|
78
78
|
x
|
79
79
|
end
|
80
80
|
|
81
|
-
CONTENT_TYPE = "Content-Type"
|
82
|
-
SET_COOKIE = "Set-Cookie"
|
83
|
-
LOCATION = "Location"
|
84
|
-
NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
|
81
|
+
CONTENT_TYPE = "Content-Type"
|
82
|
+
SET_COOKIE = "Set-Cookie"
|
83
|
+
LOCATION = "Location"
|
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
|
+
|
89
101
|
include Rack::Response::Helpers
|
90
102
|
# Aliasing these off because AD::Http::Cache::Response defines them.
|
91
103
|
alias :_cache_control :cache_control
|
@@ -105,7 +117,7 @@ module ActionDispatch # :nodoc:
|
|
105
117
|
|
106
118
|
def body
|
107
119
|
@str_body ||= begin
|
108
|
-
buf = ""
|
120
|
+
buf = +""
|
109
121
|
each { |chunk| buf << chunk }
|
110
122
|
buf
|
111
123
|
end
|
@@ -142,7 +154,6 @@ module ActionDispatch # :nodoc:
|
|
142
154
|
end
|
143
155
|
|
144
156
|
private
|
145
|
-
|
146
157
|
def each_chunk(&block)
|
147
158
|
@buf.each(&block)
|
148
159
|
end
|
@@ -224,16 +235,6 @@ module ActionDispatch # :nodoc:
|
|
224
235
|
@status = Rack::Utils.status_code(status)
|
225
236
|
end
|
226
237
|
|
227
|
-
# Sets the HTTP content type.
|
228
|
-
def content_type=(content_type)
|
229
|
-
return unless content_type
|
230
|
-
new_header_info = parse_content_type(content_type.to_s)
|
231
|
-
prev_header_info = parsed_content_type_header
|
232
|
-
charset = new_header_info.charset || prev_header_info.charset
|
233
|
-
charset ||= self.class.default_charset unless prev_header_info.mime_type
|
234
|
-
set_content_type new_header_info.mime_type, charset
|
235
|
-
end
|
236
|
-
|
237
238
|
# Sets the HTTP response's content MIME type. For example, in the controller
|
238
239
|
# you could write this:
|
239
240
|
#
|
@@ -242,8 +243,22 @@ module ActionDispatch # :nodoc:
|
|
242
243
|
# If a character set has been defined for this response (see charset=) then
|
243
244
|
# the character set information will also be included in the content type
|
244
245
|
# information.
|
246
|
+
def content_type=(content_type)
|
247
|
+
return unless content_type
|
248
|
+
new_header_info = parse_content_type(content_type.to_s)
|
249
|
+
prev_header_info = parsed_content_type_header
|
250
|
+
charset = new_header_info.charset || prev_header_info.charset
|
251
|
+
charset ||= self.class.default_charset unless prev_header_info.mime_type
|
252
|
+
set_content_type new_header_info.mime_type, charset
|
253
|
+
end
|
245
254
|
|
255
|
+
# Content type of response.
|
246
256
|
def content_type
|
257
|
+
super.presence
|
258
|
+
end
|
259
|
+
|
260
|
+
# Media type of response.
|
261
|
+
def media_type
|
247
262
|
parsed_content_type_header.mime_type
|
248
263
|
end
|
249
264
|
|
@@ -404,15 +419,18 @@ module ActionDispatch # :nodoc:
|
|
404
419
|
end
|
405
420
|
|
406
421
|
private
|
407
|
-
|
408
422
|
ContentTypeHeader = Struct.new :mime_type, :charset
|
409
423
|
NullContentTypeHeader = ContentTypeHeader.new nil, nil
|
410
424
|
|
425
|
+
CONTENT_TYPE_PARSER = /
|
426
|
+
\A
|
427
|
+
(?<mime_type>[^;\s]+\s*(?:;\s*(?:(?!charset)[^;\s])+)*)?
|
428
|
+
(?:;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?
|
429
|
+
/x # :nodoc:
|
430
|
+
|
411
431
|
def parse_content_type(content_type)
|
412
|
-
if content_type
|
413
|
-
|
414
|
-
type = nil if type && type.empty?
|
415
|
-
ContentTypeHeader.new(type, charset)
|
432
|
+
if content_type && match = CONTENT_TYPE_PARSER.match(content_type)
|
433
|
+
ContentTypeHeader.new(match[:mime_type], match[:charset])
|
416
434
|
else
|
417
435
|
NullContentTypeHeader
|
418
436
|
end
|
@@ -425,8 +443,8 @@ module ActionDispatch # :nodoc:
|
|
425
443
|
end
|
426
444
|
|
427
445
|
def set_content_type(content_type, charset)
|
428
|
-
type =
|
429
|
-
type
|
446
|
+
type = content_type || ""
|
447
|
+
type = "#{type}; charset=#{charset.to_s.downcase}" if charset
|
430
448
|
set_header CONTENT_TYPE, type
|
431
449
|
end
|
432
450
|
|
@@ -459,7 +477,7 @@ module ActionDispatch # :nodoc:
|
|
459
477
|
end
|
460
478
|
|
461
479
|
def assign_default_content_type_and_charset!
|
462
|
-
return if
|
480
|
+
return if media_type
|
463
481
|
|
464
482
|
ct = parsed_content_type_header
|
465
483
|
set_content_type(ct.mime_type || Mime[:html].to_s,
|
@@ -486,7 +504,7 @@ module ActionDispatch # :nodoc:
|
|
486
504
|
end
|
487
505
|
|
488
506
|
def respond_to?(method, include_private = false)
|
489
|
-
if method.
|
507
|
+
if method.to_sym == :to_path
|
490
508
|
@response.stream.respond_to?(method)
|
491
509
|
else
|
492
510
|
super
|
@@ -517,4 +535,6 @@ module ActionDispatch # :nodoc:
|
|
517
535
|
end
|
518
536
|
end
|
519
537
|
end
|
538
|
+
|
539
|
+
ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
|
520
540
|
end
|
@@ -20,7 +20,6 @@ module ActionDispatch
|
|
20
20
|
# A +Tempfile+ object with the actual uploaded file. Note that some of
|
21
21
|
# its interface is available directly.
|
22
22
|
attr_accessor :tempfile
|
23
|
-
alias :to_io :tempfile
|
24
23
|
|
25
24
|
# A string with the headers of the multipart request.
|
26
25
|
attr_accessor :headers
|
@@ -65,6 +64,11 @@ module ActionDispatch
|
|
65
64
|
@tempfile.path
|
66
65
|
end
|
67
66
|
|
67
|
+
# Shortcut for +tempfile.to_path+.
|
68
|
+
def to_path
|
69
|
+
@tempfile.to_path
|
70
|
+
end
|
71
|
+
|
68
72
|
# Shortcut for +tempfile.rewind+.
|
69
73
|
def rewind
|
70
74
|
@tempfile.rewind
|
@@ -79,6 +83,10 @@ module ActionDispatch
|
|
79
83
|
def eof?
|
80
84
|
@tempfile.eof?
|
81
85
|
end
|
86
|
+
|
87
|
+
def to_io
|
88
|
+
@tempfile.to_io
|
89
|
+
end
|
82
90
|
end
|
83
91
|
end
|
84
92
|
end
|
@@ -9,6 +9,7 @@ module ActionDispatch
|
|
9
9
|
HOST_REGEXP = /(^[^:]+:\/\/)?(\[[^\]]+\]|[^:]+)(?::(\d+$))?/
|
10
10
|
PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
|
11
11
|
|
12
|
+
mattr_accessor :secure_protocol, default: false
|
12
13
|
mattr_accessor :tld_length, default: 1
|
13
14
|
|
14
15
|
class << self
|
@@ -67,7 +68,7 @@ module ActionDispatch
|
|
67
68
|
end
|
68
69
|
|
69
70
|
def path_for(options)
|
70
|
-
path = options[:script_name].to_s.chomp("/"
|
71
|
+
path = options[:script_name].to_s.chomp("/")
|
71
72
|
path << options[:path] if options.key?(:path)
|
72
73
|
|
73
74
|
add_trailing_slash(path) if options[:trailing_slash]
|
@@ -78,109 +79,108 @@ module ActionDispatch
|
|
78
79
|
end
|
79
80
|
|
80
81
|
private
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
path << "?#{query}" unless query.empty?
|
87
|
-
end
|
88
|
-
|
89
|
-
def add_anchor(path, anchor)
|
90
|
-
if anchor
|
91
|
-
path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
|
82
|
+
def add_params(path, params)
|
83
|
+
params = { params: params } unless params.is_a?(Hash)
|
84
|
+
params.reject! { |_, v| v.to_param.nil? }
|
85
|
+
query = params.to_query
|
86
|
+
path << "?#{query}" unless query.empty?
|
92
87
|
end
|
93
|
-
end
|
94
88
|
|
95
|
-
|
96
|
-
|
97
|
-
|
89
|
+
def add_anchor(path, anchor)
|
90
|
+
if anchor
|
91
|
+
path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
|
92
|
+
end
|
93
|
+
end
|
98
94
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
95
|
+
def extract_domain_from(host, tld_length)
|
96
|
+
host.split(".").last(1 + tld_length).join(".")
|
97
|
+
end
|
103
98
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
elsif !path.include?(".")
|
108
|
-
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
99
|
+
def extract_subdomains_from(host, tld_length)
|
100
|
+
parts = host.split(".")
|
101
|
+
parts[0..-(tld_length + 2)]
|
109
102
|
end
|
110
|
-
end
|
111
103
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
117
110
|
end
|
118
111
|
|
119
|
-
|
120
|
-
|
112
|
+
def build_host_url(host, port, protocol, options, path)
|
113
|
+
if match = host.match(HOST_REGEXP)
|
114
|
+
protocol ||= match[1] unless protocol == false
|
115
|
+
host = match[2]
|
116
|
+
port = match[3] unless options.key? :port
|
117
|
+
end
|
121
118
|
|
122
|
-
|
119
|
+
protocol = normalize_protocol protocol
|
120
|
+
host = normalize_host(host, options)
|
123
121
|
|
124
|
-
|
125
|
-
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
126
|
-
end
|
122
|
+
result = protocol.dup
|
127
123
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
124
|
+
if options[:user] && options[:password]
|
125
|
+
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
126
|
+
end
|
132
127
|
|
133
|
-
|
134
|
-
|
128
|
+
result << host
|
129
|
+
normalize_port(port, protocol) { |normalized_port|
|
130
|
+
result << ":#{normalized_port}"
|
131
|
+
}
|
135
132
|
|
136
|
-
|
137
|
-
|
138
|
-
end
|
133
|
+
result.concat path
|
134
|
+
end
|
139
135
|
|
140
|
-
|
141
|
-
|
142
|
-
when nil
|
143
|
-
"http://"
|
144
|
-
when false, "//"
|
145
|
-
"//"
|
146
|
-
when PROTOCOL_REGEXP
|
147
|
-
"#{$1}://"
|
148
|
-
else
|
149
|
-
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
136
|
+
def named_host?(host)
|
137
|
+
!IP_HOST_REGEXP.match?(host)
|
150
138
|
end
|
151
|
-
end
|
152
139
|
|
153
|
-
|
154
|
-
|
140
|
+
def normalize_protocol(protocol)
|
141
|
+
case protocol
|
142
|
+
when nil
|
143
|
+
secure_protocol ? "https://" : "http://"
|
144
|
+
when false, "//"
|
145
|
+
"//"
|
146
|
+
when PROTOCOL_REGEXP
|
147
|
+
"#{$1}://"
|
148
|
+
else
|
149
|
+
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def normalize_host(_host, options)
|
154
|
+
return _host unless named_host?(_host)
|
155
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
156
|
+
tld_length = options[:tld_length] || @@tld_length
|
157
|
+
subdomain = options.fetch :subdomain, true
|
158
|
+
domain = options[:domain]
|
159
159
|
|
160
|
-
|
161
|
-
|
162
|
-
|
160
|
+
host = +""
|
161
|
+
if subdomain == true
|
162
|
+
return _host if domain.nil?
|
163
163
|
|
164
|
-
|
165
|
-
|
166
|
-
|
164
|
+
host << extract_subdomains_from(_host, tld_length).join(".")
|
165
|
+
elsif subdomain
|
166
|
+
host << subdomain.to_param
|
167
|
+
end
|
168
|
+
host << "." unless host.empty?
|
169
|
+
host << (domain || extract_domain_from(_host, tld_length))
|
170
|
+
host
|
167
171
|
end
|
168
|
-
host << "." unless host.empty?
|
169
|
-
host << (domain || extract_domain_from(_host, tld_length))
|
170
|
-
host
|
171
|
-
end
|
172
172
|
|
173
|
-
|
174
|
-
|
173
|
+
def normalize_port(port, protocol)
|
174
|
+
return unless port
|
175
175
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
176
|
+
case protocol
|
177
|
+
when "//" then yield port
|
178
|
+
when "https://"
|
179
|
+
yield port unless port.to_i == 443
|
180
|
+
else
|
181
|
+
yield port unless port.to_i == 80
|
182
|
+
end
|
182
183
|
end
|
183
|
-
end
|
184
184
|
end
|
185
185
|
|
186
186
|
def initialize
|
@@ -231,7 +231,7 @@ module ActionDispatch
|
|
231
231
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
232
232
|
# req.host # => "example.com"
|
233
233
|
def host
|
234
|
-
raw_host_with_port.sub(/:\d+$/, ""
|
234
|
+
raw_host_with_port.sub(/:\d+$/, "")
|
235
235
|
end
|
236
236
|
|
237
237
|
# Returns a \host:\port string for this request, such as "example.com" or
|