actionpack 7.0.8.1 → 7.2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +94 -500
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +119 -106
- data/lib/abstract_controller/caching/fragments.rb +51 -52
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +94 -67
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +121 -91
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +14 -13
- data/lib/abstract_controller/translation.rb +12 -30
- data/lib/abstract_controller/url_for.rb +9 -5
- data/lib/abstract_controller.rb +8 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +78 -73
- data/lib/action_controller/base.rb +199 -141
- data/lib/action_controller/caching.rb +16 -11
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +21 -16
- data/lib/action_controller/log_subscriber.rb +19 -5
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +187 -174
- data/lib/action_controller/metal/content_security_policy.rb +26 -25
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +65 -54
- data/lib/action_controller/metal/default_headers.rb +6 -2
- data/lib/action_controller/metal/etag_with_flash.rb +4 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +18 -14
- data/lib/action_controller/metal/exceptions.rb +19 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +20 -16
- data/lib/action_controller/metal/helpers.rb +64 -67
- data/lib/action_controller/metal/http_authentication.rb +214 -200
- data/lib/action_controller/metal/implicit_render.rb +21 -17
- data/lib/action_controller/metal/instrumentation.rb +22 -12
- data/lib/action_controller/metal/live.rb +125 -92
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +58 -58
- data/lib/action_controller/metal/permissions_policy.rb +14 -13
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +110 -84
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -82
- data/lib/action_controller/metal/request_forgery_protection.rb +279 -161
- data/lib/action_controller/metal/rescue.rb +12 -8
- data/lib/action_controller/metal/streaming.rb +174 -132
- data/lib/action_controller/metal/strong_parameters.rb +598 -473
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +23 -14
- data/lib/action_controller/metal.rb +145 -61
- data/lib/action_controller/railtie.rb +25 -9
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +105 -66
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +157 -128
- data/lib/action_controller.rb +17 -3
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +28 -29
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -49
- data/lib/action_dispatch/http/filter_parameters.rb +27 -12
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +23 -21
- data/lib/action_dispatch/http/mime_negotiation.rb +37 -48
- data/lib/action_dispatch/http/mime_type.rb +60 -30
- data/lib/action_dispatch/http/mime_types.rb +5 -1
- data/lib/action_dispatch/http/parameters.rb +12 -10
- data/lib/action_dispatch/http/permissions_policy.rb +32 -34
- data/lib/action_dispatch/http/rack_cache.rb +4 -0
- data/lib/action_dispatch/http/request.rb +132 -79
- data/lib/action_dispatch/http/response.rb +136 -103
- data/lib/action_dispatch/http/upload.rb +19 -15
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +19 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +18 -15
- data/lib/action_dispatch/journey/route.rb +12 -9
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +13 -10
- data/lib/action_dispatch/journey/routes.rb +6 -4
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +7 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +4 -0
- data/lib/action_dispatch/middleware/cookies.rb +192 -194
- data/lib/action_dispatch/middleware/debug_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/debug_locks.rb +18 -13
- data/lib/action_dispatch/middleware/debug_view.rb +9 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
- data/lib/action_dispatch/middleware/executor.rb +9 -1
- data/lib/action_dispatch/middleware/flash.rb +65 -46
- data/lib/action_dispatch/middleware/host_authorization.rb +22 -17
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -8
- data/lib/action_dispatch/middleware/reloader.rb +9 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +88 -83
- data/lib/action_dispatch/middleware/request_id.rb +15 -8
- data/lib/action_dispatch/middleware/server_timing.rb +8 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +7 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +14 -7
- data/lib/action_dispatch/middleware/session/cookie_store.rb +32 -25
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +42 -28
- data/lib/action_dispatch/middleware/ssl.rb +60 -45
- data/lib/action_dispatch/middleware/stack.rb +15 -9
- data/lib/action_dispatch/middleware/static.rb +40 -34
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +47 -38
- data/lib/action_dispatch/railtie.rb +12 -4
- data/lib/action_dispatch/request/session.rb +39 -27
- data/lib/action_dispatch/request/utils.rb +10 -3
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +59 -9
- data/lib/action_dispatch/routing/mapper.rb +686 -639
- data/lib/action_dispatch/routing/polymorphic_routes.rb +70 -61
- data/lib/action_dispatch/routing/redirection.rb +52 -38
- data/lib/action_dispatch/routing/route_set.rb +106 -62
- data/lib/action_dispatch/routing/routes_proxy.rb +16 -19
- data/lib/action_dispatch/routing/url_for.rb +131 -122
- data/lib/action_dispatch/routing.rb +152 -150
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +27 -19
- data/lib/action_dispatch/system_testing/driver.rb +16 -22
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +53 -31
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +9 -7
- data/lib/action_dispatch/testing/assertions/response.rb +36 -26
- data/lib/action_dispatch/testing/assertions/routing.rb +203 -95
- data/lib/action_dispatch/testing/assertions.rb +5 -1
- data/lib/action_dispatch/testing/integration.rb +240 -229
- data/lib/action_dispatch/testing/request_encoder.rb +6 -1
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +14 -9
- data/lib/action_dispatch/testing/test_request.rb +4 -2
- data/lib/action_dispatch/testing/test_response.rb +34 -19
- data/lib/action_dispatch.rb +52 -21
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +18 -17
- metadata +91 -32
@@ -1,13 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "action_dispatch/middleware/exception_wrapper"
|
4
6
|
require "action_dispatch/routing/inspector"
|
5
7
|
|
6
8
|
require "action_view"
|
7
9
|
|
8
10
|
module ActionDispatch
|
9
|
-
#
|
10
|
-
#
|
11
|
+
# # Action Dispatch DebugExceptions
|
12
|
+
#
|
13
|
+
# This middleware is responsible for logging exceptions and showing a debugging
|
14
|
+
# page in case the request is local.
|
11
15
|
class DebugExceptions
|
12
16
|
cattr_reader :interceptors, instance_accessor: false, default: []
|
13
17
|
|
@@ -24,26 +28,26 @@ module ActionDispatch
|
|
24
28
|
end
|
25
29
|
|
26
30
|
def call(env)
|
27
|
-
request = ActionDispatch::Request.new env
|
28
31
|
_, headers, body = response = @app.call(env)
|
29
32
|
|
30
|
-
if headers[
|
33
|
+
if headers[Constants::X_CASCADE] == "pass"
|
31
34
|
body.close if body.respond_to?(:close)
|
32
35
|
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
33
36
|
end
|
34
37
|
|
35
38
|
response
|
36
39
|
rescue Exception => exception
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
request = ActionDispatch::Request.new env
|
41
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
42
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
43
|
+
|
44
|
+
invoke_interceptors(request, exception, wrapper)
|
45
|
+
raise exception unless wrapper.show?(request)
|
46
|
+
render_exception(request, exception, wrapper)
|
40
47
|
end
|
41
48
|
|
42
49
|
private
|
43
|
-
def invoke_interceptors(request, exception)
|
44
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
45
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
46
|
-
|
50
|
+
def invoke_interceptors(request, exception, wrapper)
|
47
51
|
@interceptors.each do |interceptor|
|
48
52
|
interceptor.call(request, exception)
|
49
53
|
rescue Exception
|
@@ -51,9 +55,7 @@ module ActionDispatch
|
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
|
-
def render_exception(request, exception)
|
55
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
56
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
58
|
+
def render_exception(request, exception, wrapper)
|
57
59
|
log_error(request, wrapper)
|
58
60
|
|
59
61
|
if request.get_header("action_dispatch.show_detailed_exceptions")
|
@@ -94,7 +96,7 @@ module ActionDispatch
|
|
94
96
|
wrapper.status_code,
|
95
97
|
Rack::Utils::HTTP_STATUS_CODES[500]
|
96
98
|
),
|
97
|
-
exception: wrapper.
|
99
|
+
exception: wrapper.exception_inspect,
|
98
100
|
traces: wrapper.traces
|
99
101
|
}
|
100
102
|
|
@@ -115,19 +117,19 @@ module ActionDispatch
|
|
115
117
|
DebugView.new(
|
116
118
|
request: request,
|
117
119
|
exception_wrapper: wrapper,
|
120
|
+
# Everything should use the wrapper, but we need to pass `exception` for legacy
|
121
|
+
# code.
|
118
122
|
exception: wrapper.exception,
|
119
123
|
traces: wrapper.traces,
|
120
124
|
show_source_idx: wrapper.source_to_show_id,
|
121
125
|
trace_to_show: wrapper.trace_to_show,
|
122
|
-
routes_inspector: routes_inspector(wrapper
|
126
|
+
routes_inspector: routes_inspector(wrapper),
|
123
127
|
source_extracts: wrapper.source_extracts,
|
124
|
-
line_number: wrapper.line_number,
|
125
|
-
file: wrapper.file
|
126
128
|
)
|
127
129
|
end
|
128
130
|
|
129
131
|
def render(status, body, format)
|
130
|
-
[status, {
|
132
|
+
[status, { Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
131
133
|
end
|
132
134
|
|
133
135
|
def log_error(request, wrapper)
|
@@ -136,26 +138,33 @@ module ActionDispatch
|
|
136
138
|
return unless logger
|
137
139
|
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
140
|
|
139
|
-
exception = wrapper.exception
|
140
141
|
trace = wrapper.exception_trace
|
141
142
|
|
142
143
|
message = []
|
143
144
|
message << " "
|
144
|
-
message << "#{
|
145
|
-
|
145
|
+
message << "#{wrapper.exception_class_name} (#{wrapper.message}):"
|
146
|
+
if wrapper.has_cause?
|
147
|
+
message << "\nCauses:"
|
148
|
+
wrapper.wrapped_causes.each do |wrapped_cause|
|
149
|
+
message << "#{wrapped_cause.exception_class_name} (#{wrapped_cause.message})"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
message.concat(wrapper.annotated_source_code)
|
146
153
|
message << " "
|
147
154
|
message.concat(trace)
|
148
155
|
|
149
|
-
log_array(logger, message)
|
156
|
+
log_array(logger, message, request)
|
150
157
|
end
|
151
158
|
|
152
|
-
def log_array(logger, lines)
|
159
|
+
def log_array(logger, lines, request)
|
153
160
|
return if lines.empty?
|
154
161
|
|
162
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
163
|
+
|
155
164
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
156
|
-
logger.
|
165
|
+
logger.add(level, lines.join("\n#{logger.formatter.tags_text}"))
|
157
166
|
else
|
158
|
-
logger.
|
167
|
+
logger.add(level, lines.join("\n"))
|
159
168
|
end
|
160
169
|
end
|
161
170
|
|
@@ -168,7 +177,7 @@ module ActionDispatch
|
|
168
177
|
end
|
169
178
|
|
170
179
|
def routes_inspector(exception)
|
171
|
-
if @routes_app.respond_to?(:routes) && (exception.
|
180
|
+
if @routes_app.respond_to?(:routes) && (exception.routing_error? || exception.template_error?)
|
172
181
|
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
|
173
182
|
end
|
174
183
|
end
|
@@ -1,17 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
6
|
+
# # Action Dispatch DebugLocks
|
7
|
+
#
|
4
8
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
5
9
|
#
|
6
10
|
# To use it, insert it near the top of the middleware stack, using
|
7
|
-
#
|
11
|
+
# `config/application.rb`:
|
8
12
|
#
|
9
13
|
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
10
14
|
#
|
11
|
-
# After restarting the application and re-triggering the deadlock condition,
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
+
# After restarting the application and re-triggering the deadlock condition, the
|
16
|
+
# route `/rails/locks` will show a summary of all threads currently known to the
|
17
|
+
# interlock, which lock level they are holding or awaiting, and their current
|
18
|
+
# backtrace.
|
15
19
|
#
|
16
20
|
# Generally a deadlock will be caused by the interlock conflicting with some
|
17
21
|
# other external lock or blocking I/O call. These cannot be automatically
|
@@ -44,14 +48,14 @@ module ActionDispatch
|
|
44
48
|
private
|
45
49
|
def render_details(req)
|
46
50
|
threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
|
47
|
-
# The Interlock itself comes to a complete halt as long as this block
|
48
|
-
#
|
49
|
-
#
|
51
|
+
# The Interlock itself comes to a complete halt as long as this block is
|
52
|
+
# executing. That gives us a more consistent picture of everything, but creates
|
53
|
+
# a pretty strong Observer Effect.
|
50
54
|
#
|
51
|
-
# Most directly, that means we need to do as little as possible in
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
+
# Most directly, that means we need to do as little as possible in this block.
|
56
|
+
# More widely, it means this middleware should remain a strictly diagnostic tool
|
57
|
+
# (to be used when something has gone wrong), and not for any sort of general
|
58
|
+
# monitoring.
|
55
59
|
|
56
60
|
raw_threads.each.with_index do |(thread, info), idx|
|
57
61
|
info[:index] = idx
|
@@ -97,7 +101,8 @@ module ActionDispatch
|
|
97
101
|
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
98
102
|
end.join("\n\n---\n\n\n")
|
99
103
|
|
100
|
-
[200, {
|
104
|
+
[200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
|
105
|
+
Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
|
101
106
|
end
|
102
107
|
|
103
108
|
def blocked_by?(victim, blocker, all_threads)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "pp"
|
4
6
|
|
5
7
|
require "action_view"
|
@@ -7,18 +9,23 @@ require "action_view/base"
|
|
7
9
|
|
8
10
|
module ActionDispatch
|
9
11
|
class DebugView < ActionView::Base # :nodoc:
|
10
|
-
|
12
|
+
RESCUES_TEMPLATE_PATHS = [File.expand_path("templates", __dir__)]
|
11
13
|
|
12
14
|
def initialize(assigns)
|
13
|
-
paths =
|
15
|
+
paths = RESCUES_TEMPLATE_PATHS.dup
|
14
16
|
lookup_context = ActionView::LookupContext.new(paths)
|
15
17
|
super(lookup_context, assigns, nil)
|
18
|
+
@exception_wrapper = assigns[:exception_wrapper]
|
16
19
|
end
|
17
20
|
|
18
21
|
def compiled_method_container
|
19
22
|
self.class
|
20
23
|
end
|
21
24
|
|
25
|
+
def error_highlight_available?
|
26
|
+
@exception_wrapper.error_highlight_available?
|
27
|
+
end
|
28
|
+
|
22
29
|
def debug_params(params)
|
23
30
|
clean_params = params.clone
|
24
31
|
clean_params.delete("action")
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/module/attribute_accessors"
|
6
|
+
require "active_support/syntax_error_proxy"
|
7
|
+
require "active_support/core_ext/thread/backtrace/location"
|
4
8
|
require "rack/utils"
|
5
9
|
|
6
10
|
module ActionDispatch
|
@@ -41,22 +45,76 @@ module ActionDispatch
|
|
41
45
|
"ActionDispatch::Http::MimeNegotiation::InvalidType"
|
42
46
|
]
|
43
47
|
|
44
|
-
attr_reader :backtrace_cleaner, :
|
48
|
+
attr_reader :backtrace_cleaner, :wrapped_causes, :exception_class_name, :exception
|
45
49
|
|
46
50
|
def initialize(backtrace_cleaner, exception)
|
47
51
|
@backtrace_cleaner = backtrace_cleaner
|
48
|
-
@
|
49
|
-
@exception_class_name = @exception.class.name
|
52
|
+
@exception_class_name = exception.class.name
|
50
53
|
@wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
|
54
|
+
@exception = exception
|
55
|
+
if exception.is_a?(SyntaxError)
|
56
|
+
@exception = ActiveSupport::SyntaxErrorProxy.new(exception)
|
57
|
+
end
|
58
|
+
@backtrace = build_backtrace
|
59
|
+
end
|
60
|
+
|
61
|
+
def routing_error?
|
62
|
+
@exception.is_a?(ActionController::RoutingError)
|
63
|
+
end
|
64
|
+
|
65
|
+
def template_error?
|
66
|
+
@exception.is_a?(ActionView::Template::Error)
|
67
|
+
end
|
68
|
+
|
69
|
+
def sub_template_message
|
70
|
+
@exception.sub_template_message
|
71
|
+
end
|
72
|
+
|
73
|
+
def has_cause?
|
74
|
+
@exception.cause
|
75
|
+
end
|
76
|
+
|
77
|
+
def failures
|
78
|
+
@exception.failures
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_corrections?
|
82
|
+
@exception.respond_to?(:original_message) && @exception.respond_to?(:corrections)
|
83
|
+
end
|
84
|
+
|
85
|
+
def original_message
|
86
|
+
@exception.original_message
|
87
|
+
end
|
88
|
+
|
89
|
+
def corrections
|
90
|
+
@exception.corrections
|
91
|
+
end
|
51
92
|
|
52
|
-
|
93
|
+
def file_name
|
94
|
+
@exception.file_name
|
95
|
+
end
|
96
|
+
|
97
|
+
def line_number
|
98
|
+
@exception.line_number
|
99
|
+
end
|
100
|
+
|
101
|
+
def actions
|
102
|
+
ActiveSupport::ActionableError.actions(@exception)
|
53
103
|
end
|
54
104
|
|
55
105
|
def unwrapped_exception
|
56
106
|
if wrapper_exceptions.include?(@exception_class_name)
|
57
|
-
exception.cause
|
107
|
+
@exception.cause
|
58
108
|
else
|
59
|
-
exception
|
109
|
+
@exception
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def annotated_source_code
|
114
|
+
if exception.respond_to?(:annotated_source_code)
|
115
|
+
exception.annotated_source_code
|
116
|
+
else
|
117
|
+
[]
|
60
118
|
end
|
61
119
|
end
|
62
120
|
|
@@ -118,21 +176,37 @@ module ActionDispatch
|
|
118
176
|
Rack::Utils.status_code(@@rescue_responses[class_name])
|
119
177
|
end
|
120
178
|
|
179
|
+
def show?(request)
|
180
|
+
# We're treating `nil` as "unset", and we want the default setting to be `:all`.
|
181
|
+
# This logic should be extracted to `env_config` and calculated once.
|
182
|
+
config = request.get_header("action_dispatch.show_exceptions")
|
183
|
+
|
184
|
+
case config
|
185
|
+
when :none
|
186
|
+
false
|
187
|
+
when :rescuable
|
188
|
+
rescue_response?
|
189
|
+
else
|
190
|
+
true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
121
194
|
def rescue_response?
|
122
195
|
@@rescue_responses.key?(exception.class.name)
|
123
196
|
end
|
124
197
|
|
125
198
|
def source_extracts
|
126
199
|
backtrace.map do |trace|
|
127
|
-
|
128
|
-
|
129
|
-
{
|
130
|
-
code: source_fragment(file, line_number),
|
131
|
-
line_number: line_number
|
132
|
-
}
|
200
|
+
extract_source(trace)
|
133
201
|
end
|
134
202
|
end
|
135
203
|
|
204
|
+
def error_highlight_available?
|
205
|
+
# ErrorHighlight.spot with backtrace_location keyword is available since
|
206
|
+
# error_highlight 0.4.0
|
207
|
+
defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
|
208
|
+
end
|
209
|
+
|
136
210
|
def trace_to_show
|
137
211
|
if traces["Application Trace"].empty? && rescue_template != "routing_error"
|
138
212
|
"Full Trace"
|
@@ -145,9 +219,65 @@ module ActionDispatch
|
|
145
219
|
(traces[trace_to_show].first || {})[:id]
|
146
220
|
end
|
147
221
|
|
222
|
+
def exception_name
|
223
|
+
exception.cause.class.to_s
|
224
|
+
end
|
225
|
+
|
226
|
+
def message
|
227
|
+
exception.message
|
228
|
+
end
|
229
|
+
|
230
|
+
def exception_inspect
|
231
|
+
exception.inspect
|
232
|
+
end
|
233
|
+
|
234
|
+
def exception_id
|
235
|
+
exception.object_id
|
236
|
+
end
|
237
|
+
|
148
238
|
private
|
149
|
-
|
150
|
-
|
239
|
+
class SourceMapLocation < DelegateClass(Thread::Backtrace::Location) # :nodoc:
|
240
|
+
def initialize(location, template)
|
241
|
+
super(location)
|
242
|
+
@template = template
|
243
|
+
end
|
244
|
+
|
245
|
+
def spot(exc)
|
246
|
+
if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) && __getobj__.is_a?(Thread::Backtrace::Location)
|
247
|
+
location = @template.spot(__getobj__)
|
248
|
+
else
|
249
|
+
location = super
|
250
|
+
end
|
251
|
+
|
252
|
+
if location
|
253
|
+
@template.translate_location(__getobj__, location)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
attr_reader :backtrace
|
259
|
+
|
260
|
+
def build_backtrace
|
261
|
+
built_methods = {}
|
262
|
+
|
263
|
+
ActionView::PathRegistry.all_resolvers.each do |resolver|
|
264
|
+
resolver.built_templates.each do |template|
|
265
|
+
built_methods[template.method_name] = template
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
(@exception.backtrace_locations || []).map do |loc|
|
270
|
+
if built_methods.key?(loc.label.to_s)
|
271
|
+
thread_backtrace_location = if loc.respond_to?(:__getobj__)
|
272
|
+
loc.__getobj__
|
273
|
+
else
|
274
|
+
loc
|
275
|
+
end
|
276
|
+
SourceMapLocation.new(thread_backtrace_location, built_methods[loc.label.to_s])
|
277
|
+
else
|
278
|
+
loc
|
279
|
+
end
|
280
|
+
end
|
151
281
|
end
|
152
282
|
|
153
283
|
def causes_for(exception)
|
@@ -168,29 +298,53 @@ module ActionDispatch
|
|
168
298
|
end
|
169
299
|
end
|
170
300
|
|
301
|
+
def extract_source(trace)
|
302
|
+
spot = trace.spot(@exception)
|
303
|
+
|
304
|
+
if spot
|
305
|
+
line = spot[:first_lineno]
|
306
|
+
code = extract_source_fragment_lines(spot[:script_lines], line)
|
307
|
+
|
308
|
+
if line == spot[:last_lineno]
|
309
|
+
code[line] = [
|
310
|
+
code[line][0, spot[:first_column]],
|
311
|
+
code[line][spot[:first_column]...spot[:last_column]],
|
312
|
+
code[line][spot[:last_column]..-1],
|
313
|
+
]
|
314
|
+
end
|
315
|
+
|
316
|
+
return {
|
317
|
+
code: code,
|
318
|
+
line_number: line
|
319
|
+
}
|
320
|
+
end
|
321
|
+
|
322
|
+
file, line_number = extract_file_and_line_number(trace)
|
323
|
+
|
324
|
+
{
|
325
|
+
code: source_fragment(file, line_number),
|
326
|
+
line_number: line_number
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
330
|
+
def extract_source_fragment_lines(source_lines, line)
|
331
|
+
start = [line - 3, 0].max
|
332
|
+
lines = source_lines.drop(start).take(6)
|
333
|
+
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
334
|
+
end
|
335
|
+
|
171
336
|
def source_fragment(path, line)
|
172
337
|
return unless Rails.respond_to?(:root) && Rails.root
|
173
338
|
full_path = Rails.root.join(path)
|
174
339
|
if File.exist?(full_path)
|
175
340
|
File.open(full_path, "r") do |file|
|
176
|
-
|
177
|
-
lines = file.each_line.drop(start).take(6)
|
178
|
-
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
341
|
+
extract_source_fragment_lines(file.each_line, line)
|
179
342
|
end
|
180
343
|
end
|
181
344
|
end
|
182
345
|
|
183
346
|
def extract_file_and_line_number(trace)
|
184
|
-
|
185
|
-
# Windows and Unix path styles.
|
186
|
-
file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
|
187
|
-
[file, line.to_i]
|
188
|
-
end
|
189
|
-
|
190
|
-
def expand_backtrace
|
191
|
-
@exception.backtrace.unshift(
|
192
|
-
@exception.to_s.split("\n")
|
193
|
-
).flatten!
|
347
|
+
[trace.path, trace.lineno]
|
194
348
|
end
|
195
349
|
end
|
196
350
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "rack/body_proxy"
|
4
6
|
|
5
7
|
module ActionDispatch
|
@@ -12,9 +14,15 @@ module ActionDispatch
|
|
12
14
|
state = @executor.run!(reset: true)
|
13
15
|
begin
|
14
16
|
response = @app.call(env)
|
17
|
+
|
18
|
+
if env["action_dispatch.report_exception"]
|
19
|
+
error = env["action_dispatch.exception"]
|
20
|
+
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
|
21
|
+
end
|
22
|
+
|
15
23
|
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
16
24
|
rescue => error
|
17
|
-
@executor.error_reporter.report(error, handled: false)
|
25
|
+
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
|
18
26
|
raise
|
19
27
|
ensure
|
20
28
|
state.complete! unless returned
|