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 4     4   2304 use 5.008005;
  4         15  
4 4     4   20 use strict;
  4         4  
  4         78  
5 4     4   14 use warnings;
  4         6  
  4         89  
6 4     4   348 use parent qw/Plack::Middleware/;
  4         232  
  4         18  
7 4     4   11240 use Digest::SHA1 qw//;
  4         1850  
  4         96  
8 4     4   1400 use Cookie::Baker;
  4         6006  
  4         177  
9 4     4   21 use Plack::Util;
  4         9  
  4         86  
10 4     4   17 use Scalar::Util qw/blessed/;
  4         7  
  4         192  
11 4         23 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 4     4   18 /;
  4         9  
24              
25             our $VERSION = "0.04";
26              
27             sub prepare_app {
28 3     3 1 798 my $self = shift;
29              
30 3         9 my $store = $self->store;
31 3 50 33     136 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 3 50       9 $self->cookie_name('simple_session') unless $self->cookie_name;
38 3 50       18 $self->path('/') unless defined $self->path;
39 3 100       31 $self->keep_empty(1) unless defined $self->keep_empty;
40              
41 3 50       26 if ( !$self->sid_generator ) {
42             $self->sid_generator(sub{
43 0     0   0 Digest::SHA1::sha1_hex(rand() . $$ . {} . time)
44 3         21 });
45             }
46 3 50       16 if ( !$self->sid_validator ) {
47 3         19 $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 4     4   3422 use strict;
  4         14  
  4         100  
172 4     4   20 use warnings;
  4         8  
  4         93  
173 4     4   404 use Tie::Hash;
  4         757  
  4         106  
174 4     4   19 use base qw/Tie::ExtraHash/;
  4         29  
  4         1535  
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__