rack 1.0.1 → 2.2.21

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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +829 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +355 -0
  6. data/Rakefile +64 -98
  7. data/SPEC.rdoc +292 -0
  8. data/bin/rackup +3 -174
  9. data/contrib/rack.png +0 -0
  10. data/contrib/rack.svg +150 -0
  11. data/contrib/rack_logo.svg +164 -111
  12. data/contrib/rdoc.css +412 -0
  13. data/example/lobster.ru +2 -0
  14. data/example/protectedlobster.rb +4 -2
  15. data/example/protectedlobster.ru +3 -1
  16. data/lib/rack/auth/abstract/handler.rb +7 -5
  17. data/lib/rack/auth/abstract/request.rb +12 -2
  18. data/lib/rack/auth/basic.rb +6 -4
  19. data/lib/rack/auth/digest/md5.rb +20 -13
  20. data/lib/rack/auth/digest/nonce.rb +6 -4
  21. data/lib/rack/auth/digest/params.rb +11 -12
  22. data/lib/rack/auth/digest/request.rb +15 -12
  23. data/lib/rack/body_proxy.rb +45 -0
  24. data/lib/rack/builder.rb +213 -19
  25. data/lib/rack/cascade.rb +50 -18
  26. data/lib/rack/chunked.rb +95 -27
  27. data/lib/rack/common_logger.rb +84 -0
  28. data/lib/rack/conditional_get.rb +83 -0
  29. data/lib/rack/config.rb +22 -0
  30. data/lib/rack/content_length.rb +19 -10
  31. data/lib/rack/content_type.rb +12 -5
  32. data/lib/rack/core_ext/regexp.rb +14 -0
  33. data/lib/rack/deflater.rb +90 -42
  34. data/lib/rack/directory.rb +118 -72
  35. data/lib/rack/etag.rb +77 -0
  36. data/lib/rack/events.rb +153 -0
  37. data/lib/rack/file.rb +4 -85
  38. data/lib/rack/files.rb +218 -0
  39. data/lib/rack/handler/cgi.rb +22 -24
  40. data/lib/rack/handler/fastcgi.rb +34 -24
  41. data/lib/rack/handler/lsws.rb +16 -15
  42. data/lib/rack/handler/scgi.rb +38 -29
  43. data/lib/rack/handler/thin.rb +23 -7
  44. data/lib/rack/handler/webrick.rb +95 -37
  45. data/lib/rack/handler.rb +49 -14
  46. data/lib/rack/head.rb +19 -13
  47. data/lib/rack/lint.rb +466 -217
  48. data/lib/rack/lobster.rb +19 -14
  49. data/lib/rack/lock.rb +24 -8
  50. data/lib/rack/logger.rb +20 -0
  51. data/lib/rack/media_type.rb +53 -0
  52. data/lib/rack/method_override.rb +52 -0
  53. data/lib/rack/mime.rb +652 -171
  54. data/lib/rack/mock.rb +203 -64
  55. data/lib/rack/multipart/generator.rb +97 -0
  56. data/lib/rack/multipart/parser.rb +440 -0
  57. data/lib/rack/multipart/uploaded_file.rb +41 -0
  58. data/lib/rack/multipart.rb +64 -0
  59. data/lib/rack/null_logger.rb +39 -0
  60. data/lib/rack/query_parser.rb +266 -0
  61. data/lib/rack/recursive.rb +18 -11
  62. data/lib/rack/reloader.rb +13 -5
  63. data/lib/rack/request.rb +607 -191
  64. data/lib/rack/response.rb +245 -110
  65. data/lib/rack/rewindable_input.rb +15 -21
  66. data/lib/rack/runtime.rb +34 -0
  67. data/lib/rack/sendfile.rb +192 -0
  68. data/lib/rack/server.rb +466 -0
  69. data/lib/rack/session/abstract/id.rb +449 -68
  70. data/lib/rack/session/cookie.rb +159 -47
  71. data/lib/rack/session/memcache.rb +4 -103
  72. data/lib/rack/session/pool.rb +39 -49
  73. data/lib/rack/show_exceptions.rb +390 -0
  74. data/lib/rack/{showstatus.rb → show_status.rb} +21 -14
  75. data/lib/rack/static.rb +164 -14
  76. data/lib/rack/tempfile_reaper.rb +22 -0
  77. data/lib/rack/urlmap.rb +60 -18
  78. data/lib/rack/utils.rb +508 -277
  79. data/lib/rack/version.rb +29 -0
  80. data/lib/rack.rb +85 -34
  81. data/rack.gemspec +43 -51
  82. metadata +119 -207
  83. data/KNOWN-ISSUES +0 -18
  84. data/RDOX +0 -428
  85. data/README +0 -364
  86. data/SPEC +0 -164
  87. data/lib/rack/adapter/camping.rb +0 -22
  88. data/lib/rack/auth/openid.rb +0 -480
  89. data/lib/rack/commonlogger.rb +0 -61
  90. data/lib/rack/conditionalget.rb +0 -47
  91. data/lib/rack/handler/evented_mongrel.rb +0 -8
  92. data/lib/rack/handler/mongrel.rb +0 -87
  93. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  94. data/lib/rack/methodoverride.rb +0 -27
  95. data/lib/rack/showexceptions.rb +0 -349
  96. data/test/cgi/lighttpd.conf +0 -20
  97. data/test/cgi/test +0 -9
  98. data/test/cgi/test.fcgi +0 -8
  99. data/test/cgi/test.ru +0 -7
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/empty +0 -10
  102. data/test/multipart/ie +0 -6
  103. data/test/multipart/nested +0 -10
  104. data/test/multipart/none +0 -9
  105. data/test/multipart/semicolon +0 -6
  106. data/test/multipart/text +0 -10
  107. data/test/spec_rack_auth_basic.rb +0 -73
  108. data/test/spec_rack_auth_digest.rb +0 -226
  109. data/test/spec_rack_auth_openid.rb +0 -84
  110. data/test/spec_rack_builder.rb +0 -84
  111. data/test/spec_rack_camping.rb +0 -51
  112. data/test/spec_rack_cascade.rb +0 -50
  113. data/test/spec_rack_cgi.rb +0 -89
  114. data/test/spec_rack_chunked.rb +0 -62
  115. data/test/spec_rack_commonlogger.rb +0 -32
  116. data/test/spec_rack_conditionalget.rb +0 -41
  117. data/test/spec_rack_content_length.rb +0 -43
  118. data/test/spec_rack_content_type.rb +0 -30
  119. data/test/spec_rack_deflater.rb +0 -127
  120. data/test/spec_rack_directory.rb +0 -61
  121. data/test/spec_rack_fastcgi.rb +0 -89
  122. data/test/spec_rack_file.rb +0 -75
  123. data/test/spec_rack_handler.rb +0 -43
  124. data/test/spec_rack_head.rb +0 -30
  125. data/test/spec_rack_lint.rb +0 -521
  126. data/test/spec_rack_lobster.rb +0 -45
  127. data/test/spec_rack_lock.rb +0 -38
  128. data/test/spec_rack_methodoverride.rb +0 -60
  129. data/test/spec_rack_mock.rb +0 -157
  130. data/test/spec_rack_mongrel.rb +0 -189
  131. data/test/spec_rack_recursive.rb +0 -77
  132. data/test/spec_rack_request.rb +0 -504
  133. data/test/spec_rack_response.rb +0 -218
  134. data/test/spec_rack_rewindable_input.rb +0 -118
  135. data/test/spec_rack_session_cookie.rb +0 -82
  136. data/test/spec_rack_session_memcache.rb +0 -240
  137. data/test/spec_rack_session_pool.rb +0 -172
  138. data/test/spec_rack_showexceptions.rb +0 -21
  139. data/test/spec_rack_showstatus.rb +0 -72
  140. data/test/spec_rack_static.rb +0 -37
  141. data/test/spec_rack_thin.rb +0 -91
  142. data/test/spec_rack_urlmap.rb +0 -185
  143. data/test/spec_rack_utils.rb +0 -387
  144. data/test/spec_rack_webrick.rb +0 -130
  145. data/test/testrequest.rb +0 -57
  146. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  147. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,15 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
