rack 1.4.0 → 1.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. data/COPYING +1 -1
  2. data/KNOWN-ISSUES +9 -0
  3. data/README.rdoc +118 -4
  4. data/Rakefile +18 -11
  5. data/SPEC +3 -1
  6. data/contrib/rack.png +0 -0
  7. data/contrib/rack.svg +150 -0
  8. data/contrib/rdoc.css +412 -0
  9. data/lib/rack/auth/abstract/request.rb +5 -1
  10. data/lib/rack/auth/basic.rb +1 -1
  11. data/lib/rack/auth/digest/nonce.rb +1 -1
  12. data/lib/rack/backports/uri/common_18.rb +14 -28
  13. data/lib/rack/backports/uri/common_192.rb +14 -17
  14. data/lib/rack/backports/uri/common_193.rb +29 -0
  15. data/lib/rack/body_proxy.rb +15 -2
  16. data/lib/rack/builder.rb +1 -1
  17. data/lib/rack/cascade.rb +12 -1
  18. data/lib/rack/commonlogger.rb +18 -5
  19. data/lib/rack/deflater.rb +5 -1
  20. data/lib/rack/directory.rb +1 -1
  21. data/lib/rack/etag.rb +6 -3
  22. data/lib/rack/file.rb +20 -16
  23. data/lib/rack/head.rb +1 -0
  24. data/lib/rack/lint.rb +3 -1
  25. data/lib/rack/lock.rb +3 -4
  26. data/lib/rack/mime.rb +1 -1
  27. data/lib/rack/mock.rb +3 -2
  28. data/lib/rack/multipart/parser.rb +22 -20
  29. data/lib/rack/multipart.rb +2 -2
  30. data/lib/rack/reloader.rb +1 -1
  31. data/lib/rack/request.rb +4 -6
  32. data/lib/rack/response.rb +19 -17
  33. data/lib/rack/server.rb +28 -2
  34. data/lib/rack/session/abstract/id.rb +8 -3
  35. data/lib/rack/session/cookie.rb +20 -7
  36. data/lib/rack/showstatus.rb +2 -2
  37. data/lib/rack/static.rb +91 -9
  38. data/lib/rack/utils.rb +74 -36
  39. data/lib/rack.rb +13 -1
  40. data/rack.gemspec +3 -3
  41. data/test/builder/line.ru +1 -0
  42. data/test/cgi/assets/folder/test.js +1 -0
  43. data/test/cgi/assets/fonts/font.eot +1 -0
  44. data/test/cgi/assets/images/image.png +1 -0
  45. data/test/cgi/assets/index.html +1 -0
  46. data/test/cgi/assets/javascripts/app.js +1 -0
  47. data/test/cgi/assets/stylesheets/app.css +1 -0
  48. data/test/multipart/filename_with_unescaped_percentages +6 -0
  49. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  50. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  51. data/test/spec_auth.rb +57 -0
  52. data/test/spec_auth_basic.rb +8 -0
  53. data/test/spec_auth_digest.rb +14 -0
  54. data/test/spec_body_proxy.rb +21 -0
  55. data/test/spec_builder.rb +7 -1
  56. data/test/spec_cascade.rb +14 -3
  57. data/test/spec_chunked.rb +6 -6
  58. data/test/spec_config.rb +0 -1
  59. data/test/spec_content_length.rb +26 -13
  60. data/test/spec_content_type.rb +15 -5
  61. data/test/spec_deflater.rb +35 -17
  62. data/test/spec_directory.rb +20 -1
  63. data/test/spec_etag.rb +29 -13
  64. data/test/spec_file.rb +58 -30
  65. data/test/spec_head.rb +25 -7
  66. data/test/spec_lobster.rb +20 -5
  67. data/test/spec_lock.rb +46 -21
  68. data/test/spec_logger.rb +2 -7
  69. data/test/spec_methodoverride.rb +21 -22
  70. data/test/spec_mock.rb +12 -7
  71. data/test/spec_multipart.rb +129 -2
  72. data/test/spec_nulllogger.rb +13 -2
  73. data/test/spec_recursive.rb +12 -9
  74. data/test/spec_request.rb +15 -2
  75. data/test/spec_response.rb +41 -3
  76. data/test/spec_runtime.rb +15 -5
  77. data/test/spec_sendfile.rb +13 -9
  78. data/test/spec_server.rb +47 -0
  79. data/test/spec_session_cookie.rb +93 -3
  80. data/test/spec_session_memcache.rb +10 -8
  81. data/test/spec_session_pool.rb +13 -10
  82. data/test/spec_showexceptions.rb +9 -4
  83. data/test/spec_showstatus.rb +10 -5
  84. data/test/spec_static.rb +89 -8
  85. data/test/spec_urlmap.rb +10 -10
  86. data/test/spec_utils.rb +34 -7
  87. data/test/static/another/index.html +1 -0
  88. metadata +26 -8
