File Coverage

blib/lib/HTTP/Session.pm
Criterion Covered Total %
statement 110 113 97.3
branch 25 30 83.3
condition 12 15 80.0
subroutine 25 25 100.0
pod 10 10 100.0
total 182 193 94.3


line stmt bran cond sub pod time code
1             package HTTP::Session;
2 26     26   2190600 use strict;
  26         46  
  26         915  
3 26     26   210 use warnings;
  26         39  
  26         1322  
4 26     26   151 use base qw/Class::Accessor::Fast/;
  26         57  
  26         11888  
5 26     26   66144 use 5.00800;
  26         104  
6             our $VERSION = '0.54';
7 26     26   116 use Carp ();
  26         46  
  26         376  
8 26     26   95 use Scalar::Util ();
  26         35  
  26         416  
9 26     26   11871 use Module::Runtime ();
  26         42211  
  26         728  
10 26     26   10601 use HTTP::Session::Finalized;
  26         68  
  26         740  
11 26     26   9899 use HTTP::Session::Expired;
  26         71  
  26         24625  
12              
13             __PACKAGE__->mk_ro_accessors(qw/store request sid_length save_modified_session_only/);
14             __PACKAGE__->mk_accessors(qw/session_id _data is_changed is_fresh state/);
15              
16             sub new {
17 73     73 1 275221 my $class = shift;
18 73 50       340 my %args = ref($_[0]) ? %{$_[0]} : @_;
  0         0  
19             # check required parameters
20 73         157 for my $key (qw/store state request/) {
21 217 100       924 Carp::croak "missing parameter $key" unless $args{$key};
22             }
23             # set default values
24 72   50     362 $args{_data} ||= {};
25 72   100     263 $args{save_modified_session_only} ||= 0;
26 72   50     258 $args{is_changed} ||= 0;
27 72   50     236 $args{is_fresh} ||= 0;
28 72   100     248 $args{sid_length} ||= 32;
29 72 50       457 if ( $args{sid_length} !~ /\A[1-9][0-9]*\z/ ) {
30 0         0 Carp::croak "sid_length must be a positive integer";
31             }
32 72   100     306 $args{id} ||= 'HTTP::Session::ID::Urandom';
33 72         369 my $self = bless {%args}, $class;
34 72         216 $self->_load_session();
35 72 50       511 Carp::croak "[BUG] we have bug" unless $self->{request};
36 72         388 $self;
37             }
38              
39             sub _load_session {
40 72     72   107 my $self = shift;
41              
42 72         1659 my $session_id = $self->state->get_session_id($self->request);
43 72 100       3583 if ( $session_id ) {
44 44         729 my $data = $self->store->select($session_id);
45 44 100       331 if ($data) {
46 35         457 $self->session_id( $session_id );
47 35         600 $self->_data($data);
48             } else {
49 9 100       207 if ($self->state->permissive) {
50 2         64 $self->session_id( $session_id );
51 2         78 $self->is_fresh(1);
52             } else {
53             # session was expired? or session fixation?
54             # regen session id.
55 7         204 $self->session_id( $self->_generate_session_id() );
56 7         150 $self->is_fresh(1);
57             }
58             }
59             } else {
60             # no sid; generate it
61 28         79 $self->session_id( $self->_generate_session_id() );
62 28         705 $self->is_fresh(1);
63             }
64             }
65              
66             sub _generate_session_id {
67 41     41   86 my $self = shift;
68 41 50       208 Module::Runtime::require_module($self->{id}) or die $@;
69 41         1732 $self->{id}->generate_id($self->sid_length);
70             }
71              
72             sub response_filter {
73 29     29 1 16061 my ($self, $response) = @_;
74 29 100       323 Carp::croak "missing response" unless ref $response;
75              
76 28         657 $self->state->response_filter($self->session_id, $response);
77             }
78              
79             sub finalize {
80 72     72 1 206 my ($self, ) = @_;
81              
82 72 100       1980 if ($self->is_fresh) {
83 38 100 100     822 if ($self->is_changed || !$self->save_modified_session_only) {
84 37         1368 $self->store->insert( $self->session_id, $self->_data );
85             }
86             } else {
87 34 100       796 if ($self->is_changed) {
88 2         36 $self->store->update( $self->session_id, $self->_data );
89             }
90             }
91              
92 72         1835 delete $self->{$_} for keys %$self;
93 72         2408 bless $self, 'HTTP::Session::Finalized';
94             }
95              
96             sub DESTROY {
97 67     67   41543 my $self = shift;
98              
99 67 50       262 if ($self->{store}) {
100 67         211 $self->finalize();
101             } else {
102             # this case happen at global destruction?
103 0         0 Carp::carp "you should call HTTP::Session->finalize method manually";
104             }
105             }
106              
107             sub keys {
108 1     1 1 2977 my $self = shift;
109 1         3 return keys %{ $self->_data };
  1         48  
110             }
111              
112             sub get {
113 24     24 1 7003 my ($self, $key) = @_;
114 24         486 $self->_data->{$key};
115             }
116              
117             sub set {
118 15     15 1 6034 my ($self, $key, $val) = @_;
119 15         297 $self->is_changed(1);
120 15         314 $self->_data->{$key} = $val;
121             }
122              
123             sub remove {
124 3     3 1 849 my ( $self, $key ) = @_;
125 3         108 $self->is_changed(1);
126 3         75 delete $self->_data->{$key};
127             }
128              
129             sub as_hashref {
130 3     3 1 4829 my $self = shift;
131 3         7 return { %{ $self->_data } }; # shallow copy
  3         126  
132             }
133              
134             sub expire {
135 2     2 1 26 my $self = shift;
136 2         36 $self->store->delete($self->session_id);
137              
138             # XXX tricky bit to unlock
139 2         19 delete $self->{$_} for qw(is_fresh is_changed);
140 2         7 $self->DESTROY;
141              
142             # rebless to null class
143 2         7 bless $self, 'HTTP::Session::Expired';
144             }
145              
146             sub regenerate_session_id {
147 3     3 1 2632 my ($self, $delete_old) = @_;
148 3         5 $self->_data( { %{ $self->_data } } );
  3         53  
149              
150 3 100       29 if ($delete_old) {
151 1         14 my $oldsid = $self->session_id;
152 1         15 $self->store->delete($oldsid);
153             }
154 3         11 my $session_id = $self->_generate_session_id();
155 3         49 $self->session_id( $session_id );
156 3         47 $self->is_fresh(1);
157             }
158              
159             BEGIN {
160 26     26   201 no strict 'refs';
  26         203  
  26         3140  
161 26     26   101 for my $meth (qw/redirect_filter header_filter html_filter/) {
162 78         1200 *{__PACKAGE__ . '::' . $meth} = sub {
163 6     6   2810 my ($self, $stuff) = @_;
164 6 100       132 if ($self->state->can($meth)) {
165 4         117 $self->state->$meth($self->session_id, $stuff);
166             } else {
167 2         23 $stuff;
168             }
169 78         351 };
170             }
171             }
172              
173             1;
174             __END__