Skip to content

Commit d1c9ce9

Browse files
committed
Add source code of Compression Message Encoder Sample
1 parent 2078717 commit d1c9ce9

File tree

18 files changed

+843
-0
lines changed

18 files changed

+843
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<Using Include="System.ServiceModel" />
12+
<Using Include="System.ServiceModel.Channels" />
13+
<Using Include="CoreWcf.Samples.CompressionMessageEncoder" />
14+
<Using Include="CoreWcf.Samples.GZipEncoder" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="System.ServiceModel.Http" Version="4.*" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\GZipEncoder\GZipEncoder.csproj" />
23+
</ItemGroup>
24+
25+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"ExtendedData": {
3+
"inputs": [
4+
"http://localhost:5000/gzipMessageEncoding?wsdl"
5+
],
6+
"collectionTypes": [
7+
"System.Array",
8+
"System.Collections.Generic.Dictionary`2"
9+
],
10+
"namespaceMappings": [
11+
"*, CoreWcf.Samples.CompressionMessageEncoder"
12+
],
13+
"sync": true,
14+
"targetFramework": "net6.0",
15+
"typeReuseMode": "All"
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
namespace CoreWcf.Samples.CompressionMessageEncoder
11+
{
12+
13+
14+
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
15+
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="CoreWcf.Samples.CompressionMessageEncoder.IEchoService")]
16+
public interface IEchoService
17+
{
18+
19+
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/Echo", ReplyAction="http://tempuri.org/IEchoService/EchoResponse")]
20+
string Echo(string input);
21+
22+
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/Echo", ReplyAction="http://tempuri.org/IEchoService/EchoResponse")]
23+
System.Threading.Tasks.Task<string> EchoAsync(string input);
24+
25+
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/BigEcho", ReplyAction="http://tempuri.org/IEchoService/BigEchoResponse")]
26+
string[] BigEcho(string[] input);
27+
28+
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IEchoService/BigEcho", ReplyAction="http://tempuri.org/IEchoService/BigEchoResponse")]
29+
System.Threading.Tasks.Task<string[]> BigEchoAsync(string[] input);
30+
}
31+
32+
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
33+
public interface IEchoServiceChannel : CoreWcf.Samples.CompressionMessageEncoder.IEchoService, System.ServiceModel.IClientChannel
34+
{
35+
}
36+
37+
[System.Diagnostics.DebuggerStepThroughAttribute()]
38+
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
39+
public partial class EchoServiceClient : System.ServiceModel.ClientBase<CoreWcf.Samples.CompressionMessageEncoder.IEchoService>, CoreWcf.Samples.CompressionMessageEncoder.IEchoService
40+
{
41+
42+
public EchoServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
43+
base(binding, remoteAddress)
44+
{
45+
}
46+
47+
public string Echo(string input)
48+
{
49+
return base.Channel.Echo(input);
50+
}
51+
52+
public System.Threading.Tasks.Task<string> EchoAsync(string input)
53+
{
54+
return base.Channel.EchoAsync(input);
55+
}
56+
57+
public string[] BigEcho(string[] input)
58+
{
59+
return base.Channel.BigEcho(input);
60+
}
61+
62+
public System.Threading.Tasks.Task<string[]> BigEchoAsync(string[] input)
63+
{
64+
return base.Channel.BigEchoAsync(input);
65+
}
66+
67+
public virtual System.Threading.Tasks.Task OpenAsync()
68+
{
69+
return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
70+
}
71+
}
72+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
//The service contract is defined using Connected Service "WCF Web Service", generated from the service by the dotnet svcutil tool.
5+
6+
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
7+
HttpTransportBindingElement httpTransportBindingElement = basicHttpBinding.CreateBindingElements().Find<HttpTransportBindingElement>();
8+
MessageEncodingBindingElement encodingBindingElement = new GZipMessageEncodingBindingElement();
9+
CustomBinding binding = new CustomBinding(new BindingElement[]
10+
{
11+
encodingBindingElement,
12+
httpTransportBindingElement
13+
});
14+
15+
var endpointAddress = new EndpointAddress("http://localhost:5000/gzipMessageEncoding");
16+
17+
// Create a client with given client endpoint configuration
18+
EchoServiceClient client = new EchoServiceClient(binding, endpointAddress);
19+
20+
Console.WriteLine("Calling Echo(string):");
21+
Console.WriteLine("Server responds: {0}", client.Echo("Simple hello"));
22+
23+
Console.WriteLine();
24+
Console.WriteLine("Calling BigEcho(string[]):");
25+
string[] messages = new string[64];
26+
for (int i = 0; i < 64; i++)
27+
{
28+
messages[i] = "Hello " + i;
29+
}
30+
31+
Console.WriteLine("Server responds: {0}", client.BigEcho(messages));
32+
33+
//Closing the client gracefully closes the connection and cleans up resources
34+
client.Close();
35+
36+
Console.WriteLine();
37+
Console.WriteLine("Press <ENTER> to terminate client.");
38+
Console.ReadLine();
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.2.32422.2
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Service", "Service\Service.csproj", "{BF126326-3393-407C-B24A-8FCCC388BE27}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{B533CADA-93BB-40E1-8FBA-FE37100062C3}"
9+
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZipEncoder", "GZipEncoder\GZipEncoder.csproj", "{D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GZipEncoderCoreWCF", "GZipEncoderCoreWCF\GZipEncoderCoreWCF.csproj", "{5DA1D92E-E835-49B7-8009-BD75E1ECC050}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Release|Any CPU = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{BF126326-3393-407C-B24A-8FCCC388BE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{BF126326-3393-407C-B24A-8FCCC388BE27}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{BF126326-3393-407C-B24A-8FCCC388BE27}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{BF126326-3393-407C-B24A-8FCCC388BE27}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{B533CADA-93BB-40E1-8FBA-FE37100062C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{B533CADA-93BB-40E1-8FBA-FE37100062C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{B533CADA-93BB-40E1-8FBA-FE37100062C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{B533CADA-93BB-40E1-8FBA-FE37100062C3}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{D1EAF072-0F66-4BD0-BC53-D4ABC45163C5}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{5DA1D92E-E835-49B7-8009-BD75E1ECC050}.Release|Any CPU.Build.0 = Release|Any CPU
36+
EndGlobalSection
37+
GlobalSection(SolutionProperties) = preSolution
38+
HideSolutionNode = FALSE
39+
EndGlobalSection
40+
GlobalSection(ExtensibilityGlobals) = postSolution
41+
SolutionGuid = {AD996EFD-70DC-4431-B411-5A2771DD02D3}
42+
EndGlobalSection
43+
EndGlobal
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<Using Include="System.ServiceModel" />
11+
<Using Include="System.ServiceModel.Channels" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="System.ServiceModel.Http" Version="4.*" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+

2+
using System.IO.Compression;
3+
4+
namespace CoreWcf.Samples.GZipEncoder
5+
{
6+
//This class is used to create the custom encoder (GZipMessageEncoder)
7+
internal class GZipMessageEncoderFactory : MessageEncoderFactory
8+
{
9+
private readonly MessageEncoder _encoder;
10+
11+
//The GZip encoder wraps an inner encoder
12+
//We require a factory to be passed in that will create this inner encoder
13+
public GZipMessageEncoderFactory(MessageEncoderFactory messageEncoderFactory)
14+
{
15+
if (messageEncoderFactory == null)
16+
throw new ArgumentNullException(nameof(messageEncoderFactory), "A valid message encoder factory must be passed to the GZipEncoder");
17+
_encoder = new GZipMessageEncoder(messageEncoderFactory.Encoder);
18+
}
19+
20+
//The service framework uses this property to obtain an encoder from this encoder factory
21+
public override MessageEncoder Encoder => _encoder;
22+
23+
public override MessageVersion MessageVersion => _encoder.MessageVersion;
24+
25+
//This is the actual GZip encoder
26+
public class GZipMessageEncoder : MessageEncoder
27+
{
28+
private static readonly string s_gZipContentType = "application/x-gzip";
29+
30+
//This implementation wraps an inner encoder that actually converts a WCF Message
31+
//into textual XML, binary XML or some other format. This implementation then compresses the results.
32+
//The opposite happens when reading messages.
33+
//This member stores this inner encoder.
34+
private readonly MessageEncoder _innerEncoder;
35+
36+
//We require an inner encoder to be supplied (see comment above)
37+
internal GZipMessageEncoder(MessageEncoder messageEncoder)
38+
: base()
39+
{
40+
_innerEncoder = messageEncoder ?? throw new ArgumentNullException(nameof(messageEncoder), "A valid message encoder must be passed to the GZipEncoder");
41+
}
42+
43+
public override string ContentType => s_gZipContentType;
44+
45+
public override string MediaType => s_gZipContentType;
46+
47+
//SOAP version to use - we delegate to the inner encoder for this
48+
public override MessageVersion MessageVersion => _innerEncoder.MessageVersion;
49+
50+
//Helper method to compress an array of bytes
51+
static ArraySegment<byte> CompressBuffer(ArraySegment<byte> buffer, BufferManager bufferManager, int messageOffset)
52+
{
53+
MemoryStream memoryStream = new MemoryStream();
54+
55+
using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
56+
{
57+
gzStream.Write(buffer.Array, buffer.Offset, buffer.Count);
58+
}
59+
60+
byte[] compressedBytes = memoryStream.ToArray();
61+
int totalLength = messageOffset + compressedBytes.Length;
62+
byte[] bufferedBytes = bufferManager.TakeBuffer(totalLength);
63+
64+
Array.Copy(compressedBytes, 0, bufferedBytes, messageOffset, compressedBytes.Length);
65+
66+
bufferManager.ReturnBuffer(buffer.Array);
67+
ArraySegment<byte> byteArray = new ArraySegment<byte>(bufferedBytes, messageOffset, bufferedBytes.Length - messageOffset);
68+
69+
return byteArray;
70+
}
71+
72+
//Helper method to decompress an array of bytes
73+
static ArraySegment<byte> DecompressBuffer(ArraySegment<byte> buffer, BufferManager bufferManager)
74+
{
75+
MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count);
76+
MemoryStream decompressedStream = new MemoryStream();
77+
int totalRead = 0;
78+
int blockSize = 1024;
79+
byte[] tempBuffer = bufferManager.TakeBuffer(blockSize);
80+
using (GZipStream gzStream = new GZipStream(memoryStream, CompressionMode.Decompress))
81+
{
82+
while (true)
83+
{
84+
int bytesRead = gzStream.Read(tempBuffer, 0, blockSize);
85+
if (bytesRead == 0)
86+
break;
87+
decompressedStream.Write(tempBuffer, 0, bytesRead);
88+
totalRead += bytesRead;
89+
}
90+
}
91+
bufferManager.ReturnBuffer(tempBuffer);
92+
93+
byte[] decompressedBytes = decompressedStream.ToArray();
94+
byte[] bufferManagerBuffer = bufferManager.TakeBuffer(decompressedBytes.Length + buffer.Offset);
95+
Array.Copy(buffer.Array, 0, bufferManagerBuffer, 0, buffer.Offset);
96+
Array.Copy(decompressedBytes, 0, bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
97+
98+
ArraySegment<byte> byteArray = new ArraySegment<byte>(bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
99+
bufferManager.ReturnBuffer(buffer.Array);
100+
101+
return byteArray;
102+
}
103+
104+
//One of the two main entry points into the encoder. Called by WCF to decode a buffered byte array into a Message.
105+
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
106+
{
107+
//Decompress the buffer
108+
ArraySegment<byte> decompressedBuffer = DecompressBuffer(buffer, bufferManager);
109+
//Use the inner encoder to decode the decompressed buffer
110+
Message returnMessage = _innerEncoder.ReadMessage(decompressedBuffer, bufferManager);
111+
returnMessage.Properties.Encoder = this;
112+
return returnMessage;
113+
}
114+
115+
//One of the two main entry points into the encoder. Called by WCF to encode a Message into a buffered byte array.
116+
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
117+
{
118+
//Use the inner encoder to encode a Message into a buffered byte array
119+
ArraySegment<byte> buffer = _innerEncoder.WriteMessage(message, maxMessageSize, bufferManager, 0);
120+
//Compress the resulting byte array
121+
return CompressBuffer(buffer, bufferManager, messageOffset);
122+
}
123+
124+
public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
125+
{
126+
//Pass false for the "leaveOpen" parameter to the GZipStream constructor.
127+
//This will ensure that the inner stream gets closed when the message gets closed, which
128+
//will ensure that resources are available for reuse/release.
129+
GZipStream gzStream = new GZipStream(stream, CompressionMode.Decompress, false);
130+
return _innerEncoder.ReadMessage(gzStream, maxSizeOfHeaders);
131+
}
132+
133+
public override void WriteMessage(Message message, System.IO.Stream stream)
134+
{
135+
using (GZipStream gzStream = new GZipStream(stream, CompressionMode.Compress, true))
136+
{
137+
_innerEncoder.WriteMessage(message, gzStream);
138+
}
139+
140+
// innerEncoder.WriteMessage(message, gzStream) depends on that it can flush data by flushing
141+
// the stream passed in, but the implementation of GZipStream.Flush will not flush underlying
142+
// stream, so we need to flush here.
143+
stream.Flush();
144+
}
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)