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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +829 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +355 -0
- data/Rakefile +64 -98
- data/SPEC.rdoc +292 -0
- data/bin/rackup +3 -174
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +150 -0
- data/contrib/rack_logo.svg +164 -111
- data/contrib/rdoc.css +412 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +7 -5
- data/lib/rack/auth/abstract/request.rb +12 -2
- data/lib/rack/auth/basic.rb +6 -4
- data/lib/rack/auth/digest/md5.rb +20 -13
- data/lib/rack/auth/digest/nonce.rb +6 -4
- data/lib/rack/auth/digest/params.rb +11 -12
- data/lib/rack/auth/digest/request.rb +15 -12
- data/lib/rack/body_proxy.rb +45 -0
- data/lib/rack/builder.rb +213 -19
- data/lib/rack/cascade.rb +50 -18
- data/lib/rack/chunked.rb +95 -27
- data/lib/rack/common_logger.rb +84 -0
- data/lib/rack/conditional_get.rb +83 -0
- data/lib/rack/config.rb +22 -0
- data/lib/rack/content_length.rb +19 -10
- data/lib/rack/content_type.rb +12 -5
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +90 -42
- data/lib/rack/directory.rb +118 -72
- data/lib/rack/etag.rb +77 -0
- data/lib/rack/events.rb +153 -0
- data/lib/rack/file.rb +4 -85
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +22 -24
- data/lib/rack/handler/fastcgi.rb +34 -24
- data/lib/rack/handler/lsws.rb +16 -15
- data/lib/rack/handler/scgi.rb +38 -29
- data/lib/rack/handler/thin.rb +23 -7
- data/lib/rack/handler/webrick.rb +95 -37
- data/lib/rack/handler.rb +49 -14
- data/lib/rack/head.rb +19 -13
- data/lib/rack/lint.rb +466 -217
- data/lib/rack/lobster.rb +19 -14
- data/lib/rack/lock.rb +24 -8
- data/lib/rack/logger.rb +20 -0
- data/lib/rack/media_type.rb +53 -0
- data/lib/rack/method_override.rb +52 -0
- data/lib/rack/mime.rb +652 -171
- data/lib/rack/mock.rb +203 -64
- data/lib/rack/multipart/generator.rb +97 -0
- data/lib/rack/multipart/parser.rb +440 -0
- data/lib/rack/multipart/uploaded_file.rb +41 -0
- data/lib/rack/multipart.rb +64 -0
- data/lib/rack/null_logger.rb +39 -0
- data/lib/rack/query_parser.rb +266 -0
- data/lib/rack/recursive.rb +18 -11
- data/lib/rack/reloader.rb +13 -5
- data/lib/rack/request.rb +607 -191
- data/lib/rack/response.rb +245 -110
- data/lib/rack/rewindable_input.rb +15 -21
- data/lib/rack/runtime.rb +34 -0
- data/lib/rack/sendfile.rb +192 -0
- data/lib/rack/server.rb +466 -0
- data/lib/rack/session/abstract/id.rb +449 -68
- data/lib/rack/session/cookie.rb +159 -47
- data/lib/rack/session/memcache.rb +4 -103
- data/lib/rack/session/pool.rb +39 -49
- data/lib/rack/show_exceptions.rb +390 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +21 -14
- data/lib/rack/static.rb +164 -14
- data/lib/rack/tempfile_reaper.rb +22 -0
- data/lib/rack/urlmap.rb +60 -18
- data/lib/rack/utils.rb +508 -277
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +85 -34
- data/rack.gemspec +43 -51
- metadata +119 -207
- data/KNOWN-ISSUES +0 -18
- data/RDOX +0 -428
- data/README +0 -364
- data/SPEC +0 -164
- data/lib/rack/adapter/camping.rb +0 -22
- data/lib/rack/auth/openid.rb +0 -480
- data/lib/rack/commonlogger.rb +0 -61
- data/lib/rack/conditionalget.rb +0 -47
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -87
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/methodoverride.rb +0 -27
- data/lib/rack/showexceptions.rb +0 -349
- data/test/cgi/lighttpd.conf +0 -20
- data/test/cgi/test +0 -9
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -7
- data/test/multipart/binary +0 -0
- data/test/multipart/empty +0 -10
- data/test/multipart/ie +0 -6
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -10
- data/test/spec_rack_auth_basic.rb +0 -73
- data/test/spec_rack_auth_digest.rb +0 -226
- data/test/spec_rack_auth_openid.rb +0 -84
- data/test/spec_rack_builder.rb +0 -84
- data/test/spec_rack_camping.rb +0 -51
- data/test/spec_rack_cascade.rb +0 -50
- data/test/spec_rack_cgi.rb +0 -89
- data/test/spec_rack_chunked.rb +0 -62
- data/test/spec_rack_commonlogger.rb +0 -32
- data/test/spec_rack_conditionalget.rb +0 -41
- data/test/spec_rack_content_length.rb +0 -43
- data/test/spec_rack_content_type.rb +0 -30
- data/test/spec_rack_deflater.rb +0 -127
- data/test/spec_rack_directory.rb +0 -61
- data/test/spec_rack_fastcgi.rb +0 -89
- data/test/spec_rack_file.rb +0 -75
- data/test/spec_rack_handler.rb +0 -43
- data/test/spec_rack_head.rb +0 -30
- data/test/spec_rack_lint.rb +0 -521
- data/test/spec_rack_lobster.rb +0 -45
- data/test/spec_rack_lock.rb +0 -38
- data/test/spec_rack_methodoverride.rb +0 -60
- data/test/spec_rack_mock.rb +0 -157
- data/test/spec_rack_mongrel.rb +0 -189
- data/test/spec_rack_recursive.rb +0 -77
- data/test/spec_rack_request.rb +0 -504
- data/test/spec_rack_response.rb +0 -218
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_session_cookie.rb +0 -82
- data/test/spec_rack_session_memcache.rb +0 -240
- data/test/spec_rack_session_pool.rb +0 -172
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rack_static.rb +0 -37
- data/test/spec_rack_thin.rb +0 -91
- data/test/spec_rack_urlmap.rb +0 -185
- data/test/spec_rack_utils.rb +0 -387
- data/test/spec_rack_webrick.rb +0 -130
- data/test/testrequest.rb +0 -57
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/session/cookie.rb
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'openssl'
|
|
2
|
-
require '
|
|
3
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
end
|
|
132
|
+
def extract_session_id(request)
|
|
133
|
+
unpacked_cookie_data(request)["session_id"]
|
|
134
|
+
end
|
|
51
135
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
session_data =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
session_data = [session_data].pack("m*")
|
|
155
|
+
class SessionId < DelegateClass(Session::SessionId)
|
|
156
|
+
attr_reader :cookie_value
|
|
66
157
|
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
73
|
-
|
|
173
|
+
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
|
174
|
+
nil
|
|
74
175
|
else
|
|
75
|
-
|
|
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
|
|
86
|
-
|
|
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
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'rack/session/
|
|
4
|
-
require 'memcache'
|
|
3
|
+
require 'rack/session/dalli'
|
|
5
4
|
|
|
6
5
|
module Rack
|
|
7
6
|
module Session
|
|
8
|
-
|
|
9
|
-
|
|
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
|
data/lib/rack/session/pool.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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::
|
|
29
|
+
class Pool < Abstract::PersistedSecure
|
|
28
30
|
attr_reader :mutex, :pool
|
|
29
|
-
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
|
|
94
|
-
update.each{|k| cur[k] = new[k] }
|
|
83
|
+
private
|
|
95
84
|
|
|
96
|
-
|
|
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
|