rack 1.3.10 → 1.4.0

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 (83) hide show
  1. data/COPYING +1 -1
  2. data/KNOWN-ISSUES +0 -9
  3. data/README.rdoc +4 -118
  4. data/Rakefile +15 -0
  5. data/SPEC +3 -5
  6. data/lib/rack.rb +0 -12
  7. data/lib/rack/auth/abstract/request.rb +1 -5
  8. data/lib/rack/auth/basic.rb +1 -1
  9. data/lib/rack/auth/digest/nonce.rb +1 -1
  10. data/lib/rack/backports/uri/common_18.rb +28 -14
  11. data/lib/rack/backports/uri/common_192.rb +17 -14
  12. data/lib/rack/body_proxy.rb +0 -10
  13. data/lib/rack/builder.rb +26 -18
  14. data/lib/rack/cascade.rb +1 -12
  15. data/lib/rack/chunked.rb +2 -0
  16. data/lib/rack/content_type.rb +7 -1
  17. data/lib/rack/deflater.rb +1 -5
  18. data/lib/rack/directory.rb +5 -1
  19. data/lib/rack/file.rb +26 -9
  20. data/lib/rack/handler.rb +2 -2
  21. data/lib/rack/head.rb +0 -1
  22. data/lib/rack/lint.rb +3 -5
  23. data/lib/rack/methodoverride.rb +10 -4
  24. data/lib/rack/mime.rb +606 -171
  25. data/lib/rack/mock.rb +2 -1
  26. data/lib/rack/multipart.rb +2 -2
  27. data/lib/rack/multipart/parser.rb +3 -10
  28. data/lib/rack/reloader.rb +1 -1
  29. data/lib/rack/request.rb +45 -13
  30. data/lib/rack/response.rb +15 -14
  31. data/lib/rack/sendfile.rb +8 -6
  32. data/lib/rack/server.rb +4 -30
  33. data/lib/rack/session/abstract/id.rb +25 -6
  34. data/lib/rack/session/cookie.rb +12 -16
  35. data/lib/rack/static.rb +21 -8
  36. data/lib/rack/urlmap.rb +28 -13
  37. data/lib/rack/utils.rb +22 -28
  38. data/rack.gemspec +5 -5
  39. data/test/builder/end.ru +2 -0
  40. data/test/cgi/lighttpd.conf +1 -0
  41. data/test/cgi/sample_rackup.ru +1 -1
  42. data/test/cgi/test+directory/test+file +1 -0
  43. data/test/cgi/test.ru +1 -1
  44. data/test/gemloader.rb +6 -2
  45. data/test/spec_auth_basic.rb +4 -9
  46. data/test/spec_auth_digest.rb +3 -16
  47. data/test/spec_body_proxy.rb +0 -4
  48. data/test/spec_builder.rb +63 -20
  49. data/test/spec_cascade.rb +10 -13
  50. data/test/spec_cgi.rb +1 -1
  51. data/test/spec_chunked.rb +39 -12
  52. data/test/spec_commonlogger.rb +4 -3
  53. data/test/spec_conditionalget.rb +16 -12
  54. data/test/spec_content_length.rb +1 -1
  55. data/test/spec_content_type.rb +6 -0
  56. data/test/spec_deflater.rb +2 -2
  57. data/test/spec_directory.rb +12 -0
  58. data/test/spec_fastcgi.rb +1 -1
  59. data/test/spec_file.rb +58 -8
  60. data/test/spec_head.rb +6 -18
  61. data/test/spec_lint.rb +2 -2
  62. data/test/spec_methodoverride.rb +15 -0
  63. data/test/spec_mock.rb +6 -2
  64. data/test/spec_mongrel.rb +8 -8
  65. data/test/spec_multipart.rb +10 -63
  66. data/test/spec_request.rb +94 -21
  67. data/test/spec_response.rb +22 -24
  68. data/test/spec_sendfile.rb +3 -0
  69. data/test/spec_server.rb +2 -49
  70. data/test/spec_session_cookie.rb +58 -22
  71. data/test/spec_session_memcache.rb +31 -1
  72. data/test/spec_session_pool.rb +10 -4
  73. data/test/spec_static.rb +8 -0
  74. data/test/spec_thin.rb +2 -2
  75. data/test/spec_utils.rb +38 -35
  76. data/test/spec_webrick.rb +5 -3
  77. data/test/static/index.html +1 -0
  78. metadata +13 -18
  79. data/contrib/rack.png +0 -0
  80. data/contrib/rack.svg +0 -150
  81. data/lib/rack/backports/uri/common_193.rb +0 -29
  82. data/test/builder/line.ru +0 -1
  83. data/test/spec_auth.rb +0 -57
