X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FPlack%2FMiddleware%2FSession.pm;h=1dbe441d39c57fce1201d7f6642099dcb364d57f;hb=08b2e16d35e53b1c4ac4ec0e292cebc424f0c0eb;hp=d85dd5a02ba4ac29fa86edba410866ee7820bbd3;hpb=fe1bfe7d50bcca90a8e2dc1e62d142b093bfbd6e;p=catagits%2FWeb-Session.git diff --git a/lib/Plack/Middleware/Session.pm b/lib/Plack/Middleware/Session.pm index d85dd5a..1dbe441 100644 --- a/lib/Plack/Middleware/Session.pm +++ b/lib/Plack/Middleware/Session.pm @@ -2,45 +2,270 @@ package Plack::Middleware::Session; use strict; use warnings; -use Plack::Session; +our $VERSION = '0.03'; +our $AUTHORITY = 'cpan:STEVAN'; + use Plack::Request; use Plack::Response; -use Plack::Session::State::Cookie; -use Plack::Session::Store; +use Plack::Util; +use Scalar::Util; use parent 'Plack::Middleware'; -use Plack::Util::Accessor qw( state store ); +use Plack::Util::Accessor qw( + state + store + session_class +); sub prepare_app { my $self = shift; - unless ($self->state) { - $self->state( Plack::Session::State::Cookie->new ); - } - unless ($self->store) { - $self->store( Plack::Session::Store->new ); - } + $self->state( 'Cookie' ) unless $self->state; + $self->state( $self->inflate_backend('Plack::Session::State', $self->state) ); + $self->store( $self->inflate_backend('Plack::Session::Store', $self->store) ); + + Plack::Util::load_class($self->session_class) if $self->session_class; +} + +sub inflate_backend { + my($self, $prefix, $backend) = @_; + + return $backend if defined $backend && Scalar::Util::blessed $backend; + + my @class; + push @class, $backend if defined $backend; # undef means the root class + push @class, $prefix; + + Plack::Util::load_class(@class)->new(); } sub call { my $self = shift; my $env = shift; - $env->{'psgix.session'} = Plack::Session->new( - state => $self->state || $self->default_state, - store => $self->store, - request => Plack::Request->new( $env ) - ); + my $request = Plack::Request->new($env); + + my($id, $session) = $self->get_session($request); + if ($id && $session) { + $env->{'psgix.session'} = $session; + } else { + $id = $self->generate_id($request); + $env->{'psgix.session'} = {}; + } + + $env->{'psgix.session.options'} = { id => $id }; + + if ($self->session_class) { + $env->{'plack.session'} = $self->session_class->new($env); + } my $res = $self->app->($env); $self->response_cb($res, sub { my $res = Plack::Response->new(@{$_[0]}); - $env->{'psgix.session'}->finalize( $res ); - @{$_[0]} = @{$res->finalize}; + $self->finalize($request, $res); + $res = $res->finalize; + $_[0]->[0] = $res->[0]; + $_[0]->[1] = $res->[1]; }); } +sub get_session { + my($self, $request) = @_; + + my $id = $self->state->extract($request) or return; + my $session = $self->store->fetch($id) or return; + + return ($id, $session); +} + +sub generate_id { + my($self, $request) = @_; + $self->state->generate($request); +} + +sub commit { + my($self, $session, $options) = @_; + if ($options->{expire}) { + $self->store->remove($options->{id}); + } else { + $self->store->store($options->{id}, $session); + } +} + +sub finalize { + my($self, $request, $response) = @_; + + my $session = $request->env->{'psgix.session'}; + my $options = $request->env->{'psgix.session.options'}; + + $self->commit($session, $options) unless $options->{no_store}; + if ($options->{expire}) { + $self->expire_session($options->{id}, $response, $session, $options); + } else { + $self->save_state($options->{id}, $response, $session, $options); + } +} + +sub expire_session { + my($self, $id, $res, $session, $options) = @_; + $self->state->expire_session_id($options->{id}, $res, $options); +} + +sub save_state { + my($self, $id, $res, $session, $options) = @_; + $self->state->finalize($id, $res, $options); +} + 1; __END__ + +=pod + +=head1 NAME + +Plack::Middleware::Session - Middleware for session management + +=head1 SYNOPSIS + + use Plack::Builder; + + my $app = sub { + my $env = shift; + my $session = $env->{'psgix.session'}; + return [ + 200, + [ 'Content-Type' => 'text/plain' ], + [ "Hello, you've been here for ", $session->{counter}++, "th time!" ], + ]; + }; + + builder { + enable 'Session'; + $app; + }; + + # Or, use the File store backend (great if you use multiprocess server) + # For more options, see perldoc Plack::Session::Store::File + builder { + enable 'Session', store => 'File'; + $app; + }; + +=head1 DESCRIPTION + +This is a Plack Middleware component for session management. By +default it will use cookies to keep session state and store data in +memory. This distribution also comes with other state and store +solutions. See perldoc for these backends how to use them. + +It should be noted that we store the current session as a hash +reference in the C key inside the C<$env> where you can +access it as needed. + +B As of version 0.04 the session is stored in C +instead of C. + +Also, if you set I option (see below), we create a +session object out of the hash reference in C. + +=head2 State + +=over 4 + +=item L + +This will maintain session state by passing the session through +the request params. It does not do this automatically though, +you are responsible for passing the session param. + +=item L + +This will maintain session state using browser cookies. + +=back + +=head2 Store + +=over 4 + +=item L + +This is your basic in-memory session data store. It is volatile storage +and not recommended for multiprocessing environments. However it is +very useful for development and testing. + +=item L + +This will persist session data in a file. By default it uses +L but it can be configured to have a custom serializer and +deserializer. + +=item L + +This will persist session data using the L interface. + +=item L + +Sometimes you don't care about storing session data, in that case +you can use this noop module. + +=back + +=head1 OPTIONS + +The following are options that can be passed to this mdoule. + +=over 4 + +=item I + +This is expected to be an instance of L or an +object that implements the same interface. If no option is provided +the default L will be used. + +=item I + +This is expected to be an instance of L or an +object that implements the same interface. If no option is provided +the default L will be used. + +It should be noted that this default is an in-memory volatile store +is only suitable for development (or single process servers). For a +more robust solution see L or +L. + +=item I + +This can be used to create an actual session object in +C environment. Defaults to none, which means the +session object is not created but you can set C to +create an object for you. + +=back + +=head1 BUGS + +All complex software has bugs lurking in it, and this module is no +exception. If you find a bug please either email me, or add the bug +to cpan-RT. + +=head1 AUTHOR + +Tatsuhiko Miyagawa + +Stevan Little Estevan.little@iinteractive.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright 2009, 2010 Infinity Interactive, Inc. + +L + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +