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,27 +1,219 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
4
  # bugrep: Andreas Zehnder
3
5
 
6
+ require_relative '../../../rack'
4
7
  require 'time'
5
- require 'rack/request'
6
- require 'rack/response'
8
+ require 'securerandom'
9
+ require 'digest/sha2'
7
10
 
8
11
  module Rack
9
12
 
10
13
  module Session
11
14
 
15
+ class SessionId
16
+ ID_VERSION = 2
17
+
18
+ attr_reader :public_id
19
+
20
+ def initialize(public_id)
21
+ @public_id = public_id
22
+ end
23
+
24
+ def private_id
25
+ "#{ID_VERSION}::#{hash_sid(public_id)}"
26
+ end
27
+
28
+ alias :cookie_value :public_id
29
+ alias :to_s :public_id
30
+
31
+ def empty?; false; end
32
+ def inspect; public_id.inspect; end
33
+
34
+ private
35
+
36
+ def hash_sid(sid)
37
+ Digest::SHA256.hexdigest(sid)
38
+ end
39
+ end
40
+
12
41
  module Abstract
42
+ # SessionHash is responsible to lazily load the session from store.
43
+
44
+ class SessionHash
45
+ include Enumerable
46
+ attr_writer :id
47
+
48
+ Unspecified = Object.new
49
+
50
+ def self.find(req)
51
+ req.get_header RACK_SESSION
52
+ end
53
+
54
+ def self.set(req, session)
55
+ req.set_header RACK_SESSION, session
56
+ end
57
+
58
+ def self.set_options(req, options)
59
+ req.set_header RACK_SESSION_OPTIONS, options.dup
60
+ end
61
+
62
+ def initialize(store, req)
63
+ @store = store
64
+ @req = req
65
+ @loaded = false
66
+ end
67
+
68
+ def id
69
+ return @id if @loaded or instance_variable_defined?(:@id)
70
+ @id = @store.send(:extract_session_id, @req)
71
+ end
72
+
73
+ def options
74
+ @req.session_options
75
+ end
76
+
77
+ def each(&block)
78
+ load_for_read!
79
+ @data.each(&block)
80
+ end
81
+
82
+ def [](key)
83
+ load_for_read!
84
+ @data[key.to_s]
85
+ end
86
+
87
+ def dig(key, *keys)
88
+ load_for_read!
89
+ @data.dig(key.to_s, *keys)
90
+ end
91
+
92
+ def fetch(key, default = Unspecified, &block)
93
+ load_for_read!
94
+ if default == Unspecified
95
+ @data.fetch(key.to_s, &block)
96
+ else
97
+ @data.fetch(key.to_s, default, &block)
98
+ end
99
+ end
100
+
101
+ def has_key?(key)
102
+ load_for_read!
103
+ @data.has_key?(key.to_s)
104
+ end
105
+ alias :key? :has_key?
106
+ alias :include? :has_key?
107
+
108
+ def []=(key, value)
109
+ load_for_write!
110
+ @data[key.to_s] = value
111
+ end
112
+ alias :store :[]=
113
+
114
+ def clear
115
+ load_for_write!
116
+ @data.clear
117
+ end
118
+
119
+ def destroy
120
+ clear
121
+ @id = @store.send(:delete_session, @req, id, options)
122
+ end
123
+
124
+ def to_hash
125
+ load_for_read!
126
+ @data.dup
127
+ end
128
+
129
+ def update(hash)
130
+ load_for_write!
131
+ @data.update(stringify_keys(hash))
132
+ end
133
+ alias :merge! :update
134
+
135
+ def replace(hash)
136
+ load_for_write!
137
+ @data.replace(stringify_keys(hash))
138
+ end
139
+
140
+ def delete(key)
141
+ load_for_write!
142
+ @data.delete(key.to_s)
143
+ end
144
+
145
+ def inspect
146
+ if loaded?
147
+ @data.inspect
148
+ else
149
+ "#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
150
+ end
151
+ end
152
+
153
+ def exists?
154
+ return @exists if instance_variable_defined?(:@exists)
155
+ @data = {}
156
+ @exists = @store.send(:session_exists?, @req)
157
+ end
158
+
159
+ def loaded?
160
+ @loaded
161
+ end
162
+
163
+ def empty?
164
+ load_for_read!
165
+ @data.empty?
166
+ end
167
+
168
+ def keys
169
+ load_for_read!
170
+ @data.keys
171
+ end
172
+
173
+ def values
174
+ load_for_read!
175
+ @data.values
176
+ end
177
+
178
+ private
179
+
180
+ def load_for_read!
181
+ load! if !loaded? && exists?
182
+ end
183
+
184
+ def load_for_write!
185
+ load! unless loaded?
186
+ end
187
+
188
+ def load!
189
+ @id, session = @store.send(:load_session, @req)
190
+ @data = stringify_keys(session)
191
+ @loaded = true
192
+ end
193
+
194
+ def stringify_keys(other)
195
+ # Use transform_keys after dropping Ruby 2.4 support
196
+ hash = {}
197
+ other.to_hash.each do |key, value|
198
+ hash[key.to_s] = value
199
+ end
200
+ hash
201
+ end
202
+ end
13
203
 
