Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / vendor / rack-1.0 / rack / session / cookie.rb
1 require 'openssl'
2 require 'rack/request'
3 require 'rack/response'
4
5 module Rack
6
7 module Session
8
9 # Rack::Session::Cookie provides simple cookie based session management.
10 # The session is a Ruby Hash stored as base64 encoded marshalled data
11 # set to :key (default: rack.session).
12 # When the secret key is set, cookie data is checked for data integrity.
13 #
14 # Example:
15 #
16 # use Rack::Session::Cookie, :key => 'rack.session',
17 # :domain => 'foo.com',
18 # :path => '/',
19 # :expire_after => 2592000,
20 # :secret => 'change_me'
21 #
22 # All parameters are optional.
23
24 class Cookie
25
26 def initialize(app, options={})
27 @app = app
28 @key = options[:key] || "rack.session"
29 @secret = options[:secret]
30 @default_options = {:domain => nil,
31 :path => "/",
32 :expire_after => nil}.merge(options)
33 end
34
35 def call(env)
36 load_session(env)
37 status, headers, body = @app.call(env)
38 commit_session(env, status, headers, body)
39 end
40
41 private
42
43 def load_session(env)
44 request = Rack::Request.new(env)
45 session_data = request.cookies[@key]
46
47 if @secret && session_data
48 session_data, digest = session_data.split("--")
49 session_data = nil unless digest == generate_hmac(session_data)
50 end
51
52 begin
53 session_data = session_data.unpack("m*").first
54 session_data = Marshal.load(session_data)
55 env["rack.session"] = session_data
56 rescue
57 env["rack.session"] = Hash.new
58 end
59
60 env["rack.session.options"] = @default_options.dup
61 end
62
63 def commit_session(env, status, headers, body)
64 session_data = Marshal.dump(env["rack.session"])
65 session_data = [session_data].pack("m*")
66
67 if @secret
68 session_data = "#{session_data}--#{generate_hmac(session_data)}"
69 end
70
71 if session_data.size > (4096 - @key.size)
72 env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
73 [status, headers, body]
74 else
75 options = env["rack.session.options"]
76 cookie = Hash.new
77 cookie[:value] = session_data
78 cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
79 response = Rack::Response.new(body, status, headers)
80 response.set_cookie(@key, cookie.merge(options))
81 response.to_a
82 end
83 end
84
85 def generate_hmac(data)
86 OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
87 end
88
89 end
90 end
91 end