rack 1.0.1 → 1.1.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 (82) hide show
  1. data/COPYING +1 -1
  2. data/KNOWN-ISSUES +3 -0
  3. data/RDOX +0 -428
  4. data/README +61 -26
  5. data/SPEC +8 -1
  6. data/bin/rackup +2 -174
  7. data/lib/rack.rb +10 -8
  8. data/lib/rack/builder.rb +17 -0
  9. data/lib/rack/cascade.rb +17 -12
  10. data/lib/rack/chunked.rb +2 -2
  11. data/lib/rack/commonlogger.rb +31 -43
  12. data/lib/rack/config.rb +15 -0
  13. data/lib/rack/content_type.rb +1 -1
  14. data/lib/rack/directory.rb +6 -2
  15. data/lib/rack/etag.rb +23 -0
  16. data/lib/rack/file.rb +4 -2
  17. data/lib/rack/handler.rb +19 -0
  18. data/lib/rack/handler/cgi.rb +1 -1
  19. data/lib/rack/handler/fastcgi.rb +2 -3
  20. data/lib/rack/handler/lsws.rb +4 -1
  21. data/lib/rack/handler/mongrel.rb +8 -5
  22. data/lib/rack/handler/scgi.rb +4 -4
  23. data/lib/rack/handler/webrick.rb +2 -4
  24. data/lib/rack/lint.rb +44 -15
  25. data/lib/rack/logger.rb +20 -0
  26. data/lib/rack/mime.rb +3 -1
  27. data/lib/rack/mock.rb +30 -4
  28. data/lib/rack/nulllogger.rb +18 -0
  29. data/lib/rack/reloader.rb +4 -1
  30. data/lib/rack/request.rb +40 -15
  31. data/lib/rack/response.rb +5 -39
  32. data/lib/rack/runtime.rb +27 -0
  33. data/lib/rack/sendfile.rb +142 -0
  34. data/lib/rack/server.rb +212 -0
  35. data/lib/rack/session/abstract/id.rb +3 -5
  36. data/lib/rack/session/cookie.rb +3 -4
  37. data/lib/rack/session/memcache.rb +53 -43
  38. data/lib/rack/session/pool.rb +1 -1
  39. data/lib/rack/urlmap.rb +9 -8
  40. data/lib/rack/utils.rb +230 -11
  41. data/rack.gemspec +33 -49
  42. data/test/spec_rack_cascade.rb +3 -5
  43. data/test/spec_rack_cgi.rb +3 -3
  44. data/test/spec_rack_commonlogger.rb +39 -10
  45. data/test/spec_rack_config.rb +24 -0
  46. data/test/spec_rack_directory.rb +1 -1
  47. data/test/spec_rack_etag.rb +17 -0
  48. data/test/spec_rack_fastcgi.rb +2 -2
  49. data/test/spec_rack_file.rb +1 -1
  50. data/test/spec_rack_lint.rb +26 -19
  51. data/test/spec_rack_logger.rb +21 -0
  52. data/test/spec_rack_mock.rb +87 -1
  53. data/test/spec_rack_mongrel.rb +4 -4
  54. data/test/spec_rack_nulllogger.rb +13 -0
  55. data/test/spec_rack_request.rb +47 -6
  56. data/test/spec_rack_response.rb +3 -0
  57. data/test/spec_rack_runtime.rb +35 -0
  58. data/test/spec_rack_sendfile.rb +86 -0
  59. data/test/spec_rack_session_cookie.rb +1 -10
  60. data/test/spec_rack_session_memcache.rb +53 -20
  61. data/test/spec_rack_urlmap.rb +30 -0
  62. data/test/spec_rack_utils.rb +171 -6
  63. data/test/spec_rack_webrick.rb +4 -4
  64. data/test/spec_rackup.rb +154 -0
  65. metadata +37 -79
  66. data/Rakefile +0 -164
  67. data/lib/rack/auth/openid.rb +0 -480
  68. data/test/cgi/lighttpd.conf +0 -20
  69. data/test/cgi/test +0 -9
  70. data/test/cgi/test.fcgi +0 -8
  71. data/test/cgi/test.ru +0 -7
  72. data/test/multipart/binary +0 -0
  73. data/test/multipart/empty +0 -10
  74. data/test/multipart/ie +0 -6
  75. data/test/multipart/nested +0 -10
  76. data/test/multipart/none +0 -9
  77. data/test/multipart/semicolon +0 -6
  78. data/test/multipart/text +0 -10
  79. data/test/spec_rack_auth_openid.rb +0 -84
  80. data/test/testrequest.rb +0 -57
  81. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  82. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -6,14 +6,14 @@ require 'rack/urlmap'
