Init version of session unit tests.
This commit is contained in:
parent
2416f0ecde
commit
34a300238e
3 changed files with 482 additions and 144 deletions
454
lib/sessions.rb
454
lib/sessions.rb
|
@ -18,6 +18,18 @@ module Sessions
|
|||
@@user_threads = {}
|
||||
@@client_threads = {}
|
||||
|
||||
=begin
|
||||
|
||||
start new session
|
||||
|
||||
Sessions.create( client_id, session_data, { :type => 'websocket' } )
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.create( client_id, session, meta )
|
||||
path = @path + '/' + client_id.to_s
|
||||
FileUtils.mkpath path
|
||||
|
@ -29,7 +41,6 @@ module Sessions
|
|||
},
|
||||
:meta => meta,
|
||||
}
|
||||
# puts 'CREATE' + Marshal.dump(data)
|
||||
file.write Marshal.dump(data)
|
||||
}
|
||||
|
||||
|
@ -42,6 +53,307 @@ module Sessions
|
|||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
list of all session
|
||||
|
||||
client_ids = Sessions.sessions
|
||||
|
||||
returns
|
||||
|
||||
['4711', '4712']
|
||||
|
||||
=end
|
||||
|
||||
def self.sessions
|
||||
path = @path + '/'
|
||||
|
||||
# just make sure that spool path exists
|
||||
if !File::exists?( path )
|
||||
FileUtils.mkpath path
|
||||
end
|
||||
|
||||
data = []
|
||||
Dir.foreach( path ) do |entry|
|
||||
next if entry == '.' || entry == '..' || entry == 'spool'
|
||||
data.push entry.to_s
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
list of all session
|
||||
|
||||
Sessions.session_exists?(client_id)
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.session_exists?(client_id)
|
||||
client_ids = self.sessions
|
||||
client_ids.include? client_id.to_s
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
list of all session with data
|
||||
|
||||
client_ids_with_data = Sessions.list
|
||||
|
||||
returns
|
||||
|
||||
{
|
||||
'4711' => {
|
||||
:user => {
|
||||
:id => 123,
|
||||
},
|
||||
:meta => {
|
||||
:type => 'websocket',
|
||||
:last_ping => time_of_last_ping,
|
||||
}
|
||||
},
|
||||
'4712' => {
|
||||
:user => {
|
||||
:id => 124,
|
||||
},
|
||||
:meta => {
|
||||
:type => 'ajax',
|
||||
:last_ping => time_of_last_ping,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
=end
|
||||
|
||||
def self.list
|
||||
client_ids = self.sessions
|
||||
session_list = {}
|
||||
client_ids.each { |client_id|
|
||||
data = self.get(client_id)
|
||||
next if !data
|
||||
session_list[client_id] = data
|
||||
}
|
||||
session_list
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
destroy session
|
||||
|
||||
Sessions.destory?(client_id)
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.destory( client_id )
|
||||
path = @path + '/' + client_id.to_s
|
||||
FileUtils.rm_rf path
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
destroy idle session
|
||||
|
||||
list_of_client_ids = Sessions.destory_idle_sessions
|
||||
|
||||
returns
|
||||
|
||||
['4711', '4712']
|
||||
|
||||
=end
|
||||
|
||||
def self.destory_idle_sessions(idle_time_in_min = 4)
|
||||
list_of_closed_sessions = []
|
||||
clients = Sessions.list
|
||||
clients.each { |client_id, client|
|
||||
if ( client[:meta][:last_ping].to_i + ( 60 * idle_time_in_min ) ) < Time.now.to_i
|
||||
list_of_closed_sessions.push client_id
|
||||
Sessions.destory( client_id )
|
||||
end
|
||||
}
|
||||
list_of_closed_sessions
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
touch session
|
||||
|
||||
Sessions.touch(client_id)
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.touch( client_id )
|
||||
data = self.get(client_id)
|
||||
return false if !data
|
||||
path = @path + '/' + client_id.to_s
|
||||
data[:meta][:last_ping] = Time.new.to_i.to_s
|
||||
File.open( path + '/session', 'wb' ) { |file|
|
||||
file.write Marshal.dump(data)
|
||||
}
|
||||
true
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
get session data
|
||||
|
||||
data = Sessions.get(client_id)
|
||||
|
||||
returns
|
||||
|
||||
{
|
||||
:user => {
|
||||
:id => 123,
|
||||
},
|
||||
:meta => {
|
||||
:type => 'websocket',
|
||||
:last_ping => time_of_last_ping,
|
||||
}
|
||||
}
|
||||
|
||||
=end
|
||||
|
||||
def self.get( client_id )
|
||||
session_file = @path + '/' + client_id.to_s + '/session'
|
||||
data = nil
|
||||
return if !File.exist? session_file
|
||||
begin
|
||||
File.open( session_file, 'rb' ) { |file|
|
||||
file.flock( File::LOCK_EX )
|
||||
all = file.read
|
||||
file.flock( File::LOCK_UN )
|
||||
data = Marshal.load( all )
|
||||
}
|
||||
rescue Exception => e
|
||||
File.delete(session_file)
|
||||
puts "Error reading '#{session_file}':"
|
||||
puts e.inspect
|
||||
return
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
send message to client
|
||||
|
||||
Sessions.send(client_id_of_recipient, data)
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.send( client_id, data )
|
||||
path = @path + '/' + client_id.to_s + '/'
|
||||
filename = 'send-' + Time.new().to_f.to_s# + '-' + rand(99999999).to_s
|
||||
check = true
|
||||
count = 0
|
||||
while check
|
||||
if File::exists?( path + filename )
|
||||
count += 1
|
||||
filename = filename + '-' + count
|
||||
else
|
||||
check = false
|
||||
end
|
||||
end
|
||||
return false if !File.directory? path
|
||||
File.open( path + 'a-' + filename, 'wb' ) { |file|
|
||||
file.flock( File::LOCK_EX )
|
||||
file.write data.to_json
|
||||
file.flock( File::LOCK_UN )
|
||||
file.close
|
||||
}
|
||||
return false if !File.exists?( path + 'a-' + filename )
|
||||
FileUtils.mv( path + 'a-' + filename, path + filename )
|
||||
true
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
send message to all client
|
||||
|
||||
Sessions.broadcast(data)
|
||||
|
||||
returns
|
||||
|
||||
true|false
|
||||
|
||||
=end
|
||||
|
||||
def self.broadcast( data )
|
||||
|
||||
# list all current clients
|
||||
client_list = self.sessions
|
||||
client_list.each {|client_id|
|
||||
Sessions.send( client_id, data )
|
||||
}
|
||||
true
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
get messages for client
|
||||
|
||||
messages = Sessions.queue(client_id_of_recipient)
|
||||
|
||||
returns
|
||||
|
||||
[
|
||||
{
|
||||
key1 => 'some data of message 1',
|
||||
key2 => 'some data of message 1',
|
||||
},
|
||||
{
|
||||
key1 => 'some data of message 2',
|
||||
key2 => 'some data of message 2',
|
||||
},
|
||||
]
|
||||
|
||||
=end
|
||||
|
||||
def self.queue( client_id )
|
||||
path = @path + '/' + client_id.to_s + '/'
|
||||
data = []
|
||||
files = []
|
||||
Dir.foreach( path ) {|entry|
|
||||
next if entry == '.' || entry == '..'
|
||||
files.push entry
|
||||
}
|
||||
files.sort.each {|entry|
|
||||
filename = path + '/' + entry
|
||||
if /^send/.match( entry )
|
||||
data.push Sessions.queue_file_read( path, entry )
|
||||
end
|
||||
}
|
||||
data
|
||||
end
|
||||
|
||||
def self.queue_file_read( path, filename )
|
||||
file_old = path + filename
|
||||
file_new = path + 'a-' + filename
|
||||
FileUtils.mv( file_old, file_new )
|
||||
data = nil
|
||||
all = ''
|
||||
File.open( file_new, 'rb' ) { |file|
|
||||
all = file.read
|
||||
}
|
||||
File.delete( file_new )
|
||||
JSON.parse( all )
|
||||
end
|
||||
|
||||
def self.spool_create( msg )
|
||||
path = @path + '/spool/'
|
||||
FileUtils.mkpath path
|
||||
|
@ -51,7 +363,6 @@ module Sessions
|
|||
:msg => msg,
|
||||
:timestamp => Time.now.to_i,
|
||||
}
|
||||
# puts 'CREATE' + Marshal.dump(data)
|
||||
file.write data.to_json
|
||||
}
|
||||
end
|
||||
|
@ -120,75 +431,6 @@ module Sessions
|
|||
return data
|
||||
end
|
||||
|
||||
def self.list
|
||||
client_ids = self.sessions
|
||||
session_list = {}
|
||||
client_ids.each { |client_id|
|
||||
data = self.get(client_id)
|
||||
next if !data
|
||||
session_list[client_id] = data
|
||||
}
|
||||
return session_list
|
||||
end
|
||||
|
||||
def self.touch( client_id )
|
||||
data = self.get(client_id)
|
||||
return if !data
|
||||
path = @path + '/' + client_id.to_s
|
||||
data[:meta][:last_ping] = Time.new.to_i.to_s
|
||||
File.open( path + '/session', 'wb' ) { |file|
|
||||
file.write Marshal.dump(data)
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
def self.get( client_id )
|
||||
session_file = @path + '/' + client_id.to_s + '/session'
|
||||
data = nil
|
||||
return if !File.exist? session_file
|
||||
begin
|
||||
File.open( session_file, 'rb' ) { |file|
|
||||
file.flock( File::LOCK_EX )
|
||||
all = file.read
|
||||
file.flock( File::LOCK_UN )
|
||||
data = Marshal.load( all )
|
||||
}
|
||||
rescue Exception => e
|
||||
File.delete(session_file)
|
||||
puts "Error reading '#{session_file}':"
|
||||
puts e.inspect
|
||||
return
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
def self.send( client_id, data )
|
||||
path = @path + '/' + client_id.to_s + '/'
|
||||
filename = 'send-' + Time.new().to_f.to_s# + '-' + rand(99999999).to_s
|
||||
check = true
|
||||
count = 0
|
||||
while check
|
||||
if File::exists?( path + filename )
|
||||
count += 1
|
||||
filename = filename + '-' + count
|
||||
# filename = filename + '-' + rand(99999).to_s
|
||||
# filename = filename + '-' + rand(99999).to_s
|
||||
else
|
||||
check = false
|
||||
end
|
||||
end
|
||||
return false if !File.directory? path
|
||||
File.open( path + 'a-' + filename, 'wb' ) { |file|
|
||||
file.flock( File::LOCK_EX )
|
||||
file.write data.to_json
|
||||
file.flock( File::LOCK_UN )
|
||||
file.close
|
||||
}
|
||||
return false if !File.exists?( path + 'a-' + filename )
|
||||
FileUtils.mv( path + 'a-' + filename, path + filename )
|
||||
return true
|
||||
end
|
||||
|
||||
def self.jobs
|
||||
|
||||
# just make sure that spool path exists
|
||||
|
@ -248,12 +490,13 @@ module Sessions
|
|||
def self.thread_worker(user_id, try_count = 0, try_run_time = Time.now)
|
||||
puts "LOOP WORKER #{user_id} - #{try_count}"
|
||||
begin
|
||||
Sessions::Worker.new(user_id)
|
||||
Sessions::Worker.new(user_id)
|
||||
rescue => e
|
||||
puts "thread_worker exited with error #{ e.inspect }"
|
||||
sleep 10
|
||||
begin
|
||||
ActiveRecord::Base.connection.reconnect!
|
||||
# ActiveRecord::Base.remove_connection
|
||||
ActiveRecord::Base.connection_pool.reap
|
||||
rescue => e
|
||||
puts "Can't reconnect to database #{ e.inspect }"
|
||||
end
|
||||
|
@ -285,7 +528,8 @@ module Sessions
|
|||
puts "thread_client exited with error #{ e.inspect }"
|
||||
sleep 10
|
||||
begin
|
||||
ActiveRecord::Base.connection.reconnect!
|
||||
# ActiveRecord::Base.remove_connection
|
||||
ActiveRecord::Base.connection_pool.reap
|
||||
rescue => e
|
||||
puts "Can't reconnect to database #{ e.inspect }"
|
||||
end
|
||||
|
@ -309,66 +553,4 @@ module Sessions
|
|||
puts "/LOOP #{client_id} - #{try_count}"
|
||||
end
|
||||
|
||||
def self.sessions
|
||||
path = @path + '/'
|
||||
|
||||
# just make sure that spool path exists
|
||||
if !File::exists?( path )
|
||||
FileUtils.mkpath path
|
||||
end
|
||||
|
||||
data = []
|
||||
Dir.foreach( path ) do |entry|
|
||||
next if entry == '.' || entry == '..' || entry == 'spool'
|
||||
data.push entry.to_s
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
def self.queue( client_id )
|
||||
path = @path + '/' + client_id.to_s + '/'
|
||||
data = []
|
||||
files = []
|
||||
Dir.foreach( path ) {|entry|
|
||||
next if entry == '.' || entry == '..'
|
||||
files.push entry
|
||||
}
|
||||
files.sort.each {|entry|
|
||||
filename = path + '/' + entry
|
||||
if /^send/.match( entry )
|
||||
data.push Sessions.queue_file( path, entry )
|
||||
end
|
||||
}
|
||||
return data
|
||||
end
|
||||
|
||||
def self.queue_file( path, filename )
|
||||
file_old = path + filename
|
||||
file_new = path + 'a-' + filename
|
||||
FileUtils.mv( file_old, file_new )
|
||||
data = nil
|
||||
all = ''
|
||||
File.open( file_new, 'rb' ) { |file|
|
||||
all = file.read
|
||||
}
|
||||
File.delete( file_new )
|
||||
data = JSON.parse( all )
|
||||
return data
|
||||
end
|
||||
|
||||
def self.broadcast( data )
|
||||
|
||||
# list all current clients
|
||||
client_list = self.list
|
||||
client_list.each {|local_client_id, local_client|
|
||||
Sessions.send( local_client_id, data )
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
def self.destory( client_id )
|
||||
path = @path + '/' + client_id.to_s
|
||||
FileUtils.rm_rf path
|
||||
end
|
||||
|
||||
end
|
|
@ -311,14 +311,10 @@ EventMachine.run {
|
|||
end
|
||||
}
|
||||
|
||||
# ajax
|
||||
clients = Sessions.list
|
||||
clients.each { |client_id, client|
|
||||
next if client[:meta][:type] == 'websocket'
|
||||
if ( client[:meta][:last_ping].to_i + ( 60 * idle_time_in_min ) ) < Time.now.to_i
|
||||
log 'notice', "closing idle ajax connection", client_id
|
||||
Sessions.destory( client_id )
|
||||
end
|
||||
# close unused sessions
|
||||
clients = Sessions.destory_idle_sessions(idle_time_in_min)
|
||||
clients.each { |client_id|
|
||||
log 'notice', "closing idle connection", client_id
|
||||
}
|
||||
end
|
||||
|
||||
|
|
160
test/unit/session_test.rb
Normal file
160
test/unit/session_test.rb
Normal file
|
@ -0,0 +1,160 @@
|
|||
# encoding: utf-8
|
||||
require 'test_helper'
|
||||
|
||||
class SessionTest < ActiveSupport::TestCase
|
||||
test 'base' do
|
||||
|
||||
# create users
|
||||
roles = Role.where( :name => [ 'Agent'] )
|
||||
groups = Group.all
|
||||
|
||||
UserInfo.current_user_id = 1
|
||||
agent1 = User.create_or_update(
|
||||
:login => 'session-agent-1',
|
||||
:firstname => 'Session',
|
||||
:lastname => 'Agent 1',
|
||||
:email => 'session-agent1@example.com',
|
||||
:password => 'agentpw',
|
||||
:active => true,
|
||||
:roles => roles,
|
||||
:groups => groups,
|
||||
)
|
||||
agent2 = User.create_or_update(
|
||||
:login => 'session-agent-2',
|
||||
:firstname => 'Session',
|
||||
:lastname => 'Agent 2',
|
||||
:email => 'session-agent2@example.com',
|
||||
:password => 'agentpw',
|
||||
:active => true,
|
||||
:roles => roles,
|
||||
:groups => groups,
|
||||
)
|
||||
agent3 = User.create_or_update(
|
||||
:login => 'session-agent-3',
|
||||
:firstname => 'Session',
|
||||
:lastname => 'Agent 3',
|
||||
:email => 'session-agent3@example.com',
|
||||
:password => 'agentpw',
|
||||
:active => true,
|
||||
:roles => roles,
|
||||
:groups => groups,
|
||||
)
|
||||
|
||||
# create sessions
|
||||
client_id1 = '1234'
|
||||
client_id2 = '123456'
|
||||
client_id3 = 'abc'
|
||||
Sessions.destory(client_id1)
|
||||
Sessions.destory(client_id2)
|
||||
Sessions.destory(client_id3)
|
||||
Sessions.create( client_id1, agent1.attributes, { :type => 'websocket' } )
|
||||
Sessions.create( client_id2, agent2.attributes, { :type => 'ajax' } )
|
||||
Sessions.create( client_id3, agent3.attributes, { :type => 'ajax' } )
|
||||
|
||||
# check if session exists
|
||||
assert( Sessions.session_exists?(client_id1), "check if session exists" )
|
||||
assert( Sessions.session_exists?(client_id2), "check if session exists" )
|
||||
assert( Sessions.session_exists?(client_id3), "check if session exists" )
|
||||
|
||||
# check if session still exists after idle cleanup
|
||||
sleep 1
|
||||
Sessions.destory_idle_sessions(1)
|
||||
assert( Sessions.session_exists?(client_id1), "check if session exists after 1 sec" )
|
||||
assert( Sessions.session_exists?(client_id2), "check if session exists after 1 sec" )
|
||||
assert( Sessions.session_exists?(client_id3), "check if session exists after 1 sec" )
|
||||
|
||||
|
||||
|
||||
# check client threads
|
||||
|
||||
|
||||
# check worker threads
|
||||
|
||||
|
||||
|
||||
# check if session still exists after idle cleanup with touched sessions
|
||||
sleep 62
|
||||
Sessions.touch(client_id1)
|
||||
Sessions.touch(client_id2)
|
||||
Sessions.touch(client_id3)
|
||||
Sessions.destory_idle_sessions(1)
|
||||
assert( Sessions.session_exists?(client_id1), "check if session exists after touch" )
|
||||
assert( Sessions.session_exists?(client_id2), "check if session exists after touch" )
|
||||
assert( Sessions.session_exists?(client_id3), "check if session exists after touch" )
|
||||
|
||||
# check session data
|
||||
data = Sessions.get(client_id1)
|
||||
assert( data[:meta], "check if meta exists" )
|
||||
assert( data[:user], "check if user exists" )
|
||||
assert_equal( data[:user][:id], agent1.id, "check if user id is correct" )
|
||||
|
||||
data = Sessions.get(client_id2)
|
||||
assert( data[:meta], "check if meta exists" )
|
||||
assert( data[:user], "check if user exists" )
|
||||
assert_equal( data[:user][:id], agent2.id, "check if user id is correct" )
|
||||
|
||||
data = Sessions.get(client_id3)
|
||||
assert( data[:meta], "check if meta exists" )
|
||||
assert( data[:user], "check if user exists" )
|
||||
assert_equal( data[:user][:id], agent3.id, "check if user id is correct" )
|
||||
|
||||
# send data to one client
|
||||
Sessions.send( client_id1, { :msg => 'äöüß123' } )
|
||||
Sessions.send( client_id1, { :msg => 'äöüß1234' } )
|
||||
messages = Sessions.queue(client_id1)
|
||||
assert_equal( 3, messages.count, 'messages count')
|
||||
assert_equal( 'ws:login', messages[0]['event'], 'messages 1')
|
||||
assert_equal( true, messages[0]['data']['success'], 'messages 1')
|
||||
assert_equal( 'äöüß123', messages[1]['msg'], 'messages 2')
|
||||
assert_equal( 'äöüß1234', messages[2]['msg'], 'messages 3')
|
||||
|
||||
messages = Sessions.queue(client_id2)
|
||||
assert_equal( messages.count, 1, 'messages count')
|
||||
assert_equal( 'ws:login', messages[0]['event'], 'messages 1')
|
||||
assert_equal( true, messages[0]['data']['success'], 'messages 1')
|
||||
|
||||
messages = Sessions.queue(client_id3)
|
||||
assert_equal( messages.count, 1, 'messages count')
|
||||
assert_equal( 'ws:login', messages[0]['event'], 'messages 1')
|
||||
assert_equal( true, messages[0]['data']['success'], 'messages 1')
|
||||
|
||||
|
||||
# broadcast to all clients
|
||||
Sessions.broadcast( { :msg => 'ooo123123123123123123'} )
|
||||
messages = Sessions.queue(client_id1)
|
||||
assert_equal( messages.count, 1, 'messages count')
|
||||
assert_equal( 'ooo123123123123123123', messages[0]['msg'], 'messages broadcast 1')
|
||||
|
||||
messages = Sessions.queue(client_id2)
|
||||
assert_equal( messages.count, 1, 'messages count')
|
||||
assert_equal( 'ooo123123123123123123', messages[0]['msg'], 'messages broadcast 1')
|
||||
|
||||
messages = Sessions.queue(client_id3)
|
||||
assert_equal( messages.count, 1, 'messages count')
|
||||
assert_equal( 'ooo123123123123123123', messages[0]['msg'], 'messages broadcast 1')
|
||||
|
||||
|
||||
# check client threads
|
||||
|
||||
|
||||
# check worker threads
|
||||
|
||||
|
||||
# check if session still exists after idle cleanup
|
||||
sleep 62
|
||||
client_ids = Sessions.destory_idle_sessions(1)
|
||||
|
||||
# check client sessions
|
||||
assert( !Sessions.session_exists?(client_id1), "check if session is removed" )
|
||||
assert( !Sessions.session_exists?(client_id2), "check if session is removed" )
|
||||
assert( !Sessions.session_exists?(client_id3), "check if session is removed" )
|
||||
|
||||
# check client threads
|
||||
|
||||
|
||||
# check worker threads
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue