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