File Coverage

blib/lib/Plack/Middleware/Session/Simple.pm
Criterion Covered Total %
statement 48 116 41.3
branch 7 52 13.4
condition 3 61 4.9
subroutine 14 25 56.0
pod 2 4 50.0
total 74 258 28.6


line stmt bran cond sub pod time code
1             package Plack::Middleware::Session::Simple;
2              
3 3     3   1796987 use 5.008005;
  3         12  
4 3     3   17 use strict;
  3         11  
  3         102  
5 3     3   20 use warnings;
  3         6  
  3         210  
6 3     3   19 use parent qw/Plack::Middleware/;
  3         5  
  3         26  
7 3     3   20042 use Crypt::SysRandom qw/random_bytes/;
  3         5582  
  3         268  
8 3     3   1678 use Cookie::Baker;
  3         8902  
  3         228  
9 3     3   23 use Plack::Util;
  3         5  
  3         94  
10 3     3   19 use Scalar::Util qw/blessed/;
  3         5  
  3         191  
11 3         26 use Plack::Util::Accessor qw/
12             store
13             cookie_name
14             sid_generator
15             sid_validator
16             keep_empty
17             path
18             domain
19             expires
20             secure
21             httponly
22             serializer
23 3     3   19 /;
  3         6  
24              
25             our $VERSION = "0.05";
26              
27             sub prepare_app {
28 2     2 1 675 my $self = shift;
29              
30 2         13 my $store = $self->store;
31 2 50 33     145 die('store require get, set and remove method.')
      33        
      33        
32             unless blessed $store
33             && $store->can('get')
34             && $store->can('set')
35             && $store->can('remove');
36              
37 2 50       17 $self->cookie_name('simple_session') unless $self->cookie_name;
38 2 50       19 $self->path('/') unless defined $self->path;
39 2 100       23 $self->keep_empty(1) unless defined $self->keep_empty;
40              
41 2 50       18 if ( !$self->sid_generator ) {
42             $self->sid_generator(sub{
43 0     0   0 unpack('H*', Crypt::SysRandom::random_bytes(20));
44 2         16 });
45             }
46 2 50       12 if ( !$self->sid_validator ) {
47 2         16 $self->sid_validator(
48             qr/\A[0-9a-f]{40}\Z/
49             );
50             }
51              
52             }
53              
54             sub call {
55 0     0 1   my ($self,$env) = @_;
56              
57 0           my($id, $session) = $self->get_session($env);
58              
59 0           my $tied;
60 0 0 0       if ($id && $session) {
61 0           $tied = tie my %session,
62             'Plack::Middleware::Session::Simple::Session', %$session;
63 0           $env->{'psgix.session'} = \%session;
64 0           $env->{'psgix.session.options'} = {
65             id => $id,
66             };
67             } else {
68 0           my $id = $self->{sid_generator}->();
69 0           $tied = tie my %session,
70             'Plack::Middleware::Session::Simple::Session';
71 0           $env->{'psgix.session'} = \%session;
72 0           $env->{'psgix.session.options'} = {
73             id => $id,
74             new_session => 1,
75             };
76             }
77              
78 0           my $res = $self->app->($env);
79              
80             $self->response_cb(
81             $res, sub {
82 0     0     $self->finalize($env, $_[0], $tied)
83             }
84 0           );
85             }
86              
87             sub get_session {
88 0     0 0   my ($self, $env) = @_;
89 0   0       my $cookie = crush_cookie($env->{HTTP_COOKIE} || '')->{$self->{cookie_name}};
90 0 0         return unless defined $cookie;
91 0 0         return unless $cookie =~ $self->{sid_validator};
92              
93 0 0         my $session = $self->{store}->get($cookie) or return;
94 0 0         $session = $self->{serializer}->[1]->($session) if $self->{serializer};
95 0           return ($cookie, $session);
96             }
97              
98             sub finalize {
99 0     0 0   my ($self, $env, $res, $session) = @_;
100 0           my $options = $env->{'psgix.session.options'};
101 0           my $new_session = delete $options->{new_session};
102              
103 0           my $need_store;
104 0 0 0       if ( ($new_session && $self->{keep_empty} && ! $session->has_key )
      0        
      0        
      0        
      0        
105             || $session->[1] || $options->{expire} || $options->{change_id}) {
106 0           $need_store = 1;
107             }
108 0 0         $need_store = 0 if $options->{no_store};
109              
110 0           my $set_cookie;
111 0 0 0       if ( ($new_session && $self->{keep_empty} && ! $session->has_key )
      0        
      0        
      0        
      0        
      0        
112             || ($new_session && $session->[1] )
113             || $options->{expire} || $options->{change_id}) {
114 0           $set_cookie = 1;
115             }
116              
117 0 0         if ( $need_store ) {
118 0 0         if ($options->{expire}) {
    0          
119 0           $self->{store}->remove($options->{id});
120             } elsif ($options->{change_id}) {
121 0           $self->{store}->remove($options->{id});
122 0           $options->{id} = $self->{sid_generator}->();
123 0           my $val = $session->[0];
124 0 0         $val = $self->{serializer}->[0]->($val) if $self->{serializer};
125 0           $self->{store}->set($options->{id}, $val);
126             } else {
127 0           my $val = $session->[0];
128 0 0         $val = $self->{serializer}->[0]->($val) if $self->{serializer};
129 0           $self->{store}->set($options->{id}, $val);
130             }
131             }
132              
133 0 0         if ( $set_cookie ) {
134 0 0         if ($options->{expire}) {
135             $self->_set_cookie(
136 0           $options->{id}, $res, %$options, expires => 'now');
137             } else {
138             $self->_set_cookie(
139 0           $options->{id}, $res, %$options);
140             }
141             }
142             }
143              
144             sub _set_cookie {
145 0     0     my($self, $id, $res, %options) = @_;
146              
147 0           delete $options{id};
148              
149 0 0 0       $options{path} = $self->{path} || '/' if !exists $options{path};
150 0 0 0       $options{domain} = $self->{domain} if !exists $options{domain} && defined $self->{domain};
151 0 0 0       $options{secure} = $self->{secure} if !exists $options{secure} && defined $self->{secure};
152 0 0 0       $options{httponly} = $self->{httponly} if !exists $options{httponly} && defined $self->{httponly};
153              
154 0 0 0       if (!exists $options{expires} && defined $self->{expires}) {
155 0           $options{expires} = $self->{expires};
156             }
157              
158             my $cookie = bake_cookie(
159             $self->{cookie_name}, {
160 0           value => $id,
161             %options,
162             }
163             );
164 0           Plack::Util::header_push($res->[1], 'Set-Cookie', $cookie);
165             }
166              
167             1;
168              
169             package Plack::Middleware::Session::Simple::Session;
170              
171 3     3   4102 use strict;
  3         6  
  3         77  
172 3     3   13 use warnings;
  3         7  
  3         204  
173 3     3   647 use Tie::Hash;
  3         1227  
  3         111  
174 3     3   14 use base qw/Tie::ExtraHash/;
  3         8  
  3         1724  
175              
176             sub TIEHASH {
177 0     0     my $class = shift;
178 0           bless [{@_},0, scalar @_], $class;
179             }
180              
181             sub STORE {
182 0     0     $_[0]->[1]++;
183 0           $_[0]->[0]{$_[1]} = $_[2]
184             }
185              
186             sub DELETE {
187 0     0     $_[0]->[1]++;
188 0           delete $_[0]->[0]->{$_[1]}
189             }
190              
191             sub CLEAR {
192 0     0     $_[0]->[1]++;
193 0           %{$_[0]->[0]} = ()
  0            
194             }
195              
196             sub has_key {
197 0     0     scalar keys %{$_[0]->[0]}
  0            
198             }
199              
200             1;
201              
202             __END__