actionpack 6.0.0

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +311 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +58 -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 +267 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +150 -0
  10. data/lib/abstract_controller/callbacks.rb +224 -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 +32 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +67 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +271 -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 +81 -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 +280 -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 +151 -0
  32. data/lib/action_controller/metal/default_headers.rb +17 -0
  33. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  35. data/lib/action_controller/metal/exceptions.rb +74 -0
  36. data/lib/action_controller/metal/flash.rb +61 -0
  37. data/lib/action_controller/metal/force_ssl.rb +58 -0
  38. data/lib/action_controller/metal/head.rb +60 -0
  39. data/lib/action_controller/metal/helpers.rb +122 -0
  40. data/lib/action_controller/metal/http_authentication.rb +518 -0
  41. data/lib/action_controller/metal/implicit_render.rb +63 -0
  42. data/lib/action_controller/metal/instrumentation.rb +105 -0
  43. data/lib/action_controller/metal/live.rb +314 -0
  44. data/lib/action_controller/metal/mime_responds.rb +324 -0
  45. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  46. data/lib/action_controller/metal/params_wrapper.rb +297 -0
  47. data/lib/action_controller/metal/redirecting.rb +133 -0
  48. data/lib/action_controller/metal/renderers.rb +181 -0
  49. data/lib/action_controller/metal/rendering.rb +122 -0
  50. data/lib/action_controller/metal/request_forgery_protection.rb +456 -0
  51. data/lib/action_controller/metal/rescue.rb +28 -0
  52. data/lib/action_controller/metal/streaming.rb +223 -0
  53. data/lib/action_controller/metal/strong_parameters.rb +1105 -0
  54. data/lib/action_controller/metal/testing.rb +16 -0
  55. data/lib/action_controller/metal/url_for.rb +58 -0
  56. data/lib/action_controller/railtie.rb +89 -0
  57. data/lib/action_controller/railties/helpers.rb +24 -0
  58. data/lib/action_controller/renderer.rb +130 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +626 -0
  61. data/lib/action_dispatch.rb +114 -0
  62. data/lib/action_dispatch/http/cache.rb +226 -0
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +284 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +86 -0
  66. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  67. data/lib/action_dispatch/http/headers.rb +132 -0
  68. data/lib/action_dispatch/http/mime_negotiation.rb +177 -0
  69. data/lib/action_dispatch/http/mime_type.rb +350 -0
  70. data/lib/action_dispatch/http/mime_types.rb +50 -0
  71. data/lib/action_dispatch/http/parameter_filter.rb +12 -0
  72. data/lib/action_dispatch/http/parameters.rb +136 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  74. data/lib/action_dispatch/http/request.rb +427 -0
  75. data/lib/action_dispatch/http/response.rb +534 -0
  76. data/lib/action_dispatch/http/upload.rb +92 -0
  77. data/lib/action_dispatch/http/url.rb +350 -0
  78. data/lib/action_dispatch/journey.rb +7 -0
  79. data/lib/action_dispatch/journey/formatter.rb +189 -0
  80. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  81. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  82. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  83. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  84. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  85. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  86. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +141 -0
  88. data/lib/action_dispatch/journey/parser.rb +199 -0
  89. data/lib/action_dispatch/journey/parser.y +50 -0
  90. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +203 -0
  92. data/lib/action_dispatch/journey/route.rb +204 -0
  93. data/lib/action_dispatch/journey/router.rb +153 -0
  94. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  95. data/lib/action_dispatch/journey/routes.rb +81 -0
  96. data/lib/action_dispatch/journey/scanner.rb +71 -0
  97. data/lib/action_dispatch/journey/visitors.rb +268 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  102. data/lib/action_dispatch/middleware/callbacks.rb +34 -0
  103. data/lib/action_dispatch/middleware/cookies.rb +663 -0
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +185 -0
  105. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  106. data/lib/action_dispatch/middleware/debug_view.rb +68 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -0
  108. data/lib/action_dispatch/middleware/executor.rb +21 -0
  109. data/lib/action_dispatch/middleware/flash.rb +300 -0
  110. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +61 -0
  112. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  113. data/lib/action_dispatch/middleware/remote_ip.rb +181 -0
  114. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +113 -0
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  120. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  121. data/lib/action_dispatch/middleware/stack.rb +148 -0
  122. data/lib/action_dispatch/middleware/static.rb +129 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +24 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +29 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +38 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +165 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  148. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  149. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +203 -0
  150. data/lib/action_dispatch/railtie.rb +58 -0
  151. data/lib/action_dispatch/request/session.rb +242 -0
  152. data/lib/action_dispatch/request/utils.rb +78 -0
  153. data/lib/action_dispatch/routing.rb +261 -0
  154. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  155. data/lib/action_dispatch/routing/inspector.rb +274 -0
  156. data/lib/action_dispatch/routing/mapper.rb +2289 -0
  157. data/lib/action_dispatch/routing/polymorphic_routes.rb +351 -0
  158. data/lib/action_dispatch/routing/redirection.rb +201 -0
  159. data/lib/action_dispatch/routing/route_set.rb +887 -0
  160. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  161. data/lib/action_dispatch/routing/url_for.rb +237 -0
  162. data/lib/action_dispatch/system_test_case.rb +168 -0
  163. data/lib/action_dispatch/system_testing/browser.rb +80 -0
  164. data/lib/action_dispatch/system_testing/driver.rb +68 -0
  165. data/lib/action_dispatch/system_testing/server.rb +31 -0
  166. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +97 -0
  167. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +33 -0
  168. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  169. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  170. data/lib/action_dispatch/testing/assertions.rb +24 -0
  171. data/lib/action_dispatch/testing/assertions/response.rb +106 -0
  172. data/lib/action_dispatch/testing/assertions/routing.rb +234 -0
  173. data/lib/action_dispatch/testing/integration.rb +659 -0
  174. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  175. data/lib/action_dispatch/testing/test_process.rb +50 -0
  176. data/lib/action_dispatch/testing/test_request.rb +71 -0
  177. data/lib/action_dispatch/testing/test_response.rb +25 -0
  178. data/lib/action_pack.rb +26 -0
  179. data/lib/action_pack/gem_version.rb +17 -0
  180. data/lib/action_pack/version.rb +10 -0
  181. metadata +329 -0
