edgar-rack 1.2.1

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 (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