actionpack 4.2.11.3 → 5.0.7.2
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 +890 -384
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/base.rb +28 -38
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/abstract_controller/callbacks.rb +54 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +28 -18
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/abstract_controller.rb +6 -2
- data/lib/action_controller/api/api_rendering.rb +14 -0
- data/lib/action_controller/api.rb +147 -0
- data/lib/action_controller/base.rb +14 -11
- data/lib/action_controller/caching.rb +13 -58
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +3 -10
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +106 -34
- data/lib/action_controller/metal/cookies.rb +1 -3
- data/lib/action_controller/metal/data_streaming.rb +14 -34
- data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +11 -11
- data/lib/action_controller/metal/head.rb +14 -8
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +44 -35
- data/lib/action_controller/metal/implicit_render.rb +61 -6
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/live.rb +71 -88
- data/lib/action_controller/metal/mime_responds.rb +27 -42
- data/lib/action_controller/metal/params_wrapper.rb +9 -9
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +83 -40
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
- data/lib/action_controller/metal/rescue.rb +3 -12
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +527 -134
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/metal.rb +88 -63
- data/lib/action_controller/railtie.rb +11 -7
- data/lib/action_controller/renderer.rb +113 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +311 -374
- data/lib/action_controller.rb +12 -9
- data/lib/action_dispatch/http/cache.rb +73 -34
- data/lib/action_dispatch/http/filter_parameters.rb +16 -12
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +45 -14
- data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
- data/lib/action_dispatch/http/mime_type.rb +126 -90
- data/lib/action_dispatch/http/mime_types.rb +3 -4
- data/lib/action_dispatch/http/parameter_filter.rb +19 -9
- data/lib/action_dispatch/http/parameters.rb +70 -40
- data/lib/action_dispatch/http/request.rb +144 -89
- data/lib/action_dispatch/http/response.rb +215 -102
- data/lib/action_dispatch/http/upload.rb +6 -2
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +47 -30
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser.rb +2 -0
- data/lib/action_dispatch/journey/parser_extras.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +38 -42
- data/lib/action_dispatch/journey/route.rb +88 -26
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/router.rb +8 -10
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +89 -44
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/cookies.rb +188 -134
- data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
- data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +66 -45
- data/lib/action_dispatch/middleware/params_parser.rb +32 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +14 -58
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +124 -36
- data/lib/action_dispatch/middleware/stack.rb +44 -40
- data/lib/action_dispatch/middleware/static.rb +51 -35
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +2 -2
- data/lib/action_dispatch/request/session.rb +69 -33
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing/inspector.rb +32 -43
- data/lib/action_dispatch/routing/mapper.rb +515 -348
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +5 -4
- data/lib/action_dispatch/routing/route_set.rb +148 -240
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/routing.rb +17 -13
- data/lib/action_dispatch/testing/assertion_response.rb +45 -0
- data/lib/action_dispatch/testing/assertions/response.rb +38 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +377 -149
- data/lib/action_dispatch/testing/request_encoder.rb +53 -0
- data/lib/action_dispatch/testing/test_process.rb +24 -20
- data/lib/action_dispatch/testing/test_request.rb +22 -31
- data/lib/action_dispatch/testing/test_response.rb +12 -4
- data/lib/action_dispatch.rb +4 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +32 -34
- 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/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/router/strexp.rb +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
- /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
require 'action_dispatch/http/request'
|
|
2
2
|
require 'action_dispatch/middleware/exception_wrapper'
|
|
3
3
|
require 'action_dispatch/routing/inspector'
|
|
4
|
+
require 'action_view'
|
|
5
|
+
require 'action_view/base'
|
|
6
|
+
|
|
7
|
+
require 'pp'
|
|
4
8
|
|
|
5
9
|
module ActionDispatch
|
|
6
10
|
# This middleware is responsible for logging exceptions and
|
|
@@ -8,12 +12,40 @@ module ActionDispatch
|
|
|
8
12
|
class DebugExceptions
|
|
9
13
|
RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__)
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
class DebugView < ActionView::Base
|
|
16
|
+
def debug_params(params)
|
|
17
|
+
clean_params = params.clone
|
|
18
|
+
clean_params.delete("action")
|
|
19
|
+
clean_params.delete("controller")
|
|
20
|
+
|
|
21
|
+
if clean_params.empty?
|
|
22
|
+
'None'
|
|
23
|
+
else
|
|
24
|
+
PP.pp(clean_params, "", 200)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def debug_headers(headers)
|
|
29
|
+
if headers.present?
|
|
30
|
+
headers.inspect.gsub(',', ",\n")
|
|
31
|
+
else
|
|
32
|
+
'None'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def debug_hash(object)
|
|
37
|
+
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def initialize(app, routes_app = nil, response_format = :default)
|
|
42
|
+
@app = app
|
|
43
|
+
@routes_app = routes_app
|
|
44
|
+
@response_format = response_format
|
|
14
45
|
end
|
|
15
46
|
|
|
16
47
|
def call(env)
|
|
48
|
+
request = ActionDispatch::Request.new env
|
|
17
49
|
_, headers, body = response = @app.call(env)
|
|
18
50
|
|
|
19
51
|
if headers['X-Cascade'] == 'pass'
|
|
@@ -23,61 +55,99 @@ module ActionDispatch
|
|
|
23
55
|
|
|
24
56
|
response
|
|
25
57
|
rescue Exception => exception
|
|
26
|
-
raise exception
|
|
27
|
-
render_exception(
|
|
58
|
+
raise exception unless request.show_exceptions?
|
|
59
|
+
render_exception(request, exception)
|
|
28
60
|
end
|
|
29
61
|
|
|
30
62
|
private
|
|
31
63
|
|
|
32
|
-
def render_exception(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
def render_exception(request, exception)
|
|
65
|
+
backtrace_cleaner = request.get_header('action_dispatch.backtrace_cleaner')
|
|
66
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
|
67
|
+
log_error(request, wrapper)
|
|
68
|
+
|
|
69
|
+
if request.get_header('action_dispatch.show_detailed_exceptions')
|
|
70
|
+
case @response_format
|
|
71
|
+
when :api
|
|
72
|
+
render_for_api_application(request, wrapper)
|
|
73
|
+
when :default
|
|
74
|
+
render_for_default_application(request, wrapper)
|
|
43
75
|
end
|
|
76
|
+
else
|
|
77
|
+
raise exception
|
|
78
|
+
end
|
|
79
|
+
end
|
|
44
80
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
81
|
+
def render_for_default_application(request, wrapper)
|
|
82
|
+
template = create_template(request, wrapper)
|
|
83
|
+
file = "rescues/#{wrapper.rescue_template}"
|
|
48
84
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
)
|
|
60
|
-
file = "rescues/#{wrapper.rescue_template}"
|
|
61
|
-
|
|
62
|
-
if request.xhr?
|
|
63
|
-
body = template.render(template: file, layout: false, formats: [:text])
|
|
64
|
-
format = "text/plain"
|
|
65
|
-
else
|
|
66
|
-
body = template.render(template: file, layout: 'rescues/layout')
|
|
67
|
-
format = "text/html"
|
|
68
|
-
end
|
|
69
|
-
render(wrapper.status_code, body, format)
|
|
85
|
+
if request.xhr?
|
|
86
|
+
body = template.render(template: file, layout: false, formats: [:text])
|
|
87
|
+
format = "text/plain"
|
|
70
88
|
else
|
|
71
|
-
|
|
89
|
+
body = template.render(template: file, layout: 'rescues/layout')
|
|
90
|
+
format = "text/html"
|
|
91
|
+
end
|
|
92
|
+
render(wrapper.status_code, body, format)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def render_for_api_application(request, wrapper)
|
|
96
|
+
body = {
|
|
97
|
+
status: wrapper.status_code,
|
|
98
|
+
error: Rack::Utils::HTTP_STATUS_CODES.fetch(
|
|
99
|
+
wrapper.status_code,
|
|
100
|
+
Rack::Utils::HTTP_STATUS_CODES[500]
|
|
101
|
+
),
|
|
102
|
+
exception: wrapper.exception.inspect,
|
|
103
|
+
traces: wrapper.traces
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
content_type = request.formats.first
|
|
107
|
+
to_format = "to_#{content_type.to_sym}"
|
|
108
|
+
|
|
109
|
+
if content_type && body.respond_to?(to_format)
|
|
110
|
+
formatted_body = body.public_send(to_format)
|
|
111
|
+
format = content_type
|
|
112
|
+
else
|
|
113
|
+
formatted_body = body.to_json
|
|
114
|
+
format = Mime[:json]
|
|
72
115
|
end
|
|
116
|
+
|
|
117
|
+
render(wrapper.status_code, formatted_body, format)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def create_template(request, wrapper)
|
|
121
|
+
traces = wrapper.traces
|
|
122
|
+
|
|
123
|
+
trace_to_show = 'Application Trace'
|
|
124
|
+
if traces[trace_to_show].empty? && wrapper.rescue_template != 'routing_error'
|
|
125
|
+
trace_to_show = 'Full Trace'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if source_to_show = traces[trace_to_show].first
|
|
129
|
+
source_to_show_id = source_to_show[:id]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
DebugView.new([RESCUES_TEMPLATE_PATH],
|
|
133
|
+
request: request,
|
|
134
|
+
exception: wrapper.exception,
|
|
135
|
+
traces: traces,
|
|
136
|
+
show_source_idx: source_to_show_id,
|
|
137
|
+
trace_to_show: trace_to_show,
|
|
138
|
+
routes_inspector: routes_inspector(wrapper.exception),
|
|
139
|
+
source_extracts: wrapper.source_extracts,
|
|
140
|
+
line_number: wrapper.line_number,
|
|
141
|
+
file: wrapper.file
|
|
142
|
+
)
|
|
73
143
|
end
|
|
74
144
|
|
|
75
145
|
def render(status, body, format)
|
|
76
146
|
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
|
|
77
147
|
end
|
|
78
148
|
|
|
79
|
-
def log_error(
|
|
80
|
-
logger = logger(
|
|
149
|
+
def log_error(request, wrapper)
|
|
150
|
+
logger = logger(request)
|
|
81
151
|
return unless logger
|
|
82
152
|
|
|
83
153
|
exception = wrapper.exception
|
|
@@ -86,15 +156,24 @@ module ActionDispatch
|
|
|
86
156
|
trace = wrapper.framework_trace if trace.empty?
|
|
87
157
|
|
|
88
158
|
ActiveSupport::Deprecation.silence do
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
logger.fatal
|
|
159
|
+
logger.fatal " "
|
|
160
|
+
logger.fatal "#{exception.class} (#{exception.message}):"
|
|
161
|
+
log_array logger, exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
|
|
162
|
+
logger.fatal " "
|
|
163
|
+
log_array logger, trace
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def log_array(logger, array)
|
|
168
|
+
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
|
169
|
+
logger.fatal array.join("\n#{logger.formatter.tags_text}")
|
|
170
|
+
else
|
|
171
|
+
logger.fatal array.join("\n")
|
|
93
172
|
end
|
|
94
173
|
end
|
|
95
174
|
|
|
96
|
-
def logger(
|
|
97
|
-
|
|
175
|
+
def logger(request)
|
|
176
|
+
request.logger || ActionView::Base.logger || stderr_logger
|
|
98
177
|
end
|
|
99
178
|
|
|
100
179
|
def stderr_logger
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module ActionDispatch
|
|
2
|
+
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
|
3
|
+
#
|
|
4
|
+
# To use it, insert it near the top of the middleware stack, using
|
|
5
|
+
# <tt>config/application.rb</tt>:
|
|
6
|
+
#
|
|
7
|
+
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
|
8
|
+
#
|
|
9
|
+
# After restarting the application and re-triggering the deadlock condition,
|
|
10
|
+
# <tt>/rails/locks</tt> will show a summary of all threads currently known to
|
|
11
|
+
# the interlock, which lock level they are holding or awaiting, and their
|
|
12
|
+
# current backtrace.
|
|
13
|
+
#
|
|
14
|
+
# Generally a deadlock will be caused by the interlock conflicting with some
|
|
15
|
+
# other external lock or blocking I/O call. These cannot be automatically
|
|
16
|
+
# identified, but should be visible in the displayed backtraces.
|
|
17
|
+
#
|
|
18
|
+
# NOTE: The formatting and content of this middleware's output is intended for
|
|
19
|
+
# human consumption, and should be expected to change between releases.
|
|
20
|
+
#
|
|
21
|
+
# This middleware exposes operational details of the server, with no access
|
|
22
|
+
# control. It should only be enabled when in use, and removed thereafter.
|
|
23
|
+
class DebugLocks
|
|
24
|
+
def initialize(app, path = '/rails/locks')
|
|
25
|
+
@app = app
|
|
26
|
+
@path = path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def call(env)
|
|
30
|
+
req = ActionDispatch::Request.new env
|
|
31
|
+
|
|
32
|
+
if req.get?
|
|
33
|
+
path = req.path_info.chomp('/'.freeze)
|
|
34
|
+
if path == @path
|
|
35
|
+
return render_details(req)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@app.call(env)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def render_details(req)
|
|
44
|
+
threads = ActiveSupport::Dependencies.interlock.raw_state do |threads|
|
|
45
|
+
# The Interlock itself comes to a complete halt as long as this block
|
|
46
|
+
# is executing. That gives us a more consistent picture of everything,
|
|
47
|
+
# but creates a pretty strong Observer Effect.
|
|
48
|
+
#
|
|
49
|
+
# Most directly, that means we need to do as little as possible in
|
|
50
|
+
# this block. More widely, it means this middleware should remain a
|
|
51
|
+
# strictly diagnostic tool (to be used when something has gone wrong),
|
|
52
|
+
# and not for any sort of general monitoring.
|
|
53
|
+
|
|
54
|
+
threads.each.with_index do |(thread, info), idx|
|
|
55
|
+
info[:index] = idx
|
|
56
|
+
info[:backtrace] = thread.backtrace
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
threads
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
str = threads.map do |thread, info|
|
|
63
|
+
if info[:exclusive]
|
|
64
|
+
lock_state = 'Exclusive'
|
|
65
|
+
elsif info[:sharing] > 0
|
|
66
|
+
lock_state = 'Sharing'
|
|
67
|
+
lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
|
|
68
|
+
else
|
|
69
|
+
lock_state = 'No lock'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if info[:waiting]
|
|
73
|
+
lock_state << ' (yielded share)'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
|
|
77
|
+
|
|
78
|
+
if info[:sleeper]
|
|
79
|
+
msg << " Waiting in #{info[:sleeper]}"
|
|
80
|
+
msg << " to #{info[:purpose].to_s.inspect}" unless info[:purpose].nil?
|
|
81
|
+
msg << "\n"
|
|
82
|
+
|
|
83
|
+
if info[:compatible]
|
|
84
|
+
compat = info[:compatible].map { |c| c == false ? "share" : c.to_s.inspect }
|
|
85
|
+
msg << " may be pre-empted for: #{compat.join(', ')}\n"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
blockers = threads.values.select { |binfo| blocked_by?(info, binfo, threads.values) }
|
|
89
|
+
msg << " blocked by: #{blockers.map {|i| i[:index] }.join(', ')}\n" if blockers.any?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
blockees = threads.values.select { |binfo| blocked_by?(binfo, info, threads.values) }
|
|
93
|
+
msg << " blocking: #{blockees.map {|i| i[:index] }.join(', ')}\n" if blockees.any?
|
|
94
|
+
|
|
95
|
+
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
|
96
|
+
end.join("\n\n---\n\n\n")
|
|
97
|
+
|
|
98
|
+
[200, { "Content-Type" => "text/plain", "Content-Length" => str.size }, [str]]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def blocked_by?(victim, blocker, all_threads)
|
|
102
|
+
return false if victim.equal?(blocker)
|
|
103
|
+
|
|
104
|
+
case victim[:sleeper]
|
|
105
|
+
when :start_sharing
|
|
106
|
+
blocker[:exclusive] ||
|
|
107
|
+
(!victim[:waiting] && blocker[:compatible] && !blocker[:compatible].include?(false))
|
|
108
|
+
when :start_exclusive
|
|
109
|
+
blocker[:sharing] > 0 ||
|
|
110
|
+
blocker[:exclusive] ||
|
|
111
|
+
(blocker[:compatible] && !blocker[:compatible].include?(victim[:purpose]))
|
|
112
|
+
when :yield_shares
|
|
113
|
+
blocker[:exclusive]
|
|
114
|
+
when :stop_exclusive
|
|
115
|
+
blocker[:exclusive] ||
|
|
116
|
+
victim[:compatible] &&
|
|
117
|
+
victim[:compatible].include?(blocker[:purpose]) &&
|
|
118
|
+
all_threads.all? { |other| !other[:compatible] || blocker.equal?(other) || other[:compatible].include?(blocker[:purpose]) }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require 'action_controller/metal/exceptions'
|
|
2
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
|
2
|
+
require 'rack/utils'
|
|
3
3
|
|
|
4
4
|
module ActionDispatch
|
|
5
5
|
class ExceptionWrapper
|
|
@@ -16,7 +16,9 @@ module ActionDispatch
|
|
|
16
16
|
'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity,
|
|
17
17
|
'ActionDispatch::ParamsParser::ParseError' => :bad_request,
|
|
18
18
|
'ActionController::BadRequest' => :bad_request,
|
|
19
|
-
'ActionController::ParameterMissing' => :bad_request
|
|
19
|
+
'ActionController::ParameterMissing' => :bad_request,
|
|
20
|
+
'Rack::QueryParser::ParameterTypeError' => :bad_request,
|
|
21
|
+
'Rack::QueryParser::InvalidParameterError' => :bad_request
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
cattr_accessor :rescue_templates
|
|
@@ -28,13 +30,13 @@ module ActionDispatch
|
|
|
28
30
|
'ActionView::Template::Error' => 'template_error'
|
|
29
31
|
)
|
|
30
32
|
|
|
31
|
-
attr_reader :
|
|
33
|
+
attr_reader :backtrace_cleaner, :exception, :line_number, :file
|
|
32
34
|
|
|
33
|
-
def initialize(
|
|
34
|
-
@
|
|
35
|
+
def initialize(backtrace_cleaner, exception)
|
|
36
|
+
@backtrace_cleaner = backtrace_cleaner
|
|
35
37
|
@exception = original_exception(exception)
|
|
36
38
|
|
|
37
|
-
expand_backtrace if exception.is_a?(SyntaxError) || exception.
|
|
39
|
+
expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
def rescue_template
|
|
@@ -58,7 +60,7 @@ module ActionDispatch
|
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
def traces
|
|
61
|
-
|
|
63
|
+
application_trace_with_ids = []
|
|
62
64
|
framework_trace_with_ids = []
|
|
63
65
|
full_trace_with_ids = []
|
|
64
66
|
|
|
@@ -66,7 +68,7 @@ module ActionDispatch
|
|
|
66
68
|
trace_with_id = { id: idx, trace: trace }
|
|
67
69
|
|
|
68
70
|
if application_trace.include?(trace)
|
|
69
|
-
|
|
71
|
+
application_trace_with_ids << trace_with_id
|
|
70
72
|
else
|
|
71
73
|
framework_trace_with_ids << trace_with_id
|
|
72
74
|
end
|
|
@@ -75,7 +77,7 @@ module ActionDispatch
|
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
{
|
|
78
|
-
"Application Trace" =>
|
|
80
|
+
"Application Trace" => application_trace_with_ids,
|
|
79
81
|
"Framework Trace" => framework_trace_with_ids,
|
|
80
82
|
"Full Trace" => full_trace_with_ids
|
|
81
83
|
}
|
|
@@ -87,8 +89,7 @@ module ActionDispatch
|
|
|
87
89
|
|
|
88
90
|
def source_extracts
|
|
89
91
|
backtrace.map do |trace|
|
|
90
|
-
file,
|
|
91
|
-
line_number = line.to_i
|
|
92
|
+
file, line_number = extract_file_and_line_number(trace)
|
|
92
93
|
|
|
93
94
|
{
|
|
94
95
|
code: source_fragment(file, line_number),
|
|
@@ -104,17 +105,13 @@ module ActionDispatch
|
|
|
104
105
|
end
|
|
105
106
|
|
|
106
107
|
def original_exception(exception)
|
|
107
|
-
if
|
|
108
|
-
exception.
|
|
108
|
+
if @@rescue_responses.has_key?(exception.cause.class.name)
|
|
109
|
+
exception.cause
|
|
109
110
|
else
|
|
110
111
|
exception
|
|
111
112
|
end
|
|
112
113
|
end
|
|
113
114
|
|
|
114
|
-
def registered_original_exception?(exception)
|
|
115
|
-
exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
115
|
def clean_backtrace(*args)
|
|
119
116
|
if backtrace_cleaner
|
|
120
117
|
backtrace_cleaner.clean(backtrace, *args)
|
|
@@ -123,10 +120,6 @@ module ActionDispatch
|
|
|
123
120
|
end
|
|
124
121
|
end
|
|
125
122
|
|
|
126
|
-
def backtrace_cleaner
|
|
127
|
-
@backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
|
|
128
|
-
end
|
|
129
|
-
|
|
130
123
|
def source_fragment(path, line)
|
|
131
124
|
return unless Rails.respond_to?(:root) && Rails.root
|
|
132
125
|
full_path = Rails.root.join(path)
|
|
@@ -139,6 +132,13 @@ module ActionDispatch
|
|
|
139
132
|
end
|
|
140
133
|
end
|
|
141
134
|
|
|
135
|
+
def extract_file_and_line_number(trace)
|
|
136
|
+
# Split by the first colon followed by some digits, which works for both
|
|
137
|
+
# Windows and Unix path styles.
|
|
138
|
+
file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
|
|
139
|
+
[file, line.to_i]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
142
|
def expand_backtrace
|
|
143
143
|
@exception.backtrace.unshift(
|
|
144
144
|
@exception.to_s.split("\n")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'rack/body_proxy'
|
|
2
|
+
|
|
3
|
+
module ActionDispatch
|
|
4
|
+
class Executor
|
|
5
|
+
def initialize(app, executor)
|
|
6
|
+
@app, @executor = app, executor
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
state = @executor.run!
|
|
11
|
+
begin
|
|
12
|
+
response = @app.call(env)
|
|
13
|
+
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
|
14
|
+
ensure
|
|
15
|
+
state.complete! unless returned
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
require 'active_support/core_ext/hash/keys'
|
|
2
2
|
|
|
3
3
|
module ActionDispatch
|
|
4
|
-
class Request < Rack::Request
|
|
5
|
-
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
|
|
6
|
-
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
|
|
7
|
-
# to put a new one.
|
|
8
|
-
def flash
|
|
9
|
-
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
4
|
# The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
|
|
14
5
|
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
|
|
15
6
|
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
|
|
@@ -47,6 +38,45 @@ module ActionDispatch
|
|
|
47
38
|
class Flash
|
|
48
39
|
KEY = 'action_dispatch.request.flash_hash'.freeze
|
|
49
40
|
|
|
41
|
+
module RequestMethods
|
|
42
|
+
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
|
|
43
|
+
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
|
|
44
|
+
# to put a new one.
|
|
45
|
+
def flash
|
|
46
|
+
flash = flash_hash
|
|
47
|
+
return flash if flash
|
|
48
|
+
self.flash = Flash::FlashHash.from_session_value(session["flash"])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def flash=(flash)
|
|
52
|
+
set_header Flash::KEY, flash
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def flash_hash # :nodoc:
|
|
56
|
+
get_header Flash::KEY
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def commit_flash # :nodoc:
|
|
60
|
+
session = self.session || {}
|
|
61
|
+
flash_hash = self.flash_hash
|
|
62
|
+
|
|
63
|
+
if flash_hash && (flash_hash.present? || session.key?('flash'))
|
|
64
|
+
session["flash"] = flash_hash.to_session_value
|
|
65
|
+
self.flash = flash_hash.dup
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
|
|
69
|
+
session.key?('flash') && session['flash'].nil?
|
|
70
|
+
session.delete('flash')
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def reset_session # :nodoc
|
|
75
|
+
super
|
|
76
|
+
self.flash = nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
50
80
|
class FlashNow #:nodoc:
|
|
51
81
|
attr_accessor :flash
|
|
52
82
|
|
|
@@ -80,24 +110,30 @@ module ActionDispatch
|
|
|
80
110
|
include Enumerable
|
|
81
111
|
|
|
82
112
|
def self.from_session_value(value) #:nodoc:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
case value
|
|
114
|
+
when FlashHash # Rails 3.1, 3.2
|
|
115
|
+
flashes = value.instance_variable_get(:@flashes)
|
|
116
|
+
if discard = value.instance_variable_get(:@used)
|
|
117
|
+
flashes.except!(*discard)
|
|
118
|
+
end
|
|
119
|
+
new(flashes, flashes.keys)
|
|
120
|
+
when Hash # Rails 4.0
|
|
121
|
+
flashes = value['flashes']
|
|
122
|
+
if discard = value['discard']
|
|
123
|
+
flashes.except!(*discard)
|
|
124
|
+
end
|
|
125
|
+
new(flashes, flashes.keys)
|
|
126
|
+
else
|
|
127
|
+
new
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Builds a hash containing the flashes to keep for the next request.
|
|
132
|
+
# If there are none to keep, returns nil.
|
|
98
133
|
def to_session_value #:nodoc:
|
|
99
|
-
|
|
100
|
-
|
|
134
|
+
flashes_to_keep = @flashes.except(*@discard)
|
|
135
|
+
return nil if flashes_to_keep.empty?
|
|
136
|
+
{ 'discard' => [], 'flashes' => flashes_to_keep }
|
|
101
137
|
end
|
|
102
138
|
|
|
103
139
|
def initialize(flashes = {}, discard = []) #:nodoc:
|
|
@@ -252,25 +288,10 @@ module ActionDispatch
|
|
|
252
288
|
end
|
|
253
289
|
end
|
|
254
290
|
|
|
255
|
-
def
|
|
256
|
-
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def call(env)
|
|
260
|
-
@app.call(env)
|
|
261
|
-
ensure
|
|
262
|
-
session = Request::Session.find(env) || {}
|
|
263
|
-
flash_hash = env[KEY]
|
|
264
|
-
|
|
265
|
-
if flash_hash && (flash_hash.present? || session.key?('flash'))
|
|
266
|
-
session["flash"] = flash_hash.to_session_value
|
|
267
|
-
env[KEY] = flash_hash.dup
|
|
268
|
-
end
|
|
291
|
+
def self.new(app) app; end
|
|
292
|
+
end
|
|
269
293
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
session.delete('flash')
|
|
273
|
-
end
|
|
274
|
-
end
|
|
294
|
+
class Request
|
|
295
|
+
prepend Flash::RequestMethods
|
|
275
296
|
end
|
|
276
297
|
end
|