diff --git a/src/Http/Http.Abstractions/src/PathString.cs b/src/Http/Http.Abstractions/src/PathString.cs index 485f99b4cda8..15b5798fba9d 100644 --- a/src/Http/Http.Abstractions/src/PathString.cs +++ b/src/Http/Http.Abstractions/src/PathString.cs @@ -212,6 +212,12 @@ public static PathString FromUriComponent(Uri uri) /// /// The to compare. /// true if value matches the beginning of this string; otherwise, false. + /// + /// When the parameter contains a trailing slash, the being checked + /// must either exactly match or include a trailing slash. For instance, for a of "/a/b", + /// this method will return true for "/a", but will return false for "/a/". + /// Whereas, a of "/a//b/" will return true when compared with "/a/". + /// public bool StartsWithSegments(PathString other) { return StartsWithSegments(other, StringComparison.OrdinalIgnoreCase); @@ -224,6 +230,12 @@ public bool StartsWithSegments(PathString other) /// The to compare. /// One of the enumeration values that determines how this and value are compared. /// true if value matches the beginning of this string; otherwise, false. + /// + /// When the parameter contains a trailing slash, the being checked + /// must either exactly match or include a trailing slash. For instance, for a of "/a/b", + /// this method will return true for "/a", but will return false for "/a/". + /// Whereas, a of "/a//b/" will return true when compared with "/a/". + /// public bool StartsWithSegments(PathString other, StringComparison comparisonType) { var value1 = Value ?? string.Empty; @@ -242,6 +254,12 @@ public bool StartsWithSegments(PathString other, StringComparison comparisonType /// The to compare. /// The remaining segments after the match. /// true if value matches the beginning of this string; otherwise, false. + /// + /// When the parameter contains a trailing slash, the being checked + /// must either exactly match or include a trailing slash. For instance, for a of "/a/b", + /// this method will return true for "/a", but will return false for "/a/". + /// Whereas, a of "/a//b/" will return true when compared with "/a/". + /// public bool StartsWithSegments(PathString other, out PathString remaining) { return StartsWithSegments(other, StringComparison.OrdinalIgnoreCase, out remaining); @@ -255,6 +273,12 @@ public bool StartsWithSegments(PathString other, out PathString remaining) /// One of the enumeration values that determines how this and value are compared. /// The remaining segments after the match. /// true if value matches the beginning of this string; otherwise, false. + /// + /// When the parameter contains a trailing slash, the being checked + /// must either exactly match or include a trailing slash. For instance, for a of "/a/b", + /// this method will return true for "/a", but will return false for "/a/". + /// Whereas, a of "/a//b/" will return true when compared with "/a/". + /// public bool StartsWithSegments(PathString other, StringComparison comparisonType, out PathString remaining) { var value1 = Value ?? string.Empty; @@ -279,6 +303,12 @@ public bool StartsWithSegments(PathString other, StringComparison comparisonType /// The matched segments with the original casing in the source value. /// The remaining segments after the match. /// true if value matches the beginning of this string; otherwise, false. + /// + /// When the parameter contains a trailing slash, the being checked + /// must either exactly match or include a trailing slash. For instance, for a of "/a/b", + /// this method will return true for "/a", but will return false for "/a/". + /// Whereas, a of "/a//b/" will return true when compared with "/a/". + /// public bool StartsWithSegments(PathString other, out PathString matched, out PathString remaining) { return StartsWithSegments(other, StringComparison.OrdinalIgnoreCase, out matched, out remaining); @@ -293,6 +323,12 @@ public bool StartsWithSegments(PathString other, out PathString matched, out Pat /// The matched segments with the original casing in the source value. /// The remaining segments after the match. /// true if value matches the beginning of this string; otherwise, false. + /// + /// When the parameter contains a trailing slash, the being checked + /// must either exactly match or include a trailing slash. For instance, for a of "/a/b", + /// this method will return true for "/a", but will return false for "/a/". + /// Whereas, a of "/a//b/" will return true when compared with "/a/". + /// public bool StartsWithSegments(PathString other, StringComparison comparisonType, out PathString matched, out PathString remaining) { var value1 = Value ?? string.Empty; diff --git a/src/Http/Http.Abstractions/test/PathStringTests.cs b/src/Http/Http.Abstractions/test/PathStringTests.cs index b45278c3977c..7b1ec3782e97 100644 --- a/src/Http/Http.Abstractions/test/PathStringTests.cs +++ b/src/Http/Http.Abstractions/test/PathStringTests.cs @@ -126,6 +126,22 @@ public void StartsWithSegments_DoesACaseInsensitiveMatch(string sourcePath, stri Assert.Equal(expectedResult, result); } + [Theory] + [InlineData("/a/", "/a/", true)] + [InlineData("/a/b", "/a/", false)] + [InlineData("/a/b/", "/a/", false)] + [InlineData("/a//b", "/a/", true)] + [InlineData("/a//b/", "/a/", true)] + public void StartsWithSegments_DoesMatchExactPathOrPathWithExtraTrailingSlash(string sourcePath, string testPath, bool expectedResult) + { + var source = new PathString(sourcePath); + var test = new PathString(testPath); + + var result = source.StartsWithSegments(test); + + Assert.Equal(expectedResult, result); + } + [Theory] [InlineData("/test/path", "/TEST", true)] [InlineData("/test/path", "/TEST/pa", false)] @@ -142,6 +158,22 @@ public void StartsWithSegmentsWithRemainder_DoesACaseInsensitiveMatch(string sou Assert.Equal(expectedResult, result); } + [Theory] + [InlineData("/a/", "/a/", true)] + [InlineData("/a/b", "/a/", false)] + [InlineData("/a/b/", "/a/", false)] + [InlineData("/a//b", "/a/", true)] + [InlineData("/a//b/", "/a/", true)] + public void StartsWithSegmentsWithRemainder_DoesMatchExactPathOrPathWithExtraTrailingSlash(string sourcePath, string testPath, bool expectedResult) + { + var source = new PathString(sourcePath); + var test = new PathString(testPath); + + var result = source.StartsWithSegments(test, out var remaining); + + Assert.Equal(expectedResult, result); + } + [Theory] [InlineData("/test/path", "/TEST", StringComparison.OrdinalIgnoreCase, true)] [InlineData("/test/path", "/TEST", StringComparison.Ordinal, false)] @@ -163,6 +195,27 @@ public void StartsWithSegments_DoesMatchUsingSpecifiedComparison(string sourcePa Assert.Equal(expectedResult, result); } + [Theory] + [InlineData("/a/", "/a/", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("/a/", "/a/", StringComparison.Ordinal, true)] + [InlineData("/a/b", "/a/", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("/a/b", "/a/", StringComparison.Ordinal, false)] + [InlineData("/a/b/", "/a/", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("/a/b/", "/a/", StringComparison.Ordinal, false)] + [InlineData("/a//b", "/a/", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("/a//b", "/a/", StringComparison.Ordinal, true)] + [InlineData("/a//b/", "/a/", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("/a//b/", "/a/", StringComparison.Ordinal, true)] + public void StartsWithSegments_DoesMatchExactPathOrPathWithExtraTrailingSlashUsingSpecifiedComparison(string sourcePath, string testPath, StringComparison comparison, bool expectedResult) + { + var source = new PathString(sourcePath); + var test = new PathString(testPath); + + var result = source.StartsWithSegments(test, comparison); + + Assert.Equal(expectedResult, result); + } + [Theory] [InlineData("/test/path", "/TEST", StringComparison.OrdinalIgnoreCase, true)] [InlineData("/test/path", "/TEST", StringComparison.Ordinal, false)] @@ -184,6 +237,27 @@ public void StartsWithSegmentsWithRemainder_DoesMatchUsingSpecifiedComparison(st Assert.Equal(expectedResult, result); } + [Theory] + [InlineData("/a/", "/a/", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("/a/", "/a/", StringComparison.Ordinal, true)] + [InlineData("/a/b", "/a/", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("/a/b", "/a/", StringComparison.Ordinal, false)] + [InlineData("/a/b/", "/a/", StringComparison.OrdinalIgnoreCase, false)] + [InlineData("/a/b/", "/a/", StringComparison.Ordinal, false)] + [InlineData("/a//b", "/a/", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("/a//b", "/a/", StringComparison.Ordinal, true)] + [InlineData("/a//b/", "/a/", StringComparison.OrdinalIgnoreCase, true)] + [InlineData("/a//b/", "/a/", StringComparison.Ordinal, true)] + public void StartsWithSegmentsWithRemainder_DoesMatchExactPathOrPathWithExtraTrailingSlashUsingSpecifiedComparison(string sourcePath, string testPath, StringComparison comparison, bool expectedResult) + { + var source = new PathString(sourcePath); + var test = new PathString(testPath); + + var result = source.StartsWithSegments(test, comparison, out var remaining); + + Assert.Equal(expectedResult, result); + } + [Theory] // unreserved [InlineData("/abc123.-_~", "/abc123.-_~")]