Apache-ASP
view release on metacpan or search on metacpan
lib/Apache/ASP/StateManager.pm view on Meta::CPAN
# cookieless session support, cascading values
$self->{session_url_parse_match} = &config($self, 'SessionQueryParseMatch');
$self->{session_url_parse} = $self->{session_url_parse_match} || &config($self, 'SessionQueryParse');
$self->{session_url_match} = $self->{session_url_parse_match} || &config($self, 'SessionQueryMatch');
$self->{session_url} = $self->{session_url_parse} || $self->{session_url_match} || &config($self, 'SessionQuery');
$self->{session_url_force} = &config($self, 'SessionQueryForce');
$self->{session_serialize} = &config($self, 'SessionSerialize');
$self->{secure_session} = &config($self, 'SecureSession');
$self->{http_only_session} = &config($self, 'HTTPOnlySession');
# session timeout in seconds since that is what we work with internally
$self->{session_timeout} = &config($self, 'SessionTimeout', undef, $SessionTimeout) * 60;
$self->{'ua'} = $self->{headers_in}->get('User-Agent') || 'UNKNOWN UA';
# refresh group by some increment smaller than session timeout
# to withstand DoS, bruteforce guessing attacks
# defaults to checking the group once every 2 minutes
$self->{group_refresh} = int($self->{session_timeout} / $self->{state_manager});
# Session state is dependent on internal state
# load at runtime for CGI environments, preloaded for mod_perl
require Apache::ASP::Session;
$session = $self->{Session} = &Apache::ASP::Session::new($self)
|| $self->Die("can't create session");
$self->{state_serialize} && $session->Lock();
} else {
$self->{dbg} && $self->Debug("no sessions allowed config");
}
# update after long state init, possible with SessionSerialize config
$self->{Response}->IsClientConnected();
# POSTPOSE STATE EVENTS, so we can delay the Response object creation
# until after the state objects are created
if($session) {
my $last_session_timeout;
if($session->Started()) {
# we only want one process purging at a time
if($self->{app_state}) {
$internal->LOCK();
if(($last_session_timeout = $internal->{LastSessionTimeout} || 0) < time()) {
$internal->{'LastSessionTimeout'} = $self->{session_timeout} + time;
$internal->UNLOCK();
$self->{Application}->Lock;
my $obj = tied(%{$self->{Application}});
if($self->CleanupGroups('PURGE')) {
$last_session_timeout && $global_asa->ApplicationOnEnd();
$global_asa->ApplicationOnStart();
}
$self->{Application}->UnLock;
}
$internal->UNLOCK();
}
$global_asa->SessionOnStart();
}
if($self->{app_state}) {
# The last session timeout should only be updated every group_refresh period
# another optimization, rand() so not all at once either
$internal->LOCK();
$last_session_timeout ||= $internal->{'LastSessionTimeout'};
if($last_session_timeout < $self->{session_timeout} + time +
(rand() * $self->{group_refresh} / 2))
{
$self->{dbg} && $self->Debug("updating LastSessionTimeout from $last_session_timeout");
$internal->{'LastSessionTimeout'} =
$self->{session_timeout} + time() + $self->{group_refresh};
}
$internal->UNLOCK();
}
}
$self;
}
# Cleanup a state group, by default the group of the current session
# We do this currently in DESTROY, which happens after the current
# script has been executed, so that cleanup doesn't happen until
# after output to user
#
# We always exit unless there is a $Session defined, since we only
# cleanup groups of sessions if sessions are allowed for this script
sub CleanupGroup {
my($self, $group_id, $force) = @_;
return unless $self->{Session};
my $asp = $self; # bad hack for some moved around code
$force ||= 0;
# GET GROUP_ID
my $state;
unless($group_id) {
$state = $self->{Session}{_STATE};
$group_id = $state->GroupId();
}
# we must have a group id to work with
$asp->Error("no group id") unless $group_id;
my $group_key = "GroupId" . $group_id;
# cleanup timed out sessions, from current group
my $internal = $asp->{Internal};
$internal->LOCK();
my $group_check = $internal->{$group_key} || 0;
unless($force || ($group_check < time())) {
$internal->UNLOCK();
return;
}
# set the next group_check, randomize a bit to unclump the group checks,
# for 20 minute session timeout, had rand() / 2 + .5, but it was still
# too clumpy, going with pure rand() now, even if a bit less efficient
my $next_check = int($asp->{group_refresh} * rand()) + 1;
$internal->{$group_key} = time() + $next_check;
$internal->UNLOCK();
## GET STATE for group
( run in 0.512 second using v1.01-cache-2.11-cpan-39bf76dae61 )