rack 1.6.13 → 2.0.1

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 (144) hide show
  1. checksums.yaml +5 -5
  2. data/COPYING +1 -1
  3. data/HISTORY.md +138 -8
  4. data/README.rdoc +17 -25
  5. data/Rakefile +6 -14
  6. data/SPEC +8 -9
  7. data/contrib/rack_logo.svg +164 -111
  8. data/example/protectedlobster.rb +1 -1
  9. data/example/protectedlobster.ru +1 -1
  10. data/lib/rack/auth/abstract/request.rb +5 -1
  11. data/lib/rack/auth/digest/params.rb +2 -3
  12. data/lib/rack/auth/digest/request.rb +1 -1
  13. data/lib/rack/body_proxy.rb +14 -9
  14. data/lib/rack/builder.rb +3 -3
  15. data/lib/rack/chunked.rb +5 -5
  16. data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
  17. data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
  18. data/lib/rack/content_length.rb +2 -2
  19. data/lib/rack/deflater.rb +4 -4
  20. data/lib/rack/directory.rb +66 -54
  21. data/lib/rack/etag.rb +4 -3
  22. data/lib/rack/events.rb +154 -0
  23. data/lib/rack/file.rb +63 -39
  24. data/lib/rack/handler/cgi.rb +15 -16
  25. data/lib/rack/handler/fastcgi.rb +13 -14
  26. data/lib/rack/handler/lsws.rb +11 -11
  27. data/lib/rack/handler/scgi.rb +15 -15
  28. data/lib/rack/handler/thin.rb +3 -0
  29. data/lib/rack/handler/webrick.rb +22 -24
  30. data/lib/rack/handler.rb +3 -25
  31. data/lib/rack/head.rb +15 -17
  32. data/lib/rack/lint.rb +38 -38
  33. data/lib/rack/lobster.rb +1 -1
  34. data/lib/rack/lock.rb +6 -10
  35. data/lib/rack/logger.rb +2 -2
  36. data/lib/rack/media_type.rb +38 -0
  37. data/lib/rack/{methodoverride.rb → method_override.rb} +4 -11
  38. data/lib/rack/mime.rb +18 -5
  39. data/lib/rack/mock.rb +35 -53
  40. data/lib/rack/multipart/generator.rb +5 -5
  41. data/lib/rack/multipart/parser.rb +272 -158
  42. data/lib/rack/multipart/uploaded_file.rb +1 -2
  43. data/lib/rack/multipart.rb +35 -6
  44. data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
  45. data/lib/rack/query_parser.rb +192 -0
  46. data/lib/rack/recursive.rb +8 -8
  47. data/lib/rack/request.rb +383 -307
  48. data/lib/rack/response.rb +129 -56
  49. data/lib/rack/rewindable_input.rb +1 -12
  50. data/lib/rack/runtime.rb +10 -18
  51. data/lib/rack/sendfile.rb +5 -7
  52. data/lib/rack/server.rb +31 -25
  53. data/lib/rack/session/abstract/id.rb +95 -135
  54. data/lib/rack/session/cookie.rb +26 -28
  55. data/lib/rack/session/memcache.rb +8 -14
  56. data/lib/rack/session/pool.rb +14 -21
  57. data/lib/rack/show_exceptions.rb +386 -0
  58. data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
  59. data/lib/rack/static.rb +30 -5
  60. data/lib/rack/tempfile_reaper.rb +2 -2
  61. data/lib/rack/urlmap.rb +15 -14
  62. data/lib/rack/utils.rb +135 -210
  63. data/lib/rack.rb +70 -21
  64. data/rack.gemspec +7 -5
  65. data/test/builder/an_underscore_app.rb +5 -0
  66. data/test/builder/options.ru +1 -1
  67. data/test/cgi/test.fcgi +1 -0
  68. data/test/cgi/test.gz +0 -0
  69. data/test/helper.rb +34 -0
  70. data/test/multipart/filename_with_encoded_words +7 -0
  71. data/test/multipart/{filename_with_null_byte → filename_with_single_quote} +1 -1
  72. data/test/multipart/quoted +15 -0
  73. data/test/multipart/rack-logo.png +0 -0
  74. data/test/multipart/unity3d_wwwform +11 -0
  75. data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
  76. data/test/spec_auth_basic.rb +27 -19
  77. data/test/spec_auth_digest.rb +47 -46
  78. data/test/spec_body_proxy.rb +27 -27
  79. data/test/spec_builder.rb +51 -41
  80. data/test/spec_cascade.rb +24 -22
  81. data/test/spec_cgi.rb +49 -67
  82. data/test/spec_chunked.rb +36 -34
  83. data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
  84. data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
  85. data/test/spec_config.rb +3 -2
  86. data/test/spec_content_length.rb +18 -17
  87. data/test/spec_content_type.rb +13 -12
  88. data/test/spec_deflater.rb +66 -40
  89. data/test/spec_directory.rb +87 -27
  90. data/test/spec_etag.rb +32 -31
  91. data/test/spec_events.rb +133 -0
  92. data/test/spec_fastcgi.rb +50 -72
  93. data/test/spec_file.rb +107 -77
  94. data/test/spec_handler.rb +19 -34
  95. data/test/spec_head.rb +15 -14
  96. data/test/spec_lint.rb +162 -197
  97. data/test/spec_lobster.rb +24 -23
  98. data/test/spec_lock.rb +69 -39
  99. data/test/spec_logger.rb +4 -3
  100. data/test/spec_media_type.rb +42 -0
  101. data/test/spec_method_override.rb +83 -0
  102. data/test/spec_mime.rb +19 -19
  103. data/test/spec_mock.rb +196 -151
  104. data/test/spec_multipart.rb +317 -201
  105. data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
  106. data/test/spec_recursive.rb +17 -14
  107. data/test/spec_request.rb +768 -607
  108. data/test/spec_response.rb +214 -111
  109. data/test/spec_rewindable_input.rb +50 -40
  110. data/test/spec_runtime.rb +11 -10
  111. data/test/spec_sendfile.rb +30 -35
  112. data/test/spec_server.rb +78 -52
  113. data/test/spec_session_abstract_id.rb +11 -33
  114. data/test/spec_session_abstract_session_hash.rb +28 -0
  115. data/test/spec_session_cookie.rb +97 -65
  116. data/test/spec_session_memcache.rb +63 -101
  117. data/test/spec_session_pool.rb +48 -84
  118. data/test/spec_show_exceptions.rb +80 -0
  119. data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
  120. data/test/spec_static.rb +71 -32
  121. data/test/spec_tempfile_reaper.rb +11 -10
  122. data/test/spec_thin.rb +55 -50
  123. data/test/spec_urlmap.rb +79 -78
  124. data/test/spec_utils.rb +441 -346
  125. data/test/spec_version.rb +2 -8
  126. data/test/spec_webrick.rb +91 -67
  127. data/test/static/foo.html +1 -0
  128. data/test/testrequest.rb +1 -1
  129. data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
  130. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
  131. metadata +103 -69
  132. data/KNOWN-ISSUES +0 -44
  133. data/lib/rack/backports/uri/common_18.rb +0 -56
  134. data/lib/rack/backports/uri/common_192.rb +0 -52
  135. data/lib/rack/backports/uri/common_193.rb +0 -29
  136. data/lib/rack/handler/evented_mongrel.rb +0 -8
  137. data/lib/rack/handler/mongrel.rb +0 -106
  138. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  139. data/lib/rack/showexceptions.rb +0 -387
  140. data/lib/rack/utils/okjson.rb +0 -600
  141. data/test/spec_methodoverride.rb +0 -111
  142. data/test/spec_mongrel.rb +0 -182
  143. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  144. data/test/spec_showexceptions.rb +0 -98
