rack 2.2.4 → 3.0.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +223 -71
  3. data/CONTRIBUTING.md +53 -47
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +309 -0
  6. data/SPEC.rdoc +183 -131
  7. data/lib/rack/auth/abstract/handler.rb +3 -1
  8. data/lib/rack/auth/abstract/request.rb +3 -1
  9. data/lib/rack/auth/basic.rb +0 -2
  10. data/lib/rack/auth/digest/md5.rb +1 -131
  11. data/lib/rack/auth/digest/nonce.rb +1 -54
  12. data/lib/rack/auth/digest/params.rb +1 -54
  13. data/lib/rack/auth/digest/request.rb +1 -43
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +3 -1
  16. data/lib/rack/builder.rb +83 -63
  17. data/lib/rack/cascade.rb +2 -0
  18. data/lib/rack/chunked.rb +16 -13
  19. data/lib/rack/common_logger.rb +23 -18
  20. data/lib/rack/conditional_get.rb +18 -15
  21. data/lib/rack/constants.rb +64 -0
  22. data/lib/rack/content_length.rb +12 -16
  23. data/lib/rack/content_type.rb +8 -5
  24. data/lib/rack/deflater.rb +40 -26
  25. data/lib/rack/directory.rb +9 -3
  26. data/lib/rack/etag.rb +14 -23
  27. data/lib/rack/events.rb +4 -0
  28. data/lib/rack/file.rb +2 -0
  29. data/lib/rack/files.rb +15 -17
  30. data/lib/rack/head.rb +9 -8
  31. data/lib/rack/headers.rb +154 -0
  32. data/lib/rack/lint.rb +783 -682
  33. data/lib/rack/lock.rb +2 -5
  34. data/lib/rack/logger.rb +2 -0
  35. data/lib/rack/media_type.rb +1 -1
  36. data/lib/rack/method_override.rb +6 -2
  37. data/lib/rack/mime.rb +8 -0
  38. data/lib/rack/mock.rb +1 -271
  39. data/lib/rack/mock_request.rb +166 -0
  40. data/lib/rack/mock_response.rb +126 -0
  41. data/lib/rack/multipart/generator.rb +7 -5
  42. data/lib/rack/multipart/parser.rb +134 -65
  43. data/lib/rack/multipart/uploaded_file.rb +4 -0
  44. data/lib/rack/multipart.rb +20 -40
  45. data/lib/rack/null_logger.rb +9 -0
  46. data/lib/rack/query_parser.rb +78 -46
  47. data/lib/rack/recursive.rb +2 -0
  48. data/lib/rack/reloader.rb +0 -2
  49. data/lib/rack/request.rb +226 -108
  50. data/lib/rack/response.rb +136 -61
  51. data/lib/rack/rewindable_input.rb +24 -5
  52. data/lib/rack/runtime.rb +7 -6
  53. data/lib/rack/sendfile.rb +30 -25
  54. data/lib/rack/show_exceptions.rb +15 -2
  55. data/lib/rack/show_status.rb +17 -7
  56. data/lib/rack/static.rb +8 -8
  57. data/lib/rack/tempfile_reaper.rb +15 -4
  58. data/lib/rack/urlmap.rb +4 -2
  59. data/lib/rack/utils.rb +223 -185
  60. data/lib/rack/version.rb +9 -4
  61. data/lib/rack.rb +6 -76
  62. metadata +18 -38
  63. data/README.rdoc +0 -306
  64. data/Rakefile +0 -130
  65. data/bin/rackup +0 -5
  66. data/contrib/rack.png +0 -0
  67. data/contrib/rack.svg +0 -150
  68. data/contrib/rack_logo.svg +0 -164
  69. data/contrib/rdoc.css +0 -412
  70. data/example/lobster.ru +0 -6
  71. data/example/protectedlobster.rb +0 -16
  72. data/example/protectedlobster.ru +0 -10
  73. data/lib/rack/core_ext/regexp.rb +0 -14
  74. data/lib/rack/handler/cgi.rb +0 -59
  75. data/lib/rack/handler/fastcgi.rb +0 -100
  76. data/lib/rack/handler/lsws.rb +0 -61
  77. data/lib/rack/handler/scgi.rb +0 -71
  78. data/lib/rack/handler/thin.rb +0 -36
  79. data/lib/rack/handler/webrick.rb +0 -129
  80. data/lib/rack/handler.rb +0 -104
  81. data/lib/rack/lobster.rb +0 -70
  82. data/lib/rack/server.rb +0 -466
  83. data/lib/rack/session/abstract/id.rb +0 -523
  84. data/lib/rack/session/cookie.rb +0 -203
  85. data/lib/rack/session/memcache.rb +0 -10
  86. data/lib/rack/session/pool.rb +0 -85
  87. data/rack.gemspec +0 -46
@@ -1,523 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
4
- # bugrep: Andreas Zehnder
5
-
6
- require_relative '../../../rack'
7
- require 'time'
8
- require 'securerandom'
9
- require 'digest/sha2'
10
-
11
- module Rack
12
-
13
- module Session
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
-
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
203
-
204
- # ID sets up a basic framework for implementing an id based sessioning
205
- # service. Cookies sent to the client for maintaining sessions will only
206
- # contain an id reference. Only #find_session, #write_session and
207
- # #delete_session are required to be overwritten.
208
- #
209
- # All parameters are optional.
210
- # * :key determines the name of the cookie, by default it is
211
- # 'rack.session'
212
- # * :path, :domain, :expire_after, :secure, and :httponly set the related
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
217
- # * :renew (implementation dependent) will prompt the generation of a new
218
- # session id, and migration of data to be referenced at the new id. If
219
- # :defer is set, it will be overridden and the cookie will be set.
220
- # * :sidbits sets the number of bits in length that a generated session
221
- # id will be.
222
- #
223
- # These options can be set on a per request basis, at the location of
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.
227
- #
228
- # Is Rack::Utils::Context compatible.
229
- #
230
- # Not included by default; you must require 'rack/session/abstract/id'
231
- # to use.
232
-
233
- class Persisted
234
- DEFAULT_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 = {})
251
- @app = app
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
257
- end
258
-
259
- def call(env)
260
- context(env)
261
- end
262
-
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]
270
- end
271
-
272
- private
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
-
284
- # Generate a new session id using Ruby #rand. The size of the
285
- # session id is controlled by the :sidbits option.
286
- # Monkey patch this to use custom methods for session id generation.
287
-
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
307
- end
308
-
309
- # Extracts the session id from provided cookies and passes it and the
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)
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
362
-
363
- def security_matches?(request, options)
364
- return true unless options[:secure]
365
- request.ssl?
366
- end
367
-
368
- # Acquires the session from the environment and the session id from
369
- # the session options and passes them to #write_session. If successful
370
- # and the :defer option is not true, a cookie will be added to the
371
- # response with the session's id.
372
-
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? }
387
-
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.")
390
- elsif options[:defer] and not options[:renew]
391
- req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
392
- else
393
- cookie = Hash.new
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)
493
- end
494
- super
495
- end
496
-
497
- # All thread safety and session retrieval procedures should occur here.
498
- # Should return [session_id, session].
499
- # If nil is provided as the session id, generation of a new valid id
500
- # should occur within.
501
-
502
- def find_session(req, sid)
503
- get_session req.env, sid
504
- end
505
-
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
519
- end
520
- end
521
- end
522
- end
523
- end
@@ -1,203 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'openssl'
4
- require 'zlib'
5
- require_relative 'abstract/id'
6
- require 'json'
7
- require 'base64'
8
-
9
- module Rack
10
-
11
- module Session
12
-
13
- # Rack::Session::Cookie provides simple cookie based session management.
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
- #
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.
21
- #
22
- # Example:
23
- #
24
- # use Rack::Session::Cookie, :key => 'rack.session',
25
- # :domain => 'foo.com',
26
- # :path => '/',
27
- # :expire_after => 2592000,
28
- # :secret => 'change_me',
29
- # :old_secret => 'also_change_me'
30
- #
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
- #
48
-
49
- class Cookie < Abstract::PersistedSecure
50
- # Encode session cookies as Base64
51
- class Base64
52
- def encode(str)
53
- ::Base64.strict_encode64(str)
54
- end
55
-
56
- def decode(str)
57
- ::Base64.decode64(str)
58
- end
59
-
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
103
- end
104
-
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))
122
- end
123
-
124
- private
125
-
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
131
-
132
- def extract_session_id(request)
133
- unpacked_cookie_data(request)["session_id"]
134
- end
135
-
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) || {})
146
- end
147
- end
148
-
149
- def persistent_session_id!(data, sid = nil)
150
- data ||= {}
151
- data["session_id"] ||= sid || generate_sid
152
- data
153
- end
154
-
155
- class SessionId < DelegateClass(Session::SessionId)
156
- attr_reader :cookie_value
157
-
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)}"
170
- end
171
-
172
- if session_data.size > (4096 - @key.size)
173
- req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
174
- nil
175
- else
176
- SessionId.new(session_id, session_data)
177
- end
178
- end
179
-
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])
199
- end
200
-
201
- end
202
- end
203
- end