# class Rack::ShowExceptions

`Rack::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.

### Constants

CONTEXT

### Public Class Methods

new(app) click to toggle source

    # File lib/rack/show_exceptions.rb, line 18
    def initialize(app)
      @app = app
    end

### Public Instance Methods

call(env) click to toggle source

    # File lib/rack/show_exceptions.rb, line 22
    def call(env)
      @app.call(env)
    rescue StandardError, LoadError, SyntaxError => e
      exception_string = dump_exception(e)

      env[RACK_ERRORS].puts(exception_string)
      env[RACK_ERRORS].flush

      if accepts_html?(env)
        content_type = "text/html"
        body = pretty(env, e)
      else
        content_type = "text/plain"
        body = exception_string
      end

      [
        500,
        {
          CONTENT_TYPE => content_type,
          CONTENT_LENGTH => body.bytesize.to_s,
        },
        [body],
      ]
    end

dump_exception(exception) click to toggle source

    # File lib/rack/show_exceptions.rb, line 57
    def dump_exception(exception)
      string = "#{exception.class}: #{exception.message}\n".dup
      string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
      string
    end

prefers_plaintext?(env) click to toggle source

    # File lib/rack/show_exceptions.rb, line 48
    def prefers_plaintext?(env)
      !accepts_html?(env)
    end

pretty(env, exception) click to toggle source

    # File lib/rack/show_exceptions.rb, line 63
    def pretty(env, exception)
      req = Rack::Request.new(env)

      # This double assignment is to prevent an "unused variable" warning.
      # Yes, it is dumb, but I don't like Ruby yelling at me.
      path = path = (req.script_name + req.path_info).squeeze("/")

      # This double assignment is to prevent an "unused variable" warning.
      # Yes, it is dumb, but I don't like Ruby yelling at me.
      frames = frames = exception.backtrace.map { |line|
        frame = OpenStruct.new
        if line =~ /(.*?):(\d+)(:in `(.*)')?/
          frame.filename = $1
          frame.lineno = $2.to_i
          frame.function = $4

          begin
            lineno = frame.lineno - 1
            lines = ::File.readlines(frame.filename)
            frame.pre_context_lineno = [lineno - CONTEXT, 0].max
            frame.pre_context = lines[frame.pre_context_lineno...lineno]
            frame.context_line = lines[lineno].chomp
            frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
            frame.post_context = lines[lineno + 1..frame.post_context_lineno]
          rescue
          end

          frame
        else
          nil
        end
      }.compact

      template.result(binding)
    end

template() click to toggle source

    # File lib/rack/show_exceptions.rb, line 99
    def template
      TEMPLATE
    end

### Private Instance Methods

accepts_html?(env) click to toggle source

    # File lib/rack/show_exceptions.rb, line 52
    def accepts_html?(env)
      Rack::Utils.best_q_match(env["HTTP_ACCEPT"], %w[text/html])
    end