edgar-rack 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +401 -0
  4. data/Rakefile +101 -0
  5. data/SPEC +171 -0
  6. data/bin/rackup +4 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +81 -0
  12. data/lib/rack/auth/abstract/handler.rb +37 -0
  13. data/lib/rack/auth/abstract/request.rb +43 -0
  14. data/lib/rack/auth/basic.rb +58 -0
  15. data/lib/rack/auth/digest/md5.rb +124 -0
  16. data/lib/rack/auth/digest/nonce.rb +51 -0
  17. data/lib/rack/auth/digest/params.rb +53 -0
  18. data/lib/rack/auth/digest/request.rb +40 -0
  19. data/lib/rack/builder.rb +80 -0
  20. data/lib/rack/cascade.rb +41 -0
  21. data/lib/rack/chunked.rb +52 -0
  22. data/lib/rack/commonlogger.rb +49 -0
  23. data/lib/rack/conditionalget.rb +63 -0
  24. data/lib/rack/config.rb +15 -0
  25. data/lib/rack/content_length.rb +29 -0
  26. data/lib/rack/content_type.rb +23 -0
  27. data/lib/rack/deflater.rb +96 -0
  28. data/lib/rack/directory.rb +157 -0
  29. data/lib/rack/etag.rb +59 -0
  30. data/lib/rack/file.rb +118 -0
  31. data/lib/rack/handler.rb +88 -0
  32. data/lib/rack/handler/cgi.rb +61 -0
  33. data/lib/rack/handler/evented_mongrel.rb +8 -0
  34. data/lib/rack/handler/fastcgi.rb +90 -0
  35. data/lib/rack/handler/lsws.rb +61 -0
  36. data/lib/rack/handler/mongrel.rb +90 -0
  37. data/lib/rack/handler/scgi.rb +59 -0
  38. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  39. data/lib/rack/handler/thin.rb +17 -0
  40. data/lib/rack/handler/webrick.rb +73 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +567 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +44 -0
  45. data/lib/rack/logger.rb +18 -0
  46. data/lib/rack/methodoverride.rb +27 -0
  47. data/lib/rack/mime.rb +210 -0
  48. data/lib/rack/mock.rb +185 -0
  49. data/lib/rack/nulllogger.rb +18 -0
  50. data/lib/rack/recursive.rb +61 -0
  51. data/lib/rack/reloader.rb +109 -0
  52. data/lib/rack/request.rb +307 -0
  53. data/lib/rack/response.rb +151 -0
  54. data/lib/rack/rewindable_input.rb +104 -0
  55. data/lib/rack/runtime.rb +27 -0
  56. data/lib/rack/sendfile.rb +139 -0
  57. data/lib/rack/server.rb +289 -0
  58. data/lib/rack/session/abstract/id.rb +348 -0
  59. data/lib/rack/session/cookie.rb +152 -0
  60. data/lib/rack/session/memcache.rb +93 -0
  61. data/lib/rack/session/pool.rb +79 -0
  62. data/lib/rack/showexceptions.rb +378 -0
  63. data/lib/rack/showstatus.rb +113 -0
  64. data/lib/rack/static.rb +53 -0
  65. data/lib/rack/urlmap.rb +55 -0
  66. data/lib/rack/utils.rb +698 -0
  67. data/rack.gemspec +39 -0
  68. data/test/cgi/lighttpd.conf +25 -0
  69. data/test/cgi/rackup_stub.rb +6 -0
  70. data/test/cgi/sample_rackup.ru +5 -0
  71. data/test/cgi/test +9 -0
  72. data/test/cgi/test.fcgi +8 -0
  73. data/test/cgi/test.ru +5 -0
  74. data/test/gemloader.rb +6 -0
  75. data/test/multipart/bad_robots +259 -0
  76. data/test/multipart/binary +0 -0
  77. data/test/multipart/empty +10 -0
  78. data/test/multipart/fail_16384_nofile +814 -0
  79. data/test/multipart/file1.txt +1 -0
  80. data/test/multipart/filename_and_modification_param +7 -0
  81. data/test/multipart/filename_with_escaped_quotes +6 -0
  82. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  83. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  84. data/test/multipart/filename_with_unescaped_quotes +6 -0
  85. data/test/multipart/ie +6 -0
  86. data/test/multipart/nested +10 -0
  87. data/test/multipart/none +9 -0
  88. data/test/multipart/semicolon +6 -0
  89. data/test/multipart/text +15 -0
  90. data/test/rackup/config.ru +31 -0
  91. data/test/spec_auth_basic.rb +70 -0
  92. data/test/spec_auth_digest.rb +241 -0
  93. data/test/spec_builder.rb +123 -0
  94. data/test/spec_cascade.rb +45 -0
  95. data/test/spec_cgi.rb +102 -0
  96. data/test/spec_chunked.rb +60 -0
  97. data/test/spec_commonlogger.rb +56 -0
  98. data/test/spec_conditionalget.rb +86 -0
  99. data/test/spec_config.rb +23 -0
  100. data/test/spec_content_length.rb +36 -0
  101. data/test/spec_content_type.rb +29 -0
  102. data/test/spec_deflater.rb +125 -0
  103. data/test/spec_directory.rb +57 -0
  104. data/test/spec_etag.rb +75 -0
  105. data/test/spec_fastcgi.rb +107 -0
  106. data/test/spec_file.rb +92 -0
  107. data/test/spec_handler.rb +49 -0
  108. data/test/spec_head.rb +30 -0
  109. data/test/spec_lint.rb +515 -0
  110. data/test/spec_lobster.rb +43 -0
  111. data/test/spec_lock.rb +142 -0
  112. data/test/spec_logger.rb +28 -0
  113. data/test/spec_methodoverride.rb +58 -0
  114. data/test/spec_mock.rb +241 -0
  115. data/test/spec_mongrel.rb +182 -0
  116. data/test/spec_nulllogger.rb +12 -0
  117. data/test/spec_recursive.rb +69 -0
  118. data/test/spec_request.rb +774 -0
  119. data/test/spec_response.rb +245 -0
  120. data/test/spec_rewindable_input.rb +118 -0
  121. data/test/spec_runtime.rb +39 -0
  122. data/test/spec_sendfile.rb +83 -0
  123. data/test/spec_server.rb +8 -0
  124. data/test/spec_session_abstract_id.rb +43 -0
  125. data/test/spec_session_cookie.rb +171 -0
  126. data/test/spec_session_memcache.rb +289 -0
  127. data/test/spec_session_pool.rb +200 -0
  128. data/test/spec_showexceptions.rb +87 -0
  129. data/test/spec_showstatus.rb +79 -0
  130. data/test/spec_static.rb +48 -0
  131. data/test/spec_thin.rb +86 -0
  132. data/test/spec_urlmap.rb +213 -0
  133. data/test/spec_utils.rb +678 -0
  134. data/test/spec_webrick.rb +141 -0
  135. data/test/testrequest.rb +78 -0
  136. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  137. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  138. metadata +329 -0