@@ -1,81 +1,48 @@
1
1
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
2
  # bugrep: Andreas Zehnder
3
3
 
4
+ require 'rack'
4
5
  require 'time'
5
6
  require 'rack/request'
6
7
  require 'rack/response'
7
- begin
8
- require 'securerandom'
9
- rescue LoadError
10
- # We just won't get securerandom
11
- end
12
- require "digest/sha2"
8
+ require 'securerandom'
13
9
 
14
10
  module Rack
15
11
 
16
12
  module Session
17
13
 
18
- class SessionId
19
- ID_VERSION = 2
20
-
21
- attr_reader :public_id
22
-
23
- def initialize(public_id)
24
- @public_id = public_id
25
- end
26
-
27
- def private_id
28
- "#{ID_VERSION}::#{hash_sid(public_id)}"
29
- end
30
-
31
- alias :cookie_value :public_id
32
- alias :to_s :public_id
33
-
34
- def empty?; false; end
35
- def inspect; public_id.inspect; end
36
-
37
- private
38
-
39
- def hash_sid(sid)
40
- Digest::SHA256.hexdigest(sid)
41
- end
42
- end
43
-
44
14
  module Abstract
45
- ENV_SESSION_KEY = 'rack.session'.freeze
46
- ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
47
-
48
15
  # SessionHash is responsible to lazily load the session from store.
49
16
 
50
17
  class SessionHash
51
18
  include Enumerable
52
19
  attr_writer :id
53
20
 
54
- def self.find(env)
55
- env[ENV_SESSION_KEY]
21
+ def self.find(req)
22
+ req.get_header RACK_SESSION
56
23
  end
57
24
 
58
- def self.set(env, session)
59
- env[ENV_SESSION_KEY] = session
25
+ def self.set(req, session)
26
+ req.set_header RACK_SESSION, session
60
27
  end
61
28
 
62
- def self.set_options(env, options)
63
- env[ENV_SESSION_OPTIONS_KEY] = options.dup
29
+ def self.set_options(req, options)
30
+ req.set_header RACK_SESSION_OPTIONS, options.dup
64
31
  end
65
32
 
66
- def initialize(store, env)
33
+ def initialize(store, req)
67
34
  @store = store
68
- @env = env
35
+ @req = req
69
36
  @loaded = false
70
37
  end
71
38
 
72
39
  def id
73
40
  return @id if @loaded or instance_variable_defined?(:@id)
74
- @id = @store.send(:extract_session_id, @env)
41
+ @id = @store.send(:extract_session_id, @req)
75
42
  end
76
43
 
77
44
  def options
78
- @env[ENV_SESSION_OPTIONS_KEY]
45
+ @req.session_options
79
46
  end
80
47
 
81
48
  def each(&block)
@@ -109,7 +76,7 @@ module Rack
109
76
 
110
77
  def destroy
111
78
  clear
112
- @id = @store.send(:destroy_session, @env, id, options)
79
+ @id = @store.send(:delete_session, @req, id, options)
113
80
  end
114
81
 
115
82
  def to_hash
@@ -144,7 +111,7 @@ module Rack
144
111
  def exists?
145
112
  return @exists if instance_variable_defined?(:@exists)
146
113
  @data = {}
147
- @exists = @store.send(:session_exists?, @env)
114
+ @exists = @store.send(:session_exists?, @req)
148
115
  end
149
116
 
150
117
  def loaded?
@@ -157,10 +124,12 @@ module Rack
157
124
  end
158
125
 
159
126
  def keys
127
+ load_for_read!
160
128
  @data.keys
161
129
  end
162
130
 
163
131
  def values
132
+ load_for_read!
164
133
  @data.values
165
134
  end
166
135
 
@@ -175,7 +144,7 @@ module Rack
175
144
  end
176
145
 
177
146
  def load!
