class Sinatra::ShowExceptions

Sinatra::ShowExceptions catches all exceptions raised from the app it wraps. It shows a useful backtrace with the sourcefile and clickable context, the whole Rack environment and the request data.

Be careful when you use this on public-facing sites as it could reveal information helpful to attackers.

Public Class Methods

new(app) click to toggle source
   # File lib/sinatra/show_exceptions.rb
17 def initialize(app)
18   @app = app
19 end

Public Instance Methods

call(env) click to toggle source
   # File lib/sinatra/show_exceptions.rb
21 def call(env)
22   @app.call(env)
23 rescue Exception => e
24   errors, env["rack.errors"] = env["rack.errors"], @@eats_errors
25 
26   if prefers_plain_text?(env)
27     content_type = "text/plain"
28     body = dump_exception(e)
29   else
30     content_type = "text/html"
31     body = pretty(env, e)
32   end
33 
34   env["rack.errors"] = errors
35 
36   [
37     500,
38     {
39       "Content-Type" => content_type,
40       "Content-Length" => body.bytesize.to_s
41     },
42     [body]
43   ]
44 end
pretty(env, exception) click to toggle source

Pulled from Rack::ShowExceptions in order to override TEMPLATE. If Rack provides another way to override, this could be removed in the future.

   # File lib/sinatra/show_exceptions.rb
49 def pretty(env, exception)
50   req = Rack::Request.new(env)
51 
52   # This double assignment is to prevent an "unused variable" warning on
53   # Ruby 1.9.3.  Yes, it is dumb, but I don't like Ruby yelling at me.
54   path = path = (req.script_name + req.path_info).squeeze("/")
55 
56   # This double assignment is to prevent an "unused variable" warning on
57   # Ruby 1.9.3.  Yes, it is dumb, but I don't like Ruby yelling at me.
58   frames = frames = exception.backtrace.map { |line|
59     frame = OpenStruct.new
60     if line =~ /(.*?):(\d+)(:in `(.*)')?/
61       frame.filename = $1
62       frame.lineno = $2.to_i
63       frame.function = $4
64 
65       begin
66         lineno = frame.lineno-1
67         lines = ::File.readlines(frame.filename)
68         frame.pre_context_lineno = [lineno-CONTEXT, 0].max
69         frame.pre_context = lines[frame.pre_context_lineno...lineno]
70         frame.context_line = lines[lineno].chomp
71         frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
72         frame.post_context = lines[lineno+1..frame.post_context_lineno]
73       rescue
74       end
75 
76       frame
77     else
78       nil
79     end
80   }.compact
81 
82   TEMPLATE.result(binding)
83 end

Private Instance Methods

bad_request?(e) click to toggle source
   # File lib/sinatra/show_exceptions.rb
87 def bad_request?(e)
88   Sinatra::BadRequest === e
89 end
frame_class(frame) click to toggle source
    # File lib/sinatra/show_exceptions.rb
 96 def frame_class(frame)
 97   if frame.filename =~ %r{lib/sinatra.*\.rb}
 98     "framework"
 99   elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
100         frame.filename =~ %r{/bin/(\w+)\z}
101     "system"
102   else
103     "app"
104   end
105 end
prefers_plain_text?(env) click to toggle source
   # File lib/sinatra/show_exceptions.rb
91 def prefers_plain_text?(env)
92   !(Request.new(env).preferred_type("text/plain","text/html") == "text/html") &&
93   [/curl/].index { |item| item =~ env["HTTP_USER_AGENT"] }
94 end