data/contrib/rdoc.css ADDED
@@ -0,0 +1,412 @@
1
+ /* Forked from the Darkfish templates rdoc.css file, much hacked, probably
2
+ * imperfect */
3
+
4
+ html { max-width: 960px; margin: 0 auto; }
5
+ body {
6
+ font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif;
7
+ }
8
+ body.file-popup {
9
+ font-size: 90%;
10
+ margin-left: 0;
11
+ }
12
+
13
+ h1 {
14
+ color: #4183C4;
15
+ }
16
+
17
+ :link,
18
+ :visited {
19
+ color: #4183C4;
20
+ text-decoration: none;
21
+ }
22
+ :link:hover,
23
+ :visited:hover {
24
+ border-bottom: 1px dotted #4183C4;
25
+ }
26
+
27
+ pre, pre.description {
28
+ font: 12px Monaco,"Courier New","DejaVu Sans Mono","Bitstream Vera Sans Mono",monospace;
29
+ padding: 1em;
30
+ overflow: auto;
31
+ line-height: 1.4;
32
+ }
33
+
34
+ /* @group Generic Classes */
35
+
36
+ .initially-hidden {
37
+ display: none;
38
+ }
39
+
40
+ #search-field {
41
+ width: 98%;
42
+ }
43
+
44
+ .missing-docs {
45
+ font-size: 120%;
46
+ background: white url(images/wrench_orange.png) no-repeat 4px center;
47
+ color: #ccc;
48
+ line-height: 2em;
49
+ border: 1px solid #d00;
50
+ opacity: 1;
51
+ text-indent: 24px;
52
+ letter-spacing: 3px;
53
+ font-weight: bold;
54
+ -webkit-border-radius: 5px;
55
+ -moz-border-radius: 5px;
56
+ }
57
+
58
+ .target-section {
59
+ border: 2px solid #dcce90;
60
+ border-left-width: 8px;
61
+ background: #fff3c2;
62
+ }
63
+
64
+ /* @end */
65
+
66
+ /* @group Index Page, Standalone file pages */
67
+ .indexpage ul {
68
+ line-height: 160%;
69
+ list-style: none;
70
+ }
71
+ .indexpage ul :link,
72
+ .indexpage ul :visited {
73
+ font-size: 16px;
74
+ }
75
+
76
+ .indexpage li {
77
+ padding-left: 20px;
78
+ }
79
+
80
+ .indexpage ul > li {
81
+ background: url(images/bullet_black.png) no-repeat left 4px;
82
+ }
83
+ .indexpage li.method {
84
+ background: url(images/plugin.png) no-repeat left 4px;
85
+ }
86
+ .indexpage li.module {
87
+ background: url(images/package.png) no-repeat left 4px;
88
+ }
89
+ .indexpage li.class {
90
+ background: url(images/ruby.png) no-repeat left 4px;
91
+ }
92
+ .indexpage li.file {
93
+ background: url(images/page_white_text.png) no-repeat left 4px;
94
+ }
95
+ .indexpage li li {
96
+ background: url(images/tag_blue.png) no-repeat left 4px;
97
+ }
98
+ .indexpage li .toc-toggle {
99
+ width: 16px;
100
+ height: 16px;
101
+ background: url(images/add.png) no-repeat;
102
+ }
103
+
104
+ .indexpage li .toc-toggle.open {
105
+ background: url(images/delete.png) no-repeat;
106
+ }
107
+
108
+ /* @end */
109
+
110
+ /* @group Top-Level Structure */
111
+
112
+ .project-section, #file-metadata, #class-metadata {
113
+ display: block;
114
+ background: #f5f5f5;
115
+ margin-bottom: 1em;
116
+ padding: 0.5em;
117
+ }
118
+ .project-section h3, #file-metadata h3, #class-metadata h3 {
119
+ margin: 0;
120
+ }
121
+
122
+ #metadata {
123
+ float: left;
124
+ width: 280px;
125
+ }
126
+
127
+ #documentation {
128
+ margin: 2em 1em 2em 300px;
129
+ }
130
+
131
+ #validator-badges {
132
+ clear: both;
133
+ margin: 1em 1em 2em;
134
+ font-size: smaller;
135
+ }
136
+
137
+ /* @end */
138
+
139
+ /* @group Metadata Section */
140
+
141
+ #metadata ul,
142
+ #metadata dl,
143
+ #metadata p {
144
+ padding: 0px;
145
+ list-style: none;
146
+ }
147
+
148
+ dl.svninfo {
149
+ color: #666;
150
+ margin: 0;
151
+ }
152
+ dl.svninfo dt {
153
+ font-weight: bold;
154
+ }
155
+
156
+ ul.link-list li {
157
+ white-space: nowrap;
158
+ }
159
+ ul.link-list .type {
160
+ font-size: 8px;
161
+ text-transform: uppercase;
162
+ color: white;
163
+ background: #969696;
164
+ }
165
+
166
+ /* @end */
167
+
168
+ /* @group Documentation Section */
169
+
170
+ .note-list {
171
+ margin: 8px 0;
172
+ }
173
+
174
+ .label-list {
175
+ margin: 8px 1.5em;
176
+ border: 1px solid #ccc;
177
+ }
178
+ .description .label-list {
179
+ font-size: 14px;
180
+ }
181
+
182
+ .note-list dt {
183
+ font-weight: bold;
184
+ }
185
+ .note-list dd {
186
+ }
187
+
188
+ .label-list dt {
189
+ font-weight: bold;
190
+ background: #ddd;
191
+ }
192
+ .label-list dd {
193
+ }
194
+ .label-list dd + dt,
195
+ .note-list dd + dt {
196
+ margin-top: 0.7em;
197
+ }
198
+
199
+ #documentation .section {
200
+ font-size: 90%;
201
+ }
202
+
203
+ #documentation h2.section-header {
204
+ color: #333;
205
+ font-size: 175%;
206
+ }
207
+
208
+ .documentation-section-title {
209
+ position: relative;
210
+ }
211
+ .documentation-section-title .section-click-top {
212
+ position: absolute;
213
+ top: 6px;
214
+ right: 12px;
215
+ font-size: 10px;
216
+ visibility: hidden;
217
+ }
218
+
219
+ .documentation-section-title:hover .section-click-top {
220
+ visibility: visible;
221
+ }
222
+
223
+ #documentation h3.section-header {
224
+ color: #333;
225
+ font-size: 150%;
226
+ }
227
+
228
+ #constants-list > dl,
229
+ #attributes-list > dl {
230
+ margin: 1em 0 2em;
231
+ border: 0;
232
+ }
233
+ #constants-list > dl dt,
234
+ #attributes-list > dl dt {
235
+ font-weight: bold;
236
+ font-family: Monaco, "Andale Mono";
237
+ background: inherit;
238
+ }
239
+ #constants-list > dl dt a,
240
+ #attributes-list > dl dt a {
241
+ color: inherit;
242
+ }
243
+ #constants-list > dl dd,
244
+ #attributes-list > dl dd {
245
+ margin: 0 0 1em 0;
246
+ color: #666;
247
+ }
248
+
249
+ .documentation-section h2 {
250
+ position: relative;
251
+ }
252
+
253
+ .documentation-section h2 a {
254
+ position: absolute;
255
+ top: 8px;
256
+ right: 10px;
257
+ font-size: 12px;
258
+ color: #9b9877;
259
+ visibility: hidden;
260
+ }
261
+
262
+ .documentation-section h2:hover a {
263
+ visibility: visible;
264
+ }
265
+
266
+ /* @group Method Details */
267
+
268
+ #documentation .method-source-code {
269
+ display: none;
270
+ }
271
+
272
+ #documentation .method-detail {
273
+ margin: 0.2em 0.2em;
274
+ padding: 0.5em;
275
+ }
276
+ #documentation .method-detail:hover {
277
+ background-color: #f5f5f5;
278
+ }
279
+ #documentation .method-heading {
280
+ cursor: pointer;
281
+ position: relative;
282
+ font-size: 125%;
283
+ line-height: 125%;
284
+ font-weight: bold;
285
+ color: #333;
286
+ background: url(images/brick.png) no-repeat left bottom;
287
+ padding-left: 20px;
288
+ }
289
+ #documentation .method-heading :link,
290
+ #documentation .method-heading :visited {
291
+ color: inherit;
292
+ }
293
+ #documentation .method-click-advice {
294
+ position: absolute;
295
+ right: 5px;
296
+ font-size: 10px;
297
+ color: #aaa;
298
+ visibility: hidden;
299
+ background: url(images/zoom.png) no-repeat right 5px;
300
+ padding-right: 20px;
301
+ overflow: show;
302
+ }
303
+ #documentation .method-heading:hover .method-click-advice {
304
+ visibility: visible;
305
+ }
306
+
307
+ #documentation .method-alias .method-heading {
308
+ color: #666;
309
+ background: url(images/brick_link.png) no-repeat left bottom;
310
+ }
311
+
312
+ #documentation .method-description,
313
+ #documentation .aliases {
314
+ margin: 0 20px;
315
+ color: #666;
316
+ }
317
+
318
+ #documentation .method-description p,
319
+ #documentation .aliases p {
320
+ line-height: 1.2em;
321
+ }
322
+
323
+ #documentation .aliases {
324
+ font-style: italic;
325
+ cursor: default;
326
+ }
327
+ #documentation .method-description p {
328
+ margin-bottom: 0.5em;
329
+ }
330
+ #documentation .method-description ul {
331
+ margin-left: 1.5em;
332
+ }
333
+
334
+ #documentation .attribute-method-heading {
335
+ background: url(images/tag_green.png) no-repeat left bottom;
336
+ }
337
+ #documentation #attribute-method-details .method-detail:hover {
338
+ background-color: transparent;
339
+ cursor: default;
340
+ }
341
+ #documentation .attribute-access-type {
342
+ font-size: 60%;
343
+ text-transform: uppercase;
344
+ vertical-align: super;
345
+ }
346
+
347
+ .method-section .method-source-code {
348
+ background: white;
349
+ }
350
+
351
+ /* @group Source Code */
352
+
353
+ .ruby-constant .ruby-keyword .ruby-ivar .ruby-operator .ruby-identifier
354
+ .ruby-node .ruby-comment .ruby-regexp .ruby-value {
355
+ background: transparent;
356
+ }
357
+
358
+ /* Thanks GitHub!!! */
359
+ .ruby-constant { color: #458; font-weight: bold; }
360
+ .ruby-keyword { color: black; font-weight: bold; }
361
+ .ruby-ivar { color: teal; }
362
+ .ruby-operator { color: #000; }
363
+ .ruby-identifier { color: black; }
364
+ .ruby-node { color: red; }
365
+ .ruby-comment { color: #998; font-weight: bold; }
366
+ .ruby-regexp { color: #009926; }
367
+ .ruby-value { color: #099; }
368
+ .ruby-string { color: red; }
369
+
370
+ /* @group search results */
371
+
372
+ #search-section .section-header {
373
+ margin: 0;
374
+ padding: 0;
375
+ }
376
+
377
+ #search-results {
378
+ width: 100%;
379
+ list-style: none;
380
+ margin: 0;
381
+ padding: 0;
382
+ }
383
+
384
+ #search-results h1 {
385
+ font-size: 1em;
386
+ font-weight: normal;
387
+ text-shadow: none;
388
+ }
389
+
390
+ #search-results .current {
391
+ background: #eee;
392
+ }
393
+
394
+ #search-results li {
395
+ list-style: none;
396
+ line-height: 1em;
397
+ padding: 0.5em;
398
+ border-bottom: 1px solid black;
399
+ }
400
+
401
+ #search-results .search-namespace {
402
+ font-weight: bold;
403
+ }
404
+
405
+ #search-results li em {
406
+ background: yellow;
407
+ font-style: normal;
408
+ }
409
+
410
+ #search-results pre {
411
+ margin: 0.5em;
412
+ }
@@ -21,7 +21,11 @@ module Rack
21
21
  end