- require 'rack/request'
3
- require 'rack/response'
4
+ require 'zlib'
5
+ require_relative 'abstract/id'
6
+ require 'json'
7
+ require 'delegate'
4
8
 
5
9
  module Rack
6
10
 
7
11
  module Session
8
12
 
9
13
  # Rack::Session::Cookie provides simple cookie based session management.
10
- # The session is a Ruby Hash stored as base64 encoded marshalled data
11
- # set to :key (default: rack.session).
14
+ # By default, the session is a Ruby Hash stored as base64 encoded marshalled
15
+ # data set to :key (default: rack.session). The object that encodes the
16
+ # session data is configurable and must respond to +encode+ and +decode+.
17
+ # Both methods must take a string and return a string.
18
+ #
12
19
  # When the secret key is set, cookie data is checked for data integrity.
20
+ # The old secret key is also accepted and allows graceful secret rotation.
13
21
  #
14
22
  # Example:
15
23
  #
@@ -17,73 +25,177 @@ module Rack
17
25
  # :domain => 'foo.com',
18
26
  # :path => '/',
19
27
  # :expire_after => 2592000,
20
- # :secret => 'change_me'
28
+ # :secret => 'change_me',
29
+ # :old_secret => 'also_change_me'
21
30
  #
22
31
  # All parameters are optional.
