Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 159 lines (135 sloc) 6.074 kb
b1aee9f @josh All AD modules are "deferrable"
josh authored
1 require 'active_support/core_ext/exception'
53c6984 @josevalim Add notifications to ActionDispatch::ShowExceptions, this can be used as...
josevalim authored
2 require 'active_support/notifications'
b1aee9f @josh All AD modules are "deferrable"
josh authored
3 require 'action_dispatch/http/request'
86fc43f ActionPack components should no longer have undeclared dependencies.
Yehuda Katz + Carl Lerche authored
4
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
5 module ActionDispatch
53c6984 @josevalim Add notifications to ActionDispatch::ShowExceptions, this can be used as...
josevalim authored
6 # This middleware rescues any exception returned by the application and renders
7 # nice exception pages if it's being rescued locally.
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
8 class ShowExceptions
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
9 RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
10
11 cattr_accessor :rescue_responses
12 @@rescue_responses = Hash.new(:internal_server_error)
13 @@rescue_responses.update({
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
14 'ActionController::RoutingError' => :not_found,
7217d64 @josh Use AbstractController error constants
josh authored
15 'AbstractController::ActionNotFound' => :not_found,
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
16 'ActiveRecord::RecordNotFound' => :not_found,
17 'ActiveRecord::StaleObjectError' => :conflict,
18 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
19 'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
20 'ActionController::MethodNotAllowed' => :method_not_allowed,
21 'ActionController::NotImplemented' => :not_implemented,
22 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
23 })
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
24
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
25 cattr_accessor :rescue_templates
26 @@rescue_templates = Hash.new('diagnostics')
27 @@rescue_templates.update({
a1f280e Got all the dispatch tests running on new base
Carl Lerche authored
28 'ActionView::MissingTemplate' => 'missing_template',
29 'ActionController::RoutingError' => 'routing_error',
7217d64 @josh Use AbstractController error constants
josh authored
30 'AbstractController::ActionNotFound' => 'unknown_action',
c130409 Reorganize autoloads:
Carlhuda authored
31 'ActionView::Template::Error' => 'template_error'
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
32 })
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
33
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
34 FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
760cb63 @FooBarWidget Make the default 500 Internal Server Error page more friendly. Many peop...
FooBarWidget authored
35 ["<html><body><h1>500 Internal Server Error</h1>" <<
36 "If you are the administrator of this website, then please read this web " <<
37 "application's log file and/or the web server's log file to find out what " <<
38 "went wrong.</body></html>"]]
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
39
40 def initialize(app, consider_all_requests_local = false)
41 @app = app
42 @consider_all_requests_local = consider_all_requests_local
43 end
44
45 def call(env)
117cad8 @krekoten Refactor to handle the X-Cascade without having to raise an exception
krekoten authored
46 begin
47 status, headers, body = @app.call(env)
48 exception = nil
6c280f3 RouteSet does not raise ActionController::RoutingError when no routes ma...
Carl Lerche authored
49
117cad8 @krekoten Refactor to handle the X-Cascade without having to raise an exception
krekoten authored
50 # Only this middleware cares about RoutingError. So, let's just raise
51 # it here.
52 if headers['X-Cascade'] == 'pass'
8874733 @tenderlove use raise to create exceptions and to set the backtrace
tenderlove authored
53 raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
117cad8 @krekoten Refactor to handle the X-Cascade without having to raise an exception
krekoten authored
54 end
55 rescue Exception => exception
20062e7 @tenderlove reraising should be in the rescue block
tenderlove authored
56 raise exception if env['action_dispatch.show_exceptions'] == false
6c280f3 RouteSet does not raise ActionController::RoutingError when no routes ma...
Carl Lerche authored
57 end
117cad8 @krekoten Refactor to handle the X-Cascade without having to raise an exception
krekoten authored
58
59 exception ? render_exception(env, exception) : [status, headers, body]
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
60 end
00a9d4b Merge branch 'master' into wip_abstract_controller
Yehuda Katz + Carl Lerche authored
61
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
62 private
63 def render_exception(env, exception)
64 log_error(exception)
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
65
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
66 request = Request.new(env)
3698da6 @spastorino Moves local_request? to require.local?
spastorino authored
67 if @consider_all_requests_local || request.local?
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
68 rescue_action_locally(request, exception)
69 else
70 rescue_action_in_public(exception)
71 end
72 rescue Exception => failsafe_error
bd98058 @jeremy Include backtrace in failsafe log. Rescue possible exceptions in failsaf...
jeremy authored
73 $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
74 FAILSAFE_RESPONSE
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
75 end
76
77 # Render detailed diagnostics for unhandled exceptions rescued from
78 # a controller action.
79 def rescue_action_locally(request, exception)
80 template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
81 :request => request,
eb39d0f @josh Use backtrace cleaner for dev mode exception page
josh authored
82 :exception => exception,
83 :application_trace => application_trace(exception),
84 :framework_trace => framework_trace(exception),
85 :full_trace => full_trace(exception)
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
86 )
87 file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
88 body = template.render(:file => file, :layout => 'rescues/layout.erb')
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
89 render(status_code(exception), body)
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
90 end
91
92 # Attempts to render a static error page based on the
93 # <tt>status_code</tt> thrown, or just return headers if no such file
94 # exists. At first, it will try to render a localized static page.
95 # For example, if a 500 error is being handled Rails and locale is :da,
96 # it will first attempt to render the file at <tt>public/500.da.html</tt>
97 # then attempt to render <tt>public/500.html</tt>. If none of them exist,
98 # the body of the response will be left empty.
99 def rescue_action_in_public(exception)
100 status = status_code(exception)
101 locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
102 path = "#{public_path}/#{status}.html"
103
104 if locale_path && File.exist?(locale_path)
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
105 render(status, File.read(locale_path))
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
106 elsif File.exist?(path)
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
107 render(status, File.read(path))
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
108 else
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
109 render(status, '')
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
110 end
111 end
112
113 def status_code(exception)
a1bf2f9 @josh AD::StatusCodes support is now part of rack
josh authored
114 Rack::Utils.status_code(@@rescue_responses[exception.class.name])
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
115 end
116
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
117 def render(status, body)
bb6cd6d @tarsolya Use Rack::Utils.bytesize when calculating content-length of exception pa...
tarsolya authored
118 [status, {'Content-Type' => 'text/html', 'Content-Length' => body.bytesize.to_s}, [body]]
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
119 end
120
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
121 def public_path
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
122 defined?(Rails.public_path) ? Rails.public_path : 'public_path'
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
123 end
124
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
125 def log_error(exception)
126 return unless logger
127
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
128 ActiveSupport::Deprecation.silence do
6c2d974 @josevalim Use annoted source code in Template:Error to avoid special cases in the ...
josevalim authored
129 message = "\n#{exception.class} (#{exception.message}):\n"
047e411 @josevalim annoted_source_code may return nil if an erro rhappens during template c...
josevalim authored
130 message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
2117994 @josevalim Ensure show exceptions middleware properly filters backtrace before logg...
josevalim authored
131 message << " " << application_trace(exception).join("\n ")
6c2d974 @josevalim Use annoted source code in Template:Error to avoid special cases in the ...
josevalim authored
132 logger.fatal("#{message}\n\n")
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
133 end
134 end
135
eb39d0f @josh Use backtrace cleaner for dev mode exception page
josh authored
136 def application_trace(exception)
137 clean_backtrace(exception, :silent)
138 end
139
140 def framework_trace(exception)
141 clean_backtrace(exception, :noise)
142 end
143
144 def full_trace(exception)
145 clean_backtrace(exception, :all)
146 end
147
148 def clean_backtrace(exception, *args)
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
149 defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
eb39d0f @josh Use backtrace cleaner for dev mode exception page
josh authored
150 Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
151 exception.backtrace
152 end
153
154 def logger
8118fca @josh Merge Failsafe middleware into ShowExceptions
josh authored
155 defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
11af089 @josh Extract ActionController rescue templates into Rescue and ShowExceptions...
josh authored
156 end
157 end
158 end
Something went wrong with that request. Please try again.