22
22
 
23
23
  def scheme
24
- @scheme ||= parts.first.downcase.to_sym
24
+ @scheme ||=
25
+ begin
26
+ s = parts.first.downcase
27
+ Rack::Auth.schemes.include?(s) ? s.to_sym : s
28
+ end
25
29
  end
26
30
 
27
31
  def params
@@ -41,7 +41,7 @@ module Rack
41
41
 
42
42
  class Request < Auth::AbstractRequest
43
43
  def basic?
44
- :basic == scheme
44
+ !parts.first.nil? && :basic == scheme
45
45
  end
46
46
 
47
47
  def credentials
@@ -38,7 +38,7 @@ module Rack
38
38
  end
39
39
 
40
40
  def stale?
41
- !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit
41
+ !self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit
42
42
  end
43
43
 
44
44
  def fresh?
@@ -8,7 +8,21 @@
8
8
 
9
9
  module URI
10
10
  TBLENCWWWCOMP_ = {} # :nodoc:
11
+ 256.times do |i|
12
+ TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
13
+ end
14
+ TBLENCWWWCOMP_[' '] = '+'
15
+ TBLENCWWWCOMP_.freeze
11
16
  TBLDECWWWCOMP_ = {} # :nodoc:
17
+ 256.times do |i|
18
+ h, l = i>>4, i&15
19
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
20
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
21
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
22
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
23
+ end
24
+ TBLDECWWWCOMP_['+'] = ' '
25
+ TBLDECWWWCOMP_.freeze
12
26
 
