Skip to content

Commit 535ab4c

Browse files
authored
Remove cookie name decoding (#368)
1 parent 635c92f commit 535ab4c

File tree

5 files changed

+57
-15
lines changed

5 files changed

+57
-15
lines changed

src/Microsoft.Owin/Infrastructure/OwinHelpers.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -530,13 +530,13 @@ internal static IDictionary<string, string> GetCookies(IOwinRequest request)
530530
if (request.Get<string>("Microsoft.Owin.Cookies#text") != text)
531531
{
532532
cookies.Clear();
533-
ParseDelimited(text, SemicolonAndComma, AddCookieCallback, cookies);
533+
ParseDelimited(text, SemicolonAndComma, AddCookieCallback, decodePlus: false, decodeKey: false, state: cookies);
534534
request.Set("Microsoft.Owin.Cookies#text", text);
535535
}
536536
return cookies;
537537
}
538538

539-
internal static void ParseDelimited(string text, char[] delimiters, Action<string, string, object> callback, object state)
539+
internal static void ParseDelimited(string text, char[] delimiters, Action<string, string, object> callback, bool decodePlus, bool decodeKey, object state)
540540
{
541541
int textLength = text.Length;
542542
int equalIndex = text.IndexOf('=');
@@ -560,10 +560,17 @@ internal static void ParseDelimited(string text, char[] delimiters, Action<strin
560560
}
561561
string name = text.Substring(scanIndex, equalIndex - scanIndex);
562562
string value = text.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1);
563-
callback(
564-
Uri.UnescapeDataString(name.Replace('+', ' ')),
565-
Uri.UnescapeDataString(value.Replace('+', ' ')),
566-
state);
563+
if (decodePlus)
564+
{
565+
name.Replace('+', ' ');
566+
value.Replace('+', ' ');
567+
}
568+
if (decodeKey)
569+
{
570+
name = Uri.UnescapeDataString(name);
571+
}
572+
value = Uri.UnescapeDataString(value);
573+
callback(name, value, state);
567574
equalIndex = text.IndexOf('=', delimiterIndex);
568575
if (equalIndex == -1)
569576
{
@@ -799,7 +806,7 @@ internal static IDictionary<string, string[]> GetQuery(IOwinRequest request)
799806
{
800807
query.Clear();
801808
var accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
802-
ParseDelimited(text, AmpersandAndSemicolon, AppendItemCallback, accumulator);
809+
ParseDelimited(text, AmpersandAndSemicolon, AppendItemCallback, decodePlus: true, decodeKey: true, state: accumulator);
803810
foreach (var kv in accumulator)
804811
{
805812
query.Add(kv.Key, kv.Value.ToArray());
@@ -813,7 +820,7 @@ internal static IFormCollection GetForm(string text)
813820
{
814821
IDictionary<string, string[]> form = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
815822
var accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
816-
ParseDelimited(text, new[] { '&' }, AppendItemCallback, accumulator);
823+
ParseDelimited(text, new[] { '&' }, AppendItemCallback, decodePlus: false, decodeKey: true, state: accumulator);
817824
foreach (var kv in accumulator)
818825
{
819826
form.Add(kv.Key, kv.Value.ToArray());

src/Microsoft.Owin/RequestCookieCollection.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ public string this[string key]
3737
get
3838
{
3939
string value;
40-
Store.TryGetValue(key, out value);
41-
return value;
40+
if (Store.TryGetValue(key, out value) || Store.TryGetValue(Uri.EscapeDataString(key), out value))
41+
{
42+
return value;
43+
}
44+
return null;
4245
}
4346
}
4447

tests/Microsoft.Owin.Tests/FormsTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class FormsTests
1414
private static readonly string[] RawValues = new[] { "v1", "v2, v3", "\"v4, b\"", "v5, v6", "v7", };
1515
private const string JoinedValues = "v1,v2, v3,\"v4, b\",v5, v6,v7";
1616

17-
private const string OriginalFormsString = "q1=v1&q2=v2,b&q3=v3&q3=v4&q4&q5=v5&q5=v5";
17+
private const string OriginalFormsString = "q1=v1&q2=v2,b&q3=v3&q3=v4&q4&q5=v5&q5=v+5";
1818

1919
[Fact]
2020
public void ParseForm()
@@ -30,7 +30,7 @@ public void ParseForm()
3030
Assert.Equal("v2,b", form.Get("Q2"));
3131
Assert.Equal("v3,v4", form.Get("q3"));
3232
Assert.Null(form.Get("q4"));
33-
Assert.Equal("v5,v5", form.Get("Q5"));
33+
Assert.Equal("v5,v+5", form.Get("Q5"));
3434
Assert.True(stream.CanRead);
3535
}
3636

@@ -89,7 +89,7 @@ public void ReadFromStream()
8989
Assert.Equal("v2,b", form.Get("Q2"));
9090
Assert.Equal("v3,v4", form.Get("q3"));
9191
Assert.Null(form.Get("q4"));
92-
Assert.Equal("v5,v5", form.Get("Q5"));
92+
Assert.Equal("v5,v+5", form.Get("Q5"));
9393
}
9494

9595
[Fact]
@@ -107,14 +107,14 @@ public void ReadFromStreamTwice()
107107
Assert.Equal("v2,b", form.Get("Q2"));
108108
Assert.Equal("v3,v4", form.Get("q3"));
109109
Assert.Null(form.Get("q4"));
110-
Assert.Equal("v5,v5", form.Get("Q5"));
110+
Assert.Equal("v5,v+5", form.Get("Q5"));
111111

112112
form = request.ReadFormAsync().Result;
113113
Assert.Equal("v1", form.Get("q1"));
114114
Assert.Equal("v2,b", form.Get("Q2"));
115115
Assert.Equal("v3,v4", form.Get("q3"));
116116
Assert.Null(form.Get("q4"));
117-
Assert.Equal("v5,v5", form.Get("Q5"));
117+
Assert.Equal("v5,v+5", form.Get("Q5"));
118118
}
119119
}
120120
}