@@ -40,6 +40,7 @@ describe Rack::Sendfile do
40
40
  request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
41
41
  response.should.be.ok
42
42
  response.body.should.be.empty
43
+ response.headers['Content-Length'].should == '0'
43
44
  response.headers['X-Sendfile'].should.equal '/tmp/hello.txt'
44
45
  end
45
46
  end
@@ -48,6 +49,7 @@ describe Rack::Sendfile do
48
49
  request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response|
49
50
  response.should.be.ok
50
51
  response.body.should.be.empty
52
+ response.headers['Content-Length'].should == '0'
51
53
  response.headers['X-Lighttpd-Send-File'].should.equal '/tmp/hello.txt'
52
54
  end
53
55
  end
@@ -60,6 +62,7 @@ describe Rack::Sendfile do
60
62
  request headers do |response|
61
63
  response.should.be.ok
62
64
  response.body.should.be.empty
65
+ response.headers['Content-Length'].should == '0'
63
66
  response.headers['X-Accel-Redirect'].should.equal '/foo/bar/hello.txt'
64
67
  end
65
68
  end
@@ -10,13 +10,6 @@ describe Rack::Server do
10
10
  lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] }
11
11
  end
12
12
 
13
- def with_stderr
14
- old, $stderr = $stderr, StringIO.new
15
- yield $stderr
16
- ensure
17
- $stderr = old
18
- end
19
-
20
13
  it "overrides :config if :app is passed in" do
21
14
  server = Rack::Server.new(:app => "FOO")
22
15
  server.app.should == "FOO"
@@ -68,8 +61,8 @@ describe Rack::Server do
68
61
  :daemonize => false,
69
62
  :server => 'webrick'
70
63
  )
71
- t = Thread.new { server.start }
72
- until t.status == 'sleep'; t.join(0.01) end
64
+ t = Thread.new { server.start { |s| Thread.current[:server] = s } }
65
+ t.join(0.01) until t[:server] && t[:server].status != :Stop
73
66
  body = open("http://127.0.0.1:#{server.options[:Port]}/") { |f| f.read }
74
67
  body.should.eql('success')
75
68
 
@@ -78,44 +71,4 @@ describe Rack::Server do
78
71
  open(pidfile) { |f| f.read.should.eql $$.to_s }
79
72
  end
80
73
 
81
- should "check pid file presence and running process" do
82
- pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path
83
- server = Rack::Server.new(:pid => pidfile)
84
- server.send(:pidfile_process_status).should.eql :running
85
- end
86
-
87
- should "check pid file presence and dead process" do
88
- dead_pid = `echo $$`.to_i
89
- pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path
90
- server = Rack::Server.new(:pid => pidfile)
91
- server.send(:pidfile_process_status).should.eql :dead
92
- end
93
-
94
- should "check pid file presence and exited process" do
95
- pidfile = Tempfile.open('pidfile') { |f| break f }.path
96
- ::File.delete(pidfile)
97
- server = Rack::Server.new(:pid => pidfile)
98
- server.send(:pidfile_process_status).should.eql :exited
99
- end
100
-
101
- should "check pid file presence and not owned process" do
102
- pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
103
- server = Rack::Server.new(:pid => pidfile)
104
- server.send(:pidfile_process_status).should.eql :not_owned
105
- end
106
-
107
- should "inform the user about existing pidfiles with running processes" do
108
- pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
109
- server = Rack::Server.new(:pid => pidfile)
110
- with_stderr do |err|
111
- should.raise(SystemExit) do
112
- server.start
113
- end
114
- err.rewind
115
- output = err.read
116
- output.should.match(/already running/)
117
- output.should.include? pidfile
118
- end
119
- end
120
-
121
74
  end
@@ -11,7 +11,7 @@ describe Rack::Session::Cookie do
11
11
  end
12
12
 
13
13
  session_id = lambda do |env|
14
- Rack::Response.new(env["rack.session"].inspect).to_a
14
+ Rack::Response.new(env["rack.session"].to_hash.inspect).to_a
15
15
  end
16
16
 
17
17
  session_option = lambda do |opt|
@@ -24,17 +24,6 @@ describe Rack::Session::Cookie do
24
24
  Rack::Response.new("Nothing").to_a