178
- @id, session = @store.send(:load_session, @env)
147
+ @id, session = @store.send(:load_session, @req)
179
148
  @data = stringify_keys(session)
180
149
  @loaded = true
181
150
  end
@@ -191,7 +160,7 @@ module Rack
191
160
 
192
161
  # ID sets up a basic framework for implementing an id based sessioning
193
162
  # service. Cookies sent to the client for maintaining sessions will only
194
- # contain an id reference. Only #get_session and #set_session are
163
+ # contain an id reference. Only #find_session and #write_session are
195
164
  # required to be overwritten.
196
165
  #
197
166
  # All parameters are optional.
@@ -209,9 +178,9 @@ module Rack
209
178
  # id will be.
210
179
  #
211
180
  # These options can be set on a per request basis, at the location of
212
- # env['rack.session.options']. Additionally the id of the session can be
213
- # found within the options hash at the key :id. It is highly not
214
- # recommended to change its value.
181
+ # <tt>env['rack.session.options']</tt>. Additionally the id of the
182
+ # session can be found within the options hash at the key :id. It is
183
+ # highly not recommended to change its value.
215
184
  #
216
185
  # Is Rack::Utils::Context compatible.
217
186
  #
@@ -220,7 +189,7 @@ module Rack
220
189
 
221
190
  class Persisted
222
191
  DEFAULT_OPTIONS = {
223
- :key => 'rack.session',
192
+ :key => RACK_SESSION,
224
193
  :path => '/',
225
194
  :domain => nil,
226
195
  :expire_after => nil,
@@ -230,10 +199,10 @@ module Rack
230
199
  :renew => false,
231
200
  :sidbits => 128,
232
201
  :cookie_only => true,
233
- :secure_random => (::SecureRandom rescue false)
202
+ :secure_random => ::SecureRandom
234
203
  }
235
204
 
236
- attr_reader :key, :default_options
205
+ attr_reader :key, :default_options, :sid_secure
237
206
 
238
207
  def initialize(app, options={})
239
208
  @app = app
@@ -248,13 +217,20 @@ module Rack
248
217
  end
249
218
 
250
219
  def context(env, app=@app)
251
- prepare_session(env)
252
- status, headers, body = app.call(env)
253
- commit_session(env, status, headers, body)
220
+ req = make_request env
221
+ prepare_session(req)
222
+ status, headers, body = app.call(req.env)
223
+ res = Rack::Response::Raw.new status, headers
224
+ commit_session(req, res)
225
+ [status, headers, body]
254
226
  end
255
227
 
256
228
  private
257
229
 
230
+ def make_request(env)
231
+ Rack::Request.new env
232
+ end
233
+
258
234
  def initialize_sid
259
235
  @sidbits = @default_options[:sidbits]
260
236
  @sid_secure = @default_options[:secure_random]
@@ -278,26 +254,26 @@ module Rack
278
254
  # Sets the lazy session at 'rack.session' and places options and session
279
255
  # metadata into 'rack.session.options'.
280
256
 
281
- def prepare_session(env)
282
- session_was = env[ENV_SESSION_KEY]
283
- env[ENV_SESSION_KEY] = session_class.new(self, env)
284
- env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
285
- env[ENV_SESSION_KEY].merge! session_was if session_was
257
+ def prepare_session(req)
258
+ session_was = req.get_header RACK_SESSION
259
+ session = session_class.new(self, req)
260
+ req.set_header RACK_SESSION, session
261
+ req.set_header RACK_SESSION_OPTIONS, @default_options.dup
262
+ session.merge! session_was if session_was
286
263
  end
287
264
 
288
265
  # Extracts the session id from provided cookies and passes it and the
289
- # environment to #get_session.
266
+ # environment to #find_session.
290
267
 
291
- def load_session(env)
292
- sid = current_session_id(env)
293
- sid, session = get_session(env, sid)
268
+ def load_session(req)
269
+ sid = current_session_id(req)
270
+ sid, session = find_session(req, sid)
294
271
  [sid, session || {}]
