Skip to content

Commit 196a2f2

Browse files
authored
Support max-age negative value (dotnet#45099)
1 parent f8e3466 commit 196a2f2

File tree

4 files changed

+63
-4
lines changed

4 files changed

+63
-4
lines changed

src/Http/Headers/src/HeaderUtilities.cs

+21
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,27 @@ public static string FormatNonNegativeInt64(long value)
517517
return ((ulong)value).ToString(NumberFormatInfo.InvariantInfo);
518518
}
519519

520+
/// <summary>
521+
/// Converts the 64-bit numeric value to its equivalent string representation.
522+
/// </summary>
523+
/// <param name="value">
524+
/// The number to convert.
525+
/// </param>
526+
/// <returns>
527+
/// The string representation of the value of this instance, consisting of a sequence of digits ranging from 0 to 9 with no leading zeroes.
528+
/// In case of negative numeric value it will have a leading minus sign.
529+
/// </returns>
530+
internal static string FormatInt64(long value)
531+
{
532+
return value switch
533+
{
534+
0 => "0",
535+
1 => "1",
536+
-1 => "-1",
537+
_ => value.ToString(NumberFormatInfo.InvariantInfo)
538+
};
539+
}
540+
520541
/// <summary>
521542
///Attempts to parse the specified <paramref name="input"/> as a <see cref="DateTimeOffset"/> value.
522543
/// </summary>

src/Http/Headers/src/SetCookieHeaderValue.cs

+17-3
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public override string ToString()
200200

201201
if (MaxAge.HasValue)
202202
{
203-
maxAge = HeaderUtilities.FormatNonNegativeInt64((long)MaxAge.GetValueOrDefault().TotalSeconds);
203+
maxAge = HeaderUtilities.FormatInt64((long)MaxAge.GetValueOrDefault().TotalSeconds);
204204
length += SeparatorToken.Length + MaxAgeToken.Length + EqualsToken.Length + maxAge.Length;
205205
}
206206

@@ -347,7 +347,7 @@ public void AppendToStringBuilder(StringBuilder builder)
347347

348348
if (MaxAge.HasValue)
349349
{
350-
AppendSegment(builder, MaxAgeToken, HeaderUtilities.FormatNonNegativeInt64((long)MaxAge.GetValueOrDefault().TotalSeconds));
350+
AppendSegment(builder, MaxAgeToken, HeaderUtilities.FormatInt64((long)MaxAge.GetValueOrDefault().TotalSeconds));
351351
}
352352

353353
if (Domain != null)
@@ -552,7 +552,7 @@ private static int GetSetCookieLength(StringSegment input, int startIndex, out S
552552
}
553553
result.Expires = expirationDate;
554554
}
555-
// max-age-av = "Max-Age=" non-zero-digit *DIGIT
555+
// max-age-av = "Max-Age=" digit *DIGIT ; valid positive and negative values following the RFC6265, Section 5.2.2
556556
else if (StringSegment.Equals(token, MaxAgeToken, StringComparison.OrdinalIgnoreCase))
557557
{
558558
// = (no spaces)
@@ -561,18 +561,32 @@ private static int GetSetCookieLength(StringSegment input, int startIndex, out S
561561
return 0;
562562
}
563563

564+
var isNegative = false;
565+
if (input[offset] == '-')
566+
{
567+
isNegative = true;
568+
offset++;
569+
}
570+
564571
itemLength = HttpRuleParser.GetNumberLength(input, offset, allowDecimal: false);
565572
if (itemLength == 0)
566573
{
567574
return 0;
568575
}
576+
569577
var numberString = input.Subsegment(offset, itemLength);
570578
long maxAge;
571579
if (!HeaderUtilities.TryParseNonNegativeInt64(numberString, out maxAge))
572580
{
573581
// Invalid expiration date, abort
574582
return 0;
575583
}
584+
585+
if (isNegative)
586+
{
587+
maxAge = -maxAge;
588+
}
589+
576590
result.MaxAge = TimeSpan.FromSeconds(maxAge);
577591
offset += itemLength;
578592
}

src/Http/Headers/test/HeaderUtilitiesTest.cs

+13
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ public void FormatNonNegativeInt64_Throws_ForNegativeValues(long value)
115115
Assert.Throws<ArgumentOutOfRangeException>(() => HeaderUtilities.FormatNonNegativeInt64(value));
116116
}
117117

118+
[Theory]
119+
[InlineData(0)]
120+
[InlineData(1)]
121+
[InlineData(-1)]
122+
[InlineData(-1234567890)]
123+
[InlineData(1234567890)]
124+
[InlineData(long.MinValue)]
125+
[InlineData(long.MaxValue)]
126+
public void FormatInt64_MatchesToString(long value)
127+
{
128+
Assert.Equal(value.ToString(CultureInfo.InvariantCulture), HeaderUtilities.FormatInt64(value));
129+
}
130+
118131
[Theory]
119132
[InlineData("h", "h", true)]
120133
[InlineData("h=", "h", true)]

src/Http/Headers/test/SetCookieHeaderValueTest.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ public static TheoryData<SetCookieHeaderValue, string> SetCookieHeaderDataSet
6262
header8.Extensions.Add("extension2=value");
6363
dataset.Add(header8, "name8=value8; extension1; extension2=value");
6464

65+
var header9 = new SetCookieHeaderValue("name9", "value9")
66+
{
67+
MaxAge = TimeSpan.FromDays(-1),
68+
};
69+
dataset.Add(header9, "name9=value9; max-age=-86400");
70+
71+
var header10 = new SetCookieHeaderValue("name10", "value10")
72+
{
73+
MaxAge = TimeSpan.FromDays(0),
74+
};
75+
dataset.Add(header10, "name10=value10; max-age=0");
76+
6577
return dataset;
6678
}
6779
}
@@ -74,7 +86,6 @@ public static TheoryData<string> InvalidSetCookieHeaderDataSet
7486
{
7587
"expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1",
7688
"name=value; expires=Sun, 06 Nov 1994 08:49:37 ZZZ; max-age=86400; domain=domain1",
77-
"name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=-86400; domain=domain1",
7889
};
7990
}
8091
}

0 commit comments

Comments
 (0)