32
+ #
33
+ # Example of a cookie with no encoding:
34
+ #
35
+ # Rack::Session::Cookie.new(application, {
36
+ # :coder => Rack::Session::Cookie::Identity.new
37
+ # })
38
+ #
39
+ # Example of a cookie with custom encoding:
40
+ #
41
+ # Rack::Session::Cookie.new(application, {
42
+ # :coder => Class.new {
43
+ # def encode(str); str.reverse; end
44
+ # def decode(str); str.reverse; end
45
+ # }.new
46
+ # })
47
+ #
23
48
 
24
- class Cookie
49
+ class Cookie < Abstract::PersistedSecure
50
+ # Encode session cookies as Base64
51
+ class Base64
52
+ def encode(str)
53
+ [str].pack("m0")
54
+ end
55
+
56
+ def decode(str)
57
+ str.unpack("m").first
58
+ end
25
59
 
26
- def initialize(app, options={})
27
- @app = app
28
- @key = options[:key] || "rack.session"
29
- @secret = options[:secret]
30
- @default_options = {:domain => nil,
31
- :path => "/",
32
- :expire_after => nil}.merge(options)
60
+ # Encode session cookies as Marshaled Base64 data
61
+ class Marshal < Base64
62
+ def encode(str)
63
+ super(::Marshal.dump(str))
64
+ end
65
+
66
+ def decode(str)
67
+ return unless str
68
+ ::Marshal.load(super(str)) rescue nil
69
+ end
70
+ end
71
+
72
+ # N.B. Unlike other encoding methods, the contained objects must be a
73
+ # valid JSON composite type, either a Hash or an Array.
74
+ class JSON < Base64
75
+ def encode(obj)
76
+ super(::JSON.dump(obj))
77
+ end
78
+
79
+ def decode(str)
80
+ return unless str
81
+ ::JSON.parse(super(str)) rescue nil
82
+ end
83
+ end
84
+
85
+ class ZipJSON < Base64
86
+ def encode(obj)
87
+ super(Zlib::Deflate.deflate(::JSON.dump(obj)))
88
+ end
89
+
90
+ def decode(str)
91
+ return unless str
92
+ ::JSON.parse(Zlib::Inflate.inflate(super(str)))
93
+ rescue
94
+ nil
95
+ end
96
+ end
97
+ end
98
+
99
+ # Use no encoding for session cookies
100
+ class Identity
101
+ def encode(str); str; end
102
+ def decode(str); str; end
33
103
  end
34
104
 
