Skip to content

Commit

Permalink
Add source code of Compression Message Encoder Sample
Browse files Browse the repository at this point in the history
  • Loading branch information
afifi-ins committed Apr 10, 2023
1 parent 2078717 commit d1c9ce9
Show file tree
Hide file tree
Showing 18 changed files with 843 additions and 0 deletions.
25 changes: 25 additions & 0 deletions Extensibility/MessageEncoder/Compression/Client/Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<Using Include="System.ServiceModel" />
<Using Include="System.ServiceModel.Channels" />
<Using Include="CoreWcf.Samples.CompressionMessageEncoder" />
<Using Include="CoreWcf.Samples.GZipEncoder" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.ServiceModel.Http" Version="4.*" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\GZipEncoder\GZipEncoder.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

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<string> 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<string[]> 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>, 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<string> EchoAsync(string input)
{
return base.Channel.EchoAsync(input);
}

public string[] BigEcho(string[] input)
{
return base.Channel.BigEcho(input);
}

public System.Threading.Tasks.Task<string[]> 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.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
}
}
}
38 changes: 38 additions & 0 deletions Extensibility/MessageEncoder/Compression/Client/Program.cs
Original file line number Diff line number Diff line change
@@ -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<HttpTransportBindingElement>();
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 <ENTER> to terminate client.");
Console.ReadLine();
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<Using Include="System.ServiceModel" />
<Using Include="System.ServiceModel.Channels" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.ServiceModel.Http" Version="4.*" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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<byte> CompressBuffer(ArraySegment<byte> 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<byte> byteArray = new ArraySegment<byte>(bufferedBytes, messageOffset, bufferedBytes.Length - messageOffset);

return byteArray;
}

//Helper method to decompress an array of bytes
static ArraySegment<byte> DecompressBuffer(ArraySegment<byte> 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<byte> byteArray = new ArraySegment<byte>(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<byte> buffer, BufferManager bufferManager, string contentType)
{
//Decompress the buffer
ArraySegment<byte> 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<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
//Use the inner encoder to encode a Message into a buffered byte array
ArraySegment<byte> 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();
}
}
}
}
Loading

0 comments on commit d1c9ce9

Please sign in to comment.