actionpack 5.2.3

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.

Files changed (170) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +429 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +265 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +166 -0
  10. data/lib/abstract_controller/callbacks.rb +212 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +31 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +66 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +276 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +78 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +274 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +152 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  34. data/lib/action_controller/metal/exceptions.rb +53 -0
  35. data/lib/action_controller/metal/flash.rb +61 -0
  36. data/lib/action_controller/metal/force_ssl.rb +99 -0
  37. data/lib/action_controller/metal/head.rb +60 -0
  38. data/lib/action_controller/metal/helpers.rb +123 -0
  39. data/lib/action_controller/metal/http_authentication.rb +519 -0
  40. data/lib/action_controller/metal/implicit_render.rb +73 -0
  41. data/lib/action_controller/metal/instrumentation.rb +107 -0
  42. data/lib/action_controller/metal/live.rb +312 -0
  43. data/lib/action_controller/metal/mime_responds.rb +313 -0
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +293 -0
  46. data/lib/action_controller/metal/redirecting.rb +133 -0
  47. data/lib/action_controller/metal/renderers.rb +181 -0
  48. data/lib/action_controller/metal/rendering.rb +122 -0
  49. data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
  50. data/lib/action_controller/metal/rescue.rb +28 -0
  51. data/lib/action_controller/metal/streaming.rb +223 -0
  52. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  53. data/lib/action_controller/metal/testing.rb +16 -0
  54. data/lib/action_controller/metal/url_for.rb +58 -0
  55. data/lib/action_controller/railtie.rb +89 -0
  56. data/lib/action_controller/railties/helpers.rb +24 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +629 -0
  60. data/lib/action_dispatch.rb +112 -0
  61. data/lib/action_dispatch/http/cache.rb +222 -0
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +84 -0
  64. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  65. data/lib/action_dispatch/http/headers.rb +132 -0
  66. data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
  67. data/lib/action_dispatch/http/mime_type.rb +342 -0
  68. data/lib/action_dispatch/http/mime_types.rb +50 -0
  69. data/lib/action_dispatch/http/parameter_filter.rb +86 -0
  70. data/lib/action_dispatch/http/parameters.rb +126 -0
  71. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  72. data/lib/action_dispatch/http/request.rb +430 -0
  73. data/lib/action_dispatch/http/response.rb +519 -0
  74. data/lib/action_dispatch/http/upload.rb +84 -0
  75. data/lib/action_dispatch/http/url.rb +350 -0
  76. data/lib/action_dispatch/journey.rb +7 -0
  77. data/lib/action_dispatch/journey/formatter.rb +189 -0
  78. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  81. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  82. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  85. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  86. data/lib/action_dispatch/journey/parser.rb +199 -0
  87. data/lib/action_dispatch/journey/parser.y +50 -0
  88. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  89. data/lib/action_dispatch/journey/path/pattern.rb +198 -0
  90. data/lib/action_dispatch/journey/route.rb +203 -0
  91. data/lib/action_dispatch/journey/router.rb +156 -0
  92. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  93. data/lib/action_dispatch/journey/routes.rb +82 -0
  94. data/lib/action_dispatch/journey/scanner.rb +64 -0
  95. data/lib/action_dispatch/journey/visitors.rb +268 -0
  96. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  97. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  98. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  99. data/lib/action_dispatch/middleware/callbacks.rb +36 -0
  100. data/lib/action_dispatch/middleware/cookies.rb +685 -0
  101. data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
  102. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  103. data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
  104. data/lib/action_dispatch/middleware/executor.rb +21 -0
  105. data/lib/action_dispatch/middleware/flash.rb +300 -0
  106. data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
  107. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  108. data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
  109. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  110. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  111. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  112. data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
  113. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  114. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  115. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  116. data/lib/action_dispatch/middleware/stack.rb +116 -0
  117. data/lib/action_dispatch/middleware/static.rb +130 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  139. data/lib/action_dispatch/railtie.rb +55 -0
  140. data/lib/action_dispatch/request/session.rb +234 -0
  141. data/lib/action_dispatch/request/utils.rb +78 -0
  142. data/lib/action_dispatch/routing.rb +260 -0
  143. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  144. data/lib/action_dispatch/routing/inspector.rb +225 -0
  145. data/lib/action_dispatch/routing/mapper.rb +2267 -0
  146. data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
  147. data/lib/action_dispatch/routing/redirection.rb +201 -0
  148. data/lib/action_dispatch/routing/route_set.rb +890 -0
  149. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  150. data/lib/action_dispatch/routing/url_for.rb +236 -0
  151. data/lib/action_dispatch/system_test_case.rb +147 -0
  152. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  153. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  154. data/lib/action_dispatch/system_testing/server.rb +31 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  158. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  159. data/lib/action_dispatch/testing/assertions.rb +24 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +107 -0
  161. data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
  162. data/lib/action_dispatch/testing/integration.rb +652 -0
  163. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  164. data/lib/action_dispatch/testing/test_process.rb +50 -0
  165. data/lib/action_dispatch/testing/test_request.rb +71 -0
  166. data/lib/action_dispatch/testing/test_response.rb +53 -0
  167. data/lib/action_pack.rb +26 -0
  168. data/lib/action_pack/gem_version.rb +17 -0
  169. data/lib/action_pack/version.rb +10 -0
  170. metadata +318 -0
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/http/request"
4
+ require "action_dispatch/middleware/exception_wrapper"
5
+ require "action_dispatch/routing/inspector"
6
+ require "action_view"
7
+ require "action_view/base"
8
+
9
+ require "pp"
10
+
11
+ module ActionDispatch
12
+ # This middleware is responsible for logging exceptions and
13
+ # showing a debugging page in case the request is local.
14
+ class DebugExceptions
15
+ RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
16
+
17
+ class DebugView < ActionView::Base
18
+ def debug_params(params)
19
+ clean_params = params.clone
20
+ clean_params.delete("action")
21
+ clean_params.delete("controller")
22
+
23
+ if clean_params.empty?
24
+ "None"
25
+ else
26
+ PP.pp(clean_params, "".dup, 200)
27
+ end
28
+ end
29
+
30
+ def debug_headers(headers)
31
+ if headers.present?
32
+ headers.inspect.gsub(",", ",\n")
33
+ else
34
+ "None"
35
+ end
36
+ end
37
+
38
+ def debug_hash(object)
39
+ object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
40
+ end
41
+
42
+ def render(*)
43
+ logger = ActionView::Base.logger
44
+
45
+ if logger && logger.respond_to?(:silence)
46
+ logger.silence { super }
47
+ else
48
+ super
49
+ end
50
+ end
51
+ end
52
+
53
+ def initialize(app, routes_app = nil, response_format = :default)
54
+ @app = app
55
+ @routes_app = routes_app
56
+ @response_format = response_format
57
+ end
58
+
59
+ def call(env)
60
+ request = ActionDispatch::Request.new env
61
+ _, headers, body = response = @app.call(env)
62
+
63
+ if headers["X-Cascade"] == "pass"
64
+ body.close if body.respond_to?(:close)
65
+ raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
66
+ end
67
+
68
+ response
69
+ rescue Exception => exception
70
+ raise exception unless request.show_exceptions?
71
+ render_exception(request, exception)
72
+ end
73
+
74
+ private
75
+
76
+ def render_exception(request, exception)
77
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
78
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
79
+ log_error(request, wrapper)
80
+
81
+ if request.get_header("action_dispatch.show_detailed_exceptions")
82
+ content_type = request.formats.first
83
+
84
+ if api_request?(content_type)
85
+ render_for_api_request(content_type, wrapper)
86
+ else
87
+ render_for_browser_request(request, wrapper)
88
+ end
89
+ else
90
+ raise exception
91
+ end
92
+ end
93
+
94
+ def render_for_browser_request(request, wrapper)
95
+ template = create_template(request, wrapper)
96
+ file = "rescues/#{wrapper.rescue_template}"
97
+
98
+ if request.xhr?
99
+ body = template.render(template: file, layout: false, formats: [:text])
100
+ format = "text/plain"
101
+ else
102
+ body = template.render(template: file, layout: "rescues/layout")
103
+ format = "text/html"
104
+ end
105
+ render(wrapper.status_code, body, format)
106
+ end
107
+
108
+ def render_for_api_request(content_type, wrapper)
109
+ body = {
110
+ status: wrapper.status_code,
111
+ error: Rack::Utils::HTTP_STATUS_CODES.fetch(
112
+ wrapper.status_code,
113
+ Rack::Utils::HTTP_STATUS_CODES[500]
114
+ ),
115
+ exception: wrapper.exception.inspect,
116
+ traces: wrapper.traces
117
+ }
118
+
119
+ to_format = "to_#{content_type.to_sym}"
120
+
121
+ if content_type && body.respond_to?(to_format)
122
+ formatted_body = body.public_send(to_format)
123
+ format = content_type
124
+ else
125
+ formatted_body = body.to_json
126
+ format = Mime[:json]
127
+ end
128
+
129
+ render(wrapper.status_code, formatted_body, format)
130
+ end
131
+
132
+ def create_template(request, wrapper)
133
+ traces = wrapper.traces
134
+
135
+ trace_to_show = "Application Trace"
136
+ if traces[trace_to_show].empty? && wrapper.rescue_template != "routing_error"
137
+ trace_to_show = "Full Trace"
138
+ end
139
+
140
+ if source_to_show = traces[trace_to_show].first
141
+ source_to_show_id = source_to_show[:id]
142
+ end
143
+
144
+ DebugView.new([RESCUES_TEMPLATE_PATH],
145
+ request: request,
146
+ exception: wrapper.exception,
147
+ traces: traces,
148
+ show_source_idx: source_to_show_id,
149
+ trace_to_show: trace_to_show,
150
+ routes_inspector: routes_inspector(wrapper.exception),
151
+ source_extracts: wrapper.source_extracts,
152
+ line_number: wrapper.line_number,
153
+ file: wrapper.file
154
+ )
155
+ end
156
+
157
+ def render(status, body, format)
158
+ [status, { "Content-Type" => "#{format}; charset=#{Response.default_charset}", "Content-Length" => body.bytesize.to_s }, [body]]
159
+ end
160
+
161
+ def log_error(request, wrapper)
162
+ logger = logger(request)
163
+ return unless logger
164
+
165
+ exception = wrapper.exception
166
+
167
+ trace = wrapper.application_trace
168
+ trace = wrapper.framework_trace if trace.empty?
169
+
170
+ ActiveSupport::Deprecation.silence do
171
+ logger.fatal " "
172
+ logger.fatal "#{exception.class} (#{exception.message}):"
173
+ log_array logger, exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
174
+ logger.fatal " "
175
+ log_array logger, trace
176
+ end
177
+ end
178
+
179
+ def log_array(logger, array)
180
+ if logger.formatter && logger.formatter.respond_to?(:tags_text)
181
+ logger.fatal array.join("\n#{logger.formatter.tags_text}")
182
+ else
183
+ logger.fatal array.join("\n")
184
+ end
185
+ end
186
+
187
+ def logger(request)
188
+ request.logger || ActionView::Base.logger || stderr_logger
189
+ end
190
+
191
+ def stderr_logger
192
+ @stderr_logger ||= ActiveSupport::Logger.new($stderr)
193
+ end
194
+
195
+ def routes_inspector(exception)
196
+ if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
197
+ ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
198
+ end
199
+ end
200
+
201
+ def api_request?(content_type)
202
+ @response_format == :api && !content_type.html?
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ # This middleware can be used to diagnose deadlocks in the autoload interlock.
5
+ #
6
+ # To use it, insert it near the top of the middleware stack, using
7
+ # <tt>config/application.rb</tt>:
8
+ #
9
+ # config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
10
+ #
11
+ # After restarting the application and re-triggering the deadlock condition,
12
+ # <tt>/rails/locks</tt> will show a summary of all threads currently known to
13
+ # the interlock, which lock level they are holding or awaiting, and their
14
+ # current backtrace.
15
+ #
16
+ # Generally a deadlock will be caused by the interlock conflicting with some
17
+ # other external lock or blocking I/O call. These cannot be automatically
18
+ # identified, but should be visible in the displayed backtraces.
19
+ #
20
+ # NOTE: The formatting and content of this middleware's output is intended for
21
+ # human consumption, and should be expected to change between releases.
22
+ #
23
+ # This middleware exposes operational details of the server, with no access
24
+ # control. It should only be enabled when in use, and removed thereafter.
25
+ class DebugLocks
26
+ def initialize(app, path = "/rails/locks")
27
+ @app = app
28
+ @path = path
29
+ end
30
+
31
+ def call(env)
32
+ req = ActionDispatch::Request.new env
33
+
34
+ if req.get?
35
+ path = req.path_info.chomp("/".freeze)
36
+ if path == @path
37
+ return render_details(req)
38
+ end
39
+ end
40
+
41
+ @app.call(env)
42
+ end
43
+
44
+ private
45
+ def render_details(req)
46
+ threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
47
+ # The Interlock itself comes to a complete halt as long as this block
48
+ # is executing. That gives us a more consistent picture of everything,
49
+ # but creates a pretty strong Observer Effect.
50
+ #
51
+ # Most directly, that means we need to do as little as possible in
52
+ # this block. More widely, it means this middleware should remain a
53
+ # strictly diagnostic tool (to be used when something has gone wrong),
54
+ # and not for any sort of general monitoring.
55
+
56
+ raw_threads.each.with_index do |(thread, info), idx|
57
+ info[:index] = idx
58
+ info[:backtrace] = thread.backtrace
59
+ end
60
+
61
+ raw_threads
62
+ end
63
+
64
+ str = threads.map do |thread, info|
65
+ if info[:exclusive]
66
+ lock_state = "Exclusive".dup
67
+ elsif info[:sharing] > 0
68
+ lock_state = "Sharing".dup
69
+ lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
70
+ else
71
+ lock_state = "No lock".dup
72
+ end
73
+
74
+ if info[:waiting]
75
+ lock_state << " (yielded share)"
76
+ end
77
+
78
+ msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n".dup
79
+
80
+ if info[:sleeper]
81
+ msg << " Waiting in #{info[:sleeper]}"
82
+ msg << " to #{info[:purpose].to_s.inspect}" unless info[:purpose].nil?
83
+ msg << "\n"
84
+
85
+ if info[:compatible]
86
+ compat = info[:compatible].map { |c| c == false ? "share" : c.to_s.inspect }
87
+ msg << " may be pre-empted for: #{compat.join(', ')}\n"
88
+ end
89
+
90
+ blockers = threads.values.select { |binfo| blocked_by?(info, binfo, threads.values) }
91
+ msg << " blocked by: #{blockers.map { |i| i[:index] }.join(', ')}\n" if blockers.any?
92
+ end
93
+
94
+ blockees = threads.values.select { |binfo| blocked_by?(binfo, info, threads.values) }
95
+ msg << " blocking: #{blockees.map { |i| i[:index] }.join(', ')}\n" if blockees.any?
96
+
97
+ msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
98
+ end.join("\n\n---\n\n\n")
99
+
100
+ [200, { "Content-Type" => "text/plain", "Content-Length" => str.size }, [str]]
101
+ end
102
+
103
+ def blocked_by?(victim, blocker, all_threads)
104
+ return false if victim.equal?(blocker)
105
+
106
+ case victim[:sleeper]
107
+ when :start_sharing
108
+ blocker[:exclusive] ||
109
+ (!victim[:waiting] && blocker[:compatible] && !blocker[:compatible].include?(false))
110
+ when :start_exclusive
111
+ blocker[:sharing] > 0 ||
112
+ blocker[:exclusive] ||
113
+ (blocker[:compatible] && !blocker[:compatible].include?(victim[:purpose]))
114
+ when :yield_shares
115
+ blocker[:exclusive]
116
+ when :stop_exclusive
117
+ blocker[:exclusive] ||
118
+ victim[:compatible] &&
119
+ victim[:compatible].include?(blocker[:purpose]) &&
120
+ all_threads.all? { |other| !other[:compatible] || blocker.equal?(other) || other[:compatible].include?(blocker[:purpose]) }
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "rack/utils"
5
+
6
+ module ActionDispatch
7
+ class ExceptionWrapper
8
+ cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
9
+ "ActionController::RoutingError" => :not_found,
10
+ "AbstractController::ActionNotFound" => :not_found,
11
+ "ActionController::MethodNotAllowed" => :method_not_allowed,
12
+ "ActionController::UnknownHttpMethod" => :method_not_allowed,
13
+ "ActionController::NotImplemented" => :not_implemented,
14
+ "ActionController::UnknownFormat" => :not_acceptable,
15
+ "ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
16
+ "ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
17
+ "ActionDispatch::Http::Parameters::ParseError" => :bad_request,
18
+ "ActionController::BadRequest" => :bad_request,
19
+ "ActionController::ParameterMissing" => :bad_request,
20
+ "Rack::QueryParser::ParameterTypeError" => :bad_request,
21
+ "Rack::QueryParser::InvalidParameterError" => :bad_request
22
+ )
23
+
24
+ cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
25
+ "ActionView::MissingTemplate" => "missing_template",
26
+ "ActionController::RoutingError" => "routing_error",
27
+ "AbstractController::ActionNotFound" => "unknown_action",
28
+ "ActiveRecord::StatementInvalid" => "invalid_statement",
29
+ "ActionView::Template::Error" => "template_error"
30
+ )
31
+
32
+ attr_reader :backtrace_cleaner, :exception, :line_number, :file
33
+
34
+ def initialize(backtrace_cleaner, exception)
35
+ @backtrace_cleaner = backtrace_cleaner
36
+ @exception = original_exception(exception)
37
+
38
+ expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
39
+ end
40
+
41
+ def rescue_template
42
+ @@rescue_templates[@exception.class.name]
43
+ end
44
+
45
+ def status_code
46
+ self.class.status_code_for_exception(@exception.class.name)
47
+ end
48
+
49
+ def application_trace
50
+ clean_backtrace(:silent)
51
+ end
52
+
53
+ def framework_trace
54
+ clean_backtrace(:noise)
55
+ end
56
+
57
+ def full_trace
58
+ clean_backtrace(:all)
59
+ end
60
+
61
+ def traces
62
+ application_trace_with_ids = []
63
+ framework_trace_with_ids = []
64
+ full_trace_with_ids = []
65
+
66
+ full_trace.each_with_index do |trace, idx|
67
+ trace_with_id = { id: idx, trace: trace }
68
+
69
+ if application_trace.include?(trace)
70
+ application_trace_with_ids << trace_with_id
71
+ else
72
+ framework_trace_with_ids << trace_with_id
73
+ end
74
+
75
+ full_trace_with_ids << trace_with_id
76
+ end
77
+
78
+ {
79
+ "Application Trace" => application_trace_with_ids,
80
+ "Framework Trace" => framework_trace_with_ids,
81
+ "Full Trace" => full_trace_with_ids
82
+ }
83
+ end
84
+
85
+ def self.status_code_for_exception(class_name)
86
+ Rack::Utils.status_code(@@rescue_responses[class_name])
87
+ end
88
+
89
+ def source_extracts
90
+ backtrace.map do |trace|
91
+ file, line_number = extract_file_and_line_number(trace)
92
+
93
+ {
94
+ code: source_fragment(file, line_number),
95
+ line_number: line_number
96
+ }
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def backtrace
103
+ Array(@exception.backtrace)
104
+ end
105
+
106
+ def original_exception(exception)
107
+ if @@rescue_responses.has_key?(exception.cause.class.name)
108
+ exception.cause
109
+ else
110
+ exception
111
+ end
112
+ end
113
+
114
+ def clean_backtrace(*args)
115
+ if backtrace_cleaner
116
+ backtrace_cleaner.clean(backtrace, *args)
117
+ else
118
+ backtrace
119
+ end
120
+ end
121
+
122
+ def source_fragment(path, line)
123
+ return unless Rails.respond_to?(:root) && Rails.root
124
+ full_path = Rails.root.join(path)
125
+ if File.exist?(full_path)
126
+ File.open(full_path, "r") do |file|
127
+ start = [line - 3, 0].max
128
+ lines = file.each_line.drop(start).take(6)
129
+ Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
130
+ end
131
+ end
132
+ end
133
+
134
+ def extract_file_and_line_number(trace)
135
+ # Split by the first colon followed by some digits, which works for both
136
+ # Windows and Unix path styles.
137
+ file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
138
+ [file, line.to_i]
139
+ end
140
+
141
+ def expand_backtrace
142
+ @exception.backtrace.unshift(
143
+ @exception.to_s.split("\n")
144
+ ).flatten!
145
+ end
146
+ end
147
+ end