tests/Microsoft.Owin.Tests/Microsoft.Owin.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<Compile Include="Builder\AppBuilderTestExtensions.cs" />
6464
<Compile Include="Builder\AppBuilderTests.cs" />
6565
<Compile Include="CookieChunkingTests.cs" />
66+
<Compile Include="RequestCookieTests.cs" />
6667
<Compile Include="FormsTests.cs" />
6768
<Compile Include="HeaderTests.cs" />
6869
<Compile Include="HostStringTests.cs" />
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
using Xunit;
7+
using Xunit.Extensions;
8+
9+
namespace Microsoft.Owin.Tests
10+
{
11+
public class RequestCookieTests
12+
{
13+
[Theory]
14+
[InlineData("key=value", "key", "value")]
15+
[InlineData("__secure-key=value", "__secure-key", "value")]
16+
[InlineData("key%2C=%21value", "key,", "!value")]
17+
[InlineData("ke%23y%2C=val%5Eue", "ke#y,", "val^ue")]
18+
[InlineData("base64=QUI%2BREU%2FRw%3D%3D", "base64", "QUI+REU/Rw==")]
19+
[InlineData("base64=QUI+REU/Rw==", "base64", "QUI+REU/Rw==")]
20+
public void UnEscapesValues(string input, string expectedKey, string expectedValue)
21+
{
22+
var context = new OwinRequest();
23+
context.Headers["Cookie"] = input;
24+
var cookies = context.Cookies;
25+
26+
Assert.Equal(1, cookies.Count());
27+
Assert.Equal(Uri.EscapeDataString(expectedKey), cookies.Single().Key);
28+
Assert.Equal(expectedValue, cookies[expectedKey]);
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)