295
272
  end
296
273
 
297
274
  # Extract session id from request object.
298
275
 
299
- def extract_session_id(env)
300
- request = Rack::Request.new(env)
276
+ def extract_session_id(request)
301
277
  sid = request.cookies[@key]
302
278
  sid ||= request.params[@key] unless @cookie_only
303
279
  sid
@@ -305,26 +281,26 @@ module Rack
305
281
 
306
282
  # Returns the current session id from the SessionHash.
307
283
 
308
- def current_session_id(env)
309
- env[ENV_SESSION_KEY].id
284
+ def current_session_id(req)
285
+ req.get_header(RACK_SESSION).id
310
286
  end
311
287
 
312
288
  # Check if the session exists or not.
313
289
 
314
- def session_exists?(env)
315
- value = current_session_id(env)
290
+ def session_exists?(req)
291
+ value = current_session_id(req)
316
292
  value && !value.empty?
317
293
  end
318
294
 
319
295
  # Session should be committed if it was loaded, any of specific options like :renew, :drop
320
296
  # or :expire_after was given and the security permissions match. Skips if skip is given.
321
297
 
322
- def commit_session?(env, session, options)
298
+ def commit_session?(req, session, options)
323
299
  if options[:skip]
324
300
  false
325
301
  else
326
302
  has_session = loaded_session?(session) || forced_session_update?(session, options)
327
- has_session && security_matches?(env, options)
303
+ has_session && security_matches?(req, options)
328
304
  end
329
305
  end
330
306
 
@@ -340,58 +316,52 @@ module Rack
340
316
  options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
341
317
  end
342
318
 
343
- def security_matches?(env, options)
319
+ def security_matches?(request, options)
344
320
  return true unless options[:secure]
345
- request = Rack::Request.new(env)
346
321
  request.ssl?
347
322
  end
348
323
 
349
324
  # Acquires the session from the environment and the session id from
350
- # the session options and passes them to #set_session. If successful
325
+ # the session options and passes them to #write_session. If successful
351
326
  # and the :defer option is not true, a cookie will be added to the
352
327
  # response with the session's id.
353
328
 
354
- def commit_session(env, status, headers, body)
355
- session = env[ENV_SESSION_KEY]
329
+ def commit_session(req, res)
330
+ session = req.get_header RACK_SESSION
356
331
  options = session.options
357
332
 
358
333
  if options[:drop] || options[:renew]
359
- session_id = destroy_session(env, session.id || generate_sid, options)
360
- return [status, headers, body] unless session_id
334
+ session_id = delete_session(req, session.id || generate_sid, options)
335
+ return unless session_id
361
336
  end
362
337
 
363
- return [status, headers, body] unless commit_session?(env, session, options)
338
+ return unless commit_session?(req, session, options)
364
339
 
365
340
  session.send(:load!) unless loaded_session?(session)
366
341
  session_id ||= session.id
367
342
  session_data = session.to_hash.delete_if { |k,v| v.nil? }
368
343
 
369
- if not data = set_session(env, session_id, session_data, options)
370
- env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
344
+ if not data = write_session(req, session_id, session_data, options)
345
+ req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
371
346
  elsif options[:defer] and not options[:renew]
372
- env["rack.errors"].puts("Deferring cookie for #{session_id.public_id}") if $VERBOSE
347
+ req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
373
348
  else
374
349
  cookie = Hash.new
375
- cookie[:value] = cookie_value(data)
350
+ cookie[:value] = data
376
351
  cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
377
352
  cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
378
- set_cookie(env, headers, cookie.merge!(options))
353
+ set_cookie(req, res, cookie.merge!(options))
379
354
  end
380
-
381
- [status, headers, body]
382
- end
383
-
384
- def cookie_value(data)
385
- data
386
355
  end
356
+ public :commit_session
387
357
 
