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