| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package CGI::Cookie::Splitter; | 
| 2 |  |  |  |  |  |  | BEGIN { | 
| 3 | 1 |  |  | 1 |  | 34229 | $CGI::Cookie::Splitter::AUTHORITY = 'cpan:NUFFIN'; | 
| 4 |  |  |  |  |  |  | } | 
| 5 |  |  |  |  |  |  | # git description: v0.03-5-gd375e1a | 
| 6 |  |  |  |  |  |  | $CGI::Cookie::Splitter::VERSION = '0.04'; | 
| 7 |  |  |  |  |  |  | # ABSTRACT: Split big cookies into smaller ones. | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 1 |  |  | 1 |  | 11 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 34 |  | 
| 10 | 1 |  |  | 1 |  | 6 | use warnings; | 
|  | 1 |  |  |  |  | 11 |  | 
|  | 1 |  |  |  |  | 48 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 1 |  |  | 1 |  | 6 | use Scalar::Util qw/blessed/; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 159 |  | 
| 13 | 1 |  |  | 1 |  | 1450 | use CGI::Simple::Util qw/escape unescape/; | 
|  | 1 |  |  |  |  | 5575 |  | 
|  | 1 |  |  |  |  | 131 |  | 
| 14 | 1 |  |  | 1 |  | 14 | use Carp qw/croak/; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 1430 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | sub new { | 
| 17 | 14 |  |  | 14 | 1 | 174406 | my ( $class, %params ) = @_; | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 14 | 100 |  |  |  | 81 | $params{size} = 4096 unless exists $params{size}; | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 14 | 50 | 33 |  |  | 198 | croak "size has to be a positive integer ($params{size} is invalid)" | 
| 22 |  |  |  |  |  |  | unless $params{size} =~ /^\d+$/ and $params{size} > 1; | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 14 |  |  |  |  | 70 | bless \%params, $class; | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 308 |  |  | 308 | 0 | 298813 | sub size { $_[0]{size} } | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | sub split { | 
| 30 | 14 |  |  | 14 | 1 | 15232 | my ( $self, @cookies ) = @_; | 
| 31 | 14 |  |  |  |  | 36 | map { $self->split_cookie($_) } @cookies; | 
|  | 24 |  |  |  |  | 73 |  | 
| 32 |  |  |  |  |  |  | } | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | sub split_cookie { | 
| 35 | 24 |  |  | 24 | 0 | 38 | my ( $self, $cookie ) = @_; | 
| 36 | 24 | 100 |  |  |  | 60 | return $cookie unless $self->should_split( $cookie ); | 
| 37 | 4052 |  |  |  |  | 31383 | return $self->do_split_cookie( | 
| 38 |  |  |  |  |  |  | $self->new_cookie( $cookie, | 
| 39 |  |  |  |  |  |  | name => $self->mangle_name( $cookie->name, 0 ), | 
| 40 | 14 |  |  |  |  | 79 | value => CORE::join("&",map { escape($_) } $cookie->value) # simplifies the string splitting | 
| 41 |  |  |  |  |  |  | ) | 
| 42 |  |  |  |  |  |  | ); | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | sub do_split_cookie { | 
| 46 | 100 |  |  | 100 | 0 | 2157 | my ( $self, $head ) = @_; | 
| 47 |  |  |  |  |  |  |  | 
| 48 | 100 |  |  |  |  | 404 | my $tail = $self->new_cookie( $head, value => '', name => $self->mangle_name_next( $head->name ) ); | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 100 |  |  |  |  | 15554 | my $max_value_size = $self->size - ( $self->cookie_size( $head ) - length( escape($head->value) ) ); | 
| 51 | 100 |  |  |  |  | 131515 | $max_value_size -= 30; # account for overhead the cookie serializer might add | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 100 | 50 |  |  |  | 317 | die "Internal math error, please file a bug for CGI::Cookie::Splitter: max size should be > 0, but is $max_value_size (perhaps other attrs are too big?)" | 
| 54 |  |  |  |  |  |  | unless ( $max_value_size > 0 ); | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 100 |  |  |  |  | 320 | my ( $head_v, $tail_v ) = $self->split_value( $max_value_size, $head->value ); | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 100 |  |  |  |  | 344 | $head->value( $head_v ); | 
| 59 | 100 |  |  |  |  | 1418 | $tail->value( $tail_v ); | 
| 60 |  |  |  |  |  |  |  | 
| 61 | 100 | 50 |  |  |  | 1048 | die "Internal math error, please file a bug for CGI::Cookie::Splitter" | 
| 62 |  |  |  |  |  |  | unless $self->cookie_size( $head ) <= $self->size; # 10 is not enough overhead | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 100 | 100 |  |  |  | 891 | return $head unless $tail_v; | 
| 65 | 86 |  |  |  |  | 263 | return ( $head, $self->do_split_cookie( $tail ) ); | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub split_value { | 
| 69 | 100 |  |  | 100 | 0 | 864 | my ( $self, $max_size, $value ) = @_; | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 100 |  |  |  |  | 147 | my $adjusted_size = $max_size; | 
| 72 |  |  |  |  |  |  |  | 
| 73 | 100 |  |  |  |  | 124 | my ( $head, $tail ); | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 100 | 100 |  |  |  | 275 | return ( $value, '' ) if length($value) <= $adjusted_size; | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 244 | 50 |  |  |  | 498 | split_value: { | 
| 78 | 86 |  |  |  |  | 113 | croak "Can't reduce the size of the cookie anymore (adjusted = $adjusted_size, max = $max_size)" unless $adjusted_size > 0; | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 244 |  |  |  |  | 799 | $head = substr( $value, 0, $adjusted_size ); | 
| 81 | 244 |  |  |  |  | 1110 | $tail = substr( $value, $adjusted_size ); | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 244 | 100 |  |  |  | 717 | if ( length(my $escaped = escape($head)) > $max_size ) { | 
| 84 | 158 |  |  |  |  | 36394 | my $adjustment = int( ( length($escaped) - length($head) ) / 3 ) + 1; | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 158 | 50 |  |  |  | 327 | die "Internal math error, please file a bug for CGI::Cookie::Splitter" | 
| 87 |  |  |  |  |  |  | unless $adjustment; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 158 |  |  |  |  | 201 | $adjusted_size -= $adjustment; | 
| 90 | 158 |  |  |  |  | 364 | redo split_value; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 86 |  |  |  |  | 11828 | return ( $head, $tail ); | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | sub cookie_size { | 
| 98 | 308 |  |  | 308 | 0 | 449 | my ( $self, $cookie ) = @_; | 
| 99 | 308 |  |  |  |  | 941 | length( $cookie->as_string ); | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | sub new_cookie { | 
| 103 | 128 |  |  | 128 | 0 | 2544 | my ( $self, $cookie, %params ) = @_; | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 128 |  |  |  |  | 168 | my %out_params; | 
| 106 | 128 |  |  |  |  | 366 | for (qw/name secure path domain expires value/) { | 
| 107 | 768 | 100 |  |  |  | 5541 | $out_params{"-$_"} = (exists($params{$_}) | 
| 108 |  |  |  |  |  |  | ? $params{$_} : $cookie->$_ | 
| 109 |  |  |  |  |  |  | ); | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 128 |  |  |  |  | 966 | blessed($cookie)->new( %out_params ); | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | sub should_split { | 
| 116 | 108 |  |  | 108 | 1 | 152632 | my ( $self, $cookie ) = @_; | 
| 117 | 108 |  |  |  |  | 332 | $self->cookie_size( $cookie ) > $self->size; | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub join { | 
| 121 | 14 |  |  | 14 | 1 | 21796 | my ( $self, @cookies ) = @_; | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 14 |  |  |  |  | 32 | my %split; | 
| 124 |  |  |  |  |  |  | my @ret; | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 14 |  |  |  |  | 36 | foreach my $cookie ( @cookies ) { | 
| 127 | 110 |  |  |  |  | 335 | my ( $name, $index ) = $self->demangle_name( $cookie->name ); | 
| 128 | 110 | 100 |  |  |  | 209 | if ( $name ) { | 
| 129 | 100 |  |  |  |  | 280 | $split{$name}[$index] = $cookie; | 
| 130 |  |  |  |  |  |  | } else { | 
| 131 | 10 |  |  |  |  | 28 | push @ret, $cookie; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 14 |  |  |  |  | 73 | foreach my $name ( sort { $a cmp $b } keys %split ) { | 
|  | 5 |  |  |  |  | 14 |  | 
| 136 | 14 |  |  |  |  | 535 | my $split_cookie = $split{$name}; | 
| 137 | 14 | 50 |  |  |  | 31 | croak "The cookie $name is missing some chunks" if grep { !defined } @$split_cookie; | 
|  | 100 |  |  |  |  | 182 |  | 
| 138 | 14 |  |  |  |  | 111 | push @ret, $self->join_cookie( $name => @$split_cookie ); | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 14 |  |  |  |  | 3235 | return @ret; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub join_cookie { | 
| 145 | 14 |  |  | 14 | 0 | 44 | my ( $self, $name, @cookies ) = @_; | 
| 146 | 14 |  |  |  |  | 27 | $self->new_cookie( $cookies[0], name => $name, value => $self->join_value( map { $_->value } @cookies ) ); | 
|  | 100 |  |  |  |  | 649 |  | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | sub join_value { | 
| 150 | 14 |  |  | 14 | 0 | 125 | my ( $self, @values ) = @_; | 
| 151 | 14 |  |  |  |  | 875 | return [ map { unescape($_) } split('&', CORE::join("", @values)) ]; | 
|  | 4052 |  |  |  |  | 34151 |  | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | sub mangle_name_next { | 
| 155 | 100 |  |  | 100 | 1 | 613 | my ( $self, $mangled ) = @_; | 
| 156 | 100 |  |  |  |  | 253 | my ( $name, $index ) = $self->demangle_name( $mangled ); | 
| 157 | 100 | 50 |  |  |  | 448 | $self->mangle_name( $name, 1 + ((defined($index) ? $index : 0)) ); # can't trust magic incr because it might overflow and fudge 'chunk' | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | sub mangle_name { | 
| 161 | 114 |  |  | 114 | 1 | 625 | my ( $self, $name, $index ) = @_; | 
| 162 | 114 | 50 |  |  |  | 770 | return sprintf '_bigcookie_%s_chunk%d', +(defined($name) ? $name : ''), $index; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub demangle_name { | 
| 166 | 290 |  |  | 290 | 1 | 1628 | my ( $self, $mangled_name ) = @_; | 
| 167 | 290 |  |  |  |  | 1651 | my ( $name, $index ) = ( $mangled_name =~ /^_bigcookie_(.+?)_chunk(\d+)$/ ); | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 290 |  |  |  |  | 1039 | return ( $name, $index ); | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | __PACKAGE__; | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | __END__ |