14
204
  # ID sets up a basic framework for implementing an id based sessioning
15
205
  # 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.
206
+ # contain an id reference. Only #find_session, #write_session and
207
+ # #delete_session are required to be overwritten.
18
208
  #
19
209
  # All parameters are optional.
20
210
  # * :key determines the name of the cookie, by default it is
21
211
  # 'rack.session'
22
212
  # * :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.
213
+ # cookie options as by Rack::Response#set_cookie
214
+ # * :skip will not a set a cookie in the response nor update the session state
215
+ # * :defer will not set a cookie in the response but still update the session
216
+ # state if it is used with a backend
25
217
  # * :renew (implementation dependent) will prompt the generation of a new
26
218
  # session id, and migration of data to be referenced at the new id. If
27
219
  # :defer is set, it will be overridden and the cookie will be set.
@@ -29,112 +221,301 @@ module Rack
29
221
  # id will be.
30
222
  #
31
223
  # 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.
224
+ # <tt>env['rack.session.options']</tt>. Additionally the id of the
225
+ # session can be found within the options hash at the key :id. It is
226
+ # highly not recommended to change its value.
35
227
  #
36
228
  # Is Rack::Utils::Context compatible.
229
+ #
230
+ # Not included by default; you must require 'rack/session/abstract/id'
231
+ # to use.
37
232
 
38
- class ID
233
+ class Persisted
39
234
  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={})
235
+ key: RACK_SESSION,
236
+ path: '/',
237
+ domain: nil,
238
+ expire_after: nil,
239
+ secure: false,
240
+ httponly: true,
241
+ defer: false,
242
+ renew: false,
243
+ sidbits: 128,
244
+ cookie_only: true,
245
+ secure_random: ::SecureRandom
246
+ }.freeze
247
+
248
+ attr_reader :key, :default_options, :sid_secure
249
+
250
+ def initialize(app, options = {})
52
251
  @app = app
53
- @key = options[:key] || "rack.session"
54
252
  @default_options = self.class::DEFAULT_OPTIONS.merge(options)
253
+ @key = @default_options.delete(:key)
254
+ @cookie_only = @default_options.delete(:cookie_only)
255
+ @same_site = @default_options.delete(:same_site)
256
+ initialize_sid
55
257
  end
56
258
 
57
259
  def call(env)
58
260
  context(env)
59
261
  end
60
262
 
61
- def context(env, app=@app)
62
- load_session(env)
63
- status, headers, body = app.call(env)
64
- commit_session(env, status, headers, body)
263
+ def context(env, app = @app)
264
+ req = make_request env
265
+ prepare_session(req)
266
+ status, headers, body = app.call(req.env)
267
+ res = Rack::Response::Raw.new status, headers
268
+ commit_session(req, res)
269
+ [status, headers, body]
65
270
  end
66
271
 
67
272
  private
68
273
 
274
+ def make_request(env)
275
+ Rack::Request.new env
276
+ end
277
+
278
+ def initialize_sid
279
+ @sidbits = @default_options[:sidbits]
280
+ @sid_secure = @default_options[:secure_random]
281
+ @sid_length = @sidbits / 4
282
+ end
283
+
69
284
  # Generate a new session id using Ruby #rand. The size of the
70
285
  # session id is controlled by the :sidbits option.
71
286
  # Monkey patch this to use custom methods for session id generation.
72
287
 
73
- def generate_sid
74
- "%0#{@default_options[:sidbits] / 4}x" %
75
- rand(2**@default_options[:sidbits] - 1)
288
+ def generate_sid(secure = @sid_secure)
289
+ if secure
290
+ secure.hex(@sid_length)
291
+ else
292
+ "%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
293
+ end
294
+ rescue NotImplementedError
295
+ generate_sid(false)
296
+ end
297
+
298
+ # Sets the lazy session at 'rack.session' and places options and session
299
+ # metadata into 'rack.session.options'.
300
+
301
+ def prepare_session(req)
302
+ session_was = req.get_header RACK_SESSION
303
+ session = session_class.new(self, req)
304
+ req.set_header RACK_SESSION, session
305
+ req.set_header RACK_SESSION_OPTIONS, @default_options.dup
306
+ session.merge! session_was if session_was
76
307
  end
77
308
 
78
309
  # 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
310
+ # environment to #find_session.
311
+
312
+ def load_session(req)
313
+ sid = current_session_id(req)
314
+ sid, session = find_session(req, sid)
315
+ [sid, session || {}]
316
+ end
317
+
318
+ # Extract session id from request object.
319
+
320
+ def extract_session_id(request)
321
+ sid = request.cookies[@key]
322
+ sid ||= request.params[@key] unless @cookie_only
323
+ sid
324
+ end
325
+
326
+ # Returns the current session id from the SessionHash.
327
+
328
+ def current_session_id(req)
329
+ req.get_header(RACK_SESSION).id
330
+ end
331
+
332
+ # Check if the session exists or not.
333
+
334
+ def session_exists?(req)
335
+ value = current_session_id(req)
336
+ value && !value.empty?
337
+ end
338
+
339
+ # Session should be committed if it was loaded, any of specific options like :renew, :drop
340
+ # or :expire_after was given and the security permissions match. Skips if skip is given.
341
+
342
+ def commit_session?(req, session, options)
343
+ if options[:skip]
344
+ false
345
+ else
346
+ has_session = loaded_session?(session) || forced_session_update?(session, options)
347
+ has_session && security_matches?(req, options)
92
348
  end
349
+ end
350
+
351
+ def loaded_session?(session)
352
+ !session.is_a?(session_class) || session.loaded?
353
+ end
354
+
355
+ def forced_session_update?(session, options)
356
+ force_options?(options) && session && !session.empty?
357
+ end
358
+
359
+ def force_options?(options)
360
+ options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
361
+ end
93
362
 
94
- env['rack.session.options'] = @default_options.
95
- merge(:id => session_id)
363
+ def security_matches?(request, options)
364
+ return true unless options[:secure]
365
+ request.ssl?
96
366
  end
97
367
 
98
368
  # Acquires the session from the environment and the session id from
99
- # the session options and passes them to #set_session. If successful
369
+ # the session options and passes them to #write_session. If successful
100
370
  # and the :defer option is not true, a cookie will be added to the
101
371
  # response with the session's id.
102
372
 
103
- def commit_session(env, status, headers, body)
104
- session = env['rack.session']
105
- options = env['rack.session.options']
106
- session_id = options[:id]
373
+ def commit_session(req, res)
374
+ session = req.get_header RACK_SESSION
375
+ options = session.options
376
+
377
+ if options[:drop] || options[:renew]
378
+ session_id = delete_session(req, session.id || generate_sid, options)
379
+ return unless session_id
380
+ end
381
+
382
+ return unless commit_session?(req, session, options)
383
+
384
+ session.send(:load!) unless loaded_session?(session)
385
+ session_id ||= session.id
386
+ session_data = session.to_hash.delete_if { |k, v| v.nil? }
107
387
 
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
- [status, headers, body]
388
+ if not data = write_session(req, session_id, session_data, options)
389
+ req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
111
390
  elsif options[:defer] and not options[:renew]
112
- env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
113
- [status, headers, body]
391
+ req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
114
392
  else
115
393
  cookie = Hash.new
