File Coverage

blib/lib/MasonX/Request/WithMultiSession.pm
Criterion Covered Total %
statement 65 73 89.0
branch 19 24 79.1
condition 3 6 50.0
subroutine 12 12 100.0
pod 4 4 100.0
total 103 119 86.5


line stmt bran cond sub pod time code
1             package MasonX::Request::WithMultiSession;
2              
3 1     1   30745 use strict;
  1         4  
  1         43  
4              
5 1     1   1263 use Digest::SHA1 ();
  1         1262  
  1         33  
6 1     1   2076 use Time::HiRes;
  1         2366  
  1         6  
7              
8 1     1   222 use base qw(MasonX::Request::WithApacheSession);
  1         3  
  1         886  
9              
10 1     1   7 use HTML::Mason::Exceptions ( abbr => [ qw( param_error error ) ] );
  1         2  
  1         7  
11              
12 1     1   70 use Params::Validate qw( validate SCALAR );
  1         1  
  1         1069  
13             Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } );
14              
15             __PACKAGE__->valid_params
16             ( multi_session_args_param =>
17             { type => SCALAR,
18             default => 'sub_session_id',
19             descr => 'The parameter name which contains the sub-session id',
20             },
21              
22             multi_session_expire =>
23             { type => Params::Validate::SCALAR,
24             default => undef,
25             descr => 'How long a sub-session stays valid',
26             },
27             );
28              
29             sub session
30             {
31 15     15 1 47 my $self = shift;
32              
33 15 100       51 return $self->parent_request->session(@_) if $self->is_subrequest;
34              
35 14         215 my %p = @_;
36              
37 14 100       50 my %super_p = exists $p{session_id} ? ( session_id => $p{session_id} ) : ();
38 14         82 my $session = $self->SUPER::session(%super_p);
39              
40 14 100       2114 my %sub_session_p =
41             exists $p{sub_session_id} ? ( sub_session_id => $p{sub_session_id} ) : ();
42 14         126 my $id = $self->sub_session_id(%sub_session_p);
43              
44 14 100 66     103 if ( $p{clone} || $p{new} )
45             {
46             # forces creation of a new id
47 1         2 delete $self->{sub_session_id};
48 1         4 my $new_id = $self->_make_new_sub_session_id;
49              
50 1 50       5 if ( $p{clone} )
51             {
52             # shallow copy of old session
53 1         2 $session->{sub_sessions}{$new_id} = { %{ $session->{sub_sessions}{$id} } };
  1         5  
54             }
55              
56 1         16 $id = $new_id;
57             }
58              
59 14         75 $session->{sub_session_ids}{$id} = int(time);
60              
61 14         141 return $session->{sub_sessions}{$id};
62             }
63              
64             sub sub_session_id
65             {
66 15     15 1 30 my $self = shift;
67 15         282 my %p = validate( @_,
68             { sub_session_id =>
69             { type => SCALAR,
70             optional => 1,
71             },
72             } );
73              
74 15 100       103 unless ( exists $self->{sub_session_id} )
75             {
76 8         62 my $args = $self->request_args;
77              
78 8         70 my $args_key = $self->{multi_session_args_param};
79              
80 8         36 my $session = $self->SUPER::session;
81 8 100 33     280 if ( exists $p{sub_session_id} )
    50          
82             {
83 2 100       11 unless ( exists $session->{sub_session_ids}{ $p{sub_session_id} } )
84             {
85 1         23 $session->{sub_sessions}{ $p{sub_session_id} } = {};
86             }
87              
88 2         30 $self->{sub_session_id} = $p{sub_session_id};
89             }
90             elsif ( exists $args->{$args_key} &&
91             exists $session->{sub_session_ids}{ $args->{$args_key} } )
92             {
93 0         0 $self->{sub_session_id} = $args->{$args_key};
94             }
95             else
96             {
97 6         24 $self->_make_new_sub_session_id;
98             }
99             }
100              
101 15         98 return $self->{sub_session_id};
102             }
103              
104             sub _make_new_sub_session_id
105             {
106 7     7   11 my $self = shift;
107              
108 7         28 my $session = $self->SUPER::session;
109              
110 7         255 my $new_id;
111              
112             do
113 7         32 {
114             # using Time::HiRes means that we get times with very high
115             # floating point resolutions (to 10 or 11 decimal places), so
116             # this is a good seed for a hashing algorithm
117 7         284 $new_id = Digest::SHA1::sha1_hex( time() . {} . rand() . $$ );
118             } while ( exists $session->{sub_session_ids}{$new_id} );
119              
120 7         241 $session->{sub_sessions}{$new_id} = {};
121              
122 7         112 $self->{sub_session_id} = $new_id;
123              
124 7         40 return $new_id;
125             }
126              
127             sub delete_sub_session
128             {
129 2     2 1 7 my $self = shift;
130              
131 2         8 my $session = $self->SUPER::session;
132              
133 2         97 my %p = validate( @_,
134             { sub_session_id =>
135             { type => SCALAR,
136             optional => 1,
137             },
138             } );
139              
140 2 100       17 my $sub_id = $p{sub_session_id} ? $p{sub_session_id} : delete $self->{sub_session_id};
141              
142 2         9 delete $session->{sub_sessions}{$sub_id};
143 2         22 delete $session->{sub_session_ids}{$sub_id};
144             }
145              
146             sub delete_session
147             {
148 1     1 1 3 my $self = shift;
149              
150 1         10 $self->SUPER::delete_session;
151              
152 1         309 delete $self->{sub_session_id};
153             }
154              
155             sub DESTROY
156             {
157 8     8   2019 my $self = shift;
158              
159 8 50       144 return unless defined $self->{multi_session_expire};
160              
161 0           my $session = $self->SUPER::session;
162              
163 0           my $cutoff = int(time) - $self->{multi_session_expire};
164 0           foreach my $id ( keys %{ $session->{sub_session_ids} } )
  0            
165             {
166 0 0         if ( $session->{sub_session_ids}{$id} < $cutoff )
167             {
168 0           delete $session->{sub_sessions}{$id};
169 0           delete $session->{sub_session_ids}{$id};
170             }
171             }
172             }
173              
174              
175             1;
176              
177             __END__