File Coverage

blib/lib/HTTP/Session.pm
Criterion Covered Total %
statement 104 107 97.2
branch 24 28 85.7
condition 12 15 80.0
subroutine 24 27 88.8
pod 10 10 100.0
total 174 187 93.0


line stmt bran cond sub pod time code
1             package HTTP::Session;
2 25     25   1142365 use strict;
  25         208  
  25         650  
3 25     25   110 use warnings;
  25         40  
  25         609  
4 25     25   115 use base qw/Class::Accessor::Fast/;
  25         37  
  25         9929  
5 25     25   57070 use 5.00800;
  25         77  
6             our $VERSION = '0.48';
7 25     25   129 use Carp ();
  25         35  
  25         318  
8 25     25   99 use Scalar::Util ();
  25         41  
  25         280  
9 25     25   11333 use Module::Runtime ();
  25         37874  
  25         18837  
10              
11             __PACKAGE__->mk_ro_accessors(qw/store request sid_length save_modified_session_only/);
12             __PACKAGE__->mk_accessors(qw/session_id _data is_changed is_fresh state/);
13              
14             sub new {
15 61     61 1 26943 my $class = shift;
16 61 50       267 my %args = ref($_[0]) ? %{$_[0]} : @_;
  0         0  
17             # check required parameters
18 61         136 for my $key (qw/store state request/) {
19 181 100       760 Carp::croak "missing parameter $key" unless $args{$key};
20             }
21             # set default values
22 60   50     286 $args{_data} ||= {};
23 60   100     238 $args{save_modified_session_only} ||= 0;
24 60   50     236 $args{is_changed} ||= 0;
25 60   50     221 $args{is_fresh} ||= 0;
26 60   100     221 $args{sid_length} ||= 32;
27 60   100     225 $args{id} ||= 'HTTP::Session::ID::SHA1';
28 60         302 my $self = bless {%args}, $class;
29 60         186 $self->_load_session();
30 60 50       452 Carp::croak "[BUG] we have bug" unless $self->{request};
31 60         238 $self;
32             }
33              
34             sub _load_session {
35 60     60   122 my $self = shift;
36              
37 60         1277 my $session_id = $self->state->get_session_id($self->request);
38 60 100       1879 if ( $session_id ) {
39 33         545 my $data = $self->store->select($session_id);
40 33 100       296 if ($data) {
41 25         402 $self->session_id( $session_id );
42 25         479 $self->_data($data);
43             } else {
44 8 100       144 if ($self->state->permissive) {
45 2         77 $self->session_id( $session_id );
46 2         45 $self->is_fresh(1);
47             } else {
48             # session was expired? or session fixation?
49             # regen session id.
50 6         146 $self->session_id( $self->_generate_session_id() );
51 6         151 $self->is_fresh(1);
52             }
53             }
54             } else {
55             # no sid; generate it
56 27         69 $self->session_id( $self->_generate_session_id() );
57 27         563 $self->is_fresh(1);
58             }
59             }
60              
61             sub _generate_session_id {
62 39     39   64 my $self = shift;
63 39 50       129 Module::Runtime::require_module($self->{id}) or die $@;
64 39         1312 $self->{id}->generate_id($self->sid_length);
65             }
66              
67             sub response_filter {
68 19     19 1 8582 my ($self, $response) = @_;
69 19 100       219 Carp::croak "missing response" unless ref $response;
70              
71 18         354 $self->state->response_filter($self->session_id, $response);
72             }
73              
74             sub finalize {
75 60     60 1 158 my ($self, ) = @_;
76              
77 60 100       1180 if ($self->is_fresh) {
78 36 100 100     660 if ($self->is_changed || !$self->save_modified_session_only) {
79 35         1224 $self->store->insert( $self->session_id, $self->_data );
80             }
81             } else {
82 24 100       456 if ($self->is_changed) {
83 2         36 $self->store->update( $self->session_id, $self->_data );
84             }
85             }
86              
87 60         1322 delete $self->{$_} for keys %$self;
88 60         2169 bless $self, 'HTTP::Session::Finalized';
89             }
90              
91             sub DESTROY {
92 55     55   26192 my $self = shift;
93              
94 55 50       169 if ($self->{store}) {
95 55         135 $self->finalize();
96             } else {
97             # this case happen at global destruction?
98 0         0 Carp::carp "you should call HTTP::Session->finalize method manually";
99             }
100             }
101              
102             sub keys {
103 1     1 1 491 my $self = shift;
104 1         2 return keys %{ $self->_data };
  1         21  
105             }
106              
107             sub get {
108 24     24 1 5293 my ($self, $key) = @_;
109 24         412 $self->_data->{$key};
110             }
111              
112             sub set {
113 15     15 1 2701 my ($self, $key, $val) = @_;
114 15         270 $self->is_changed(1);
115 15         309 $self->_data->{$key} = $val;
116             }
117              
118             sub remove {
119 3     3 1 445 my ( $self, $key ) = @_;
120 3         51 $self->is_changed(1);
121 3         53 delete $self->_data->{$key};
122             }
123              
124             sub as_hashref {
125 3     3 1 748 my $self = shift;
126 3         7 return { %{ $self->_data } }; # shallow copy
  3         56  
127             }
128              
129             sub expire {
130 2     2 1 29 my $self = shift;
131 2         31 $self->store->delete($self->session_id);
132              
133             # XXX tricky bit to unlock
134 2         16 delete $self->{$_} for qw(is_fresh is_changed);
135 2         8 $self->DESTROY;
136              
137             # rebless to null class
138 2         7 bless $self, 'HTTP::Session::Expired';
139             }
140              
141             sub regenerate_session_id {
142 3     3 1 2008 my ($self, $delete_old) = @_;
143 3         5 $self->_data( { %{ $self->_data } } );
  3         58  
144              
145 3 100       37 if ($delete_old) {
146 1         26 my $oldsid = $self->session_id;
147 1         19 $self->store->delete($oldsid);
148             }
149 3         12 my $session_id = $self->_generate_session_id();
150 3         61 $self->session_id( $session_id );
151 3         62 $self->is_fresh(1);
152             }
153              
154             BEGIN {
155 25     25   181 no strict 'refs';
  25         56  
  25         2544  
156 25     25   92 for my $meth (qw/redirect_filter header_filter html_filter/) {
157 75         2256 *{__PACKAGE__ . '::' . $meth} = sub {
158 6     6   2042 my ($self, $stuff) = @_;
159 6 100       116 if ($self->state->can($meth)) {
160 4         98 $self->state->$meth($self->session_id, $stuff);
161             } else {
162 2         22 $stuff;
163             }
164 75         290 };
165             }
166             }
167              
168             package HTTP::Session::Finalized;
169 0     0   0 sub is_fresh { 0 }
170       0     sub AUTOLOAD { }
171              
172             package HTTP::Session::Expired;
173 1     1   313 sub is_fresh { 0 }
174       0     sub AUTOLOAD { }
175              
176             1;
177             __END__