Skip to content

Commit eda251c

Browse files
gedemgedem
authored andcommitted
Message header and body, tests, improvements
1 parent 49ace85 commit eda251c

34 files changed

+833
-51
lines changed

NetCoreStack.WebSockets.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NetCoreStack.WebSockets", "
2424
EndProject
2525
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NetCoreStack.WebSockets.ProxyClient", "src\NetCoreStack.WebSockets.ProxyClient\NetCoreStack.WebSockets.ProxyClient.xproj", "{7E772C91-3D01-4886-A591-BD663D9B017F}"
2626
EndProject
27+
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WebClientTestApp2", "test\WebClientTestApp2\WebClientTestApp2.xproj", "{7B3B956E-DF5A-4AD8-BCB9-14235D53471C}"
28+
EndProject
29+
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Common.Libs", "test\Common.Libs\Common.Libs.xproj", "{7ACFEED0-C1D7-43DE-95D7-3D2CEFDF6F9F}"
30+
EndProject
2731
Global
2832
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2933
Debug|Any CPU = Debug|Any CPU
@@ -50,6 +54,14 @@ Global
5054
{7E772C91-3D01-4886-A591-BD663D9B017F}.Debug|Any CPU.Build.0 = Debug|Any CPU
5155
{7E772C91-3D01-4886-A591-BD663D9B017F}.Release|Any CPU.ActiveCfg = Release|Any CPU
5256
{7E772C91-3D01-4886-A591-BD663D9B017F}.Release|Any CPU.Build.0 = Release|Any CPU
57+
{7B3B956E-DF5A-4AD8-BCB9-14235D53471C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58+
{7B3B956E-DF5A-4AD8-BCB9-14235D53471C}.Debug|Any CPU.Build.0 = Debug|Any CPU
59+
{7B3B956E-DF5A-4AD8-BCB9-14235D53471C}.Release|Any CPU.ActiveCfg = Release|Any CPU
60+
{7B3B956E-DF5A-4AD8-BCB9-14235D53471C}.Release|Any CPU.Build.0 = Release|Any CPU
61+
{7ACFEED0-C1D7-43DE-95D7-3D2CEFDF6F9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62+
{7ACFEED0-C1D7-43DE-95D7-3D2CEFDF6F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
63+
{7ACFEED0-C1D7-43DE-95D7-3D2CEFDF6F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
64+
{7ACFEED0-C1D7-43DE-95D7-3D2CEFDF6F9F}.Release|Any CPU.Build.0 = Release|Any CPU
5365
EndGlobalSection
5466
GlobalSection(SolutionProperties) = preSolution
5567
HideSolutionNode = FALSE
@@ -60,5 +72,7 @@ Global
6072
{079F8A19-BC40-46AC-A31F-B133F7D7F1B8} = {A1704016-03C8-4917-9C50-AE9DDF55ECB7}
6173
{C6BD4266-256A-4FF6-B991-D44EFC825FC6} = {52C54080-F48B-4DBA-A418-8057611CAF6D}
6274
{7E772C91-3D01-4886-A591-BD663D9B017F} = {52C54080-F48B-4DBA-A418-8057611CAF6D}
75+
{7B3B956E-DF5A-4AD8-BCB9-14235D53471C} = {A1704016-03C8-4917-9C50-AE9DDF55ECB7}
76+
{7ACFEED0-C1D7-43DE-95D7-3D2CEFDF6F9F} = {A1704016-03C8-4917-9C50-AE9DDF55ECB7}
6377
EndGlobalSection
6478
EndGlobal

src/NetCoreStack.WebSockets/ConnectionManager.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.Concurrent;
88
using System.Collections.Generic;
9+
using System.Diagnostics;
910
using System.IO;
1011
using System.Linq;
1112
using System.Net.WebSockets;
@@ -39,17 +40,17 @@ public ConnectionManager(IStreamCompressor compressor,
3940
Connections = new ConcurrentDictionary<string, WebSocketTransport>();
4041
}
4142

42-
private async Task<byte[]> PrepareBytesAsync(byte[] input, IDictionary<string, object> properties)
43+
private async Task<byte[]> PrepareBytesAsync(byte[] body, IDictionary<string, object> properties)
4344
{
44-
if (input == null)
45+
if (body == null)
4546
{
46-
throw new ArgumentNullException(nameof(input));
47+
throw new ArgumentNullException(nameof(body));
4748
}
4849

4950
if (properties == null)
5051
properties = new Dictionary<string, object>();
5152

52-
bool compressed = GZipHelper.IsGZipHeader(input);
53+
bool compressed = GZipHelper.IsGZipBody(body);
5354

5455
object key = null;
5556
if (properties.TryGetValue(CompressedKey, out key))
@@ -58,17 +59,22 @@ private async Task<byte[]> PrepareBytesAsync(byte[] input, IDictionary<string, o
5859
properties.Add(CompressedKey, compressed);
5960

6061
string props = JsonConvert.SerializeObject(properties);
61-
byte[] header = Encoding.UTF8.GetBytes($"{props}{Splitter}");
62+
byte[] header = Encoding.UTF8.GetBytes($"{props}");
6263

63-
if (compressed)
64-
header = await _compressor.CompressAsync(header);
65-
66-
input = header.Concat(input).ToArray();
64+
#if DEBUG
65+
if (properties.TryGetValue("Key", out key))
66+
{
67+
int length = body.Length;
68+
Debug.WriteLine($"=====Key: {key?.ToString()}=====Length: {length}=====");
69+
}
70+
#endif
6771

6872
if (!compressed)
69-
return await _compressor.CompressAsync(input);
73+
body = await _compressor.CompressAsync(body);
74+
75+
body = header.Concat(Splitter).Concat(body).ToArray();
7076

71-
return input;
77+
return body;
7278
}
7379

7480
private async Task SendAsync(WebSocketTransport transport, WebSocketMessageDescriptor descriptor)
@@ -111,7 +117,7 @@ public async Task ConnectAsync(WebSocket webSocket)
111117
var context = new WebSocketMessageContext();
112118
context.Command = WebSocketCommands.Handshake;
113119
context.Value = connectionId;
114-
context.State = await _initState.GetStateAsync();
120+
context.Header = await _initState.GetStateAsync();
115121
Connections.TryAdd(connectionId, transport);
116122

117123
await SendAsync(connectionId, context);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.Linq;
3+
using static NetCoreStack.WebSockets.Internal.SocketsConstants;
4+
5+
namespace NetCoreStack.WebSockets
6+
{
7+
internal static class ByteExtensions
8+
{
9+
public static Tuple<byte[], byte[]> Split(this byte[] input)
10+
{
11+
if (input == null)
12+
{
13+
throw new ArgumentNullException(nameof(input));
14+
}
15+
16+
var value = Splitter.First();
17+
var index = Array.IndexOf(input, value, 1);
18+
if (index == -1)
19+
{
20+
throw new InvalidOperationException($"Invalid data format! " +
21+
$"Check the splitter pattern exist: \"{Splitter}\"");
22+
}
23+
24+
var header = new ArraySegment<byte>(input, 0, index);
25+
var body = new ArraySegment<byte>(input, (index + 1), input.Length - (index + 1));
26+
27+
return new Tuple<byte[], byte[]>(header.ToArray(), body.ToArray());
28+
}
29+
}
30+
}

src/NetCoreStack.WebSockets/Extensions/WebSocketExtensions.cs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,52 +46,49 @@ public static async Task<WebSocketMessageContext> ToBinaryContextAsync(this WebS
4646
throw new ArgumentNullException(nameof(result));
4747
}
4848

49+
var content = input.Split();
50+
byte[] header = content.Item1;
51+
byte[] body = content.Item2;
52+
4953
var webSocketContext = new WebSocketMessageContext();
50-
bool compression = GZipHelper.IsGZipHeader(input);
51-
byte[] bytes = null;
52-
if (compression)
53-
{
54-
bytes = await compressor.DeCompressAsync(input);
55-
}
56-
else
54+
bool isCompressed = GZipHelper.IsGZipBody(body);
55+
if (isCompressed)
5756
{
58-
bytes = input;
57+
body = await compressor.DeCompressAsync(body);
5958
}
6059

61-
using (var ms = new MemoryStream(bytes))
60+
using (var ms = new MemoryStream(header))
6261
using (var sr = new StreamReader(ms))
6362
{
64-
var content = await sr.ReadToEndAsync();
65-
if (content != null)
63+
var data = await sr.ReadToEndAsync();
64+
if (data != null)
6665
{
67-
string[] parts = content.Split(new string[] { SocketsConstants.Splitter }, StringSplitOptions.None);
68-
if (parts.Length != 2)
69-
{
70-
throw new InvalidOperationException($"Invalid binary data format! " +
71-
$"Check the splitter pattern exist: \"{SocketsConstants.Splitter}\"");
72-
}
73-
74-
webSocketContext.Value = parts.Last();
75-
7666
try
7767
{
78-
webSocketContext.State = JsonConvert.DeserializeObject<Dictionary<string, object>>(parts.First());
68+
webSocketContext.Header = JsonConvert.DeserializeObject<Dictionary<string, object>>(data);
7969
}
8070
catch (Exception ex)
8171
{
82-
webSocketContext.State = new Dictionary<string, object>
72+
webSocketContext.Header = new Dictionary<string, object>
8373
{
8474
["Exception"] = ex.Message,
85-
["Unknown"] = "Unknown binary message"
75+
["Unknown"] = "Unknown binary message!"
8676
};
8777
}
8878
}
79+
}
8980

90-
webSocketContext.Length = input.Length;
91-
webSocketContext.MessageType = WebSocketMessageType.Binary;
92-
webSocketContext.Command = WebSocketCommands.DataSend;
81+
using (var ms = new MemoryStream(body))
82+
using (var sr = new StreamReader(ms))
83+
{
84+
var data = await sr.ReadToEndAsync();
85+
webSocketContext.Value = data;
9386
}
9487

88+
webSocketContext.Length = input.Length;
89+
webSocketContext.MessageType = WebSocketMessageType.Binary;
90+
webSocketContext.Command = WebSocketCommands.DataSend;
91+
9592
return webSocketContext;
9693
}
9794

src/NetCoreStack.WebSockets/Internal/GZipHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public static class GZipHelper
55
/// <summary>
66
/// Checks the first two bytes in a GZIP file, which must be 31 and 139.
77
/// </summary>
8-
public static bool IsGZipHeader(byte[] arr)
8+
public static bool IsGZipBody(byte[] arr)
99
{
1010
return arr.Length >= 2 &&
1111
arr[0] == 31 &&

src/NetCoreStack.WebSockets/Internal/SocketsConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
public class SocketsConstants
44
{
5-
public const string Splitter = "<|>";
5+
public static byte[] Splitter = new byte[] { 0x1f };
66
public const string CompressedKey = "Compressed";
77
public const int ChunkSize = 1024 * 4;
88
}

src/NetCoreStack.WebSockets/WebSocketMessageContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class WebSocketMessageContext
1111

1212
public object Value { get; set; }
1313

14-
public IDictionary<string, object> State { get; set; }
14+
public IDictionary<string, object> Header { get; set; }
1515

1616
public int Length { get; set; }
1717

@@ -30,7 +30,7 @@ public string CommandText
3030

3131
public WebSocketMessageContext()
3232
{
33-
State = new Dictionary<string, object>();
33+
Header = new Dictionary<string, object>();
3434
}
3535
}
3636
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Common.Libs
2+
{
3+
public static class ApplicationVariables
4+
{
5+
public static int CacheReady { get; set; }
6+
}
7+
}

test/Common.Libs/Common.Libs.xproj

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
8+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
9+
<PropertyGroup Label="Globals">
10+
<ProjectGuid>7acfeed0-c1d7-43de-95d7-3d2cefdf6f9f</ProjectGuid>
11+
<RootNamespace>Common.Libs</RootNamespace>
12+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
13+
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
14+
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
15+
</PropertyGroup>
16+
17+
<PropertyGroup>
18+
<SchemaVersion>2.0</SchemaVersion>
19+
</PropertyGroup>
20+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
21+
</Project>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Microsoft.Extensions.Caching.Memory;
2+
using System;
3+
4+
namespace Common.Libs
5+
{
6+
public class InMemoryCacheProvider
7+
{
8+
private readonly IMemoryCache _memoryCache;
9+
10+
public InMemoryCacheProvider(IMemoryCache memoryCache)
11+
{
12+
_memoryCache = memoryCache;
13+
}
14+
15+
public object SetObject(string key, object value, MemoryCacheEntryOptions options)
16+
{
17+
if (string.IsNullOrEmpty(key))
18+
{
19+
throw new ArgumentNullException(nameof(key));
20+
}
21+
22+
if (value == null)
23+
{
24+
throw new ArgumentNullException("Cache value is null");
25+
}
26+
27+
if (options == null)
28+
{
29+
throw new ArgumentNullException(nameof(options));
30+
}
31+
32+
var entryOptions = new MemoryCacheEntryOptions
33+
{
34+
AbsoluteExpiration = options.AbsoluteExpiration,
35+
Priority = options.Priority
36+
};
37+
38+
_memoryCache.Set(key, value, entryOptions);
39+
return value;
40+
}
41+
42+
public object GetObject(string key)
43+
{
44+
return _memoryCache.Get(key);
45+
}
46+
47+
public T GetObject<T>(string key)
48+
{
49+
return (T)_memoryCache.Get(key);
50+
}
51+
52+
public void Remove(string key)
53+
{
54+
_memoryCache.Remove(key);
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)