class ADAM6050::Session

The session object is used by the server to keep track of authenticated clients. Once a client is registered with the session it will be reported as valid for a period of time, after which it is again marked as invalid and needs to register again. Any activity before the timeout will reset the countdown.

Usage

The following example creates a session with a 10 second timeout and registers a sender. `#validate!` is then called at a later point in time to verify that the sender is still valid.

session = Session.new timeout: 10.0
session.register sender

# The following call will raise an exception if more
# than 10 seconds has passed.
session.validate! sender

Constants

DEFAULT_CLEANUP_INTERVALL

@return [Numeric] the default number of seconds to wait after one cleanup

before perfoming the next.
DEFAULT_TIMEOUT

@return [Numeric] the default number of seconds a sender is valid with no

interaction.

Public Class Methods

new(timeout: DEFAULT_TIMEOUT, cleanup_interval: DEFAULT_CLEANUP_INTERVALL) click to toggle source

@param timeout [Numeric] the number of seconds a sender is valid with no

interaction.

@param cleanup_interval [Numeric] the number of seconds to wait after one

cleanup before perfoming the next.
# File lib/adam6050/session.rb, line 49
def initialize(timeout: DEFAULT_TIMEOUT,
               cleanup_interval: DEFAULT_CLEANUP_INTERVALL)
  @session          = {}
  @timeout          = timeout
  @cleanup_interval = cleanup_interval
  @next_cleanup     = 0.0
end

Public Instance Methods

cleanup!(time: monotonic_timestamp) click to toggle source

Removes invalid senders from the session.

@param time [Numeric] the current time. The current time will be used if

not specified.

@return [Numeric] the next time before which no cleanup will be performed.

# File lib/adam6050/session.rb, line 112
def cleanup!(time: monotonic_timestamp)
  return if time < @next_cleanup

  delete_expired! time, @timeout

  @next_cleanup = time + @cleanup_interval
end
register(sender, time: monotonic_timestamp) click to toggle source

Register a new sender as valid in the current session.

@param sender [Socket::UDPSource] the udp client. @param time [Numeric] the current time. The current time will be used if

not specified.

@return [nil]

# File lib/adam6050/session.rb, line 70
def register(sender, time: monotonic_timestamp)
  @session[session_key sender] = time
  nil
end
size() click to toggle source

@return [Integer] the number of senders currently known by the session.

Note that this may include invalid senders that have not yet been
cleaned up.
# File lib/adam6050/session.rb, line 60
def size
  @session.size
end
valid?(sender, time: monotonic_timestamp) click to toggle source

A sender is valid as long as it is registered and has not expired within the current session.

@param sender [Socket::UDPSource] the udp client. @param time [Numeric] the current time. The current time will be used if

not specified.

@return [true] if the sender has authenticated and has been active within

the configured timeout.

@return [false] otherwise.

# File lib/adam6050/session.rb, line 84
def valid?(sender, time: monotonic_timestamp)
  !expired? @session.fetch(session_key(sender), 0.0), time, @timeout
end
validate!(sender, time: monotonic_timestamp) click to toggle source

Renews the given sender if it is still valid within the session and raises an exception otherwise.

@raise [UnknownSender] if the given sender is not registered. @raise [InvalidSender] if the given sender is not valid.

@param sender [Socket::UDPSource] the udp client. @param time [Numeric] the current time. The current time will be used if

not specified.

@return [nil]

# File lib/adam6050/session.rb, line 98
def validate!(sender, time: monotonic_timestamp)
  key = session_key sender
  last_observed = @session.fetch(key) { raise UnknownSender, sender }
  raise InvalidSender, sender if expired? last_observed, time, @timeout

  @session[key] = time
  nil
end

Private Instance Methods

delete_expired!(time, timeout) click to toggle source

@param time [Numeric] the current time. @param timeout [Numeric] the time after which expired clients should be

deleted.

@return [Hash] the session hash with expired clients deleted.

# File lib/adam6050/session.rb, line 151
def delete_expired!(time, timeout)
  @session.delete_if { |_, t| expired? t, time, timeout }
end
expired?(last_seen, time, timeout) click to toggle source

@param last_seen [Numeric] the time when the client was last seen. @param time [Numeric] the current time. @param timeout [Numeric] the time after which expired clients should be

deleted.

@return [false] if the time last seen is within the timeout. @return [true] otherwise.

# File lib/adam6050/session.rb, line 142
def expired?(last_seen, time, timeout)
  threshold = time - timeout
  last_seen < threshold
end
monotonic_timestamp() click to toggle source

This is slightly faster than calling Time.now since no new object needs to be allocated.

@return [Numeric] the current monotonic process time.

# File lib/adam6050/session.rb, line 132
def monotonic_timestamp
  Process.clock_gettime Process::CLOCK_MONOTONIC
end
session_key(sender) click to toggle source

@param sender [Socket::UDPSource] the UDP client. @return [#hash] a unique identifier for the sender.

# File lib/adam6050/session.rb, line 124
def session_key(sender)
  sender.remote_address.ip_address
end