388
358
  # Sets the cookie back to the client with session id. We skip the cookie
389
359
  # setting if the value didn't change (sid is the same) or expires was given.
390
360
 
391
- def set_cookie(env, headers, cookie)
392
- request = Rack::Request.new(env)
361
+ def set_cookie(request, res, cookie)
393
362
  if request.cookies[@key] != cookie[:value] || cookie[:expires]
394
- Utils.set_cookie_header!(headers, @key, cookie)
363
+ res.set_cookie_header =
364
+ Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
395
365
  end
396
366
  end
397
367
 
@@ -406,68 +376,58 @@ module Rack
406
376
  # If nil is provided as the session id, generation of a new valid id
407
377
  # should occur within.
408
378
 
409
- def get_session(env, sid)
410
- raise '#get_session not implemented.'
379
+ def find_session(env, sid)
380
+ raise '#find_session not implemented.'
411
381
  end
412
382
 
413
383
  # All thread safety and session storage procedures should occur here.
414
384
  # Must return the session id if the session was saved successfully, or
415
385
  # false if the session could not be saved.
416
386
 
417
- def set_session(env, sid, session, options)
418
- raise '#set_session not implemented.'
387
+ def write_session(req, sid, session, options)
388
+ raise '#write_session not implemented.'
419
389
  end
420
390
 
421
391
  # All thread safety and session destroy procedures should occur here.
422
392
  # Should return a new session id or nil if options[:drop]
423
393
 
424
- def destroy_session(env, sid, options)
425
- raise '#destroy_session not implemented'
394
+ def delete_session(req, sid, options)
395
+ raise '#delete_session not implemented'
426
396
  end
427
397
  end
428
398
 
429
- class PersistedSecure < Persisted
430
- class SecureSessionHash < SessionHash
431
- def [](key)
432
- if key == "session_id"
433
- load_for_read!
434
- id.public_id if id
435
- else
436
- super
437
- end
399
+ class ID < Persisted
400
+ def self.inherited(klass)
401
+ k = klass.ancestors.find { |kl| kl.superclass == ID }
402
+ unless k.instance_variable_defined?(:"@_rack_warned")
403
+ warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
404
+ k.instance_variable_set(:"@_rack_warned", true)
438
405
  end
406
+ super
439
407
  end
440
408
 
441
- def generate_sid(*)
442
- public_id = super
443
-
444
- SessionId.new(public_id)
445
- end
409
+ # All thread safety and session retrieval procedures should occur here.
410
+ # Should return [session_id, session].
411
+ # If nil is provided as the session id, generation of a new valid id
412
+ # should occur within.
446
413
 
447
- def extract_session_id(*)
448
- public_id = super
449
- public_id && SessionId.new(public_id)
414
+ def find_session(req, sid)
415
+ get_session req.env, sid
450
416
  end
451
417
 
452
- private
418
+ # All thread safety and session storage procedures should occur here.
419
+ # Must return the session id if the session was saved successfully, or
420
+ # false if the session could not be saved.
453
421
 
454
- def session_class
455
- SecureSessionHash
422
+ def write_session(req, sid, session, options)
423
+ set_session req.env, sid, session, options
456
424
  end
457
425
 
458
- def cookie_value(data)
459
- data.cookie_value
460
- end
461
- end
426
+ # All thread safety and session destroy procedures should occur here.
427
+ # Should return a new session id or nil if options[:drop]
462
428
 
463
- class ID < Persisted
464
- def self.inherited(klass)
465
- k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
466
- unless k.instance_variable_defined?(:"@_rack_warned")
467
- warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
468
- k.instance_variable_set(:"@_rack_warned", true)
469
- end
470
- super
429
+ def delete_session(req, sid, options)
430
+ destroy_session req.env, sid, options
471
431
  end
472
432
  end
473
433
  end
@@ -3,6 +3,7 @@ require 'zlib'
3
3
  require 'rack/request'
4
4
  require 'rack/response'
5
5
  require 'rack/session/abstract/id'
6
+ require 'json'
6
7
 
7
8
  module Rack
8
9
 
@@ -44,7 +45,7 @@ module Rack
44
45
  # })
45
46
  #
46
47
 
47
- class Cookie < Abstract::PersistedSecure
48
+ class Cookie < Abstract::Persisted
48
49
  # Encode session cookies as Base64
49
50
  class Base64
50
51
  def encode(str)
@@ -71,23 +72,23 @@ module Rack
71
72
  # valid JSON composite type, either a Hash or an Array.
72
73
  class JSON < Base64
73
74
  def encode(obj)
74
- super(::Rack::Utils::OkJson.encode(obj))
75
+ super(::JSON.dump(obj))
75
76
  end
76
77
 
77
78
  def decode(str)
78
79
  return unless str
79
- ::Rack::Utils::OkJson.decode(super(str)) rescue nil
80
+ ::JSON.parse(super(str)) rescue nil
80
81
  end
81
82
  end
82
83
 
83
84
  class ZipJSON < Base64
84
85
  def encode(obj)
85
- super(Zlib::Deflate.deflate(::Rack::Utils::OkJson.encode(obj)))
86
+ super(Zlib::Deflate.deflate(::JSON.dump(obj)))
86
87
  end
87
88
 
88
89
  def decode(str)
89
90
  return unless str
90
- ::Rack::Utils::OkJson.decode(Zlib::Inflate.inflate(super(str)))
91
+ ::JSON.parse(Zlib::Inflate.inflate(super(str)))
91
92
  rescue
92
93
  nil
93
94
  end
@@ -104,7 +105,9 @@ module Rack
104
105
 
105
106
  def initialize(app, options={})
106
107
  @secrets = options.values_at(:secret, :old_secret).compact
107
- warn <<-MSG unless @secrets.size >= 1
108
+ @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
109
+
110
+ warn <<-MSG unless secure?(options)
108
111
  SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
109
112
  This poses a security threat. It is strongly recommended that you
110
113
  provide a secret to prevent exploits that may be possible from crafted
@@ -119,19 +122,18 @@ module Rack
119
122
 
120
123
  private
121
124
 
122
- def get_session(env, sid)
123
- data = unpacked_cookie_data(env)
125
+ def find_session(req, sid)
126
+ data = unpacked_cookie_data(req)
124
127
  data = persistent_session_id!(data)
125
128
  [data["session_id"], data]
126
129
  end
127
130
 
128
- def extract_session_id(env)
129
- unpacked_cookie_data(env)["session_id"]
131
+ def extract_session_id(request)
132
+ unpacked_cookie_data(request)["session_id"]
130
133
  end
131
134
 
132
- def unpacked_cookie_data(env)
133
- env["rack.session.unpacked_cookie_data"] ||= begin
134
- request = Rack::Request.new(env)
135
+ def unpacked_cookie_data(request)
136
+ request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
135
137
  session_data = request.cookies[@key]
136
138
 
137
139
  if @secrets.size > 0 && session_data
@@ -141,7 +143,7 @@ module Rack
141
143
  session_data = nil unless digest_match?(session_data, digest)
142
144
  end
143
145
 
144
- coder.decode(session_data) || {}
146
+ request.set_header(k, coder.decode(session_data) || {})
145
147
  end
146
148
  end
147
149
 
@@ -151,16 +153,7 @@ module Rack
151
153
  data
152
154
  end
153
155
 
154
- class SessionId < DelegateClass(Session::SessionId)
155
- attr_reader :cookie_value
156
-
157
- def initialize(session_id, cookie_value)
158
- super(session_id)
159
- @cookie_value = cookie_value
160
- end
161
- end
162
-
163
- def set_session(env, session_id, session, options)
156
+ def write_session(req, session_id, session, options)
164
157
  session = session.merge("session_id" => session_id)
