# This file is part of the "Utopia Framework" project, and is licensed under the GNU AGPLv3. # Copyright 2010 Samuel Williams. All rights reserved. # See for licensing details. require 'utopia/middleware' module Utopia module Middleware class FailedRequestError < StandardError def initialize(resource_path, resource_status, error_path, error_status) @resource_path = resource_path @resource_status = resource_status @error_path = error_path @error_status = error_status end def to_s "Requested resource #{@resource_path} resulted in a #{@resource_status} error. Requested error handler #{@error_path} resulted in a #{@error_status} error." end end class ExceptionHandler def initialize(app, location) @app = app @location = location end def fatal_error(env, ex) body = StringIO.new body.puts "Fatal Error" body.puts "

Fatal Error

" body.puts "

While requesting resource #{env['PATH_INFO'].to_html}, a fatal error occurred.

" body.puts "
#{ex.class.name.to_html}: #{ex.to_s.to_html}
" body.puts "

There is nothing more we can do to fix the problem at this point.

" body.puts "

We apologize for the inconvenience.

" body.puts "" body.rewind return [400, {"Content-Type" => "text/html"}, body] end def redirect(env, ex) return @app.call(env.merge('PATH_INFO' => @location, 'REQUEST_METHOD' => 'GET')) end def call(env) begin return @app.call(env) rescue Exception => ex if env['PATH_INFO'] == @location return fatal_error(env, ex) else # If redirection fails begin return redirect(env, ex) rescue return fatal_error(env, ex) end end end end end # Legacy Support ExceptionRedirector = ExceptionHandler class Redirector private def normalize_strings(strings) normalized = {} strings.each_pair do |key, value| if Array === key key.each { |s| normalized[s] = value } else normalized[key] = value end end return normalized end def normalize_patterns(patterns) normalized = [] patterns.each do |pattern| uri = pattern.pop pattern.each do |key| normalized.push([key, uri]) end end return normalized end public def initialize(app, options = {}) @app = app @strings = options[:strings] || {} @patterns = options[:patterns] || {} @strings = normalize_strings(@strings) @patterns = normalize_patterns(@patterns) @errors = options[:errors] LOG.info "#{self.class.name}: Running with #{@strings.size + @patterns.size} rules" end def redirect(uri, match_data) if uri.respond_to? :call return uri.call(match_data) else return [301, {"Location" => uri.to_s}, []] end end def call(env) base_path = env['PATH_INFO'] if uri = @strings[base_path] return redirect(@strings[base_path], base_path) end @patterns.each do |pattern, uri| $stderr.puts "Trying #{pattern} for #{base_path}" if match_data = pattern.match(base_path) result = redirect(uri, match_data) return result if result != nil end end response = @app.call(env) if @errors && response[0] >= 400 && uri = @errors[response[0]] error_request = env.merge("PATH_INFO" => uri, "REQUEST_METHOD" => "GET") error_response = @app.call(error_request) if error_response[0] >= 400 raise FailedRequestError.new(env['PATH_INFO'], response[0], uri, error_response[0]) else # Feed the error code back with the error document error_response[0] = response[0] return error_response end else return response end end end end end