lib/rsence/sessionstorage.rb in rsence-pre-2.3.0.9 vs lib/rsence/sessionstorage.rb in rsence-pre-2.3.0.10

- old
+ new

@@ -8,23 +8,25 @@ module RSence - require 'rubygems' - require 'sequel' - ## HValue class for session restoration require 'rsence/value' - + + if RSence.config[:database][:ses_db].start_with? 'mongodb://' + require 'rsence/session/mongo_sessionstorage' + SessionBackend = MongoSessionStorage + else + require 'rsence/session/sequel_sessionstorage' + SessionBackend = SequelSessionStorage + end + # SessionStorage doesn't do anything by itself, it's simply - # the superclass for SessionManager that does all the boring - # housekeeping duties. - # - # Spliced of as a separate file to reduce the complexity - # of SessionManager. - class SessionStorage + # the superclass for SessionManager that handles the persistent + # storage. + class SessionStorage < SessionBackend attr_accessor :db def initialize ## Session data storage (by ses_id) @sessions = {} @@ -55,245 +57,83 @@ else @db_avail = false puts "Warning: Session database is not available. Can't use persistent sessions." @id_counter = 0 end - @accept_requests = true - end - + attr_reader :db_avail attr_reader :accept_requests - - def db_test - begin - db_open - if @db.table_exists?(:rsence_test) - @db.drop_table(:rsence_test) - end - @db.create_table(:rsence_test) { primary_key :id; String :test } - test_id = @db[:rsence_test].insert( :test => 'TestFoo' ) - @db[:rsence_test].filter( :id => test_id ).update( :test => 'TestFoo2' ) - @db[:rsence_test].filter( :id => test_id ).delete - @db[:rsence_test].delete - @db.drop_table(:rsence_test) - db_close - return true - rescue => e - if RSence.args[:debug] - err_msg = [ - "ERROR: SessionStorage couldn't open database", - "#{e.class.to_s}, #{e.message}", - "Backtrace:", - "\t"+e.backtrace.join("\n\t") - ].join("\n")+"\n" - $stderr.write( err_msg ) - elsif RSence.args[:verbose] - puts "Failed to open database '#{@db_uri}'." - puts "Run RSence in debug mode for full error output." - end - return false - end - end - - def db_close - @db.disconnect - end - - def db_open - # work-around for windows (drive letters causing confusion) - if @db_uri.start_with?('sqlite://') - @db = Sequel.sqlite( @db_uri.split('sqlite://')[1] ) + + ## Returns a new, unique session identifier by storing the params to the database + def new_ses_id( cookie_key, ses_key, timeout_secs, user_id=0 ) + if @db_avail + return insert_session_data( { + :cookie_key => cookie_key, + :ses_key => ses_key, + :ses_timeout => timeout_secs, + :user_id => user_id + } ) else - @db = Sequel.connect(@db_uri) + @id_counter += 1 + return @id_counter end end - - ## Creates the 'rsence_session' table, if necessary - ## This table is used to store sessions - def create_session_table - db_open - unless @db.table_exists?(:rsence_session) - puts "Creating session table..." if RSence.args[:verbose] - @db.create_table :rsence_session do - primary_key( :id ) - column( :cookie_key, String ) - column( :ses_key, String ) - column( :ses_timeout, Integer ) - column( :user_id, Integer ) - column( :ses_active, TrueClass ) - column( :ses_stored, Integer ) - column( :ses_data, File ) - end - end - db_close - end - - ## Creates the 'rsence_version' table, if necessary - ## This table is used to check for the need of future database upgrades - def create_version_table - db_open - unless @db.table_exists?(:rsence_version) - puts "Creating version info table..." if RSence.args[:verbose] - @db.create_table :rsence_version do - Integer :version - end - @db[:rsence_version].insert(:version => 586) - end - db_close - end - - ## Creates the 'rsence_uploads' table, if necessary - ## This table is used for storing temporary uploads before processing - def create_uploads_table - db_open - unless @db.table_exists?(:rsence_uploads) - puts "Creating uploads table..." if RSence.args[:verbose] - @db.create_table :rsence_uploads do - primary_key( :id ) - foreign_key( :ses_id, :rsence_session ) - column( :upload_date, Integer ) - column( :upload_done, Integer ) - column( :ticket_id, String ) - column( :file_size, Integer ) - column( :file_name, String ) - column( :file_mime, String ) - column( :file_data, File ) - end - end - db_close - end - - # returns the version in the rsence_version table - def table_version - db_open - rsence_version = @db[:rsence_version].select(:version).all[0][:version] - db_close - return rsence_version - end - - ## Checks database connectivity and loads stored sessions from the database - def db_init - - create_session_table - create_version_table - create_uploads_table - - ## Used for future upgrades: - # version = table_version - - return true - end - - ## Deletes all rows from rsence_session as well as rsence_uploads - def reset_sessions - unless @db_avail - puts "Warning: Can't reset sessions: No database!" if RSence.args[:verbose] - return - end - db_open - @db[:rsence_session].delete if @db.table_exists?(:rsence_session) - @db[:rsence_uploads].delete if @db.table_exists?(:rsence_uploads) - db_close - end - - ## Restores all saved sessions from db to ram - def restore_sessions - unless @db_avail - puts "Warning: Can't restore sessions: No database!" if RSence.args[:verbose] - return - end - puts "Restoring sessions..." if RSence.args[:verbose] - db_open - @db[:rsence_session].all do |ses_row| - ses_id = ses_row[:id] - ses_data_dump = ses_row[:ses_data] - - if ses_data_dump == nil - @db[:rsence_session].filter(:id => ses_id).delete - @db[:rsence_uploads].filter(:ses_id => ses_id).delete - else - begin - ses_data = Marshal.load( ses_data_dump ) - ses_key = ses_data[:ses_key] - @sessions[ses_id] = ses_data - @session_keys[ ses_key ] = ses_id - @session_cookie_keys[ ses_data[:cookie_key] ] = ses_id - if @plugins - @plugins.delegate( :load_ses_id, ses_id ) - @plugins.delegate( :load_ses, ses_data ) - end - rescue => e - warn "Unable to load session: #{ses_id}, because: #{e.message}" - @db[:rsence_session].filter(:id => ses_id).delete - @db[:rsence_uploads].filter(:ses_id => ses_id).delete - end - end - end - db_close - end - + ## Stores all sessions to db from ram def store_sessions unless @db_avail puts "Warning: Can't store sessions: No database!" if RSence.args[:verbose] return end puts "Storing sessions..." if RSence.args[:verbose] - db_open ses_ids = @sessions.keys ses_ids.each do |ses_id| - ses_data = @sessions[ses_id] + ses_data = @sessions[ses_id].clone if @plugins @plugins.delegate( :dump_ses, ses_data ) @plugins.delegate( :dump_ses_id, ses_id ) end begin - ses_data_dump = Marshal.dump( ses_data ) - @db[:rsence_session].filter( - :id => ses_id - ).update( - :cookie_key => ses_data[:cookie_key], - :ses_key => ses_data[:ses_key], - :user_id => ses_data[:user_id], - :ses_data => ses_data_dump.to_sequel_blob, - :ses_timeout => ses_data[:timeout], - :ses_stored => Time.now.to_i - ) + store_session_data( ses_data ) rescue => e warn "Unable to dump session: #{ses_id}, because: #{e.message}" end end - db_close end - - ## Shut-down signal, triggers store_sessions for now - def shutdown - @accept_requests = false - puts "Session shutdown in progress..." if RSence.args[:verbose] - store_sessions - puts "Session shutdown complete." if RSence.args[:verbose] + + ## Restores a single session, called from the database backend + def restore_session( ses_id, ses_data ) + ses_key = ses_data[:ses_key] + @sessions[ses_id] = ses_data + @session_keys[ ses_key ] = ses_id + @session_cookie_keys[ ses_data[:cookie_key] ] = ses_id + if @plugins + @plugins.delegate( :load_ses_id, ses_id ) + @plugins.delegate( :load_ses, ses_data ) + end end - - - ## Returns a new, unique session identifier by storing the params to the database - def new_ses_id( cookie_key, ses_key, timeout_secs, user_id=0 ) + + ## Deletes all rows from rsence_session as well as rsence_uploads + def reset_sessions unless @db_avail - @id_counter += 1 - return @id_counter + puts "Warning: Can't reset sessions: No database!" if RSence.args[:verbose] + return end - db_open - new_id = @db[:rsence_session].insert( - :cookie_key => cookie_key, - :ses_key => ses_key, - :ses_timeout => timeout_secs, - :user_id => user_id - ) - db_close - return new_id + remove_all_session_data end - + + ## Restores all saved sessions from db to ram + def restore_sessions + unless @db_avail + puts "Warning: Can't restore sessions: No database!" if RSence.args[:verbose] + return + end + puts "Restoring sessions..." if RSence.args[:verbose] + load_session_data + end + ## Expires a session by its identifier def expire_session( ses_id ) return unless @sessions.has_key? ses_id @@ -328,19 +168,15 @@ end @clone_targets.delete( ses_id ) if @clone_targets.has_key?( ses_id ) end if @db_avail - db_open - # Deletes the session's row from the database - @db[:rsence_uploads].filter(:ses_id => ses_id).delete - @db[:rsence_session].filter(:id => ses_id).delete - db_close + remove_session_data( ses_id ) end end - + ## Expires all sessions that meet the timeout criteria def expire_sessions # Loop through all sessions in memory: @sessions.each_key do |ses_id| @@ -349,8 +185,16 @@ ## Deletes the session, if the session is too old expire_session( ses_id ) if timed_out end + end + + ## Shut-down signal, triggers store_sessions for now + def shutdown + @accept_requests = false + puts "Session shutdown in progress..." if RSence.args[:verbose] + store_sessions + puts "Session shutdown complete." if RSence.args[:verbose] end end end