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