Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 6307

Show
Ignore:
Timestamp:
03/04/07 12:31:21 (2 years ago)
Author:
bitsweat
Message:

Improve dispatcher failsafe responses. Beef up compatibility with Mongrel's CGI wrapper.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/railties/lib/dispatcher.rb

    r5794 r6307  
    4040      end 
    4141    rescue Exception => exception  # errors from CGI dispatch 
    42       failsafe_response(output, '500 Internal Server Error', exception) do 
     42      failsafe_response(cgi, output, '500 Internal Server Error', exception) do 
    4343        controller ||= ApplicationController rescue LoadError nil 
    4444        controller ||= ActionController::Base 
     
    9191      attr_accessor_with_default :preparation_callbacks_run, false 
    9292      alias_method :preparation_callbacks_run?, :preparation_callbacks_run 
    93        
     93 
    9494      # CGI.new plus exception handling.  CGI#read_multipart raises EOFError 
    9595      # if body.empty? or body.size != Content-Length and raises ArgumentError 
    9696      # if Content-Length is non-integer. 
    9797      def new_cgi(output) 
    98         failsafe_response(output, '400 Bad Request') { CGI.new } 
     98        failsafe_response(nil, output, '400 Bad Request') { CGI.new } 
    9999      end 
    100100 
     
    132132 
    133133      # If the block raises, send status code as a last-ditch response. 
    134       def failsafe_response(output, status, exception = nil) 
     134      def failsafe_response(cgi, fallback_output, status, exception = nil) 
    135135        yield 
    136       rescue Exception  # errors from executed block 
     136      rescue Exception 
    137137        begin 
    138           output.write "Status: #{status}\r\n" 
    139            
    140           if exception 
    141             message    = exception.to_s + "\r\n" + exception.backtrace.join("\r\n") 
    142             error_path = File.join(RAILS_ROOT, 'public', '500.html') 
     138          log_failsafe_exception(cgi, status, exception) 
    143139 
    144             if defined?(RAILS_DEFAULT_LOGGER) && !RAILS_DEFAULT_LOGGER.nil? 
    145               RAILS_DEFAULT_LOGGER.fatal(message) 
     140          body = failsafe_response_body(status) 
     141          if cgi 
     142            head = { 'status' => status, 'type' => 'text/html' } 
    146143 
    147               output.write "Content-Type: text/html\r\n\r\n" 
     144            # FIXME: using CGI differently than CGIResponse does breaks 
     145            # the Mongrel CGI wrapper. 
     146            if defined?(Mongrel) && cgi.is_a?(Mongrel::CGIWrapper) 
     147              # FIXME: set a dummy cookie so the Mongrel CGI wrapper will 
     148              # also consider @output_cookies (used for session cookies.) 
     149              head['cookie'] = [] 
     150              cgi.header(head) 
     151              fallback_output << body 
     152            else 
     153              cgi.out(head) { body } 
     154            end 
     155          else 
     156            fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}" 
     157          end 
     158          nil 
     159        rescue Exception  # Logger or IO errors 
     160        end 
     161      end 
    148162 
    149               if File.exists?(error_path) 
    150                 output.write(IO.read(error_path)) 
    151               else 
    152                 output.write("<html><body><h1>Application error (Rails)</h1></body></html>") 
    153               end 
    154             else 
    155               output.write "Content-Type: text/plain\r\n\r\n" 
    156               output.write(message) 
    157             end 
    158           end 
    159         rescue Exception  # Logger or IO errors 
     163      def failsafe_response_body(status) 
     164        error_path = "#{RAILS_ROOT}/public/#{status[0..3]}.html" 
     165 
     166        if File.exists?(error_path) 
     167          File.read(error_path) 
     168        else 
     169          "<html><body><h1>#{status}</h1></body></html>" 
     170        end 
     171      end 
     172 
     173      def log_failsafe_exception(cgi, status, exception) 
     174        fell_back = cgi ? 'has cgi' : 'no cgi, fallback ouput' 
     175        message = "DISPATCHER FAILSAFE RESPONSE (#{fell_back}) #{Time.now}\n  Status: #{status}\n" 
     176        message << "  #{exception}\n    #{exception.backtrace.join("\n    ")}" if exception 
     177        failsafe_logger.fatal message 
     178      end 
     179 
     180      def failsafe_logger 
     181        if defined?(RAILS_DEFAULT_LOGGER) && !RAILS_DEFAULT_LOGGER.nil? 
     182          RAILS_DEFAULT_LOGGER 
     183        else 
     184          Logger.new($stderr) 
    160185        end 
    161186      end