diff --git a/projects/RabbitMQ.Client/client/api/IBasicProperties.cs b/projects/RabbitMQ.Client/client/api/IBasicProperties.cs index c1ccc74349..16b4e477c4 100644 --- a/projects/RabbitMQ.Client/client/api/IBasicProperties.cs +++ b/projects/RabbitMQ.Client/client/api/IBasicProperties.cs @@ -121,9 +121,9 @@ public interface IBasicProperties : IContentHeader string ReplyTo { get; set; } /// - /// Convenience property; parses property using , + /// Convenience property; parses property using , /// and serializes it using . - /// Returns null if property cannot be parsed by . + /// Returns null if property cannot be parsed by . /// PublicationAddress ReplyToAddress { get; set; } diff --git a/projects/RabbitMQ.Client/client/api/PublicationAddress.cs b/projects/RabbitMQ.Client/client/api/PublicationAddress.cs index 60fdbd0c80..9f819f8e7f 100644 --- a/projects/RabbitMQ.Client/client/api/PublicationAddress.cs +++ b/projects/RabbitMQ.Client/client/api/PublicationAddress.cs @@ -116,6 +116,32 @@ public static PublicationAddress Parse(string uriLikeString) return null; } + public static bool TryParse(string uriLikeString, out PublicationAddress result) + { + // Callers such as IBasicProperties.ReplyToAddress + // expect null result for invalid input. + // The regex.Match() throws on null arguments so we perform explicit check here + if (uriLikeString == null) + { + result = null; + return false; + } + else + { + try + { + var res = Parse(uriLikeString); + result = res; + return true; + } + catch + { + result = null; + return false; + } + } + } + /// /// Reconstruct the "uri" from its constituents. /// diff --git a/projects/RabbitMQ.Client/client/impl/BasicProperties.cs b/projects/RabbitMQ.Client/client/impl/BasicProperties.cs index b824801b83..05ffda72a2 100644 --- a/projects/RabbitMQ.Client/client/impl/BasicProperties.cs +++ b/projects/RabbitMQ.Client/client/impl/BasicProperties.cs @@ -109,13 +109,16 @@ public bool Persistent public abstract string ReplyTo { get; set; } /// - /// Convenience property; parses property using , + /// Convenience property; parses property using , /// and serializes it using . - /// Returns null if property cannot be parsed by . + /// Returns null if property cannot be parsed by . /// public PublicationAddress ReplyToAddress { - get { return PublicationAddress.Parse(ReplyTo); } + get { + PublicationAddress.TryParse(ReplyTo, out var result); + return result; + } set { ReplyTo = value.ToString(); } } diff --git a/projects/Unit/APIApproval.Approve.verified.txt b/projects/Unit/APIApproval.Approve.verified.txt index f07a3050cd..3d8a9c9e03 100644 --- a/projects/Unit/APIApproval.Approve.verified.txt +++ b/projects/Unit/APIApproval.Approve.verified.txt @@ -483,6 +483,7 @@ namespace RabbitMQ.Client public string RoutingKey { get; } public override string ToString() { } public static RabbitMQ.Client.PublicationAddress Parse(string uriLikeString) { } + public static bool TryParse(string uriLikeString, out RabbitMQ.Client.PublicationAddress result) { } } public class QueueDeclareOk { diff --git a/projects/Unit/TestBasicProperties.cs b/projects/Unit/TestBasicProperties.cs index 3b7da08dc1..3693309c8e 100644 --- a/projects/Unit/TestBasicProperties.cs +++ b/projects/Unit/TestBasicProperties.cs @@ -52,7 +52,6 @@ public void TestPersistentPropertyChangesDeliveryMode_PersistentTrueDelivery2() // Arrange var subject = new Framing.BasicProperties { - // Act Persistent = true }; @@ -120,5 +119,35 @@ public void TestNullableProperties_CanWrite( Assert.AreEqual(isCorrelationIdPresent, propertiesFromStream.IsCorrelationIdPresent()); Assert.AreEqual(isMessageIdPresent, propertiesFromStream.IsMessageIdPresent()); } + + [Test] + public void TestProperties_ReplyTo([Values(null, "foo_1", "fanout://name/key")] string replyTo) + { + // Arrange + var subject = new Framing.BasicProperties + { + // Act + ReplyTo = replyTo, + }; + + // Assert + bool isReplyToPresent = replyTo != null; + PublicationAddress result; + PublicationAddress.TryParse(replyTo, out result); + string replyToAddress = result?.ToString(); + Assert.AreEqual(isReplyToPresent, subject.IsReplyToPresent()); + + var writer = new Impl.ContentHeaderPropertyWriter(new byte[1024]); + subject.WritePropertiesTo(ref writer); + + // Read from Stream + var propertiesFromStream = new Framing.BasicProperties(); + var reader = new Impl.ContentHeaderPropertyReader(writer.Memory.Slice(0, writer.Offset)); + propertiesFromStream.ReadPropertiesFrom(ref reader); + + Assert.AreEqual(replyTo, propertiesFromStream.ReplyTo); + Assert.AreEqual(isReplyToPresent, propertiesFromStream.IsReplyToPresent()); + Assert.AreEqual(replyToAddress, propertiesFromStream.ReplyToAddress?.ToString()); + } } } diff --git a/projects/Unit/TestPropertiesClone.cs b/projects/Unit/TestPropertiesClone.cs index f5827b5a0e..06be6fffea 100644 --- a/projects/Unit/TestPropertiesClone.cs +++ b/projects/Unit/TestPropertiesClone.cs @@ -68,10 +68,10 @@ private void TestBasicPropertiesClone(BasicProperties bp) bp.ContentType = "foo_1"; bp.ContentEncoding = "foo_2"; bp.Headers = new Dictionary - { - { "foo_3", "foo_4" }, - { "foo_5", "foo_6" } - }; + { + { "foo_3", "foo_4" }, + { "foo_5", "foo_6" } + }; bp.DeliveryMode = 2; // Persistent also changes DeliveryMode's value to 2 bp.Persistent = true; @@ -123,6 +123,7 @@ private void TestBasicPropertiesClone(BasicProperties bp) Assert.AreEqual(12, bpClone.Priority); Assert.AreEqual("foo_7", bpClone.CorrelationId); Assert.AreEqual("foo_8", bpClone.ReplyTo); + Assert.AreEqual(null, bpClone.ReplyToAddress); Assert.AreEqual("foo_9", bpClone.Expiration); Assert.AreEqual("foo_10", bpClone.MessageId); Assert.AreEqual(new AmqpTimestamp(123), bpClone.Timestamp); @@ -141,10 +142,10 @@ private void TestBasicPropertiesNoneClone(BasicProperties bp) bp.ContentType = "foo_1"; bp.ContentEncoding = "foo_2"; bp.Headers = new Dictionary - { - { "foo_3", "foo_4" }, - { "foo_5", "foo_6" } - }; + { + { "foo_3", "foo_4" }, + { "foo_5", "foo_6" } + }; bp.DeliveryMode = 2; // Persistent also changes DeliveryMode's value to 2 bp.Persistent = true; diff --git a/projects/Unit/TestPublicationAddress.cs b/projects/Unit/TestPublicationAddress.cs index 0d9b7ca924..7371959177 100644 --- a/projects/Unit/TestPublicationAddress.cs +++ b/projects/Unit/TestPublicationAddress.cs @@ -57,9 +57,29 @@ public void TestParseOk() } [Test] - public void TestParseFail() + public void TestParseFailWithANE() { - Assert.IsNull(PublicationAddress.Parse("not a valid uri")); + Assert.That(()=> PublicationAddress.Parse(null), Throws.ArgumentNullException); + } + + [Test] + public void TestParseFailWithUnparseableInput() + { + Assert.IsNull(PublicationAddress.Parse("not a valid URI")); + } + + [Test] + public void TestTryParseFail() + { + PublicationAddress result; + PublicationAddress.TryParse(null, out result); + Assert.IsNull(result); + + PublicationAddress.TryParse("not a valid URI", out result); + Assert.IsNull(result); + + PublicationAddress.TryParse("}}}}}}}}", out result); + Assert.IsNull(result); } [Test]