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.
- checksums.yaml +5 -5
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +17 -25
- data/Rakefile +6 -14
- data/SPEC +8 -9
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -4
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +4 -3
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +63 -39
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +22 -24
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +38 -38
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +6 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +4 -11
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +35 -53
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +272 -158
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +383 -307
- data/lib/rack/response.rb +129 -56
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +31 -25
- data/lib/rack/session/abstract/id.rb +95 -135
- data/lib/rack/session/cookie.rb +26 -28
- data/lib/rack/session/memcache.rb +8 -14
- data/lib/rack/session/pool.rb +14 -21
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +135 -210
- data/lib/rack.rb +70 -21
- data/rack.gemspec +7 -5
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/{filename_with_null_byte → filename_with_single_quote} +1 -1
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +36 -34
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +66 -40
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +107 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +162 -197
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +69 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/spec_method_override.rb +83 -0
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +196 -151
- data/test/spec_multipart.rb +317 -201
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +768 -607
- data/test/spec_response.rb +214 -111
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +28 -0
- data/test/spec_session_cookie.rb +97 -65
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/spec_show_exceptions.rb +80 -0
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +91 -67
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +103 -69
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_methodoverride.rb +0 -111
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- 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
|
-
|
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(
|
55
|
-
|
21
|
+
def self.find(req)
|
22
|
+
req.get_header RACK_SESSION
|
56
23
|
end
|
57
24
|
|
58
|
-
def self.set(
|
59
|
-
|
25
|
+
def self.set(req, session)
|
26
|
+
req.set_header RACK_SESSION, session
|
60
27
|
end
|
61
28
|
|
62
|
-
def self.set_options(
|
63
|
-
|
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,
|
33
|
+
def initialize(store, req)
|
67
34
|
@store = store
|
68
|
-
@
|
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, @
|
41
|
+
@id = @store.send(:extract_session_id, @req)
|
75
42
|
end
|
76
43
|
|
77
44
|
def options
|
78
|
-
@
|
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(:
|
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?, @
|
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, @
|
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 #
|
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']
|
213
|
-
# found within the options hash at the key :id. It is
|
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 =>
|
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 =>
|
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
|
-
|
252
|
-
|
253
|
-
|
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(
|
282
|
-
session_was
|
283
|
-
|
284
|
-
|
285
|
-
|
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 #
|
266
|
+
# environment to #find_session.
|
290
267
|
|
291
|
-
def load_session(
|
292
|
-
sid = current_session_id(
|
293
|
-
sid, session =
|
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(
|
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(
|
309
|
-
|
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?(
|
315
|
-
value = current_session_id(
|
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?(
|
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?(
|
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?(
|
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 #
|
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(
|
355
|
-
session =
|
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 =
|
360
|
-
return
|
334
|
+
session_id = delete_session(req, session.id || generate_sid, options)
|
335
|
+
return unless session_id
|
361
336
|
end
|
362
337
|
|
363
|
-
return
|
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 =
|
370
|
-
|
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
|
-
|
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] =
|
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(
|
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(
|
392
|
-
request = Rack::Request.new(env)
|
361
|
+
def set_cookie(request, res, cookie)
|
393
362
|
if request.cookies[@key] != cookie[:value] || cookie[:expires]
|
394
|
-
|
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
|
410
|
-
raise '#
|
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
|
418
|
-
raise '#
|
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
|
425
|
-
raise '#
|
394
|
+
def delete_session(req, sid, options)
|
395
|
+
raise '#delete_session not implemented'
|
426
396
|
end
|
427
397
|
end
|
428
398
|
|
429
|
-
class
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
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
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
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
|
448
|
-
|
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
|
-
|
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
|
455
|
-
|
422
|
+
def write_session(req, sid, session, options)
|
423
|
+
set_session req.env, sid, session, options
|
456
424
|
end
|
457
425
|
|
458
|
-
|
459
|
-
|
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
|
-
|
464
|
-
|
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
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -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::
|
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(::
|
75
|
+
super(::JSON.dump(obj))
|
75
76
|
end
|
76
77
|
|
77
78
|
def decode(str)
|
78
79
|
return unless str
|
79
|
-
::
|
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(::
|
86
|
+
super(Zlib::Deflate.deflate(::JSON.dump(obj)))
|
86
87
|
end
|
87
88
|
|
88
89
|
def decode(str)
|
89
90
|
return unless str
|
90
|
-
::
|
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
|
-
|
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
|
123
|
-
data = unpacked_cookie_data(
|
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(
|
129
|
-
unpacked_cookie_data(
|
131
|
+
def extract_session_id(request)
|
132
|
+
unpacked_cookie_data(request)["session_id"]
|
130
133
|
end
|
131
134
|
|
132
|
-
def unpacked_cookie_data(
|
133
|
-
|
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
|
-
|
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
|
-
|
165
|
+
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
173
166
|
nil
|
174
167
|
else
|
175
|
-
|
168
|
+
session_data
|
176
169
|
end
|
177
170
|
end
|
178
171
|
|
179
|
-
def
|
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(
|
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::
|
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?
|
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
|
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 =
|
51
|
+
unless sid and session = @pool.get(sid)
|
52
52
|
sid, session = generate_sid, {}
|
53
|
-
unless /^STORED/ =~ @pool.add(sid
|
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
|
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
|
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[
|
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
|