35
- def call(env)
36
- load_session(env)
37
- status, headers, body = @app.call(env)
38
- commit_session(env, status, headers, body)
105
+ attr_reader :coder
106
+
107
+ def initialize(app, options = {})
108
+ @secrets = options.values_at(:secret, :old_secret).compact
109
+ @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
110
+
111
+ warn <<-MSG unless secure?(options)
112
+ SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
113
+ This poses a security threat. It is strongly recommended that you
114
+ provide a secret to prevent exploits that may be possible from crafted
115
+ cookies. This will not be supported in future versions of Rack, and
116
+ future versions will even invalidate your existing user cookies.
117
+
118
+ Called from: #{caller[0]}.
119
+ MSG
120
+ @coder = options[:coder] ||= Base64::Marshal.new
121
+ super(app, options.merge!(cookie_only: true))
39
122
  end
40
123
 
41
124
  private
42
125
 
43
- def load_session(env)
44
- request = Rack::Request.new(env)
45
- session_data = request.cookies[@key]
126
+ def find_session(req, sid)
127
+ data = unpacked_cookie_data(req)
128
+ data = persistent_session_id!(data)
129
+ [data["session_id"], data]
130
+ end
46
131
 
47
- if @secret && session_data
48
- session_data, digest = session_data.split("--")
49
- session_data = nil unless digest == generate_hmac(session_data)
50
- end
132
+ def extract_session_id(request)
133
+ unpacked_cookie_data(request)["session_id"]
134
+ end
51
135
 
52
- begin
53
- session_data = session_data.unpack("m*").first
54
- session_data = Marshal.load(session_data)
55
- env["rack.session"] = session_data
56
- rescue
57
- env["rack.session"] = Hash.new
136
+ def unpacked_cookie_data(request)
137
+ request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
138
+ session_data = request.cookies[@key]
139
+
140
+ if @secrets.size > 0 && session_data
141
+ session_data, _, digest = session_data.rpartition('--')
142
+ session_data = nil unless digest_match?(session_data, digest)
143
+ end
144
+
145
+ request.set_header(k, coder.decode(session_data) || {})
58
146
  end
147
+ end
59
148
 
60
- env["rack.session.options"] = @default_options.dup
149
+ def persistent_session_id!(data, sid = nil)
150
+ data ||= {}
151
+ data["session_id"] ||= sid || generate_sid
152
+ data
61
153
  end
62
154
 
63
- def commit_session(env, status, headers, body)
64
- session_data = Marshal.dump(env["rack.session"])
65
- session_data = [session_data].pack("m*")
155
+ class SessionId < DelegateClass(Session::SessionId)
156
+ attr_reader :cookie_value
66
157
 
67
- if @secret
68
- session_data = "#{session_data}--#{generate_hmac(session_data)}"
158
+ def initialize(session_id, cookie_value)
159
+ super(session_id)
160
+ @cookie_value = cookie_value
161
+ end
162
+ end
163
+
164
+ def write_session(req, session_id, session, options)
165
+ session = session.merge("session_id" => session_id)
166
+ session_data = coder.encode(session)
167
+
168
+ if @secrets.first
169
+ session_data << "--#{generate_hmac(session_data, @secrets.first)}"
69
170
  end
70
171
 
71
172
  if session_data.size > (4096 - @key.size)
72
- env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
73
- [status, headers, body]
173
+ req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
174
+ nil
74
175
  else
75
- options = env["rack.session.options"]
76
- cookie = Hash.new
77
- cookie[:value] = session_data
78
- cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
79
- response = Rack::Response.new(body, status, headers)
80
- response.set_cookie(@key, cookie.merge(options))
81
- response.to_a
176
+ SessionId.new(session_id, session_data)
82
177
  end
83
178
  end
84
179
 
85
- def generate_hmac(data)
86
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
180
+ def delete_session(req, session_id, options)
181
+ # Nothing to do here, data is in the client
182
+ generate_sid unless options[:drop]
183
+ end
184
+
185
+ def digest_match?(data, digest)
186
+ return unless data && digest
187
+ @secrets.any? do |secret|
188
+ Rack::Utils.secure_compare(digest, generate_hmac(data, secret))
189
+ end
190
+ end
191
+
192
+ def generate_hmac(data, secret)
193
+ OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
194
+ end
195
+
196
+ def secure?(options)
197
+ @secrets.size >= 1 ||
198
+ (options[:coder] && options[:let_coder_handle_secure_encoding])
87
199
  end
