Skip to content

Commit 6f5167c

Browse files
committed
Merge branch 'cookie-semicolon'
2 parents f831493 + 646adc9 commit 6f5167c

File tree

3 files changed

+151
-152
lines changed

3 files changed

+151
-152
lines changed

Changes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{{$NEXT}}
22

33
[ BUG FIXES ]
4-
* None
4+
* GH #1701: Split cookie values on & only (Yanick Champoux)
55

66
[ ENHANCEMENTS ]
77
* GH #1723: Enable use of a different Template Toolkit base class

lib/Dancer2/Core/Request.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ sub _build_cookies {
579579
$cookies->{$name} = Dancer2::Core::Cookie->new(
580580
name => $name,
581581
# HTTP::XSCookies v0.17+ will do the split and return an arrayref
582-
value => (is_arrayref($value) ? $value : [split(/[&;]/, $value)])
582+
value => is_arrayref($value) ? $value : [split '&', $value ]
583583
);
584584
}
585585
return $cookies;

t/cookie.t

Lines changed: 149 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -4,89 +4,86 @@ use Test::Fatal;
44
use Test::More;
55

66
BEGIN {
7-
87
# Freeze time at Tue, 15-Jun-2010 00:00:00 GMT
98
*CORE::GLOBAL::time = sub { return 1276560000 }
109
}
1110

1211
use Dancer2::Core::Cookie;
1312
use Dancer2::Core::Request;
1413

