actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,82 +1,205 @@
1
- require 'action_dispatch/http/request'
2
- require 'action_dispatch/middleware/exception_wrapper'
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"
3
10
 
4
11
  module ActionDispatch
5
12
  # This middleware is responsible for logging exceptions and
6
13
  # showing a debugging page in case the request is local.
7
14
  class DebugExceptions
8
- RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
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
9
37
 
10
- def initialize(app)
11
- @app = app
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
12
57
  end
13
58
 
14
59
  def call(env)
15
- begin
16
- response = @app.call(env)
60
+ request = ActionDispatch::Request.new env
61
+ _, headers, body = response = @app.call(env)
17
62
 
18
- if response[1]['X-Cascade'] == 'pass'
19
- body = response[2]
20
- body.close if body.respond_to?(:close)
21
- raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
22
- end
23
- rescue Exception => exception
24
- raise exception if env['action_dispatch.show_exceptions'] == false
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}"
25
66
  end
26
67
 
27
- exception ? render_exception(env, exception) : response
68
+ response
69
+ rescue Exception => 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)
35
-
36
- if env['action_dispatch.show_detailed_exceptions']
37
- template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
38
- :request => Request.new(env),
39
- :exception => wrapper.exception,
40
- :application_trace => wrapper.application_trace,
41
- :framework_trace => wrapper.framework_trace,
42
- :full_trace => wrapper.full_trace
43
- )
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
44
93
 
94
+ def render_for_browser_request(request, wrapper)
95
+ template = create_template(request, wrapper)
45
96
  file = "rescues/#{wrapper.rescue_template}"
46
- body = template.render(:template => file, :layout => 'rescues/layout')
47
- render(wrapper.status_code, body)
48
- else
49
- raise exception
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)
50
106
  end
51
- end
52
107
 
53
- def render(status, body)
54
- [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
55
- end
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
56
128
 
57
- def log_error(env, wrapper)
58
- logger = logger(env)
59
- return unless logger
129
+ render(wrapper.status_code, formatted_body, format)
130
+ end
60
131
 
61
- exception = wrapper.exception
132
+ def create_template(request, wrapper)
133
+ traces = wrapper.traces
62
134
 
63
- trace = wrapper.application_trace
64
- trace = wrapper.framework_trace if trace.empty?
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
65
139
 
66
- ActiveSupport::Deprecation.silence do
67
- message = "\n#{exception.class} (#{exception.message}):\n"
68
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
69
- message << " " << trace.join("\n ")
70
- logger.fatal("#{message}\n\n")
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
+ )
71
155
  end
72
- end
73
156
 
74
- def logger(env)
75
- env['action_dispatch.logger'] || stderr_logger
76
- end
157
+ def render(status, body, format)
158
+ [status, { "Content-Type" => "#{format}; charset=#{Response.default_charset}", "Content-Length" => body.bytesize.to_s }, [body]]
159
+ end
77
160
 
78
- def stderr_logger
79
- @stderr_logger ||= Logger.new($stderr)
80
- end
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
81
204
  end
82
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,32 +1,41 @@
1
- require 'action_controller/metal/exceptions'
2
- require 'active_support/core_ext/exception'
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::NotImplemented' => :not_implemented,
13
- 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
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
14
22
  )
15
23
 
16
- cattr_accessor :rescue_templates
17
- @@rescue_templates = Hash.new('diagnostics')
18
- @@rescue_templates.merge!(
19
- 'ActionView::MissingTemplate' => 'missing_template',
20
- 'ActionController::RoutingError' => 'routing_error',
21
- 'AbstractController::ActionNotFound' => 'unknown_action',
22
- '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"
23
30
  )
24
31
 
25
- attr_reader :env, :exception
32
+ attr_reader :backtrace_cleaner, :exception, :line_number, :file
26
33
 
27
- def initialize(env, exception)
28
- @env = env
34
+ def initialize(backtrace_cleaner, exception)
35
+ @backtrace_cleaner = backtrace_cleaner
29
36
  @exception = original_exception(exception)
37
+
38
+ expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
30
39
  end
31
40
 
32
41
  def rescue_template
@@ -49,34 +58,90 @@ module ActionDispatch
49
58
  clean_backtrace(:all)
50
59
  end
51
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
+
52
85
  def self.status_code_for_exception(class_name)
53
86
  Rack::Utils.status_code(@@rescue_responses[class_name])
54
87
  end
55
88
 
56
- private
89
+ def source_extracts
90
+ backtrace.map do |trace|
91
+ file, line_number = extract_file_and_line_number(trace)
57
92
 
58
- def original_exception(exception)
59
- if registered_original_exception?(exception)
60
- exception.original_exception
61
- else
62
- exception
93
+ {
94
+ code: source_fragment(file, line_number),
95
+ line_number: line_number
96
+ }
63
97
  end
64
98
  end
65
99
 
66
- def registered_original_exception?(exception)
67
- exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
68
- end
100
+ private
69
101
 
70
- def clean_backtrace(*args)
71
- if backtrace_cleaner
72
- backtrace_cleaner.clean(@exception.backtrace, *args)
73
- else
74
- @exception.backtrace
102
+ def backtrace
103
+ Array(@exception.backtrace)
75
104
  end
76
- end
77
105
 
78
- def backtrace_cleaner
79
- @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
80
- end
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
81
146
  end
82
147
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/body_proxy"
4
+
5
+ module ActionDispatch
6
+ class Executor
7
+ def initialize(app, executor)
8
+ @app, @executor = app, executor
9
+ end
10
+
11
+ def call(env)
12
+ state = @executor.run!
13
+ begin
14
+ response = @app.call(env)
15
+ returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
16
+ ensure
17
+ state.complete! unless returned
18
+ end
19
+ end
20
+ end
21
+ end