diff --git a/.gitignore b/.gitignore
index 2a7adc0..9549575 100644
--- a/.gitignore
+++ b/.gitignore
@@ -249,6 +249,10 @@ $RECYCLE.BIN/
.DS_Store
.vs/config/applicationhost.config
.vs/
+
+# VS Code
+.vscode
+*.code-workspace
.idea/
# Windows Installer files
diff --git a/Obvs.Tests/GZippedMessageSerializationTest.cs b/Obvs.Tests/GZippedMessageSerializationTest.cs
new file mode 100644
index 0000000..fbde0c1
--- /dev/null
+++ b/Obvs.Tests/GZippedMessageSerializationTest.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Text;
+using System.IO;
+
+using FakeItEasy;
+
+using Obvs.Types;
+using Obvs.Serialization;
+
+using Xunit;
+
+namespace Obvs.Tests {
+
+ ///
+ /// Test Gzipped message serialization
+ ///
+ public class GZippedMessageSerializationTest {
+
+ public class TestMessage: IMessage {
+ public string Content {get; set;} = null;
+
+ public override bool Equals(object obj)
+ {
+ //
+ // See the full list of guidelines at
+ // http://go.microsoft.com/fwlink/?LinkID=85237
+ // and also the guidance for operator== at
+ // http://go.microsoft.com/fwlink/?LinkId=85238
+ //
+
+ if (obj == null || GetType() != obj.GetType())
+ {
+ return false;
+ }
+
+ return this.Content.Equals(((TestMessage) obj).Content);
+ }
+
+ // override object.GetHashCode
+ public override int GetHashCode()
+ {
+ return Content.GetHashCode();
+ }
+
+ }
+
+ #region "Serialization tests"
+ [Fact]
+ public void Test_GZippedMessageSerializerNullActionConstruction_Fails() {
+ Assert.Throws(typeof(ArgumentNullException),
+ () => new GZippedMessageSerializer(null as Action));
+ }
+
+ [Fact]
+ public void Test_GZippedMessageSerializerNullGzippedSerializerConstruction_Fails() {
+ Assert.Throws(typeof(ArgumentNullException),
+ () => new GZippedMessageSerializer(null as GZippedMessageSerializer));
+ }
+
+ [Fact]
+ public void Test_GZippedMessageSerializerGzippedSerializerConstruction_Fails() {
+ Assert.Throws(typeof(ArgumentException),
+ () => new GZippedMessageSerializer(new GZippedMessageSerializer( (Stream stream, object obj) => {})));
+ }
+
+ #endregion
+
+ #region "Deserialization tests"
+
+ [Fact]
+ public void Test_GZippedMessageDeserializerNullActionConstruction_Fails() {
+ Assert.Throws(
+ typeof(ArgumentNullException),
+ () => new GZippedMessageDeserializer(null as Func));
+ }
+
+
+ [Fact]
+ public void Test_GZippedMessageDeserializerNullGzippedSerializerConstruction_Fails() {
+ Assert.Throws(typeof(ArgumentNullException),
+ () => new GZippedMessageDeserializer(null as GZippedMessageDeserializer));
+ }
+ #endregion
+
+ [Fact]
+ public void Test_GZippedMessageDeserializerGzippedSerializerConstruction_Fails() {
+ Assert.Throws(typeof(ArgumentException),
+ () => new GZippedMessageDeserializer(new GZippedMessageDeserializer( (Stream stream) => new TestMessage{})));
+ }
+
+ [Fact]
+ public void Test_GzippedMessageFullSerialization_Succeeds() {
+ var content = "test";
+ var wasSerializeActionCalled = false;
+ Action serializeAction = (stream, obj) => {
+ wasSerializeActionCalled = true;
+ var message = obj as TestMessage;
+ var contentBytes = Encoding.UTF8.GetBytes(message.Content);
+ stream.Write(contentBytes, 0, contentBytes.Length);
+ };
+ var messageSerializer = new GZippedMessageSerializer(serializeAction);
+
+ var wasDeserializeFuncCalled = false;
+ Func deserializeFunction = stream => {
+ wasDeserializeFuncCalled = true;
+ var buffer = new byte[Encoding.UTF8.GetByteCount(content)];
+ var numBytesRead = stream.Read(buffer, 0, buffer.Length);
+ var deserializedMessage = new TestMessage {
+ Content = Encoding.UTF8.GetString(buffer)
+ };
+ return deserializedMessage;
+ };
+ var messageDeserializer = new GZippedMessageDeserializer(deserializeFunction);
+
+ var testMessage = new TestMessage {
+ Content = content
+ };
+ var messageStream = new MemoryStream();
+ messageSerializer.Serialize(messageStream, testMessage);
+ Assert.True(wasSerializeActionCalled);
+ messageStream.Position = 0;
+ var deserializedTestMessage = messageDeserializer.Deserialize(messageStream);
+ Assert.True(wasDeserializeFuncCalled);
+
+ Assert.Equal(testMessage, deserializedTestMessage);
+ }
+
+ #region "IMessage serializer extensions"
+ [Fact]
+ public void Test_SerializeGZipped_Fails() {
+ var messageSerializer = new GZippedMessageSerializer((stream, obj) => {});
+ Assert.Throws(typeof(ArgumentException), () => messageSerializer.SerializeGZipped());
+ }
+
+ [Fact]
+ public void Test_DeserializeGZipped_Fails() {
+ var messageDeserializer = new GZippedMessageDeserializer((stream) => new TestMessage{});
+ Assert.Throws(typeof(ArgumentException), () => messageDeserializer.DeserializeGZipped());
+ }
+
+ [Fact]
+ public void Test_SerializeGZipped_Success() {
+ var messageSerializer = A.Fake();
+ var gzippedMessageSerializer = messageSerializer.SerializeGZipped();
+ Assert.NotNull(gzippedMessageSerializer);
+ }
+
+ [Fact]
+ public void Test_DeserializeGZipped_Success() {
+ var messageDeserializer = A.Fake>();
+ var gzippedMessageDeserializer = messageDeserializer.DeserializeGZipped();
+ Assert.NotNull(gzippedMessageDeserializer);
+ }
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/Obvs.Tests/Obvs.Tests.csproj b/Obvs.Tests/Obvs.Tests.csproj
index a656c5f..6ccc5ef 100644
--- a/Obvs.Tests/Obvs.Tests.csproj
+++ b/Obvs.Tests/Obvs.Tests.csproj
@@ -3,8 +3,9 @@
net5.0
Christopher Read
Copyright © Christopher Read 2014
-
+
false
+ true
@@ -17,6 +18,6 @@
-
+
\ No newline at end of file
diff --git a/Obvs/Obvs.csproj b/Obvs/Obvs.csproj
index 5b86eff..88580d7 100644
--- a/Obvs/Obvs.csproj
+++ b/Obvs/Obvs.csproj
@@ -9,6 +9,7 @@
obvs messaging microservice bus messagebus rx reactive servicebus
New csproj format
An observable microservice bus .NET library, based on Reactive Extensions. Search 'Obvs' for other transport, serialization, and logging extensions.
+ true
diff --git a/Obvs/Serialization/GZippedMessageDeserializer.cs b/Obvs/Serialization/GZippedMessageDeserializer.cs
new file mode 100644
index 0000000..098bb60
--- /dev/null
+++ b/Obvs/Serialization/GZippedMessageDeserializer.cs
@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+
+namespace Obvs.Serialization
+{
+
+ ///
+ /// Gzipped message deserializer
+ ///
+ /// Type of message
+ public class GZippedMessageDeserializer : MessageDeserializerBase
+ where TMessage : class
+ {
+ ///
+ /// Message from stream deserializer functor
+ ///
+ private readonly Func _messageStreamDeserializerFn;
+
+ ///
+ /// Constructor
+ ///
+ /// Message deserializer fn
+ public GZippedMessageDeserializer(Func messageStreamDeserializerFn)
+ {
+ if (messageStreamDeserializerFn == null) {
+ throw new ArgumentNullException(nameof(messageStreamDeserializerFn));
+ }
+ _messageStreamDeserializerFn = messageStreamDeserializerFn;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Message deserializer implementation<
+ public GZippedMessageDeserializer(IMessageDeserializer messageDeserializer) {
+ if (messageDeserializer == null) {
+ throw new ArgumentNullException(nameof(messageDeserializer));
+ }
+ var gzippedMessageDeserializer = messageDeserializer as GZippedMessageDeserializer;
+ if (gzippedMessageDeserializer != null) {
+ throw new ArgumentException("Invalid message deserializer GZippedMessageDeserializer");
+ }
+ _messageStreamDeserializerFn = messageDeserializer.Deserialize;
+ }
+
+ ///
+ public override TMessage Deserialize(Stream stream)
+ {
+ using (var gzipStream = new GZipStream(stream, CompressionMode.Decompress, true))
+ {
+ return _messageStreamDeserializerFn(gzipStream);
+ }
+ }
+ }
+
+ public static class GZippedMessageDeserializerExtensions {
+
+ ///
+ /// Apply GZip decompression to an existing message serializer
+ ///
+ /// Type of message
+ public static GZippedMessageDeserializer DeserializeGZipped(
+ this IMessageDeserializer messageDeserializer
+ ) where TMessage : class {
+ if (messageDeserializer is GZippedMessageDeserializer) {
+ throw new ArgumentException("Message serializer implementation cannot be GzippedMessageDeserializer");
+ }
+ return new GZippedMessageDeserializer(messageDeserializer.Deserialize);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Obvs/Serialization/GZippedMessageSerializer.cs b/Obvs/Serialization/GZippedMessageSerializer.cs
new file mode 100644
index 0000000..94b13ea
--- /dev/null
+++ b/Obvs/Serialization/GZippedMessageSerializer.cs
@@ -0,0 +1,78 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+
+namespace Obvs.Serialization
+{
+ ///
+ /// Gzipped message serializer
+ ///
+ public class GZippedMessageSerializer : IMessageSerializer
+ {
+
+ ///
+ /// Message to strem serializer functor
+ ///
+ private readonly Action _messageStreamSerializerFn;
+
+ ///
+ /// Compression level
+ ///
+ private readonly CompressionLevel _compressionLevel;
+
+ ///
+ /// Constructor
+ ///
+ /// Message stream serializer
+ /// Compression level
+ public GZippedMessageSerializer(Action messageStreamSerializerFn, CompressionLevel compressionLevel = CompressionLevel.Optimal)
+ {
+ if (messageStreamSerializerFn == null) {
+ throw new ArgumentNullException(nameof(messageStreamSerializerFn));
+ }
+ _messageStreamSerializerFn = messageStreamSerializerFn;
+ _compressionLevel = compressionLevel;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Message stream serializer
+ /// Compression level
+ public GZippedMessageSerializer(IMessageSerializer messageSerializer, CompressionLevel compressionLevel = CompressionLevel.Optimal)
+ {
+ if (messageSerializer == null) {
+ throw new ArgumentNullException(nameof(messageSerializer));
+ }
+ var gzippedMessageSerializer = messageSerializer as GZippedMessageSerializer;
+ if (gzippedMessageSerializer != null) {
+ throw new ArgumentException("Invalid message deserializer GZippedMessageSerializer");
+ }
+ _messageStreamSerializerFn = messageSerializer.Serialize;
+ _compressionLevel = compressionLevel;
+ }
+
+ ///
+ public void Serialize(Stream stream, object message)
+ {
+ using (var gzipStream = new GZipStream(stream, _compressionLevel, true))
+ {
+ _messageStreamSerializerFn(gzipStream, message);
+ }
+ }
+ }
+
+ public static class GZippedMessageSerializerExtensions {
+
+ ///
+ /// Apply GZip compression to an existing message serializer
+ ///
+ /// Type of message
+ public static GZippedMessageSerializer SerializeGZipped(this IMessageSerializer messageSerializer) {
+ if (messageSerializer is GZippedMessageSerializer) {
+ throw new ArgumentException("Message serializer implementation cannot be GzippedMessageSerializer");
+ }
+ return new GZippedMessageSerializer(messageSerializer.Serialize);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Obvs/Serialization/IMessageSerializer.cs b/Obvs/Serialization/IMessageSerializer.cs
index 7d92f6a..24fef17 100644
--- a/Obvs/Serialization/IMessageSerializer.cs
+++ b/Obvs/Serialization/IMessageSerializer.cs
@@ -2,7 +2,7 @@
namespace Obvs.Serialization
{
- public static class MessageSerializerExtentions
+ public static class MessageSerializerExtensions
{
public static byte[] Serialize(this IMessageSerializer serializer, object obj)
{