File Coverage

blib/lib/Dancer2/Core/Cookie.pm
Criterion Covered Total %
statement 45 46 97.8
branch 18 24 75.0
condition 2 3 66.6
subroutine 11 11 100.0
pod 0 2 0.0
total 76 86 88.3


line stmt bran cond sub pod time code
1             package Dancer2::Core::Cookie;
2             # ABSTRACT: A cookie representing class
3             $Dancer2::Core::Cookie::VERSION = '1.0.0';
4 148     148   73095 use Moo;
  148         6871  
  148         2320  
5 148     148   71376 use URI::Escape;
  148         47862  
  148         9660  
6 148     148   51404 use Dancer2::Core::Types;
  148         579  
  148         1407  
7 148     148   1960667 use Dancer2::Core::Time;
  148         562  
  148         5077  
8 148     148   1215 use Carp 'croak';
  148         423  
  148         7819  
9 148     148   1604 use Ref::Util qw< is_arrayref is_hashref >;
  148         1022  
  148         9074  
10 148     148   1125 use overload '""' => \&_get_value;
  148         462  
  148         1584  
11              
12             BEGIN {
13             my $try_xs =
14             exists($ENV{PERL_HTTP_XSCOOKIES}) ? !!$ENV{PERL_HTTP_XSCOOKIES} :
15             exists($ENV{PERL_ONLY}) ? !$ENV{PERL_ONLY} :
16 148 50   148   33890 1;
    50          
17              
18 148         556 my $use_xs = 0;
19 148 50       725 $try_xs and eval {
20 148         68359 require HTTP::XSCookies;
21 148         74447 $use_xs++;
22             };
23 148 50       681 if ( $use_xs ) {
24 148         712 *to_header = \&xs_to_header;
25             }
26             else {
27 0         0 *to_header = \&pp_to_header;
28             }
29 148 50       102696 *_USE_XS = $use_xs ? sub () { !!1 } : sub () { !!0 };
30             }
31              
32             sub xs_to_header {
33 96     96 0 2692 my $self = shift;
34              
35             # HTTP::XSCookies can't handle multi-value cookies.
36 96 100       156 return $self->pp_to_header(@_) if @{[ $self->value ]} > 1;
  96         1897  
37              
38 95         1595 return HTTP::XSCookies::bake_cookie(
39             $self->name,
40             { value => $self->value,
41             path => $self->path,
42             domain => $self->domain,
43             expires => $self->expires,
44             httponly => $self->http_only,
45             secure => $self->secure,
46             samesite => $self->same_site,
47             }
48             );
49             }
50              
51             sub pp_to_header {
52 9     9 0 198 my $self = shift;
53              
54 9         167 my $value = join( '&', map uri_escape($_), $self->value );
55 9   66     296 my $no_httponly = defined( $self->http_only ) && $self->http_only == 0;
56              
57 9         347 my @headers = $self->name . '=' . $value;
58 9 50       182 push @headers, "Path=" . $self->path if $self->path;
59 9 100       337 push @headers, "Expires=" . $self->expires if $self->expires;
60 9 100       193 push @headers, "Domain=" . $self->domain if $self->domain;
61 9 100       188 push @headers, "SameSite=" . $self->same_site if $self->same_site;
62 9 100       202 push @headers, "Secure" if $self->secure;
63 9 100       62 push @headers, 'HttpOnly' unless $no_httponly;
64              
65 9         50 return join '; ', @headers;
66             }
67              
68             has value => (
69             is => 'rw',
70             isa => ArrayRef,
71             required => 0,
72             coerce => sub {
73             my $value = shift;
74             my @values =
75             is_arrayref($value) ? @$value
76             : is_hashref($value) ? %$value
77             : ($value);
78             return [@values];
79             },
80             );
81              
82             around value => sub {
83             my $orig = shift;
84             my $self = shift;
85             my $array = $orig->( $self, @_ );
86             return wantarray ? @$array : $array->[0];
87             };
88              
89             # this is only for overloading; need a real sub to refer to, as the Moose
90             # attribute accessor won't be available at that point.
91 88     88   1729 sub _get_value { shift->value }
92              
93             has name => (
94             is => 'rw',
95             isa => Str,
96             required => 1,
97             );
98              
99             has expires => (
100             is => 'rw',
101             isa => Str,
102             required => 0,
103             coerce => sub {
104             Dancer2::Core::Time->new( expression => $_[0] )->gmt_string;
105             },
106             );
107              
108             has domain => (
109             is => 'rw',
110             isa => Str,
111             required => 0,
112             );
113              
114             has path => (
115             is => 'rw',
116             isa => Str,
117             default => sub {'/'},
118             predicate => 1,
119             );
120              
121             has secure => (
122             is => 'rw',
123             isa => Bool,
124             required => 0,
125             default => sub {0},
126             );
127              
128             has http_only => (
129             is => 'rw',
130             isa => Bool,
131             required => 0,
132             default => sub {1},
133             );
134              
135             has same_site => (
136             is => 'rw',
137             isa => Enum[qw[Strict Lax None]],
138             required => 0,
139             );
140              
141             1;
142              
143             __END__
144              
145             =pod
146              
147             =encoding UTF-8
148              
149             =head1 NAME
150              
151             Dancer2::Core::Cookie - A cookie representing class
152              
153             =head1 VERSION
154              
155             version 1.0.0
156              
157             =head1 SYNOPSIS
158              
159             use Dancer2::Core::Cookie;
160              
161             my $cookie = Dancer2::Core::Cookie->new(
162             name => $cookie_name, value => $cookie_value
163             );
164              
165             my $value = $cookie->value;
166              
167             print "$cookie"; # objects stringify to their value.
168              
169             =head1 DESCRIPTION
170              
171             Dancer2::Core::Cookie provides a HTTP cookie object to work with cookies.
172              
173             =head1 ATTRIBUTES
174              
175             =head2 value
176              
177             The cookie's value.
178              
179             (Note that cookie objects use overloading to stringify to their value, so if
180             you say e.g. return "Hi, $cookie", you'll get the cookie's value there.)
181              
182             In list context, returns a list of potentially multiple values; in scalar
183             context, returns just the first value. (So, if you expect a cookie to have
184             multiple values, use list context.)
185              
186             =head2 name
187              
188             The cookie's name.
189              
190             =head2 expires
191              
192             The cookie's expiration date. There are several formats.
193              
194             Unix epoch time like 1288817656 to mean "Wed, 03-Nov-2010 20:54:16 GMT"
195              
196             It also supports a human readable offset from the current time such as "2 hours".
197             See the documentation of L<Dancer2::Core::Time> for details of all supported
198             formats.
199              
200             =head2 domain
201              
202             The cookie's domain.
203              
204             =head2 path
205              
206             The cookie's path.
207              
208             =head2 secure
209              
210             If true, it instructs the client to only serve the cookie over secure
211             connections such as https.
212              
213             =head2 http_only
214              
215             By default, cookies are created with a property, named C<HttpOnly>,
216             that can be used for security, forcing the cookie to be used only by
217             the server (via HTTP) and not by any JavaScript code.
218              
219             If your cookie is meant to be used by some JavaScript code, set this
220             attribute to 0.
221              
222             =head2 same_site
223              
224             Whether the cookie ought not to be sent along with cross-site requests.
225             Valid values are C<Strict>, C<Lax>, or C<None>. Default is unset.
226             Refer to
227             L<RFC6265bis|https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site>
228             for further details regarding same-site context.
229              
230             =head1 METHODS
231              
232             =head2 my $cookie=Dancer2::Core::Cookie->new(%opts);
233              
234             Create a new Dancer2::Core::Cookie object.
235              
236             You can set any attribute described in the I<ATTRIBUTES> section above.
237              
238             =head2 my $header=$cookie->to_header();
239              
240             Creates a proper HTTP cookie header from the content.
241              
242             =head1 AUTHOR
243              
244             Dancer Core Developers
245              
246             =head1 COPYRIGHT AND LICENSE
247              
248             This software is copyright (c) 2023 by Alexis Sukrieh.
249              
250             This is free software; you can redistribute it and/or modify it under
251             the same terms as the Perl 5 programming language system itself.
252              
253             =cut