rack 1.5.5 → 1.6.0.beta

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/KNOWN-ISSUES +14 -0
  3. data/README.rdoc +10 -6
  4. data/Rakefile +3 -4
  5. data/SPEC +59 -23
  6. data/lib/rack.rb +2 -1
  7. data/lib/rack/auth/abstract/request.rb +1 -1
  8. data/lib/rack/auth/basic.rb +1 -1
  9. data/lib/rack/auth/digest/md5.rb +1 -1
  10. data/lib/rack/backports/uri/common_18.rb +1 -1
  11. data/lib/rack/builder.rb +19 -4
  12. data/lib/rack/cascade.rb +2 -2
  13. data/lib/rack/chunked.rb +12 -1
  14. data/lib/rack/commonlogger.rb +13 -5
  15. data/lib/rack/conditionalget.rb +14 -2
  16. data/lib/rack/content_length.rb +5 -1
  17. data/lib/rack/deflater.rb +52 -13
  18. data/lib/rack/directory.rb +8 -2
  19. data/lib/rack/etag.rb +14 -6
  20. data/lib/rack/file.rb +10 -14
  21. data/lib/rack/handler.rb +2 -0
  22. data/lib/rack/handler/fastcgi.rb +4 -1
  23. data/lib/rack/handler/mongrel.rb +8 -2
  24. data/lib/rack/handler/scgi.rb +4 -1
  25. data/lib/rack/handler/thin.rb +8 -2
  26. data/lib/rack/handler/webrick.rb +46 -6
  27. data/lib/rack/head.rb +7 -2
  28. data/lib/rack/lint.rb +73 -25
  29. data/lib/rack/lobster.rb +8 -3
  30. data/lib/rack/methodoverride.rb +14 -3
  31. data/lib/rack/mime.rb +1 -15
  32. data/lib/rack/mock.rb +15 -7
  33. data/lib/rack/multipart.rb +2 -2
  34. data/lib/rack/multipart/parser.rb +107 -53
  35. data/lib/rack/multipart/uploaded_file.rb +2 -2
  36. data/lib/rack/nulllogger.rb +21 -2
  37. data/lib/rack/request.rb +38 -24
  38. data/lib/rack/response.rb +5 -0
  39. data/lib/rack/sendfile.rb +10 -5
  40. data/lib/rack/server.rb +45 -17
  41. data/lib/rack/session/abstract/id.rb +7 -6
  42. data/lib/rack/session/cookie.rb +17 -7
  43. data/lib/rack/session/memcache.rb +4 -4
  44. data/lib/rack/session/pool.rb +3 -6
  45. data/lib/rack/showexceptions.rb +20 -11
  46. data/lib/rack/showstatus.rb +1 -1
  47. data/lib/rack/static.rb +27 -30
  48. data/lib/rack/tempfile_reaper.rb +22 -0
  49. data/lib/rack/urlmap.rb +17 -3
  50. data/lib/rack/utils.rb +78 -47
  51. data/lib/rack/utils/okjson.rb +90 -91
  52. data/rack.gemspec +3 -3
  53. data/test/multipart/filename_and_no_name +6 -0
  54. data/test/multipart/invalid_character +6 -0
  55. data/test/spec_builder.rb +13 -4
  56. data/test/spec_chunked.rb +16 -0
  57. data/test/spec_commonlogger.rb +36 -0
  58. data/test/spec_content_length.rb +3 -1
  59. data/test/spec_deflater.rb +283 -148
  60. data/test/spec_etag.rb +11 -2
  61. data/test/spec_file.rb +11 -3
  62. data/test/spec_head.rb +2 -0
  63. data/test/spec_lobster.rb +1 -1
  64. data/test/spec_mock.rb +8 -0
  65. data/test/spec_multipart.rb +111 -49
  66. data/test/spec_request.rb +109 -25
  67. data/test/spec_response.rb +30 -0
  68. data/test/spec_server.rb +20 -5
  69. data/test/spec_session_cookie.rb +45 -2
  70. data/test/spec_session_memcache.rb +1 -1
  71. data/test/spec_showexceptions.rb +29 -36
  72. data/test/spec_showstatus.rb +19 -0
  73. data/test/spec_tempfile_reaper.rb +63 -0
  74. data/test/spec_urlmap.rb +23 -0
  75. data/test/spec_utils.rb +60 -10
  76. data/test/spec_webrick.rb +41 -0
  77. metadata +12 -9
  78. data/test/cgi/lighttpd.errors +0 -1
  79. data/test/multipart/three_files_three_fields +0 -31