25
25
  end
26
26
 
27
- before do
28
- @warnings = warnings = []
29
- Rack::Session::Cookie.class_eval do
30
- define_method(:warn) { |m| warnings << m }
31
- end
32
- end
33
-
34
- after do
35
- Rack::Session::Cookie.class_eval { remove_method :warn }
36
- end
37
-
38
27
  describe 'Base64' do
39
28
  it 'uses base64 to encode' do
40
29
  coder = Rack::Session::Cookie::Base64.new
@@ -68,14 +57,6 @@ describe Rack::Session::Cookie do
68
57
  end
69
58
  end
70
59
 
71
- it "warns if no secret is given" do
72
- cookie = Rack::Session::Cookie.new(incrementor)
73
- @warnings.first.should =~ /no secret/i
74
- @warnings.clear
75
- cookie = Rack::Session::Cookie.new(incrementor, :secret => 'abc')
76
- @warnings.should.be.empty?
77
- end
78
-
79
60
  it 'uses a coder' do
80
61
  identity = Class.new {
81
62
  attr_reader :calls
@@ -118,18 +99,23 @@ describe Rack::Session::Cookie do
118
99
  end
119
100
 
120
101
  only_session_id = lambda do |env|
121
- Rack::Response.new(env["rack.session"]["session_id"]).to_a
102
+ Rack::Response.new(env["rack.session"]["session_id"].to_s).to_a
122
103
  end
123
104
 
124
105
  it "renew session id" do
125
106
  res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
126
107
  res = Rack::MockRequest.new(Rack::Session::Cookie.new(only_session_id)).
127
108
  get("/", "HTTP_COOKIE" => res["Set-Cookie"])
109
+
110
+ res.body.should.not.equal ""
128
111
  old_session_id = res.body
112
+
129
113
  res = Rack::MockRequest.new(Rack::Session::Cookie.new(renewer)).
130
114
  get("/", "HTTP_COOKIE" => res["Set-Cookie"])
131
115
  res = Rack::MockRequest.new(Rack::Session::Cookie.new(only_session_id)).
132
116
  get("/", "HTTP_COOKIE" => res["Set-Cookie"])
117
+
118
+ res.body.should.not.equal ""
133
119
  res.body.should.not.equal old_session_id
134
120
  end
135
121
 
@@ -163,6 +149,18 @@ describe Rack::Session::Cookie do
163
149
  res.body.should.equal '{"counter"=>3}'
164
150
  end
165
151
 
152
+ it "loads from a cookie wih accept-only integrity hash for graceful key rotation" do
153
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
154
+ cookie = res["Set-Cookie"]
155
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test2', :old_secret => 'test')).
156
+ get("/", "HTTP_COOKIE" => cookie)
157
+ res.body.should.equal '{"counter"=>2}'
158
+ cookie = res["Set-Cookie"]
159
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test3', :old_secret => 'test2')).
160
+ get("/", "HTTP_COOKIE" => cookie)
161
+ res.body.should.equal '{"counter"=>3}'
162
+ end
163
+
166
164
  it "ignores tampered with session cookies" do
167
165
  app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
168
166
  response1 = Rack::MockRequest.new(app).get("/")
@@ -177,6 +175,38 @@ describe Rack::Session::Cookie do
177
175
  response2.body.should.equal '{"counter"=>1}'
178
176
  end
179
177
 
178
+ describe "1.9 bugs relating to inspecting yet-to-be-loaded from cookie data: Rack::Session::Abstract::SessionHash" do
179
+
180
+ it "can handle Rack::Lint middleware" do
181
+ app = Rack::Session::Cookie.new(incrementor)
182
+ res = Rack::MockRequest.new(app).get("/")
183
+
184
+ app = Rack::Session::Cookie.new(Rack::Lint.new(session_id))
185
+ res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
186
+ res.body.should.not.be.nil
187
+ end
188
+
189
+ it "can handle a middleware that inspects the env" do
190
+ class TestEnvInspector
191
+ def initialize(app)
192
+ @app = app
193
+ end
194
+ def call(env)
195
+ env.inspect
196
+ @app.call(env)
197
+ end
198
+ end
199
+
200
+ app = Rack::Session::Cookie.new(incrementor)
201
+ res = Rack::MockRequest.new(app).get("/")
202
+
203
+ app = Rack::Session::Cookie.new(TestEnvInspector.new(session_id))
204
+ res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
205
+ res.body.should.not.be.nil
206
+ end
207
+
208
+ end
209
+
180
210
  it "returns the session id in the session hash" do
181
211
  app = Rack::Session::Cookie.new(incrementor)
182
212
  res = Rack::MockRequest.new(app).get("/")
@@ -211,10 +241,16 @@ describe Rack::Session::Cookie do
211
241
 
212
242
  it "returns even if not read/written if :expire_after is set" do
213
243
  app = Rack::Session::Cookie.new(nothing, :expire_after => 3600)
214
- res = Rack::MockRequest.new(app).get("/")
244
+ res = Rack::MockRequest.new(app).get("/", 'rack.session' => {'not' => 'empty'})
215
245
  res["Set-Cookie"].should.not.be.nil
216
246
  end
217
247
 
248
+ it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do
249
+ app = Rack::Session::Cookie.new(nothing, :expire_after => 3600)
250
+ res = Rack::MockRequest.new(app).get("/")
251
+ res["Set-Cookie"].should.be.nil
252
+ end
253
+
218
254
  it "exposes :secret in env['rack.session.option']" do
219
255
  app = Rack::Session::Cookie.new(session_option[:secret], :secret => "foo")
220
256
  res = Rack::MockRequest.new(app).get("/")
@@ -23,6 +23,10 @@ begin
23
23
  env['rack.session.options'][:defer] = true
24
24
  incrementor.call(env)
25
25
  end
26
+ skip_session = proc do |env|
27
+ env['rack.session.options'][:skip] = true
28
+ incrementor.call(env)
29
+ end
26
30
 
27
31
  # test memcache connection
28
32
  Rack::Session::Memcache.new(incrementor)
@@ -168,14 +172,40 @@ begin
168
172
  res4.body.should.equal '{"counter"=>1}'
169
173
  end
170
174
 
171
- it "omits cookie with :defer option" do
175
+ it "omits cookie with :defer option but still updates the state" do
172
176
  pool = Rack::Session::Memcache.new(incrementor)
177
+ count = Rack::Utils::Context.new(pool, incrementor)
173
178
  defer = Rack::Utils::Context.new(pool, defer_session)
174
179
  dreq = Rack::MockRequest.new(defer)
180
+ creq = Rack::MockRequest.new(count)
175
181
 
176
182
  res0 = dreq.get("/")
177
183
  res0["Set-Cookie"].should.equal nil
178
184
  res0.body.should.equal '{"counter"=>1}'
185
+
186
+ res0 = creq.get("/")
187
+ res1 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
188
+ res1.body.should.equal '{"counter"=>2}'
189
+ res2 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
190
+ res2.body.should.equal '{"counter"=>3}'
191
+ end
192
+
193
+ it "omits cookie and state update with :skip option" do
194
+ pool = Rack::Session::Memcache.new(incrementor)
195
+ count = Rack::Utils::Context.new(pool, incrementor)
196
+ skip = Rack::Utils::Context.new(pool, skip_session)
197
+ sreq = Rack::MockRequest.new(skip)
198
+ creq = Rack::MockRequest.new(count)
199
+
200
+ res0 = sreq.get("/")
201
+ res0["Set-Cookie"].should.equal nil
202
+ res0.body.should.equal '{"counter"=>1}'
203
+
204
+ res0 = creq.get("/")
205
+ res1 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
206
+ res1.body.should.equal '{"counter"=>2}'
207
+ res2 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
208
+ res2.body.should.equal '{"counter"=>2}'
179
209
  end
180
210
 
181
211
  it "updates deep hashes correctly" do
@@ -181,20 +181,26 @@ describe Rack::Session::Pool do
181
181
  end
182
182
 
183
183
  it "does not return a cookie if cookie was not read/written" do
184
- app = Rack::Session::Cookie.new(nothing, :secret => 'abc')
184
+ app = Rack::Session::Pool.new(nothing)
185
185
  res = Rack::MockRequest.new(app).get("/")
186
186
  res["Set-Cookie"].should.be.nil
187
187
  end
188
188
 
189
189
  it "does not return a cookie if cookie was not written (only read)" do
190
- app = Rack::Session::Cookie.new(session_id, :secret => 'abc')
190
+ app = Rack::Session::Pool.new(session_id)
191
191
  res = Rack::MockRequest.new(app).get("/")
192
192
  res["Set-Cookie"].should.be.nil
193
193
  end
194
194
 
195
195
  it "returns even if not read/written if :expire_after is set" do
196
- app = Rack::Session::Cookie.new(nothing, :expire_after => 3600, :secret => 'abc')
197
- res = Rack::MockRequest.new(app).get("/")
196
+ app = Rack::Session::Pool.new(nothing, :expire_after => 3600)
197
+ res = Rack::MockRequest.new(app).get("/", 'rack.session' => {'not' => 'empty'})
198
198
  res["Set-Cookie"].should.not.be.nil
199
199
  end
200
+
201
+ it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do
202
+ app = Rack::Session::Pool.new(nothing, :expire_after => 3600)
203
+ res = Rack::MockRequest.new(app).get("/")
204
+ res["Set-Cookie"].should.be.nil
205
+ end
200
206
  end
@@ -11,9 +11,11 @@ describe Rack::Static do
11
11
  root = File.expand_path(File.dirname(__FILE__))
12
12
 
13
13
  OPTIONS = {:urls => ["/cgi"], :root => root}
14
+ STATIC_OPTIONS = {:urls => [""], :root => root, :index => 'static/index.html'}
14
15
  HASH_OPTIONS = {:urls => {"/cgi/sekret" => 'cgi/test'}, :root => root}
15
16
 
16
17
  @request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, OPTIONS))
