actionpack 5.2.4.4 → 6.1.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 +264 -322
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/action_controller.rb +7 -4
- 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.rb +10 -8
- 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 +3 -5
- 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 +24 -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 +30 -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 +31 -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 +62 -34
- 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 +167 -58
- data/lib/action_controller/metal/url_for.rb +1 -1
- 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 +70 -65
- data/lib/action_dispatch.rb +9 -3
- data/lib/action_dispatch/http/cache.rb +26 -21
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +33 -19
- 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 +42 -23
- 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.rb +0 -2
- data/lib/action_dispatch/journey/formatter.rb +54 -30
- 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.rb +26 -34
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- 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/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 +121 -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 +3 -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 +3 -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 +10 -9
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing.rb +21 -20
- 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 +3 -3
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/system_test_case.rb +54 -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.rb +1 -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/integration.rb +61 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +38 -26
- 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
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
# Allows configuring default headers that will be automatically merged into
|
5
|
+
# each response.
|
6
|
+
module DefaultHeaders
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def make_response!(request)
|
11
|
+
ActionDispatch::Response.create.tap do |res|
|
12
|
+
res.request = request
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -26,10 +26,8 @@ module ActionController
|
|
26
26
|
included do
|
27
27
|
class_attribute :etag_with_template_digest, default: true
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
determine_template_etag(options) if etag_with_template_digest
|
32
|
-
end
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -51,7 +49,7 @@ module ActionController
|
|
51
49
|
end
|
52
50
|
|
53
51
|
def lookup_and_digest_template(template)
|
54
|
-
ActionView::Digestor.digest name: template, finder: lookup_context
|
52
|
+
ActionView::Digestor.digest name: template, format: nil, finder: lookup_context
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
@@ -22,12 +22,45 @@ module ActionController
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
class
|
25
|
+
class UrlGenerationError < ActionControllerError #:nodoc:
|
26
|
+
attr_reader :routes, :route_name, :method_name
|
27
|
+
|
28
|
+
def initialize(message, routes = nil, route_name = nil, method_name = nil)
|
29
|
+
@routes = routes
|
30
|
+
@route_name = route_name
|
31
|
+
@method_name = method_name
|
32
|
+
|
33
|
+
super(message)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Correction
|
37
|
+
def initialize(error)
|
38
|
+
@error = error
|
39
|
+
end
|
40
|
+
|
41
|
+
def corrections
|
42
|
+
if @error.method_name
|
43
|
+
maybe_these = @error.routes.named_routes.helper_names.grep(/#{@error.route_name}/)
|
44
|
+
maybe_these -= [@error.method_name.to_s] # remove exact match
|
45
|
+
|
46
|
+
maybe_these.sort_by { |n|
|
47
|
+
DidYouMean::Jaro.distance(@error.route_name, n)
|
48
|
+
}.reverse.first(4)
|
49
|
+
else
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
56
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
57
|
+
DidYouMean.correct_error(self, Correction)
|
58
|
+
end
|
26
59
|
end
|
27
60
|
|
28
61
|
class MethodNotAllowed < ActionControllerError #:nodoc:
|
29
62
|
def initialize(*allowed_methods)
|
30
|
-
super("Only #{allowed_methods.to_sentence
|
63
|
+
super("Only #{allowed_methods.to_sentence} requests are allowed.")
|
31
64
|
end
|
32
65
|
end
|
33
66
|
|
@@ -50,4 +83,25 @@ module ActionController
|
|
50
83
|
|
51
84
|
class UnknownFormat < ActionControllerError #:nodoc:
|
52
85
|
end
|
86
|
+
|
87
|
+
# Raised when a nested respond_to is triggered and the content types of each
|
88
|
+
# are incompatible. For example:
|
89
|
+
#
|
90
|
+
# respond_to do |outer_type|
|
91
|
+
# outer_type.js do
|
92
|
+
# respond_to do |inner_type|
|
93
|
+
# inner_type.html { render body: "HTML" }
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
class RespondToMismatchError < ActionControllerError
|
98
|
+
DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
|
99
|
+
|
100
|
+
def initialize(message = nil)
|
101
|
+
super(message || DEFAULT_MESSAGE)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class MissingExactTemplate < UnknownFormat #:nodoc:
|
106
|
+
end
|
53
107
|
end
|
@@ -36,7 +36,7 @@ module ActionController #:nodoc:
|
|
36
36
|
define_method(type) do
|
37
37
|
request.flash[type]
|
38
38
|
end
|
39
|
-
helper_method
|
39
|
+
helper_method(type) if respond_to?(:helper_method)
|
40
40
|
|
41
41
|
self._flash_types += [type]
|
42
42
|
end
|
@@ -44,18 +44,18 @@ module ActionController #:nodoc:
|
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
47
|
-
def redirect_to(options = {},
|
47
|
+
def redirect_to(options = {}, response_options_and_flash = {}) #:doc:
|
48
48
|
self.class._flash_types.each do |flash_type|
|
49
|
-
if type =
|
49
|
+
if type = response_options_and_flash.delete(flash_type)
|
50
50
|
flash[flash_type] = type
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
if other_flashes =
|
54
|
+
if other_flashes = response_options_and_flash.delete(:flash)
|
55
55
|
flash.update(other_flashes)
|
56
56
|
end
|
57
57
|
|
58
|
-
super(options,
|
58
|
+
super(options, response_options_and_flash)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -29,19 +29,22 @@ module ActionController
|
|
29
29
|
content_type = options.delete(:content_type)
|
30
30
|
|
31
31
|
options.each do |key, value|
|
32
|
-
headers[key.to_s.
|
32
|
+
headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
|
33
33
|
end
|
34
34
|
|
35
35
|
self.status = status
|
36
36
|
self.location = url_for(location) if location
|
37
37
|
|
38
|
-
self.response_body = ""
|
39
|
-
|
40
38
|
if include_content?(response_code)
|
41
|
-
self.
|
39
|
+
unless self.media_type
|
40
|
+
self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
|
41
|
+
end
|
42
|
+
|
42
43
|
response.charset = false
|
43
44
|
end
|
44
45
|
|
46
|
+
self.response_body = ""
|
47
|
+
|
45
48
|
true
|
46
49
|
end
|
47
50
|
|
@@ -11,7 +11,12 @@ module ActionController
|
|
11
11
|
#
|
12
12
|
# In previous versions of \Rails the controller will include a helper which
|
13
13
|
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
|
14
|
-
# include <tt>MyHelper</tt>.
|
14
|
+
# include <tt>MyHelper</tt>. You can revert to the old behavior with the following:
|
15
|
+
#
|
16
|
+
# # config/application.rb
|
17
|
+
# class Application < Rails::Application
|
18
|
+
# config.action_controller.include_all_helpers = false
|
19
|
+
# end
|
15
20
|
#
|
16
21
|
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
17
22
|
# controller which inherits from it.
|
@@ -34,7 +39,7 @@ module ActionController
|
|
34
39
|
# end
|
35
40
|
# end
|
36
41
|
#
|
37
|
-
# Then, in any view rendered by <tt>
|
42
|
+
# Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called:
|
38
43
|
#
|
39
44
|
# <% @events.each do |event| -%>
|
40
45
|
# <p>
|
@@ -73,9 +78,14 @@ module ActionController
|
|
73
78
|
end
|
74
79
|
|
75
80
|
# Provides a proxy to access helper methods from outside the view.
|
81
|
+
#
|
82
|
+
# Note that the proxy is rendered under a different view context.
|
83
|
+
# This may cause incorrect behaviour with capture methods. Consider
|
84
|
+
# using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
|
85
|
+
# instead when using +capture+.
|
76
86
|
def helpers
|
77
87
|
@helper_proxy ||= begin
|
78
|
-
proxy = ActionView::Base.
|
88
|
+
proxy = ActionView::Base.empty
|
79
89
|
proxy.config = config.inheritable_copy
|
80
90
|
proxy.extend(_helpers)
|
81
91
|
end
|
@@ -100,8 +110,7 @@ module ActionController
|
|
100
110
|
# # => ["application", "chart", "rubygems"]
|
101
111
|
def all_helpers_from_path(path)
|
102
112
|
helpers = Array(path).flat_map do |_path|
|
103
|
-
|
104
|
-
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
|
113
|
+
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
|
105
114
|
names.sort!
|
106
115
|
end
|
107
116
|
helpers.uniq!
|
@@ -56,8 +56,9 @@ module ActionController
|
|
56
56
|
# In your integration tests, you can do something like this:
|
57
57
|
#
|
58
58
|
# def test_access_granted_from_xml
|
59
|
-
#
|
60
|
-
#
|
59
|
+
# authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
60
|
+
#
|
61
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
61
62
|
#
|
62
63
|
# assert_equal 200, status
|
63
64
|
# end
|
@@ -68,21 +69,22 @@ module ActionController
|
|
68
69
|
extend ActiveSupport::Concern
|
69
70
|
|
70
71
|
module ClassMethods
|
71
|
-
def http_basic_authenticate_with(
|
72
|
-
before_action(options
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
72
|
+
def http_basic_authenticate_with(name:, password:, realm: nil, **options)
|
73
|
+
before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
|
78
|
+
authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
|
79
|
+
# This comparison uses & so that it doesn't short circuit and
|
80
|
+
# uses `secure_compare` so that length information isn't leaked.
|
81
|
+
ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
|
82
|
+
ActiveSupport::SecurityUtils.secure_compare(given_password, password)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
84
|
-
def authenticate_or_request_with_http_basic(realm =
|
85
|
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
|
86
|
+
def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
|
87
|
+
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
|
86
88
|
end
|
87
89
|
|
88
90
|
def authenticate_with_http_basic(&login_procedure)
|
@@ -126,7 +128,7 @@ module ActionController
|
|
126
128
|
|
127
129
|
def authentication_request(controller, realm, message)
|
128
130
|
message ||= "HTTP Basic: Access denied.\n"
|
129
|
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'
|
131
|
+
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
|
130
132
|
controller.status = 401
|
131
133
|
controller.response_body = message
|
132
134
|
end
|
@@ -136,7 +138,7 @@ module ActionController
|
|
136
138
|
#
|
137
139
|
# === Simple \Digest example
|
138
140
|
#
|
139
|
-
# require
|
141
|
+
# require "digest/md5"
|
140
142
|
# class PostsController < ApplicationController
|
141
143
|
# REALM = "SuperSecret"
|
142
144
|
# USERS = {"dhh" => "secret", #plain text password
|
@@ -389,10 +391,9 @@ module ActionController
|
|
389
391
|
# In your integration tests, you can do something like this:
|
390
392
|
#
|
391
393
|
# def test_access_granted_from_xml
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
# )
|
394
|
+
# authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
|
395
|
+
#
|
396
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
396
397
|
#
|
397
398
|
# assert_equal 200, status
|
398
399
|
# end
|
@@ -474,7 +475,7 @@ module ActionController
|
|
474
475
|
|
475
476
|
# This removes the <tt>"</tt> characters wrapping the value.
|
476
477
|
def rewrite_param_values(array_params)
|
477
|
-
array_params.each { |param| (param[1] || ""
|
478
|
+
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
|
478
479
|
end
|
479
480
|
|
480
481
|
# This method takes an authorization body and splits up the key-value
|
@@ -483,7 +484,7 @@ module ActionController
|
|
483
484
|
def raw_params(auth)
|
484
485
|
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
485
486
|
|
486
|
-
if !
|
487
|
+
if !_raw_params.first.start_with?(TOKEN_KEY)
|
487
488
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
488
489
|
end
|
489
490
|
|
@@ -511,7 +512,7 @@ module ActionController
|
|
511
512
|
# Returns nothing.
|
512
513
|
def authentication_request(controller, realm, message = nil)
|
513
514
|
message ||= "HTTP Token: Access denied.\n"
|
514
|
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'
|
515
|
+
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
|
515
516
|
controller.__send__ :render, plain: message, status: :unauthorized
|
516
517
|
end
|
517
518
|
end
|
@@ -22,7 +22,7 @@ module ActionController
|
|
22
22
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
23
|
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
24
|
# you reasonably expect to have rendered a template, then we raise
|
25
|
-
# <tt>
|
25
|
+
# <tt>ActionController::MissingExactTemplate</tt> with an explanation.
|
26
26
|
#
|
27
27
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
28
|
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
@@ -30,9 +30,9 @@ module ActionController
|
|
30
30
|
# :stopdoc:
|
31
31
|
include BasicImplicitRender
|
32
32
|
|
33
|
-
def default_render
|
33
|
+
def default_render
|
34
34
|
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
|
35
|
-
render
|
35
|
+
render
|
36
36
|
elsif any_templates?(action_name.to_s, _prefixes)
|
37
37
|
message = "#{self.class.name}\##{action_name} is missing a template " \
|
38
38
|
"for this request format and variant.\n" \
|
@@ -41,18 +41,8 @@ module ActionController
|
|
41
41
|
|
42
42
|
raise ActionController::UnknownFormat, message
|
43
43
|
elsif interactive_browser_request?
|
44
|
-
message = "#{self.class.name}\##{action_name} is missing a template "
|
45
|
-
|
46
|
-
"request.formats: #{request.formats.map(&:to_s).inspect}\n" \
|
47
|
-
"request.variant: #{request.variant.inspect}\n\n" \
|
48
|
-
"NOTE! For XHR/Ajax or API requests, this action would normally " \
|
49
|
-
"respond with 204 No Content: an empty white screen. Since you're " \
|
50
|
-
"loading it in a web browser, we assume that you expected to " \
|
51
|
-
"actually render a template, not nothing, so we're showing an " \
|
52
|
-
"error to be extra-clear. If you expect 204 No Content, carry on. " \
|
53
|
-
"That's what you'll get from an XHR or API request. Give it a shot."
|
54
|
-
|
55
|
-
raise ActionController::UnknownFormat, message
|
44
|
+
message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
|
45
|
+
raise ActionController::MissingExactTemplate, message
|
56
46
|
else
|
57
47
|
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
|
58
48
|
super
|
@@ -16,10 +16,11 @@ module ActionController
|
|
16
16
|
|
17
17
|
attr_internal :view_runtime
|
18
18
|
|
19
|
-
def process_action(*
|
19
|
+
def process_action(*)
|
20
20
|
raw_payload = {
|
21
21
|
controller: self.class.name,
|
22
22
|
action: action_name,
|
23
|
+
request: request,
|
23
24
|
params: request.filtered_parameters,
|
24
25
|
headers: request.headers,
|
25
26
|
format: request.format.ref,
|
@@ -27,20 +28,19 @@ module ActionController
|
|
27
28
|
path: request.fullpath
|
28
29
|
}
|
29
30
|
|
30
|
-
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload
|
31
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
31
32
|
|
32
33
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
34
|
+
result = super
|
35
|
+
payload[:response] = response
|
36
|
+
payload[:status] = response.status
|
37
|
+
result
|
38
|
+
ensure
|
39
|
+
append_info_to_payload(payload)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def render(*
|
43
|
+
def render(*)
|
44
44
|
render_output = nil
|
45
45
|
self.view_runtime = cleanup_view_runtime do
|
46
46
|
Benchmark.ms { render_output = super }
|
@@ -61,8 +61,8 @@ module ActionController
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
def redirect_to(*
|
65
|
-
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
|
64
|
+
def redirect_to(*)
|
65
|
+
ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
|
66
66
|
result = super
|
67
67
|
payload[:status] = response.status
|
68
68
|
payload[:location] = response.filtered_location
|
@@ -71,9 +71,8 @@ module ActionController
|
|
71
71
|
end
|
72
72
|
|
73
73
|
private
|
74
|
-
|
75
74
|
# A hook invoked every time a before callback is halted.
|
76
|
-
def halted_callback_hook(filter)
|
75
|
+
def halted_callback_hook(filter, _)
|
77
76
|
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
78
77
|
end
|
79
78
|
|
@@ -86,7 +86,7 @@ module ActionController
|
|
86
86
|
# Note: SSEs are not currently supported by IE. However, they are supported
|
87
87
|
# by Chrome, Firefox, Opera, and Safari.
|
88
88
|
class SSE
|
89
|
-
|
89
|
+
PERMITTED_OPTIONS = %w( retry event id )
|
90
90
|
|
91
91
|
def initialize(stream, options = {})
|
92
92
|
@stream = stream
|
@@ -107,17 +107,16 @@ module ActionController
|
|
107
107
|
end
|
108
108
|
|
109
109
|
private
|
110
|
-
|
111
110
|
def perform_write(json, options)
|
112
111
|
current_options = @options.merge(options).stringify_keys
|
113
112
|
|
114
|
-
|
113
|
+
PERMITTED_OPTIONS.each do |option_name|
|
115
114
|
if (option_value = current_options[option_name])
|
116
115
|
@stream.write "#{option_name}: #{option_value}\n"
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
120
|
-
message = json.gsub("\n"
|
119
|
+
message = json.gsub("\n", "\ndata: ")
|
121
120
|
@stream.write "data: #{message}\n\n"
|
122
121
|
end
|
123
122
|
end
|
@@ -137,16 +136,16 @@ module ActionController
|
|
137
136
|
attr_accessor :ignore_disconnect
|
138
137
|
|
139
138
|
def initialize(response)
|
139
|
+
super(response, SizedQueue.new(10))
|
140
140
|
@error_callback = lambda { true }
|
141
141
|
@cv = new_cond
|
142
142
|
@aborted = false
|
143
143
|
@ignore_disconnect = false
|
144
|
-
super(response, SizedQueue.new(10))
|
145
144
|
end
|
146
145
|
|
147
146
|
def write(string)
|
148
147
|
unless @response.committed?
|
149
|
-
@response.
|
148
|
+
@response.headers["Cache-Control"] ||= "no-cache"
|
150
149
|
@response.delete_header "Content-Length"
|
151
150
|
end
|
152
151
|
|
@@ -205,7 +204,6 @@ module ActionController
|
|
205
204
|
end
|
206
205
|
|
207
206
|
private
|
208
|
-
|
209
207
|
def each_chunk(&block)
|
210
208
|
loop do
|
211
209
|
str = nil
|
@@ -220,7 +218,6 @@ module ActionController
|
|
220
218
|
|
221
219
|
class Response < ActionDispatch::Response #:nodoc: all
|
222
220
|
private
|
223
|
-
|
224
221
|
def before_committed
|
225
222
|
super
|
226
223
|
jar = request.cookie_jar
|
@@ -280,33 +277,34 @@ module ActionController
|
|
280
277
|
raise error if error
|
281
278
|
end
|
282
279
|
|
283
|
-
# Spawn a new thread to serve up the controller in. This is to get
|
284
|
-
# around the fact that Rack isn't based around IOs and we need to use
|
285
|
-
# a thread to stream data from the response bodies. Nobody should call
|
286
|
-
# this method except in Rails internals. Seriously!
|
287
|
-
def new_controller_thread # :nodoc:
|
288
|
-
Thread.new {
|
289
|
-
t2 = Thread.current
|
290
|
-
t2.abort_on_exception = true
|
291
|
-
yield
|
292
|
-
}
|
293
|
-
end
|
294
|
-
|
295
|
-
def log_error(exception)
|
296
|
-
logger = ActionController::Base.logger
|
297
|
-
return unless logger
|
298
|
-
|
299
|
-
logger.fatal do
|
300
|
-
message = "\n#{exception.class} (#{exception.message}):\n".dup
|
301
|
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
|
302
|
-
message << " " << exception.backtrace.join("\n ")
|
303
|
-
"#{message}\n\n"
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
280
|
def response_body=(body)
|
308
281
|
super
|
309
282
|
response.close if response
|
310
283
|
end
|
284
|
+
|
285
|
+
private
|
286
|
+
# Spawn a new thread to serve up the controller in. This is to get
|
287
|
+
# around the fact that Rack isn't based around IOs and we need to use
|
288
|
+
# a thread to stream data from the response bodies. Nobody should call
|
289
|
+
# this method except in Rails internals. Seriously!
|
290
|
+
def new_controller_thread # :nodoc:
|
291
|
+
Thread.new {
|
292
|
+
t2 = Thread.current
|
293
|
+
t2.abort_on_exception = true
|
294
|
+
yield
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
298
|
+
def log_error(exception)
|
299
|
+
logger = ActionController::Base.logger
|
300
|
+
return unless logger
|
301
|
+
|
302
|
+
logger.fatal do
|
303
|
+
message = +"\n#{exception.class} (#{exception.message}):\n"
|
304
|
+
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
|
305
|
+
message << " " << exception.backtrace.join("\n ")
|
306
|
+
"#{message}\n\n"
|
307
|
+
end
|
308
|
+
end
|
311
309
|
end
|
312
310
|
end
|