Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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