13
27
  # Encode given +s+ to URL-encoded form data.
14
28
  #
@@ -26,18 +40,6 @@ module URI
26
40
  '%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase
27
41
  end.tr(' ', '+')
28
42
  else
29
- if TBLENCWWWCOMP_.empty?
30
- tbl = {}
31
- 256.times do |i|
32
- tbl[i.chr] = '%%%02X' % i
33
- end
34
- tbl[' '] = '+'
35
- begin
36
- TBLENCWWWCOMP_.replace(tbl)
37
- TBLENCWWWCOMP_.freeze
38
- rescue
39
- end
40
- end
41
43
  str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]}
42
44
  end
43
45
  end
@@ -48,22 +50,6 @@ module URI
48
50
  #
49
51
  # See URI.encode_www_form_component, URI.decode_www_form
50
52
  def self.decode_www_form_component(str, enc=nil)
51
- if TBLDECWWWCOMP_.empty?
52
- tbl = {}
53
- 256.times do |i|
54
- h, l = i>>4, i&15
55
- tbl['%%%X%X' % [h, l]] = i.chr
56
- tbl['%%%x%X' % [h, l]] = i.chr
57
- tbl['%%%X%x' % [h, l]] = i.chr
58
- tbl['%%%x%x' % [h, l]] = i.chr
59
- end
60
- tbl['+'] = ' '
61
- begin
62
- TBLDECWWWCOMP_.replace(tbl)
63
- TBLDECWWWCOMP_.freeze
64
- rescue
65
- end
66
- end
67
53
  raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str
