[Catalyst] Session trouble
Bill Moseley
moseley at hank.org
Fri Jul 13 07:25:23 GMT 2007
I'm bringing this over from a discussion on IRC with nothingmuch.
All up-to-date as of yesterday (fresh install on new machine).
I set a flag in the session that adjusts how long cookies persist.
Then override this method in my application:
sub calculate_session_cookie_expires {
my $c = shift;
return $c->session->{remember_me}
? $c->session_expires
: $c->NEXT::calculate_session_cookie_expires;
}
If I wait a while when the session expires (but the cookie hasn't
expired yet) I get this behavior:
1) session expired.
2) request comes in with expired session id in the cookie.
3) session/cookie cleared
4) new session created
5) cookie set in headers
6) session data cleared.
7) session data requested -- but session data already clear
8) so, create new session id
9) write session to store, but using new session id, not
one set in cookie header.
Here's a Cat application to duplicate (which I've been able to do on
two machines).
The trick to make it fail is to have an invalid cookie. You can
generate a few requests and see that the same cookie is sent each
time.
Then edit your browser's cookie to make it invalid (change a single
digit). Then reload /bar and watch how a new cookie is generated each
time.
(Note: If the cookie is an invalid *format* you get:
[error] Caught exception in Foo->bar "Tried to set invalid session ID '7xxx0da4dfba9790d232f9dfc479ecc23bc0c2d83db' at /usr/local/share/perl/5.8.8/Catalyst/Action.pm line 47"
which is another problem. Seems like should just ignore it and set a
new cookie.)
package Foo;
use strict;
use warnings;
use Catalyst::Runtime;
use Catalyst (
'Session',
'Session::State::Cookie',
'Session::Store::FastMmap',
'Cache::FastMmap',
);
__PACKAGE__->config( name => 'Foo' );
__PACKAGE__->setup;
__PACKAGE__->config->{session} = {
cookie_expires => 0, # Session cookie
expires => 604800,
cookie_name => 'my_cookie',
};
sub bar : Local {
my ( $self, $c ) = @_;
$c->session->{stuff} = 'keep this';
$c->res->body('in bar');
}
sub calculate_session_cookie_expires {
my $c = shift;
warn ">>>>>App::calculate_session_cookie_expires\n";
my $x = $c->session->{foo}; # force a reload of the session.
return $c->NEXT::calculate_session_cookie_expires;
}
sub finalize_body {
my $c = shift;
warn join( "\n",
'','------ finalize_body -----------------',
$c->req->path,
'Request:', $c->req->headers->as_string,
'Response:', $c->res->headers->as_string,
);
return $c->NEXT::finalize_body( @_ );
}
1;
Here's an annotated dump of a single request in the session code.
Note also how many sessions are created.
Request Cookie: my_cookie=810906f7a29c3f46f8b61dcf564f426c5209b254
# Now try and fetch the expired session:
Session::_load_session: get_session_id returned: 810906f7a29c3f46f8b61dcf564f426c5209b254
Store::get_session_data: fetched [expires:810906f7a29c3f46f8b61dcf564f426c5209b254]: [undef]
Session::_load_session_expires: expires = 0 810906f7a29c3f46f8b61dcf564f426c5209b254.
# Session is expired so purge it:
calling delete_session()<<
State::Cookie::update_session_cookie: setting cookie {
expires => 0,
value => "810906f7a29c3f46f8b61dcf564f426c5209b254",
}
Session::_clear_session_instance_data
>>
Session::_load_session_expires: after calling delete_session
# And create a new session:
Session::session(): creating session
Session::generate_session_id = 6d30ea44e84f658ae647f249bd7237e8a117740b
# Now my overridden calculate_session_cookie_expires() is called:
>>>>> App::calculate_session_cookie_expires
# Which triggers a session fetch, but there is no session data yet:
Store::get_session_data: fetched [expires:6d30ea44e84f658ae647f249bd7237e8a117740b]: [undef]
Session::_load_session_expires: expires = 0 6d30ea44e84f658ae647f249bd7237e8a117740b.
# So it's considered expired and deletes the session again:
calling delete_session()<<
State::Cookie::update_session_cookie: setting cookie { expires => 0, value => "6d30ea44e84f658ae647f249bd7237e8a117740b" }
Session::_clear_session_instance_data
>>
Session::_load_session_expires: after calling delete_session
# And yet another session is created and the cookie is set:
Session::session(): creating session
Session::generate_session_id = 4cc5a1526a6958df043edfcd77de025ece5e6334
# But it's the previous session id:
State::Cookie::update_session_cookie: setting cookie {
expires => undef,
value => "6d30ea44e84f658ae647f249bd7237e8a117740b",
}
Session::prepare_action start for path: login
# And when the data is finally stored it's using the latest session
id, but not the same one used for the cookie:
Store::store_session_data: setting [session:4cc5a1526a6958df043edfcd77de025ece5e6334] [HASH(0x9d937e8)]
Store::store_session_data: setting [expires:4cc5a1526a6958df043edfcd77de025ece5e6334] [1184906823]
------- Finalize body -------
# And here's the cookie in the response headers. Matches the last
update_session_cookie() but not the session written to the store.
Set-Cookie: my_cookie=6d30ea44e84f658ae647f249bd7237e8a117740b; path=/
--
Bill Moseley
moseley at hank.org
More information about the Catalyst
mailing list