@@ -21,14 +21,16 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  # See https://github.com/kr/okjson for updates.
24
+ # Imported from the above repo @ d4e8643ad92e14b37d11326855499c7e4108ed17
25
+ # Namespace modified for vendoring under Rack::Utils
24
26
 
25
27
  require 'stringio'
26
28
 
27
29
  # Some parts adapted from
28
- # https://golang.org/src/encoding/json/decode.go and
29
- # https://golang.org/src/unicode/utf8/utf8.go
30
+ # http://golang.org/src/pkg/json/decode.go and
31
+ # http://golang.org/src/pkg/utf8/utf8.go
30
32
  module Rack::Utils::OkJson
31
- Upstream = '43'
33
+ Upstream = 'LTD7LBKLZWFF7OZK'
32
34
  extend self
33
35
 
34
36
 
@@ -50,49 +52,12 @@ module Rack::Utils::OkJson
50
52
  end
51
53
 
52
54
 
53
- # Encodes x into a json text. It may contain only
54
- # Array, Hash, String, Numeric, true, false, nil.
55
- # (Note, this list excludes Symbol.)
56
- # X itself must be an Array or a Hash.
57
- # No other value can be encoded, and an error will
58
- # be raised if x contains any other value, such as
59
- # Nan, Infinity, Symbol, and Proc, or if a Hash key
60
- # is not a String.
61
- # Strings contained in x must be valid UTF-8.
62
- def encode(x)
63
- case x
64
- when Hash then objenc(x)
65
- when Array then arrenc(x)
66
- else
67
- raise Error, 'root value must be an Array or a Hash'
68
- end
69
- end
70
-
71
-
72
- def valenc(x)
73
- case x
74
- when Hash then objenc(x)
75
- when Array then arrenc(x)
76
- when String then strenc(x)
77
- when Numeric then numenc(x)
78
- when true then "true"
79
- when false then "false"
80
- when nil then "null"
81
- else
82
- raise Error, "cannot encode #{x.class}: #{x.inspect}"
83
- end
84
- end
85
-
86
-
87
- private
88
-
89
-
90
55
  # Parses a "json text" in the sense of RFC 4627.
91
56
  # Returns the parsed value and any trailing tokens.
92
57
  # Note: this is almost the same as valparse,
93
58
  # except that it does not accept atomic values.
94
59
  def textparse(ts)
95
- if ts.length <= 0
60
+ if ts.length < 0
96
61
  raise Error, 'empty'
97
62
  end
98
63
 
@@ -109,7 +74,7 @@ private
109
74
  # Parses a "value" in the sense of RFC 4627.
110
75
  # Returns the parsed value and any trailing tokens.
111
76
  def valparse(ts)
112
- if ts.length <= 0
77
+ if ts.length < 0
113
78
  raise Error, 'empty'
114
79
  end
115
80
 
@@ -238,19 +203,21 @@ private
238
203
  # it is the lexeme.
239
204
  def tok(s)
240
205
  case s[0]
241
- when ?{ then ['{', s[0,1], s[0,1]]
242
- when ?} then ['}', s[0,1], s[0,1]]
243
- when ?: then [':', s[0,1], s[0,1]]
244
- when ?, then [',', s[0,1], s[0,1]]
245
- when ?[ then ['[', s[0,1], s[0,1]]
246
- when ?] then [']', s[0,1], s[0,1]]
247
- when ?n then nulltok(s)
248
- when ?t then truetok(s)
249
- when ?f then falsetok(s)
250
- when ?" then strtok(s)
251
- when Spc, ?\t, ?\n, ?\r then [:space, s[0,1], s[0,1]]
252
- else
253
- numtok(s)
206
+ when ?{ then ['{', s[0,1], s[0,1]]
207
+ when ?} then ['}', s[0,1], s[0,1]]
208
+ when ?: then [':', s[0,1], s[0,1]]
209
+ when ?, then [',', s[0,1], s[0,1]]
210
+ when ?[ then ['[', s[0,1], s[0,1]]
211
+ when ?] then [']', s[0,1], s[0,1]]
212
+ when ?n then nulltok(s)
213
+ when ?t then truetok(s)
214
+ when ?f then falsetok(s)
215
+ when ?" then strtok(s)
216
+ when Spc then [:space, s[0,1], s[0,1]]
217
+ when ?\t then [:space, s[0,1], s[0,1]]
218
+ when ?\n then [:space, s[0,1], s[0,1]]
219
+ when ?\r then [:space, s[0,1], s[0,1]]
220
+ else numtok(s)
254
221
  end