68
54
  str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]}
69
55
  end
@@ -17,6 +17,19 @@
17
17
  require 'uri/common'
18
18
 
19
19
  module URI
20
+ TBLDECWWWCOMP_ = {} unless const_defined?(:TBLDECWWWCOMP_) #:nodoc:
21
+ if TBLDECWWWCOMP_.empty?
22
+ 256.times do |i|
23
+ h, l = i>>4, i&15
24
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
25
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
26
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
27
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
28
+ end
29
+ TBLDECWWWCOMP_['+'] = ' '
30
+ TBLDECWWWCOMP_.freeze
31
+ end
32
+
20
33
  def self.decode_www_form(str, enc=Encoding::UTF_8)
21
34
  return [] if str.empty?
22
35
  unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str
@@ -30,26 +43,10 @@ module URI
30
43
  end
31
44
 
32
45
  def self.decode_www_form_component(str, enc=Encoding::UTF_8)
33
- if TBLDECWWWCOMP_.empty?
34
- tbl = {}
35
- 256.times do |i|
36
- h, l = i>>4, i&15
37
- tbl['%%%X%X' % [h, l]] = i.chr
38
- tbl['%%%x%X' % [h, l]] = i.chr
39
- tbl['%%%X%x' % [h, l]] = i.chr
40
- tbl['%%%x%x' % [h, l]] = i.chr
41
- end
42
- tbl['+'] = ' '
43
- begin
44
- TBLDECWWWCOMP_.replace(tbl)
45
- TBLDECWWWCOMP_.freeze
46
- rescue
47
- end
48
- end
49
46
  raise ArgumentError, "invalid %-encoding (#{str})" unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