@@ -0,0 +1,185 @@
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
+
7
+ require "active_support/actionable_error"
8
+
9
+ require "action_view"
10
+ require "action_view/base"
11
+
12
+ module ActionDispatch
13
+ # This middleware is responsible for logging exceptions and
14
+ # showing a debugging page in case the request is local.
15
+ class DebugExceptions
16
+ cattr_reader :interceptors, instance_accessor: false, default: []
17
+
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
28
+ end
29
+
30
+ def call(env)
31
+ request = ActionDispatch::Request.new env
32
+ _, headers, body = response = @app.call(env)
33
+
34
+ if headers["X-Cascade"] == "pass"
35
+ body.close if body.respond_to?(:close)
36
+ raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
37
+ end
38
+
39
+ response
40
+ rescue Exception => exception
41
+ invoke_interceptors(request, exception)
42
+ raise exception unless request.show_exceptions?
43
+ render_exception(request, exception)
44
+ end
45
+
46
+ private
47
+
48
+ def invoke_interceptors(request, exception)
49
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
50
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
51
+
52
+ @interceptors.each do |interceptor|
53
+ interceptor.call(request, exception)
54
+ rescue Exception
55
+ log_error(request, wrapper)
56
+ end
57
+ end
58
+
59
+ def render_exception(request, exception)
60
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
61
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
62
+ log_error(request, wrapper)
63
+
64
+ if request.get_header("action_dispatch.show_detailed_exceptions")
65
+ begin
66
+ content_type = request.formats.first
67
+ rescue Mime::Type::InvalidMimeType
68
+ render_for_api_request(Mime[:text], wrapper)
69
+ end
70
+
71
+ if api_request?(content_type)
72
+ render_for_api_request(content_type, wrapper)
73
+ else
74
+ render_for_browser_request(request, wrapper)
75
+ end
76
+ else
77
+ raise exception
78
+ end
79
+ end
80
+
81
+ def render_for_browser_request(request, wrapper)
82
+ template = create_template(request, wrapper)
83
+ file = "rescues/#{wrapper.rescue_template}"
84
+
85
+ if request.xhr?
86
+ body = template.render(template: file, layout: false, formats: [:text])
87
+ format = "text/plain"
88
+ else
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_request(content_type, 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
+ to_format = "to_#{content_type.to_sym}"
107
+
108
+ if content_type && body.respond_to?(to_format)
109
+ formatted_body = body.public_send(to_format)
110
+ format = content_type
111
+ else
112
+ formatted_body = body.to_json
113
+ format = Mime[:json]
114
+ end
115
+
116
+ render(wrapper.status_code, formatted_body, format)
117
+ end
118
+
119
+ def create_template(request, wrapper)
120
+ DebugView.new(
121
+ request: request,
122
+ exception_wrapper: wrapper,
123
+ exception: wrapper.exception,
124
+ traces: wrapper.traces,
125
+ show_source_idx: wrapper.source_to_show_id,
126
+ trace_to_show: wrapper.trace_to_show,
127
+ routes_inspector: routes_inspector(wrapper.exception),
128
+ source_extracts: wrapper.source_extracts,
129
+ line_number: wrapper.line_number,
130
+ file: wrapper.file
131
+ )
132
+ end
133
+
134
+ def render(status, body, format)
135
+ [status, { "Content-Type" => "#{format}; charset=#{Response.default_charset}", "Content-Length" => body.bytesize.to_s }, [body]]
136
+ end
137
+
138
+ def log_error(request, wrapper)
139
+ logger = logger(request)
140
+ return unless logger
141
+
142
+ exception = wrapper.exception
143
+
144
+ trace = wrapper.application_trace
145
+ trace = wrapper.framework_trace if trace.empty?
146
+
147
+ ActiveSupport::Deprecation.silence do
148
+ message = []
149
+ message << " "
150
+ message << "#{exception.class} (#{exception.message}):"
151
+ message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
152
+ message << " "
153
+ message.concat(trace)
154
+
155
+ log_array(logger, message)
156
+ end
157
+ end
158
+
159
+ def log_array(logger, array)
160
+ if logger.formatter && logger.formatter.respond_to?(:tags_text)
161
+ logger.fatal array.join("\n#{logger.formatter.tags_text}")
162
+ else
163
+ logger.fatal array.join("\n")
164
+ end
165
+ end
166
+
167
+ def logger(request)
168
+ request.logger || ActionView::Base.logger || stderr_logger
169
+ end
170
+
171
+ def stderr_logger
172
+ @stderr_logger ||= ActiveSupport::Logger.new($stderr)
173
+ end
174
+
175
+ def routes_inspector(exception)
176
+ if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
177
+ ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
178
+ end
179
+ end
180
+
181
+ def api_request?(content_type)
182
+ @response_format == :api && !content_type.html?
183
+ end
184
+ end
185
+ 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("/")
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"
67
+ elsif info[:sharing] > 0
68
+ lock_state = +"Sharing"
69
+ lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
70
+ else
71
+ lock_state = +"No lock"
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"
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,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pp"
4
+
5
+ require "action_view"
6
+ require "action_view/base"
7
+
8
+ module ActionDispatch
9
+ class DebugView < ActionView::Base # :nodoc:
10
+ RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
11
+
12
+ def initialize(assigns)
13
+ paths = [RESCUES_TEMPLATE_PATH]
14
+ lookup_context = ActionView::LookupContext.new(paths)
15
+ super(lookup_context, assigns)
16
+ end
17
+
18
+ def compiled_method_container
19
+ self.class
20
+ end
21
+
22
+ def debug_params(params)
23
+ clean_params = params.clone
24
+ clean_params.delete("action")
25
+ clean_params.delete("controller")
26
+
27
+ if clean_params.empty?
28
+ "None"
29
+ else
30
+ PP.pp(clean_params, +"", 200)
31
+ end
32
+ end
33
+
34
+ def debug_headers(headers)
35
+ if headers.present?
36
+ headers.inspect.gsub(",", ",\n")
37
+ else
38
+ "None"
39
+ end
40
+ end
41
+
42
+ def debug_hash(object)
43
+ object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
44
+ end
45
+
46
+ def render(*)
47
+ logger = ActionView::Base.logger
48
+
49
+ if logger && logger.respond_to?(:silence)
50
+ logger.silence { super }
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def protect_against_forgery?
57
+ false
58
+ end
59
+
60
+ def params_valid?
61
+ begin
62
+ @request.parameters
63
+ rescue ActionController::BadRequest
64
+ false
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,181 @@
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
+ "Mime::Type::InvalidMimeType" => :not_acceptable,
16
+ "ActionController::MissingExactTemplate" => :not_acceptable,
17
+ "ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
18
+ "ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
19
+ "ActionDispatch::Http::Parameters::ParseError" => :bad_request,
20
+ "ActionController::BadRequest" => :bad_request,
21
+ "ActionController::ParameterMissing" => :bad_request,
22
+ "Rack::QueryParser::ParameterTypeError" => :bad_request,
23
+ "Rack::QueryParser::InvalidParameterError" => :bad_request
24
+ )
25
+
26
+ cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
27
+ "ActionView::MissingTemplate" => "missing_template",
28
+ "ActionController::RoutingError" => "routing_error",
29
+ "AbstractController::ActionNotFound" => "unknown_action",
30
+ "ActiveRecord::StatementInvalid" => "invalid_statement",
31
+ "ActionView::Template::Error" => "template_error",
32
+ "ActionController::MissingExactTemplate" => "missing_exact_template",
33
+ )
34
+
35
+ cattr_accessor :wrapper_exceptions, default: [
36
+ "ActionView::Template::Error"
37
+ ]
38
+
39
+ attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file
40
+
41
+ def initialize(backtrace_cleaner, exception)
42
+ @backtrace_cleaner = backtrace_cleaner
43
+ @exception = exception
44
+ @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
45
+
46
+ expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
47
+ end
48
+
49
+ def unwrapped_exception
50
+ if wrapper_exceptions.include?(exception.class.to_s)
51
+ exception.cause
52
+ else
53
+ exception
54
+ end
55
+ end
56
+
57
+ def rescue_template
58
+ @@rescue_templates[@exception.class.name]
59
+ end
60
+
61
+ def status_code
62
+ self.class.status_code_for_exception(unwrapped_exception.class.name)
63
+ end
64
+
65
+ def application_trace
66
+ clean_backtrace(:silent)
67
+ end
68
+
69
+ def framework_trace
70
+ clean_backtrace(:noise)
71
+ end
72
+
73
+ def full_trace
74
+ clean_backtrace(:all)
75
+ end
76
+
77
+ def traces
78
+ application_trace_with_ids = []
79
+ framework_trace_with_ids = []
80
+ full_trace_with_ids = []
81
+
82
+ full_trace.each_with_index do |trace, idx|
83
+ trace_with_id = {
84
+ exception_object_id: @exception.object_id,
85
+ id: idx,
86
+ trace: trace
87
+ }
88
+
89
+ if application_trace.include?(trace)
90
+ application_trace_with_ids << trace_with_id
91
+ else
92
+ framework_trace_with_ids << trace_with_id
93
+ end
94
+
95
+ full_trace_with_ids << trace_with_id
96
+ end
97
+
98
+ {
99
+ "Application Trace" => application_trace_with_ids,
100
+ "Framework Trace" => framework_trace_with_ids,
101
+ "Full Trace" => full_trace_with_ids
102
+ }
103
+ end
104
+
105
+ def self.status_code_for_exception(class_name)
106
+ Rack::Utils.status_code(@@rescue_responses[class_name])
107
+ end
108
+
109
+ def source_extracts
110
+ backtrace.map do |trace|
111
+ file, line_number = extract_file_and_line_number(trace)
112
+
113
+ {
114
+ code: source_fragment(file, line_number),
115
+ line_number: line_number
116
+ }
117
+ end
118
+ end
119
+
120
+ def trace_to_show
121
+ if traces["Application Trace"].empty? && rescue_template != "routing_error"
122
+ "Full Trace"
123
+ else
124
+ "Application Trace"
125
+ end
126
+ end
127
+
128
+ def source_to_show_id
129
+ (traces[trace_to_show].first || {})[:id]
130
+ end
131
+
132
+ private
133
+
134
+ def backtrace
135
+ Array(@exception.backtrace)
136
+ end
137
+
138
+ def causes_for(exception)
139
+ return enum_for(__method__, exception) unless block_given?
140
+
141
+ yield exception while exception = exception.cause
142
+ end
143
+
144
+ def wrapped_causes_for(exception, backtrace_cleaner)
145
+ causes_for(exception).map { |cause| self.class.new(backtrace_cleaner, cause) }
146
+ end
147
+
148
+ def clean_backtrace(*args)
149
+ if backtrace_cleaner
150
+ backtrace_cleaner.clean(backtrace, *args)
151
+ else
152
+ backtrace
153
+ end
154
+ end
155
+
156
+ def source_fragment(path, line)
157
+ return unless Rails.respond_to?(:root) && Rails.root
158
+ full_path = Rails.root.join(path)
159
+ if File.exist?(full_path)
160
+ File.open(full_path, "r") do |file|
161
+ start = [line - 3, 0].max
162
+ lines = file.each_line.drop(start).take(6)
163
+ Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
164
+ end
165
+ end
166
+ end
167
+
168
+ def extract_file_and_line_number(trace)
169
+ # Split by the first colon followed by some digits, which works for both
170
+ # Windows and Unix path styles.
171
+ file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
172
+ [file, line.to_i]
173
+ end
174
+
175
+ def expand_backtrace
176
+ @exception.backtrace.unshift(
177
+ @exception.to_s.split("\n")
178
+ ).flatten!
179
+ end
180
+ end
181
+ end