actionpack 4.2.10 → 7.2.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- 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/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- 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 +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -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 +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- 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 +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,110 +1,193 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "action_dispatch/middleware/exception_wrapper"
|
6
|
+
require "action_dispatch/routing/inspector"
|
7
|
+
|
8
|
+
require "action_view"
|
4
9
|
|
5
10
|
module ActionDispatch
|
6
|
-
#
|
7
|
-
#
|
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.
|
8
15
|
class DebugExceptions
|
9
|
-
|
16
|
+
cattr_reader :interceptors, instance_accessor: false, default: []
|
10
17
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
18
|
+
def self.register_interceptor(object = nil, &block)
|
19
|
+
interceptor = object || block
|
20
|
+
interceptors << interceptor
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(app, routes_app = nil, response_format = :default, interceptors = self.class.interceptors)
|
24
|
+
@app = app
|
25
|
+
@routes_app = routes_app
|
26
|
+
@response_format = response_format
|
27
|
+
@interceptors = interceptors
|
14
28
|
end
|
15
29
|
|
16
30
|
def call(env)
|
17
31
|
_, headers, body = response = @app.call(env)
|
18
32
|
|
19
|
-
if headers[
|
33
|
+
if headers[Constants::X_CASCADE] == "pass"
|
20
34
|
body.close if body.respond_to?(:close)
|
21
35
|
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
22
36
|
end
|
23
37
|
|
24
38
|
response
|
25
39
|
rescue Exception => exception
|
26
|
-
|
27
|
-
|
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)
|
28
47
|
end
|
29
48
|
|
30
49
|
private
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
if env['action_dispatch.show_detailed_exceptions']
|
37
|
-
request = Request.new(env)
|
38
|
-
traces = wrapper.traces
|
39
|
-
|
40
|
-
trace_to_show = 'Application Trace'
|
41
|
-
if traces[trace_to_show].empty? && wrapper.rescue_template != 'routing_error'
|
42
|
-
trace_to_show = 'Full Trace'
|
50
|
+
def invoke_interceptors(request, exception, wrapper)
|
51
|
+
@interceptors.each do |interceptor|
|
52
|
+
interceptor.call(request, exception)
|
53
|
+
rescue Exception
|
54
|
+
log_error(request, wrapper)
|
43
55
|
end
|
56
|
+
end
|
44
57
|
|
45
|
-
|
46
|
-
|
58
|
+
def render_exception(request, exception, wrapper)
|
59
|
+
log_error(request, wrapper)
|
60
|
+
|
61
|
+
if request.get_header("action_dispatch.show_detailed_exceptions")
|
62
|
+
begin
|
63
|
+
content_type = request.formats.first
|
64
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
65
|
+
content_type = Mime[:text]
|
66
|
+
end
|
67
|
+
|
68
|
+
if api_request?(content_type)
|
69
|
+
render_for_api_request(content_type, wrapper)
|
70
|
+
else
|
71
|
+
render_for_browser_request(request, wrapper)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
raise exception
|
47
75
|
end
|
76
|
+
end
|
48
77
|
|
49
|
-
|
50
|
-
|
51
|
-
exception: wrapper.exception,
|
52
|
-
traces: traces,
|
53
|
-
show_source_idx: source_to_show_id,
|
54
|
-
trace_to_show: trace_to_show,
|
55
|
-
routes_inspector: routes_inspector(exception),
|
56
|
-
source_extracts: wrapper.source_extracts,
|
57
|
-
line_number: wrapper.line_number,
|
58
|
-
file: wrapper.file
|
59
|
-
)
|
78
|
+
def render_for_browser_request(request, wrapper)
|
79
|
+
template = create_template(request, wrapper)
|
60
80
|
file = "rescues/#{wrapper.rescue_template}"
|
61
81
|
|
62
82
|
if request.xhr?
|
63
83
|
body = template.render(template: file, layout: false, formats: [:text])
|
64
84
|
format = "text/plain"
|
65
85
|
else
|
66
|
-
body = template.render(template: file, layout:
|
86
|
+
body = template.render(template: file, layout: "rescues/layout")
|
67
87
|
format = "text/html"
|
68
88
|
end
|
69
89
|
render(wrapper.status_code, body, format)
|
70
|
-
else
|
71
|
-
raise exception
|
72
90
|
end
|
73
|
-
end
|
74
91
|
|
75
|
-
|
76
|
-
|
77
|
-
|
92
|
+
def render_for_api_request(content_type, wrapper)
|
93
|
+
body = {
|
94
|
+
status: wrapper.status_code,
|
95
|
+
error: Rack::Utils::HTTP_STATUS_CODES.fetch(
|
96
|
+
wrapper.status_code,
|
97
|
+
Rack::Utils::HTTP_STATUS_CODES[500]
|
98
|
+
),
|
99
|
+
exception: wrapper.exception_inspect,
|
100
|
+
traces: wrapper.traces
|
101
|
+
}
|
102
|
+
|
103
|
+
to_format = "to_#{content_type.to_sym}"
|
104
|
+
|
105
|
+
if content_type && body.respond_to?(to_format)
|
106
|
+
formatted_body = body.public_send(to_format)
|
107
|
+
format = content_type
|
108
|
+
else
|
109
|
+
formatted_body = body.to_json
|
110
|
+
format = Mime[:json]
|
111
|
+
end
|
78
112
|
|
79
|
-
|
80
|
-
|
81
|
-
return unless logger
|
113
|
+
render(wrapper.status_code, formatted_body, format)
|
114
|
+
end
|
82
115
|
|
83
|
-
|
116
|
+
def create_template(request, wrapper)
|
117
|
+
DebugView.new(
|
118
|
+
request: request,
|
119
|
+
exception_wrapper: wrapper,
|
120
|
+
# Everything should use the wrapper, but we need to pass `exception` for legacy
|
121
|
+
# code.
|
122
|
+
exception: wrapper.exception,
|
123
|
+
traces: wrapper.traces,
|
124
|
+
show_source_idx: wrapper.source_to_show_id,
|
125
|
+
trace_to_show: wrapper.trace_to_show,
|
126
|
+
routes_inspector: routes_inspector(wrapper),
|
127
|
+
source_extracts: wrapper.source_extracts,
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
def render(status, body, format)
|
132
|
+
[status, { Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
133
|
+
end
|
134
|
+
|
135
|
+
def log_error(request, wrapper)
|
136
|
+
logger = logger(request)
|
137
|
+
|
138
|
+
return unless logger
|
139
|
+
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
140
|
+
|
141
|
+
trace = wrapper.exception_trace
|
84
142
|
|
85
|
-
|
86
|
-
|
143
|
+
message = []
|
144
|
+
message << " "
|
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)
|
153
|
+
message << " "
|
154
|
+
message.concat(trace)
|
87
155
|
|
88
|
-
|
89
|
-
message = "\n#{exception.class} (#{exception.message}):\n"
|
90
|
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
|
91
|
-
message << " " << trace.join("\n ")
|
92
|
-
logger.fatal("#{message}\n\n")
|
156
|
+
log_array(logger, message, request)
|
93
157
|
end
|
94
|
-
end
|
95
158
|
|
96
|
-
|
97
|
-
|
98
|
-
end
|
159
|
+
def log_array(logger, lines, request)
|
160
|
+
return if lines.empty?
|
99
161
|
|
100
|
-
|
101
|
-
|
102
|
-
|
162
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
163
|
+
|
164
|
+
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
165
|
+
logger.add(level, lines.join("\n#{logger.formatter.tags_text}"))
|
166
|
+
else
|
167
|
+
logger.add(level, lines.join("\n"))
|
168
|
+
end
|
169
|
+
end
|
103
170
|
|
104
|
-
|
105
|
-
|
106
|
-
|
171
|
+
def logger(request)
|
172
|
+
request.logger || ActionView::Base.logger || stderr_logger
|
173
|
+
end
|
174
|
+
|
175
|
+
def stderr_logger
|
176
|
+
@stderr_logger ||= ActiveSupport::Logger.new($stderr)
|
177
|
+
end
|
178
|
+
|
179
|
+
def routes_inspector(exception)
|
180
|
+
if @routes_app.respond_to?(:routes) && (exception.routing_error? || exception.template_error?)
|
181
|
+
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def api_request?(content_type)
|
186
|
+
@response_format == :api && !content_type.html?
|
187
|
+
end
|
188
|
+
|
189
|
+
def log_rescued_responses?(request)
|
190
|
+
request.get_header("action_dispatch.log_rescued_responses")
|
107
191
|
end
|
108
|
-
end
|
109
192
|
end
|
110
193
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
# # Action Dispatch DebugLocks
|
7
|
+
#
|
8
|
+
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
9
|
+
#
|
10
|
+
# To use it, insert it near the top of the middleware stack, using
|
11
|
+
# `config/application.rb`:
|
12
|
+
#
|
13
|
+
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
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.
|
19
|
+
#
|
20
|
+
# Generally a deadlock will be caused by the interlock conflicting with some
|
21
|
+
# other external lock or blocking I/O call. These cannot be automatically
|
22
|
+
# identified, but should be visible in the displayed backtraces.
|
23
|
+
#
|
24
|
+
# NOTE: The formatting and content of this middleware's output is intended for
|
25
|
+
# human consumption, and should be expected to change between releases.
|
26
|
+
#
|
27
|
+
# This middleware exposes operational details of the server, with no access
|
28
|
+
# control. It should only be enabled when in use, and removed thereafter.
|
29
|
+
class DebugLocks
|
30
|
+
def initialize(app, path = "/rails/locks")
|
31
|
+
@app = app
|
32
|
+
@path = path
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(env)
|
36
|
+
req = ActionDispatch::Request.new env
|
37
|
+
|
38
|
+
if req.get?
|
39
|
+
path = req.path_info.chomp("/")
|
40
|
+
if path == @path
|
41
|
+
return render_details(req)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@app.call(env)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def render_details(req)
|
50
|
+
threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
|
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.
|
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.
|
59
|
+
|
60
|
+
raw_threads.each.with_index do |(thread, info), idx|
|
61
|
+
info[:index] = idx
|
62
|
+
info[:backtrace] = thread.backtrace
|
63
|
+
end
|
64
|
+
|
65
|
+
raw_threads
|
66
|
+
end
|
67
|
+
|
68
|
+
str = threads.map do |thread, info|
|
69
|
+
if info[:exclusive]
|
70
|
+
lock_state = +"Exclusive"
|
71
|
+
elsif info[:sharing] > 0
|
72
|
+
lock_state = +"Sharing"
|
73
|
+
lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
|
74
|
+
else
|
75
|
+
lock_state = +"No lock"
|
76
|
+
end
|
77
|
+
|
78
|
+
if info[:waiting]
|
79
|
+
lock_state << " (yielded share)"
|
80
|
+
end
|
81
|
+
|
82
|
+
msg = +"Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
|
83
|
+
|
84
|
+
if info[:sleeper]
|
85
|
+
msg << " Waiting in #{info[:sleeper]}"
|
86
|
+
msg << " to #{info[:purpose].to_s.inspect}" unless info[:purpose].nil?
|
87
|
+
msg << "\n"
|
88
|
+
|
89
|
+
if info[:compatible]
|
90
|
+
compat = info[:compatible].map { |c| c == false ? "share" : c.to_s.inspect }
|
91
|
+
msg << " may be pre-empted for: #{compat.join(', ')}\n"
|
92
|
+
end
|
93
|
+
|
94
|
+
blockers = threads.values.select { |binfo| blocked_by?(info, binfo, threads.values) }
|
95
|
+
msg << " blocked by: #{blockers.map { |i| i[:index] }.join(', ')}\n" if blockers.any?
|
96
|
+
end
|
97
|
+
|
98
|
+
blockees = threads.values.select { |binfo| blocked_by?(binfo, info, threads.values) }
|
99
|
+
msg << " blocking: #{blockees.map { |i| i[:index] }.join(', ')}\n" if blockees.any?
|
100
|
+
|
101
|
+
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
102
|
+
end.join("\n\n---\n\n\n")
|
103
|
+
|
104
|
+
[200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
|
105
|
+
Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
|
106
|
+
end
|
107
|
+
|
108
|
+
def blocked_by?(victim, blocker, all_threads)
|
109
|
+
return false if victim.equal?(blocker)
|
110
|
+
|
111
|
+
case victim[:sleeper]
|
112
|
+
when :start_sharing
|
113
|
+
blocker[:exclusive] ||
|
114
|
+
(!victim[:waiting] && blocker[:compatible] && !blocker[:compatible].include?(false))
|
115
|
+
when :start_exclusive
|
116
|
+
blocker[:sharing] > 0 ||
|
117
|
+
blocker[:exclusive] ||
|
118
|
+
(blocker[:compatible] && !blocker[:compatible].include?(victim[:purpose]))
|
119
|
+
when :yield_shares
|
120
|
+
blocker[:exclusive]
|
121
|
+
when :stop_exclusive
|
122
|
+
blocker[:exclusive] ||
|
123
|
+
victim[:compatible] &&
|
124
|
+
victim[:compatible].include?(blocker[:purpose]) &&
|
125
|
+
all_threads.all? { |other| !other[:compatible] || blocker.equal?(other) || other[:compatible].include?(blocker[:purpose]) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "pp"
|
6
|
+
|
7
|
+
require "action_view"
|
8
|
+
require "action_view/base"
|
9
|
+
|
10
|
+
module ActionDispatch
|
11
|
+
class DebugView < ActionView::Base # :nodoc:
|
12
|
+
RESCUES_TEMPLATE_PATHS = [File.expand_path("templates", __dir__)]
|
13
|
+
|
14
|
+
def initialize(assigns)
|
15
|
+
paths = RESCUES_TEMPLATE_PATHS.dup
|
16
|
+
lookup_context = ActionView::LookupContext.new(paths)
|
17
|
+
super(lookup_context, assigns, nil)
|
18
|
+
@exception_wrapper = assigns[:exception_wrapper]
|
19
|
+
end
|
20
|
+
|
21
|
+
def compiled_method_container
|
22
|
+
self.class
|
23
|
+
end
|
24
|
+
|
25
|
+
def error_highlight_available?
|
26
|
+
@exception_wrapper.error_highlight_available?
|
27
|
+
end
|
28
|
+
|
29
|
+
def debug_params(params)
|
30
|
+
clean_params = params.clone
|
31
|
+
clean_params.delete("action")
|
32
|
+
clean_params.delete("controller")
|
33
|
+
|
34
|
+
if clean_params.empty?
|
35
|
+
"None"
|
36
|
+
else
|
37
|
+
PP.pp(clean_params, +"", 200)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def debug_headers(headers)
|
42
|
+
if headers.present?
|
43
|
+
headers.inspect.gsub(",", ",\n")
|
44
|
+
else
|
45
|
+
"None"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def debug_hash(object)
|
50
|
+
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
def render(*)
|
54
|
+
logger = ActionView::Base.logger
|
55
|
+
|
56
|
+
if logger && logger.respond_to?(:silence)
|
57
|
+
logger.silence { super }
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def protect_against_forgery?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def params_valid?
|
68
|
+
@request.parameters
|
69
|
+
rescue ActionController::BadRequest
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|