Only format non-negative int64 #760

This commit is contained in:
John Luo 2017-02-24 14:17:12 -08:00
parent b9bba9cbb1
commit 15adff9433
12 changed files with 53 additions and 62 deletions

View File

@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Http
{ typeof(RangeHeaderValue), new Func<string, RangeHeaderValue>(value => { RangeHeaderValue result; return RangeHeaderValue.TryParse(value, out result) ? result : null; }) },
{ typeof(EntityTagHeaderValue), new Func<string, EntityTagHeaderValue>(value => { EntityTagHeaderValue result; return EntityTagHeaderValue.TryParse(value, out result) ? result : null; }) },
{ typeof(DateTimeOffset?), new Func<string, DateTimeOffset?>(value => { DateTimeOffset result; return HeaderUtilities.TryParseDate(value, out result) ? result : (DateTimeOffset?)null; }) },
{ typeof(long?), new Func<string, long?>(value => { long result; return HeaderUtilities.TryParseInt64(value, out result) ? result : (long?)null; }) },
{ typeof(long?), new Func<string, long?>(value => { long result; return HeaderUtilities.TryParseNonNegativeInt64(value, out result) ? result : (long?)null; }) },
};
private static IDictionary<Type, object> KnownListParsers = new Dictionary<Type, object>()

View File

@ -97,11 +97,11 @@ namespace Microsoft.AspNetCore.Http.Headers
{
get
{
return Headers.Get<long?>(HeaderNames.ContentLength);
return Headers.ContentLength;
}
set
{
Headers.Set(HeaderNames.ContentLength, value.HasValue ? HeaderUtilities.FormatInt64(value.Value) : null);
Headers.ContentLength = value;
}
}

View File

@ -50,11 +50,11 @@ namespace Microsoft.AspNetCore.Http.Headers
{
get
{
return Headers.Get<long?>(HeaderNames.ContentLength);
return Headers.ContentLength;
}
set
{
Headers.Set(HeaderNames.ContentLength, value.HasValue ? HeaderUtilities.FormatInt64(value.Value) : null);
Headers.ContentLength = value;
}
}

View File