6
6
  require 'rack/lint'
7
7
  require 'testrequest'
8
8
  require 'timeout'
9
-
9
+
10
10
  Thread.abort_on_exception = true
11
11
  $tcp_defer_accept_opts = nil
12
12
  $tcp_cork_opts = nil
13
13
 
14
14
  context "Rack::Handler::Mongrel" do
15
15
  include TestRequest::Helpers
16
-
16
+
17
17
  setup do
18
18
  server = Mongrel::HttpServer.new(@host='0.0.0.0', @port=9201)
19
19
  server.register('/test',
@@ -41,7 +41,7 @@ context "Rack::Handler::Mongrel" do
41
41
 
42
42
  specify "should have rack headers" do
43
43
  GET("/test")
44
- response["rack.version"].should.equal [1,0]
44
+ response["rack.version"].should.equal [1,1]
45
45
  response["rack.multithread"].should.be true
46
46
  response["rack.multiprocess"].should.be false
47
47
  response["rack.run_once"].should.be false
@@ -52,7 +52,7 @@ context "Rack::Handler::Mongrel" do
52
52
  response["REQUEST_METHOD"].should.equal "GET"
53
53
  response["SCRIPT_NAME"].should.equal "/test"
54
54
  response["REQUEST_PATH"].should.equal "/test"
55
- response["PATH_INFO"].should.be.nil
55
+ response["PATH_INFO"].should.be.equal ""
56
56
  response["QUERY_STRING"].should.equal ""
57
57
  response["test.postdata"].should.equal ""
58
58
 
@@ -0,0 +1,13 @@
1
+ require 'rack/nulllogger'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+
5
+ context "Rack::NullLogger" do
6
+ specify "acks as a nop logger" do
7
+ app = lambda { |env|
8
+ env['rack.logger'].warn "b00m"
9
+ [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
10
+ }
11
+ Rack::NullLogger.new(app).call({})
12
+ end
13
+ end
@@ -35,9 +35,18 @@ context "Rack::Request" do
35
35
  req.host.should.equal "www2.example.org"
36
36
 
37
37
  req = Rack::Request.new \
38
- Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org:9292")
38
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
39
39
  req.host.should.equal "example.org"
40
40
 
41
+ req = Rack::Request.new \
42
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
43
+ req.host.should.equal "example.org"
44
+
45
+ env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
46
+ env.delete("SERVER_NAME")
47
+ req = Rack::Request.new(env)
48
+ req.host.should.equal "192.168.1.1"
49
+
41
50
  env = Rack::MockRequest.env_for("/")
42
51
  env.delete("SERVER_NAME")
43
52
  req = Rack::Request.new(env)
@@ -52,9 +61,16 @@ context "Rack::Request" do
52
61
  req.params.should.equal "foo" => "bar", "quux" => "bla"
53
62
  end
54
63
 
55
- specify "can parse POST data" do
64
+ specify "raises if rack.input is missing" do
65
+ req = Rack::Request.new({})
66
+ lambda { req.POST }.should.raise(RuntimeError)
67
+ end
68
+
69
+ specify "can parse POST data when method is POST and no Content-Type given" do
56
70
  req = Rack::Request.new \
57
- Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla")
71
+ Rack::MockRequest.env_for("/?foo=quux",
72
+ "REQUEST_METHOD" => 'POST',
73
+ :input => "foo=bar&quux=bla")
58
74
  req.content_type.should.be.nil
59
75
  req.media_type.should.be.nil
60
76
  req.query_string.should.equal "foo=quux"
@@ -63,7 +79,7 @@ context "Rack::Request" do
63
79
  req.params.should.equal "foo" => "bar", "quux" => "bla"
64
80
  end
65
81
 
66
- specify "can parse POST data with explicit content type" do
82
+ specify "can parse POST data with explicit content type regardless of method" do
67
83
  req = Rack::Request.new \
68
84
  Rack::MockRequest.env_for("/",
69
85
  "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
@@ -78,6 +94,7 @@ context "Rack::Request" do
78
94
  specify "does not parse POST data when media type is not form-data" do
79
95
  req = Rack::Request.new \
80
96
  Rack::MockRequest.env_for("/?foo=quux",
97
+ "REQUEST_METHOD" => 'POST',
81
98
  "CONTENT_TYPE" => 'text/plain;charset=utf-8',
82
99
  :input => "foo=bar&quux=bla")
83
100
  req.content_type.should.equal 'text/plain;charset=utf-8'
@@ -88,6 +105,16 @@ context "Rack::Request" do
88
105
  req.body.read.should.equal "foo=bar&quux=bla"
89
106
  end
90
107
 
108
+ specify "can parse POST data on PUT when media type is form-data" do
109
+ req = Rack::Request.new \
110
+ Rack::MockRequest.env_for("/?foo=quux",
111
+ "REQUEST_METHOD" => 'PUT',
112
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded',
113
+ :input => "foo=bar&quux=bla")
114
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
115
+ req.body.read.should.equal "foo=bar&quux=bla"
116
+ end
117
+
91
118
  specify "rewinds input after parsing POST data" do
92
119
  input = StringIO.new("foo=bar&quux=bla")
93
120
  req = Rack::Request.new \
@@ -100,7 +127,8 @@ context "Rack::Request" do
100
127
 
101
128
  specify "cleans up Safari's ajax POST body" do
102
129
  req = Rack::Request.new \
103
- Rack::MockRequest.env_for("/", :input => "foo=bar&quux=bla\0")
130
+ Rack::MockRequest.env_for("/",
131
+ 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
104
132
  req.POST.should.equal "foo" => "bar", "quux" => "bla"
105
133
  end
106
134
 
@@ -147,9 +175,21 @@ context "Rack::Request" do
147
175
  req.referer.should.equal "/"
148
176
  end
149
177
 
178
+ specify "user agent should be extracted correct" do
179
+ req = Rack::Request.new \
180
+ Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
181
+ req.user_agent.should.equal "Mozilla/4.0 (compatible)"
182
+
183
+ req = Rack::Request.new \
184
+ Rack::MockRequest.env_for("/")
185
+ req.user_agent.should.equal nil
186
+ end
187
+
150
188
  specify "can cache, but invalidates the cache" do
151
189
  req = Rack::Request.new \
152
- Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla")
190
+ Rack::MockRequest.env_for("/?foo=quux",
191
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
192
+ :input => "foo=bar&quux=bla")
153
193
  req.GET.should.equal "foo" => "quux"
154
194
  req.GET.should.equal "foo" => "quux"
155
195
  req.env["QUERY_STRING"] = "bla=foo"
@@ -424,6 +464,7 @@ Content-Transfer-Encoding: base64\r
424
464
  /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
425
465
  --AaB03x--\r
426
466
  EOF
467
+ input.force_encoding("ASCII-8BIT") if input.respond_to? :force_encoding
427
468
  res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
428
469
  "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
429
470
  "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
@@ -118,6 +118,9 @@ context "Rack::Response" do
118
118
 
119
119
  r = Rack::Response.new([], 500)
120
120
  r.status.should.equal 500
121
+
122
+ r = Rack::Response.new([], "200 OK")
123
+ r.status.should.equal 200
121
124
  end
122
125
 
123
126
  specify "has a constructor that can take a block" do
@@ -0,0 +1,35 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/runtime'
4
+
5
+ context "Rack::Runtime" do
6
+ specify "sets X-Runtime is none is set" do
7
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
8
+ response = Rack::Runtime.new(app).call({})
9
+ response[1]['X-Runtime'].should =~ /[\d\.]+/
10
+ end
11
+
12
+ specify "does not set the X-Runtime if it is already set" do
13
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', "X-Runtime" => "foobar"}, "Hello, World!"] }
14
+ response = Rack::Runtime.new(app).call({})
15
+ response[1]['X-Runtime'].should == "foobar"
16
+ end
17
+
18
+ specify "should allow a suffix to be set" do
19
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
20
+ response = Rack::Runtime.new(app, "Test").call({})
21
+ response[1]['X-Runtime-Test'].should =~ /[\d\.]+/
22
+ end
23
+
24
+ specify "should allow multiple timers to be set" do
25
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
26
+ runtime1 = Rack::Runtime.new(app, "App")
27
+ runtime2 = Rack::Runtime.new(runtime1, "All")
28
+ response = runtime2.call({})
29
+
30
+ response[1]['X-Runtime-App'].should =~ /[\d\.]+/
31
+ response[1]['X-Runtime-All'].should =~ /[\d\.]+/
32
+
33
+ Float(response[1]['X-Runtime-All']).should > Float(response[1]['X-Runtime-App'])
34
+ end
35
+ end
@@ -0,0 +1,86 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/sendfile'
4
+
5
+ context "Rack::File" do
6
+ specify "should respond to #to_path" do
7
+ Rack::File.new(Dir.pwd).should.respond_to :to_path
8
+ end
9
+ end
10
+
11
+ context "Rack::Sendfile" do
12
+ def sendfile_body
13
+ res = ['Hello World']
14
+ def res.to_path ; "/tmp/hello.txt" ; end
15
+ res
16
+ end
17
+
18
+ def simple_app(body=sendfile_body)
19
+ lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
20
+ end
21
+
22
+ def sendfile_app(body=sendfile_body)
23
+ Rack::Sendfile.new(simple_app(body))
24
+ end
25
+
26
+ setup do
27
+ @request = Rack::MockRequest.new(sendfile_app)
28
+ end
29
+
30
+ def request(headers={})
31
+ yield @request.get('/', headers)
32
+ end
33
+
34
+ specify "does nothing when no X-Sendfile-Type header present" do
35
+ request do |response|
36
+ response.should.be.ok
37
+ response.body.should.equal 'Hello World'
38
+ response.headers.should.not.include 'X-Sendfile'
39
+ end
40
+ end
41
+
42
+ specify "sets X-Sendfile response header and discards body" do
43
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
44
+ response.should.be.ok
45
+ response.body.should.be.empty
46
+ response.headers['X-Sendfile'].should.equal '/tmp/hello.txt'
47
+ end
48
+ end
49
+
50
+ specify "sets X-Lighttpd-Send-File response header and discards body" do
51
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response|
52
+ response.should.be.ok
53
+ response.body.should.be.empty
54
+ response.headers['X-Lighttpd-Send-File'].should.equal '/tmp/hello.txt'
55
+ end
56
+ end
57
+
58
+ specify "sets X-Accel-Redirect response header and discards body" do
59
+ headers = {
60
+ 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect',
61
+ 'HTTP_X_ACCEL_MAPPING' => '/tmp/=/foo/bar/'
62
+ }
63
+ request headers do |response|
64
+ response.should.be.ok
65
+ response.body.should.be.empty
66
+ response.headers['X-Accel-Redirect'].should.equal '/foo/bar/hello.txt'
67
+ end
68
+ end
69
+
70
+ specify 'writes to rack.error when no X-Accel-Mapping is specified' do
71
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect' do |response|
72
+ response.should.be.ok
73
+ response.body.should.equal 'Hello World'
74
+ response.headers.should.not.include 'X-Accel-Redirect'
75
+ response.errors.should.include 'X-Accel-Mapping'
76
+ end
77
+ end
78
+
79
+ specify 'does nothing when body does not respond to #to_path' do
80
+ @request = Rack::MockRequest.new(sendfile_app(['Not a file...']))
81
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
82
+ response.body.should.equal 'Not a file...'
83
+ response.headers.should.not.include 'X-Sendfile'
84
+ end
85
+ end
86
+ end
@@ -46,16 +46,7 @@ context "Rack::Session::Cookie" do
46
46
  get("/", :fatal => true)
47
47
  }.should.raise(Rack::MockRequest::FatalWarning)
48
48
  end
49
-
50
- specify "creates a new cookie with integrity hash" do
51
- res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
52
- if RUBY_VERSION < "1.9"
53
- res["Set-Cookie"].should.match("rack.session=BAh7BiIMY291bnRlcmkG%0A--1439b4d37b9d4b04c603848382f712d6fcd31088")
54
- else
55
- res["Set-Cookie"].should.match("rack.session=BAh7BkkiDGNvdW50ZXIGOg1lbmNvZGluZyINVVMtQVNDSUlpBg%3D%3D%0A--d7a6637b94d2728194a96c18484e1f7ed9074a83")
56
- end
57
- end
58
-
49
+
59
50
  specify "loads from a cookie wih integrity hash" do
60
51
  res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
61
52
  cookie = res["Set-Cookie"]
@@ -8,7 +8,7 @@ begin
8
8
 
9
9
  context "Rack::Session::Memcache" do
10
10
  session_key = Rack::Session::Memcache::DEFAULT_OPTIONS[:key]
11
- session_match = /#{session_key}=[0-9a-fA-F]+;/
11
+ session_match = /#{session_key}=([0-9a-fA-F]+);/
12
12
  incrementor = lambda do |env|
13
13
  env["rack.session"]["counter"] ||= 0
14
14
  env["rack.session"]["counter"] += 1
@@ -27,14 +27,20 @@ begin
27
27
  incrementor.call(env)
28
28
  end
29
29
 
30
- specify "MemCache can connect to existing server" do
31
- test_pool = MemCache.new :namespace => 'test:rack:session'
30
+ specify "faults on no connection" do
31
+ if RUBY_VERSION < "1.9"
32
+ lambda do
33
+ Rack::Session::Memcache.new incrementor, :memcache_server => 'nosuchserver'
34
+ end.should.raise
35
+ else
36
+ lambda do
37
+ Rack::Session::Memcache.new incrementor, :memcache_server => 'nosuchserver'
38
+ end.should.raise ArgumentError
39
+ end
32
40
  end
33
41
 
34
- specify "faults on no connection" do
35
- lambda do
36
- Rack::Session::Memcache.new(incrementor, :memcache_server => '')
37
- end.should.raise
42
+ specify "connect to existing server" do
43
+ test_pool = MemCache.new incrementor, :namespace => 'test:rack:session'
38
44
  end
39
45
 
40
46
  specify "creates a new cookie" do
@@ -151,6 +157,31 @@ begin
151
157
  res3.body.should.equal '{"counter"=>4}'
152
158
  end
153
159
 
160
+ specify "deep hashes are correctly updated" do
161
+ store = nil
162
+ hash_check = proc do |env|
163
+ session = env['rack.session']
164
+ unless session.include? 'test'
165
+ session.update :a => :b, :c => { :d => :e },
166
+ :f => { :g => { :h => :i} }, 'test' => true
167
+ else
168
+ session[:f][:g][:h] = :j
169
+ end
170
+ [200, {}, session.inspect]
171
+ end
172
+ pool = Rack::Session::Memcache.new(hash_check)
173
+ req = Rack::MockRequest.new(pool)
174
+
175
+ res0 = req.get("/")
176
+ session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
177
+ ses0 = pool.pool.get(session_id, true)
178
+
179
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
180
+ ses1 = pool.pool.get(session_id, true)
181
+
182
+ ses1.should.not.equal ses0
183
+ end
184
+
154
185
  # anyone know how to do this better?
155
186
  specify "multithread: should cleanly merge sessions" do
156
187
  next unless $DEBUG
@@ -161,7 +192,7 @@ begin
161
192
  res = req.get('/')
162
193
  res.body.should.equal '{"counter"=>1}'
163
194
  cookie = res["Set-Cookie"]
164
- sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
195
+ session_id = cookie[session_match, 1]
165
196
 
166
197
  delta_incrementor = lambda do |env|
167
198
  # emulate disconjoinment of threading
@@ -178,12 +209,12 @@ begin
178
209
  run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
179
210
  end
180
211
  end.reverse.map{|t| t.run.join.value }
181
- r.each do |res|
182
- res['Set-Cookie'].should.equal cookie
183
- res.body.should.include '"counter"=>2'
212
+ r.each do |request|
213
+ request['Set-Cookie'].should.equal cookie
214
+ request.body.should.include '"counter"=>2'
184
215
  end
185
216
 
186
- session = pool.pool.get(sess_id)
217
+ session = pool.pool.get(session_id)
187
218
  session.size.should.be tnum+1 # counter
188
219
  session['counter'].should.be 2 # meeeh
189
220
 
@@ -202,12 +233,12 @@ begin
202
233
  run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
203
234
  end
204
235
  end.reverse.map{|t| t.run.join.value }
205
- r.each do |res|
206
- res['Set-Cookie'].should.equal cookie
207
- res.body.should.include '"counter"=>3'
236
+ r.each do |request|
237
+ request['Set-Cookie'].should.equal cookie
238
+ request.body.should.include '"counter"=>3'
208
239
  end
209
240
 
210
- session = pool.pool.get(sess_id)
241
+ session = pool.pool.get(session_id)
211
242
  session.size.should.be tnum+1
212
243
  session['counter'].should.be 3
213
244
 
@@ -224,17 +255,19 @@ begin
224
255
  run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
225
256
  end
226
257
  end.reverse.map{|t| t.run.join.value }
227
- r.each do |res|
228
- res['Set-Cookie'].should.equal cookie
229
- res.body.should.include '"foo"=>"bar"'
258
+ r.each do |request|
259
+ request['Set-Cookie'].should.equal cookie
260
+ request.body.should.include '"foo"=>"bar"'
230
261
  end
231
262
 
232
- session = pool.pool.get(sess_id)
263
+ session = pool.pool.get(session_id)
233
264
  session.size.should.be r.size+1
234
265
  session['counter'].should.be.nil?
235
266
  session['foo'].should.equal 'bar'
236
267
  end
237
268
  end
269
+ rescue RuntimeError
270
+ $stderr.puts "Skipping Rack::Session::Memcache tests. Start memcached and try again."
238
271
  rescue LoadError
239
272
  $stderr.puts "Skipping Rack::Session::Memcache tests (Memcache is required). `gem install memcache-client` and try again."
240
273
  end
@@ -44,6 +44,12 @@ context "Rack::URLMap" do
44
44
  res["X-ScriptName"].should.equal "/foo/bar"
45
45
  res["X-PathInfo"].should.equal "/"
46
46
 
47
+ res = Rack::MockRequest.new(map).get("/foo///bar//quux")
48
+ res.status.should.equal 200
49
+ res.should.be.ok
50
+ res["X-ScriptName"].should.equal "/foo/bar"
51
+ res["X-PathInfo"].should.equal "//quux"
52
+
47
53
  res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh")
48
54
  res.should.be.ok
49
55
  res["X-ScriptName"].should.equal "/bleh/foo"
@@ -182,4 +188,28 @@ context "Rack::URLMap" do
182
188
  res["X-PathInfo"].should.equal "/"
183
189
  res["X-ScriptName"].should.equal ""
184
190
  end
191
+
192
+ specify "should not squeeze slashes" do
193
+ map = Rack::URLMap.new("/" => lambda { |env|
194
+ [200,
195
+ { "Content-Type" => "text/plain",
196
+ "X-Position" => "root",
197
+ "X-PathInfo" => env["PATH_INFO"],
198
+ "X-ScriptName" => env["SCRIPT_NAME"]
199
+ }, [""]]},
200
+ "/foo" => lambda { |env|
201
+ [200,
202
+ { "Content-Type" => "text/plain",
203
+ "X-Position" => "foo",
204
+ "X-PathInfo" => env["PATH_INFO"],
205
+ "X-ScriptName" => env["SCRIPT_NAME"]
206
+ }, [""]]}
207
+ )
208
+
209
+ res = Rack::MockRequest.new(map).get("/http://example.org/bar")
210
+ res.should.be.ok
211
+ res["X-Position"].should.equal "root"
212
+ res["X-PathInfo"].should.equal "/http://example.org/bar"
213
+ res["X-ScriptName"].should.equal ""
214
+ end
185
215
  end