From f1e08b351d496ae71b48d86b46be1805486de823 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 4 Mar 2007 12:31:21 +0000 Subject: Improve dispatcher failsafe responses. Beef up compatibility with Mongrel's CGI wrapper. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6307 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- railties/lib/dispatcher.rb | 71 +++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/railties/lib/dispatcher.rb b/railties/lib/dispatcher.rb index c121923da1..7ed3426832 100644 --- a/railties/lib/dispatcher.rb +++ b/railties/lib/dispatcher.rb @@ -39,7 +39,7 @@ class Dispatcher controller.process(request, response).out(output) end rescue Exception => exception # errors from CGI dispatch - failsafe_response(output, '500 Internal Server Error', exception) do + failsafe_response(cgi, output, '500 Internal Server Error', exception) do controller ||= ApplicationController rescue LoadError nil controller ||= ActionController::Base controller.process_with_exception(request, response, exception).out(output) @@ -90,12 +90,12 @@ class Dispatcher attr_accessor_with_default :preparation_callbacks, [] attr_accessor_with_default :preparation_callbacks_run, false alias_method :preparation_callbacks_run?, :preparation_callbacks_run - + # CGI.new plus exception handling. CGI#read_multipart raises EOFError # if body.empty? or body.size != Content-Length and raises ArgumentError # if Content-Length is non-integer. def new_cgi(output) - failsafe_response(output, '400 Bad Request') { CGI.new } + failsafe_response(nil, output, '400 Bad Request') { CGI.new } end def prepare_application @@ -131,34 +131,59 @@ class Dispatcher end # If the block raises, send status code as a last-ditch response. - def failsafe_response(output, status, exception = nil) + def failsafe_response(cgi, fallback_output, status, exception = nil) yield - rescue Exception # errors from executed block + rescue Exception begin - output.write "Status: #{status}\r\n" - - if exception - message = exception.to_s + "\r\n" + exception.backtrace.join("\r\n") - error_path = File.join(RAILS_ROOT, 'public', '500.html') - - if defined?(RAILS_DEFAULT_LOGGER) && !RAILS_DEFAULT_LOGGER.nil? - RAILS_DEFAULT_LOGGER.fatal(message) - - output.write "Content-Type: text/html\r\n\r\n" - - if File.exists?(error_path) - output.write(IO.read(error_path)) - else - output.write("

Application error (Rails)

") - end + log_failsafe_exception(cgi, status, exception) + + body = failsafe_response_body(status) + if cgi + head = { 'status' => status, 'type' => 'text/html' } + + # FIXME: using CGI differently than CGIResponse does breaks + # the Mongrel CGI wrapper. + if defined?(Mongrel) && cgi.is_a?(Mongrel::CGIWrapper) + # FIXME: set a dummy cookie so the Mongrel CGI wrapper will + # also consider @output_cookies (used for session cookies.) + head['cookie'] = [] + cgi.header(head) + fallback_output << body else - output.write "Content-Type: text/plain\r\n\r\n" - output.write(message) + cgi.out(head) { body } end + else + fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}" end + nil rescue Exception # Logger or IO errors end end + + def failsafe_response_body(status) + error_path = "#{RAILS_ROOT}/public/#{status[0..3]}.html" + + if File.exists?(error_path) + File.read(error_path) + else + "

#{status}

" + end + end + + def log_failsafe_exception(cgi, status, exception) + fell_back = cgi ? 'has cgi' : 'no cgi, fallback ouput' + message = "DISPATCHER FAILSAFE RESPONSE (#{fell_back}) #{Time.now}\n Status: #{status}\n" + message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception + failsafe_logger.fatal message + end + + def failsafe_logger + if defined?(RAILS_DEFAULT_LOGGER) && !RAILS_DEFAULT_LOGGER.nil? + RAILS_DEFAULT_LOGGER + else + Logger.new($stderr) + end + end end end -- cgit v1.2.3