actionpack 4.2.8 → 5.2.4.2

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 (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  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 +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -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 +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,110 +1,205 @@
1
- require 'action_dispatch/http/request'
2
- require 'action_dispatch/middleware/exception_wrapper'
3
- require 'action_dispatch/routing/inspector'
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"
4
10
 
5
11
  module ActionDispatch
6
12
  # This middleware is responsible for logging exceptions and
7
13
  # showing a debugging page in case the request is local.
8
14
  class DebugExceptions
9
- RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__)
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
10
52
 
11
- def initialize(app, routes_app = nil)
12
- @app = app
13
- @routes_app = routes_app
53
+ def initialize(app, routes_app = nil, response_format = :default)
54
+ @app = app
55
+ @routes_app = routes_app
56
+ @response_format = response_format
14
57
  end
15
58
 
16
59
  def call(env)
60
+ request = ActionDispatch::Request.new env
17
61
  _, headers, body = response = @app.call(env)
18
62
 
19
- if headers['X-Cascade'] == 'pass'
63
+ if headers["X-Cascade"] == "pass"
20
64
  body.close if body.respond_to?(:close)
21
65
  raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
22
66
  end
23
67
 
24
68
  response
25
69
  rescue Exception => exception
26
- raise exception if env['action_dispatch.show_exceptions'] == false
27
- render_exception(env, exception)
70
+ raise exception unless request.show_exceptions?
71
+ render_exception(request, exception)
28
72
  end
29
73
 
30
74
  private
31
75
 
32
- def render_exception(env, exception)
33
- wrapper = ExceptionWrapper.new(env, exception)
34
- log_error(env, wrapper)
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
35
131
 
36
- if env['action_dispatch.show_detailed_exceptions']
37
- request = Request.new(env)
132
+ def create_template(request, wrapper)
38
133
  traces = wrapper.traces
39
134
 
40
- trace_to_show = 'Application Trace'
41
- if traces[trace_to_show].empty? && wrapper.rescue_template != 'routing_error'
42
- trace_to_show = 'Full Trace'
135
+ trace_to_show = "Application Trace"
136
+ if traces[trace_to_show].empty? && wrapper.rescue_template != "routing_error"
137
+ trace_to_show = "Full Trace"
43
138
  end
44
139
 
45
140
  if source_to_show = traces[trace_to_show].first
46
141
  source_to_show_id = source_to_show[:id]
47
142
  end
48
143
 
49
- template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
144
+ DebugView.new([RESCUES_TEMPLATE_PATH],
50
145
  request: request,
51
146
  exception: wrapper.exception,
52
147
  traces: traces,
53
148
  show_source_idx: source_to_show_id,
54
149
  trace_to_show: trace_to_show,
55
- routes_inspector: routes_inspector(exception),
150
+ routes_inspector: routes_inspector(wrapper.exception),
56
151
  source_extracts: wrapper.source_extracts,
57
152
  line_number: wrapper.line_number,
58
153
  file: wrapper.file
59
154
  )
60
- file = "rescues/#{wrapper.rescue_template}"
155
+ end
61
156
 
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)
70
- else
71
- raise exception
157
+ def render(status, body, format)
158
+ [status, { "Content-Type" => "#{format}; charset=#{Response.default_charset}", "Content-Length" => body.bytesize.to_s }, [body]]
72
159
  end
73
- end
74
160
 
75
- def render(status, body, format)
76
- [status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
77
- end
161
+ def log_error(request, wrapper)
162
+ logger = logger(request)
163
+ return unless logger
78
164
 
79
- def log_error(env, wrapper)
80
- logger = logger(env)
81
- return unless logger
165
+ exception = wrapper.exception
82
166
 
83
- exception = wrapper.exception
167
+ trace = wrapper.application_trace
168
+ trace = wrapper.framework_trace if trace.empty?
84
169
 
85
- trace = wrapper.application_trace
86
- trace = wrapper.framework_trace if trace.empty?
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
87
178
 
88
- ActiveSupport::Deprecation.silence do
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")
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
93
185
  end
94
- end
95
186
 
96
- def logger(env)
97
- env['action_dispatch.logger'] || stderr_logger
98
- end
187
+ def logger(request)
188
+ request.logger || ActionView::Base.logger || stderr_logger
189
+ end
99
190
 
100
- def stderr_logger
101
- @stderr_logger ||= ActiveSupport::Logger.new($stderr)
102
- end
191
+ def stderr_logger
192
+ @stderr_logger ||= ActiveSupport::Logger.new($stderr)
193
+ end
103
194
 
104
- def routes_inspector(exception)
105
- if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
106
- ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
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?
107
203
  end
108
- end
109
204
  end
110
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
@@ -1,40 +1,41 @@
1
- require 'action_controller/metal/exceptions'
2
- require 'active_support/core_ext/module/attribute_accessors'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "rack/utils"
3
5
 
4
6
  module ActionDispatch
5
7
  class ExceptionWrapper
6
- cattr_accessor :rescue_responses
7
- @@rescue_responses = Hash.new(:internal_server_error)
8
- @@rescue_responses.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::ParamsParser::ParseError' => :bad_request,
18
- 'ActionController::BadRequest' => :bad_request,
19
- 'ActionController::ParameterMissing' => :bad_request
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
20
22
  )
