Skip to content

Commit 8182bb1

Browse files
authored
Disable cookie name encoding/decoding. (#23579)
1 parent 36856ca commit 8182bb1

File tree

5 files changed

+62
-42
lines changed

5 files changed

+62
-42
lines changed

src/Http/Http/src/Internal/RequestCookieCollection.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public string? this[string key]
5757
}
5858

5959
public static RequestCookieCollection Parse(IList<string> values)
60+
=> ParseInternal(values, AppContext.TryGetSwitch(ResponseCookies.EnableCookieNameEncoding, out var enabled) && enabled);
61+
62+
internal static RequestCookieCollection ParseInternal(IList<string> values, bool enableCookieNameEncoding)
6063
{
6164
if (values.Count == 0)
6265
{
@@ -75,7 +78,7 @@ public static RequestCookieCollection Parse(IList<string> values)
7578
for (var i = 0; i < cookies.Count; i++)
7679
{
7780
var cookie = cookies[i];
78-
var name = Uri.UnescapeDataString(cookie.Name.Value);
81+
var name = enableCookieNameEncoding ? Uri.UnescapeDataString(cookie.Name.Value) : cookie.Name.Value;
7982
var value = Uri.UnescapeDataString(cookie.Value.Value);
8083
store[name] = value;
8184
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace Microsoft.AspNetCore.Http
1313
/// </summary>
1414
internal class ResponseCookies : IResponseCookies
1515
{
16+
internal const string EnableCookieNameEncoding = "Microsoft.AspNetCore.Http.EnableCookieNameEncoding";
17+
internal bool _enableCookieNameEncoding = AppContext.TryGetSwitch(EnableCookieNameEncoding, out var enabled) && enabled;
18+
1619
/// <summary>
1720
/// Create a new wrapper.
1821
/// </summary>
@@ -33,7 +36,7 @@ public ResponseCookies(IHeaderDictionary headers)
3336
public void Append(string key, string value)
3437
{
3538
var setCookieHeaderValue = new SetCookieHeaderValue(
36-
Uri.EscapeDataString(key),
39+
_enableCookieNameEncoding ? Uri.EscapeDataString(key) : key,
3740
Uri.EscapeDataString(value))
3841
{
3942
Path = "/"
@@ -52,7 +55,7 @@ public void Append(string key, string value, CookieOptions options)
5255
}
5356

5457
var setCookieHeaderValue = new SetCookieHeaderValue(
55-
Uri.EscapeDataString(key),
58+
_enableCookieNameEncoding ? Uri.EscapeDataString(key) : key,
5659
Uri.EscapeDataString(value))
5760
{
5861
Domain = options.Domain,
@@ -83,7 +86,7 @@ public void Delete(string key, CookieOptions options)
8386
throw new ArgumentNullException(nameof(options));
8487
}
8588

86-
var encodedKeyPlusEquals = Uri.EscapeDataString(key) + "=";
89+
var encodedKeyPlusEquals = (_enableCookieNameEncoding ? Uri.EscapeDataString(key) : key) + "=";
8790
bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
8891
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
8992

src/Http/Http/test/Internal/DefaultHttpRequestTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,15 @@ public void Cookies_GetAndSet()
175175
Assert.Null(cookies0["key0"]);
176176
Assert.False(cookies0.ContainsKey("key0"));
177177

178-
var newCookies = new[] { "name0=value0%2C", "%5Ename1=value1" };
178+
var newCookies = new[] { "name0=value0%2C", "name1=value1" };
179179
request.Headers["Cookie"] = newCookies;
180180

181181
cookies0 = RequestCookieCollection.Parse(newCookies);
182182
var cookies1 = request.Cookies;
183183
Assert.Equal(cookies0, cookies1);
184184
Assert.Equal(2, cookies1.Count);
185185
Assert.Equal("value0,", cookies1["name0"]);
186-
Assert.Equal("value1", cookies1["^name1"]);
186+
Assert.Equal("value1", cookies1["name1"]);
187187
Assert.Equal(newCookies, request.Headers["Cookie"]);
188188

189189
var cookies2 = new RequestCookieCollection(new Dictionary<string,string>()

src/Http/Http/test/RequestCookiesCollectionTests.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,30 @@ namespace Microsoft.AspNetCore.Http.Tests
99
{
1010
public class RequestCookiesCollectionTests
1111
{
12-
public static TheoryData UnEscapesKeyValues_Data
12+
[Theory]
13+
[InlineData("key=value", "key", "value")]
14+
[InlineData("key%2C=%21value", "key%2C", "!value")]
15+
[InlineData("ke%23y%2C=val%5Eue", "ke%23y%2C", "val^ue")]
16+
[InlineData("base64=QUI%2BREU%2FRw%3D%3D", "base64", "QUI+REU/Rw==")]
17+
[InlineData("base64=QUI+REU/Rw==", "base64", "QUI+REU/Rw==")]
18+
public void UnEscapesValues(string input, string expectedKey, string expectedValue)
1319
{
14-
get
15-
{
16-
// key, value, expected
17-
return new TheoryData<string, string, string>
18-
{
19-
{ "key=value", "key", "value" },
20-
{ "key%2C=%21value", "key,", "!value" },
21-
{ "ke%23y%2C=val%5Eue", "ke#y,", "val^ue" },
22-
{ "base64=QUI%2BREU%2FRw%3D%3D", "base64", "QUI+REU/Rw==" },
23-
{ "base64=QUI+REU/Rw==", "base64", "QUI+REU/Rw==" },
24-
};
25-
}
20+
var cookies = RequestCookieCollection.Parse(new StringValues(input));
21+
22+
Assert.Equal(1, cookies.Count);
23+
Assert.Equal(expectedKey, cookies.Keys.Single());
24+
Assert.Equal(expectedValue, cookies[expectedKey]);
2625
}
2726

2827
[Theory]
29-
[MemberData(nameof(UnEscapesKeyValues_Data))]
30-
public void UnEscapesKeyValues(
31-
string input,
32-
string expectedKey,
33-
string expectedValue)
28+
[InlineData("key=value", "key", "value")]
29+
[InlineData("key%2C=%21value", "key,", "!value")]
30+
[InlineData("ke%23y%2C=val%5Eue", "ke#y,", "val^ue")]
31+
[InlineData("base64=QUI%2BREU%2FRw%3D%3D", "base64", "QUI+REU/Rw==")]
32+
[InlineData("base64=QUI+REU/Rw==", "base64", "QUI+REU/Rw==")]
33+
public void AppContextSwitchUnEscapesKeysAndValues(string input, string expectedKey, string expectedValue)
3434
{
35-
var cookies = RequestCookieCollection.Parse(new StringValues(input));
35+
var cookies = RequestCookieCollection.ParseInternal(new StringValues(input), enableCookieNameEncoding: true);
3636

3737
Assert.Equal(1, cookies.Count);
3838
Assert.Equal(expectedKey, cookies.Keys.Single());

src/Http/Http/test/ResponseCookiesTest.cs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -88,30 +88,44 @@ public void ProvidesMaxAgeWithCookieOptionsArgumentExpectMaxAgeToBeSet()
8888
Assert.Contains($"max-age={maxAgeTime.TotalSeconds.ToString()}", cookieHeaderValues[0]);
8989
}
9090

91-
public static TheoryData EscapesKeyValuesBeforeSettingCookieData
91+
[Theory]
92+
[InlineData("value", "key=value")]
93+
[InlineData("!value", "key=%21value")]
94+
[InlineData("val^ue", "key=val%5Eue")]
95+
[InlineData("QUI+REU/Rw==", "key=QUI%2BREU%2FRw%3D%3D")]
96+
public void EscapesValuesBeforeSettingCookie(string value, string expected)
9297
{
93-
get
94-
{
95-
// key, value, object pool, expected
96-
return new TheoryData<string, string, string>
97-
{
98-
{ "key", "value", "key=value" },
99-
{ "key,", "!value", "key%2C=%21value" },
100-
{ "ke#y,", "val^ue", "ke%23y%2C=val%5Eue" },
101-
{ "base64", "QUI+REU/Rw==", "base64=QUI%2BREU%2FRw%3D%3D" },
102-
};
103-
}
98+
var headers = new HeaderDictionary();
99+
var cookies = new ResponseCookies(headers);
100+
101+
cookies.Append("key", value);
102+
103+
var cookieHeaderValues = headers[HeaderNames.SetCookie];
104+
Assert.Single(cookieHeaderValues);
105+
Assert.StartsWith(expected, cookieHeaderValues[0]);
106+
}
107+
108+
[Theory]
109+
[InlineData("key,")]
110+
[InlineData("ke@y")]
111+
public void InvalidKeysThrow(string key)
112+
{
113+
var headers = new HeaderDictionary();
114+
var cookies = new ResponseCookies(headers);
115+
116+
Assert.Throws<ArgumentException>(() => cookies.Append(key, "1"));
104117
}
105118

106119
[Theory]
107-
[MemberData(nameof(EscapesKeyValuesBeforeSettingCookieData))]
108-
public void EscapesKeyValuesBeforeSettingCookie(
109-
string key,
110-
string value,
111-
string expected)
120+
[InlineData("key", "value", "key=value")]
121+
[InlineData("key,", "!value", "key%2C=%21value")]
122+
[InlineData("ke#y,", "val^ue", "ke%23y%2C=val%5Eue")]
123+
[InlineData("base64", "QUI+REU/Rw==", "base64=QUI%2BREU%2FRw%3D%3D")]
124+
public void AppContextSwitchEscapesKeysAndValuesBeforeSettingCookie(string key, string value, string expected)
112125
{
113126
var headers = new HeaderDictionary();
114127
var cookies = new ResponseCookies(headers);
128+
cookies._enableCookieNameEncoding = true;
115129

116130
cookies.Append(key, value);
117131

0 commit comments

Comments
 (0)