rack 1.4.7 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (183) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +122 -456
  5. data/Rakefile +32 -31
  6. data/SPEC +119 -29
  7. data/bin/rackup +1 -0
  8. data/contrib/rack_logo.svg +164 -111
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +4 -2
  11. data/example/protectedlobster.ru +3 -1
  12. data/lib/rack/auth/abstract/handler.rb +7 -5
  13. data/lib/rack/auth/abstract/request.rb +8 -6
  14. data/lib/rack/auth/basic.rb +5 -2
  15. data/lib/rack/auth/digest/md5.rb +10 -8
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +5 -4
  18. data/lib/rack/auth/digest/request.rb +4 -2
  19. data/lib/rack/body_proxy.rb +11 -9
  20. data/lib/rack/builder.rb +63 -20
  21. data/lib/rack/cascade.rb +10 -9
  22. data/lib/rack/chunked.rb +45 -11
  23. data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
  24. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
  25. data/lib/rack/config.rb +7 -0
  26. data/lib/rack/content_length.rb +12 -6
  27. data/lib/rack/content_type.rb +4 -2
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +73 -42
  30. data/lib/rack/directory.rb +77 -56
  31. data/lib/rack/etag.rb +25 -13
  32. data/lib/rack/events.rb +156 -0
  33. data/lib/rack/file.rb +4 -143
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler/cgi.rb +18 -17
  36. data/lib/rack/handler/fastcgi.rb +21 -17
  37. data/lib/rack/handler/lsws.rb +14 -12
  38. data/lib/rack/handler/scgi.rb +27 -21
  39. data/lib/rack/handler/thin.rb +19 -5
  40. data/lib/rack/handler/webrick.rb +66 -24
  41. data/lib/rack/handler.rb +29 -19
  42. data/lib/rack/head.rb +21 -14
  43. data/lib/rack/lint.rb +259 -65
  44. data/lib/rack/lobster.rb +17 -10
  45. data/lib/rack/lock.rb +19 -10
  46. data/lib/rack/logger.rb +4 -2
  47. data/lib/rack/media_type.rb +43 -0
  48. data/lib/rack/method_override.rb +52 -0
  49. data/lib/rack/mime.rb +43 -6
  50. data/lib/rack/mock.rb +109 -44
  51. data/lib/rack/multipart/generator.rb +11 -12
  52. data/lib/rack/multipart/parser.rb +302 -115
  53. data/lib/rack/multipart/uploaded_file.rb +4 -3
  54. data/lib/rack/multipart.rb +40 -9
  55. data/lib/rack/null_logger.rb +39 -0
  56. data/lib/rack/query_parser.rb +218 -0
  57. data/lib/rack/recursive.rb +14 -11
  58. data/lib/rack/reloader.rb +12 -5
  59. data/lib/rack/request.rb +484 -270
  60. data/lib/rack/response.rb +196 -77
  61. data/lib/rack/rewindable_input.rb +5 -14
  62. data/lib/rack/runtime.rb +13 -6
  63. data/lib/rack/sendfile.rb +44 -20
  64. data/lib/rack/server.rb +175 -61
  65. data/lib/rack/session/abstract/id.rb +276 -133
  66. data/lib/rack/session/cookie.rb +75 -40
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +24 -18
  69. data/lib/rack/show_exceptions.rb +392 -0
  70. data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
  71. data/lib/rack/static.rb +65 -38
  72. data/lib/rack/tempfile_reaper.rb +24 -0
  73. data/lib/rack/urlmap.rb +40 -15
  74. data/lib/rack/utils.rb +316 -285
  75. data/lib/rack.rb +78 -23
  76. data/rack.gemspec +26 -19
  77. metadata +44 -209
  78. data/KNOWN-ISSUES +0 -30
  79. data/lib/rack/backports/uri/common_18.rb +0 -56
  80. data/lib/rack/backports/uri/common_192.rb +0 -52
  81. data/lib/rack/backports/uri/common_193.rb +0 -29
  82. data/lib/rack/handler/evented_mongrel.rb +0 -8
  83. data/lib/rack/handler/mongrel.rb +0 -100
  84. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  85. data/lib/rack/methodoverride.rb +0 -33
  86. data/lib/rack/nulllogger.rb +0 -18
  87. data/lib/rack/showexceptions.rb +0 -378
  88. data/test/builder/anything.rb +0 -5
  89. data/test/builder/comment.ru +0 -4
  90. data/test/builder/end.ru +0 -5
  91. data/test/builder/line.ru +0 -1
  92. data/test/builder/options.ru +0 -2
  93. data/test/cgi/assets/folder/test.js +0 -1
  94. data/test/cgi/assets/fonts/font.eot +0 -1
  95. data/test/cgi/assets/images/image.png +0 -1
  96. data/test/cgi/assets/index.html +0 -1
  97. data/test/cgi/assets/javascripts/app.js +0 -1
  98. data/test/cgi/assets/stylesheets/app.css +0 -1
  99. data/test/cgi/lighttpd.conf +0 -26
  100. data/test/cgi/lighttpd.errors +0 -1
  101. data/test/cgi/rackup_stub.rb +0 -6
  102. data/test/cgi/sample_rackup.ru +0 -5
  103. data/test/cgi/test +0 -9
  104. data/test/cgi/test+directory/test+file +0 -1
  105. data/test/cgi/test.fcgi +0 -8
  106. data/test/cgi/test.ru +0 -5
  107. data/test/gemloader.rb +0 -10
  108. data/test/multipart/bad_robots +0 -259
  109. data/test/multipart/binary +0 -0
  110. data/test/multipart/content_type_and_no_filename +0 -6
  111. data/test/multipart/empty +0 -10
  112. data/test/multipart/fail_16384_nofile +0 -814
  113. data/test/multipart/file1.txt +0 -1
  114. data/test/multipart/filename_and_modification_param +0 -7
  115. data/test/multipart/filename_with_escaped_quotes +0 -6
  116. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  117. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages +0 -6
  119. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  120. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  121. data/test/multipart/filename_with_unescaped_quotes +0 -6
  122. data/test/multipart/ie +0 -6
  123. data/test/multipart/mixed_files +0 -21
  124. data/test/multipart/nested +0 -10
  125. data/test/multipart/none +0 -9
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth.rb +0 -57
  133. data/test/spec_auth_basic.rb +0 -81
  134. data/test/spec_auth_digest.rb +0 -259
  135. data/test/spec_body_proxy.rb +0 -69
  136. data/test/spec_builder.rb +0 -207
  137. data/test/spec_cascade.rb +0 -61
  138. data/test/spec_cgi.rb +0 -102
  139. data/test/spec_chunked.rb +0 -87
  140. data/test/spec_commonlogger.rb +0 -57
  141. data/test/spec_conditionalget.rb +0 -102
  142. data/test/spec_config.rb +0 -22
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -45
  145. data/test/spec_deflater.rb +0 -187
  146. data/test/spec_directory.rb +0 -88
  147. data/test/spec_etag.rb +0 -98
  148. data/test/spec_fastcgi.rb +0 -107
  149. data/test/spec_file.rb +0 -200
  150. data/test/spec_handler.rb +0 -59
  151. data/test/spec_head.rb +0 -48
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -58
  154. data/test/spec_lock.rb +0 -167
  155. data/test/spec_logger.rb +0 -23
  156. data/test/spec_methodoverride.rb +0 -72
  157. data/test/spec_mock.rb +0 -269
  158. data/test/spec_mongrel.rb +0 -182
  159. data/test/spec_multipart.rb +0 -479
  160. data/test/spec_nulllogger.rb +0 -23
  161. data/test/spec_recursive.rb +0 -72
  162. data/test/spec_request.rb +0 -955
  163. data/test/spec_response.rb +0 -313
  164. data/test/spec_rewindable_input.rb +0 -118
  165. data/test/spec_runtime.rb +0 -49
  166. data/test/spec_sendfile.rb +0 -90
  167. data/test/spec_server.rb +0 -121
  168. data/test/spec_session_abstract_id.rb +0 -43
  169. data/test/spec_session_cookie.rb +0 -361
  170. data/test/spec_session_memcache.rb +0 -321
  171. data/test/spec_session_pool.rb +0 -209
  172. data/test/spec_showexceptions.rb +0 -92
  173. data/test/spec_showstatus.rb +0 -84
  174. data/test/spec_static.rb +0 -145
  175. data/test/spec_thin.rb +0 -86
  176. data/test/spec_urlmap.rb +0 -213
  177. data/test/spec_utils.rb +0 -554
  178. data/test/spec_webrick.rb +0 -143
  179. data/test/static/another/index.html +0 -1
  180. data/test/static/index.html +0 -1
  181. data/test/testrequest.rb +0 -78
  182. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  183. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -0,0 +1,392 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+ require 'erb'