255
222
  end
256
223
 
@@ -263,12 +230,12 @@ private
263
230
  def numtok(s)
264
231
  m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
265
232
  if m && m.begin(0) == 0
266
- if !m[2] && !m[3]
267
- [:val, m[0], Integer(m[0])]
233
+ if m[3] && !m[2]
234
+ [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
268
235
  elsif m[2]
269
236
  [:val, m[0], Float(m[0])]
270
237
  else
271
- [:val, m[0], Integer(m[1])*(10**m[3][1..-1].to_i(10))]
238
+ [:val, m[0], Integer(m[0])]
272
239
  end
273
240
  else
274
241
  []
@@ -300,14 +267,17 @@ private
300
267
  def unquote(q)
301
268
  q = q[1...-1]
302
269
  a = q.dup # allocate a big enough string
270
+ rubydoesenc = false
303
271
  # In ruby >= 1.9, a[w] is a codepoint, not a byte.
304
- if rubydoesenc?
272
+ if a.class.method_defined?(:force_encoding)
305
273
  a.force_encoding('UTF-8')
274
+ rubydoesenc = true
306
275
  end
307
276
  r, w = 0, 0
308
277
  while r < q.length
309
278
  c = q[r]
310
- if c == ?\\
279
+ case true
280
+ when c == ?\\
311
281
  r += 1
312
282
  if r >= q.length
313
283
  raise Error, "string literal ends with a \"\\\": \"#{q}\""
@@ -340,7 +310,7 @@ private
340
310
  end
341
311
  end
342
312
  end
343
- if rubydoesenc?
313
+ if rubydoesenc
344
314
  a[w] = '' << uchar
345
315
  w += 1
346
316
  else
@@ -349,7 +319,7 @@ private
349
319
  else
350
320
  raise Error, "invalid escape char #{q[r]} in \"#{q}\""
351
321
  end
352
- elsif c == ?" || c < Spc
322
+ when c == ?", c < Spc
353
323
  raise Error, "invalid character in string literal \"#{q}\""
354
324
  else
355
325
  # Copy anything else byte-for-byte.
@@ -370,14 +340,15 @@ private
370
340
  # bytes in string a at position i.
371
341
  # Returns the number of bytes written.
372
342
  def ucharenc(a, i, u)
373
- if u <= Uchar1max
343
+ case true
344
+ when u <= Uchar1max
374
345
  a[i] = (u & 0xff).chr
375
346
  1
376
- elsif u <= Uchar2max
347
+ when u <= Uchar2max
377
348
  a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
378
349
  a[i+1] = (Utagx | (u&Umaskx)).chr
379
350
  2
380
- elsif u <= Uchar3max
351
+ when u <= Uchar3max
381
352
  a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
382
353
  a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
383
354
  a[i+2] = (Utagx | (u&Umaskx)).chr
@@ -414,15 +385,50 @@ private
414
385
 
415
386
 
416
387
  def nibble(c)
417
- if ?0 <= c && c <= ?9 then c.ord - ?0.ord
418
- elsif ?a <= c && c <= ?z then c.ord - ?a.ord + 10
419
- elsif ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
388
+ case true
389
+ when ?0 <= c && c <= ?9 then c.ord - ?0.ord
390
+ when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
391
+ when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
420
392
  else
421
393
  raise Error, "invalid hex code #{c}"
422
394
  end
423
395
  end
424
396
 
425
397
 
398
+ # Encodes x into a json text. It may contain only
399
+ # Array, Hash, String, Numeric, true, false, nil.
400
+ # (Note, this list excludes Symbol.)
401
+ # X itself must be an Array or a Hash.
402
+ # No other value can be encoded, and an error will
403
+ # be raised if x contains any other value, such as
404
+ # Nan, Infinity, Symbol, and Proc, or if a Hash key
405
+ # is not a String.
406
+ # Strings contained in x must be valid UTF-8.
407
+ def encode(x)
408
+ case x
409
+ when Hash then objenc(x)
410
+ when Array then arrenc(x)
411
+ else
412
+ raise Error, 'root value must be an Array or a Hash'
413
+ end
414
+ end
415
+
416
+
417
+ def valenc(x)
418
+ case x
419
+ when Hash then objenc(x)
420
+ when Array then arrenc(x)
421
+ when String then strenc(x)
422
+ when Numeric then numenc(x)
423
+ when true then "true"
424
+ when false then "false"
425
+ when nil then "null"
426
+ else
427
+ raise Error, "cannot encode #{x.class}: #{x.inspect}"
428
+ end
429
+ end
430
+
431
+
426
432
  def objenc(x)
427
433
  '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
428
434
  end
@@ -447,6 +453,9 @@ private
447
453
  t.putc(?")
448
454
  r = 0
449
455
 
456
+ # In ruby >= 1.9, s[r] is a codepoint, not a byte.
457
+ rubydoesenc = s.class.method_defined?(:encoding)
458
+
450
459
  while r < s.length
451
460
  case s[r]
452
461
  when ?" then t.print('\\"')
@@ -458,20 +467,15 @@ private
458
467
  when ?\t then t.print('\\t')
459
468
  else
460
469
  c = s[r]
461
- # In ruby >= 1.9, s[r] is a codepoint, not a byte.
462
- if rubydoesenc?
470
+ case true
471
+ when rubydoesenc
463
472
  begin
464
- # c.ord will raise an error if c is invalid UTF-8
465
- if c.ord < Spc.ord
466
- c = "\\u%04x" % [c.ord]
467
- end
473
+ c.ord # will raise an error if c is invalid UTF-8
468
474
  t.write(c)
469
475
  rescue
470
476
  t.write(Ustrerr)
471
477
  end
472
- elsif c < Spc
473
- t.write("\\u%04x" % c)
474
- elsif Spc <= c && c <= ?~
478
+ when Spc <= c && c <= ?~
475
479
  t.putc(c)
476
480
  else
477
481
  n = ucharcopy(t, s, r) # ensure valid UTF-8 output
@@ -563,11 +567,6 @@ private
563
567
  end
564
568
 
565
569
 
566
- def rubydoesenc?
567
- ::String.method_defined?(:force_encoding)
568
- end
569
-
570
-
571
570
  class Utf8Error < ::StandardError
572
571
  end
573
572
 
@@ -576,15 +575,15 @@ private
576
575
  end
577
576
 
578
577
 
579
- Utagx = 0b1000_0000
580
- Utag2 = 0b1100_0000
581
- Utag3 = 0b1110_0000
582
- Utag4 = 0b1111_0000
583
- Utag5 = 0b1111_1000
584
- Umaskx = 0b0011_1111
585
- Umask2 = 0b0001_1111
586
- Umask3 = 0b0000_1111
587
- Umask4 = 0b0000_0111
578
+ Utagx = 0x80 # 1000 0000
579
+ Utag2 = 0xc0 # 1100 0000
580
+ Utag3 = 0xe0 # 1110 0000
581
+ Utag4 = 0xf0 # 1111 0000
582
+ Utag5 = 0xF8 # 1111 1000
583
+ Umaskx = 0x3f # 0011 1111
584
+ Umask2 = 0x1f # 0001 1111
585
+ Umask3 = 0x0f # 0000 1111
586
+ Umask4 = 0x07 # 0000 0111
588
587
  Uchar1max = (1<<7) - 1
589
588
  Uchar2max = (1<<11) - 1
590
589
  Uchar3max = (1<<16) - 1
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack"
3
- s.version = "1.5.5"
3
+ s.version = "1.6.0.beta"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.summary = "a modular Ruby webserver interface"
6
6
  s.license = "MIT"
@@ -12,7 +12,7 @@ the simplest way possible, it unifies and distills the API for web
12
12
  servers, web frameworks, and software in between (the so-called
13
13
  middleware) into a single method call.
14
14
 
15
- Also see http://rack.github.com/.
15
+ Also see http://rack.github.io/.
16
16
  EOF
17
17
 
18
18
  s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] +
@@ -25,7 +25,7 @@ EOF
25
25
 
26
26
  s.author = 'Christian Neukirchen'
27
27
  s.email = 'chneukirchen@gmail.com'
28
- s.homepage = 'http://rack.github.com/'
28
+ s.homepage = 'http://rack.github.io/'
29
29
  s.rubyforge_project = 'rack'
30
30
 
31
31
  s.add_development_dependency 'bacon'
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; filename="file1.txt"
3
+ Content-Type: text/plain
4
+
5
+ contents
6
+ --AaB03x--
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="files"; filename="invalid�.txt"
3
+ Content-Type: text/plain
4
+
5
+ contents
6
+ --AaB03x--
@@ -130,6 +130,17 @@ describe Rack::Builder do
130
130
  Rack::MockRequest.new(app).get("/foo").should.be.server_error
131
131
  end
132
132
 
133
+ it "yields the generated app to a block for warmup" do
134
+ warmed_up_app = nil
135
+
136
+ app = Rack::Builder.new do
137
+ warmup { |a| warmed_up_app = a }
138
+ run lambda { |env| [200, {}, []] }
139
+ end.to_app
140
+
141
+ warmed_up_app.should.equal app
142
+ end
143
+
133
144
  should "initialize apps once" do
134
145
  app = builder do
135
146
  class AppClass
@@ -180,8 +191,7 @@ describe Rack::Builder do
180
191
  end
181
192
 
182
193
  it "removes __END__ before evaluating app" do
183
- app, options = Rack::Builder.parse_file config_file('end.ru')
184
- options = nil # ignored, prevents warning
194
+ app, _ = Rack::Builder.parse_file config_file('end.ru')
185
195
  Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
186
196
  end
187
197
 
@@ -199,8 +209,7 @@ describe Rack::Builder do
199
209
  end
200
210
 
201
211
  it "sets __LINE__ correctly" do
202
- app, options = Rack::Builder.parse_file config_file('line.ru')
203
- options = nil # ignored, prevents warning
212
+ app, _ = Rack::Builder.parse_file config_file('line.ru')
204
213
  Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
205
214
  end
206
215
  end
@@ -64,6 +64,22 @@ describe Rack::Chunked do
64
64
  body.join.should.equal 'Hello World!'
65
65
  end
66
66
 
67
+ should 'not modify response when client is ancient, pre-HTTP/1.0' do
68
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
69
+ check = lambda do
70
+ status, headers, body = chunked(app).call(@env.dup)
71
+ status.should.equal 200
72
+ headers.should.not.include 'Transfer-Encoding'
73
+ body.join.should.equal 'Hello World!'
74
+ end
75
+
76
+ @env.delete('HTTP_VERSION') # unicorn will do this on pre-HTTP/1.0 requests
77
+ check.call
78
+
79
+ @env['HTTP_VERSION'] = 'HTTP/0.9' # not sure if this happens in practice
80
+ check.call
81
+ end
82
+
67
83
  should 'not modify response when Transfer-Encoding header already present' do
68
84
  app = lambda { |env|
69
85
  [200, {"Content-Type" => "text/plain", 'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']]
@@ -2,6 +2,8 @@ require 'rack/commonlogger'
2
2
  require 'rack/lint'
3
3
  require 'rack/mock'
4
4
 
5
+ require 'logger'
6
+
5
7
  describe Rack::CommonLogger do
6
8
  obj = 'foobar'
7
9
  length = obj.size
@@ -33,6 +35,14 @@ describe Rack::CommonLogger do
33
35
  log.string.should =~ /"GET \/ " 200 #{length} /
34
36
  end
35
37
 
38
+ should "work with standartd library logger" do
39
+ logdev = StringIO.new
40
+ log = Logger.new(logdev)
41
+ Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
42
+
43
+ logdev.string.should =~ /"GET \/ " 200 #{length} /
44
+ end
45
+
36
46
  should "log - content length if header is missing" do
37
47
  res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
38
48
 
@@ -47,6 +57,32 @@ describe Rack::CommonLogger do
47
57
  res.errors.should =~ /"GET \/ " 200 - /
48
58
  end
49
59
 
60
+ def with_mock_time(t = 0)
61
+ mc = class <<Time; self; end
62
+ mc.send :alias_method, :old_now, :now
63
+ mc.send :define_method, :now do
64
+ at(t)
65
+ end
66
+ yield
67
+ ensure
68
+ mc.send :alias_method, :now, :old_now
69
+ end
70
+
71
+ should "log in common log format" do
72
+ log = StringIO.new
73
+ with_mock_time do
74
+ Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
75
+ end
76
+
77
+ md = /- - - \[([^\]]+)\] "(\w+) \/ " (\d{3}) \d+ ([\d\.]+)/.match(log.string)
78
+ md.should.not.equal nil
79
+ time, method, status, duration = *md.captures
80
+ time.should.equal Time.at(0).strftime("%d/%b/%Y:%H:%M:%S %z")
81
+ method.should.equal "GET"
82
+ status.should.equal "200"
83
+ (0..1).should.include?(duration.to_f)
84
+ end
85
+
50
86
  def length
51
87
  123
52
88
  end