50
47
  str.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
51
48
  end
52
49
 
53
- remove_const :WFKV_
50
+ remove_const :WFKV_ if const_defined?(:WFKV_)
54
51
  WFKV_ = '(?:[^%#=;&]*(?:%\h\h[^%#=;&]*)*)' # :nodoc:
55
52
  end
@@ -0,0 +1,29 @@
1
+ # :stopdoc:
2
+
3
+ require 'uri/common'
4
+
5
+ # Issue:
6
+ # http://bugs.ruby-lang.org/issues/5925
7
+ #
8
+ # Relevant commit:
9
+ # https://github.com/ruby/ruby/commit/edb7cdf1eabaff78dfa5ffedfbc2e91b29fa9ca1
10
+
11
+ module URI
12
+ 256.times do |i|
13
+ TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
14
+ end
15
+ TBLENCWWWCOMP_[' '] = '+'
16
+ TBLENCWWWCOMP_.freeze
17
+
18
+ 256.times do |i|
19
+ h, l = i>>4, i&15
20
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
21
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
22
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
23
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
24
+ end
25
+ TBLDECWWWCOMP_['+'] = ' '
26
+ TBLDECWWWCOMP_.freeze
27
+ end
28
+
29
+ # :startdoc:
@@ -5,21 +5,34 @@ module Rack
5
5
  end
6
6
 
7
7
  def respond_to?(*args)