18
+ @static_request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, STATIC_OPTIONS))
17
19
  @hash_request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, HASH_OPTIONS))
18
20
 
19
21
  it "serves files" do
@@ -33,6 +35,12 @@ describe Rack::Static do
33
35
  res.body.should == "Hello World"
34
36
  end
35
37
 
38
+ it "calls index file when requesting root" do
39
+ res = @static_request.get("/")
40
+ res.should.be.ok
41
+ res.body.should =~ /index!/
42
+ end
43
+
36
44
  it "serves hidden files" do
37
45
  res = @hash_request.get("/cgi/sekret")
38
46
  res.should.be.ok
@@ -11,7 +11,7 @@ describe Rack::Handler::Thin do
11
11
  Thin::Logging.silent = true
12
12
 
13
13
  @thread = Thread.new do
14
- Rack::Handler::Thin.run(@app, :Host => @host='0.0.0.0', :Port => @port=9204) do |server|
14
+ Rack::Handler::Thin.run(@app, :Host => @host='127.0.0.1', :Port => @port=9204) do |server|
15
15
  @server = server
16
16
  end
17
17
  end
@@ -30,7 +30,7 @@ describe Rack::Handler::Thin do
30
30
  response["HTTP_VERSION"].should.equal "HTTP/1.1"