88
200
 
89
201
  end
@@ -1,109 +1,10 @@
1
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rack/session/abstract/id'
4
- require 'memcache'
3
+ require 'rack/session/dalli'
5
4
 
6
5
  module Rack
7
6
  module Session
8
- # Rack::Session::Memcache provides simple cookie based session management.
9
- # Session data is stored in memcached. The corresponding session key is
10
- # maintained in the cookie.
11
- # You may treat Session::Memcache as you would Session::Pool with the
12
- # following caveats.
13
- #
14
- # * Setting :expire_after to 0 would note to the Memcache server to hang
15
- # onto the session data until it would drop it according to it's own
16
- # specifications. However, the cookie sent to the client would expire
17
- # immediately.
18
- #
19
- # Note that memcache does drop data before it may be listed to expire. For
20
- # a full description of behaviour, please see memcache's documentation.
21
-
22
- class Memcache < Abstract::ID
23
- attr_reader :mutex, :pool
24
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
25
- :namespace => 'rack:session',
26
- :memcache_server => 'localhost:11211'
27
-
28
- def initialize(app, options={})
29
- super
30
-
31
- @mutex = Mutex.new
32
- @pool = MemCache.
33
- new @default_options[:memcache_server], @default_options
34
- raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?}
35
- end
36
-
37
- def generate_sid
38
- loop do
39
- sid = super
40
- break sid unless @pool.get(sid, true)
41
- end
42
- end
43
-
44
- def get_session(env, sid)
45
- session = @pool.get(sid) if sid
46
- @mutex.lock if env['rack.multithread']
47
- unless sid and session
48
- env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
49
- session = {}
50
- sid = generate_sid
51
- ret = @pool.add sid, session
52
- raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret
53
- end
54
- session.instance_variable_set('@old', {}.merge(session))
55
- return [sid, session]
56
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted
57
- warn "#{self} is unable to find server."
58
- warn $!.inspect
59
- return [ nil, {} ]
60
- ensure
61
- @mutex.unlock if env['rack.multithread']
62
- end
63
-
64
- def set_session(env, session_id, new_session, options)
65
- expiry = options[:expire_after]
66
- expiry = expiry.nil? ? 0 : expiry + 1
67
-
68
- @mutex.lock if env['rack.multithread']
69
- session = @pool.get(session_id) || {}
70
- if options[:renew] or options[:drop]
71
- @pool.delete session_id
72
- return false if options[:drop]
73
- session_id = generate_sid
74
- @pool.add session_id, 0 # so we don't worry about cache miss on #set
75
- end
76
- old_session = new_session.instance_variable_get('@old') || {}
77
- session = merge_sessions session_id, old_session, new_session, session
78
- @pool.set session_id, session, expiry
79
- return session_id
80
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted
81
- warn "#{self} is unable to find server."
82
- warn $!.inspect
83
- return false
84
- ensure
85
- @mutex.unlock if env['rack.multithread']
86
- end
87
-
88
- private
89
-
90
- def merge_sessions sid, old, new, cur=nil
91
- cur ||= {}
92
- unless Hash === old and Hash === new
93
- warn 'Bad old or new sessions provided.'
94
- return cur
95
- end
96
-
97
- delete = old.keys - new.keys
98
- warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty?
99
- delete.each{|k| cur.delete k }
100
-
101
- update = new.keys.select{|k| new[k] != old[k] }
102
- warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty?
103
- update.each{|k| cur[k] = new[k] }
104
-
105
- cur
106
- end
107
- end
7
+ warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
8
+ Memcache = Dalli
108
9
  end
109
10
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
4
  # THANKS:
3
5
  # apeiros, for session id generation, expiry setup, and threadiness
4
6
  # sergio, threadiness and bugreps
5
7
 
6
- require 'rack/session/abstract/id'
8
+ require_relative 'abstract/id'
7
9
  require 'thread'
8
10
 
9
11
  module Rack