@@ -0,0 +1,93 @@
1
+ # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
+
3
+ require 'rack/session/abstract/id'
4
+ require 'memcache'
5
+
6
+ module Rack
7
+ module Session
8
+ # Rack::Session::Memcache provides simple cookie based session management.
9
+ # Session data is stored in memcached. The corresponding session key is
10
+ # maintained in the cookie.
11
+ # You may treat Session::Memcache as you would Session::Pool with the
12
+ # following caveats.
13
+ #
14
+ # * Setting :expire_after to 0 would note to the Memcache server to hang
15
+ # onto the session data until it would drop it according to it's own
16
+ # specifications. However, the cookie sent to the client would expire
17
+ # immediately.
18
+ #
19
+ # Note that memcache does drop data before it may be listed to expire. For
20
+ # a full description of behaviour, please see memcache's documentation.
21
+
22
+ class Memcache < Abstract::ID
23
+ attr_reader :mutex, :pool
24
+
25
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
26
+ :namespace => 'rack:session',
27
+ :memcache_server => 'localhost:11211'
28
+
29
+ def initialize(app, options={})
30
+ super
31
+
32
+ @mutex = Mutex.new
33
+ mserv = @default_options[:memcache_server]
34
+ mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
35
+
36
+ @pool = options[:cache] || MemCache.new(mserv, mopts)
37
+ unless @pool.active? and @pool.servers.any?{|c| c.alive? }
38
+ raise 'No memcache servers'
39
+ end
40
+ end
41
+
42
+ def generate_sid
43
+ loop do
44
+ sid = super
45
+ break sid unless @pool.get(sid, true)
46
+ end
47
+ end
48
+
49
+ def get_session(env, sid)
50
+ with_lock(env, [nil, {}]) do
51
+ unless sid and session = @pool.get(sid)
52
+ sid, session = generate_sid, {}
53
+ unless /^STORED/ =~ @pool.add(sid, session)
54
+ raise "Session collision on '#{sid.inspect}'"
55
+ end
56
+ end
57
+ [sid, session]
58
+ end
59
+ end
60
+
61
+ def set_session(env, session_id, new_session, options)
62
+ expiry = options[:expire_after]
63
+ expiry = expiry.nil? ? 0 : expiry + 1
64
+
65
+ with_lock(env, false) do
66
+ @pool.set session_id, new_session, expiry
67
+ session_id
68
+ end
69
+ end
70
+
71
+ def destroy_session(env, session_id, options)
72
+ with_lock(env) do
73
+ @pool.delete(session_id)
74
+ generate_sid unless options[:drop]
75
+ end
76
+ end
77
+
78
+ def with_lock(env, default=nil)
79
+ @mutex.lock if env['rack.multithread']
80
+ yield
81
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
82
+ if $VERBOSE
83
+ warn "#{self} is unable to find memcached server."
84
+ warn $!.inspect
85
+ end
86
+ default
87
+ ensure
88
+ @mutex.unlock if @mutex.locked?
89
+ end
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,79 @@
1
+ # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
+ # THANKS:
3
+ # apeiros, for session id generation, expiry setup, and threadiness
4
+ # sergio, threadiness and bugreps
5
+
6
+ require 'rack/session/abstract/id'
7
+ require 'thread'
8
+
9
+ module Rack
10
+ module Session
11
+ # Rack::Session::Pool provides simple cookie based session management.
12
+ # Session data is stored in a hash held by @pool.
13
+ # In the context of a multithreaded environment, sessions being
14
+ # committed to the pool is done in a merging manner.
15
+ #
16
+ # The :drop option is available in rack.session.options if you wish to
17
+ # explicitly remove the session from the session cache.
18
+ #
19
+ # Example:
20
+ # myapp = MyRackApp.new
21
+ # sessioned = Rack::Session::Pool.new(myapp,
22
+ # :domain => 'foo.com',
23
+ # :expire_after => 2592000
24
+ # )
25
+ # Rack::Handler::WEBrick.run sessioned
26
+
27
+ class Pool < Abstract::ID
28
+ attr_reader :mutex, :pool
29
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
30
+
31
+ def initialize(app, options={})
32
+ super
33
+ @pool = Hash.new
34
+ @mutex = Mutex.new
35
+ end
36
+
37
+ def generate_sid
38
+ loop do
39
+ sid = super
40
+ break sid unless @pool.key? sid
41
+ end
42
+ end
43
+
44
+ def get_session(env, sid)
45
+ with_lock(env, [nil, {}]) do
46
+ unless sid and session = @pool[sid]
47
+ sid, session = generate_sid, {}
48
+ @pool.store sid, session
49
+ end
50
+ [sid, session]
51
+ end
52
+ end
53
+
54
+ def set_session(env, session_id, new_session, options)
55
+ with_lock(env, false) do
56
+ @pool.store session_id, new_session
57
+ session_id
58
+ end
59
+ end
60
+
61
+ def destroy_session(env, session_id, options)
62
+ with_lock(env) do
63
+ @pool.delete(session_id)
64
+ generate_sid unless options[:drop]
65
+ end
66
+ end
67
+
68
+ def with_lock(env, default=nil)
69
+ @mutex.lock if env['rack.multithread']
70
+ yield
71
+ rescue
72
+ default
73
+ ensure
74
+ @mutex.unlock if @mutex.locked?
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,378 @@
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
+ exception_string = dump_exception(e)
27
+
28
+ env["rack.errors"].puts(exception_string)
29
+ env["rack.errors"].flush
30
+
31
+ if prefers_plain_text?(env)
32
+ content_type = "text/plain"
33
+ body = [exception_string]
34
+ else
35
+ content_type = "text/html"
36
+ body = pretty(env, e)
37
+ end
38
+
39
+ [500,
40
+ {"Content-Type" => content_type,
41
+ "Content-Length" => Rack::Utils.bytesize(body.join).to_s},
42
+ body]
43
+ end
44
+
45
+ def prefers_plain_text?(env)
46
+ env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" && (!env["HTTP_ACCEPT"] || !env["HTTP_ACCEPT"].include?("text/html"))
47
+ end
48
+
49
+ def dump_exception(exception)
50
+ string = "#{exception.class}: #{exception.message}\n"
51
+ string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
52
+ string
53
+ end
54
+
55
+ def pretty(env, exception)
56
+ req = Rack::Request.new(env)
57
+
58
+ # This double assignment is to prevent an "unused variable" warning on
59
+ # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
60
+ path = path = (req.script_name + req.path_info).squeeze("/")
61
+
62
+ # This double assignment is to prevent an "unused variable" warning on
63
+ # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
64
+ frames = frames = exception.backtrace.map { |line|
65
+ frame = OpenStruct.new
66
+ if line =~ /(.*?):(\d+)(:in `(.*)')?/
67
+ frame.filename = $1
68
+ frame.lineno = $2.to_i
69
+ frame.function = $4
70
+
71
+ begin
72
+ lineno = frame.lineno-1
73
+ lines = ::File.readlines(frame.filename)
74
+ frame.pre_context_lineno = [lineno-CONTEXT, 0].max
75
+ frame.pre_context = lines[frame.pre_context_lineno...lineno]
76
+ frame.context_line = lines[lineno].chomp
77
+ frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
78
+ frame.post_context = lines[lineno+1..frame.post_context_lineno]
79
+ rescue
80
+ end
81
+
82
+ frame
83
+ else
84
+ nil
85
+ end
86
+ }.compact
87
+
88
+ [@template.result(binding)]
89
+ end
90
+
91
+ def h(obj) # :nodoc:
92
+ case obj
93
+ when String
94
+ Utils.escape_html(obj)
95
+ else
96
+ Utils.escape_html(obj.inspect)
97
+ end
98
+ end
99
+
100
+ # :stopdoc:
101
+
102
+ # adapted from Django <djangoproject.com>
103
+ # Copyright (c) 2005, the Lawrence Journal-World
104
+ # Used under the modified BSD license:
105
+ # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
106
+ TEMPLATE = <<'HTML'
107
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
108
+ <html lang="en">
109
+ <head>
110
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
111
+ <meta name="robots" content="NONE,NOARCHIVE" />
112
+ <title><%=h exception.class %> at <%=h path %></title>
113
+ <style type="text/css">
114
+ html * { padding:0; margin:0; }
115
+ body * { padding:10px 20px; }
116
+ body * * { padding:0; }
117
+ body { font:small sans-serif; }
118
+ body>div { border-bottom:1px solid #ddd; }
119
+ h1 { font-weight:normal; }
120
+ h2 { margin-bottom:.8em; }
121
+ h2 span { font-size:80%; color:#666; font-weight:normal; }
122
+ h3 { margin:1em 0 .5em 0; }
123
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
124
+ table {
125
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
126
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
127
+ thead th {
128
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
129
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
130
+ tbody th { text-align:right; color:#666; padding-right:.5em; }
131
+ table.vars { margin:5px 0 2px 40px; }
132
+ table.vars td, table.req td { font-family:monospace; }
133
+ table td.code { width:100%;}
134
+ table td.code div { overflow:hidden; }
135
+ table.source th { color:#666; }
136
+ table.source td {
137
+ font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
138
+ ul.traceback { list-style-type:none; }
139
+ ul.traceback li.frame { margin-bottom:1em; }
140
+ div.context { margin: 10px 0; }
141
+ div.context ol {
142
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
143
+ div.context ol li {
144
+ font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
145
+ div.context ol.context-line li { color:black; background-color:#ccc; }
146
+ div.context ol.context-line li span { float: right; }
147
+ div.commands { margin-left: 40px; }
148
+ div.commands a { color:black; text-decoration:none; }
149
+ #summary { background: #ffc; }
150
+ #summary h2 { font-weight: normal; color: #666; }
151
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
152
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
153
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
154
+ #explanation { background:#eee; }
155
+ #template, #template-not-exist { background:#f6f6f6; }
156
+ #template-not-exist ul { margin: 0 0 0 20px; }
157
+ #traceback { background:#eee; }
158
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
159
+ #summary table { border:none; background:transparent; }
160
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
161
+ #requestinfo h3 { margin-bottom:-1em; }
162
+ .error { background: #ffc; }
163
+ .specific { color:#cc3300; font-weight:bold; }
164
+ </style>
165
+ <script type="text/javascript">
166
+ //<!--
167
+ function getElementsByClassName(oElm, strTagName, strClassName){
168
+ // Written by Jonathan Snook, http://www.snook.ca/jon;
169
+ // Add-ons by Robert Nyman, http://www.robertnyman.com
170
+ var arrElements = (strTagName == "*" && document.all)? document.all :
171
+ oElm.getElementsByTagName(strTagName);
172
+ var arrReturnElements = new Array();
173
+ strClassName = strClassName.replace(/\-/g, "\\-");
174
+ var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
175
+ var oElement;
176
+ for(var i=0; i<arrElements.length; i++){
177
+ oElement = arrElements[i];
178
+ if(oRegExp.test(oElement.className)){
179
+ arrReturnElements.push(oElement);
180
+ }
181
+ }
182
+ return (arrReturnElements)
183
+ }
184
+ function hideAll(elems) {
185
+ for (var e = 0; e < elems.length; e++) {
186
+ elems[e].style.display = 'none';
187
+ }
188
+ }
189
+ window.onload = function() {
190
+ hideAll(getElementsByClassName(document, 'table', 'vars'));
191
+ hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
192
+ hideAll(getElementsByClassName(document, 'ol', 'post-context'));
193
+ }
194
+ function toggle() {
195
+ for (var i = 0; i < arguments.length; i++) {
196
+ var e = document.getElementById(arguments[i]);
197
+ if (e) {
198
+ e.style.display = e.style.display == 'none' ? 'block' : 'none';
199
+ }
200
+ }
201
+ return false;
202
+ }
203
+ function varToggle(link, id) {
204
+ toggle('v' + id);
205
+ var s = link.getElementsByTagName('span')[0];
206
+ var uarr = String.fromCharCode(0x25b6);
207
+ var darr = String.fromCharCode(0x25bc);
208
+ s.innerHTML = s.innerHTML == uarr ? darr : uarr;
209
+ return false;
210
+ }
211
+ //-->
212
+ </script>
213
+ </head>
214
+ <body>
215
+
216
+ <div id="summary">
217
+ <h1><%=h exception.class %> at <%=h path %></h1>
218
+ <h2><%=h exception.message %></h2>
219
+ <table><tr>
220
+ <th>Ruby</th>
221
+ <td>
222
+ <% if first = frames.first %>
223
+ <code><%=h first.filename %></code>: in <code><%=h first.function %></code>, line <%=h frames.first.lineno %>
224
+ <% else %>
225
+ unknown location
226
+ <% end %>
227
+ </td>
228
+ </tr><tr>
229
+ <th>Web</th>
230
+ <td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td>
231
+ </tr></table>
232
+
233
+ <h3>Jump to:</h3>
234
+ <ul id="quicklinks">
235
+ <li><a href="#get-info">GET</a></li>
236
+ <li><a href="#post-info">POST</a></li>
237
+ <li><a href="#cookie-info">Cookies</a></li>
238
+ <li><a href="#env-info">ENV</a></li>
239
+ </ul>
240
+ </div>
241
+
242
+ <div id="traceback">
243
+ <h2>Traceback <span>(innermost first)</span></h2>
244
+ <ul class="traceback">
245
+ <% frames.each { |frame| %>
246
+ <li class="frame">
247
+ <code><%=h frame.filename %></code>: in <code><%=h frame.function %></code>
248
+
249
+ <% if frame.context_line %>
250
+ <div class="context" id="c<%=h frame.object_id %>">
251
+ <% if frame.pre_context %>
252
+ <ol start="<%=h frame.pre_context_lineno+1 %>" class="pre-context" id="pre<%=h frame.object_id %>">
253
+ <% frame.pre_context.each { |line| %>
254
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
255
+ <% } %>
256
+ </ol>
257
+ <% end %>
258
+
259
+ <ol start="<%=h frame.lineno %>" class="context-line">
260
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h frame.context_line %><span>...</span></li></ol>
261
+
262
+ <% if frame.post_context %>
263
+ <ol start='<%=h frame.lineno+1 %>' class="post-context" id="post<%=h frame.object_id %>">
264
+ <% frame.post_context.each { |line| %>
265
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
266
+ <% } %>
267
+ </ol>
268
+ <% end %>
269
+ </div>
270
+ <% end %>
271
+ </li>
272
+ <% } %>
273
+ </ul>
274
+ </div>
275
+
276
+ <div id="requestinfo">
277
+ <h2>Request information</h2>
278
+
279
+ <h3 id="get-info">GET</h3>
280
+ <% unless req.GET.empty? %>
281
+ <table class="req">
282
+ <thead>
283
+ <tr>
284
+ <th>Variable</th>
285
+ <th>Value</th>
286
+ </tr>
287
+ </thead>
288
+ <tbody>
289
+ <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
290
+ <tr>
291
+ <td><%=h key %></td>
292
+ <td class="code"><div><%=h val.inspect %></div></td>
293
+ </tr>
294
+ <% } %>
295
+ </tbody>
296
+ </table>
297
+ <% else %>
298
+ <p>No GET data.</p>
299
+ <% end %>
300
+
301
+ <h3 id="post-info">POST</h3>
302
+ <% unless req.POST.empty? %>
303
+ <table class="req">
304
+ <thead>
305
+ <tr>
306
+ <th>Variable</th>
307
+ <th>Value</th>
308
+ </tr>
309
+ </thead>
310
+ <tbody>
311
+ <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
312
+ <tr>
313
+ <td><%=h key %></td>
314
+ <td class="code"><div><%=h val.inspect %></div></td>
315
+ </tr>
316
+ <% } %>
317
+ </tbody>
318
+ </table>
319
+ <% else %>
320
+ <p>No POST data.</p>
321
+ <% end %>
322
+
323
+
324
+ <h3 id="cookie-info">COOKIES</h3>
325
+ <% unless req.cookies.empty? %>
326
+ <table class="req">
327
+ <thead>
328
+ <tr>
329
+ <th>Variable</th>
330
+ <th>Value</th>
331
+ </tr>
332
+ </thead>
333
+ <tbody>
334
+ <% req.cookies.each { |key, val| %>
335
+ <tr>
336
+ <td><%=h key %></td>
337
+ <td class="code"><div><%=h val.inspect %></div></td>
338
+ </tr>
339
+ <% } %>
340
+ </tbody>
341
+ </table>
342
+ <% else %>
343
+ <p>No cookie data.</p>
344
+ <% end %>
345
+
346
+ <h3 id="env-info">Rack ENV</h3>
347
+ <table class="req">
348
+ <thead>
349
+ <tr>
350
+ <th>Variable</th>
351
+ <th>Value</th>
352
+ </tr>
353
+ </thead>
354
+ <tbody>
355
+ <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
356
+ <tr>
357
+ <td><%=h key %></td>
358
+ <td class="code"><div><%=h val %></div></td>
359
+ </tr>
360
+ <% } %>
361
+ </tbody>
362
+ </table>
363
+
364
+ </div>
365
+
366
+ <div id="explanation">
367
+ <p>
368
+ You're seeing this error because you use <code>Rack::ShowExceptions</code>.
369
+ </p>
370
+ </div>
371
+
372
+ </body>
373
+ </html>
374
+ HTML
375
+
376
+ # :startdoc:
377
+ end
378
+ end