2009-12-22 18:11:21 -05:00
require 'active_support/core_ext/exception'
2011-06-29 09:38:09 -04:00
require 'action_controller/metal/exceptions'
2010-01-03 17:33:34 -05:00
require 'active_support/notifications'
2009-12-22 18:11:21 -05:00
require 'action_dispatch/http/request'
2011-11-22 09:36:58 -05:00
require 'active_support/deprecation'
2009-06-08 16:33:18 -04:00
2009-05-03 00:02:22 -04:00
module ActionDispatch
2010-01-03 17:33:34 -05:00
# This middleware rescues any exception returned by the application and renders
# nice exception pages if it's being rescued locally.
2009-05-03 00:02:22 -04:00
class ShowExceptions
2009-05-17 13:24:42 -04:00
RESCUES_TEMPLATE_PATH = File . join ( File . dirname ( __FILE__ ) , 'templates' )
cattr_accessor :rescue_responses
@@rescue_responses = Hash . new ( :internal_server_error )
@@rescue_responses . update ( {
2009-05-03 00:02:22 -04:00
'ActionController::RoutingError' = > :not_found ,
2009-12-16 17:11:42 -05:00
'AbstractController::ActionNotFound' = > :not_found ,
2009-05-03 00:02:22 -04:00
'ActiveRecord::RecordNotFound' = > :not_found ,
'ActiveRecord::StaleObjectError' = > :conflict ,
'ActiveRecord::RecordInvalid' = > :unprocessable_entity ,
'ActiveRecord::RecordNotSaved' = > :unprocessable_entity ,
'ActionController::MethodNotAllowed' = > :method_not_allowed ,
'ActionController::NotImplemented' = > :not_implemented ,
'ActionController::InvalidAuthenticityToken' = > :unprocessable_entity
2009-05-17 13:24:42 -04:00
} )
2009-05-03 00:02:22 -04:00
2009-05-17 13:24:42 -04:00
cattr_accessor :rescue_templates
@@rescue_templates = Hash . new ( 'diagnostics' )
@@rescue_templates . update ( {
2009-05-26 18:03:09 -04:00
'ActionView::MissingTemplate' = > 'missing_template' ,
'ActionController::RoutingError' = > 'routing_error' ,
2009-12-16 17:11:42 -05:00
'AbstractController::ActionNotFound' = > 'unknown_action' ,
2009-12-02 23:01:01 -05:00
'ActionView::Template::Error' = > 'template_error'
2009-05-17 13:24:42 -04:00
} )
2009-05-03 00:02:22 -04:00
2009-05-17 13:24:42 -04:00
FAILSAFE_RESPONSE = [ 500 , { 'Content-Type' = > 'text/html' } ,
2009-05-27 16:01:03 -04:00
[ " <html><body><h1>500 Internal Server Error</h1> " <<
" If you are the administrator of this website, then please read this web " <<
" application's log file and/or the web server's log file to find out what " <<
" went wrong.</body></html> " ] ]
2009-05-03 00:02:22 -04:00
2011-11-22 09:36:58 -05:00
def initialize ( app , consider_all_requests_local = nil )
ActiveSupport :: Deprecation . warn " Passing consider_all_requests_local option to ActionDispatch::ShowExceptions middleware no longer works " unless consider_all_requests_local . nil?
2009-05-03 00:02:22 -04:00
@app = app
end
def call ( env )
2010-10-18 17:05:22 -04:00
begin
status , headers , body = @app . call ( env )
exception = nil
2010-04-30 19:40:42 -04:00
2010-10-18 17:05:22 -04:00
# Only this middleware cares about RoutingError. So, let's just raise
# it here.
if headers [ 'X-Cascade' ] == 'pass'
2011-04-22 14:23:12 -04:00
raise ActionController :: RoutingError , " No route matches [ #{ env [ 'REQUEST_METHOD' ] } ] #{ env [ 'PATH_INFO' ] . inspect } "
2010-10-18 17:05:22 -04:00
end
rescue Exception = > exception
2011-01-12 13:01:31 -05:00
raise exception if env [ 'action_dispatch.show_exceptions' ] == false
2010-04-30 19:40:42 -04:00
end
2010-10-18 17:05:22 -04:00
exception ? render_exception ( env , exception ) : [ status , headers , body ]
2009-05-17 13:24:42 -04:00
end
2009-05-11 20:07:05 -04:00
2009-05-17 13:24:42 -04:00
private
def render_exception ( env , exception )
log_error ( exception )
2010-10-11 10:55:07 -04:00
exception = original_exception ( exception )
2009-05-03 00:02:22 -04:00
2011-11-21 12:13:54 -05:00
if env [ 'action_dispatch.show_detailed_exceptions' ] == true
rescue_action_diagnostics ( env , exception )
2009-05-17 13:24:42 -04:00
else
2011-11-21 12:13:54 -05:00
rescue_action_error_page ( exception )
2009-05-17 13:24:42 -04:00
end
rescue Exception = > failsafe_error
2010-05-29 22:26:02 -04:00
$stderr . puts " Error during failsafe response: #{ failsafe_error } \n #{ failsafe_error . backtrace * " \n " } "
2009-05-17 13:24:42 -04:00
FAILSAFE_RESPONSE
2009-05-03 00:02:22 -04:00
end
# Render detailed diagnostics for unhandled exceptions rescued from
# a controller action.
2011-11-21 12:13:54 -05:00
def rescue_action_diagnostics ( env , exception )
2009-05-03 00:02:22 -04:00
template = ActionView :: Base . new ( [ RESCUES_TEMPLATE_PATH ] ,
2011-11-21 12:13:54 -05:00
:request = > Request . new ( env ) ,
2010-01-16 22:34:35 -05:00
:exception = > exception ,
:application_trace = > application_trace ( exception ) ,
:framework_trace = > framework_trace ( exception ) ,
:full_trace = > full_trace ( exception )
2009-05-03 00:02:22 -04:00
)
2011-09-22 09:37:38 -04:00
file = " rescues/ #{ @@rescue_templates [ exception . class . name ] } "
body = template . render ( :template = > file , :layout = > 'rescues/layout' )
2009-05-17 13:24:42 -04:00
render ( status_code ( exception ) , body )
2009-05-03 00:02:22 -04:00
end
# Attempts to render a static error page based on the
# <tt>status_code</tt> thrown, or just return headers if no such file
# exists. At first, it will try to render a localized static page.
# For example, if a 500 error is being handled Rails and locale is :da,
# it will first attempt to render the file at <tt>public/500.da.html</tt>
# then attempt to render <tt>public/500.html</tt>. If none of them exist,
# the body of the response will be left empty.
2011-11-21 12:13:54 -05:00
def rescue_action_error_page ( exception )
2009-05-03 00:02:22 -04:00
status = status_code ( exception )
locale_path = " #{ public_path } / #{ status } . #{ I18n . locale } .html " if I18n . locale
path = " #{ public_path } / #{ status } .html "
if locale_path && File . exist? ( locale_path )
2009-05-17 13:24:42 -04:00
render ( status , File . read ( locale_path ) )
2009-05-03 00:02:22 -04:00
elsif File . exist? ( path )
2009-05-17 13:24:42 -04:00
render ( status , File . read ( path ) )
2009-05-03 00:02:22 -04:00
else
2009-05-17 13:24:42 -04:00
render ( status , '' )
2009-05-03 00:02:22 -04:00
end
end
def status_code ( exception )
2009-12-22 17:08:03 -05:00
Rack :: Utils . status_code ( @@rescue_responses [ exception . class . name ] )
2009-05-03 00:02:22 -04:00
end
2009-05-17 13:24:42 -04:00
def render ( status , body )
2011-05-03 09:30:59 -04:00
[ status , { 'Content-Type' = > " text/html; charset= #{ Response . default_charset } " , 'Content-Length' = > body . bytesize . to_s } , [ body ] ]
2009-05-17 13:24:42 -04:00
end
2009-05-03 00:02:22 -04:00
def public_path
2009-05-17 13:24:42 -04:00
defined? ( Rails . public_path ) ? Rails . public_path : 'public_path'
2009-05-03 00:02:22 -04:00
end
2009-05-17 13:24:42 -04:00
def log_error ( exception )
return unless logger
2009-05-03 00:02:22 -04:00
ActiveSupport :: Deprecation . silence do
2010-05-09 05:52:26 -04:00
message = " \n #{ exception . class } ( #{ exception . message } ): \n "
2010-08-16 13:08:59 -04:00
message << exception . annoted_source_code . to_s if exception . respond_to? ( :annoted_source_code )
2010-06-09 16:48:50 -04:00
message << " " << application_trace ( exception ) . join ( " \n " )
2010-05-09 05:52:26 -04:00
logger . fatal ( " #{ message } \n \n " )
2009-05-03 00:02:22 -04:00
end
end
2010-01-16 22:34:35 -05:00
def application_trace ( exception )
clean_backtrace ( exception , :silent )
end
def framework_trace ( exception )
clean_backtrace ( exception , :noise )
end
def full_trace ( exception )
clean_backtrace ( exception , :all )
end
def clean_backtrace ( exception , * args )
2009-05-03 00:02:22 -04:00
defined? ( Rails ) && Rails . respond_to? ( :backtrace_cleaner ) ?
2010-01-16 22:34:35 -05:00
Rails . backtrace_cleaner . clean ( exception . backtrace , * args ) :
2009-05-03 00:02:22 -04:00
exception . backtrace
end
def logger
2009-05-17 13:24:42 -04:00
defined? ( Rails . logger ) ? Rails . logger : Logger . new ( $stderr )
2009-05-03 00:02:22 -04:00
end
2010-10-11 10:55:07 -04:00
def original_exception ( exception )
if registered_original_exception? ( exception )
exception . original_exception
else
exception
end
end
def registered_original_exception? ( exception )
exception . respond_to? ( :original_exception ) && @@rescue_responses . has_key? ( exception . original_exception . class . name )
end
2009-05-03 00:02:22 -04:00
end
end