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

default_options[R]
key[R]
on_redis_down[RW]
on_session_load_error[RW]
redis[R]
serializer[R]

Public Class Methods

new(app, options = {}) click to toggle source

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
}
Calls superclass method
# 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

decode(data) click to toggle source
# File lib/redis-session-store.rb, line 123
def decode(data)
  session = serializer.load(data)
  USE_INDIFFERENT_ACCESS ? session.with_indifferent_access : session
end
delete_session(env, sid, options)
Alias for: destroy_session
destroy(env) click to toggle source
# 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
destroy_session(env, sid, options) click to toggle source
# 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
Also aliased as: delete_session
destroy_session_from_sid(sid, options = {}) click to toggle source
# 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
determine_serializer(serializer) click to toggle source
# 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
encode(session_data) click to toggle source
# File lib/redis-session-store.rb, line 147
def encode(session_data)
  serializer.dump(session_data)
end
find_session(env, sid)
Alias for: get_session
get_expiry(env, options) click to toggle source
# 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
get_session(env, sid) click to toggle source
# 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
Also aliased as: find_session
key_exists?(value) click to toggle source
# 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
load_session_from_redis(sid) click to toggle source
# 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
prefixed(sid) click to toggle source
# File lib/redis-session-store.rb, line 96
def prefixed(sid)
  "#{default_options[:key_prefix]}#{sid}"
end
session_default_values() click to toggle source
# File lib/redis-session-store.rb, line 100
def session_default_values
  [generate_sid, USE_INDIFFERENT_ACCESS ? {}.with_indifferent_access : {}]
end
session_exists?(env) click to toggle source

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
set_session(env, sid, session_data, options = nil) click to toggle source
# 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
Also aliased as: write_session
verify_handlers!() click to toggle source
# 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
write_session(env, sid, session_data, options = nil)
Alias for: set_session