edgar-rack 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +401 -0
  4. data/Rakefile +101 -0
  5. data/SPEC +171 -0
  6. data/bin/rackup +4 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +81 -0
  12. data/lib/rack/auth/abstract/handler.rb +37 -0
  13. data/lib/rack/auth/abstract/request.rb +43 -0
  14. data/lib/rack/auth/basic.rb +58 -0
  15. data/lib/rack/auth/digest/md5.rb +124 -0
  16. data/lib/rack/auth/digest/nonce.rb +51 -0
  17. data/lib/rack/auth/digest/params.rb +53 -0
  18. data/lib/rack/auth/digest/request.rb +40 -0
  19. data/lib/rack/builder.rb +80 -0
  20. data/lib/rack/cascade.rb +41 -0
  21. data/lib/rack/chunked.rb +52 -0
  22. data/lib/rack/commonlogger.rb +49 -0
  23. data/lib/rack/conditionalget.rb +63 -0
  24. data/lib/rack/config.rb +15 -0
  25. data/lib/rack/content_length.rb +29 -0
  26. data/lib/rack/content_type.rb +23 -0
  27. data/lib/rack/deflater.rb +96 -0
  28. data/lib/rack/directory.rb +157 -0
  29. data/lib/rack/etag.rb +59 -0
  30. data/lib/rack/file.rb +118 -0
  31. data/lib/rack/handler.rb +88 -0
  32. data/lib/rack/handler/cgi.rb +61 -0
  33. data/lib/rack/handler/evented_mongrel.rb +8 -0
  34. data/lib/rack/handler/fastcgi.rb +90 -0
  35. data/lib/rack/handler/lsws.rb +61 -0
  36. data/lib/rack/handler/mongrel.rb +90 -0
  37. data/lib/rack/handler/scgi.rb +59 -0
  38. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  39. data/lib/rack/handler/thin.rb +17 -0
  40. data/lib/rack/handler/webrick.rb +73 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +567 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +44 -0
  45. data/lib/rack/logger.rb +18 -0
  46. data/lib/rack/methodoverride.rb +27 -0
  47. data/lib/rack/mime.rb +210 -0
  48. data/lib/rack/mock.rb +185 -0
  49. data/lib/rack/nulllogger.rb +18 -0
  50. data/lib/rack/recursive.rb +61 -0
  51. data/lib/rack/reloader.rb +109 -0
  52. data/lib/rack/request.rb +307 -0
  53. data/lib/rack/response.rb +151 -0
  54. data/lib/rack/rewindable_input.rb +104 -0
  55. data/lib/rack/runtime.rb +27 -0
  56. data/lib/rack/sendfile.rb +139 -0
  57. data/lib/rack/server.rb +289 -0
  58. data/lib/rack/session/abstract/id.rb +348 -0
  59. data/lib/rack/session/cookie.rb +152 -0
  60. data/lib/rack/session/memcache.rb +93 -0
  61. data/lib/rack/session/pool.rb +79 -0
  62. data/lib/rack/showexceptions.rb +378 -0
  63. data/lib/rack/showstatus.rb +113 -0
  64. data/lib/rack/static.rb +53 -0
  65. data/lib/rack/urlmap.rb +55 -0
  66. data/lib/rack/utils.rb +698 -0
  67. data/rack.gemspec +39 -0
  68. data/test/cgi/lighttpd.conf +25 -0
  69. data/test/cgi/rackup_stub.rb +6 -0
  70. data/test/cgi/sample_rackup.ru +5 -0
  71. data/test/cgi/test +9 -0
  72. data/test/cgi/test.fcgi +8 -0
  73. data/test/cgi/test.ru +5 -0
  74. data/test/gemloader.rb +6 -0
  75. data/test/multipart/bad_robots +259 -0
  76. data/test/multipart/binary +0 -0
  77. data/test/multipart/empty +10 -0
  78. data/test/multipart/fail_16384_nofile +814 -0
  79. data/test/multipart/file1.txt +1 -0
  80. data/test/multipart/filename_and_modification_param +7 -0
  81. data/test/multipart/filename_with_escaped_quotes +6 -0
  82. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  83. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  84. data/test/multipart/filename_with_unescaped_quotes +6 -0
  85. data/test/multipart/ie +6 -0
  86. data/test/multipart/nested +10 -0
  87. data/test/multipart/none +9 -0
  88. data/test/multipart/semicolon +6 -0
  89. data/test/multipart/text +15 -0
  90. data/test/rackup/config.ru +31 -0
  91. data/test/spec_auth_basic.rb +70 -0
  92. data/test/spec_auth_digest.rb +241 -0
  93. data/test/spec_builder.rb +123 -0
  94. data/test/spec_cascade.rb +45 -0
  95. data/test/spec_cgi.rb +102 -0
  96. data/test/spec_chunked.rb +60 -0
  97. data/test/spec_commonlogger.rb +56 -0
  98. data/test/spec_conditionalget.rb +86 -0
  99. data/test/spec_config.rb +23 -0
  100. data/test/spec_content_length.rb +36 -0
  101. data/test/spec_content_type.rb +29 -0
  102. data/test/spec_deflater.rb +125 -0
  103. data/test/spec_directory.rb +57 -0
  104. data/test/spec_etag.rb +75 -0
  105. data/test/spec_fastcgi.rb +107 -0
  106. data/test/spec_file.rb +92 -0
  107. data/test/spec_handler.rb +49 -0
  108. data/test/spec_head.rb +30 -0
  109. data/test/spec_lint.rb +515 -0
  110. data/test/spec_lobster.rb +43 -0
  111. data/test/spec_lock.rb +142 -0
  112. data/test/spec_logger.rb +28 -0
  113. data/test/spec_methodoverride.rb +58 -0
  114. data/test/spec_mock.rb +241 -0
  115. data/test/spec_mongrel.rb +182 -0
  116. data/test/spec_nulllogger.rb +12 -0
  117. data/test/spec_recursive.rb +69 -0
  118. data/test/spec_request.rb +774 -0
  119. data/test/spec_response.rb +245 -0
  120. data/test/spec_rewindable_input.rb +118 -0
  121. data/test/spec_runtime.rb +39 -0
  122. data/test/spec_sendfile.rb +83 -0
  123. data/test/spec_server.rb +8 -0
  124. data/test/spec_session_abstract_id.rb +43 -0
  125. data/test/spec_session_cookie.rb +171 -0
  126. data/test/spec_session_memcache.rb +289 -0
  127. data/test/spec_session_pool.rb +200 -0
  128. data/test/spec_showexceptions.rb +87 -0
  129. data/test/spec_showstatus.rb +79 -0
  130. data/test/spec_static.rb +48 -0
  131. data/test/spec_thin.rb +86 -0
  132. data/test/spec_urlmap.rb +213 -0
  133. data/test/spec_utils.rb +678 -0
  134. data/test/spec_webrick.rb +141 -0
  135. data/test/testrequest.rb +78 -0
  136. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  137. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  138. metadata +329 -0
