redis-rack-1.5.0/0000755000175000017500000000000012436301143015024 5ustar balasankarcbalasankarcredis-rack-1.5.0/Gemfile0000644000175000017500000000013512436301142016315 0ustar balasankarcbalasankarcsource 'https://rubygems.org' gemspec gem 'redis-store', '~> 1.1.0', path: '../redis-store' redis-rack-1.5.0/.gitignore0000644000175000017500000000004612436301142017013 0ustar balasankarcbalasankarc*.gem .bundle Gemfile.lock pkg/* tmp/*redis-rack-1.5.0/Rakefile0000644000175000017500000000021512436301142016466 0ustar balasankarcbalasankarcrequire 'bundler' Bundler.setup require 'rake' require 'bundler/gem_tasks' load 'tasks/redis.tasks.rb' task :default => 'redis:test:suite' redis-rack-1.5.0/metadata.yml0000644000175000017500000000646312436301143017340 0ustar balasankarcbalasankarc--- !ruby/object:Gem::Specification name: redis-rack version: !ruby/object:Gem::Version version: 1.5.0 platform: ruby authors: - Luca Guidi autorequire: bindir: bin cert_chain: [] date: 2013-08-20 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: redis-store requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: 1.1.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: 1.1.0 - !ruby/object:Gem::Dependency name: rack requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.5' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.5' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '10' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '10' - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: mocha requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: 0.14.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: 0.14.0 - !ruby/object:Gem::Dependency name: minitest requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '5' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '5' description: Redis Store for Rack email: - me@lucaguidi.com executables: [] extensions: [] extra_rdoc_files: [] files: - .gitignore - Gemfile - MIT-LICENSE - README.md - Rakefile - lib/rack/session/redis.rb - lib/redis-rack.rb - lib/redis/rack/version.rb - redis-rack.gemspec - test/rack/session/redis_test.rb - test/redis/rack/version_test.rb - test/test_helper.rb homepage: http://redis-store.org/redis-rack licenses: [] metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: redis-rack rubygems_version: 2.0.3 signing_key: specification_version: 4 summary: Redis Store for Rack test_files: - test/rack/session/redis_test.rb - test/redis/rack/version_test.rb - test/test_helper.rb redis-rack-1.5.0/lib/0000755000175000017500000000000012436301142015571 5ustar balasankarcbalasankarcredis-rack-1.5.0/lib/redis/0000755000175000017500000000000012436301142016677 5ustar balasankarcbalasankarcredis-rack-1.5.0/lib/redis/rack/0000755000175000017500000000000012436301142017617 5ustar balasankarcbalasankarcredis-rack-1.5.0/lib/redis/rack/version.rb0000644000175000017500000000007312436301142021631 0ustar balasankarcbalasankarcclass Redis module Rack VERSION = '1.5.0' end end redis-rack-1.5.0/lib/rack/0000755000175000017500000000000012436301142016511 5ustar balasankarcbalasankarcredis-rack-1.5.0/lib/rack/session/0000755000175000017500000000000012436301142020174 5ustar balasankarcbalasankarcredis-rack-1.5.0/lib/rack/session/redis.rb0000644000175000017500000000322012436301142021624 0ustar balasankarcbalasankarcrequire 'rack/session/abstract/id' require 'redis-store' require 'thread' module Rack module Session class Redis < Abstract::ID attr_reader :mutex, :pool DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ :redis_server => 'redis://127.0.0.1:6379/0/rack:session' def initialize(app, options = {}) super @mutex = Mutex.new @pool = ::Redis::Store::Factory.create @default_options[:redis_server] end def generate_sid loop do sid = super break sid unless @pool.get(sid) end end def get_session(env, sid) with_lock(env, [nil, {}]) do unless sid and session = @pool.get(sid) sid, session = generate_sid, {} unless /^OK/ =~ @pool.set(sid, session, @default_options) raise "Session collision on '#{sid.inspect}'" end end [sid, session] end end def set_session(env, session_id, new_session, options) with_lock(env, false) do @pool.set session_id, new_session, options session_id end end def destroy_session(env, session_id, options) with_lock(env) do @pool.del(session_id) generate_sid unless options[:drop] end end def with_lock(env, default=nil) @mutex.lock if env['rack.multithread'] yield rescue Errno::ECONNREFUSED if $VERBOSE warn "#{self} is unable to find Redis server." warn $!.inspect end default ensure @mutex.unlock if @mutex.locked? end end end end redis-rack-1.5.0/lib/redis-rack.rb0000644000175000017500000000012012436301142020133 0ustar balasankarcbalasankarcrequire 'redis-store' require 'redis/rack/version' require 'rack/session/redis' redis-rack-1.5.0/MIT-LICENSE0000644000175000017500000000204512436301142016460 0ustar balasankarcbalasankarcCopyright (c) 2009 - 2013 Luca Guidi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. redis-rack-1.5.0/redis-rack.gemspec0000644000175000017500000000175712436301142020426 0ustar balasankarcbalasankarc# -*- encoding: utf-8 -*- $:.push File.expand_path('../lib', __FILE__) require 'redis/rack/version' Gem::Specification.new do |s| s.name = 'redis-rack' s.version = Redis::Rack::VERSION s.authors = ['Luca Guidi'] s.email = ['me@lucaguidi.com'] s.homepage = "http://redis-store.org/redis-rack" s.summary = %q{Redis Store for Rack} s.description = %q{Redis Store for Rack} s.rubyforge_project = 'redis-rack' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.add_runtime_dependency 'redis-store', '~> 1.1.0' s.add_runtime_dependency 'rack', '~> 1.5' s.add_development_dependency 'rake', '~> 10' s.add_development_dependency 'bundler', '~> 1.3' s.add_development_dependency 'mocha', '~> 0.14.0' s.add_development_dependency 'minitest', '~> 5' end redis-rack-1.5.0/test/0000755000175000017500000000000012436301142016002 5ustar balasankarcbalasankarcredis-rack-1.5.0/test/redis/0000755000175000017500000000000012436301142017110 5ustar balasankarcbalasankarcredis-rack-1.5.0/test/redis/rack/0000755000175000017500000000000012436301142020030 5ustar balasankarcbalasankarcredis-rack-1.5.0/test/redis/rack/version_test.rb0000644000175000017500000000022012436301142023073 0ustar balasankarcbalasankarcrequire 'test_helper' describe Redis::Rack::VERSION do it 'returns current version' do Redis::Rack::VERSION.must_equal '1.5.0' end end redis-rack-1.5.0/test/test_helper.rb0000644000175000017500000000016512436301142020647 0ustar balasankarcbalasankarcrequire 'bundler/setup' require 'minitest/autorun' require 'mocha/setup' require 'rack' require 'rack/session/redis' redis-rack-1.5.0/test/rack/0000755000175000017500000000000012436301142016722 5ustar balasankarcbalasankarcredis-rack-1.5.0/test/rack/session/0000755000175000017500000000000012436301142020405 5ustar balasankarcbalasankarcredis-rack-1.5.0/test/rack/session/redis_test.rb0000644000175000017500000002251712436301142023106 0ustar balasankarcbalasankarcrequire 'test_helper' require 'rack/mock' require 'thread' describe Rack::Session::Redis do session_key = Rack::Session::Redis::DEFAULT_OPTIONS[:key] session_match = /#{session_key}=([0-9a-fA-F]+);/ incrementor = lambda do |env| env["rack.session"]["counter"] ||= 0 env["rack.session"]["counter"] += 1 Rack::Response.new(env["rack.session"].inspect).to_a end drop_session = proc do |env| env['rack.session.options'][:drop] = true incrementor.call(env) end renew_session = proc do |env| env['rack.session.options'][:renew] = true incrementor.call(env) end defer_session = proc do |env| env['rack.session.options'][:defer] = true incrementor.call(env) end # # test Redis connection # Rack::Session::Redis.new(incrementor) # # it "faults on no connection" do # lambda{ # Rack::Session::Redis.new(incrementor, :redis_server => 'nosuchserver') # }.must_raise(Exception) # end it "uses the default Redis server and namespace when not provided" do pool = Rack::Session::Redis.new(incrementor) pool.pool.to_s.must_match(/127\.0\.0\.1:6379 against DB 0 with namespace rack:session$/) end it "uses the specified namespace when provided" do pool = Rack::Session::Redis.new(incrementor, :redis_server => {:namespace => 'test:rack:session'}) pool.pool.to_s.must_match(/namespace test:rack:session$/) end it "uses the specified Redis server when provided" do pool = Rack::Session::Redis.new(incrementor, :redis_server => 'redis://127.0.0.1:6380/1') pool.pool.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/) end it "creates a new cookie" do pool = Rack::Session::Redis.new(incrementor) res = Rack::MockRequest.new(pool).get("/") res["Set-Cookie"].must_include("#{session_key}=") res.body.must_equal('{"counter"=>1}') end it "determines session from a cookie" do pool = Rack::Session::Redis.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get("/") cookie = res["Set-Cookie"] req.get("/", "HTTP_COOKIE" => cookie). body.must_equal('{"counter"=>2}') req.get("/", "HTTP_COOKIE" => cookie). body.must_equal('{"counter"=>3}') end it "determines session only from a cookie by default" do pool = Rack::Session::Redis.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get("/") sid = res["Set-Cookie"][session_match, 1] req.get("/?rack.session=#{sid}"). body.must_equal('{"counter"=>1}') req.get("/?rack.session=#{sid}"). body.must_equal('{"counter"=>1}') end it "determines session from params" do pool = Rack::Session::Redis.new(incrementor, :cookie_only => false) req = Rack::MockRequest.new(pool) res = req.get("/") sid = res["Set-Cookie"][session_match, 1] req.get("/?rack.session=#{sid}"). body.must_equal('{"counter"=>2}') req.get("/?rack.session=#{sid}"). body.must_equal('{"counter"=>3}') end it "survives nonexistant cookies" do bad_cookie = "rack.session=blarghfasel" pool = Rack::Session::Redis.new(incrementor) res = Rack::MockRequest.new(pool). get("/", "HTTP_COOKIE" => bad_cookie) res.body.must_equal('{"counter"=>1}') cookie = res["Set-Cookie"][session_match] cookie.wont_match(/#{bad_cookie}/) end it "maintains freshness" do pool = Rack::Session::Redis.new(incrementor, :expire_after => 3) res = Rack::MockRequest.new(pool).get('/') res.body.must_include('"counter"=>1') cookie = res["Set-Cookie"] sid = cookie[session_match, 1] res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie) res["Set-Cookie"][session_match, 1].must_equal(sid) res.body.must_include('"counter"=>2') puts 'Sleeping to expire session' if $DEBUG sleep 4 res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie) res["Set-Cookie"][session_match, 1].wont_equal(sid) res.body.must_include('"counter"=>1') end it "does not send the same session id if it did not change" do pool = Rack::Session::Redis.new(incrementor) req = Rack::MockRequest.new(pool) res0 = req.get("/") cookie = res0["Set-Cookie"] res0.body.must_equal('{"counter"=>1}') res1 = req.get("/", "HTTP_COOKIE" => cookie) res1["Set-Cookie"].must_be_nil res1.body.must_equal('{"counter"=>2}') res2 = req.get("/", "HTTP_COOKIE" => cookie) res2["Set-Cookie"].must_be_nil res2.body.must_equal('{"counter"=>3}') end it "deletes cookies with :drop option" do pool = Rack::Session::Redis.new(incrementor) req = Rack::MockRequest.new(pool) drop = Rack::Utils::Context.new(pool, drop_session) dreq = Rack::MockRequest.new(drop) res1 = req.get("/") session = (cookie = res1["Set-Cookie"])[session_match] res1.body.must_equal('{"counter"=>1}') res2 = dreq.get("/", "HTTP_COOKIE" => cookie) res2["Set-Cookie"].must_be_nil res2.body.must_equal('{"counter"=>2}') res3 = req.get("/", "HTTP_COOKIE" => cookie) res3["Set-Cookie"][session_match].wont_equal(session) res3.body.must_equal('{"counter"=>1}') end it "provides new session id with :renew option" do pool = Rack::Session::Redis.new(incrementor) req = Rack::MockRequest.new(pool) renew = Rack::Utils::Context.new(pool, renew_session) rreq = Rack::MockRequest.new(renew) res1 = req.get("/") session = (cookie = res1["Set-Cookie"])[session_match] res1.body.must_equal('{"counter"=>1}') res2 = rreq.get("/", "HTTP_COOKIE" => cookie) new_cookie = res2["Set-Cookie"] new_session = new_cookie[session_match] new_session.wont_equal(session) res2.body.must_equal('{"counter"=>2}') res3 = req.get("/", "HTTP_COOKIE" => new_cookie) res3.body.must_equal('{"counter"=>3}') # Old cookie was deleted res4 = req.get("/", "HTTP_COOKIE" => cookie) res4.body.must_equal('{"counter"=>1}') end it "omits cookie with :defer option" do pool = Rack::Session::Redis.new(incrementor) defer = Rack::Utils::Context.new(pool, defer_session) dreq = Rack::MockRequest.new(defer) res0 = dreq.get("/") res0["Set-Cookie"].must_be_nil res0.body.must_equal('{"counter"=>1}') end it "updates deep hashes correctly" do hash_check = proc do |env| session = env['rack.session'] unless session.include? 'test' session.update :a => :b, :c => { :d => :e }, :f => { :g => { :h => :i} }, 'test' => true else session[:f][:g][:h] = :j end [200, {}, [session.inspect]] end pool = Rack::Session::Redis.new(hash_check) req = Rack::MockRequest.new(pool) res0 = req.get("/") session_id = (cookie = res0["Set-Cookie"])[session_match, 1] ses0 = pool.pool.get(session_id) req.get("/", "HTTP_COOKIE" => cookie) ses1 = pool.pool.get(session_id) ses1.wont_equal(ses0) end # anyone know how to do this better? it "cleanly merges sessions when multithreaded" do unless $DEBUG 1.must_equal(1) # fake assertion to appease the mighty bacon next end warn 'Running multithread test for Session::Redis' pool = Rack::Session::Redis.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get('/') res.body.must_equal('{"counter"=>1}') cookie = res["Set-Cookie"] session_id = cookie[session_match, 1] delta_incrementor = lambda do |env| # emulate disconjoinment of threading env['rack.session'] = env['rack.session'].dup Thread.stop env['rack.session'][(Time.now.usec*rand).to_i] = true incrementor.call(env) end tses = Rack::Utils::Context.new pool, delta_incrementor treq = Rack::MockRequest.new(tses) tnum = rand(7).to_i+5 r = Array.new(tnum) do Thread.new(treq) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |request| request['Set-Cookie'].must_equal(cookie) request.body.must_include('"counter"=>2') end session = pool.pool.get(session_id) session.size.must_equal(tnum+1) # counter session['counter'].must_equal(2) # meeeh tnum = rand(7).to_i+5 r = Array.new(tnum) do |i| app = Rack::Utils::Context.new pool, time_delta req = Rack::MockRequest.new app Thread.new(req) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |request| request['Set-Cookie'].must_equal(cookie) request.body.must_include('"counter"=>3') end session = pool.pool.get(session_id) session.size.must_equal(tnum+1) session['counter'].must_equal(3) drop_counter = proc do |env| env['rack.session'].delete 'counter' env['rack.session']['foo'] = 'bar' [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect] end tses = Rack::Utils::Context.new pool, drop_counter treq = Rack::MockRequest.new(tses) tnum = rand(7).to_i+5 r = Array.new(tnum) do Thread.new(treq) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |request| request['Set-Cookie'].must_equal(cookie) request.body.must_include('"foo"=>"bar"') end session = pool.pool.get(session_id) session.size.must_equal(r.size+1) session['counter'].must_be_nil session['foo'].must_equal('bar') end end redis-rack-1.5.0/README.md0000644000175000017500000000161612436301142016306 0ustar balasankarcbalasankarc# Redis stores for Rack __`redis-rack`__ provides a Redis backed session store __Rack__. See the main [redis-store readme](https://github.com/jodosha/redis-store) for general guidelines. ## Installation # Gemfile gem 'redis-rack' ### Usage If you are using redis-store with Rails, consider using the [redis-rails gem](https://github.com/jodosha/redis-store/tree/master/redis-rails) instead. For standalone usage: # config.ru require 'rack' require 'rack/session/redis' use Rack::Session::Redis ## Running tests gem install bundler git clone git://github.com/jodosha/redis-store.git cd redis-store/redis-rack bundle install bundle exec rake If you are on **Snow Leopard** you have to run `env ARCHFLAGS="-arch x86_64" bundle exec rake` ## Copyright (c) 2009 - 2013 Luca Guidi - [http://lucaguidi.com](http://lucaguidi.com), released under the MIT license