123456789_123456789_123456789_123456789_123456789_

Class: Sinatra::ShowExceptions

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Rack::ShowExceptions
Instance Chain:
self, Rack::ShowExceptions
Inherits: Rack::ShowExceptions
  • Object
Defined in: lib/sinatra/show_exceptions.rb

Overview

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.

Constant Summary

  • TEMPLATE = Internal use only
    # File 'lib/sinatra/show_exceptions.rb', line 74
    ERB.new <<-HTML # :nodoc:
    <!DOCTYPE html>
    <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
      <title><%=h exception.class %> at <%=h path %></title>
    
      <script type="text/javascript">
      //<!--
      function toggle(id) {
        var pre  = document.getElementById("pre-" + id);
        var post = document.getElementById("post-" + id);
        var context = document.getElementById("context-" + id);
    
        if (pre.style.display == 'block') {
          pre.style.display = 'none';
          post.style.display = 'none';
          context.style.background = "none";
        } else {
          pre.style.display = 'block';
          post.style.display = 'block';
          context.style.background = "#fffed9";
        }
      }
    
      function toggleBacktrace(){
        var bt = document.getElementById("backtrace");
        var toggler = document.getElementById("expando");
    
        if (bt.className == 'condensed') {
          bt.className = 'expanded';
          toggler.innerHTML = "(condense)";
        } else {
          bt.className = 'condensed';
          toggler.innerHTML = "(expand)";
        }
      }
      //-->
      </script>
    
    <style type="text/css" media="screen">
      *                   {margin: 0; padding: 0; border: 0; outline: 0;}
      div.clear           {clear: both;}
      body                {background: #EEEEEE; margin: 0; padding: 0;
                           font-family: 'Lucida Grande', 'Lucida Sans Unicode',
                           'Garuda';}
      code                {font-family: 'Lucida Console', monospace;
                           font-size: 12px;}
      li                  {height: 18px;}
      ul                  {list-style: none; margin: 0; padding: 0;}
      ol:hover            {cursor: pointer;}
      ol li               {white-space: pre;}
      #explanation        {font-size: 12px; color: #666666;
                           margin: 20px 0 0 100px;}
    /* WRAP */
      #wrap               {width: 1000px; background: #FFFFFF; margin: 0 auto;
                           padding: 30px 50px 20px 50px;
                           border-left: 1px solid #DDDDDD;
                           border-right: 1px solid #DDDDDD;}
    /* HEADER */
      #header             {margin: 0 auto 25px auto;}
      #header img         {float: left;}
      #header #summary    {float: left; margin: 12px 0 0 20px; width:660px;
                           font-family: 'Lucida Grande', 'Lucida Sans Unicode';}
      h1                  {margin: 0; font-size: 36px; color: #981919;}
      h2                  {margin: 0; font-size: 22px; color: #333333;}
      #header ul          {margin: 0; font-size: 12px; color: #666666;}
      #header ul li strong{color: #444444;}
      #header ul li       {display: inline; padding: 0 10px;}
      #header ul li.first {padding-left: 0;}
      #header ul li.last  {border: 0; padding-right: 0;}
    /* BODY */
      #backtrace,
      #get,
      #post,
      #cookies,
      #rack               {width: 980px; margin: 0 auto 10px auto;}
      p#nav               {float: right; font-size: 14px;}
    /* BACKTRACE */
      a#expando           {float: left; padding-left: 5px; color: #666666;
                          font-size: 14px; text-decoration: none; cursor: pointer;}
      a#expando:hover     {text-decoration: underline;}
      h3                  {float: left; width: 100px; margin-bottom: 10px;
                           color: #981919; font-size: 14px; font-weight: bold;}
      #nav a              {color: #666666; text-decoration: none; padding: 0 5px;}
      #backtrace li.frame-info {background: #f7f7f7; padding-left: 10px;
                               font-size: 12px; color: #333333;}
      #backtrace ul       {list-style-position: outside; border: 1px solid #E9E9E9;
                           border-bottom: 0;}
      #backtrace ol       {width: 920px; margin-left: 50px;
                           font: 10px 'Lucida Console', monospace; color: #666666;}
      #backtrace ol li    {border: 0; border-left: 1px solid #E9E9E9;
                           padding: 2px 0;}
      #backtrace ol code  {font-size: 10px; color: #555555; padding-left: 5px;}
      #backtrace-ul li    {border-bottom: 1px solid #E9E9E9; height: auto;
                           padding: 3px 0;}
      #backtrace-ul .code {padding: 6px 0 4px 0;}
      #backtrace.condensed .system,
      #backtrace.condensed .framework {display:none;}
    /* REQUEST DATA */
      p.no-data           {padding-top: 2px; font-size: 12px; color: #666666;}
      table.req           {width: 980px; text-align: left; font-size: 12px;
                           color: #666666; padding: 0; border-spacing: 0;
                           border: 1px solid #EEEEEE; border-bottom: 0;
                           border-left: 0;
                           clear:both}
      table.req tr th     {padding: 2px 10px; font-weight: bold;
                           background: #F7F7F7; border-bottom: 1px solid #EEEEEE;
                           border-left: 1px solid #EEEEEE;}
      table.req tr td     {padding: 2px 20px 2px 10px;
                           border-bottom: 1px solid #EEEEEE;
                           border-left: 1px solid #EEEEEE;}
    /* HIDE PRE/POST CODE AT START */
      .pre-context,
      .post-context       {display: none;}
    
      table td.code       {width:750px}
      table td.code div   {width:750px;overflow:hidden}
    </style>
    </head>
    <body>
      <div id="wrap">
        <div id="header">
          <img src="<%= env['SCRIPT_NAME'] %>/__sinatra__/500.png" alt="application error" height="161" width="313" />
          <div id="summary">
            <h1><strong><%=h exception.class %></strong> at <strong><%=h path %>
              </strong></h1>
            <h2><%=h exception.message %></h2>
            <ul>
              <li class="first"><strong>file:</strong> <code>
                <%=h frames.first.filename.split("/").last %></code></li>
              <li><strong>location:</strong> <code><%=h frames.first.function %>
                </code></li>
              <li class="last"><strong>line:
                </strong> <%=h frames.first.lineno %></li>
            </ul>
          </div>
          <div class="clear"></div>
        </div>
    
        <div id="backtrace" class='condensed'>
          <h3>BACKTRACE</h3>
          <p><a href="#" id="expando"
                onclick="toggleBacktrace(); return false">(expand)</a></p>
          <p id="nav"><strong>JUMP TO:</strong>
             <% unless bad_request?(exception) %>
                <a href="#get-info">GET</a>
                <a href="#post-info">POST</a>
             <% end %>
             <a href="#cookie-info">COOKIES</a>
             <a href="#env-info">ENV</a>
          </p>
          <div class="clear"></div>
    
          <ul id="backtrace-ul">
    
          <% id = 1 %>
          <% frames.each do |frame| %>
              <% if frame.context_line && frame.context_line != "#" %>
    
                <li class="frame-info <%= frame_class(frame) %>">
                  <code><%=h frame.filename %></code> in
                    <code><strong><%=h frame.function %></strong></code>
                </li>
    
                <li class="code <%= frame_class(frame) %>">
                  <% if frame.pre_context %>
                  <ol start="<%=h frame.pre_context_lineno + 1 %>"
                      class="pre-context" id="pre-<%= id %>"
                      onclick="toggle(<%= id %>);">
                    <% frame.pre_context.each do |line| %>
                    <li class="pre-context-line"><code><%=h line %></code></li>
                    <% end %>
                  </ol>
                  <% end %>
    
                  <ol start="<%= frame.lineno %>" class="context" id="<%= id %>"
                      onclick="toggle(<%= id %>);">
                    <li class="context-line" id="context-<%= id %>"><code><%=
                      h frame.context_line %></code></li>
                  </ol>
    
                  <% if frame.post_context %>
                  <ol start="<%=h frame.lineno + 1 %>" class="post-context"
                      id="post-<%= id %>" onclick="toggle(<%= id %>);">
                    <% frame.post_context.each do |line| %>
                    <li class="post-context-line"><code><%=h line %></code></li>
                    <% end %>
                  </ol>
                  <% end %>
                  <div class="clear"></div>
                </li>
    
              <% end %>
    
            <% id += 1 %>
          <% end %>
    
          </ul>
        </div> <!-- /BACKTRACE -->
    
        <% unless bad_request?(exception) %>
          <div id="get">
            <h3 id="get-info">GET</h3>
            <% if req.GET and not req.GET.empty? %>
              <table class="req">
                <tr>
                  <th>Variable</th>
                  <th>Value</th>
                </tr>
                 <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
                <tr>
                  <td><%=h key %></td>
                  <td class="code"><div><%=h val.inspect %></div></td>
                </tr>
                <% } %>
              </table>
            <% else %>
              <p class="no-data">No GET data.</p>
            <% end %>
            <div class="clear"></div>
          </div> <!-- /GET -->
    
          <div id="post">
            <h3 id="post-info">POST</h3>
            <% if req.POST and not req.POST.empty? %>
              <table class="req">
                <tr>
                  <th>Variable</th>
                  <th>Value</th>
                </tr>
                <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
                <tr>
                  <td><%=h key %></td>
                  <td class="code"><div><%=h val.inspect %></div></td>
                </tr>
                <% } %>
              </table>
            <% else %>
              <p class="no-data">No POST data.</p>
            <% end %>
            <div class="clear"></div>
          </div> <!-- /POST -->
        <% end %>
    
        <div id="cookies">
          <h3 id="cookie-info">COOKIES</h3>
          <% unless req.cookies.empty? %>
            <table class="req">
              <tr>
                <th>Variable</th>
                <th>Value</th>
              </tr>
              <% req.cookies.each { |key, val| %>
                <tr>
                  <td><%=h key %></td>
                  <td class="code"><div><%=h val.inspect %></div></td>
                </tr>
              <% } %>
            </table>
          <% else %>
            <p class="no-data">No cookie data.</p>
          <% end %>
          <div class="clear"></div>
        </div> <!-- /COOKIES -->
    
        <div id="rack">
          <h3 id="env-info">Rack ENV</h3>
          <table class="req">
            <tr>
              <th>Variable</th>
              <th>Value</th>
            </tr>
             <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
             <tr>
               <td><%=h key %></td>
               <td class="code"><div><%=h val %></div></td>
             </tr>
             <% } %>
          </table>
          <div class="clear"></div>
        </div> <!-- /RACK ENV -->
    
        <p id="explanation">You're seeing this error because you have
    enabled the <code>show_exceptions</code> setting.</p>
      </div> <!-- /WRAP -->
      </body>
    </html