@@ -13,7 +15,7 @@ module Rack
13
15
  # In the context of a multithreaded environment, sessions being
14
16
  # committed to the pool is done in a merging manner.
15
17
  #
16
- # The :drop option is available in rack.session.options if you with to
18
+ # The :drop option is available in rack.session.options if you wish to
17
19
  # explicitly remove the session from the session cache.
18
20
  #
19
21
  # Example:
@@ -24,11 +26,11 @@ module Rack
24
26
  # )
25
27
  # Rack::Handler::WEBrick.run sessioned
26
28
 
27
- class Pool < Abstract::ID
29
+ class Pool < Abstract::PersistedSecure
28
30
  attr_reader :mutex, :pool
29
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
31
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge drop: false
30
32
 
31
- def initialize(app, options={})
33
+ def initialize(app, options = {})
32
34
  super
33
35
  @pool = Hash.new
34
36
  @mutex = Mutex.new
@@ -37,63 +39,51 @@ module Rack
37
39
  def generate_sid
38
40
  loop do
39
41
  sid = super
40
- break sid unless @pool.key? sid
42
+ break sid unless @pool.key? sid.private_id
41
43
  end
42
44
  end
43
45
 
44
- def get_session(env, sid)
45
- session = @pool[sid] if sid
46
- @mutex.lock if env['rack.multithread']
47
- unless sid and session
48
- env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
49
- session = {}
50
- sid = generate_sid
51
- @pool.store sid, session
46
+ def find_session(req, sid)
47
+ with_lock(req) do
48
+ unless sid and session = get_session_with_fallback(sid)
49
+ sid, session = generate_sid, {}
50
+ @pool.store sid.private_id, session
51
+ end
52
+ [sid, session]
52
53
  end
53
- session.instance_variable_set('@old', {}.merge(session))
54
- return [sid, session]
55
- ensure
56
- @mutex.unlock if env['rack.multithread']
57
54
  end
58
55
 
59
- def set_session(env, session_id, new_session, options)
60
- @mutex.lock if env['rack.multithread']
61
- session = @pool[session_id]
62
- if options[:renew] or options[:drop]
63
- @pool.delete session_id
64
- return false if options[:drop]
65
- session_id = generate_sid
66
- @pool.store session_id, 0
56
+ def write_session(req, session_id, new_session, options)
57
+ with_lock(req) do
58
+ return false unless get_session_with_fallback(session_id)
59
+ @pool.store session_id.private_id, new_session
60
+ session_id
67
61
  end
68
- old_session = new_session.instance_variable_get('@old') || {}
69
- session = merge_sessions session_id, old_session, new_session, session
70
- @pool.store session_id, session
71
- return session_id
72
- rescue
73
- warn "#{new_session.inspect} has been lost."
74
- warn $!.inspect
75
- ensure
76
- @mutex.unlock if env['rack.multithread']
77
62
  end
78
63
 
79
- private
80
-
81
- def merge_sessions sid, old, new, cur=nil
82
- cur ||= {}
83
- unless Hash === old and Hash === new
84
- warn 'Bad old or new sessions provided.'
85
- return cur
64
+ def delete_session(req, session_id, options)
65
+ with_lock(req) do
66
+ @pool.delete(session_id.public_id)
67
+ @pool.delete(session_id.private_id)
68
+ unless options[:drop]
69
+ sid = generate_sid
70
+ @pool.store(sid.private_id, {})
71
+ sid
72
+ end
86
73
  end
74
+ end
87
75
 
88
- delete = old.keys - new.keys
89
- warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
90
- delete.each{|k| cur.delete k }
76
+ def with_lock(req)
77
+ @mutex.lock if req.multithread?
78
+ yield
79
+ ensure
80
+ @mutex.unlock if @mutex.locked?
81
+ end
91
82
 
92
- update = new.keys.select{|k| new[k] != old[k] }
93
- warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
94
- update.each{|k| cur[k] = new[k] }
83
+ private
95
84
 
96
- cur
85
+ def get_session_with_fallback(sid)
86
+ @pool[sid.private_id] || @pool[sid.public_id]
97
87
  end
98
88
  end
99
89
  end