5
+ require 'rack/request'
6
+ require 'rack/utils'
7
+
8
+ module Rack
9
+ # Rack::ShowExceptions catches all exceptions raised from the app it
10
+ # wraps. It shows a useful backtrace with the sourcefile and
11
+ # clickable context, the whole Rack environment and the request
12
+ # data.
13
+ #
14
+ # Be careful when you use this on public-facing sites as it could
15
+ # reveal information helpful to attackers.
16
+
17
+ class ShowExceptions
18
+ CONTEXT = 7
19
+
20
+ def initialize(app)
21
+ @app = app
22
+ end
23
+
24
+ def call(env)
25
+ @app.call(env)
26
+ rescue StandardError, LoadError, SyntaxError => e
27
+ exception_string = dump_exception(e)
28
+
29
+ env[RACK_ERRORS].puts(exception_string)
30
+ env[RACK_ERRORS].flush
31
+
32
+ if accepts_html?(env)
33
+ content_type = "text/html"
34
+ body = pretty(env, e)
35
+ else
36
+ content_type = "text/plain"
37
+ body = exception_string
38
+ end
39
+
40
+ [
41
+ 500,
42
+ {
43
+ CONTENT_TYPE => content_type,
44
+ CONTENT_LENGTH => body.bytesize.to_s,
45
+ },
46
+ [body],
47
+ ]
48
+ end
49
+
50
+ def prefers_plaintext?(env)
51
+ !accepts_html?(env)
52
+ end
53
+
54
+ def accepts_html?(env)
55
+ Rack::Utils.best_q_match(env["HTTP_ACCEPT"], %w[text/html])
56
+ end
57
+ private :accepts_html?
58
+
59
+ def dump_exception(exception)
60
+ string = "#{exception.class}: #{exception.message}\n".dup
61
+ string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
62
+ string
63
+ end
64
+
65
+ def pretty(env, exception)
66
+ req = Rack::Request.new(env)
67
+
68
+ # This double assignment is to prevent an "unused variable" warning on
69
+ # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
70
+ path = path = (req.script_name + req.path_info).squeeze("/")
71
+
72
+ # This double assignment is to prevent an "unused variable" warning on
73
+ # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
74
+ frames = frames = exception.backtrace.map { |line|
75
+ frame = OpenStruct.new
76
+ if line =~ /(.*?):(\d+)(:in `(.*)')?/
77
+ frame.filename = $1
78
+ frame.lineno = $2.to_i
79
+ frame.function = $4
80
+
81
+ begin
82
+ lineno = frame.lineno - 1
83
+ lines = ::File.readlines(frame.filename)
84
+ frame.pre_context_lineno = [lineno - CONTEXT, 0].max
85
+ frame.pre_context = lines[frame.pre_context_lineno...lineno]
86
+ frame.context_line = lines[lineno].chomp
87
+ frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
88
+ frame.post_context = lines[lineno + 1..frame.post_context_lineno]
89
+ rescue
90
+ end
91
+
92
+ frame
93
+ else
94
+ nil
95
+ end
96
+ }.compact
97
+
98
+ template.result(binding)
99
+ end
100
+
101
+ def template
102
+ TEMPLATE
103
+ end
104
+
105
+ def h(obj) # :nodoc:
106
+ case obj
107
+ when String
108
+ Utils.escape_html(obj)
109
+ else
110
+ Utils.escape_html(obj.inspect)
111
+ end
112
+ end
113
+
114
+ # :stopdoc:
115
+
116
+ # adapted from Django <www.djangoproject.com>
117
+ # Copyright (c) Django Software Foundation and individual contributors.
118
+ # Used under the modified BSD license:
119
+ # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
120
+ TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
121
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
122
+ <html lang="en">
123
+ <head>
124
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
125
+ <meta name="robots" content="NONE,NOARCHIVE" />
126
+ <title><%=h exception.class %> at <%=h path %></title>
127
+ <style type="text/css">
128
+ html * { padding:0; margin:0; }
129
+ body * { padding:10px 20px; }
130
+ body * * { padding:0; }
131
+ body { font:small sans-serif; }
132
+ body>div { border-bottom:1px solid #ddd; }
133
+ h1 { font-weight:normal; }
134
+ h2 { margin-bottom:.8em; }
135
+ h2 span { font-size:80%; color:#666; font-weight:normal; }
136
+ h3 { margin:1em 0 .5em 0; }
137
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
138
+ table {
139
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
140
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
141
+ thead th {
142
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
143
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
144
+ tbody th { text-align:right; color:#666; padding-right:.5em; }
145
+ table.vars { margin:5px 0 2px 40px; }
146
+ table.vars td, table.req td { font-family:monospace; }
147
+ table td.code { width:100%;}
148
+ table td.code div { overflow:hidden; }
149
+ table.source th { color:#666; }
150
+ table.source td {
151
+ font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
152
+ ul.traceback { list-style-type:none; }
153
+ ul.traceback li.frame { margin-bottom:1em; }
154
+ div.context { margin: 10px 0; }
155
+ div.context ol {
156
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
157
+ div.context ol li {
158
+ font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
159
+ div.context ol.context-line li { color:black; background-color:#ccc; }
160
+ div.context ol.context-line li span { float: right; }
161
+ div.commands { margin-left: 40px; }
162
+ div.commands a { color:black; text-decoration:none; }
163
+ #summary { background: #ffc; }
164
+ #summary h2 { font-weight: normal; color: #666; }
165
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
166
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
167
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
168
+ #explanation { background:#eee; }
169
+ #template, #template-not-exist { background:#f6f6f6; }
170
+ #template-not-exist ul { margin: 0 0 0 20px; }
171
+ #traceback { background:#eee; }
172
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
173
+ #summary table { border:none; background:transparent; }
174
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
175
+ #requestinfo h3 { margin-bottom:-1em; }
176
+ .error { background: #ffc; }
177
+ .specific { color:#cc3300; font-weight:bold; }
178
+ </style>
179
+ <script type="text/javascript">
180
+ //<!--
181
+ function getElementsByClassName(oElm, strTagName, strClassName){
182
+ // Written by Jonathan Snook, http://www.snook.ca/jon;
183
+ // Add-ons by Robert Nyman, http://www.robertnyman.com
184
+ var arrElements = (strTagName == "*" && document.all)? document.all :
185
+ oElm.getElementsByTagName(strTagName);
186
+ var arrReturnElements = new Array();
187
+ strClassName = strClassName.replace(/\-/g, "\\-");
188
+ var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
189
+ var oElement;
190
+ for(var i=0; i<arrElements.length; i++){
191
+ oElement = arrElements[i];
192
+ if(oRegExp.test(oElement.className)){
193
+ arrReturnElements.push(oElement);
194
+ }
195
+ }
196
+ return (arrReturnElements)
197
+ }
198
+ function hideAll(elems) {
199
+ for (var e = 0; e < elems.length; e++) {
200
+ elems[e].style.display = 'none';
201
+ }
202
+ }
203
+ window.onload = function() {
204
+ hideAll(getElementsByClassName(document, 'table', 'vars'));
205
+ hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
206
+ hideAll(getElementsByClassName(document, 'ol', 'post-context'));
207
+ }
208
+ function toggle() {
209
+ for (var i = 0; i < arguments.length; i++) {
210
+ var e = document.getElementById(arguments[i]);
211
+ if (e) {
212
+ e.style.display = e.style.display == 'none' ? 'block' : 'none';
213
+ }
214
+ }
215
+ return false;
216
+ }
217
+ function varToggle(link, id) {
218
+ toggle('v' + id);
219
+ var s = link.getElementsByTagName('span')[0];
220
+ var uarr = String.fromCharCode(0x25b6);
221
+ var darr = String.fromCharCode(0x25bc);
222
+ s.innerHTML = s.innerHTML == uarr ? darr : uarr;
223
+ return false;
224
+ }
225
+ //-->
226
+ </script>
227
+ </head>
228
+ <body>
229
+
230
+ <div id="summary">
231
+ <h1><%=h exception.class %> at <%=h path %></h1>
232
+ <h2><%=h exception.message %></h2>
233
+ <table><tr>
234
+ <th>Ruby</th>
235
+ <td>
236
+ <% if first = frames.first %>
237
+ <code><%=h first.filename %></code>: in <code><%=h first.function %></code>, line <%=h frames.first.lineno %>
238
+ <% else %>
239
+ unknown location
240
+ <% end %>
241
+ </td>
242
+ </tr><tr>
243
+ <th>Web</th>
244
+ <td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td>
245
+ </tr></table>
246
+
247
+ <h3>Jump to:</h3>
248
+ <ul id="quicklinks">
249
+ <li><a href="#get-info">GET</a></li>
250
+ <li><a href="#post-info">POST</a></li>
251
+ <li><a href="#cookie-info">Cookies</a></li>
252
+ <li><a href="#env-info">ENV</a></li>
253
+ </ul>
254
+ </div>
255
+
256
+ <div id="traceback">
257
+ <h2>Traceback <span>(innermost first)</span></h2>
258
+ <ul class="traceback">
259
+ <% frames.each { |frame| %>
260
+ <li class="frame">
261
+ <code><%=h frame.filename %></code>: in <code><%=h frame.function %></code>
262
+
263
+ <% if frame.context_line %>
264
+ <div class="context" id="c<%=h frame.object_id %>">
265
+ <% if frame.pre_context %>
266
+ <ol start="<%=h frame.pre_context_lineno+1 %>" class="pre-context" id="pre<%=h frame.object_id %>">
267
+ <% frame.pre_context.each { |line| %>
268
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
269
+ <% } %>
270
+ </ol>
271
+ <% end %>
272
+
273
+ <ol start="<%=h frame.lineno %>" class="context-line">
274
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h frame.context_line %><span>...</span></li></ol>
275
+
276
+ <% if frame.post_context %>
277
+ <ol start='<%=h frame.lineno+1 %>' class="post-context" id="post<%=h frame.object_id %>">
278
+ <% frame.post_context.each { |line| %>
279
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
280
+ <% } %>
281
+ </ol>
282
+ <% end %>
283
+ </div>
284
+ <% end %>
285
+ </li>
286
+ <% } %>
287
+ </ul>
288
+ </div>
289
+
290
+ <div id="requestinfo">
291
+ <h2>Request information</h2>
292
+
293
+ <h3 id="get-info">GET</h3>
294
+ <% if req.GET and not req.GET.empty? %>
295
+ <table class="req">
296
+ <thead>
297
+ <tr>
298
+ <th>Variable</th>
299
+ <th>Value</th>
300
+ </tr>
301
+ </thead>
302
+ <tbody>
303
+ <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
304
+ <tr>
305
+ <td><%=h key %></td>
306
+ <td class="code"><div><%=h val.inspect %></div></td>
307
+ </tr>
308
+ <% } %>
309
+ </tbody>
310
+ </table>
311
+ <% else %>
312
+ <p>No GET data.</p>
313
+ <% end %>
314
+
315
+ <h3 id="post-info">POST</h3>
316
+ <% if req.POST and not req.POST.empty? %>
317
+ <table class="req">
318
+ <thead>
319
+ <tr>
320
+ <th>Variable</th>
321
+ <th>Value</th>
322
+ </tr>
323
+ </thead>
324
+ <tbody>
325
+ <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
326
+ <tr>
327
+ <td><%=h key %></td>
328
+ <td class="code"><div><%=h val.inspect %></div></td>
329
+ </tr>
330
+ <% } %>
331
+ </tbody>
332
+ </table>
333
+ <% else %>
334
+ <p>No POST data.</p>
335
+ <% end %>
336
+
337
+
338
+ <h3 id="cookie-info">COOKIES</h3>
339
+ <% unless req.cookies.empty? %>
340
+ <table class="req">
341
+ <thead>
342
+ <tr>
343
+ <th>Variable</th>
344
+ <th>Value</th>
345
+ </tr>
346
+ </thead>
347
+ <tbody>
348
+ <% req.cookies.each { |key, val| %>
349
+ <tr>
350
+ <td><%=h key %></td>
351
+ <td class="code"><div><%=h val.inspect %></div></td>
352
+ </tr>
353
+ <% } %>
354
+ </tbody>
355
+ </table>
356
+ <% else %>
357
+ <p>No cookie data.</p>
358
+ <% end %>
359
+
360
+ <h3 id="env-info">Rack ENV</h3>
361
+ <table class="req">
362
+ <thead>
363
+ <tr>
364
+ <th>Variable</th>
365
+ <th>Value</th>
366
+ </tr>
367
+ </thead>
368
+ <tbody>
369
+ <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
370
+ <tr>
371
+ <td><%=h key %></td>
372
+ <td class="code"><div><%=h val.inspect %></div></td>
373
+ </tr>
374
+ <% } %>
375
+ </tbody>
376
+ </table>
377
+
378
+ </div>
379
+
380
+ <div id="explanation">
381
+ <p>
382
+ You're seeing this error because you use <code>Rack::ShowExceptions</code>.
383
+ </p>
384
+ </div>
385
+
386
+ </body>
387
+ </html>
388
+ HTML
389
+
390
+ # :startdoc:
391
+ end
392
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
  require 'rack/request'
3
5
  require 'rack/utils'
4
6
 
5
7
  module Rack
6
- # Rack::ShowStatus catches all empty responses and replaces them
8
+ # Rack::ShowStatus catches all empty responses and replaces them
7
9
  # with a site explaining the error.
8
10
  #
9
11
  # Additional details can be put into <tt>rack.showstatus.detail</tt>
@@ -19,10 +21,10 @@ module Rack
19
21
  def call(env)
20
22
  status, headers, body = @app.call(env)
21
23
  headers = Utils::HeaderHash.new(headers)
22
- empty = headers['Content-Length'].to_i <= 0
24
+ empty = headers[CONTENT_LENGTH].to_i <= 0
23
25
 
24
26
  # client or server error, or explicit message
25
- if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
27
+ if (status.to_i >= 400 && empty) || env[RACK_SHOWSTATUS_DETAIL]
26
28
  # This double assignment is to prevent an "unused variable" warning on
27
29
  # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
28
30
  req = req = Rack::Request.new(env)
@@ -31,11 +33,11 @@ module Rack
31
33
 
32
34
  # This double assignment is to prevent an "unused variable" warning on
33
35
  # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
34
- detail = detail = env["rack.showstatus.detail"] || message
36
+ detail = detail = env[RACK_SHOWSTATUS_DETAIL] || message
35
37
 
36
38
  body = @template.result(binding)
37
- size = Rack::Utils.bytesize(body)
38
- [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]]
39
+ size = body.bytesize
40
+ [status, headers.merge(CONTENT_TYPE => "text/html", CONTENT_LENGTH => size.to_s), [body]]
39
41
  else
40
42
  [status, headers, body]
41
43
  end
@@ -52,8 +54,8 @@ module Rack
52
54
 
53
55
  # :stopdoc:
54
56
 
55
- # adapted from Django <djangoproject.com>
56
- # Copyright (c) 2005, the Lawrence Journal-World
57
+ # adapted from Django <www.djangoproject.com>
58
+ # Copyright (c) Django Software Foundation and individual contributors.
57
59
  # Used under the modified BSD license:
58
60
  # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
59
61
  TEMPLATE = <<'HTML'
@@ -96,7 +98,7 @@ TEMPLATE = <<'HTML'
96
98
  </table>
97
99
  </div>
98
100
  <div id="info">
99
- <p><%= detail %></p>
101
+ <p><%=h detail %></p>
100
102
  </div>
101
103
 
102
104
  <div id="explanation">
data/lib/rack/static.rb CHANGED
@@ -1,8 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/files"
4
+ require "rack/utils"
5
+
6
+ require_relative 'core_ext/regexp'
7
+
1
8
  module Rack
2
9
 
3
10
  # The Rack::Static middleware intercepts requests for static files
4
11
  # (javascript files, images, stylesheets, etc) based on the url prefixes or
5
- # route mappings passed in the options, and serves them using a Rack::File
12
+ # route mappings passed in the options, and serves them using a Rack::Files
6
13
  # object. This allows a Rack stack to serve both static and dynamic content.
7
14
  #
8
15
  # Examples:
@@ -53,8 +60,8 @@ module Rack
53
60
  # 4) Regular Expressions / Regexp
54
61
  # Provide a regular expression
55
62
  # %r{\.(?:css|js)\z} => Matches files ending in .css or .js
56
- # /\.(?:eot|ttf|otf|woff|svg)\z/ => Matches files ending in
57
- # the most common web font formats (.eot, .ttf, .otf, .woff, .svg)
63
+ # /\.(?:eot|ttf|otf|woff2|woff|svg)\z/ => Matches files ending in
64
+ # the most common web font formats (.eot, .ttf, .otf, .woff2, .woff, .svg)
58
65
  # Note: This Regexp is available as a shortcut, using the :fonts rule
59
66
  #
60
67
  # 5) Font Shortcut
@@ -79,24 +86,29 @@ module Rack
79
86
  # ]
80
87
  #
81
88
  class Static
89
+ using ::Rack::RegexpExtensions
82
90
 
83
- def initialize(app, options={})
91
+ def initialize(app, options = {})
84
92
  @app = app
85
93
  @urls = options[:urls] || ["/favicon.ico"]
86
94
  @index = options[:index]
95
+ @gzip = options[:gzip]
87
96
  root = options[:root] || Dir.pwd
88
97
 
89
98
  # HTTP Headers
90
99
  @header_rules = options[:header_rules] || []
91
100
  # Allow for legacy :cache_control option while prioritizing global header_rules setting
92
- @header_rules.insert(0, [:all, {'Cache-Control' => options[:cache_control]}]) if options[:cache_control]
93
- @headers = {}
101
+ @header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]
102
+
103
+ @file_server = Rack::Files.new(root)
104
+ end
94
105
 
95
- @file_server = Rack::File.new(root, @headers)
106
+ def add_index_root?(path)
107
+ @index && route_file(path) && path.end_with?('/')
96
108
  end
97
109
 
98
110
  def overwrite_file_path(path)
99
- @urls.kind_of?(Hash) && @urls.key?(path) || @index && path =~ /\/$/
111
+ @urls.kind_of?(Hash) && @urls.key?(path) || add_index_root?(path)
100
112
  end
101
113
 
102
114
  def route_file(path)
@@ -108,46 +120,61 @@ module Rack
108
120
  end
109
121
 
110
122
  def call(env)
111
- path = env["PATH_INFO"]
123
+ path = env[PATH_INFO]
112
124
 
113
125
  if can_serve(path)
114
- env["PATH_INFO"] = (path =~ /\/$/ ? path + @index : @urls[path]) if overwrite_file_path(path)
115
- @path = env["PATH_INFO"]
116
- apply_header_rules
117
- @file_server.call(env)
126
+ if overwrite_file_path(path)
127
+ env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
128
+ elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
129
+ path = env[PATH_INFO]
130
+ env[PATH_INFO] += '.gz'
131
+ response = @file_server.call(env)
132
+ env[PATH_INFO] = path
133
+
134
+ if response[0] == 404
135
+ response = nil
136
+ else
137
+ if mime_type = Mime.mime_type(::File.extname(path), 'text/plain')
138
+ response[1][CONTENT_TYPE] = mime_type
139
+ end
140
+ response[1]['Content-Encoding'] = 'gzip'
141
+ end
142
+ end
143
+
144
+ path = env[PATH_INFO]
145
+ response ||= @file_server.call(env)
146
+
147
+ headers = response[1]
148
+ applicable_rules(path).each do |rule, new_headers|
149
+ new_headers.each { |field, content| headers[field] = content }
150
+ end
151
+
152
+ response
118
153
  else
119
154
  @app.call(env)
120
155
  end
121
156
  end
122
157
 
123
158
  # Convert HTTP header rules to HTTP headers
124
- def apply_header_rules
125
- @header_rules.each do |rule, headers|
126
- apply_rule(rule, headers)
159
+ def applicable_rules(path)
160
+ @header_rules.find_all do |rule, new_headers|
161
+ case rule
162
+ when :all
163
+ true
164
+ when :fonts
165
+ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
166
+ when String
167
+ path = ::Rack::Utils.unescape(path)
168
+ path.start_with?(rule) || path.start_with?('/' + rule)
169
+ when Array
170
+ /\.(#{rule.join('|')})\z/.match?(path)
171
+ when Regexp
172
+ rule.match?(path)
173
+ else
174
+ false
175
+ end
127
176
  end
128
177
  end
129
178
 
130
- def apply_rule(rule, headers)
131
- case rule
132
- when :all # All files
133
- set_headers(headers)
134
- when :fonts # Fonts Shortcut
135
- set_headers(headers) if @path.match(/\.(?:ttf|otf|eot|woff|svg)\z/)
136
- when String # Folder
137
- path = ::Rack::Utils.unescape(@path)
138
- set_headers(headers) if (path.start_with?(rule) || path.start_with?('/' + rule))
139
- when Array # Extension/Extensions
140
- extensions = rule.join('|')
141
- set_headers(headers) if @path.match(/\.(#{extensions})\z/)
142
- when Regexp # Flexible Regexp
143
- set_headers(headers) if @path.match(rule)
144
- else
145
- end
146
- end
147
-
148
- def set_headers(headers)
149
- headers.each { |field, content| @headers[field] = content }
150
- end
151
-
152
179
  end
153
180
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/body_proxy'
4
+
5
+ module Rack
6
+
7
+ # Middleware tracks and cleans Tempfiles created throughout a request (i.e. Rack::Multipart)
8
+ # Ideas/strategy based on posts by Eric Wong and Charles Oliver Nutter
9
+ # https://groups.google.com/forum/#!searchin/rack-devel/temp/rack-devel/brK8eh-MByw/sw61oJJCGRMJ
10
+ class TempfileReaper
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ env[RACK_TEMPFILES] ||= []
17
+ status, headers, body = @app.call(env)
18
+ body_proxy = BodyProxy.new(body) do
19
+ env[RACK_TEMPFILES].each(&:close!) unless env[RACK_TEMPFILES].nil?
20
+ end
21
+ [status, headers, body_proxy]
22
+ end
23
+ end
24
+ end