eac-rack 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +18 -0
- data/KNOWN-ISSUES +21 -0
- data/README +399 -0
- data/bin/rackup +4 -0
- data/contrib/rack_logo.svg +111 -0
- data/example/lobster.ru +4 -0
- data/example/protectedlobster.rb +14 -0
- data/example/protectedlobster.ru +8 -0
- data/lib/rack.rb +92 -0
- data/lib/rack/adapter/camping.rb +22 -0
- data/lib/rack/auth/abstract/handler.rb +37 -0
- data/lib/rack/auth/abstract/request.rb +37 -0
- data/lib/rack/auth/basic.rb +58 -0
- data/lib/rack/auth/digest/md5.rb +124 -0
- data/lib/rack/auth/digest/nonce.rb +51 -0
- data/lib/rack/auth/digest/params.rb +55 -0
- data/lib/rack/auth/digest/request.rb +40 -0
- data/lib/rack/builder.rb +80 -0
- data/lib/rack/cascade.rb +41 -0
- data/lib/rack/chunked.rb +49 -0
- data/lib/rack/commonlogger.rb +49 -0
- data/lib/rack/conditionalget.rb +47 -0
- data/lib/rack/config.rb +15 -0
- data/lib/rack/content_length.rb +29 -0
- data/lib/rack/content_type.rb +23 -0
- data/lib/rack/deflater.rb +96 -0
- data/lib/rack/directory.rb +157 -0
- data/lib/rack/etag.rb +23 -0
- data/lib/rack/file.rb +90 -0
- data/lib/rack/handler.rb +88 -0
- data/lib/rack/handler/cgi.rb +61 -0
- data/lib/rack/handler/evented_mongrel.rb +8 -0
- data/lib/rack/handler/fastcgi.rb +89 -0
- data/lib/rack/handler/lsws.rb +63 -0
- data/lib/rack/handler/mongrel.rb +90 -0
- data/lib/rack/handler/scgi.rb +62 -0
- data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
- data/lib/rack/handler/thin.rb +18 -0
- data/lib/rack/handler/webrick.rb +69 -0
- data/lib/rack/head.rb +19 -0
- data/lib/rack/lint.rb +575 -0
- data/lib/rack/lobster.rb +65 -0
- data/lib/rack/lock.rb +16 -0
- data/lib/rack/logger.rb +20 -0
- data/lib/rack/methodoverride.rb +27 -0
- data/lib/rack/mime.rb +206 -0
- data/lib/rack/mock.rb +189 -0
- data/lib/rack/nulllogger.rb +18 -0
- data/lib/rack/recursive.rb +57 -0
- data/lib/rack/reloader.rb +109 -0
- data/lib/rack/request.rb +271 -0
- data/lib/rack/response.rb +149 -0
- data/lib/rack/rewindable_input.rb +100 -0
- data/lib/rack/runtime.rb +27 -0
- data/lib/rack/sendfile.rb +142 -0
- data/lib/rack/server.rb +212 -0
- data/lib/rack/session/abstract/id.rb +140 -0
- data/lib/rack/session/cookie.rb +90 -0
- data/lib/rack/session/memcache.rb +119 -0
- data/lib/rack/session/pool.rb +100 -0
- data/lib/rack/showexceptions.rb +349 -0
- data/lib/rack/showstatus.rb +106 -0
- data/lib/rack/static.rb +38 -0
- data/lib/rack/urlmap.rb +56 -0
- data/lib/rack/utils.rb +614 -0
- data/rack.gemspec +38 -0
- data/test/spec_rack_auth_basic.rb +73 -0
- data/test/spec_rack_auth_digest.rb +226 -0
- data/test/spec_rack_builder.rb +84 -0
- data/test/spec_rack_camping.rb +51 -0
- data/test/spec_rack_cascade.rb +48 -0
- data/test/spec_rack_cgi.rb +89 -0
- data/test/spec_rack_chunked.rb +62 -0
- data/test/spec_rack_commonlogger.rb +61 -0
- data/test/spec_rack_conditionalget.rb +41 -0
- data/test/spec_rack_config.rb +24 -0
- data/test/spec_rack_content_length.rb +43 -0
- data/test/spec_rack_content_type.rb +30 -0
- data/test/spec_rack_deflater.rb +127 -0
- data/test/spec_rack_directory.rb +61 -0
- data/test/spec_rack_etag.rb +17 -0
- data/test/spec_rack_fastcgi.rb +89 -0
- data/test/spec_rack_file.rb +75 -0
- data/test/spec_rack_handler.rb +43 -0
- data/test/spec_rack_head.rb +30 -0
- data/test/spec_rack_lint.rb +528 -0
- data/test/spec_rack_lobster.rb +45 -0
- data/test/spec_rack_lock.rb +38 -0
- data/test/spec_rack_logger.rb +21 -0
- data/test/spec_rack_methodoverride.rb +60 -0
- data/test/spec_rack_mock.rb +243 -0
- data/test/spec_rack_mongrel.rb +189 -0
- data/test/spec_rack_nulllogger.rb +13 -0
- data/test/spec_rack_recursive.rb +77 -0
- data/test/spec_rack_request.rb +545 -0
- data/test/spec_rack_response.rb +221 -0
- data/test/spec_rack_rewindable_input.rb +118 -0
- data/test/spec_rack_runtime.rb +35 -0
- data/test/spec_rack_sendfile.rb +86 -0
- data/test/spec_rack_session_cookie.rb +73 -0
- data/test/spec_rack_session_memcache.rb +273 -0
- data/test/spec_rack_session_pool.rb +172 -0
- data/test/spec_rack_showexceptions.rb +21 -0
- data/test/spec_rack_showstatus.rb +72 -0
- data/test/spec_rack_static.rb +37 -0
- data/test/spec_rack_thin.rb +91 -0
- data/test/spec_rack_urlmap.rb +215 -0
- data/test/spec_rack_utils.rb +554 -0
- data/test/spec_rack_webrick.rb +130 -0
- data/test/spec_rackup.rb +154 -0
- metadata +311 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
|
+
# bugrep: Andreas Zehnder
|
3
|
+
|
4
|
+
require 'time'
|
5
|
+
require 'rack/request'
|
6
|
+
require 'rack/response'
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
|
10
|
+
module Session
|
11
|
+
|
12
|
+
module Abstract
|
13
|
+
|
14
|
+
# ID sets up a basic framework for implementing an id based sessioning
|
15
|
+
# service. Cookies sent to the client for maintaining sessions will only
|
16
|
+
# contain an id reference. Only #get_session and #set_session are
|
17
|
+
# required to be overwritten.
|
18
|
+
#
|
19
|
+
# All parameters are optional.
|
20
|
+
# * :key determines the name of the cookie, by default it is
|
21
|
+
# 'rack.session'
|
22
|
+
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
23
|
+
# cookie options as by Rack::Response#add_cookie
|
24
|
+
# * :defer will not set a cookie in the response.
|
25
|
+
# * :renew (implementation dependent) will prompt the generation of a new
|
26
|
+
# session id, and migration of data to be referenced at the new id. If
|
27
|
+
# :defer is set, it will be overridden and the cookie will be set.
|
28
|
+
# * :sidbits sets the number of bits in length that a generated session
|
29
|
+
# id will be.
|
30
|
+
#
|
31
|
+
# These options can be set on a per request basis, at the location of
|
32
|
+
# env['rack.session.options']. Additionally the id of the session can be
|
33
|
+
# found within the options hash at the key :id. It is highly not
|
34
|
+
# recommended to change its value.
|
35
|
+
#
|
36
|
+
# Is Rack::Utils::Context compatible.
|
37
|
+
|
38
|
+
class ID
|
39
|
+
DEFAULT_OPTIONS = {
|
40
|
+
:path => '/',
|
41
|
+
:domain => nil,
|
42
|
+
:expire_after => nil,
|
43
|
+
:secure => false,
|
44
|
+
:httponly => true,
|
45
|
+
:defer => false,
|
46
|
+
:renew => false,
|
47
|
+
:sidbits => 128
|
48
|
+
}
|
49
|
+
|
50
|
+
attr_reader :key, :default_options
|
51
|
+
def initialize(app, options={})
|
52
|
+
@app = app
|
53
|
+
@key = options[:key] || "rack.session"
|
54
|
+
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def call(env)
|
58
|
+
context(env)
|
59
|
+
end
|
60
|
+
|
61
|
+
def context(env, app=@app)
|
62
|
+
load_session(env)
|
63
|
+
status, headers, body = app.call(env)
|
64
|
+
commit_session(env, status, headers, body)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Generate a new session id using Ruby #rand. The size of the
|
70
|
+
# session id is controlled by the :sidbits option.
|
71
|
+
# Monkey patch this to use custom methods for session id generation.
|
72
|
+
|
73
|
+
def generate_sid
|
74
|
+
"%0#{@default_options[:sidbits] / 4}x" %
|
75
|
+
rand(2**@default_options[:sidbits] - 1)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Extracts the session id from provided cookies and passes it and the
|
79
|
+
# environment to #get_session. It then sets the resulting session into
|
80
|
+
# 'rack.session', and places options and session metadata into
|
81
|
+
# 'rack.session.options'.
|
82
|
+
|
83
|
+
def load_session(env)
|
84
|
+
request = Rack::Request.new(env)
|
85
|
+
session_id = request.cookies[@key]
|
86
|
+
|
87
|
+
begin
|
88
|
+
session_id, session = get_session(env, session_id)
|
89
|
+
env['rack.session'] = session
|
90
|
+
rescue
|
91
|
+
env['rack.session'] = Hash.new
|
92
|
+
end
|
93
|
+
|
94
|
+
env['rack.session.options'] = @default_options.
|
95
|
+
merge(:id => session_id)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Acquires the session from the environment and the session id from
|
99
|
+
# the session options and passes them to #set_session. If successful
|
100
|
+
# and the :defer option is not true, a cookie will be added to the
|
101
|
+
# response with the session's id.
|
102
|
+
|
103
|
+
def commit_session(env, status, headers, body)
|
104
|
+
session = env['rack.session']
|
105
|
+
options = env['rack.session.options']
|
106
|
+
session_id = options[:id]
|
107
|
+
|
108
|
+
if not session_id = set_session(env, session_id, session, options)
|
109
|
+
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
110
|
+
elsif options[:defer] and not options[:renew]
|
111
|
+
env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
|
112
|
+
else
|
113
|
+
cookie = Hash.new
|
114
|
+
cookie[:value] = session_id
|
115
|
+
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
|
116
|
+
Utils.set_cookie_header!(headers, @key, cookie.merge(options))
|
117
|
+
end
|
118
|
+
|
119
|
+
[status, headers, body]
|
120
|
+
end
|
121
|
+
|
122
|
+
# All thread safety and session retrival proceedures should occur here.
|
123
|
+
# Should return [session_id, session].
|
124
|
+
# If nil is provided as the session id, generation of a new valid id
|
125
|
+
# should occur within.
|
126
|
+
|
127
|
+
def get_session(env, sid)
|
128
|
+
raise '#get_session not implemented.'
|
129
|
+
end
|
130
|
+
|
131
|
+
# All thread safety and session storage proceedures should occur here.
|
132
|
+
# Should return true or false dependant on whether or not the session
|
133
|
+
# was saved or not.
|
134
|
+
def set_session(env, sid, session, options)
|
135
|
+
raise '#set_session not implemented.'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'rack/request'
|
3
|
+
require 'rack/response'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
|
7
|
+
module Session
|
8
|
+
|
9
|
+
# 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).
|
12
|
+
# When the secret key is set, cookie data is checked for data integrity.
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# use Rack::Session::Cookie, :key => 'rack.session',
|
17
|
+
# :domain => 'foo.com',
|
18
|
+
# :path => '/',
|
19
|
+
# :expire_after => 2592000,
|
20
|
+
# :secret => 'change_me'
|
21
|
+
#
|
22
|
+
# All parameters are optional.
|
23
|
+
|
24
|
+
class Cookie
|
25
|
+
|
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)
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(env)
|
36
|
+
load_session(env)
|
37
|
+
status, headers, body = @app.call(env)
|
38
|
+
commit_session(env, status, headers, body)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def load_session(env)
|
44
|
+
request = Rack::Request.new(env)
|
45
|
+
session_data = request.cookies[@key]
|
46
|
+
|
47
|
+
if @secret && session_data
|
48
|
+
session_data, digest = session_data.split("--")
|
49
|
+
session_data = nil unless digest == generate_hmac(session_data)
|
50
|
+
end
|
51
|
+
|
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
|
58
|
+
end
|
59
|
+
|
60
|
+
env["rack.session.options"] = @default_options.dup
|
61
|
+
end
|
62
|
+
|
63
|
+
def commit_session(env, status, headers, body)
|
64
|
+
session_data = Marshal.dump(env["rack.session"])
|
65
|
+
session_data = [session_data].pack("m*")
|
66
|
+
|
67
|
+
if @secret
|
68
|
+
session_data = "#{session_data}--#{generate_hmac(session_data)}"
|
69
|
+
end
|
70
|
+
|
71
|
+
if session_data.size > (4096 - @key.size)
|
72
|
+
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
|
73
|
+
else
|
74
|
+
options = env["rack.session.options"]
|
75
|
+
cookie = Hash.new
|
76
|
+
cookie[:value] = session_data
|
77
|
+
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
|
78
|
+
Utils.set_cookie_header!(headers, @key, cookie.merge(options))
|
79
|
+
end
|
80
|
+
|
81
|
+
[status, headers, body]
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_hmac(data)
|
85
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
|
+
|
3
|
+
require 'rack/session/abstract/id'
|
4
|
+
require 'memcache'
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
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
|
+
mserv = @default_options[:memcache_server]
|
33
|
+
mopts = @default_options.
|
34
|
+
reject{|k,v| MemCache::DEFAULT_OPTIONS.include? k }
|
35
|
+
@pool = MemCache.new mserv, mopts
|
36
|
+
unless @pool.active? and @pool.servers.any?{|c| c.alive? }
|
37
|
+
raise 'No memcache servers'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_sid
|
42
|
+
loop do
|
43
|
+
sid = super
|
44
|
+
break sid unless @pool.get(sid, true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_session(env, session_id)
|
49
|
+
@mutex.lock if env['rack.multithread']
|
50
|
+
unless session_id and session = @pool.get(session_id)
|
51
|
+
session_id, session = generate_sid, {}
|
52
|
+
unless /^STORED/ =~ @pool.add(session_id, session)
|
53
|
+
raise "Session collision on '#{session_id.inspect}'"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
session.instance_variable_set '@old', @pool.get(session_id, true)
|
57
|
+
return [session_id, session]
|
58
|
+
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
59
|
+
# MemCache server cannot be contacted
|
60
|
+
warn "#{self} is unable to find memcached server."
|
61
|
+
warn $!.inspect
|
62
|
+
return [ nil, {} ]
|
63
|
+
ensure
|
64
|
+
@mutex.unlock if @mutex.locked?
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_session(env, session_id, new_session, options)
|
68
|
+
expiry = options[:expire_after]
|
69
|
+
expiry = expiry.nil? ? 0 : expiry + 1
|
70
|
+
|
71
|
+
@mutex.lock if env['rack.multithread']
|
72
|
+
if options[:renew] or options[:drop]
|
73
|
+
@pool.delete session_id
|
74
|
+
return false if options[:drop]
|
75
|
+
session_id = generate_sid
|
76
|
+
@pool.add session_id, {} # so we don't worry about cache miss on #set
|
77
|
+
end
|
78
|
+
|
79
|
+
session = @pool.get(session_id) || {}
|
80
|
+
old_session = new_session.instance_variable_get '@old'
|
81
|
+
old_session = old_session ? Marshal.load(old_session) : {}
|
82
|
+
|
83
|
+
unless Hash === old_session and Hash === new_session
|
84
|
+
env['rack.errors'].
|
85
|
+
puts 'Bad old_session or new_session sessions provided.'
|
86
|
+
else # merge sessions
|
87
|
+
# alterations are either update or delete, making as few changes as
|
88
|
+
# possible to prevent possible issues.
|
89
|
+
|
90
|
+
# removed keys
|
91
|
+
delete = old_session.keys - new_session.keys
|
92
|
+
if $VERBOSE and not delete.empty?
|
93
|
+
env['rack.errors'].
|
94
|
+
puts "//@#{session_id}: delete #{delete*','}"
|
95
|
+
end
|
96
|
+
delete.each{|k| session.delete k }
|
97
|
+
|
98
|
+
# added or altered keys
|
99
|
+
update = new_session.keys.
|
100
|
+
select{|k| new_session[k] != old_session[k] }
|
101
|
+
if $VERBOSE and not update.empty?
|
102
|
+
env['rack.errors'].puts "//@#{session_id}: update #{update*','}"
|
103
|
+
end
|
104
|
+
update.each{|k| session[k] = new_session[k] }
|
105
|
+
end
|
106
|
+
|
107
|
+
@pool.set session_id, session, expiry
|
108
|
+
return session_id
|
109
|
+
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
110
|
+
# MemCache server cannot be contacted
|
111
|
+
warn "#{self} is unable to find memcached server."
|
112
|
+
warn $!.inspect
|
113
|
+
return false
|
114
|
+
ensure
|
115
|
+
@mutex.unlock if @mutex.locked?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
|
+
# THANKS:
|
3
|
+
# apeiros, for session id generation, expiry setup, and threadiness
|
4
|
+
# sergio, threadiness and bugreps
|
5
|
+
|
6
|
+
require 'rack/session/abstract/id'
|
7
|
+
require 'thread'
|
8
|
+
|
9
|
+
module Rack
|
10
|
+
module Session
|
11
|
+
# Rack::Session::Pool provides simple cookie based session management.
|
12
|
+
# Session data is stored in a hash held by @pool.
|
13
|
+
# In the context of a multithreaded environment, sessions being
|
14
|
+
# committed to the pool is done in a merging manner.
|
15
|
+
#
|
16
|
+
# The :drop option is available in rack.session.options if you wish to
|
17
|
+
# explicitly remove the session from the session cache.
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
# myapp = MyRackApp.new
|
21
|
+
# sessioned = Rack::Session::Pool.new(myapp,
|
22
|
+
# :domain => 'foo.com',
|
23
|
+
# :expire_after => 2592000
|
24
|
+
# )
|
25
|
+
# Rack::Handler::WEBrick.run sessioned
|
26
|
+
|
27
|
+
class Pool < Abstract::ID
|
28
|
+
attr_reader :mutex, :pool
|
29
|
+
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
|
30
|
+
|
31
|
+
def initialize(app, options={})
|
32
|
+
super
|
33
|
+
@pool = Hash.new
|
34
|
+
@mutex = Mutex.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_sid
|
38
|
+
loop do
|
39
|
+
sid = super
|
40
|
+
break sid unless @pool.key? sid
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
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
|
52
|
+
end
|
53
|
+
session.instance_variable_set('@old', {}.merge(session))
|
54
|
+
return [sid, session]
|
55
|
+
ensure
|
56
|
+
@mutex.unlock if env['rack.multithread']
|
57
|
+
end
|
58
|
+
|
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
|
67
|
+
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
|
+
end
|
78
|
+
|
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
|
86
|
+
end
|
87
|
+
|
88
|
+
delete = old.keys - new.keys
|
89
|
+
warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
|
90
|
+
delete.each{|k| cur.delete k }
|
91
|
+
|
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] }
|
95
|
+
|
96
|
+
cur
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|