15-
diag "If you want extra speed, install HTTP::XSCookies"
16-
if !Dancer2::Core::Cookie::_USE_XS;
17-
18-
sub run_test {
19-
20-
note "Constructor";
21-
22-
my $cookie = Dancer2::Core::Cookie->new( name => "foo" );
23-
24-
isa_ok $cookie => 'Dancer2::Core::Cookie';
25-
can_ok $cookie => 'to_header';
26-
27-
28-
note "Setting values";
29-
30-
is $cookie->value("foo") => "foo", "Can set value";
31-
is $cookie->value => "foo", "Set value stuck";
32-
33-
is $cookie . "bar", "foobar", "Stringifies to desired value";
34-
35-
ok $cookie->value( [qw(a b c)] ), "can set multiple values";
36-
is $cookie->value => 'a', "get first value in scalar context";
37-
is_deeply [ $cookie->value ] => [qw(a b c)],
38-
"get all values in list context";;
39-
40-
ok $cookie->value( { x => 1, y => 2 } ), "can set values with a hashref";
41-
like $cookie->value => qr/^[xy]$/; # hashes doesn't store order...
42-
is_deeply [ sort $cookie->value ] => [ sort ( 1, 2, 'x', 'y' ) ];
43-
44-
45-
note "accessors and defaults";
46-
47-
is $cookie->name => 'foo', "name is as expected";
48-
is $cookie->name("bar") => "bar", "can change name";
49-
is $cookie->name => 'bar', "name change stuck";
50-
51-
ok !$cookie->domain, "no domain set by default";
52-
is $cookie->domain("dancer.org") => "dancer.org",
53-
"setting domain returns new value";
54-
is $cookie->domain => "dancer.org",
55-
"new domain valjue stuck";
56-
is $cookie->domain("") => "", "can clear domain";
57-
ok !$cookie->domain, "no domain set now";
58-
59-
is $cookie->path => '/', "by default, path is /";
60-
ok $cookie->has_path, "has_path";
61-
is $cookie->path("/foo") => "/foo", "setting path returns new value";
62-
ok $cookie->has_path, "has_path";
63-
is $cookie->path => "/foo", "new path stuck";
14+
if ( Dancer2::Core::Cookie::_USE_XS ) {
6415

65-
ok !$cookie->secure, "no cookie secure flag by default";
66-
is $cookie->secure(1) => 1, "enabling \$cookie->secure returns new value";
67-
is $cookie->secure => 1, "\$cookie->secure flag is enabled";
68-
is $cookie->secure(0) => 0, "disabling \$cookie->secure returns new value";
69-
ok !$cookie->secure, "\$cookie->secure flag is disabled";
16+
subtest 'with HTTP::XSCookies' => \&all_tests;
7017

71-
ok $cookie->http_only, "http_only by default";
72-
is $cookie->http_only(0) => 0,
73-
"disabling \$cookie->http_only returns new value";
74-
ok !$cookie->http_only,
75-
"\$cookie->http_only is now disabled";
18+
no warnings 'redefine';
19+
*Dancer2::Core::Cookie::to_header = \&Dancer2::Core::Cookie::pp_to_header;
20+
}
21+
else {
22+
diag "If you want extra speed, install HTTP::XSCookies";
23+
}
7624

77-
like exception { $cookie->same_site('foo') },
78-
qr/Value "foo" did not pass type constraint "Enum\["Strict","Lax","None"\]/;
25+
subtest 'w/o HTTP::XSCookies' => \&all_tests;
7926

80-
note "expiration strings";
27+
sub all_tests {
28+
my $cookie = Dancer2::Core::Cookie->new( name => "foo" );
8129

82-
my $min = 60;
83-
my $hour = 60 * $min;
84-
my $day = 24 * $hour;
85-
my $week = 7 * $day;
86-
my $mon = 30 * $day;
87-
my $year = 365 * $day;
30+
subtest "Constructor" => sub {
31+
isa_ok $cookie => 'Dancer2::Core::Cookie';
32+
can_ok $cookie => 'to_header';
33+
};
34+
35+
subtest "Setting values" => sub {
36+
is $cookie->value("foo") => "foo", "Can set value";
37+
is $cookie->value => "foo", "Set value stuck";
38+
39+
is $cookie . "bar", "foobar", "Stringifies to desired value";
40+
41+
ok $cookie->value( [qw(a b c)] ), "can set multiple values";
42+
is $cookie->value => 'a', "get first value in scalar context";
43+
is_deeply [ $cookie->value ] => [qw(a b c)],
44+
"get all values in list context";;
45+
46+
ok $cookie->value( { x => 1, y => 2 } ), "can set values with a hashref";
47+
like $cookie->value => qr/^[xy]$/; # hashes doesn't store order...
48+
is_deeply [ sort $cookie->value ] => [ sort ( 1, 2, 'x', 'y' ) ];
49+
};
50+
51+
52+
subtest "accessors and defaults" => sub {
53+
is $cookie->name => 'foo', "name is as expected";
54+
is $cookie->name("bar") => "bar", "can change name";
55+
is $cookie->name => 'bar', "name change stuck";
56+
57+
ok !$cookie->domain, "no domain set by default";
58+
is $cookie->domain("dancer.org") => "dancer.org",
59+
"setting domain returns new value";
60+
is $cookie->domain => "dancer.org",
61+
"new domain valjue stuck";
62+
is $cookie->domain("") => "", "can clear domain";
63+
ok !$cookie->domain, "no domain set now";
64+
65+
is $cookie->path => '/', "by default, path is /";
66+
ok $cookie->has_path, "has_path";
67+
is $cookie->path("/foo") => "/foo", "setting path returns new value";
68+
ok $cookie->has_path, "has_path";
69+
is $cookie->path => "/foo", "new path stuck";
70+
71+
ok !$cookie->secure, "no cookie secure flag by default";
72+
is $cookie->secure(1) => 1, "enabling \$cookie->secure returns new value";
73+
is $cookie->secure => 1, "\$cookie->secure flag is enabled";
74+
is $cookie->secure(0) => 0, "disabling \$cookie->secure returns new value";
75+
ok !$cookie->secure, "\$cookie->secure flag is disabled";
76+
77+
ok $cookie->http_only, "http_only by default";
78+
is $cookie->http_only(0) => 0,
79+
"disabling \$cookie->http_only returns new value";
80+
ok !$cookie->http_only,
81+
"\$cookie->http_only is now disabled";
82+
83+
like exception { $cookie->same_site('foo') },
84+
qr/Value "foo" did not pass type constraint "Enum\["Strict","Lax","None"\]/;
85+
};
8886

89-
ok !$cookie->expires;
9087
my %times = (
9188
"+2" => "Tue, 15-Jun-2010 00:00:02 GMT",
9289
"+2h" => "Tue, 15-Jun-2010 02:00:00 GMT",
@@ -110,98 +107,100 @@ sub run_test {
110107
"+2 something" => "+2 something",
111108
);
112109

113-
for my $exp ( keys %times ) {
114-
my $want = $times{$exp};
115-
116-
$cookie->expires($exp);
117-
is $cookie->expires => $want, "expiry $exp => $want";;
118-
}
119-
120-
121-
note "to header";
122-
123-
my @cake = (
124-
{ cookie => {
125-
name => 'bar',
126-
value => 'foo',
127-
expires => '+2h',
128-
secure => 1
110+
subtest "expiration strings" => sub {
111+
my $min = 60;
112+
my $hour = 60 * $min;
113+
my $day = 24 * $hour;
114+
my $week = 7 * $day;
115+
my $mon = 30 * $day;
116+
my $year = 365 * $day;
117+
118+
ok !$cookie->expires;
119+
120+
for my $exp ( keys %times ) {
121+
my $want = $times{$exp};
122+
123+
$cookie->expires($exp);
124+
is $cookie->expires => $want, "expiry $exp => $want";;
125+
}
126+
};
127+
128+
subtest "to header" => sub {
129+
130+
my @cake = (
131+
{ cookie => {
132+
name => 'bar',
133+
value => 'foo',
134+
expires => '+2h',
135+
secure => 1
136+
},
137+
expected => sprintf(
138+
"bar=foo; Expires=%s; HttpOnly; Path=/; Secure",
139+
$times{'+2h'},
140+
),
129141
},
130-
expected => sprintf(
131-
"bar=foo; Expires=%s; HttpOnly; Path=/; Secure",
132-
$times{'+2h'},
133-
),
134-
},
135-
{ cookie => {
136-
name => 'bar',
137-
value => 'foo',
138-
domain => 'dancer.org',
139-
path => '/dance',
140-
http_only => 1
142+
{ cookie => {
143+
name => 'bar',
144+
value => 'foo',
145+
domain => 'dancer.org',
146+
path => '/dance',
147+
http_only => 1
148+
},
149+
expected => "bar=foo; Domain=dancer.org; HttpOnly; Path=/dance",
141150
},
142-
expected => "bar=foo; Domain=dancer.org; HttpOnly; Path=/dance",
143-
},
144-
{ cookie => {
145-
name => 'bar',
146-
value => 'foo',
151+
{ cookie => {
152+
name => 'bar',
153+
value => 'foo',
154+
},
155+
expected => "bar=foo; HttpOnly; Path=/",
147156
},
148-
expected => "bar=foo; HttpOnly; Path=/",
149-
},
150-
{ cookie => {
151-
name => 'bar',
152-
value => 'foo',
153-
http_only => 0,
157+
{ cookie => {
158+
name => 'bar',
159+
value => 'foo',
160+
http_only => 0,
161+
},
162+
expected => "bar=foo; Path=/",
154163
},
155-
expected => "bar=foo; Path=/",
156-
},
157-
{ cookie => {
158-
name => 'bar',
159-
value => 'foo',
160-
http_only => '0',
164+
{ cookie => {
165+
name => 'bar',
166+
value => 'foo',
167+
http_only => '0',
168+
},
169+
expected => "bar=foo; Path=/",
161170
},
162-
expected => "bar=foo; Path=/",
163-
},
164-
{ cookie => {
165-
name => 'same-site',
166-
value => 'strict',
167-
same_site => 'Strict',
171+
{ cookie => {
172+
name => 'same-site',
173+
value => 'strict',
174+
same_site => 'Strict',
175+
},
176+
expected => 'same-site=strict; HttpOnly; Path=/; SameSite=Strict',
168177
},
169-
expected => 'same-site=strict; HttpOnly; Path=/; SameSite=Strict',
170-
},
171-
{ cookie => {
172-
name => 'same-site',
173-
value => 'lax',
174-
same_site => 'Lax',
178+
{ cookie => {
179+
name => 'same-site',
180+
value => 'lax',
181+
same_site => 'Lax',
182+
},
183+
expected => 'same-site=lax; HttpOnly; Path=/; SameSite=Lax',
175184
},
176-
expected => 'same-site=lax; HttpOnly; Path=/; SameSite=Lax',
177-
},
178-
);
179-
180-
for my $cook (@cake) {
181-
my $c = Dancer2::Core::Cookie->new(%{$cook->{cookie}});
182-
# name=value; sorted fields
183-
my @a = split /; /, $c->to_header;
184-
is join("; ", shift @a, sort @a), $cook->{expected};
185-
}
186-
187-
note 'multi-value';
185+
);
188186

189-
my $c = Dancer2::Core::Cookie->new( name => 'foo', value => [qw/bar baz/] );
187+
for my $cook (@cake) {
188+
my $c = Dancer2::Core::Cookie->new(%{$cook->{cookie}});
189+
# name=value; sorted fields
190+
my @a = split /; /, $c->to_header;
191+
is join("; ", shift @a, sort @a), $cook->{expected};
192+
}
193+
};
190194

191-
is $c->to_header, 'foo=bar&baz; Path=/; HttpOnly';
195+
subtest 'multi-value' => sub {
196+
my $c = Dancer2::Core::Cookie->new( name => 'foo', value => [qw/bar baz/] );
192197

193-
my $r = Dancer2::Core::Request->new( env => { HTTP_COOKIE => 'foo=bar&baz' } );
198+
is $c->to_header, 'foo=bar&baz; Path=/; HttpOnly';
194199

195-
is_deeply [ $r->cookies->{foo}->value ], [qw/bar baz/];
196-
}
200+
my $r = Dancer2::Core::Request->new( env => { HTTP_COOKIE => 'foo=bar&baz' } );
197201

198-
note "Run test with XS_HTTP_COOKIES" if Dancer2::Core::Cookie::_USE_XS;
199-
run_test();
200-
if ( Dancer2::Core::Cookie::_USE_XS ) {
201-
note "Run test without XS_HTTP_COOKIES";
202-
no warnings 'redefine';
203-
*Dancer2::Core::Cookie::to_header = \&Dancer2::Core::Cookie::pp_to_header;
204-
run_test();
202+
is_deeply [ $r->cookies->{foo}->value ], [qw/bar baz/];
203+
};
205204
}
206205

207206
done_testing;

0 commit comments

Comments
 (0)