Continuity

 view release on metacpan or  search on metacpan

lib/Continuity/Mapper.pm  view on Meta::CPAN

In the simple case, each "user" gets their own execution context.  By default,
users are distinguished by their IP address, which is a very bad way to try to
make this distinction.  Corporate users behind NATs and AOL users (also behind
a NAT) will all appear to be the same few users.

C<path_session> may be set true to use the pathname of the request, such as
C<foo> in C<http://bar.com/foo?baz=quux>, as part of the criteria for deciding
which execution context to associate with that hit.  This makes it possible to
write applications that give one user more than one execution contexts.  This
is necessary to run server-push concurrently with push from the user back to
the server (see the examples directory) or to have sub-applications running on
the same port, each having its own state separate from the others.

Cookies aren't issued or read by L<Continuity>, but we plan to add support for
reading them.  I expect the name of the cookie to look for would be passed in,
or perhaps a subroutine that validates the cookies and returns it (possibly
stripped of a secure hash) back out.  Other code (the main application, or
another session handling module from CPAN, or whatnot) will have the work of
picking session IDs.

To get more sophisticated or specialized session ID computing logic, subclass
this object, re-implement C<get_session_id_from_hit()> to suit your needs, and
then pass in an instance of your subclass to as the value for C<mapper> in the
call to C<< Continuity->new) >>.  Here's an example of that sort of constructor
call:

  $server = Continuity->new( 
    mapper   => Continuity::Mapper::StrongRandomSessionCookies->new( callback => \::main )
  );

=cut

sub new {

  my $class = shift; 
  my $self = bless { 
      sessions => { },
      sessions_last_access => { },
      ip_session => 0,
      path_session => 0,
      cookie_session => 'sid',
      cookie_life => '+2d',
      query_session => 0,
      debug_level => 0,
      debug_callback => sub { print "@_\n" },
      assign_session_id => sub { join '', 1+int rand 9, map int rand 10, 2..20 },
      implicit_first_next => 1,
      @_,
  }, $class;
  $self->{callback} or die "Mapper: callback not set.\n";
  return $self;

}

=head2 $mapper->get_session_id_from_hit($request)

Uses the defined strategies (ip, path, cookie) to create a session identifier
for the given request. This is what you'll most likely want to override, if
anything.

$request is generally an HTTP::Request, though technically may only have a
subset of the functionality.

=cut

sub get_session_id_from_hit {
  my ($self, $request) = @_;
  my $session_id = '';
  my $sid;
  $self->Continuity::debug(2,"        URI: ", $request->uri);

  # IP based sessions
  if($self->{ip_session}) {
    my $ip = $request->headers->header('Remote-Address')
             || $request->peerhost;
    $session_id .= '.' . $ip;
  }

  # Path sessions
  if($self->{path_session}) {
    my ($path) = $request->uri =~ m{/([^?]*)};
    $path =~ s/\.//g; # ./ and / are the same thing, and hey -- we use the dot as our separator anyway
    $path ||= '/';  # needed to make it consistent
    $session_id .= '.' . $path;
  }

  # Query sessions
  if($self->{query_session}) {
    $sid = $request->param($self->{query_session}) || '';
    $self->Continuity::debug(2,"    Session: got query '$sid'");
  }

  # Cookie sessions
  if($self->{cookie_session}) {
    my $cookie = $request->get_cookie($self->{cookie_session});
    $sid = $cookie if $cookie;
    $self->Continuity::debug(2,"    Session: got cookie '$sid'") if $sid;
  }

  if(($self->{query_session} or $self->{cookie_session}) and ! $sid) {
      $sid = $self->{assign_session_id}->($request);
      $self->Continuity::debug(2,"    New SID: $sid");
      $request->set_cookie( CGI->cookie(
        -name    => $self->{cookie_session},
        -value   => $sid,
        -expires => $self->{cookie_life},
      )) if $self->{cookie_session};
  }

  $session_id .= $sid if $sid;

  $self->Continuity::debug(2," Session ID: ", $session_id);

  return $session_id;

}

=head2 $mapper->map($request)

Send the given request to the correct session, creating it if necessary.



( run in 0.600 second using v1.01-cache-2.11-cpan-39bf76dae61 )