21
23
 
22
- cattr_accessor :rescue_templates
23
- @@rescue_templates = Hash.new('diagnostics')
24
- @@rescue_templates.merge!(
25
- 'ActionView::MissingTemplate' => 'missing_template',
26
- 'ActionController::RoutingError' => 'routing_error',
27
- 'AbstractController::ActionNotFound' => 'unknown_action',
28
- 'ActionView::Template::Error' => 'template_error'
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"
29
30
  )
30
31
 
31
- attr_reader :env, :exception, :line_number, :file
32
+ attr_reader :backtrace_cleaner, :exception, :line_number, :file
32
33
 
33
- def initialize(env, exception)
34
- @env = env
34
+ def initialize(backtrace_cleaner, exception)
35
+ @backtrace_cleaner = backtrace_cleaner
35
36
  @exception = original_exception(exception)
36
37
 
37
- expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
38
+ expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
38
39
  end
39
40
 
40
41
  def rescue_template
@@ -58,7 +59,7 @@ module ActionDispatch
58
59
  end
59
60
 
60
61
  def traces
61
- appplication_trace_with_ids = []
62
+ application_trace_with_ids = []
62
63
  framework_trace_with_ids = []
63
64
  full_trace_with_ids = []
64
65
 
@@ -66,7 +67,7 @@ module ActionDispatch
66
67
  trace_with_id = { id: idx, trace: trace }
67
68
 
68
69
  if application_trace.include?(trace)
69
- appplication_trace_with_ids << trace_with_id
70
+ application_trace_with_ids << trace_with_id
70
71
  else
71
72
  framework_trace_with_ids << trace_with_id
72
73
  end
@@ -75,7 +76,7 @@ module ActionDispatch
75
76
  end
76
77
 
77
78
  {
78
- "Application Trace" => appplication_trace_with_ids,
79
+ "Application Trace" => application_trace_with_ids,
79
80
  "Framework Trace" => framework_trace_with_ids,
80
81
  "Full Trace" => full_trace_with_ids
81
82
  }
@@ -87,8 +88,7 @@ module ActionDispatch
87
88
 
88
89
  def source_extracts
89
90
  backtrace.map do |trace|
90
- file, line = trace.split(":")
91
- line_number = line.to_i
91
+ file, line_number = extract_file_and_line_number(trace)
92
92
 
93
93
  {
94
94
  code: source_fragment(file, line_number),
@@ -99,50 +99,49 @@ module ActionDispatch
99
99
 
100
100
  private
101
101
 
102
- def backtrace
103
- Array(@exception.backtrace)
104
- end
105
-
106
- def original_exception(exception)
107
- if registered_original_exception?(exception)
108
- exception.original_exception
109
- else
110
- exception
102
+ def backtrace
103
+ Array(@exception.backtrace)
111
104
  end
112
- end
113
105
 
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
- def clean_backtrace(*args)
119
- if backtrace_cleaner
120
- backtrace_cleaner.clean(backtrace, *args)
121
- else
122
- backtrace
106
+ def original_exception(exception)
107
+ if @@rescue_responses.has_key?(exception.cause.class.name)
108
+ exception.cause
109
+ else
110
+ exception
111
+ end
123
112
  end
124
- end
125
113
 
126
- def backtrace_cleaner
127
- @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
128
- end
114
+ def clean_backtrace(*args)
115
+ if backtrace_cleaner
116
+ backtrace_cleaner.clean(backtrace, *args)
117
+ else
118
+ backtrace
119
+ end
120
+ end
129
121
 
130
- def source_fragment(path, line)
131
- return unless Rails.respond_to?(:root) && Rails.root
132
- full_path = Rails.root.join(path)
133
- if File.exist?(full_path)
134
- File.open(full_path, "r") do |file|
135
- start = [line - 3, 0].max
136
- lines = file.each_line.drop(start).take(6)
137
- Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
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
138
131
  end
139
132
  end
140
- end
141
133
 
142
- def expand_backtrace
143
- @exception.backtrace.unshift(
144
- @exception.to_s.split("\n")
145
- ).flatten!
146
- end
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
147
146
  end
148
147
  end