165
158
  session_data = coder.encode(session)
166
159
 
@@ -169,14 +162,14 @@ module Rack
169
162
  end
170
163
 
171
164
  if session_data.size > (4096 - @key.size)
172
- env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
165
+ req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
173
166
  nil
174
167
  else
175
- SessionId.new(session_id, session_data)
168
+ session_data
176
169
  end
177
170
  end
178
171
 
179
- def destroy_session(env, session_id, options)
172
+ def delete_session(req, session_id, options)
180
173
  # Nothing to do here, data is in the client
181
174
  generate_sid unless options[:drop]
182
175
  end
@@ -189,7 +182,12 @@ module Rack
189
182
  end
190
183
 
191
184
  def generate_hmac(data, secret)
192
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
185
+ OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
186
+ end
187
+
188
+ def secure?(options)
189
+ @secrets.size >= 1 ||
190
+ (options[:coder] && options[:let_coder_handle_secure_encoding])
193
191
  end
194
192
 
195
193
  end
@@ -19,7 +19,7 @@ module Rack
19
19
  # Note that memcache does drop data before it may be listed to expire. For
20
20
  # a full description of behaviour, please see memcache's documentation.
21
21
 
22
- class Memcache < Abstract::PersistedSecure
22
+ class Memcache < Abstract::ID
23
23
  attr_reader :mutex, :pool
24
24
 
25
25
  DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
@@ -34,7 +34,7 @@ module Rack
34
34
  mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
35
35
 
36
36
  @pool = options[:cache] || MemCache.new(mserv, mopts)
37
- unless @pool.active? and @pool.servers.any?{|c| c.alive? }
37
+ unless @pool.active? and @pool.servers.any?(&:alive?)
38
38
  raise 'No memcache servers'
39
39
  end
40
40
  end
@@ -42,15 +42,15 @@ module Rack
42
42
  def generate_sid
43
43
  loop do
44
44
  sid = super
45
- break sid unless @pool.get(sid.private_id, true)
45
+ break sid unless @pool.get(sid, true)
46
46
  end
47
47
  end
48
48
 
49
49
  def get_session(env, sid)
50
50
  with_lock(env) do
51
- unless sid and session = get_session_with_fallback(sid)
51
+ unless sid and session = @pool.get(sid)
52
52
  sid, session = generate_sid, {}
53
- unless /^STORED/ =~ @pool.add(sid.private_id, session)
53
+ unless /^STORED/ =~ @pool.add(sid, session)
54
54
  raise "Session collision on '#{sid.inspect}'"
55
55
  end
56
56
  end
@@ -63,21 +63,20 @@ module Rack
63
63
  expiry = expiry.nil? ? 0 : expiry + 1
64
64
 
65
65
  with_lock(env) do
66
- @pool.set session_id.private_id, new_session, expiry
66
+ @pool.set session_id, new_session, expiry
67
67
  session_id
68
68
  end
69
69
  end
70
70
 
71
71
  def destroy_session(env, session_id, options)
72
72
  with_lock(env) do
73
- @pool.delete(session_id.public_id)
74
- @pool.delete(session_id.private_id)
73
+ @pool.delete(session_id)
75
74
  generate_sid unless options[:drop]
76
75
  end
77
76
  end
78
77
 
79
78
  def with_lock(env)
80
- @mutex.lock if env['rack.multithread']
79
+ @mutex.lock if env[RACK_MULTITHREAD]
81
80
  yield
82
81
  rescue MemCache::MemCacheError, Errno::ECONNREFUSED
83
82
  if $VERBOSE
@@ -89,11 +88,6 @@ module Rack
89
88
  @mutex.unlock if @mutex.locked?
90
89
  end
91
90
 
92
- private
93
-
94
- def get_session_with_fallback(sid)
95
- @pool.get(sid.private_id) || @pool.get(sid.public_id)
96
- end
97
91
  end
98
92
  end
99
93
  end