@@ -0,0 +1,8 @@
1
+ require 'rack/server'
2
+
3
+ describe Rack::Server do
4
+ it "overrides :config if :app is passed in" do
5
+ server = Rack::Server.new(:app => "FOO")
6
+ server.app.should == "FOO"
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ ### WARNING: there be hax in this file.
2
+
3
+ require 'rack/session/abstract/id'
4
+
5
+ describe Rack::Session::Abstract::ID do
6
+ id = Rack::Session::Abstract::ID
7
+
8
+ def silence_warning
9
+ o, $VERBOSE = $VERBOSE, nil
10
+ yield
11
+ ensure
12
+ $VERBOSE = o
13
+ end
14
+
15
+ def reload_id
16
+ $".delete $".find { |part| part =~ %r{session/abstract/id.rb} }
17
+ silence_warning { require 'rack/session/abstract/id' }
18
+ end
19
+
20
+ should "use securerandom when available" do
21
+ begin
22
+ fake = false
23
+ silence_warning do
24
+ ::SecureRandom = fake = true unless defined?(SecureRandom)
25
+ end
26
+ reload_id
27
+ id::DEFAULT_OPTIONS[:secure_random].should.eql(fake || SecureRandom)
28
+ ensure
29
+ Object.send(:remove_const, :SecureRandom) if fake
30
+ end
31
+ end
32
+
33
+ should "not use securerandom when unavailable" do
34
+ begin
35
+ sr = Object.send(:remove_const, :SecureRandom) if defined?(SecureRandom)
36
+ reload_id
37
+ id::DEFAULT_OPTIONS[:secure_random].should.eql false
38
+ ensure
39
+ ::SecureRandom = sr if defined?(sr)
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,171 @@
1
+ require 'rack/session/cookie'
2
+ require 'rack/mock'
3
+
4
+ describe Rack::Session::Cookie do
5
+ incrementor = lambda do |env|
6
+ env["rack.session"]["counter"] ||= 0
7
+ env["rack.session"]["counter"] += 1
8
+ hash = env["rack.session"].dup
9
+ hash.delete("session_id")
10
+ Rack::Response.new(hash.inspect).to_a
11
+ end
12
+
13
+ session_id = lambda do |env|
14
+ Rack::Response.new(env["rack.session"].inspect).to_a
15
+ end
16
+
17
+ nothing = lambda do |env|
18
+ Rack::Response.new("Nothing").to_a
19
+ end
20
+
21
+ describe 'Base64' do
22
+ it 'uses base64 to encode' do
23
+ coder = Rack::Session::Cookie::Base64.new
24
+ str = 'fuuuuu'
25
+ coder.encode(str).should.equal [str].pack('m')
26
+ end
27
+
28
+ it 'uses base64 to decode' do
29
+ coder = Rack::Session::Cookie::Base64.new
30
+ str = ['fuuuuu'].pack('m')
31
+ coder.decode(str).should.equal str.unpack('m').first
32
+ end
33
+
34
+ describe 'Marshal' do
35
+ it 'marshals and base64 encodes' do
36
+ coder = Rack::Session::Cookie::Base64::Marshal.new
37
+ str = 'fuuuuu'
38
+ coder.encode(str).should.equal [::Marshal.dump(str)].pack('m')
39
+ end
40
+
41
+ it 'marshals and base64 decodes' do
42
+ coder = Rack::Session::Cookie::Base64::Marshal.new
43
+ str = [::Marshal.dump('fuuuuu')].pack('m')
44
+ coder.decode(str).should.equal ::Marshal.load(str.unpack('m').first)
45
+ end
46
+
47
+ it 'rescues failures on decode' do
48
+ coder = Rack::Session::Cookie::Base64::Marshal.new
49
+ coder.decode('lulz').should.equal nil
50
+ end
51
+ end
52
+ end
53
+
54
+ it 'uses a coder' do
55
+ identity = Class.new {
56
+ attr_reader :calls
57
+
58
+ def initialize
59
+ @calls = []
60
+ end
61
+
62
+ def encode(str); @calls << :encode; str; end
63
+ def decode(str); @calls << :decode; str; end
64
+ }.new
65
+ cookie = Rack::Session::Cookie.new(incrementor, :coder => identity)
66
+ res = Rack::MockRequest.new(cookie).get("/")
67
+ res["Set-Cookie"].should.include("rack.session=")
68
+ res.body.should.equal '{"counter"=>1}'
69
+ identity.calls.should.equal [:decode, :encode]
70
+ end
71
+
72
+ it "creates a new cookie" do
73
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
74
+ res["Set-Cookie"].should.include("rack.session=")
75
+ res.body.should.equal '{"counter"=>1}'
76
+ end
77
+
78
+ it "loads from a cookie" do
79
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
80
+ cookie = res["Set-Cookie"]
81
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
82
+ get("/", "HTTP_COOKIE" => cookie)
83
+ res.body.should.equal '{"counter"=>2}'
84
+ cookie = res["Set-Cookie"]
85
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
86
+ get("/", "HTTP_COOKIE" => cookie)
87
+ res.body.should.equal '{"counter"=>3}'
88
+ end
89
+
90
+ it "survives broken cookies" do
91
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
92
+ get("/", "HTTP_COOKIE" => "rack.session=blarghfasel")
93
+ res.body.should.equal '{"counter"=>1}'
94
+ end
95
+
96
+ bigcookie = lambda do |env|
97
+ env["rack.session"]["cookie"] = "big" * 3000
98
+ Rack::Response.new(env["rack.session"].inspect).to_a
99
+ end
100
+
101
+ it "barks on too big cookies" do
102
+ lambda{
103
+ Rack::MockRequest.new(Rack::Session::Cookie.new(bigcookie)).
104
+ get("/", :fatal => true)
105
+ }.should.raise(Rack::MockRequest::FatalWarning)
106
+ end
107
+
108
+ it "loads from a cookie wih integrity hash" do
109
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
110
+ cookie = res["Set-Cookie"]
111
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
112
+ get("/", "HTTP_COOKIE" => cookie)
113
+ res.body.should.equal '{"counter"=>2}'
114
+ cookie = res["Set-Cookie"]
115
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
116
+ get("/", "HTTP_COOKIE" => cookie)
117
+ res.body.should.equal '{"counter"=>3}'
118
+ end
119
+
120
+ it "ignores tampered with session cookies" do
121
+ app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
122
+ response1 = Rack::MockRequest.new(app).get("/")
123
+ response1.body.should.equal '{"counter"=>1}'
124
+
125
+ _, digest = response1["Set-Cookie"].split("--")
126
+ tampered_with_cookie = "hackerman-was-here" + "--" + digest
127
+ response2 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" =>
128
+ tampered_with_cookie)
129
+
130
+ # Tampared cookie was ignored. Counter is back to 1.
131
+ response2.body.should.equal '{"counter"=>1}'
132
+ end
133
+
134
+ it "returns the session id in the session hash" do
135
+ app = Rack::Session::Cookie.new(incrementor)
136
+ res = Rack::MockRequest.new(app).get("/")
137
+ res.body.should.equal '{"counter"=>1}'
138
+
139
+ app = Rack::Session::Cookie.new(session_id)
140
+ res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
141
+ res.body.should.match(/"session_id"=>/)
142
+ res.body.should.match(/"counter"=>1/)
143
+ end
144
+
145
+ it "does not return a cookie if set to secure but not using ssl" do
146
+ app = Rack::Session::Cookie.new(incrementor, :secure => true)
147
+ res = Rack::MockRequest.new(app).get("/")
148
+ res["Set-Cookie"].should.be.nil
149
+
150
+ res = Rack::MockRequest.new(app).get("/", "HTTPS" => "on")
151
+ res["Set-Cookie"].should.not.be.nil
152
+ end
153
+
154
+ it "does not return a cookie if cookie was not read/written" do
155
+ app = Rack::Session::Cookie.new(nothing)
156
+ res = Rack::MockRequest.new(app).get("/")
157
+ res["Set-Cookie"].should.be.nil
158
+ end
159
+
160
+ it "does not return a cookie if cookie was not written (only read)" do
161
+ app = Rack::Session::Cookie.new(session_id)
162
+ res = Rack::MockRequest.new(app).get("/")
163
+ res["Set-Cookie"].should.be.nil
164
+ end
165
+
166
+ it "returns even if not read/written if :expire_after is set" do
167
+ app = Rack::Session::Cookie.new(nothing, :expire_after => 3600)
168
+ res = Rack::MockRequest.new(app).get("/")
169
+ res["Set-Cookie"].should.not.be.nil
170
+ end
171
+ end
@@ -0,0 +1,289 @@
1
+ begin
2
+ require 'rack/session/memcache'
3
+ require 'rack/mock'
4
+ require 'thread'
5
+
6
+ describe Rack::Session::Memcache do
7
+ session_key = Rack::Session::Memcache::DEFAULT_OPTIONS[:key]
8
+ session_match = /#{session_key}=([0-9a-fA-F]+);/
9
+ incrementor = lambda do |env|
10
+ env["rack.session"]["counter"] ||= 0
11
+ env["rack.session"]["counter"] += 1
12
+ Rack::Response.new(env["rack.session"].inspect).to_a
13
+ end
14
+ drop_session = proc do |env|
15
+ env['rack.session.options'][:drop] = true
16
+ incrementor.call(env)
17
+ end
18
+ renew_session = proc do |env|
19
+ env['rack.session.options'][:renew] = true
20
+ incrementor.call(env)
21
+ end
22
+ defer_session = proc do |env|
23
+ env['rack.session.options'][:defer] = true
24
+ incrementor.call(env)
25
+ end
26
+
27
+ # test memcache connection
28
+ Rack::Session::Memcache.new(incrementor)
29
+
30
+ it "faults on no connection" do
31
+ lambda{
32
+ Rack::Session::Memcache.new(incrementor, :memcache_server => 'nosuchserver')
33
+ }.should.raise
34
+ end
35
+
36
+ it "connects to existing server" do
37
+ test_pool = MemCache.new(incrementor, :namespace => 'test:rack:session')
38
+ test_pool.namespace.should.equal 'test:rack:session'
39
+ end
40
+
41
+ it "passes options to MemCache" do
42
+ pool = Rack::Session::Memcache.new(incrementor, :namespace => 'test:rack:session')
43
+ pool.pool.namespace.should.equal 'test:rack:session'
44
+ end
45
+
46
+ it "creates a new cookie" do
47
+ pool = Rack::Session::Memcache.new(incrementor)
48
+ res = Rack::MockRequest.new(pool).get("/")
49
+ res["Set-Cookie"].should.include("#{session_key}=")
50
+ res.body.should.equal '{"counter"=>1}'
51
+ end
52
+
53
+ it "determines session from a cookie" do
54
+ pool = Rack::Session::Memcache.new(incrementor)
55
+ req = Rack::MockRequest.new(pool)
56
+ res = req.get("/")
57
+ cookie = res["Set-Cookie"]
58
+ req.get("/", "HTTP_COOKIE" => cookie).
59
+ body.should.equal '{"counter"=>2}'
60
+ req.get("/", "HTTP_COOKIE" => cookie).
61
+ body.should.equal '{"counter"=>3}'
62
+ end
63
+
64
+ it "determines session only from a cookie by default" do
65
+ pool = Rack::Session::Memcache.new(incrementor)
66
+ req = Rack::MockRequest.new(pool)
67
+ res = req.get("/")
68
+ sid = res["Set-Cookie"][session_match, 1]
69
+ req.get("/?rack.session=#{sid}").
70
+ body.should.equal '{"counter"=>1}'
71
+ req.get("/?rack.session=#{sid}").
72
+ body.should.equal '{"counter"=>1}'
73
+ end
74
+
75
+ it "determines session from params" do
76
+ pool = Rack::Session::Memcache.new(incrementor, :cookie_only => false)
77
+ req = Rack::MockRequest.new(pool)
78
+ res = req.get("/")
79
+ sid = res["Set-Cookie"][session_match, 1]
80
+ req.get("/?rack.session=#{sid}").
81
+ body.should.equal '{"counter"=>2}'
82
+ req.get("/?rack.session=#{sid}").
83
+ body.should.equal '{"counter"=>3}'
84
+ end
85
+
86
+ it "survives nonexistant cookies" do
87
+ bad_cookie = "rack.session=blarghfasel"
88
+ pool = Rack::Session::Memcache.new(incrementor)
89
+ res = Rack::MockRequest.new(pool).
90
+ get("/", "HTTP_COOKIE" => bad_cookie)
91
+ res.body.should.equal '{"counter"=>1}'
92
+ cookie = res["Set-Cookie"][session_match]
93
+ cookie.should.not.match(/#{bad_cookie}/)
94
+ end
95
+
96
+ it "maintains freshness" do
97
+ pool = Rack::Session::Memcache.new(incrementor, :expire_after => 3)
98
+ res = Rack::MockRequest.new(pool).get('/')
99
+ res.body.should.include '"counter"=>1'
100
+ cookie = res["Set-Cookie"]
101
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
102
+ res["Set-Cookie"].should.equal cookie
103
+ res.body.should.include '"counter"=>2'
104
+ puts 'Sleeping to expire session' if $DEBUG
105
+ sleep 4
106
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
107
+ res["Set-Cookie"].should.not.equal cookie
108
+ res.body.should.include '"counter"=>1'
109
+ end
110
+
111
+ it "does not send the same session id if it did not change" do
112
+ pool = Rack::Session::Memcache.new(incrementor)
113
+ req = Rack::MockRequest.new(pool)
114
+
115
+ res0 = req.get("/")
116
+ cookie = res0["Set-Cookie"][session_match]
117
+ res0.body.should.equal '{"counter"=>1}'
118
+
119
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
120
+ res1["Set-Cookie"].should.be.nil
121
+ res1.body.should.equal '{"counter"=>2}'
122
+
123
+ res2 = req.get("/", "HTTP_COOKIE" => cookie)
124
+ res2["Set-Cookie"].should.be.nil
125
+ res2.body.should.equal '{"counter"=>3}'
126
+ end
127
+
128
+ it "deletes cookies with :drop option" do
129
+ pool = Rack::Session::Memcache.new(incrementor)
130
+ req = Rack::MockRequest.new(pool)
131
+ drop = Rack::Utils::Context.new(pool, drop_session)
132
+ dreq = Rack::MockRequest.new(drop)
133
+
134
+ res1 = req.get("/")
135
+ session = (cookie = res1["Set-Cookie"])[session_match]
136
+ res1.body.should.equal '{"counter"=>1}'
137
+
138
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
139
+ res2["Set-Cookie"].should.equal nil
140
+ res2.body.should.equal '{"counter"=>2}'
141
+
142
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
143
+ res3["Set-Cookie"][session_match].should.not.equal session
144
+ res3.body.should.equal '{"counter"=>1}'
145
+ end
146
+
147
+ it "provides new session id with :renew option" do
148
+ pool = Rack::Session::Memcache.new(incrementor)
149
+ req = Rack::MockRequest.new(pool)
150
+ renew = Rack::Utils::Context.new(pool, renew_session)
151
+ rreq = Rack::MockRequest.new(renew)
152
+
153
+ res1 = req.get("/")
154
+ session = (cookie = res1["Set-Cookie"])[session_match]
155
+ res1.body.should.equal '{"counter"=>1}'
156
+
157
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
158
+ new_cookie = res2["Set-Cookie"]
159
+ new_session = new_cookie[session_match]
160
+ new_session.should.not.equal session
161
+ res2.body.should.equal '{"counter"=>2}'
162
+
163
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
164
+ res3.body.should.equal '{"counter"=>3}'
165
+
166
+ # Old cookie was deleted
167
+ res4 = req.get("/", "HTTP_COOKIE" => cookie)
168
+ res4.body.should.equal '{"counter"=>1}'
169
+ end
170
+
171
+ it "omits cookie with :defer option" do
172
+ pool = Rack::Session::Memcache.new(incrementor)
173
+ defer = Rack::Utils::Context.new(pool, defer_session)
174
+ dreq = Rack::MockRequest.new(defer)
175
+
176
+ res0 = dreq.get("/")
177
+ res0["Set-Cookie"].should.equal nil
178
+ res0.body.should.equal '{"counter"=>1}'
179
+ end
180
+
181
+ it "updates deep hashes correctly" do
182
+ hash_check = proc do |env|
183
+ session = env['rack.session']
184
+ unless session.include? 'test'
185
+ session.update :a => :b, :c => { :d => :e },
186
+ :f => { :g => { :h => :i} }, 'test' => true
187
+ else
188
+ session[:f][:g][:h] = :j
189
+ end
190
+ [200, {}, [session.inspect]]
191
+ end
192
+ pool = Rack::Session::Memcache.new(hash_check)
193
+ req = Rack::MockRequest.new(pool)
194
+
195
+ res0 = req.get("/")
196
+ session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
197
+ ses0 = pool.pool.get(session_id, true)
198
+
199
+ req.get("/", "HTTP_COOKIE" => cookie)
200
+ ses1 = pool.pool.get(session_id, true)
201
+
202
+ ses1.should.not.equal ses0
203
+ end
204
+
205
+ # anyone know how to do this better?
206
+ it "cleanly merges sessions when multithreaded" do
207
+ unless $DEBUG
208
+ 1.should.equal 1 # fake assertion to appease the mighty bacon
209
+ next
210
+ end
211
+ warn 'Running multithread test for Session::Memcache'
212
+ pool = Rack::Session::Memcache.new(incrementor)
213
+ req = Rack::MockRequest.new(pool)
214
+
215
+ res = req.get('/')
216
+ res.body.should.equal '{"counter"=>1}'
217
+ cookie = res["Set-Cookie"]
218
+ session_id = cookie[session_match, 1]
219
+
220
+ delta_incrementor = lambda do |env|
221
+ # emulate disconjoinment of threading
222
+ env['rack.session'] = env['rack.session'].dup
223
+ Thread.stop
224
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
225
+ incrementor.call(env)
226
+ end
227
+ tses = Rack::Utils::Context.new pool, delta_incrementor
228
+ treq = Rack::MockRequest.new(tses)
229
+ tnum = rand(7).to_i+5
230
+ r = Array.new(tnum) do
231
+ Thread.new(treq) do |run|
232
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
233
+ end
234
+ end.reverse.map{|t| t.run.join.value }
235
+ r.each do |request|
236
+ request['Set-Cookie'].should.equal cookie
237
+ request.body.should.include '"counter"=>2'
238
+ end
239
+
240
+ session = pool.pool.get(session_id)
241
+ session.size.should.equal tnum+1 # counter
242
+ session['counter'].should.equal 2 # meeeh
243
+
244
+ tnum = rand(7).to_i+5
245
+ r = Array.new(tnum) do |i|
246
+ app = Rack::Utils::Context.new pool, time_delta
247
+ req = Rack::MockRequest.new app
248
+ Thread.new(req) do |run|
249
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
250
+ end
251
+ end.reverse.map{|t| t.run.join.value }
252
+ r.each do |request|
253
+ request['Set-Cookie'].should.equal cookie
254
+ request.body.should.include '"counter"=>3'
255
+ end
256
+
257
+ session = pool.pool.get(session_id)
258
+ session.size.should.be tnum+1
259
+ session['counter'].should.be 3
260
+
261
+ drop_counter = proc do |env|
262
+ env['rack.session'].delete 'counter'
263
+ env['rack.session']['foo'] = 'bar'
264
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
265
+ end
266
+ tses = Rack::Utils::Context.new pool, drop_counter
267
+ treq = Rack::MockRequest.new(tses)
268
+ tnum = rand(7).to_i+5
269
+ r = Array.new(tnum) do
270
+ Thread.new(treq) do |run|
271
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
272
+ end
273
+ end.reverse.map{|t| t.run.join.value }
274
+ r.each do |request|
275
+ request['Set-Cookie'].should.equal cookie
276
+ request.body.should.include '"foo"=>"bar"'
277
+ end
278
+
279
+ session = pool.pool.get(session_id)
280
+ session.size.should.be r.size+1
281
+ session['counter'].should.be.nil?
282
+ session['foo'].should.equal 'bar'
283
+ end
284
+ end
285
+ rescue RuntimeError
286
+ $stderr.puts "Skipping Rack::Session::Memcache tests. Start memcached and try again."
287
+ rescue LoadError
288
+ $stderr.puts "Skipping Rack::Session::Memcache tests (Memcache is required). `gem install memcache-client` and try again."
289
+ end