From ed708307137c811d14e5fd2cb4ea550add381a82 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 15 Dec 2008 16:33:31 -0600 Subject: Switch to Rack based session stores. --- .../action_controller/session/abstract_store.rb | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 actionpack/lib/action_controller/session/abstract_store.rb (limited to 'actionpack/lib/action_controller/session/abstract_store.rb') diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb new file mode 100644 index 0000000000..b23bf062c4 --- /dev/null +++ b/actionpack/lib/action_controller/session/abstract_store.rb @@ -0,0 +1,129 @@ +require 'rack/utils' + +module ActionController + module Session + class AbstractStore + ENV_SESSION_KEY = 'rack.session'.freeze + ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze + + HTTP_COOKIE = 'HTTP_COOKIE'.freeze + SET_COOKIE = 'Set-Cookie'.freeze + + class SessionHash < Hash + def initialize(by, env) + @by = by + @env = env + @loaded = false + end + + def id + load! unless @loaded + @id + end + + def [](key) + load! unless @loaded + super + end + + def []=(key, value) + load! unless @loaded + super + end + + def to_hash + {}.replace(self) + end + + private + def load! + @id, session = @by.send(:load_session, @env) + replace(session) + @loaded = true + end + end + + DEFAULT_OPTIONS = { + :key => 'rack.session', + :path => '/', + :domain => nil, + :expire_after => nil, + :secure => false, + :httponly => true, + :cookie_only => true + } + + def initialize(app, options = {}) + @app = app + @default_options = DEFAULT_OPTIONS.merge(options) + @key = @default_options[:key] + @cookie_only = @default_options[:cookie_only] + end + + def call(env) + session = SessionHash.new(self, env) + original_session = session.dup + + env[ENV_SESSION_KEY] = session + env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup + + response = @app.call(env) + + session = env[ENV_SESSION_KEY] + unless session == original_session + options = env[ENV_SESSION_OPTIONS_KEY] + sid = session.id + + unless set_session(env, sid, session.to_hash) + return response + end + + cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid) + cookie << "; domain=#{options[:domain]}" if options[:domain] + cookie << "; path=#{options[:path]}" if options[:path] + if options[:expire_after] + expiry = Time.now + options[:expire_after] + cookie << "; expires=#{expiry.httpdate}" + end + cookie << "; Secure" if options[:secure] + cookie << "; HttpOnly" if options[:httponly] + + headers = response[1] + case a = headers[SET_COOKIE] + when Array + a << cookie + when String + headers[SET_COOKIE] = [a, cookie] + when nil + headers[SET_COOKIE] = cookie + end + end + + response + end + + private + def generate_sid + ActiveSupport::SecureRandom.hex(16) + end + + def load_session(env) + request = Rack::Request.new(env) + sid = request.cookies[@key] + unless @cookie_only + sid ||= request.params[@key] + end + sid, session = get_session(env, sid) + [sid, session] + end + + def get_session(env, sid) + raise '#get_session needs to be implemented.' + end + + def set_session(env, sid, session_data) + raise '#set_session needs to be implemented.' + end + end + end +end -- cgit v1.2.3