Pass $env in more Middleware APIs so subclasses can do more things flexibly in the...
[catagits/Web-Session.git] / lib / Plack / Middleware / Session.pm
CommitLineData
bd992981 1package Plack::Middleware::Session;
2use strict;
3use warnings;
4
000c696e 5our $VERSION = '0.03';
30cc0a71 6our $AUTHORITY = 'cpan:STEVAN';
7
bd992981 8use Plack::Request;
9use Plack::Response;
ad80e445 10use Plack::Util;
11use Scalar::Util;
bd992981 12
13use parent 'Plack::Middleware';
14
d6af4aa8 15use Plack::Util::Accessor qw(
16 state
17 store
18 session_class
19);
bd992981 20
fe1bfe7d 21sub prepare_app {
22 my $self = shift;
fe1bfe7d 23
4ff41723 24 $self->state( 'Cookie' ) unless $self->state;
ad80e445 25 $self->state( $self->inflate_backend('Plack::Session::State', $self->state) );
26 $self->store( $self->inflate_backend('Plack::Session::Store', $self->store) );
4ff41723 27
28 Plack::Util::load_class($self->session_class) if $self->session_class;
ad80e445 29}
30
31sub inflate_backend {
32 my($self, $prefix, $backend) = @_;
33
34 return $backend if defined $backend && Scalar::Util::blessed $backend;
35
36 my @class;
37 push @class, $backend if defined $backend; # undef means the root class
38 push @class, $prefix;
39
40 Plack::Util::load_class(@class)->new();
fe1bfe7d 41}
42
bd992981 43sub call {
44 my $self = shift;
45 my $env = shift;
46
4ff41723 47 my $request = Plack::Request->new($env);
48
dc556d28 49 my($id, $session) = $self->get_session($request);
50 if ($id && $session) {
4ff41723 51 $env->{'psgix.session'} = $session;
52 } else {
dc556d28 53 $id = $self->generate_id($request);
4ff41723 54 $env->{'psgix.session'} = {};
55 }
56
57 $env->{'psgix.session.options'} = { id => $id };
4a0cb5a0 58
4ff41723 59 if ($self->session_class) {
3bf03fd8 60 $env->{'plack.session'} = $self->session_class->new($env);
4ff41723 61 }
bd992981 62
fe1bfe7d 63 my $res = $self->app->($env);
64 $self->response_cb($res, sub {
65 my $res = Plack::Response->new(@{$_[0]});
7518e927 66 $self->finalize($env, $res);
b2504d01 67 $res = $res->finalize;
68 $_[0]->[0] = $res->[0];
69 $_[0]->[1] = $res->[1];
fe1bfe7d 70 });
bd992981 71}
72
dc556d28 73sub get_session {
74 my($self, $request) = @_;
75
76 my $id = $self->state->extract($request) or return;
77 my $session = $self->store->fetch($id) or return;
78
79 return ($id, $session);
80}
81
82sub generate_id {
83 my($self, $request) = @_;
84 $self->state->generate($request);
85}
86
4ff41723 87sub commit {
7518e927 88 my($self, $env) = @_;
89
90 my $session = $env->{'psgix.session'};
91 my $options = $env->{'psgix.session.options'};
92
4ff41723 93 if ($options->{expire}) {
08b2e16d 94 $self->store->remove($options->{id});
4ff41723 95 } else {
96 $self->store->store($options->{id}, $session);
97 }
98}
99
100sub finalize {
7518e927 101 my($self, $env, $response) = @_;
dc556d28 102
7518e927 103 my $session = $env->{'psgix.session'};
104 my $options = $env->{'psgix.session.options'};
4ff41723 105
7518e927 106 $self->commit($env) unless $options->{no_store};
98b27b31 107 if ($options->{expire}) {
7518e927 108 $self->expire_session($options->{id}, $response, $env);
4ff41723 109 } else {
7518e927 110 $self->save_state($options->{id}, $response, $env);
4ff41723 111 }
112}
113
dc556d28 114sub expire_session {
7518e927 115 my($self, $id, $res, $env) = @_;
116 $self->state->expire_session_id($id, $res, $env->{'psgix.session.options'});
dc556d28 117}
118
119sub save_state {
7518e927 120 my($self, $id, $res, $env) = @_;
121 $self->state->finalize($id, $res, $env->{'psgix.session.options'});
dc556d28 122}
123
bd992981 1241;
125
126__END__
ac4892f4 127
128=pod
129
130=head1 NAME
131
132Plack::Middleware::Session - Middleware for session management
133
134=head1 SYNOPSIS
135
3d92cf47 136 use Plack::Builder;
ac4892f4 137
3d92cf47 138 my $app = sub {
536da026 139 my $env = shift;
4ff41723 140 my $session = $env->{'psgix.session'};
536da026 141 return [
142 200,
143 [ 'Content-Type' => 'text/plain' ],
4ff41723 144 [ "Hello, you've been here for ", $session->{counter}++, "th time!" ],
536da026 145 ];
3d92cf47 146 };
147
148 builder {
149 enable 'Session';
150 $app;
151 };
152
ad80e445 153 # Or, use the File store backend (great if you use multiprocess server)
154 # For more options, see perldoc Plack::Session::Store::File
155 builder {
156 enable 'Session', store => 'File';
157 $app;
158 };
159
ac4892f4 160=head1 DESCRIPTION
161
3d92cf47 162This is a Plack Middleware component for session management. By
ad80e445 163default it will use cookies to keep session state and store data in
164memory. This distribution also comes with other state and store
165solutions. See perldoc for these backends how to use them.
3d92cf47 166
4ff41723 167It should be noted that we store the current session as a hash
168reference in the C<psgix.session> key inside the C<$env> where you can
169access it as needed.
170
171B<NOTE:> As of version 0.04 the session is stored in C<psgix.session>
172instead of C<plack.session>.
b84f31d0 173
4ff41723 174Also, if you set I<session_class> option (see below), we create a
175session object out of the hash reference in C<plack.session>.
536da026 176
3d92cf47 177=head2 State
178
179=over 4
180
181=item L<Plack::Session::State>
182
183This will maintain session state by passing the session through
184the request params. It does not do this automatically though,
185you are responsible for passing the session param.
186
187=item L<Plack::Session::State::Cookie>
188
189This will maintain session state using browser cookies.
190
191=back
192
193=head2 Store
194
195=over 4
196
197=item L<Plack::Session::Store>
198
199This is your basic in-memory session data store. It is volatile storage
200and not recommended for multiprocessing environments. However it is
201very useful for development and testing.
202
203=item L<Plack::Session::Store::File>
204
205This will persist session data in a file. By default it uses
206L<Storable> but it can be configured to have a custom serializer and
207deserializer.
208
20ede533 209=item L<Plack::Session::Store::Cache>
3d92cf47 210
20ede533 211This will persist session data using the L<Cache> interface.
3d92cf47 212
213=item L<Plack::Session::Store::Null>
214
215Sometimes you don't care about storing session data, in that case
216you can use this noop module.
217
218=back
219
30cc0a71 220=head1 OPTIONS
221
222The following are options that can be passed to this mdoule.
223
224=over 4
225
226=item I<state>
227
228This is expected to be an instance of L<Plack::Session::State> or an
229object that implements the same interface. If no option is provided
230the default L<Plack::Session::State::Cookie> will be used.
231
232=item I<store>
233
234This is expected to be an instance of L<Plack::Session::Store> or an
235object that implements the same interface. If no option is provided
236the default L<Plack::Session::Store> will be used.
237
238It should be noted that this default is an in-memory volatile store
239is only suitable for development (or single process servers). For a
240more robust solution see L<Plack::Session::Store::File> or
241L<Plack::Session::Store::Cache>.
242
243=item I<session_class>
244
4ff41723 245This can be used to create an actual session object in
246C<plack.session> environment. Defaults to none, which means the
247session object is not created but you can set C<Plack::Session> to
248create an object for you.
30cc0a71 249
250=back
251
ac4892f4 252=head1 BUGS
253
254All complex software has bugs lurking in it, and this module is no
255exception. If you find a bug please either email me, or add the bug
256to cpan-RT.
257
258=head1 AUTHOR
259
260Tatsuhiko Miyagawa
261
262Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
263
264=head1 COPYRIGHT AND LICENSE
265
000c696e 266Copyright 2009, 2010 Infinity Interactive, Inc.
ac4892f4 267
268L<http://www.iinteractive.com>
269
270This library is free software; you can redistribute it and/or modify
271it under the same terms as Perl itself.
272
273=cut
274
275