31
31
  response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
32
32
  response["SERVER_PORT"].should.equal "9204"
33
- response["SERVER_NAME"].should.equal "0.0.0.0"
33
+ response["SERVER_NAME"].should.equal "127.0.0.1"
34
34
  end
35
35
 
36
36
  should "have rack headers" do
@@ -1,7 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'rack/utils'
3
3
  require 'rack/mock'
4
- require 'timeout'
5
4
 
6
5
  describe Rack::Utils do
7
6
  def kcodeu
@@ -12,6 +11,16 @@ describe Rack::Utils do
12
11
  $KCODE = default_kcode if one8
13
12
  end
14
13
 
14
+ should "round trip binary data" do
15
+ r = [218, 0].pack 'CC'
16
+ if defined?(::Encoding)
17
+ z = Rack::Utils.unescape(Rack::Utils.escape(r), Encoding::BINARY)
18
+ else
19
+ z = Rack::Utils.unescape(Rack::Utils.escape(r))
20
+ end
21
+ r.should.equal z
22
+ end
23
+
15
24
  should "escape correctly" do
16
25
  Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
17
26
  Rack::Utils.escape("a space").should.equal "a+space"
@@ -43,7 +52,7 @@ describe Rack::Utils do
43
52
  should "unescape multibyte characters correctly if $KCODE is set to 'U'" do
44
53
  kcodeu do
45
54
  Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
46
- "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
55
+ "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
47
56
  end
48
57
  end
49
58
  end
@@ -94,14 +103,6 @@ describe Rack::Utils do
94
103
  Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
95
104
  should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
96
105
  Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
97
- Rack::Utils.parse_query("=").should.equal "" => ""
98
- Rack::Utils.parse_query("=value").should.equal "" => "value"
99
- Rack::Utils.parse_query("key=").should.equal "key" => ""
100
- Rack::Utils.parse_query("&key&").should.equal "key" => nil
101
- Rack::Utils.parse_query(";key;", ";,").should.equal "key" => nil
102
- Rack::Utils.parse_query(",key,", ";,").should.equal "key" => nil
103
- Rack::Utils.parse_query(";foo=bar,;", ";,").should.equal "foo" => "bar"
104
- Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
105
106
  end
