technomancy-rack 0.3.0

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