From 64b26ac27a7da1c89edbe76c7292a3a99de22bae Mon Sep 17 00:00:00 2001 From: Geoff Buesing Date: Sun, 17 Jan 2010 11:06:05 -0600 Subject: [PATCH] Rack::HideExceptions - Returns a user-friendly exception page when an exception is raised --- README.rdoc | 1 + lib/rack/contrib.rb | 1 + lib/rack/contrib/hide_exceptions.rb | 74 +++++++++++++++++++++++++++++++++++ test/500.html | 1 + test/spec_rack_hide_exceptions.rb | 30 ++++++++++++++ 5 files changed, 107 insertions(+), 0 deletions(-) create mode 100644 lib/rack/contrib/hide_exceptions.rb create mode 100644 test/500.html create mode 100644 test/spec_rack_hide_exceptions.rb diff --git a/README.rdoc b/README.rdoc index ca015ae..c817c49 100644 --- a/README.rdoc +++ b/README.rdoc @@ -46,6 +46,7 @@ interface: * Rack::HostMeta - Configures /host-meta using a block * Rack::Cookies - Adds simple cookie jar hash to env * Rack::Access - Limit access based on IP address +* Rack::HideExceptions - Returns a user-friendly exception page when an exception is raised === Use diff --git a/lib/rack/contrib.rb b/lib/rack/contrib.rb index 8905437..f7bce84 100644 --- a/lib/rack/contrib.rb +++ b/lib/rack/contrib.rb @@ -16,6 +16,7 @@ module Rack autoload :ETag, "rack/contrib/etag" autoload :ExpectationCascade, "rack/contrib/expectation_cascade" autoload :GarbageCollector, "rack/contrib/garbagecollector" + autoload :HideExceptions, "rack/contrib/hide_exceptions" autoload :JSONP, "rack/contrib/jsonp" autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix" autoload :Locale, "rack/contrib/locale" diff --git a/lib/rack/contrib/hide_exceptions.rb b/lib/rack/contrib/hide_exceptions.rb new file mode 100644 index 0000000..132066a --- /dev/null +++ b/lib/rack/contrib/hide_exceptions.rb @@ -0,0 +1,74 @@ +module Rack + # Returns a user-friendly exception page when an exception is raised + # Should be included at the very top of the middleware pipeline so that unhandled exceptions + # from anywhere down the pipeline are rescued. + # + # In development, you'd want to use Rack::ShowExceptions instead of this (config.ru example): + # + # if ENV['RACK_ENV'] == 'development' + # use Rack::ShowExceptions + # else + # use Rack::HideExceptions + # end + # + # The default HTML included here is a copy of the 500 page included with Rails + # You can optionally specify your own file, ex: + # + # use Rack::HideExceptions, "public/500.html" + class HideExceptions + + def initialize(app, file_path = nil) + @app = app + @file_path = file_path + end + + def call(env) + @app.call(env) + rescue ::Exception + [500, {'Content-Type' => 'text/html', 'Content-Length' => html.length.to_s}, [html]] + end + + private + + def html + @html ||= @file_path ? ::File.read(@file_path) : default_html + end + + # Exception page from Rails (500.html) + def default_html + <<-EOV + + + + + + + We're sorry, but something went wrong (500) + + + + + +
+

We're sorry, but something went wrong.

+

We've been notified about this issue and we'll take a look at it shortly.

+
+ + + EOV + end + + end +end diff --git a/test/500.html b/test/500.html new file mode 100644 index 0000000..a45ee9b --- /dev/null +++ b/test/500.html @@ -0,0 +1 @@ +Internal Server Error \ No newline at end of file diff --git a/test/spec_rack_hide_exceptions.rb b/test/spec_rack_hide_exceptions.rb new file mode 100644 index 0000000..8847c66 --- /dev/null +++ b/test/spec_rack_hide_exceptions.rb @@ -0,0 +1,30 @@ +require 'test/spec' +require 'rack/contrib/hide_exceptions' + +context "Rack::HideExceptions" do + + specify "does not interfere when no exception raised" do + app = Proc.new { [200, {'Content-Type' => 'text/html'}, ['Downstream app']]} + middleware = Rack::HideExceptions.new(app) + status, headers, body = middleware.call({}) + status.should == 200 + body[0].should == 'Downstream app' + end + + specify "returns default 500 page when exception raised" do + app = Proc.new { raise } + middleware = Rack::HideExceptions.new(app) + status, headers, body = middleware.call({}) + status.should == 500 + body[0].should =~ /We\'re sorry, but something went wrong/ + end + + specify "returns custom 500 page when exception raised" do + app = Proc.new { raise } + middleware = Rack::HideExceptions.new(app, 'test/500.html') + status, headers, body = middleware.call({}) + status.should == 500 + body[0].should == "Internal Server Error" + end + +end -- 1.6.1