Refactored Middleware::Session to call ->state and ->store inside more
[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]});
dc556d28 66 $self->finalize($request, $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 {
88 my($self, $session, $options) = @_;
89 if ($options->{expire}) {
90 $self->store->cleanup($options->{id});
91 } else {
92 $self->store->store($options->{id}, $session);
93 }
94}
95
96sub finalize {
dc556d28 97 my($self, $request, $response) = @_;
98
99 my $session = $request->env->{'psgix.session'};
100 my $options = $request->env->{'psgix.session.options'};
4ff41723 101
399edae6 102 $self->commit($session, $options) unless $options->{no_store};
98b27b31 103 if ($options->{expire}) {
dc556d28 104 $self->expire_session($options->{id}, $response, $session, $options);
4ff41723 105 } else {
dc556d28 106 $self->save_state($options->{id}, $response, $session, $options);
4ff41723 107 }
108}
109
dc556d28 110sub expire_session {
111 my($self, $id, $res, $session, $options) = @_;
112 $self->state->expire_session_id($options->{id}, $res, $options);
113}
114
115sub save_state {
116 my($self, $id, $res, $session, $options) = @_;
117 $self->state->finalize($id, $res, $options);
118}
119
bd992981 1201;
121
122__END__
ac4892f4 123
124=pod
125
126=head1 NAME
127
128Plack::Middleware::Session - Middleware for session management
129
130=head1 SYNOPSIS
131
3d92cf47 132 use Plack::Builder;
ac4892f4 133
3d92cf47 134 my $app = sub {
536da026 135 my $env = shift;
4ff41723 136 my $session = $env->{'psgix.session'};
536da026 137 return [
138 200,
139 [ 'Content-Type' => 'text/plain' ],
4ff41723 140 [ "Hello, you've been here for ", $session->{counter}++, "th time!" ],
536da026 141 ];
3d92cf47 142 };
143
144 builder {
145 enable 'Session';
146 $app;
147 };
148
ad80e445 149 # Or, use the File store backend (great if you use multiprocess server)
150 # For more options, see perldoc Plack::Session::Store::File
151 builder {
152 enable 'Session', store => 'File';
153 $app;
154 };
155
ac4892f4 156=head1 DESCRIPTION
157
3d92cf47 158This is a Plack Middleware component for session management. By
ad80e445 159default it will use cookies to keep session state and store data in
160memory. This distribution also comes with other state and store
161solutions. See perldoc for these backends how to use them.
3d92cf47 162
4ff41723 163It should be noted that we store the current session as a hash
164reference in the C<psgix.session> key inside the C<$env> where you can
165access it as needed.
166
167B<NOTE:> As of version 0.04 the session is stored in C<psgix.session>
168instead of C<plack.session>.
b84f31d0 169
4ff41723 170Also, if you set I<session_class> option (see below), we create a
171session object out of the hash reference in C<plack.session>.
536da026 172
3d92cf47 173=head2 State
174
175=over 4
176
177=item L<Plack::Session::State>
178
179This will maintain session state by passing the session through
180the request params. It does not do this automatically though,
181you are responsible for passing the session param.
182
183=item L<Plack::Session::State::Cookie>
184
185This will maintain session state using browser cookies.
186
187=back
188
189=head2 Store
190
191=over 4
192
193=item L<Plack::Session::Store>
194
195This is your basic in-memory session data store. It is volatile storage
196and not recommended for multiprocessing environments. However it is
197very useful for development and testing.
198
199=item L<Plack::Session::Store::File>
200
201This will persist session data in a file. By default it uses
202L<Storable> but it can be configured to have a custom serializer and
203deserializer.
204
20ede533 205=item L<Plack::Session::Store::Cache>
3d92cf47 206
20ede533 207This will persist session data using the L<Cache> interface.
3d92cf47 208
209=item L<Plack::Session::Store::Null>
210
211Sometimes you don't care about storing session data, in that case
212you can use this noop module.
213
214=back
215
30cc0a71 216=head1 OPTIONS
217
218The following are options that can be passed to this mdoule.
219
220=over 4
221
222=item I<state>
223
224This is expected to be an instance of L<Plack::Session::State> or an
225object that implements the same interface. If no option is provided
226the default L<Plack::Session::State::Cookie> will be used.
227
228=item I<store>
229
230This is expected to be an instance of L<Plack::Session::Store> or an
231object that implements the same interface. If no option is provided
232the default L<Plack::Session::Store> will be used.
233
234It should be noted that this default is an in-memory volatile store
235is only suitable for development (or single process servers). For a
236more robust solution see L<Plack::Session::Store::File> or
237L<Plack::Session::Store::Cache>.
238
239=item I<session_class>
240
4ff41723 241This can be used to create an actual session object in
242C<plack.session> environment. Defaults to none, which means the
243session object is not created but you can set C<Plack::Session> to
244create an object for you.
30cc0a71 245
246=back
247
ac4892f4 248=head1 BUGS
249
250All complex software has bugs lurking in it, and this module is no
251exception. If you find a bug please either email me, or add the bug
252to cpan-RT.
253
254=head1 AUTHOR
255
256Tatsuhiko Miyagawa
257
258Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
259
260=head1 COPYRIGHT AND LICENSE
261
000c696e 262Copyright 2009, 2010 Infinity Interactive, Inc.
ac4892f4 263
264L<http://www.iinteractive.com>
265
266This library is free software; you can redistribute it and/or modify
267it under the same terms as Perl itself.
268
269=cut
270
271