rack 1.6.13 → 2.0.9
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 +4 -4
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +3 -3
- 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/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- 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 +24 -26
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +40 -40
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +270 -157
- 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 +394 -305
- data/lib/rack/response.rb +130 -57
- 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 +30 -23
- data/lib/rack/session/abstract/id.rb +110 -75
- data/lib/rack/session/cookie.rb +24 -17
- data/lib/rack/session/memcache.rb +9 -9
- data/lib/rack/session/pool.rb +8 -8
- 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 +138 -211
- data/lib/rack.rb +70 -21
- data/rack.gemspec +10 -9
- 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_single_quote +7 -0
- 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 +37 -35
- 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 +85 -49
- 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 +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +164 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +780 -605
- data/test/spec_response.rb +233 -112
- 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 +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +67 -68
- data/test/spec_session_pool.rb +52 -51
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
- 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 +93 -71
- 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 +57 -36
- 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_mongrel.rb +0 -182
- /data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
@@ -1,15 +1,12 @@
|
|
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
|
-
|
9
|
-
rescue LoadError
|
10
|
-
# We just won't get securerandom
|
11
|
-
end
|
12
|
-
require "digest/sha2"
|
8
|
+
require 'securerandom'
|
9
|
+
require 'digest/sha2'
|
13
10
|
|
14
11
|
module Rack
|
15
12
|
|
@@ -42,40 +39,39 @@ module Rack
|
|
42
39
|
end
|
43
40
|
|
44
41
|
module Abstract
|
45
|
-
ENV_SESSION_KEY = 'rack.session'.freeze
|
46
|
-
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
47
|
-
|
48
42
|
# SessionHash is responsible to lazily load the session from store.
|
49
43
|
|
50
44
|
class SessionHash
|
51
45
|
include Enumerable
|
52
46
|
attr_writer :id
|
53
47
|
|
54
|
-
|
55
|
-
|
48
|
+
Unspecified = Object.new
|
49
|
+
|
50
|
+
def self.find(req)
|
51
|
+
req.get_header RACK_SESSION
|
56
52
|
end
|
57
53
|
|
58
|
-
def self.set(
|
59
|
-
|
54
|
+
def self.set(req, session)
|
55
|
+
req.set_header RACK_SESSION, session
|
60
56
|
end
|
61
57
|
|
62
|
-
def self.set_options(
|
63
|
-
|
58
|
+
def self.set_options(req, options)
|
59
|
+
req.set_header RACK_SESSION_OPTIONS, options.dup
|
64
60
|
end
|
65
61
|
|
66
|
-
def initialize(store,
|
62
|
+
def initialize(store, req)
|
67
63
|
@store = store
|
68
|
-
@
|
64
|
+
@req = req
|
69
65
|
@loaded = false
|
70
66
|
end
|
71
67
|
|
72
68
|
def id
|
73
69
|
return @id if @loaded or instance_variable_defined?(:@id)
|
74
|
-
@id = @store.send(:extract_session_id, @
|
70
|
+
@id = @store.send(:extract_session_id, @req)
|
75
71
|
end
|
76
72
|
|
77
73
|
def options
|
78
|
-
@
|
74
|
+
@req.session_options
|
79
75
|
end
|
80
76
|
|
81
77
|
def each(&block)
|
@@ -87,7 +83,15 @@ module Rack
|
|
87
83
|
load_for_read!
|
88
84
|
@data[key.to_s]
|
89
85
|
end
|
90
|
-
|
86
|
+
|
87
|
+
def fetch(key, default=Unspecified, &block)
|
88
|
+
load_for_read!
|
89
|
+
if default == Unspecified
|
90
|
+
@data.fetch(key.to_s, &block)
|
91
|
+
else
|
92
|
+
@data.fetch(key.to_s, default, &block)
|
93
|
+
end
|
94
|
+
end
|
91
95
|
|
92
96
|
def has_key?(key)
|
93
97
|
load_for_read!
|
@@ -109,7 +113,7 @@ module Rack
|
|
109
113
|
|
110
114
|
def destroy
|
111
115
|
clear
|
112
|
-
@id = @store.send(:
|
116
|
+
@id = @store.send(:delete_session, @req, id, options)
|
113
117
|
end
|
114
118
|
|
115
119
|
def to_hash
|
@@ -144,7 +148,7 @@ module Rack
|
|
144
148
|
def exists?
|
145
149
|
return @exists if instance_variable_defined?(:@exists)
|
146
150
|
@data = {}
|
147
|
-
@exists = @store.send(:session_exists?, @
|
151
|
+
@exists = @store.send(:session_exists?, @req)
|
148
152
|
end
|
149
153
|
|
150
154
|
def loaded?
|
@@ -157,10 +161,12 @@ module Rack
|
|
157
161
|
end
|
158
162
|
|
159
163
|
def keys
|
164
|
+
load_for_read!
|
160
165
|
@data.keys
|
161
166
|
end
|
162
167
|
|
163
168
|
def values
|
169
|
+
load_for_read!
|
164
170
|
@data.values
|
165
171
|
end
|
166
172
|
|
@@ -175,7 +181,7 @@ module Rack
|
|
175
181
|
end
|
176
182
|
|
177
183
|
def load!
|
178
|
-
@id, session = @store.send(:load_session, @
|
184
|
+
@id, session = @store.send(:load_session, @req)
|
179
185
|
@data = stringify_keys(session)
|
180
186
|
@loaded = true
|
181
187
|
end
|
@@ -191,14 +197,14 @@ module Rack
|
|
191
197
|
|
192
198
|
# ID sets up a basic framework for implementing an id based sessioning
|
193
199
|
# service. Cookies sent to the client for maintaining sessions will only
|
194
|
-
# contain an id reference. Only #
|
200
|
+
# contain an id reference. Only #find_session and #write_session are
|
195
201
|
# required to be overwritten.
|
196
202
|
#
|
197
203
|
# All parameters are optional.
|
198
204
|
# * :key determines the name of the cookie, by default it is
|
199
205
|
# 'rack.session'
|
200
206
|
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
201
|
-
# cookie options as by Rack::Response#
|
207
|
+
# cookie options as by Rack::Response#set_cookie
|
202
208
|
# * :skip will not a set a cookie in the response nor update the session state
|
203
209
|
# * :defer will not set a cookie in the response but still update the session
|
204
210
|
# state if it is used with a backend
|
@@ -209,9 +215,9 @@ module Rack
|
|
209
215
|
# id will be.
|
210
216
|
#
|
211
217
|
# 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.
|
218
|
+
# <tt>env['rack.session.options']</tt>. Additionally the id of the
|
219
|
+
# session can be found within the options hash at the key :id. It is
|
220
|
+
# highly not recommended to change its value.
|
215
221
|
#
|
216
222
|
# Is Rack::Utils::Context compatible.
|
217
223
|
#
|
@@ -220,7 +226,7 @@ module Rack
|
|
220
226
|
|
221
227
|
class Persisted
|
222
228
|
DEFAULT_OPTIONS = {
|
223
|
-
:key =>
|
229
|
+
:key => RACK_SESSION,
|
224
230
|
:path => '/',
|
225
231
|
:domain => nil,
|
226
232
|
:expire_after => nil,
|
@@ -230,10 +236,10 @@ module Rack
|
|
230
236
|
:renew => false,
|
231
237
|
:sidbits => 128,
|
232
238
|
:cookie_only => true,
|
233
|
-
:secure_random =>
|
234
|
-
}
|
239
|
+
:secure_random => ::SecureRandom
|
240
|
+
}.freeze
|
235
241
|
|
236
|
-
attr_reader :key, :default_options
|
242
|
+
attr_reader :key, :default_options, :sid_secure
|
237
243
|
|
238
244
|
def initialize(app, options={})
|
239
245
|
@app = app
|
@@ -248,13 +254,20 @@ module Rack
|
|
248
254
|
end
|
249
255
|
|
250
256
|
def context(env, app=@app)
|
251
|
-
|
252
|
-
|
253
|
-
|
257
|
+
req = make_request env
|
258
|
+
prepare_session(req)
|
259
|
+
status, headers, body = app.call(req.env)
|
260
|
+
res = Rack::Response::Raw.new status, headers
|
261
|
+
commit_session(req, res)
|
262
|
+
[status, headers, body]
|
254
263
|
end
|
255
264
|
|
256
265
|
private
|
257
266
|
|
267
|
+
def make_request(env)
|
268
|
+
Rack::Request.new env
|
269
|
+
end
|
270
|
+
|
258
271
|
def initialize_sid
|
259
272
|
@sidbits = @default_options[:sidbits]
|
260
273
|
@sid_secure = @default_options[:secure_random]
|
@@ -278,26 +291,26 @@ module Rack
|
|
278
291
|
# Sets the lazy session at 'rack.session' and places options and session
|
279
292
|
# metadata into 'rack.session.options'.
|
280
293
|
|
281
|
-
def prepare_session(
|
282
|
-
session_was
|
283
|
-
|
284
|
-
|
285
|
-
|
294
|
+
def prepare_session(req)
|
295
|
+
session_was = req.get_header RACK_SESSION
|
296
|
+
session = session_class.new(self, req)
|
297
|
+
req.set_header RACK_SESSION, session
|
298
|
+
req.set_header RACK_SESSION_OPTIONS, @default_options.dup
|
299
|
+
session.merge! session_was if session_was
|
286
300
|
end
|
287
301
|
|
288
302
|
# Extracts the session id from provided cookies and passes it and the
|
289
|
-
# environment to #
|
303
|
+
# environment to #find_session.
|
290
304
|
|
291
|
-
def load_session(
|
292
|
-
sid = current_session_id(
|
293
|
-
sid, session =
|
305
|
+
def load_session(req)
|
306
|
+
sid = current_session_id(req)
|
307
|
+
sid, session = find_session(req, sid)
|
294
308
|
[sid, session || {}]
|
295
309
|
end
|
296
310
|
|
297
311
|
# Extract session id from request object.
|
298
312
|
|
299
|
-
def extract_session_id(
|
300
|
-
request = Rack::Request.new(env)
|
313
|
+
def extract_session_id(request)
|
301
314
|
sid = request.cookies[@key]
|
302
315
|
sid ||= request.params[@key] unless @cookie_only
|
303
316
|
sid
|
@@ -305,26 +318,26 @@ module Rack
|
|
305
318
|
|
306
319
|
# Returns the current session id from the SessionHash.
|
307
320
|
|
308
|
-
def current_session_id(
|
309
|
-
|
321
|
+
def current_session_id(req)
|
322
|
+
req.get_header(RACK_SESSION).id
|
310
323
|
end
|
311
324
|
|
312
325
|
# Check if the session exists or not.
|
313
326
|
|
314
|
-
def session_exists?(
|
315
|
-
value = current_session_id(
|
327
|
+
def session_exists?(req)
|
328
|
+
value = current_session_id(req)
|
316
329
|
value && !value.empty?
|
317
330
|
end
|
318
331
|
|
319
332
|
# Session should be committed if it was loaded, any of specific options like :renew, :drop
|
320
333
|
# or :expire_after was given and the security permissions match. Skips if skip is given.
|
321
334
|
|
322
|
-
def commit_session?(
|
335
|
+
def commit_session?(req, session, options)
|
323
336
|
if options[:skip]
|
324
337
|
false
|
325
338
|
else
|
326
339
|
has_session = loaded_session?(session) || forced_session_update?(session, options)
|
327
|
-
has_session && security_matches?(
|
340
|
+
has_session && security_matches?(req, options)
|
328
341
|
end
|
329
342
|
end
|
330
343
|
|
@@ -340,46 +353,44 @@ module Rack
|
|
340
353
|
options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
|
341
354
|
end
|
342
355
|
|
343
|
-
def security_matches?(
|
356
|
+
def security_matches?(request, options)
|
344
357
|
return true unless options[:secure]
|
345
|
-
request = Rack::Request.new(env)
|
346
358
|
request.ssl?
|
347
359
|
end
|
348
360
|
|
349
361
|
# Acquires the session from the environment and the session id from
|
350
|
-
# the session options and passes them to #
|
362
|
+
# the session options and passes them to #write_session. If successful
|
351
363
|
# and the :defer option is not true, a cookie will be added to the
|
352
364
|
# response with the session's id.
|
353
365
|
|
354
|
-
def commit_session(
|
355
|
-
session =
|
366
|
+
def commit_session(req, res)
|
367
|
+
session = req.get_header RACK_SESSION
|
356
368
|
options = session.options
|
357
369
|
|
358
370
|
if options[:drop] || options[:renew]
|
359
|
-
session_id =
|
360
|
-
return
|
371
|
+
session_id = delete_session(req, session.id || generate_sid, options)
|
372
|
+
return unless session_id
|
361
373
|
end
|
362
374
|
|
363
|
-
return
|
375
|
+
return unless commit_session?(req, session, options)
|
364
376
|
|
365
377
|
session.send(:load!) unless loaded_session?(session)
|
366
378
|
session_id ||= session.id
|
367
379
|
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
368
380
|
|
369
|
-
if not data =
|
370
|
-
|
381
|
+
if not data = write_session(req, session_id, session_data, options)
|
382
|
+
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
371
383
|
elsif options[:defer] and not options[:renew]
|
372
|
-
|
384
|
+
req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
|
373
385
|
else
|
374
386
|
cookie = Hash.new
|
375
387
|
cookie[:value] = cookie_value(data)
|
376
388
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
377
389
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
378
|
-
set_cookie(
|
390
|
+
set_cookie(req, res, cookie.merge!(options))
|
379
391
|
end
|
380
|
-
|
381
|
-
[status, headers, body]
|
382
392
|
end
|
393
|
+
public :commit_session
|
383
394
|
|
384
395
|
def cookie_value(data)
|
385
396
|
data
|
@@ -388,10 +399,10 @@ module Rack
|
|
388
399
|
# Sets the cookie back to the client with session id. We skip the cookie
|
389
400
|
# setting if the value didn't change (sid is the same) or expires was given.
|
390
401
|
|
391
|
-
def set_cookie(
|
392
|
-
request = Rack::Request.new(env)
|
402
|
+
def set_cookie(request, res, cookie)
|
393
403
|
if request.cookies[@key] != cookie[:value] || cookie[:expires]
|
394
|
-
|
404
|
+
res.set_cookie_header =
|
405
|
+
Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
|
395
406
|
end
|
396
407
|
end
|
397
408
|
|
@@ -406,23 +417,23 @@ module Rack
|
|
406
417
|
# If nil is provided as the session id, generation of a new valid id
|
407
418
|
# should occur within.
|
408
419
|
|
409
|
-
def
|
410
|
-
raise '#
|
420
|
+
def find_session(env, sid)
|
421
|
+
raise '#find_session not implemented.'
|
411
422
|
end
|
412
423
|
|
413
424
|
# All thread safety and session storage procedures should occur here.
|
414
425
|
# Must return the session id if the session was saved successfully, or
|
415
426
|
# false if the session could not be saved.
|
416
427
|
|
417
|
-
def
|
418
|
-
raise '#
|
428
|
+
def write_session(req, sid, session, options)
|
429
|
+
raise '#write_session not implemented.'
|
419
430
|
end
|
420
431
|
|
421
432
|
# All thread safety and session destroy procedures should occur here.
|
422
433
|
# Should return a new session id or nil if options[:drop]
|
423
434
|
|
424
|
-
def
|
425
|
-
raise '#
|
435
|
+
def delete_session(req, sid, options)
|
436
|
+
raise '#delete_session not implemented'
|
426
437
|
end
|
427
438
|
end
|
428
439
|
|
@@ -469,6 +480,30 @@ module Rack
|
|
469
480
|
end
|
470
481
|
super
|
471
482
|
end
|
483
|
+
|
484
|
+
# All thread safety and session retrieval procedures should occur here.
|
485
|
+
# Should return [session_id, session].
|
486
|
+
# If nil is provided as the session id, generation of a new valid id
|
487
|
+
# should occur within.
|
488
|
+
|
489
|
+
def find_session(req, sid)
|
490
|
+
get_session req.env, sid
|
491
|
+
end
|
492
|
+
|
493
|
+
# All thread safety and session storage procedures should occur here.
|
494
|
+
# Must return the session id if the session was saved successfully, or
|
495
|
+
# false if the session could not be saved.
|
496
|
+
|
497
|
+
def write_session(req, sid, session, options)
|
498
|
+
set_session req.env, sid, session, options
|
499
|
+
end
|
500
|
+
|
501
|
+
# All thread safety and session destroy procedures should occur here.
|
502
|
+
# Should return a new session id or nil if options[:drop]
|
503
|
+
|
504
|
+
def delete_session(req, sid, options)
|
505
|
+
destroy_session req.env, sid, options
|
506
|
+
end
|
472
507
|
end
|
473
508
|
end
|
474
509
|
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
|
|
@@ -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
|
|
@@ -160,7 +162,7 @@ module Rack
|
|
160
162
|
end
|
161
163
|
end
|
162
164
|
|
163
|
-
def
|
165
|
+
def write_session(req, session_id, session, options)
|
164
166
|
session = session.merge("session_id" => session_id)
|
165
167
|
session_data = coder.encode(session)
|
166
168
|
|
@@ -169,14 +171,14 @@ module Rack
|
|
169
171
|
end
|
170
172
|
|
171
173
|
if session_data.size > (4096 - @key.size)
|
172
|
-
|
174
|
+
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
173
175
|
nil
|
174
176
|
else
|
175
177
|
SessionId.new(session_id, session_data)
|
176
178
|
end
|
177
179
|
end
|
178
180
|
|
179
|
-
def
|
181
|
+
def delete_session(req, session_id, options)
|
180
182
|
# Nothing to do here, data is in the client
|
181
183
|
generate_sid unless options[:drop]
|
182
184
|
end
|
@@ -189,7 +191,12 @@ module Rack
|
|
189
191
|
end
|
190
192
|
|
191
193
|
def generate_hmac(data, secret)
|
192
|
-
OpenSSL::HMAC.hexdigest(
|
194
|
+
OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
|
195
|
+
end
|
196
|
+
|
197
|
+
def secure?(options)
|
198
|
+
@secrets.size >= 1 ||
|
199
|
+
(options[:coder] && options[:let_coder_handle_secure_encoding])
|
193
200
|
end
|
194
201
|
|
195
202
|
end
|
@@ -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
|
@@ -46,8 +46,8 @@ module Rack
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
50
|
-
with_lock(
|
49
|
+
def find_session(req, sid)
|
50
|
+
with_lock(req) do
|
51
51
|
unless sid and session = get_session_with_fallback(sid)
|
52
52
|
sid, session = generate_sid, {}
|
53
53
|
unless /^STORED/ =~ @pool.add(sid.private_id, session)
|
@@ -58,26 +58,26 @@ module Rack
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
61
|
+
def write_session(req, session_id, new_session, options)
|
62
62
|
expiry = options[:expire_after]
|
63
63
|
expiry = expiry.nil? ? 0 : expiry + 1
|
64
64
|
|
65
|
-
with_lock(
|
65
|
+
with_lock(req) do
|
66
66
|
@pool.set session_id.private_id, new_session, expiry
|
67
67
|
session_id
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
def
|
72
|
-
with_lock(
|
71
|
+
def delete_session(req, session_id, options)
|
72
|
+
with_lock(req) do
|
73
73
|
@pool.delete(session_id.public_id)
|
74
74
|
@pool.delete(session_id.private_id)
|
75
75
|
generate_sid unless options[:drop]
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
def with_lock(
|
80
|
-
@mutex.lock if
|
79
|
+
def with_lock(req)
|
80
|
+
@mutex.lock if req.multithread?
|
81
81
|
yield
|
82
82
|
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
83
83
|
if $VERBOSE
|
data/lib/rack/session/pool.rb
CHANGED
@@ -41,8 +41,8 @@ module Rack
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
with_lock(
|
44
|
+
def find_session(req, sid)
|
45
|
+
with_lock(req) do
|
46
46
|
unless sid and session = get_session_with_fallback(sid)
|
47
47
|
sid, session = generate_sid, {}
|
48
48
|
@pool.store sid.private_id, session
|
@@ -51,23 +51,23 @@ module Rack
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
with_lock(
|
54
|
+
def write_session(req, session_id, new_session, options)
|
55
|
+
with_lock(req) do
|
56
56
|
@pool.store session_id.private_id, new_session
|
57
57
|
session_id
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
62
|
-
with_lock(
|
61
|
+
def delete_session(req, session_id, options)
|
62
|
+
with_lock(req) do
|
63
63
|
@pool.delete(session_id.public_id)
|
64
64
|
@pool.delete(session_id.private_id)
|
65
65
|
generate_sid unless options[:drop]
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
def with_lock(
|
70
|
-
@mutex.lock if
|
69
|
+
def with_lock(req)
|
70
|
+
@mutex.lock if req.multithread?
|
71
71
|
yield
|
72
72
|
ensure
|
73
73
|
@mutex.unlock if @mutex.locked?
|