From e4afd782e8e5c7e8624abc8b0343d79d3bde8836 Mon Sep 17 00:00:00 2001 From: John Luo Date: Mon, 29 Aug 2016 17:55:20 -0700 Subject: [PATCH] Add strong and weak ETag comparisons --- .../EntityTagHeaderValue.cs | 37 +++++++- .../EntityTagHeaderValueTest.cs | 91 +++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Net.Http.Headers/EntityTagHeaderValue.cs b/src/Microsoft.Net.Http.Headers/EntityTagHeaderValue.cs index 7f89a3e1..7d09920c 100644 --- a/src/Microsoft.Net.Http.Headers/EntityTagHeaderValue.cs +++ b/src/Microsoft.Net.Http.Headers/EntityTagHeaderValue.cs @@ -93,6 +93,15 @@ public override string ToString() return _tag; } + /// + /// Check against another for equality. + /// This equality check should not be used to determine if two values match under the RFC specifications (https://tools.ietf.org/html/rfc7232#section-2.3.2). + /// + /// The other value to check against for equality. + /// + /// true if the strength and tag of the two values match, + /// false if the other value is null, is not an , or if there is a mismatch of strength or tag between the two values. + /// public override bool Equals(object obj) { var other = obj as EntityTagHeaderValue; @@ -103,7 +112,7 @@ public override bool Equals(object obj) } // Since the tag is a quoted-string we treat it case-sensitive. - return ((_isWeak == other._isWeak) && (string.CompareOrdinal(_tag, other._tag) == 0)); + return _isWeak == other._isWeak && string.Equals(_tag, other._tag, StringComparison.Ordinal); } public override int GetHashCode() @@ -112,6 +121,32 @@ public override int GetHashCode() return _tag.GetHashCode() ^ _isWeak.GetHashCode(); } + /// + /// Compares against another to see if they match under the RFC specifications (https://tools.ietf.org/html/rfc7232#section-2.3.2). + /// + /// The other to compare against. + /// true to use a strong comparison, false to use a weak comparison + /// + /// true if the match for the given comparison type, + /// false if the other value is null or the comparison failed. + /// + public bool Compare(EntityTagHeaderValue other, bool useStrongComparison) + { + if (other == null) + { + return false; + } + + if (useStrongComparison) + { + return !IsWeak && !other.IsWeak && string.Equals(Tag, other.Tag, StringComparison.Ordinal); + } + else + { + return string.Equals(Tag, other.Tag, StringComparison.Ordinal); + } + } + public static EntityTagHeaderValue Parse(string input) { var index = 0; diff --git a/test/Microsoft.Net.Http.Headers.Tests/EntityTagHeaderValueTest.cs b/test/Microsoft.Net.Http.Headers.Tests/EntityTagHeaderValueTest.cs index c17c9baa..f633fec2 100644 --- a/test/Microsoft.Net.Http.Headers.Tests/EntityTagHeaderValueTest.cs +++ b/test/Microsoft.Net.Http.Headers.Tests/EntityTagHeaderValueTest.cs @@ -99,6 +99,97 @@ public void Equals_UseSameAndDifferentETags_EqualOrNotEqualNoExceptions() Assert.True(etag1.Equals(etag5), "tag vs. tag.."); } + [Fact] + public void Compare_WithNull_ReturnsFalse() + { + Assert.False(EntityTagHeaderValue.Any.Compare(null, useStrongComparison: true)); + Assert.False(EntityTagHeaderValue.Any.Compare(null, useStrongComparison: false)); + } + + public static TheoryData NotEquivalentUnderStrongComparison + { + get + { + return new TheoryData + { + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"TAG\"") }, + { new EntityTagHeaderValue("\"tag\"", true), new EntityTagHeaderValue("\"tag\"", true) }, + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"", true) }, + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag1\"") }, + { new EntityTagHeaderValue("\"tag\""), EntityTagHeaderValue.Any }, + }; + } + } + + [Theory] + [MemberData(nameof(NotEquivalentUnderStrongComparison))] + public void CompareUsingStrongComparison_NonEquivalentPairs_ReturnFalse(EntityTagHeaderValue left, EntityTagHeaderValue right) + { + Assert.False(left.Compare(right, useStrongComparison: true)); + Assert.False(right.Compare(left, useStrongComparison: true)); + } + + public static TheoryData EquivalentUnderStrongComparison + { + get + { + return new TheoryData + { + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"") }, + }; + } + } + + [Theory] + [MemberData(nameof(EquivalentUnderStrongComparison))] + public void CompareUsingStrongComparison_EquivalentPairs_ReturnTrue(EntityTagHeaderValue left, EntityTagHeaderValue right) + { + Assert.True(left.Compare(right, useStrongComparison: true)); + Assert.True(right.Compare(left, useStrongComparison: true)); + } + + public static TheoryData NotEquivalentUnderWeakComparison + { + get + { + return new TheoryData + { + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"TAG\"") }, + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag1\"") }, + { new EntityTagHeaderValue("\"tag\""), EntityTagHeaderValue.Any }, + }; + } + } + + [Theory] + [MemberData(nameof(NotEquivalentUnderWeakComparison))] + public void CompareUsingWeakComparison_NonEquivalentPairs_ReturnFalse(EntityTagHeaderValue left, EntityTagHeaderValue right) + { + Assert.False(left.Compare(right, useStrongComparison: false)); + Assert.False(right.Compare(left, useStrongComparison: false)); + } + + public static TheoryData EquivalentUnderWeakComparison + { + get + { + return new TheoryData + { + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"") }, + { new EntityTagHeaderValue("\"tag\"", true), new EntityTagHeaderValue("\"tag\"", true) }, + { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"", true) }, + }; + } + } + + [Theory] + [MemberData(nameof(EquivalentUnderWeakComparison))] + public void CompareUsingWeakComparison_EquivalentPairs_ReturnTrue(EntityTagHeaderValue left, EntityTagHeaderValue right) + { + Assert.True(left.Compare(right, useStrongComparison: false)); + Assert.True(right.Compare(left, useStrongComparison: false)); + } + [Fact] public void Parse_SetOfValidValueStrings_ParsedCorrectly() {