diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AcceptHeaderParser.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AcceptHeaderParser.cs index 45e1800067..d32765128e 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AcceptHeaderParser.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AcceptHeaderParser.cs @@ -36,6 +36,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Internal while (!string.IsNullOrEmpty(value) && charIndex < value.Length) { + var startCharIndex = charIndex; + MediaTypeSegmentWithQuality output; if (TryParseValue(value, ref charIndex, out output)) { @@ -45,6 +47,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Internal parsedValues.Add(output); } } + + if (charIndex <= startCharIndex) + { + Debug.Fail("ParseAcceptHeader should advance charIndex, this is a bug."); + break; + } } } } @@ -110,6 +118,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Internal // E. g application/json, text/plain <- We must be at ',' otherwise, we've failed parsing. if (!separatorFound && (currentIndex < value.Length)) { + index = currentIndex; return false; } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AcceptHeaderParserTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AcceptHeaderParserTest.cs index 9b86759578..9953e32ec2 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AcceptHeaderParserTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AcceptHeaderParserTest.cs @@ -122,5 +122,119 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Internal // Assert Assert.Equal(expected, parsed); } + + // The text "*/*Content-Type" parses as a valid media type value. However it's followed + // by ':' instead of whitespace or a delimiter, which means that it's actually invalid. + [Fact] + public void ParseAcceptHeader_ValidMediaType_FollowedByNondelimiter() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[0]; + + var input = "*/*Content-Type:application/json"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } + + [Fact] + public void ParseAcceptHeader_ValidMediaType_FollowedBySemicolon() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[0]; + + var input = "*/*Content-Type;application/json"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } + + [Fact] + public void ParseAcceptHeader_ValidMediaType_FollowedByComma() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[] + { + new MediaTypeSegmentWithQuality(new StringSegment("*/*Content-Type"), 1.0), + new MediaTypeSegmentWithQuality(new StringSegment("application/json"), 1.0), + }; + + var input = "*/*Content-Type,application/json"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } + + [Fact] + public void ParseAcceptHeader_ValidMediaType_FollowedByWhitespace() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[] + { + new MediaTypeSegmentWithQuality(new StringSegment("application/json"), 1.0), + }; + + var input = "*/*Content-Type application/json"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } + + [Fact] + public void ParseAcceptHeader_InvalidTokenAtStart() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[0]; + + var input = ":;:"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } + + [Fact] + public void ParseAcceptHeader_DelimiterAtStart() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[0]; + + var input = ",;:"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } + + [Fact] + public void ParseAcceptHeader_InvalidTokenAtEnd() + { + // Arrange + var expected = new MediaTypeSegmentWithQuality[0]; + + var input = "*/*:"; + + // Act + var parsed = AcceptHeaderParser.ParseAcceptHeader(new List() { input }); + + // Assert + Assert.Equal(expected, parsed); + } } }