class RedisSessionStore
Redis session storage for Rails, and for Rails only. Derived from the MemCacheStore code, simply dropping in Redis instead.
Constants
- ENV_SESSION_OPTIONS_KEY
- USE_INDIFFERENT_ACCESS
- VERSION
Attributes
Public Class Methods
Options¶ ↑
-
:key
- Same as with the other cookie stores, key name -
:redis
- A hash with redis-specific options-
:url
- Redis url, default is redis://localhost:6379/0 -
:key_prefix
- Prefix for keys used in Redis, e.g.myapp:
-
:expire_after
- A number in seconds for session timeout -
:client
- Connect to Redis with given object rather than create one
-
-
:on_redis_down:
- Called with err, env, and SID on Errno::ECONNREFUSED -
:on_session_load_error:
- Called with err and SID on Marshal.load fail -
:serializer:
- Serializer to use on session data, default is :marshal.
Examples¶ ↑
Rails.application.config.session_store :redis_session_store, { key: 'your_session_key', redis: { expire_after: 120.minutes, key_prefix: 'myapp:session:', url: 'redis://localhost:6379/0' }, on_redis_down: ->(*a) { logger.error("Redis down! #{a.inspect}") } serializer: :hybrid # migrate from Marshal to JSON }
# File lib/redis-session-store.rb, line 41 def initialize(app, options = {}) super redis_options = options[:redis] || {} @default_options[:namespace] = 'rack:session' @default_options.merge!(redis_options) @redis = redis_options[:client] || Redis.new(redis_options) @on_redis_down = options[:on_redis_down] @serializer = determine_serializer(options[:serializer]) @on_session_load_error = options[:on_session_load_error] verify_handlers! end
Private Instance Methods
# File lib/redis-session-store.rb, line 123 def decode(data) session = serializer.load(data) USE_INDIFFERENT_ACCESS ? session.with_indifferent_access : session end
# File lib/redis-session-store.rb, line 156 def destroy(env) if env['rack.request.cookie_hash'] && (sid = env['rack.request.cookie_hash'][key]) destroy_session_from_sid(sid, drop: true, env: env) end false end
# File lib/redis-session-store.rb, line 151 def destroy_session(env, sid, options) destroy_session_from_sid(sid, (options || {}).to_hash.merge(env: env)) end
# File lib/redis-session-store.rb, line 164 def destroy_session_from_sid(sid, options = {}) redis.del(prefixed(sid)) (options || {})[:drop] ? nil : generate_sid rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e on_redis_down.call(e, options[:env] || {}, sid) if on_redis_down end
# File lib/redis-session-store.rb, line 171 def determine_serializer(serializer) serializer ||= :marshal case serializer when :marshal then Marshal when :json then JsonSerializer when :hybrid then HybridSerializer else serializer end end
# File lib/redis-session-store.rb, line 147 def encode(session_data) serializer.dump(session_data) end
# File lib/redis-session-store.rb, line 142 def get_expiry(env, options) session_storage_options = options || env.fetch(ENV_SESSION_OPTIONS_KEY, {}) session_storage_options[:ttl] || session_storage_options[:expire_after] end
# File lib/redis-session-store.rb, line 104 def get_session(env, sid) sid && (session = load_session_from_redis(sid)) ? [sid, session] : session_default_values rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e on_redis_down.call(e, env, sid) if on_redis_down session_default_values end
# File lib/redis-session-store.rb, line 78 def key_exists?(value) if redis.respond_to?(:exists?) # added in redis gem v4.2 redis.exists?(prefixed(value)) else # older method, will return an integer starting in redis gem v4.3 redis.exists(prefixed(value)) end end
# File lib/redis-session-store.rb, line 112 def load_session_from_redis(sid) data = redis.get(prefixed(sid)) begin data ? decode(data) : nil rescue StandardError => e destroy_session_from_sid(sid, drop: true) on_session_load_error.call(e, sid) if on_session_load_error nil end end
# File lib/redis-session-store.rb, line 96 def prefixed(sid) "#{default_options[:key_prefix]}#{sid}" end
# File lib/redis-session-store.rb, line 100 def session_default_values [generate_sid, USE_INDIFFERENT_ACCESS ? {}.with_indifferent_access : {}] end
overrides method defined in rack to actually verify session existence Prevents needless new sessions from being created in scenario where user HAS session id, but it already expired, or is invalid for some other reason, and session was accessed only for reading.
# File lib/redis-session-store.rb, line 65 def session_exists?(env) value = current_session_id(env) !!( value && !value.empty? && key_exists?(value) ) rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e on_redis_down.call(e, env, value) if on_redis_down true end
# File lib/redis-session-store.rb, line 128 def set_session(env, sid, session_data, options = nil) expiry = get_expiry(env, options) if expiry redis.setex(prefixed(sid), expiry, encode(session_data)) else redis.set(prefixed(sid), encode(session_data)) end sid rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e on_redis_down.call(e, env, sid) if on_redis_down false end
# File lib/redis-session-store.rb, line 88 def verify_handlers! %w(on_redis_down on_session_load_error).each do |h| next unless (handler = public_send(h)) && !handler.respond_to?(:call) raise ArgumentError, "#{h} handler is not callable" end end