Add check for index advance during parsing

This commit is contained in:
John Luo 2017-01-31 11:03:20 -08:00
parent 779115b1ad
commit 3289afe007
2 changed files with 48 additions and 39 deletions

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Globalization; using System.Globalization;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
@ -200,6 +201,33 @@ namespace Microsoft.Net.Http.Headers
return current; return current;
} }
private static int AdvanceCacheDirectiveIndex(int current, string headerValue)
{
// Skip until the next potential name
current += HttpRuleParser.GetWhitespaceLength(headerValue, current);
// Skip the value if present
if (current < headerValue.Length && headerValue[current] == '=')
{
current++; // skip '='
current += NameValueHeaderValue.GetValueLength(headerValue, current);
}
// Find the next delimiter
current = headerValue.IndexOf(',', current);
if (current == -1)
{
// If no delimiter found, skip to the end
return headerValue.Length;
}
current++; // skip ','
current += HttpRuleParser.GetWhitespaceLength(headerValue, current);
return current;
}
/// <summary> /// <summary>
/// Try to find a target header value among the set of given header values and parse it as a /// Try to find a target header value among the set of given header values and parse it as a
/// <see cref="TimeSpan"/>. /// <see cref="TimeSpan"/>.
@ -237,6 +265,7 @@ namespace Microsoft.Net.Http.Headers
while (current < headerValues[i].Length) while (current < headerValues[i].Length)
{ {
long seconds; long seconds;
var initial = current;
var tokenLength = HttpRuleParser.GetTokenLength(headerValues[i], current); var tokenLength = HttpRuleParser.GetTokenLength(headerValues[i], current);
if (tokenLength == targetValue.Length if (tokenLength == targetValue.Length
&& string.Compare(headerValues[i], current, targetValue, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0 && string.Compare(headerValues[i], current, targetValue, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0
@ -246,26 +275,15 @@ namespace Microsoft.Net.Http.Headers
value = TimeSpan.FromSeconds(seconds); value = TimeSpan.FromSeconds(seconds);
return true; return true;
} }
else
current = AdvanceCacheDirectiveIndex(current + tokenLength, headerValues[i]);
// Ensure index was advanced
if (current <= initial)
{ {
// Skip until the next potential name Debug.Assert(false, $"Index '{nameof(current)}' not advanced, this is a bug.");
current += tokenLength; value = null;
current += HttpRuleParser.GetWhitespaceLength(headerValues[i], current); return false;
// Skip the value if present
if (current < headerValues[i].Length && headerValues[i][current] == '=')
{
current++; // skip '='
current += NameValueHeaderValue.GetValueLength(headerValues[i], current);
current += HttpRuleParser.GetWhitespaceLength(headerValues[i], current);
}
// Skip the delimiter
if (current < headerValues[i].Length && headerValues[i][current] == ',')
{
current++; // skip ','
current += HttpRuleParser.GetWhitespaceLength(headerValues[i], current);
}
} }
} }
} }
@ -293,7 +311,6 @@ namespace Microsoft.Net.Http.Headers
return false; return false;
} }
for (var i = 0; i < cacheControlDirectives.Count; i++) for (var i = 0; i < cacheControlDirectives.Count; i++)
{ {
// Trim leading white space // Trim leading white space
@ -301,6 +318,8 @@ namespace Microsoft.Net.Http.Headers
while (current < cacheControlDirectives[i].Length) while (current < cacheControlDirectives[i].Length)
{ {
var initial = current;
var tokenLength = HttpRuleParser.GetTokenLength(cacheControlDirectives[i], current); var tokenLength = HttpRuleParser.GetTokenLength(cacheControlDirectives[i], current);
if (tokenLength == targetDirectives.Length if (tokenLength == targetDirectives.Length
&& string.Compare(cacheControlDirectives[i], current, targetDirectives, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0) && string.Compare(cacheControlDirectives[i], current, targetDirectives, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0)
@ -308,26 +327,14 @@ namespace Microsoft.Net.Http.Headers
// Token matches target value // Token matches target value
return true; return true;
} }
else
current = AdvanceCacheDirectiveIndex(current + tokenLength, cacheControlDirectives[i]);
// Ensure index was advanced
if (current <= initial)
{ {
// Skip until the next potential name Debug.Assert(false, $"Index '{nameof(current)}' not advanced, this is a bug.");
current += tokenLength; return false;
current += HttpRuleParser.GetWhitespaceLength(cacheControlDirectives[i], current);
// Skip the value if present
if (current < cacheControlDirectives[i].Length && cacheControlDirectives[i][current] == '=')
{
current++; // skip '='
current += NameValueHeaderValue.GetValueLength(cacheControlDirectives[i], current);
current += HttpRuleParser.GetWhitespaceLength(cacheControlDirectives[i], current);
}
// Skip the delimiter
if (current < cacheControlDirectives[i].Length && cacheControlDirectives[i][current] == ',')
{
current++; // skip ','
current += HttpRuleParser.GetWhitespaceLength(cacheControlDirectives[i], current);
}
} }
} }
} }

View File

@ -71,6 +71,7 @@ namespace Microsoft.Net.Http.Headers
[InlineData("directive1", "directive")] [InlineData("directive1", "directive")]
[InlineData("h=directive", "directive")] [InlineData("h=directive", "directive")]
[InlineData("directive1, directive2=80", "directive")] [InlineData("directive1, directive2=80", "directive")]
[InlineData("directive1=;, directive2=10", "directive1")]
public void TryParseSeconds_Fails(string headerValues, string targetValue) public void TryParseSeconds_Fails(string headerValues, string targetValue)
{ {
TimeSpan? value; TimeSpan? value;
@ -124,6 +125,7 @@ namespace Microsoft.Net.Http.Headers
[InlineData("directive1", "directive", false)] [InlineData("directive1", "directive", false)]
[InlineData("h=directive", "directive", false)] [InlineData("h=directive", "directive", false)]
[InlineData("directive1, directive2=80", "directive", false)] [InlineData("directive1, directive2=80", "directive", false)]
[InlineData("directive1;, directive2=80", "directive", false)]
public void ContainsCacheDirective_MatchesExactValue(string headerValues, string targetValue, bool contains) public void ContainsCacheDirective_MatchesExactValue(string headerValues, string targetValue, bool contains)
{ {
Assert.Equal(contains, HeaderUtilities.ContainsCacheDirective(new StringValues(headerValues), targetValue)); Assert.Equal(contains, HeaderUtilities.ContainsCacheDirective(new StringValues(headerValues), targetValue));