diff --git a/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs b/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs index 4dd1081be9..827a99b437 100644 --- a/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs +++ b/src/Microsoft.Net.Http.Headers/HeaderUtilities.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Globalization; using Microsoft.Extensions.Primitives; @@ -200,6 +201,33 @@ namespace Microsoft.Net.Http.Headers 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; + } + /// /// Try to find a target header value among the set of given header values and parse it as a /// . @@ -237,6 +265,7 @@ namespace Microsoft.Net.Http.Headers while (current < headerValues[i].Length) { long seconds; + var initial = current; var tokenLength = HttpRuleParser.GetTokenLength(headerValues[i], current); if (tokenLength == targetValue.Length && string.Compare(headerValues[i], current, targetValue, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0 @@ -246,26 +275,15 @@ namespace Microsoft.Net.Http.Headers value = TimeSpan.FromSeconds(seconds); return true; } - else + + current = AdvanceCacheDirectiveIndex(current + tokenLength, headerValues[i]); + + // Ensure index was advanced + if (current <= initial) { - // Skip until the next potential name - current += tokenLength; - current += HttpRuleParser.GetWhitespaceLength(headerValues[i], current); - - // 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); - } + Debug.Assert(false, $"Index '{nameof(current)}' not advanced, this is a bug."); + value = null; + return false; } } } @@ -293,7 +311,6 @@ namespace Microsoft.Net.Http.Headers return false; } - for (var i = 0; i < cacheControlDirectives.Count; i++) { // Trim leading white space @@ -301,6 +318,8 @@ namespace Microsoft.Net.Http.Headers while (current < cacheControlDirectives[i].Length) { + var initial = current; + var tokenLength = HttpRuleParser.GetTokenLength(cacheControlDirectives[i], current); if (tokenLength == targetDirectives.Length && string.Compare(cacheControlDirectives[i], current, targetDirectives, 0, tokenLength, StringComparison.OrdinalIgnoreCase) == 0) @@ -308,26 +327,14 @@ namespace Microsoft.Net.Http.Headers // Token matches target value return true; } - else + + current = AdvanceCacheDirectiveIndex(current + tokenLength, cacheControlDirectives[i]); + + // Ensure index was advanced + if (current <= initial) { - // Skip until the next potential name - current += tokenLength; - 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); - } + Debug.Assert(false, $"Index '{nameof(current)}' not advanced, this is a bug."); + return false; } } } diff --git a/test/Microsoft.Net.Http.Headers.Tests/HeaderUtilitiesTest.cs b/test/Microsoft.Net.Http.Headers.Tests/HeaderUtilitiesTest.cs index cde811a082..62917ffc90 100644 --- a/test/Microsoft.Net.Http.Headers.Tests/HeaderUtilitiesTest.cs +++ b/test/Microsoft.Net.Http.Headers.Tests/HeaderUtilitiesTest.cs @@ -71,6 +71,7 @@ namespace Microsoft.Net.Http.Headers [InlineData("directive1", "directive")] [InlineData("h=directive", "directive")] [InlineData("directive1, directive2=80", "directive")] + [InlineData("directive1=;, directive2=10", "directive1")] public void TryParseSeconds_Fails(string headerValues, string targetValue) { TimeSpan? value; @@ -124,6 +125,7 @@ namespace Microsoft.Net.Http.Headers [InlineData("directive1", "directive", false)] [InlineData("h=directive", "directive", false)] [InlineData("directive1, directive2=80", "directive", false)] + [InlineData("directive1;, directive2=80", "directive", false)] public void ContainsCacheDirective_MatchesExactValue(string headerValues, string targetValue, bool contains) { Assert.Equal(contains, HeaderUtilities.ContainsCacheDirective(new StringValues(headerValues), targetValue));