diff --git a/Extensibility/MessageEncoder/Compression/Client/Client.csproj b/Extensibility/MessageEncoder/Compression/Client/Client.csproj
new file mode 100644
index 0000000..fb65c84
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Client/Client.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extensibility/MessageEncoder/Compression/Client/Connected Services/CoreWcf.Samples.CompressionMessageEncoder/ConnectedService.json b/Extensibility/MessageEncoder/Compression/Client/Connected Services/CoreWcf.Samples.CompressionMessageEncoder/ConnectedService.json
new file mode 100644
index 0000000..e595574
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Client/Connected Services/CoreWcf.Samples.CompressionMessageEncoder/ConnectedService.json
@@ -0,0 +1,17 @@
+{
+ "ExtendedData": {
+ "inputs": [
+ "http://localhost:5000/gzipMessageEncoding?wsdl"
+ ],
+ "collectionTypes": [
+ "System.Array",
+ "System.Collections.Generic.Dictionary`2"
+ ],
+ "namespaceMappings": [
+ "*, CoreWcf.Samples.CompressionMessageEncoder"
+ ],
+ "sync": true,
+ "targetFramework": "net6.0",
+ "typeReuseMode": "All"
+ }
+}
\ No newline at end of file
diff --git a/Extensibility/MessageEncoder/Compression/Client/Connected Services/CoreWcf.Samples.CompressionMessageEncoder/Reference.cs b/Extensibility/MessageEncoder/Compression/Client/Connected Services/CoreWcf.Samples.CompressionMessageEncoder/Reference.cs
new file mode 100644
index 0000000..c4006c9
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Client/Connected Services/CoreWcf.Samples.CompressionMessageEncoder/Reference.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace CoreWcf.Samples.CompressionMessageEncoder
+{
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
+ [System.ServiceModel.ServiceContractAttribute(ConfigurationName="CoreWcf.Samples.CompressionMessageEncoder.IEchoService")]
+ public interface IEchoService
+ {
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/Echo", ReplyAction="http://tempuri.org/IEchoService/EchoResponse")]
+ string Echo(string input);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/Echo", ReplyAction="http://tempuri.org/IEchoService/EchoResponse")]
+ System.Threading.Tasks.Task EchoAsync(string input);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/BigEcho", ReplyAction="http://tempuri.org/IEchoService/BigEchoResponse")]
+ string[] BigEcho(string[] input);
+
+ [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/BigEcho", ReplyAction="http://tempuri.org/IEchoService/BigEchoResponse")]
+ System.Threading.Tasks.Task BigEchoAsync(string[] input);
+ }
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
+ public interface IEchoServiceChannel : CoreWcf.Samples.CompressionMessageEncoder.IEchoService, System.ServiceModel.IClientChannel
+ {
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
+ public partial class EchoServiceClient : System.ServiceModel.ClientBase, CoreWcf.Samples.CompressionMessageEncoder.IEchoService
+ {
+
+ public EchoServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
+ base(binding, remoteAddress)
+ {
+ }
+
+ public string Echo(string input)
+ {
+ return base.Channel.Echo(input);
+ }
+
+ public System.Threading.Tasks.Task EchoAsync(string input)
+ {
+ return base.Channel.EchoAsync(input);
+ }
+
+ public string[] BigEcho(string[] input)
+ {
+ return base.Channel.BigEcho(input);
+ }
+
+ public System.Threading.Tasks.Task BigEchoAsync(string[] input)
+ {
+ return base.Channel.BigEchoAsync(input);
+ }
+
+ public virtual System.Threading.Tasks.Task OpenAsync()
+ {
+ return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/Client/Program.cs b/Extensibility/MessageEncoder/Compression/Client/Program.cs
new file mode 100644
index 0000000..59233a4
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Client/Program.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//The service contract is defined using Connected Service "WCF Web Service", generated from the service by the dotnet svcutil tool.
+
+BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
+HttpTransportBindingElement httpTransportBindingElement = basicHttpBinding.CreateBindingElements().Find();
+MessageEncodingBindingElement encodingBindingElement = new GZipMessageEncodingBindingElement();
+CustomBinding binding = new CustomBinding(new BindingElement[]
+{
+ encodingBindingElement,
+ httpTransportBindingElement
+});
+
+var endpointAddress = new EndpointAddress("http://localhost:5000/gzipMessageEncoding");
+
+// Create a client with given client endpoint configuration
+EchoServiceClient client = new EchoServiceClient(binding, endpointAddress);
+
+Console.WriteLine("Calling Echo(string):");
+Console.WriteLine("Server responds: {0}", client.Echo("Simple hello"));
+
+Console.WriteLine();
+Console.WriteLine("Calling BigEcho(string[]):");
+string[] messages = new string[64];
+for (int i = 0; i < 64; i++)
+{
+ messages[i] = "Hello " + i;
+}
+
+Console.WriteLine("Server responds: {0}", client.BigEcho(messages));
+
+//Closing the client gracefully closes the connection and cleans up resources
+client.Close();
+
+Console.WriteLine();
+Console.WriteLine("Press to terminate client.");
+Console.ReadLine();
diff --git a/Extensibility/MessageEncoder/Compression/Extensibility.MessageEncoder.Compression.sln b/Extensibility/MessageEncoder/Compression/Extensibility.MessageEncoder.Compression.sln
new file mode 100644
index 0000000..e061c41
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Extensibility.MessageEncoder.Compression.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32422.2
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Service", "Service\Service.csproj", "{BF126326-3393-407C-B24A-8FCCC388BE27}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{B533CADA-93BB-40E1-8FBA-FE37100062C3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZipEncoder", "GZipEncoder\GZipEncoder.csproj", "{D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZipEncoderCoreWCF", "GZipEncoderCoreWCF\GZipEncoderCoreWCF.csproj", "{5DA1D92E-E835-49B7-8009-BD75E1ECC050}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BF126326-3393-407C-B24A-8FCCC388BE27}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B533CADA-93BB-40E1-8FBA-FE37100062C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AD996EFD-70DC-4431-B411-5A2771DD02D3}
+ EndGlobalSection
+EndGlobal
diff --git a/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipEncoder.csproj b/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipEncoder.csproj
new file mode 100644
index 0000000..d0bfff1
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipEncoder.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipMessageEncoderFactory.cs b/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipMessageEncoderFactory.cs
new file mode 100644
index 0000000..cfbcb3a
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipMessageEncoderFactory.cs
@@ -0,0 +1,147 @@
+
+using System.IO.Compression;
+
+namespace CoreWcf.Samples.GZipEncoder
+{
+ //This class is used to create the custom encoder (GZipMessageEncoder)
+ internal class GZipMessageEncoderFactory : MessageEncoderFactory
+ {
+ private readonly MessageEncoder _encoder;
+
+ //The GZip encoder wraps an inner encoder
+ //We require a factory to be passed in that will create this inner encoder
+ public GZipMessageEncoderFactory(MessageEncoderFactory messageEncoderFactory)
+ {
+ if (messageEncoderFactory == null)
+ throw new ArgumentNullException(nameof(messageEncoderFactory), "A valid message encoder factory must be passed to the GZipEncoder");
+ _encoder = new GZipMessageEncoder(messageEncoderFactory.Encoder);
+ }
+
+ //The service framework uses this property to obtain an encoder from this encoder factory
+ public override MessageEncoder Encoder => _encoder;
+
+ public override MessageVersion MessageVersion => _encoder.MessageVersion;
+
+ //This is the actual GZip encoder
+ public class GZipMessageEncoder : MessageEncoder
+ {
+ private static readonly string s_gZipContentType = "application/x-gzip";
+
+ //This implementation wraps an inner encoder that actually converts a WCF Message
+ //into textual XML, binary XML or some other format. This implementation then compresses the results.
+ //The opposite happens when reading messages.
+ //This member stores this inner encoder.
+ private readonly MessageEncoder _innerEncoder;
+
+ //We require an inner encoder to be supplied (see comment above)
+ internal GZipMessageEncoder(MessageEncoder messageEncoder)
+ : base()
+ {
+ _innerEncoder = messageEncoder ?? throw new ArgumentNullException(nameof(messageEncoder), "A valid message encoder must be passed to the GZipEncoder");
+ }
+
+ public override string ContentType => s_gZipContentType;
+
+ public override string MediaType => s_gZipContentType;
+
+ //SOAP version to use - we delegate to the inner encoder for this
+ public override MessageVersion MessageVersion => _innerEncoder.MessageVersion;
+
+ //Helper method to compress an array of bytes
+ static ArraySegment CompressBuffer(ArraySegment buffer, BufferManager bufferManager, int messageOffset)
+ {
+ MemoryStream memoryStream = new MemoryStream();
+
+ using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
+ {
+ gzStream.Write(buffer.Array, buffer.Offset, buffer.Count);
+ }
+
+ byte[] compressedBytes = memoryStream.ToArray();
+ int totalLength = messageOffset + compressedBytes.Length;
+ byte[] bufferedBytes = bufferManager.TakeBuffer(totalLength);
+
+ Array.Copy(compressedBytes, 0, bufferedBytes, messageOffset, compressedBytes.Length);
+
+ bufferManager.ReturnBuffer(buffer.Array);
+ ArraySegment byteArray = new ArraySegment(bufferedBytes, messageOffset, bufferedBytes.Length - messageOffset);
+
+ return byteArray;
+ }
+
+ //Helper method to decompress an array of bytes
+ static ArraySegment DecompressBuffer(ArraySegment buffer, BufferManager bufferManager)
+ {
+ MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count);
+ MemoryStream decompressedStream = new MemoryStream();
+ int totalRead = 0;
+ int blockSize = 1024;
+ byte[] tempBuffer = bufferManager.TakeBuffer(blockSize);
+ using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Decompress))
+ {
+ while (true)
+ {
+ int bytesRead = gzStream.Read(tempBuffer, 0, blockSize);
+ if (bytesRead == 0)
+ break;
+ decompressedStream.Write(tempBuffer, 0, bytesRead);
+ totalRead += bytesRead;
+ }
+ }
+ bufferManager.ReturnBuffer(tempBuffer);
+
+ byte[] decompressedBytes = decompressedStream.ToArray();
+ byte[] bufferManagerBuffer = bufferManager.TakeBuffer(decompressedBytes.Length + buffer.Offset);
+ Array.Copy(buffer.Array, 0, bufferManagerBuffer, 0, buffer.Offset);
+ Array.Copy(decompressedBytes, 0, bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
+
+ ArraySegment byteArray = new ArraySegment(bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
+ bufferManager.ReturnBuffer(buffer.Array);
+
+ return byteArray;
+ }
+
+ //One of the two main entry points into the encoder. Called by WCF to decode a buffered byte array into a Message.
+ public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType)
+ {
+ //Decompress the buffer
+ ArraySegment decompressedBuffer = DecompressBuffer(buffer, bufferManager);
+ //Use the inner encoder to decode the decompressed buffer
+ Message returnMessage = _innerEncoder.ReadMessage(decompressedBuffer, bufferManager);
+ returnMessage.Properties.Encoder = this;
+ return returnMessage;
+ }
+
+ //One of the two main entry points into the encoder. Called by WCF to encode a Message into a buffered byte array.
+ public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
+ {
+ //Use the inner encoder to encode a Message into a buffered byte array
+ ArraySegment buffer = _innerEncoder.WriteMessage(message, maxMessageSize, bufferManager, 0);
+ //Compress the resulting byte array
+ return CompressBuffer(buffer, bufferManager, messageOffset);
+ }
+
+ public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
+ {
+ //Pass false for the "leaveOpen" parameter to the GZipStream constructor.
+ //This will ensure that the inner stream gets closed when the message gets closed, which
+ //will ensure that resources are available for reuse/release.
+ GZipStream gzStream = new GZipStream(stream, CompressionMode.Decompress, false);
+ return _innerEncoder.ReadMessage(gzStream, maxSizeOfHeaders);
+ }
+
+ public override void WriteMessage(Message message, System.IO.Stream stream)
+ {
+ using (GZipStream gzStream = new GZipStream(stream, CompressionMode.Compress, true))
+ {
+ _innerEncoder.WriteMessage(message, gzStream);
+ }
+
+ // innerEncoder.WriteMessage(message, gzStream) depends on that it can flush data by flushing
+ // the stream passed in, but the implementation of GZipStream.Flush will not flush underlying
+ // stream, so we need to flush here.
+ stream.Flush();
+ }
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipMessageEncodingBindingElement.cs b/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipMessageEncodingBindingElement.cs
new file mode 100644
index 0000000..8944af9
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/GZipEncoder/GZipMessageEncodingBindingElement.cs
@@ -0,0 +1,54 @@
+using System.Xml;
+
+namespace CoreWcf.Samples.GZipEncoder
+{
+ //This is the binding element that, when plugged into a custom binding, will enable the GZip encoder
+ public sealed class GZipMessageEncodingBindingElement : MessageEncodingBindingElement
+ {
+ //By default, use the default text encoder as the inner encoder
+ public GZipMessageEncodingBindingElement()
+ : this(new TextMessageEncodingBindingElement()) { }
+
+ public GZipMessageEncodingBindingElement(MessageEncodingBindingElement messageEncoderBindingElement)
+ {
+ InnerMessageEncodingBindingElement = messageEncoderBindingElement;
+ }
+
+ public MessageEncodingBindingElement InnerMessageEncodingBindingElement { get; }
+
+ //Main entry point into the encoder binding element. Called by WCF to get the factory that will create the message encoder
+ public override MessageEncoderFactory CreateMessageEncoderFactory()
+ {
+ return new GZipMessageEncoderFactory(InnerMessageEncodingBindingElement.CreateMessageEncoderFactory());
+ }
+
+ public override MessageVersion MessageVersion
+ {
+ get { return InnerMessageEncodingBindingElement.MessageVersion; }
+ set { InnerMessageEncodingBindingElement.MessageVersion = value; }
+ }
+
+ public override BindingElement Clone() => new GZipMessageEncodingBindingElement(InnerMessageEncodingBindingElement);
+
+ public override T GetProperty(BindingContext context)
+ {
+ if (typeof(T) == typeof(XmlDictionaryReaderQuotas))
+ {
+ return InnerMessageEncodingBindingElement.GetProperty(context);
+ }
+ else
+ {
+ return base.GetProperty(context);
+ }
+ }
+
+ public override IChannelFactory BuildChannelFactory(BindingContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException(nameof(context));
+
+ context.BindingParameters.Add(this);
+ return context.BuildInnerChannelFactory();
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipEncoderCoreWCF.csproj b/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipEncoderCoreWCF.csproj
new file mode 100644
index 0000000..0739e21
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipEncoderCoreWCF.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipMessageEncoderFactory.cs b/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipMessageEncoderFactory.cs
new file mode 100644
index 0000000..290e135
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipMessageEncoderFactory.cs
@@ -0,0 +1,147 @@
+
+using System.IO.Compression;
+
+namespace CoreWcf.Samples.GZipEncoder
+{
+ //This class is used to create the custom encoder (GZipMessageEncoder)
+ internal class GZipMessageEncoderFactory : MessageEncoderFactory
+ {
+ private readonly MessageEncoder _encoder;
+
+ //The GZip encoder wraps an inner encoder
+ //We require a factory to be passed in that will create this inner encoder
+ public GZipMessageEncoderFactory(MessageEncoderFactory messageEncoderFactory)
+ {
+ if (messageEncoderFactory == null)
+ throw new ArgumentNullException(nameof(messageEncoderFactory), "A valid message encoder factory must be passed to the GZipEncoder");
+ _encoder = new GZipMessageEncoder(messageEncoderFactory.Encoder);
+ }
+
+ //The service framework uses this property to obtain an encoder from this encoder factory
+ public override MessageEncoder Encoder => _encoder;
+
+ public override MessageVersion MessageVersion => _encoder.MessageVersion;
+
+ //This is the actual GZip encoder
+ public class GZipMessageEncoder : MessageEncoder
+ {
+ private static readonly string s_gZipContentType = "application/x-gzip";
+
+ //This implementation wraps an inner encoder that actually converts a WCF Message
+ //into textual XML, binary XML or some other format. This implementation then compresses the results.
+ //The opposite happens when reading messages.
+ //This member stores this inner encoder.
+ private readonly MessageEncoder _innerEncoder;
+
+ //We require an inner encoder to be supplied (see comment above)
+ internal GZipMessageEncoder(MessageEncoder messageEncoder)
+ : base()
+ {
+ _innerEncoder = messageEncoder ?? throw new ArgumentNullException(nameof(messageEncoder), "A valid message encoder must be passed to the GZipEncoder");
+ }
+
+ public override string ContentType => s_gZipContentType;
+
+ public override string MediaType => s_gZipContentType;
+
+ //SOAP version to use - we delegate to the inner encoder for this
+ public override MessageVersion MessageVersion => _innerEncoder.MessageVersion;
+
+ //Helper method to compress an array of bytes
+ static ArraySegment CompressBuffer(ArraySegment buffer, BufferManager bufferManager, int messageOffset)
+ {
+ MemoryStream memoryStream = new MemoryStream();
+
+ using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
+ {
+ gzStream.Write(buffer.Array, buffer.Offset, buffer.Count);
+ }
+
+ byte[] compressedBytes = memoryStream.ToArray();
+ int totalLength = messageOffset + compressedBytes.Length;
+ byte[] bufferedBytes = bufferManager.TakeBuffer(totalLength);
+
+ Array.Copy(compressedBytes, 0, bufferedBytes, messageOffset, compressedBytes.Length);
+
+ bufferManager.ReturnBuffer(buffer.Array);
+ ArraySegment byteArray = new ArraySegment(bufferedBytes, messageOffset, bufferedBytes.Length - messageOffset);
+
+ return byteArray;
+ }
+
+ //Helper method to decompress an array of bytes
+ static ArraySegment DecompressBuffer(ArraySegment buffer, BufferManager bufferManager)
+ {
+ MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count);
+ MemoryStream decompressedStream = new MemoryStream();
+ int totalRead = 0;
+ int blockSize = 1024;
+ byte[] tempBuffer = bufferManager.TakeBuffer(blockSize);
+ using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Decompress))
+ {
+ while (true)
+ {
+ int bytesRead = gzStream.Read(tempBuffer, 0, blockSize);
+ if (bytesRead == 0)
+ break;
+ decompressedStream.Write(tempBuffer, 0, bytesRead);
+ totalRead += bytesRead;
+ }
+ }
+ bufferManager.ReturnBuffer(tempBuffer);
+
+ byte[] decompressedBytes = decompressedStream.ToArray();
+ byte[] bufferManagerBuffer = bufferManager.TakeBuffer(decompressedBytes.Length + buffer.Offset);
+ Array.Copy(buffer.Array, 0, bufferManagerBuffer, 0, buffer.Offset);
+ Array.Copy(decompressedBytes, 0, bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
+
+ ArraySegment byteArray = new ArraySegment(bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
+ bufferManager.ReturnBuffer(buffer.Array);
+
+ return byteArray;
+ }
+
+ //One of the two main entry points into the encoder. Called by WCF to decode a buffered byte array into a Message.
+ public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType)
+ {
+ //Decompress the buffer
+ ArraySegment decompressedBuffer = DecompressBuffer(buffer, bufferManager);
+ //Use the inner encoder to decode the decompressed buffer
+ Message returnMessage = _innerEncoder.ReadMessage(decompressedBuffer, bufferManager);
+ returnMessage.Properties.Encoder = this;
+ return returnMessage;
+ }
+
+ //One of the two main entry points into the encoder. Called by WCF to encode a Message into a buffered byte array.
+ public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
+ {
+ //Use the inner encoder to encode a Message into a buffered byte array
+ ArraySegment buffer = _innerEncoder.WriteMessage(message, maxMessageSize, bufferManager, 0);
+ //Compress the resulting byte array
+ return CompressBuffer(buffer, bufferManager, messageOffset);
+ }
+
+ public override async Task ReadMessageAsync(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
+ {
+ //Pass false for the "leaveOpen" parameter to the GZipStream constructor.
+ //This will ensure that the inner stream gets closed when the message gets closed, which
+ //will ensure that resources are available for reuse/release.
+ GZipStream gzStream = new GZipStream(stream, CompressionMode.Decompress, false);
+ return await _innerEncoder.ReadMessageAsync(gzStream, maxSizeOfHeaders);
+ }
+
+ public override async Task WriteMessageAsync(Message message, System.IO.Stream stream)
+ {
+ using (GZipStream gzStream = new GZipStream(stream, CompressionMode.Compress, true))
+ {
+ await _innerEncoder.WriteMessageAsync(message, gzStream);
+ }
+
+ // innerEncoder.WriteMessage(message, gzStream) depends on that it can flush data by flushing
+ // the stream passed in, but the implementation of GZipStream.Flush will not flush underlying
+ // stream, so we need to flush here.
+ stream.Flush();
+ }
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipMessageEncodingBindingElement.cs b/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipMessageEncodingBindingElement.cs
new file mode 100644
index 0000000..6bf59b6
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/GZipEncoderCoreWCF/GZipMessageEncodingBindingElement.cs
@@ -0,0 +1,116 @@
+
+using System.Configuration;
+using System.Xml;
+
+namespace CoreWcf.Samples.GZipEncoder
+{
+ // This is constants for GZip message encoding policy.
+ static class GZipMessageEncodingPolicyConstants
+ {
+ public const string GZipEncodingName = "GZipEncoding";
+ public const string GZipEncodingNamespace = "http://schemas.microsoft.com/ws/06/2004/mspolicy/netgzip1";
+ public const string GZipEncodingPrefix = "gzip";
+ }
+
+ //This is the binding element that, when plugged into a custom binding, will enable the GZip encoder
+ public sealed class GZipMessageEncodingBindingElement : MessageEncodingBindingElement, IPolicyExportExtension
+ {
+ //By default, use the default text encoder as the inner encoder
+ public GZipMessageEncodingBindingElement()
+ : this(new TextMessageEncodingBindingElement()) { }
+
+ public GZipMessageEncodingBindingElement(MessageEncodingBindingElement messageEncoderBindingElement)
+ {
+ InnerMessageEncodingBindingElement = messageEncoderBindingElement;
+ }
+
+ public MessageEncodingBindingElement InnerMessageEncodingBindingElement { get; set; }
+
+ //Main entry point into the encoder binding element. Called by WCF to get the factory that will create the message encoder
+ public override MessageEncoderFactory CreateMessageEncoderFactory()
+ {
+ return new GZipMessageEncoderFactory(InnerMessageEncodingBindingElement.CreateMessageEncoderFactory());
+ }
+
+ public override MessageVersion MessageVersion
+ {
+ get { return InnerMessageEncodingBindingElement.MessageVersion; }
+ set { InnerMessageEncodingBindingElement.MessageVersion = value; }
+ }
+
+ public override BindingElement Clone() => new GZipMessageEncodingBindingElement(InnerMessageEncodingBindingElement);
+
+ public override T GetProperty(BindingContext context)
+ {
+ if (typeof(T) == typeof(XmlDictionaryReaderQuotas))
+ {
+ return InnerMessageEncodingBindingElement.GetProperty(context);
+ }
+ else
+ {
+ return base.GetProperty(context);
+ }
+ }
+
+ void IPolicyExportExtension.ExportPolicy(MetadataExporter exporter, PolicyConversionContext policyContext)
+ {
+ if (policyContext == null)
+ {
+ throw new ArgumentNullException(nameof(policyContext));
+ }
+ XmlDocument document = new XmlDocument();
+ policyContext.GetBindingAssertions().Add(document.CreateElement(
+ GZipMessageEncodingPolicyConstants.GZipEncodingPrefix,
+ GZipMessageEncodingPolicyConstants.GZipEncodingName,
+ GZipMessageEncodingPolicyConstants.GZipEncodingNamespace));
+ }
+ }
+
+ //This class is necessary to be able to plug in the GZip encoder binding element through
+ //a configuration file
+ public class GZipMessageEncodingElement : BindingElementExtensionElement
+ {
+ public GZipMessageEncodingElement()
+ {
+ }
+
+ //Called by the WCF to discover the type of binding element this config section enables
+ public override Type BindingElementType => typeof(GZipMessageEncodingBindingElement);
+
+ //The only property we need to configure for our binding element is the type of
+ //inner encoder to use. Here, we support text and binary.
+ [ConfigurationProperty("innerMessageEncoding", DefaultValue = "textMessageEncoding")]
+ public string InnerMessageEncoding
+ {
+ get { return (string)base["innerMessageEncoding"]; }
+ set { base["innerMessageEncoding"] = value; }
+ }
+
+ //Called by the WCF to apply the configuration settings (the property above) to the binding element
+ public override void ApplyConfiguration(BindingElement bindingElement)
+ {
+ GZipMessageEncodingBindingElement binding = (GZipMessageEncodingBindingElement)bindingElement;
+ PropertyInformationCollection propertyInfo = ElementInformation.Properties;
+ if (propertyInfo["innerMessageEncoding"].ValueOrigin != PropertyValueOrigin.Default)
+ {
+ switch (InnerMessageEncoding)
+ {
+ case "textMessageEncoding":
+ binding.InnerMessageEncodingBindingElement = new TextMessageEncodingBindingElement();
+ break;
+ case "binaryMessageEncoding":
+ binding.InnerMessageEncodingBindingElement = new BinaryMessageEncodingBindingElement();
+ break;
+ }
+ }
+ }
+
+ //Called by the WCF to create the binding element
+ protected override BindingElement CreateBindingElement()
+ {
+ GZipMessageEncodingBindingElement bindingElement = new GZipMessageEncodingBindingElement();
+ ApplyConfiguration(bindingElement);
+ return bindingElement;
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/Service/EchoService.cs b/Extensibility/MessageEncoder/Compression/Service/EchoService.cs
new file mode 100644
index 0000000..9b5a573
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/EchoService.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace CoreWcf.Samples.CompressionMessageEncoder
+{
+ // Service class which implements the service contract interface.
+ public class EchoService : IEchoService
+ {
+ public string Echo(string input)
+ {
+ Console.WriteLine("\n\tServer Echo(string input) called:", input);
+ Console.WriteLine("\tClient message:\t{0}\n", input);
+ return input + " " + input;
+ }
+
+ public string[] BigEcho(string[] input)
+ {
+ Console.WriteLine("\n\tServer BigEcho(string[] input) called:", input);
+ Console.WriteLine("\t{0} client messages", input.Length);
+ return input;
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/Service/IEchoService.cs b/Extensibility/MessageEncoder/Compression/Service/IEchoService.cs
new file mode 100644
index 0000000..b35f6da
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/IEchoService.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace CoreWcf.Samples.CompressionMessageEncoder
+{
+ // Define a service contract.
+ [ServiceContract]
+ public interface IEchoService
+ {
+ [OperationContract]
+ string Echo(string input);
+
+ [OperationContract]
+ string[] BigEcho(string[] input);
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/Service/Program.cs b/Extensibility/MessageEncoder/Compression/Service/Program.cs
new file mode 100644
index 0000000..d93693e
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/Program.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+var builder = WebApplication.CreateBuilder();
+
+//Enable CoreWCF Services, with metadata (WSDL) support
+builder.Services.AddServiceModelServices()
+ .AddServiceModelMetadata()
+ .AddSingleton();
+
+var app = builder.Build();
+
+app.UseServiceModel(builder =>
+{
+ // Add the Calculator Service
+ builder.AddService(serviceOptions => { });
+
+ BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
+ HttpTransportBindingElement httpTransportBindingElement = basicHttpBinding.CreateBindingElements().Find();
+ MessageEncodingBindingElement encodingBindingElement = new GZipMessageEncodingBindingElement();
+ CustomBinding binding = new CustomBinding(new BindingElement[]
+ {
+ encodingBindingElement,
+ httpTransportBindingElement
+ });
+
+ builder.AddServiceEndpoint(binding, "gzipMessageEncoding");
+
+ // Configure WSDL to be available
+ var serviceMetadataBehavior = app.Services.GetRequiredService();
+ serviceMetadataBehavior.HttpGetEnabled = true;
+});
+
+app.Run();
diff --git a/Extensibility/MessageEncoder/Compression/Service/Properties/launchSettings.json b/Extensibility/MessageEncoder/Compression/Service/Properties/launchSettings.json
new file mode 100644
index 0000000..47a16a7
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:57035/gzipMessageEncoding",
+ "sslPort": 44315
+ }
+ },
+ "profiles": {
+ "Service": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "http://localhost:5000/gzipMessageEncoding",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/Service/Service.csproj b/Extensibility/MessageEncoder/Compression/Service/Service.csproj
new file mode 100644
index 0000000..01d8e08
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/Service.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net6.0
+ enable
+ true
+ InProcess
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extensibility/MessageEncoder/Compression/Service/appsettings.Development.json b/Extensibility/MessageEncoder/Compression/Service/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/Extensibility/MessageEncoder/Compression/Service/appsettings.json b/Extensibility/MessageEncoder/Compression/Service/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/Extensibility/MessageEncoder/Compression/Service/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}