File Coverage

blib/lib/HTTP/Session.pm
Criterion Covered Total %
statement 105 110 95.4
branch 24 28 85.7
condition 12 15 80.0
subroutine 24 27 88.8
pod 10 10 100.0
total 175 190 92.1


line stmt bran cond sub pod time code
1             package HTTP::Session;
2 25     25   615484 use strict;
  25         63  
  25         845  
3 25     25   129 use warnings;
  25         47  
  25         711  
4 25     25   125 use base qw/Class::Accessor::Fast/;
  25         55  
  25         23937  
5 25     25   100810 use 5.00800;
  25         134  
  25         1251  
6             our $VERSION = '0.49';
7 25     25   173 use Carp ();
  25         49  
  25         375  
8 25     25   135 use Scalar::Util ();
  25         50  
  25         416  
9 25     25   31614 use Module::Runtime ();
  25         54369  
  25         25771  
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 119322 my $class = shift;
16 61 50       383 my %args = ref($_[0]) ? %{$_[0]} : @_;
  0         0  
17             # check required parameters
18 61         157 for my $key (qw/store state request/) {
19 181 100       1246 Carp::croak "missing parameter $key" unless $args{$key};
20             }
21             # set default values
22 60   50     394 $args{_data} ||= {};
23 60   100     306 $args{save_modified_session_only} ||= 0;
24 60   50     320 $args{is_changed} ||= 0;
25 60   50     275 $args{is_fresh} ||= 0;
26 60   100     405 $args{sid_length} ||= 32;
27 60   100     261 $args{id} ||= 'HTTP::Session::ID::SHA1';
28 60         426 my $self = bless {%args}, $class;
29 60         260 $self->_load_session();
30 60 50       500 Carp::croak "[BUG] we have bug" unless $self->{request};
31 60         330 $self;
32             }
33              
34             sub _load_session {
35 60     60   103 my $self = shift;
36              
37 60         233 my $session_id = $self->state->get_session_id($self->request);
38 60 100       1646 if ( $session_id ) {
39 33         127 my $data = $self->store->select($session_id);
40 33 100       405 if ($data) {
41 25         93 $self->session_id( $session_id );
42 25         192 $self->_data($data);
43             } else {
44 8 100       43 if ($self->state->permissive) {
45 2         35 $self->session_id( $session_id );
46 2         26 $self->is_fresh(1);
47             } else {
48             # session was expired? or session fixation?
49             # regen session id.
50 6         81 $self->session_id( $self->_generate_session_id() );
51 6         70 $self->is_fresh(1);
52             }
53             }
54             } else {
55             # no sid; generate it
56 27         93 $self->session_id( $self->_generate_session_id() );
57 27         245 $self->is_fresh(1);
58             }
59             }
60              
61             sub _generate_session_id {
62 39     39   82 my $self = shift;
63 39 50       207 Module::Runtime::require_module($self->{id}) or die $@;
64 39         939 $self->{id}->generate_id($self->sid_length);
65             }
66              
67             sub response_filter {
68 19     19 1 9680 my ($self, $response) = @_;
69 19 100       271 Carp::croak "missing response" unless ref $response;
70              
71 18         56 $self->state->response_filter($self->session_id, $response);
72             }
73              
74             sub finalize {
75 60     60 1 141 my ($self, ) = @_;
76              
77 60 100       205 if ($self->is_fresh) {
78 36 100 100     284 if ($self->is_changed || !$self->save_modified_session_only) {
79 35         526 $self->store->insert( $self->session_id, $self->_data );
80             }
81             } else {
82 24 100       174 if ($self->is_changed) {
83 2         10 $self->store->update( $self->session_id, $self->_data );
84             }
85             }
86              
87 60         1573 delete $self->{$_} for keys %$self;
88 60         3341 bless $self, 'HTTP::Session::Finalized';
89             }
90              
91             sub DESTROY {
92 55     55   32291 my $self = shift;
93              
94 55 50       271 if ($self->{store}) {
95 55         180 $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 768 my $self = shift;
104 1         3 return keys %{ $self->_data };
  1         4  
105             }
106              
107             sub get {
108 24     24 1 6407 my ($self, $key) = @_;
109 24         71 $self->_data->{$key};
110             }
111              
112             sub set {
113 15     15 1 3021 my ($self, $key, $val) = @_;
114 15         53 $self->is_changed(1);
115 15         102 $self->_data->{$key} = $val;
116             }
117              
118             sub remove {
119 3     3 1 546 my ( $self, $key ) = @_;
120 3         14 $self->is_changed(1);
121 3         21 delete $self->_data->{$key};
122             }
123              
124             sub as_hashref {
125 3     3 1 897 my $self = shift;
126 3         7 return { %{ $self->_data } }; # shallow copy
  3         10  
127             }
128              
129             sub expire {
130 2     2 1 19 my $self = shift;
131 2         9 $self->store->delete($self->session_id);
132              
133             # XXX tricky bit to unlock
134 2         19 delete $self->{$_} for qw(is_fresh is_changed);
135 2         8 $self->DESTROY;
136              
137             # rebless to null class
138 2         8 bless $self, 'HTTP::Session::Expired';
139             }
140              
141             sub regenerate_session_id {
142 3     3 1 2977 my ($self, $delete_old) = @_;
143 3         7 $self->_data( { %{ $self->_data } } );
  3         13  
144              
145 3 100       40 if ($delete_old) {
146 1         5 my $oldsid = $self->session_id;
147 1         8 $self->store->delete($oldsid);
148             }
149 3         20 my $session_id = $self->_generate_session_id();
150 3         15 $self->session_id( $session_id );
151 3         26 $self->is_fresh(1);
152             }
153              
154             BEGIN {
155 25     25   202 no strict 'refs';
  25         126  
  25         3487  
156 25     25   124 for my $meth (qw/redirect_filter header_filter html_filter/) {
157 75         3010 *{__PACKAGE__ . '::' . $meth} = sub {
158 6     6   2160 my ($self, $stuff) = @_;
159 6 100       112 if ($self->state->can($meth)) {
160 4         71 $self->state->$meth($self->session_id, $stuff);
161             } else {
162 2         25 $stuff;
163             }
164 75         347 };
165             }
166             }
167              
168             package HTTP::Session::Finalized;
169 0     0   0 sub is_fresh { 0 }
170 0     0   0 sub AUTOLOAD { }
171              
172             package HTTP::Session::Expired;
173 1     1   498 sub is_fresh { 0 }
174 0     0     sub AUTOLOAD { }
175              
176             1;
177             __END__