Skip to content

Commit 15fedb4

Browse files
authored
Delete multiple cookie with same-key when Path is different but Domain is specified (dotnet#32897)
1 parent cdd7291 commit 15fedb4

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

src/Http/Http/src/Internal/ResponseCookies.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,14 @@ public void Delete(string key, CookieOptions options)
158158
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
159159

160160
Func<string, string, CookieOptions, bool> rejectPredicate;
161-
if (domainHasValue)
161+
if (domainHasValue && pathHasValue)
162+
{
163+
rejectPredicate = (value, encKeyPlusEquals, opts) =>
164+
value.StartsWith(encKeyPlusEquals, StringComparison.OrdinalIgnoreCase) &&
165+
value.IndexOf($"domain={opts.Domain}", StringComparison.OrdinalIgnoreCase) != -1 &&
166+
value.IndexOf($"path={opts.Path}", StringComparison.OrdinalIgnoreCase) != -1;
167+
}
168+
else if (domainHasValue)
162169
{
163170
rejectPredicate = (value, encKeyPlusEquals, opts) =>
164171
value.StartsWith(encKeyPlusEquals, StringComparison.OrdinalIgnoreCase) &&

src/Http/Http/test/ResponseCookiesTest.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Linq;
56
using Microsoft.AspNetCore.Http.Features;
67
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.Logging;
@@ -74,6 +75,64 @@ public void DeleteCookieShouldSetDefaultPath()
7475
Assert.Contains("expires=Thu, 01 Jan 1970 00:00:00 GMT", cookieHeaderValues[0]);
7576
}
7677

78+
[Fact]
79+
public void DeleteCookieWithDomainAndPathDeletesPriorMatchingCookies()
80+
{
81+
var headers = (IHeaderDictionary)new HeaderDictionary();
82+
var features = MakeFeatures(headers);
83+
var responseCookies = new ResponseCookies(features);
84+
85+
var testCookies = new (string Key, string Path, string Domain)[]
86+
{
87+
new ("key1", "/path1/", null),
88+
new ("key1", "/path2/", null),
89+
new ("key2", "/path1/", "localhost"),
90+
new ("key2", "/path2/", "localhost"),
91+
};
92+
93+
foreach (var cookie in testCookies)
94+
{
95+
responseCookies.Delete(cookie.Key, new CookieOptions() { Domain = cookie.Domain, Path = cookie.Path });
96+
}
97+
98+
var deletedCookies = headers.SetCookie.ToArray();
99+
Assert.Equal(testCookies.Length, deletedCookies.Length);
100+
101+
Assert.Single(deletedCookies, cookie => cookie.StartsWith("key1", StringComparison.InvariantCulture) && cookie.Contains("path=/path1/"));
102+
Assert.Single(deletedCookies, cookie => cookie.StartsWith("key1", StringComparison.InvariantCulture) && cookie.Contains("path=/path2/"));
103+
Assert.Single(deletedCookies, cookie => cookie.StartsWith("key2", StringComparison.InvariantCulture) && cookie.Contains("path=/path1/") && cookie.Contains("domain=localhost"));
104+
Assert.Single(deletedCookies, cookie => cookie.StartsWith("key2", StringComparison.InvariantCulture) && cookie.Contains("path=/path2/") && cookie.Contains("domain=localhost"));
105+
Assert.All(deletedCookies, cookie => Assert.Contains("expires=Thu, 01 Jan 1970 00:00:00 GMT", cookie));
106+
}
107+
108+
[Fact]
109+
public void DeleteRemovesCookieWithDomainAndPathCreatedByAdd()
110+
{
111+
var headers = (IHeaderDictionary)new HeaderDictionary();
112+
var features = MakeFeatures(headers);
113+
var responseCookies = new ResponseCookies(features);
114+
115+
var testCookies = new (string Key, string Path, string Domain)[]
116+
{
117+
new ("key1", "/path1/", null),
118+
new ("key1", "/path1/", null),
119+
new ("key2", "/path1/", "localhost"),
120+
new ("key2", "/path1/", "localhost"),
121+
};
122+
123+
foreach (var cookie in testCookies)
124+
{
125+
responseCookies.Append(cookie.Key, cookie.Key, new CookieOptions() { Domain = cookie.Domain, Path = cookie.Path });
126+
responseCookies.Delete(cookie.Key, new CookieOptions() { Domain = cookie.Domain, Path = cookie.Path });
127+
}
128+
129+
var deletedCookies = headers.SetCookie.ToArray();
130+
Assert.Equal(2, deletedCookies.Length);
131+
Assert.Single(deletedCookies, cookie => cookie.StartsWith("key1", StringComparison.InvariantCulture) && cookie.Contains("path=/path1/"));
132+
Assert.Single(deletedCookies, cookie => cookie.StartsWith("key2", StringComparison.InvariantCulture) && cookie.Contains("path=/path1/") && cookie.Contains("domain=localhost"));
133+
Assert.All(deletedCookies, cookie => Assert.Contains("expires=Thu, 01 Jan 1970 00:00:00 GMT", cookie));
134+
}
135+
77136
[Fact]
78137
public void DeleteCookieWithCookieOptionsShouldKeepPropertiesOfCookieOptions()
79138
{

0 commit comments

Comments
 (0)