106
107
 
107
108
  should "parse nested query strings correctly" do
@@ -216,22 +217,22 @@ describe Rack::Utils do
216
217
  # unordered hash. Test that build_nested_query performs the inverse
217
218
  # function of parse_nested_query.
218
219
  [{"foo" => nil, "bar" => ""},
219
- {"foo" => "bar", "baz" => ""},
220
- {"foo" => ["1", "2"]},
221
- {"foo" => "bar", "baz" => ["1", "2", "3"]},
222
- {"foo" => ["bar"], "baz" => ["1", "2", "3"]},
223
- {"foo" => ["1", "2"]},
224
- {"foo" => "bar", "baz" => ["1", "2", "3"]},
225
- {"x" => {"y" => {"z" => "1"}}},
226
- {"x" => {"y" => {"z" => ["1"]}}},
227
- {"x" => {"y" => {"z" => ["1", "2"]}}},
228
- {"x" => {"y" => [{"z" => "1"}]}},
229
- {"x" => {"y" => [{"z" => ["1"]}]}},
230
- {"x" => {"y" => [{"z" => "1", "w" => "2"}]}},
231
- {"x" => {"y" => [{"v" => {"w" => "1"}}]}},
232
- {"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
233
- {"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
234
- {"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
220
+ {"foo" => "bar", "baz" => ""},
221
+ {"foo" => ["1", "2"]},
222
+ {"foo" => "bar", "baz" => ["1", "2", "3"]},
223
+ {"foo" => ["bar"], "baz" => ["1", "2", "3"]},
224
+ {"foo" => ["1", "2"]},
225
+ {"foo" => "bar", "baz" => ["1", "2", "3"]},
226
+ {"x" => {"y" => {"z" => "1"}}},
227
+ {"x" => {"y" => {"z" => ["1"]}}},
228
+ {"x" => {"y" => {"z" => ["1", "2"]}}},
229
+ {"x" => {"y" => [{"z" => "1"}]}},
230
+ {"x" => {"y" => [{"z" => ["1"]}]}},
231
+ {"x" => {"y" => [{"z" => "1", "w" => "2"}]}},
232
+ {"x" => {"y" => [{"v" => {"w" => "1"}}]}},
233
+ {"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
234
+ {"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
235
+ {"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
235
236
  ].each { |params|
236
237
  qs = Rack::Utils.build_nested_query(params)
237
238
  Rack::Utils.parse_nested_query(qs).should.equal params
@@ -242,6 +243,17 @@ describe Rack::Utils do
242
243
  message.should.equal "value must be a Hash"
243
244
  end
244
245
 
246
+ should "parse query strings that have a non-existent value" do
247
+ key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
248
+ Rack::Utils.parse_query(key).should.equal Rack::Utils.unescape(key) => nil
249
+ end
250
+
251
+ should "build query strings without = with non-existent values" do
252
+ key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
253
+ key = Rack::Utils.unescape(key)
254
+ Rack::Utils.build_query(key => nil).should.equal Rack::Utils.escape(key)
255
+ end
256
+
245
257
  should "escape html entities [&><'\"/]" do
246
258
  Rack::Utils.escape_html("foo").should.equal "foo"
247
259
  Rack::Utils.escape_html("f&o").should.equal "f&amp;o"
@@ -301,11 +313,6 @@ describe Rack::Utils do
301
313
  Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
302
314
  end
303
315
 
304
- should "should perform constant time string comparison" do
305
- Rack::Utils.secure_compare('a', 'a').should.equal true
306
- Rack::Utils.secure_compare('a', 'b').should.equal false
307
- end
308
-
309
316
  should "return status code for integer" do
310
317
  Rack::Utils.status_code(200).should.equal 200
311
318
  end
@@ -340,10 +347,6 @@ describe Rack::Utils, "byte_range" do
340
347
  Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).should.equal [(499..499)]
341
348
  end
342
349
 
343
- should "parse several byte ranges" do
344
- Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-600,601-999"},1000).should.equal [(500..600),(601..999)]
345
- end
346
-
347
350
  should "truncate byte ranges" do
348
351
  Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).should.equal [(123..499)]
349
352
  Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).should.equal []