@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Http
var rawValue = this[HeaderNames.ContentLength];
if (rawValue.Count == 1 &&
!string.IsNullOrWhiteSpace(rawValue[0]) &&
HeaderUtilities.TryParseInt64(new StringSegment(rawValue[0]).Trim(), out value))
HeaderUtilities.TryParseNonNegativeInt64(new StringSegment(rawValue[0]).Trim(), out value))
{
return value;
}
@ -117,7 +117,7 @@ namespace Microsoft.AspNetCore.Http
{
if (value.HasValue)
{
this[HeaderNames.ContentLength] = HeaderUtilities.FormatInt64(value.Value);
this[HeaderNames.ContentLength] = HeaderUtilities.FormatNonNegativeInt64(value.Value);
}
else
{

View File

@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Owin
if (rawValue.Length == 1 &&
!string.IsNullOrWhiteSpace(rawValue[0]) &&
HeaderUtilities.TryParseInt64(new StringSegment(rawValue[0]).Trim(), out value))
HeaderUtilities.TryParseNonNegativeInt64(new StringSegment(rawValue[0]).Trim(), out value))
{
return value;
}
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Owin
{
if (value.HasValue)
{
Inner[HeaderNames.ContentLength] = (StringValues)HeaderUtilities.FormatInt64(value.Value);
Inner[HeaderNames.ContentLength] = (StringValues)HeaderUtilities.FormatNonNegativeInt64(value.Value);
}
else
{

View File

@ -610,7 +610,7 @@ namespace Microsoft.Net.Http.Headers
}
int seconds;
if (!HeaderUtilities.TryParseInt32(nameValue.Value, out seconds))
if (!HeaderUtilities.TryParseNonNegativeInt32(nameValue.Value, out seconds))
{
return false;
}

View File

@ -108,7 +108,7 @@ namespace Microsoft.Net.Http.Headers
if (sizeParameter != null)
{
var sizeString = sizeParameter.Value;
if (HeaderUtilities.TryParseInt64(sizeString, out value))
if (HeaderUtilities.TryParseNonNegativeInt64(sizeString, out value))
{
return value;
}

View File

@ -354,13 +354,13 @@ namespace Microsoft.Net.Http.Headers
parsedValue = null;
long from = 0;
if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(fromStartIndex, fromLength), out from))
if ((fromLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(fromStartIndex, fromLength), out from))
{
return false;
}
long to = 0;
if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(toStartIndex, toLength), out to))
if ((toLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(toStartIndex, toLength), out to))
{
return false;
}
@ -372,7 +372,7 @@ namespace Microsoft.Net.Http.Headers
}
long length = 0;
if ((lengthLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(lengthStartIndex, lengthLength),
if ((lengthLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(lengthStartIndex, lengthLength),
out length))
{
return false;

View File

@ -12,7 +12,7 @@ namespace Microsoft.Net.Http.Headers
{
public static class HeaderUtilities
{
private static readonly int _int64MaxStringLength = 20;
private static readonly int _int64MaxStringLength = 19;
private const string QualityName = "q";
internal const string BytesUnit = "bytes";
@ -269,7 +269,7 @@ namespace Microsoft.Net.Http.Headers
var tokenLength = HttpRuleParser.GetTokenLength(headerValues[i], current);
if (tokenLength == targetValue.Length
&& string.Compare(headerValues[i], current, targetValue, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0
&& TryParseInt64FromHeaderValue(current + tokenLength, headerValues[i], out seconds))
&& TryParseNonNegativeInt64FromHeaderValue(current + tokenLength, headerValues[i], out seconds))
{
// Token matches target value and seconds were parsed
value = TimeSpan.FromSeconds(seconds);
@ -342,7 +342,7 @@ namespace Microsoft.Net.Http.Headers
return false;
}
private static unsafe bool TryParseInt64FromHeaderValue(int startIndex, string headerValue, out long result)
private static unsafe bool TryParseNonNegativeInt64FromHeaderValue(int startIndex, string headerValue, out long result)
{
// Trim leading whitespace
startIndex += HttpRuleParser.GetWhitespaceLength(headerValue, startIndex);
@ -359,7 +359,7 @@ namespace Microsoft.Net.Http.Headers
startIndex += HttpRuleParser.GetWhitespaceLength(headerValue, startIndex);
// Try parse the number
if (TryParseInt64(new StringSegment(headerValue, startIndex, HttpRuleParser.GetNumberLength(headerValue, startIndex, false)), out result))
if (TryParseNonNegativeInt64(new StringSegment(headerValue, startIndex, HttpRuleParser.GetNumberLength(headerValue, startIndex, false)), out result))
{
return true;
}
@ -368,9 +368,9 @@ namespace Microsoft.Net.Http.Headers
return false;
}
internal static bool TryParseInt32(string value, out int result)
internal static bool TryParseNonNegativeInt32(string value, out int result)
{
return TryParseInt32(new StringSegment(value), out result);
return TryParseNonNegativeInt32(new StringSegment(value), out result);
}
/// <summary>
@ -388,12 +388,12 @@ namespace Microsoft.Net.Http.Headers
/// result will be overwritten.
/// </param>
/// <returns><code>true</code> if parsing succeeded; otherwise, <code>false</code>.</returns>
public static bool TryParseInt64(string value, out long result)
public static bool TryParseNonNegativeInt64(string value, out long result)
{
return TryParseInt64(new StringSegment(value), out result);
return TryParseNonNegativeInt64(new StringSegment(value), out result);
}
internal static unsafe bool TryParseInt32(StringSegment value, out int result)
internal static unsafe bool TryParseNonNegativeInt32(StringSegment value, out int result)
{
if (string.IsNullOrEmpty(value.Buffer) || value.Length == 0)
{
@ -444,7 +444,7 @@ namespace Microsoft.Net.Http.Headers
/// originally supplied in result will be overwritten.
/// </param>
/// <returns><code>true</code> if parsing succeeded; otherwise, <code>false</code>.</returns>
public static unsafe bool TryParseInt64(StringSegment value, out long result)
public static unsafe bool TryParseNonNegativeInt64(StringSegment value, out long result)
{
if (string.IsNullOrEmpty(value.Buffer) || value.Length == 0)
{
@ -481,31 +481,22 @@ namespace Microsoft.Net.Http.Headers
}
/// <summary>
/// Converts the signed 64-bit numeric value to its equivalent string representation.
/// Converts the non-negative 64-bit numeric value to its equivalent string representation.
/// </summary>
/// <param name="value">
/// The number to convert.
/// </param>
/// <returns>
/// The string representation of the value of this instance, consisting of a minus sign if the value is
/// negative, and a sequence of digits ranging from 0 to 9 with no leading zeroes.
/// The string representation of the value of this instance, consisting of a sequence of digits ranging from 0 to 9 with no leading zeroes.
/// </returns>
public unsafe static string FormatInt64(long value)
public unsafe static string FormatNonNegativeInt64(long value)
{
var position = _int64MaxStringLength;
var negative = false;
if (value < 0)
{
// Not possible to compute absolute value of MinValue, return the exact string instead.
if (value == long.MinValue)
{
return "-9223372036854775808";
}
negative = true;
value = -value;
throw new ArgumentOutOfRangeException(nameof(value), value, "The value to be formatted must be non-negative.");
}
var position = _int64MaxStringLength;
char* charBuffer = stackalloc char[_int64MaxStringLength];
do
@ -517,11 +508,6 @@ namespace Microsoft.Net.Http.Headers
}
while (value != 0);
if (negative)
{
charBuffer[--position] = '-';
}
return new string(charBuffer, position, _int64MaxStringLength - position);
}

View File

@ -202,14 +202,14 @@ namespace Microsoft.Net.Http.Headers
// Try convert first value to int64
long from = 0;
if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(fromStartIndex, fromLength), out from))
if ((fromLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(fromStartIndex, fromLength), out from))
{
return 0;
}
// Try convert second value to int64
long to = 0;
if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(toStartIndex, toLength), out to))
if ((toLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(toStartIndex, toLength), out to))
{
return 0;
}

View File

@ -105,7 +105,7 @@ namespace Microsoft.Net.Http.Headers
if (MaxAge.HasValue)
{
maxAge = HeaderUtilities.FormatInt64((long)MaxAge.Value.TotalSeconds);
maxAge = HeaderUtilities.FormatNonNegativeInt64((long)MaxAge.Value.TotalSeconds);
length += SeparatorToken.Length + MaxAgeToken.Length + EqualsToken.Length + maxAge.Length;
}
@ -200,7 +200,7 @@ namespace Microsoft.Net.Http.Headers
if (MaxAge.HasValue)
{
AppendSegment(builder, MaxAgeToken, HeaderUtilities.FormatInt64((long)MaxAge.Value.TotalSeconds));
AppendSegment(builder, MaxAgeToken, HeaderUtilities.FormatNonNegativeInt64((long)MaxAge.Value.TotalSeconds));
}
if (Domain != null)
@ -365,7 +365,7 @@ namespace Microsoft.Net.Http.Headers
}
var numberString = input.Substring(offset, itemLength);
long maxAge;
if (!HeaderUtilities.TryParseInt64(numberString, out maxAge))
if (!HeaderUtilities.TryParseNonNegativeInt64(numberString, out maxAge))
{
// Invalid expiration date, abort
return 0;

View File

@ -87,15 +87,20 @@ namespace Microsoft.Net.Http.Headers
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(-1)]
[InlineData(1234567890)]
[InlineData(-1234567890)]
[InlineData(long.MaxValue)]
[InlineData(long.MinValue)]
[InlineData(long.MinValue + 1)]
public void FormatInt64_MatchesToString(long value)
public void FormatNonNegativeInt64_MatchesToString(long value)
{
Assert.Equal(value.ToString(CultureInfo.InvariantCulture), HeaderUtilities.FormatInt64(value));
Assert.Equal(value.ToString(CultureInfo.InvariantCulture), HeaderUtilities.FormatNonNegativeInt64(value));
}
[Theory]
[InlineData(-1)]
[InlineData(-1234567890)]
[InlineData(long.MinValue)]
public void FormatNonNegativeInt64_Throws_ForNegativeValues(long value)
{
Assert.Throws<ArgumentOutOfRangeException>(() => HeaderUtilities.FormatNonNegativeInt64(value));
}
[Theory]
@ -150,20 +155,20 @@ namespace Microsoft.Net.Http.Headers
[InlineData("a")]
[InlineData("1.1")]
[InlineData("9223372036854775808")] // long.MaxValue + 1
public void TryParseInt64_Fails(string valueString)
public void TryParseNonNegativeInt64_Fails(string valueString)
{
long value = 1;
Assert.False(HeaderUtilities.TryParseInt64(valueString, out value));
Assert.False(HeaderUtilities.TryParseNonNegativeInt64(valueString, out value));
Assert.Equal(0, value);
}
[Theory]
[InlineData("0", 0)]
[InlineData("9223372036854775807", 9223372036854775807)] // long.MaxValue
public void TryParseInt64_Succeeds(string valueString, long expected)
public void TryParseNonNegativeInt64_Succeeds(string valueString, long expected)
{
long value = 1;
Assert.True(HeaderUtilities.TryParseInt64(valueString, out value));
Assert.True(HeaderUtilities.TryParseNonNegativeInt64(valueString, out value));
Assert.Equal(expected, value);
}
@ -175,20 +180,20 @@ namespace Microsoft.Net.Http.Headers
[InlineData("1.1")]
[InlineData("1,000")]
[InlineData("2147483648")] // int.MaxValue + 1
public void TryParseInt32_Fails(string valueString)
public void TryParseNonNegativeInt32_Fails(string valueString)
{
int value = 1;
Assert.False(HeaderUtilities.TryParseInt32(valueString, out value));
Assert.False(HeaderUtilities.TryParseNonNegativeInt32(valueString, out value));
Assert.Equal(0, value);
}
[Theory]
[InlineData("0", 0)]
[InlineData("2147483647", 2147483647)] // int.MaxValue
public void TryParseInt32_Succeeds(string valueString, long expected)
public void TryParseNonNegativeInt32_Succeeds(string valueString, long expected)
{
int value = 1;
Assert.True(HeaderUtilities.TryParseInt32(valueString, out value));
Assert.True(HeaderUtilities.TryParseNonNegativeInt32(valueString, out value));
Assert.Equal(expected, value);
}
}