8
+ return false if args.first.to_s =~ /^to_ary$/
8
9
  super or @body.respond_to?(*args)
9
10
  end
10
11
 
11
12
  def close
12
13
  return if @closed
13
14
  @closed = true
14
- @body.close if @body.respond_to? :close
15
- @block.call
15
+ begin
16
+ @body.close if @body.respond_to? :close
17
+ ensure
18
+ @block.call
19
+ end
16
20
  end
17
21
 
18
22
  def closed?
19
23
  @closed
20
24
  end
21
25
 
26
+ # N.B. This method is a special case to address the bug described by #434.
27
+ # We are applying this special case for #each only. Future bugs of this
28
+ # class will be handled by requesting users to patch their ruby
29
+ # implementation, to save adding too many methods in this class.
30
+ def each(*args, &block)
31
+ @body.each(*args, &block)
32
+ end
33
+
22
34
  def method_missing(*args, &block)
35
+ super if args.first.to_s =~ /^to_ary$/
23
36
  @body.__send__(*args, &block)
24
37
  end
25
38
  end
data/lib/rack/builder.rb CHANGED
@@ -38,7 +38,7 @@ module Rack
38
38
  end
39
39
  cfgfile.sub!(/^__END__\n.*\Z/m, '')
40
40
  app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",
41
- TOPLEVEL_BINDING, config
41
+ TOPLEVEL_BINDING, config, 0
42
42
  else
43
43
  require config
44
44
  app = Object.const_get(::File.basename(config, '.rb').capitalize)
data/lib/rack/cascade.rb CHANGED
@@ -8,7 +8,7 @@ module Rack
8
8
 
9
9
  attr_reader :apps
10
10
 
11
- def initialize(apps, catch=404)
11
+ def initialize(apps, catch=[404, 405])
12
12
  @apps = []; @has_app = {}
13
13
  apps.each { |app| add app }
14
14
 
@@ -19,8 +19,19 @@ module Rack
19
19
  def call(env)
20
20
  result = NotFound
21
21
 
22
+ last_body = nil
23
+
22
24
  @apps.each do |app|
25
+ # The SPEC says that the body must be closed after it has been iterated
26
+ # by the server, or if it is replaced by a middleware action. Cascade
27
+ # replaces the body each time a cascade happens. It is assumed that nil
28
+ # does not respond to close, otherwise the previous application body
29
+ # will be closed. The final application body will not be closed, as it
30
+ # will be passed to the server as a result.
31
+ last_body.close if last_body.respond_to? :close
32
+
23
33
  result = app.call(env)
34
+ last_body = result[2]
24
35
  break unless @catch.include?(result[0].to_i)
25
36
  end
26
37
 
@@ -1,13 +1,26 @@
1
1
  require 'rack/body_proxy'
2
2
 
3
3
  module Rack
4
- # Rack::CommonLogger forwards every request to an +app+ given, and
5
- # logs a line in the Apache common log format to the +logger+, or
6
- # rack.errors by default.
4
+ # Rack::CommonLogger forwards every request to the given +app+, and
5
+ # logs a line in the
6
+ # {Apache common log format}[http://httpd.apache.org/docs/1.3/logs.html#common]
7
+ # to the +logger+.
8
+ #
9
+ # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
10
+ # an instance of Rack::NullLogger.
11
+ #
12
+ # +logger+ can be any class, including the standard library Logger, and is
13
+ # expected to have a +write+ method, which accepts the CommonLogger::FORMAT.
14
+ # According to the SPEC, the error stream must also respond to +puts+
15
+ # (which takes a single argument that responds to +to_s+), and +flush+
16
+ # (which is called without arguments in order to make the error appear for
17
+ # sure)
7
18
  class CommonLogger
8
19
  # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
9
- # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
10
- # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
20
+ #
21
+ # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
22
+ #
23
+ # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
11
24
  FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
12
25
 
13
26
  def initialize(app, logger=nil)