Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / vendor / rack-1.0 / rack / showexceptions.rb
1 require 'ostruct'
2 require 'erb'
3 require 'rack/request'
4 require 'rack/utils'
5
6 module Rack
7 # Rack::ShowExceptions catches all exceptions raised from the app it
8 # wraps. It shows a useful backtrace with the sourcefile and
9 # clickable context, the whole Rack environment and the request
10 # data.
11 #
12 # Be careful when you use this on public-facing sites as it could
13 # reveal information helpful to attackers.
14
15 class ShowExceptions
16 CONTEXT = 7
17
18 def initialize(app)
19 @app = app
20 @template = ERB.new(TEMPLATE)
21 end
22
23 def call(env)
24 @app.call(env)
25 rescue StandardError, LoadError, SyntaxError => e
26 backtrace = pretty(env, e)
27 [500,
28 {"Content-Type" => "text/html",
29 "Content-Length" => backtrace.join.size.to_s},
30 backtrace]
31 end
32
33 def pretty(env, exception)
34 req = Rack::Request.new(env)
35 path = (req.script_name + req.path_info).squeeze("/")
36
37 frames = exception.backtrace.map { |line|
38 frame = OpenStruct.new
39 if line =~ /(.*?):(\d+)(:in `(.*)')?/
40 frame.filename = $1
41 frame.lineno = $2.to_i
42 frame.function = $4
43
44 begin
45 lineno = frame.lineno-1
46 lines = ::File.readlines(frame.filename)
47 frame.pre_context_lineno = [lineno-CONTEXT, 0].max
48 frame.pre_context = lines[frame.pre_context_lineno...lineno]
49 frame.context_line = lines[lineno].chomp
50 frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
51 frame.post_context = lines[lineno+1..frame.post_context_lineno]
52 rescue
53 end
54
55 frame
56 else
57 nil
58 end
59 }.compact
60
61 env["rack.errors"].puts "#{exception.class}: #{exception.message}"
62 env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l }
63 env["rack.errors"].flush
64
65 [@template.result(binding)]
66 end
67
68 def h(obj) # :nodoc:
69 case obj
70 when String
71 Utils.escape_html(obj)
72 else
73 Utils.escape_html(obj.inspect)
74 end
75 end
76
77 # :stopdoc:
78
79 # adapted from Django <djangoproject.com>
80 # Copyright (c) 2005, the Lawrence Journal-World
81 # Used under the modified BSD license:
82 # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
83 TEMPLATE = <<'HTML'
84 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
85 <html lang="en">
86 <head>
87 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
88 <meta name="robots" content="NONE,NOARCHIVE" />
89 <title><%=h exception.class %> at <%=h path %></title>
90 <style type="text/css">
91 html * { padding:0; margin:0; }
92 body * { padding:10px 20px; }
93 body * * { padding:0; }
94 body { font:small sans-serif; }
95 body>div { border-bottom:1px solid #ddd; }
96 h1 { font-weight:normal; }
97 h2 { margin-bottom:.8em; }
98 h2 span { font-size:80%; color:#666; font-weight:normal; }
99 h3 { margin:1em 0 .5em 0; }
100 h4 { margin:0 0 .5em 0; font-weight: normal; }
101 table {
102 border:1px solid #ccc; border-collapse: collapse; background:white; }
103 tbody td, tbody th { vertical-align:top; padding:2px 3px; }
104 thead th {
105 padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
106 font-weight:normal; font-size:11px; border:1px solid #ddd; }
107 tbody th { text-align:right; color:#666; padding-right:.5em; }
108 table.vars { margin:5px 0 2px 40px; }
109 table.vars td, table.req td { font-family:monospace; }
110 table td.code { width:100%;}
111 table td.code div { overflow:hidden; }
112 table.source th { color:#666; }
113 table.source td {
114 font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
115 ul.traceback { list-style-type:none; }
116 ul.traceback li.frame { margin-bottom:1em; }
117 div.context { margin: 10px 0; }
118 div.context ol {
119 padding-left:30px; margin:0 10px; list-style-position: inside; }
120 div.context ol li {
121 font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
122 div.context ol.context-line li { color:black; background-color:#ccc; }
123 div.context ol.context-line li span { float: right; }
124 div.commands { margin-left: 40px; }
125 div.commands a { color:black; text-decoration:none; }
126 #summary { background: #ffc; }
127 #summary h2 { font-weight: normal; color: #666; }
128 #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
129 #summary ul#quicklinks li { float: left; padding: 0 1em; }
130 #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
131 #explanation { background:#eee; }
132 #template, #template-not-exist { background:#f6f6f6; }
133 #template-not-exist ul { margin: 0 0 0 20px; }
134 #traceback { background:#eee; }
135 #requestinfo { background:#f6f6f6; padding-left:120px; }
136 #summary table { border:none; background:transparent; }
137 #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
138 #requestinfo h3 { margin-bottom:-1em; }
139 .error { background: #ffc; }
140 .specific { color:#cc3300; font-weight:bold; }
141 </style>
142 <script type="text/javascript">
143 //<!--
144 function getElementsByClassName(oElm, strTagName, strClassName){
145 // Written by Jonathan Snook, http://www.snook.ca/jon;
146 // Add-ons by Robert Nyman, http://www.robertnyman.com
147 var arrElements = (strTagName == "*" && document.all)? document.all :
148 oElm.getElementsByTagName(strTagName);
149 var arrReturnElements = new Array();
150 strClassName = strClassName.replace(/\-/g, "\\-");
151 var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
152 var oElement;
153 for(var i=0; i<arrElements.length; i++){
154 oElement = arrElements[i];
155 if(oRegExp.test(oElement.className)){
156 arrReturnElements.push(oElement);
157 }
158 }
159 return (arrReturnElements)
160 }
161 function hideAll(elems) {
162 for (var e = 0; e < elems.length; e++) {
163 elems[e].style.display = 'none';
164 }
165 }
166 window.onload = function() {
167 hideAll(getElementsByClassName(document, 'table', 'vars'));
168 hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
169 hideAll(getElementsByClassName(document, 'ol', 'post-context'));
170 }
171 function toggle() {
172 for (var i = 0; i < arguments.length; i++) {
173 var e = document.getElementById(arguments[i]);
174 if (e) {
175 e.style.display = e.style.display == 'none' ? 'block' : 'none';
176 }
177 }
178 return false;
179 }
180 function varToggle(link, id) {
181 toggle('v' + id);
182 var s = link.getElementsByTagName('span')[0];
183 var uarr = String.fromCharCode(0x25b6);
184 var darr = String.fromCharCode(0x25bc);
185 s.innerHTML = s.innerHTML == uarr ? darr : uarr;
186 return false;
187 }
188 //-->
189 </script>
190 </head>
191 <body>
192
193 <div id="summary">
194 <h1><%=h exception.class %> at <%=h path %></h1>
195 <h2><%=h exception.message %></h2>
196 <table><tr>
197 <th>Ruby</th>
198 <td><code><%=h frames.first.filename %></code>: in <code><%=h frames.first.function %></code>, line <%=h frames.first.lineno %></td>
199 </tr><tr>
200 <th>Web</th>
201 <td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td>
202 </tr></table>
203
204 <h3>Jump to:</h3>
205 <ul id="quicklinks">
206 <li><a href="#get-info">GET</a></li>
207 <li><a href="#post-info">POST</a></li>
208 <li><a href="#cookie-info">Cookies</a></li>
209 <li><a href="#env-info">ENV</a></li>
210 </ul>
211 </div>
212
213 <div id="traceback">
214 <h2>Traceback <span>(innermost first)</span></h2>
215 <ul class="traceback">
216 <% frames.each { |frame| %>
217 <li class="frame">
218 <code><%=h frame.filename %></code>: in <code><%=h frame.function %></code>
219
220 <% if frame.context_line %>
221 <div class="context" id="c<%=h frame.object_id %>">
222 <% if frame.pre_context %>
223 <ol start="<%=h frame.pre_context_lineno+1 %>" class="pre-context" id="pre<%=h frame.object_id %>">
224 <% frame.pre_context.each { |line| %>
225 <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
226 <% } %>
227 </ol>
228 <% end %>
229
230 <ol start="<%=h frame.lineno %>" class="context-line">
231 <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h frame.context_line %><span>...</span></li></ol>
232
233 <% if frame.post_context %>
234 <ol start='<%=h frame.lineno+1 %>' class="post-context" id="post<%=h frame.object_id %>">
235 <% frame.post_context.each { |line| %>
236 <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
237 <% } %>
238 </ol>
239 <% end %>
240 </div>
241 <% end %>
242 </li>
243 <% } %>
244 </ul>
245 </div>
246
247 <div id="requestinfo">
248 <h2>Request information</h2>
249
250 <h3 id="get-info">GET</h3>
251 <% unless req.GET.empty? %>
252 <table class="req">
253 <thead>
254 <tr>
255 <th>Variable</th>
256 <th>Value</th>
257 </tr>
258 </thead>
259 <tbody>
260 <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
261 <tr>
262 <td><%=h key %></td>
263 <td class="code"><div><%=h val.inspect %></div></td>
264 </tr>
265 <% } %>
266 </tbody>
267 </table>
268 <% else %>
269 <p>No GET data.</p>
270 <% end %>
271
272 <h3 id="post-info">POST</h3>
273 <% unless req.POST.empty? %>
274 <table class="req">
275 <thead>
276 <tr>
277 <th>Variable</th>
278 <th>Value</th>
279 </tr>
280 </thead>
281 <tbody>
282 <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
283 <tr>
284 <td><%=h key %></td>
285 <td class="code"><div><%=h val.inspect %></div></td>
286 </tr>
287 <% } %>
288 </tbody>
289 </table>
290 <% else %>
291 <p>No POST data.</p>
292 <% end %>
293
294
295 <h3 id="cookie-info">COOKIES</h3>
296 <% unless req.cookies.empty? %>
297 <table class="req">
298 <thead>
299 <tr>
300 <th>Variable</th>
301 <th>Value</th>
302 </tr>
303 </thead>
304 <tbody>
305 <% req.cookies.each { |key, val| %>
306 <tr>
307 <td><%=h key %></td>
308 <td class="code"><div><%=h val.inspect %></div></td>
309 </tr>
310 <% } %>
311 </tbody>
312 </table>
313 <% else %>
314 <p>No cookie data.</p>
315 <% end %>
316
317 <h3 id="env-info">Rack ENV</h3>
318 <table class="req">
319 <thead>
320 <tr>
321 <th>Variable</th>
322 <th>Value</th>
323 </tr>
324 </thead>
325 <tbody>
326 <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
327 <tr>
328 <td><%=h key %></td>
329 <td class="code"><div><%=h val %></div></td>
330 </tr>
331 <% } %>
332 </tbody>
333 </table>
334
335 </div>
336
337 <div id="explanation">
338 <p>
339 You're seeing this error because you use <code>Rack::ShowExceptions</code>.
340 </p>
341 </div>
342
343 </body>
344 </html>
345 HTML
346
347 # :startdoc:
348 end
349 end