Apache2-Controller

 view release on metacpan or  search on metacpan

lib/Apache2/Controller/Session.pm  view on Meta::CPAN

saves the copy in pnotes back into the tied hash.
It does this by re-ordering the C<PerlLogHandler> stack with
L<Apache2::RequestUtil/get_handlers> and C<set_handlers()>.
So if you push another post-response handler that wants to
choose whether to save the session or not, be aware that
it may not work as you expect unless you re-order that
phase's handler stack again.

=head1 TO SAVE OR NOT TO SAVE

Generally in your code, it's complicated to decide whether everything
has worked before you save anything to the session.  It's easier just
to save stuff, and then if something goes wrong, it is as if this
rolls back.

A C<PerlLogHandler> subroutine is 'unshifted' to the request stack
which decides whether to save changes to the session.  By default,
it saves changes only if A) the connection is not aborted,
and B) your controller set HTTP status < 300,
i.e. it returned C<OK> (0), one of the C<HTTP_CONTINUE> family (100+)
or one of the C<HTTP_OK> family (200+).  

So for an C<HTTP_SERVER_ERROR>, or throwing an exception, redirecting,
forbidding access, etc (>= 300), it normally would not save changes.
If your L<Apache2::Controller> controller module returns one of these 
non-OK statuses, but you want to force the saving of the session contents, 
set C<< $self->pnotes->{a2c}{session_force_save} = 1 >> before
your response phase controller returns a status to L<Apache2::Controller>.

If the connection is aborted mid-way (i.e. the pipe was broken
due to a network failure or the user clicked 'stop'
in the browser), then the session will not be saved,
whether you set the force save flag or not.
(If this is not useful and correct behavior contact me and I
will add another switch, but it seems right to me.)

It actually re-orders the C<PerlLogHandler> stack so that
its handlers run first, before the handler pushed by 
L<Apache2::Controller::DBI::Connector> commits the database
transaction, for example.

This used to push a C<PerlCleanupHandler> to save the session,
which made sense at the time, but the OpenID auth tests revealed
that the Cleanup handler is apparently assigned a thread to
process it independently, even under prefork with C<Apache::Test>.
So, the test script was firing off a new request 
before the old request Cleanup handler ran to save the session,
which resulted in sporadic and inconsistent failures... 
yeah, THOSE kind, you know the type, the most maddening ones.

Apache::Session does not always save automatically, for
example if you change something in the bottom tier of
a multi-level hash.  If you want to, set the directive flag
C<A2C_Session_Always_Save> and this will set a top-level
timestamp C<< $r->pnotes->{a2c}{session}{a2c_timestamp} >> 
on the way out to trigger L<Apache::Session> to save everything.
But if you are potentially accessing the session contents without
setting it every time, you should just set a top-level timestamp
manually to indicate to L<Apache::Session> that you want 
things saved at the end of every request, but this may
slow you down on a busy site, so it is not the default.
See L<Apache2::Controller::Directives/A2C_Session_Always_Save>
and L<Apache::Session/BEHAVIOR>.

=head1 IMPLEMENTING TRACKER SUBCLASSES

See L<Apache2::Controller::Session::Cookie> for how to implement
a custom tracker subclass.  This implements C<$sid = get_session_id()> 
which gets a session id from a cookie, and C<set_session_id($sid)> 
which sets the session id in the cookie.

Perhaps some custom tracker subclass would implement
C<get_session_id()> to get the session_id out of the request 
query params, and C<set_session_id()> would push a C<PerlOutputFilterHandler>
to post-process all other handler output and append the session id param
onto any url links that refer to our site.  That would be cool...
release your own plug-in.
If you wanted to do it with combined cookies and url params in 
this way you could 
overload C<get_session_id()> and C<set_session_id()>, etc. etc.

=head1 ERRORS

C<<Apache2::Controller::Session>> will throw an error exception if the
session setup encounters an error.  

=head1 METHODS

=cut

use strict;
use warnings FATAL => 'all';
use English '-no_match_vars';

use base qw( 
    Apache2::Controller::NonResponseBase 
    Apache2::Controller::Methods 
);

use YAML::Syck;
use Log::Log4perl qw(:easy);
use File::Spec;
use Digest::SHA qw( sha224_base64 );

use Apache2::Const -compile => qw( OK );
use Apache2::RequestUtil ();
use Apache2::Controller::X;
use Apache2::Controller::Const qw( $DEFAULT_SESSION_SECRET );

=head2 process

The C<process()> method
attaches or creates a session, and pushes a PerlLogHandler
closure to save the session after the end of the request.

It sets the session id cookie
with an expiration that you set in your subclass as C<our $expiration = ...>
in a format that is passed to Apache2::Cookie.  (i.e. '3M', '2D', etc.)
Don't set that if you want them to expire at the end of the
browser session.



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