116
- cookie[:value] = session_id
117
- cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
118
- response = Rack::Response.new(body, status, headers)
119
- response.set_cookie(@key, cookie.merge(options))
120
- response.to_a
394
+ cookie[:value] = cookie_value(data)
395
+ cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
396
+ cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
397
+
398
+ if @same_site.respond_to? :call
399
+ cookie[:same_site] = @same_site.call(req, res)
400
+ else
401
+ cookie[:same_site] = @same_site
402
+ end
403
+ set_cookie(req, res, cookie.merge!(options))
404
+ end
405
+ end
406
+ public :commit_session
407
+
408
+ def cookie_value(data)
409
+ data
410
+ end
411
+
412
+ # Sets the cookie back to the client with session id. We skip the cookie
413
+ # setting if the value didn't change (sid is the same) or expires was given.
414
+
415
+ def set_cookie(request, res, cookie)
416
+ if request.cookies[@key] != cookie[:value] || cookie[:expires]
417
+ res.set_cookie_header =
418
+ Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
419
+ end
420
+ end
421
+
422
+ # Allow subclasses to prepare_session for different Session classes
423
+
424
+ def session_class
425
+ SessionHash
426
+ end
427
+
428
+ # All thread safety and session retrieval procedures should occur here.
429
+ # Should return [session_id, session].
430
+ # If nil is provided as the session id, generation of a new valid id
431
+ # should occur within.
432
+
433
+ def find_session(env, sid)
434
+ raise '#find_session not implemented.'
435
+ end
436
+
437
+ # All thread safety and session storage procedures should occur here.
438
+ # Must return the session id if the session was saved successfully, or
439
+ # false if the session could not be saved.
440
+
441
+ def write_session(req, sid, session, options)
442
+ raise '#write_session not implemented.'
443
+ end
444
+
445
+ # All thread safety and session destroy procedures should occur here.
446
+ # Should return a new session id or nil if options[:drop]
447
+
448
+ def delete_session(req, sid, options)
449
+ raise '#delete_session not implemented'
450
+ end
451
+ end
452
+
453
+ class PersistedSecure < Persisted
454
+ class SecureSessionHash < SessionHash
455
+ def [](key)
456
+ if key == "session_id"
457
+ load_for_read!
458
+ id.public_id if id
459
+ else
460
+ super
461
+ end
462
+ end
463
+ end
464
+
465
+ def generate_sid(*)
466
+ public_id = super
467
+
468
+ SessionId.new(public_id)
469
+ end
470
+
471
+ def extract_session_id(*)
472
+ public_id = super
473
+ public_id && SessionId.new(public_id)
474
+ end
475
+
476
+ private
477
+
478
+ def session_class
479
+ SecureSessionHash
480
+ end
481
+
482
+ def cookie_value(data)
483
+ data.cookie_value
484
+ end
485
+ end
486
+
487
+ class ID < Persisted
488
+ def self.inherited(klass)
489
+ k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
490
+ unless k.instance_variable_defined?(:"@_rack_warned")
491
+ warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
492
+ k.instance_variable_set(:"@_rack_warned", true)
121
493
  end
494
+ super
122
495
  end
123
496
 
124
- # All thread safety and session retrival proceedures should occur here.
497
+ # All thread safety and session retrieval procedures should occur here.
125
498
  # Should return [session_id, session].
126
499
  # If nil is provided as the session id, generation of a new valid id
127
500
  # should occur within.
128
501
 
129
- def get_session(env, sid)
130
- raise '#get_session not implemented.'
502
+ def find_session(req, sid)
503
+ get_session req.env, sid
131
504
  end
132
505
 
133
- # All thread safety and session storage proceedures should occur here.
134
- # Should return true or false dependant on whether or not the session
135
- # was saved or not.
136
- def set_session(env, sid, session, options)
137
- raise '#set_session not implemented.'
506
+ # All thread safety and session storage procedures should occur here.
507
+ # Must return the session id if the session was saved successfully, or
508
+ # false if the session could not be saved.
509
+
510
+ def write_session(req, sid, session, options)
511
+ set_session req.env, sid, session, options
512
+ end
513
+
514
+ # All thread safety and session destroy procedures should occur here.
515
+ # Should return a new session id or nil if options[:drop]
516
+
517
+ def delete_session(req, sid, options)
518
+ destroy_session req.env, sid, options
138
519
  end
139
520
  end
140
521
  end