Class Method Summary

Instance Method Summary

Constructor Details

.new(app) ⇒ ShowExceptions

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 18

def initialize(app)
  @app = app
end

Class Method Details

.flush

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 14

def @@eats_errors.flush(*) end

.puts

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 16

def @@eats_errors.puts(*) end

Instance Method Details

#bad_request?(exception) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 54

def bad_request?(exception)
  Sinatra::BadRequest === exception
end

#call(env)

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 22

def call(env)
  @app.call(env)
rescue Exception => e
  errors = env['rack.errors']
  env['rack.errors'] = @@eats_errors

  if prefers_plain_text?(env)
    content_type = 'text/plain'
    body = dump_exception(e)
  else
    content_type = 'text/html'
    body = pretty(env, e)
  end

  env['rack.errors'] = errors

  [
    500,
    {
      'content-type' => content_type,
      'content-length' => body.bytesize.to_s
    },
    [body]
  ]
end

#frame_class(frame) (private)

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 63

def frame_class(frame)
  if frame.filename =~ %r{lib/sinatra.*\.rb}
    'framework'
  elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
        frame.filename =~ %r{/bin/(\w+)\z}
    'system'
  else
    'app'
  end
end

#prefers_plain_text?(env) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 58

def prefers_plain_text?(env)
  Request.new(env).preferred_type('text/plain', 'text/html') != 'text/html' &&
    [/curl/].index { |item| item =~ env['HTTP_USER_AGENT'] }
end

#template

[ GitHub ]

  
# File 'lib/sinatra/show_exceptions.rb', line 48

def template
  TEMPLATE
end