diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..14286fa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +# EditorConfig: http://EditorConfig.org +# VS extension: https://visualstudiogallery.msdn.microsoft.com/c8bccfe2-650c-4b42-bc5c-845e21f96328 + +# tab indentation +[*.cs] +indent_style = tab +indent_size = 4 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..12c63a4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: csharp +mono: none +dotnet: 2.0.0 +dist: trusty + +addons: + apt: + sources: + - sourceline: 'deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main' + key_url: 'https://packages.microsoft.com/keys/microsoft.asc' + packages: + - dotnet-hostfxr-1.0.1 + - dotnet-sharedframework-microsoft.netcore.app-1.0.5 + +script: + - dotnet restore src + - dotnet build src + - dotnet test src/MessageWire.Tests + diff --git a/README.md b/README.md index aec41bf..3d6e6a0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # MessageWire + +[![Appveyor build status](https://ci.appveyor.com/api/projects/status/3pctff9644pdxx3i?svg=true)](https://ci.appveyor.com/project/yallie/messagewire) +[![Travis-CI build status](https://travis-ci.org/yallie/MessageWire.svg?branch=master)](https://travis-ci.org/yallie/MessageWire) +[![MessageWire NuGet version](https://img.shields.io/nuget/v/MessageWire.svg)](https://www.nuget.org/packages/MessageWire/) + MessageWire is a Secure Remote Password Protocol v6 implementation, a kind of zero Knowledge proof, that enables secure authentication and encryption without passing the actual identity key (password) or any other knowledge required to crack the encryption for messages exchanged between client (dealer socket) and server (router socket) using the NetMQ library, a .NET implementation of ZeroMQ. Get the [NuGet package](https://www.nuget.org/packages/MessageWire). diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..9ddc4f5 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,10 @@ +version: 1.0.{build} +image: Visual Studio 2017 +install: +- cmd: appveyor downloadfile https://dist.nuget.org/win-x86-commandline/v4.3.0/nuget.exe +before_build: +- cmd: nuget restore src +build: + verbosity: minimal +test_script: +- cmd: dotnet test src/MessageWire.Tests diff --git a/src/src/MessageWire.Tests/MessageWire.Tests.csproj b/src/MessageWire.Tests/MessageWire.Tests.csproj similarity index 69% rename from src/src/MessageWire.Tests/MessageWire.Tests.csproj rename to src/MessageWire.Tests/MessageWire.Tests.csproj index 82719c3..0c5f59d 100644 --- a/src/src/MessageWire.Tests/MessageWire.Tests.csproj +++ b/src/MessageWire.Tests/MessageWire.Tests.csproj @@ -1,12 +1,10 @@  - netcoreapp1.0 + netcoreapp2.0 MessageWire.Tests MessageWire.Tests true - 1.1.1 - $(PackageTargetFallback);dnxcore50 false false false @@ -17,9 +15,9 @@ - - - + + + diff --git a/src/src/MessageWire.Tests/Properties/AssemblyInfo.cs b/src/MessageWire.Tests/Properties/AssemblyInfo.cs similarity index 100% rename from src/src/MessageWire.Tests/Properties/AssemblyInfo.cs rename to src/MessageWire.Tests/Properties/AssemblyInfo.cs diff --git a/src/MessageWire.Tests/RouterDealerTests.cs b/src/MessageWire.Tests/RouterDealerTests.cs new file mode 100644 index 0000000..18d85a1 --- /dev/null +++ b/src/MessageWire.Tests/RouterDealerTests.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using Xunit; +using System.Threading; + +namespace MessageWire.Tests +{ + public class RouterDealerTests : IDisposable + { + public RouterDealerTests() + { + Wire.Linger = new TimeSpan(0, 0, 0, 1); + } + + void IDisposable.Dispose() + { + Wire.Cleanup(); + } + + [Fact] + public void BasicSendReceiveTest() + { + var serverReceived = false; + var clientReceived = false; + var connString = "tcp://127.0.0.1:5800"; + + using (IHost server = new Host(connString)) + { + server.MessageReceived += (s, e) => + { + Assert.Equal("Hello, I'm the client.", e.Message.Frames[0].ConvertToString()); + Assert.Equal("This is my second line.", e.Message.Frames[1].ConvertToString()); + + var replyData = new List(); + replyData.Add("Hello, I'm the server. You sent.".ConvertToBytes()); + replyData.AddRange(e.Message.Frames); + server.Send(e.Message.ClientId, replyData); + serverReceived = true; + Assert.True(e.Message.Frames.Count == 2, "Server received message did not have 2 frames."); + Assert.True(replyData.Count == 3, "Server message did not have 3 frames."); + }; + + using (IClient client = new Client(connString)) + { + client.MessageReceived += (s, e) => + { + clientReceived = true; + Assert.True(e.Message.Frames.Count == 3, "Received message did not have 3 frames."); + Assert.Equal("Hello, I'm the server. You sent.", e.Message.Frames[0].ConvertToString()); + Assert.Equal("Hello, I'm the client.", e.Message.Frames[1].ConvertToString()); + Assert.Equal("This is my second line.", e.Message.Frames[2].ConvertToString()); + }; + + var clientMessageData = new List(); + clientMessageData.Add("Hello, I'm the client.".ConvertToBytes()); + clientMessageData.Add("This is my second line.".ConvertToBytes()); + client.Send(clientMessageData); + + var count = 0; + while (count < 20 && (!clientReceived || !serverReceived)) + { + Thread.Sleep(20); + count++; + } + Assert.True(count < 100, "Test took too long."); + Assert.True(serverReceived, "Server failed to receive and send."); + Assert.True(clientReceived, "Client failed to receive."); + } + } + } + + [Fact] + public void EncryptedSendReceiveTest() + { + var serverReceived = false; + var clientReceived = false; + var connString = "tcp://127.0.0.1:5800"; + + using (IHost server = new Host(connString, new TestZkRepository())) + { + server.MessageReceived += (s, e) => + { + Assert.Equal("Hello, I'm the client.", e.Message.Frames[0].ConvertToString()); + Assert.Equal("This is my second line.", e.Message.Frames[1].ConvertToString()); + + var replyData = new List(); + replyData.Add("Hello, I'm the server. You sent.".ConvertToBytes()); + replyData.AddRange(e.Message.Frames); + server.Send(e.Message.ClientId, replyData); + serverReceived = true; + Assert.True(e.Message.Frames.Count == 2, "Server received message did not have 2 frames."); + Assert.True(replyData.Count == 3, "Server message did not have 3 frames."); + }; + + using (IClient client = new Client(connString, "testid", "....++++....")) + { + var established = false; + client.EcryptionProtocolEstablished += (s, e) => + { + established = true; + }; + + client.EcryptionProtocolFailed += (s, e) => + { + Assert.True(false, "Protocol failed."); + }; + + client.MessageReceived += (s, e) => + { + clientReceived = true; + Assert.True(e.Message.Frames.Count == 3, "Received message did not have 3 frames."); + Assert.Equal("Hello, I'm the server. You sent.", e.Message.Frames[0].ConvertToString()); + Assert.Equal("Hello, I'm the client.", e.Message.Frames[1].ConvertToString()); + Assert.Equal("This is my second line.", e.Message.Frames[2].ConvertToString()); + }; + + client.SecureConnection(blockUntilComplete: false); + + var count = 0; + while (count < 20000 && !established) + { + Thread.Sleep(20); + count++; + } + Assert.True(count < 20000, "SecureConnection took too long."); + Assert.True(established, "SecureConnection not established."); + + var clientMessageData = new List(); + clientMessageData.Add("Hello, I'm the client.".ConvertToBytes()); + clientMessageData.Add("This is my second line.".ConvertToBytes()); + client.Send(clientMessageData); + + count = 0; + while (count < 20 && (!established || !clientReceived || !serverReceived)) + { + Thread.Sleep(20); + count++; + } + Assert.True(count < 20, "Test took too long."); + Assert.True(serverReceived, "Server failed to receive and send."); + Assert.True(clientReceived, "Client failed to receive."); + } + } + } + + [Fact] + public void HeartBeatTest() + { + var serverReceived = false; + var clientReceived = false; + var connString = "tcp://127.0.0.1:5800"; + + using (IHost server = new Host(connString, new TestZkRepository())) + { + server.MessageReceived += (s, e) => + { + Assert.Equal("Hello, I'm the client.", e.Message.Frames[0].ConvertToString()); + Assert.Equal("This is my second line.", e.Message.Frames[1].ConvertToString()); + + var replyData = new List(); + replyData.Add("Hello, I'm the server. You sent.".ConvertToBytes()); + replyData.AddRange(e.Message.Frames); + server.Send(e.Message.ClientId, replyData); + serverReceived = true; + Assert.True(e.Message.Frames.Count == 2, "Server received message did not have 2 frames."); + Assert.True(replyData.Count == 3, "Server message did not have 3 frames."); + }; + + using (IClient client = new Client(connString, "testid", "....++++....", + heartBeatIntervalMs: 1000, maxSkippedHeartBeatReplies: 4)) + { + var established = false; + client.EcryptionProtocolEstablished += (s, e) => + { + established = true; + }; + + client.EcryptionProtocolFailed += (s, e) => + { + Assert.True(false, "Protocol failed."); + }; + + client.MessageReceived += (s, e) => + { + clientReceived = true; + Assert.True(e.Message.Frames.Count == 3, "Received message did not have 3 frames."); + Assert.Equal("Hello, I'm the server. You sent.", e.Message.Frames[0].ConvertToString()); + Assert.Equal("Hello, I'm the client.", e.Message.Frames[1].ConvertToString()); + Assert.Equal("This is my second line.", e.Message.Frames[2].ConvertToString()); + }; + + client.SecureConnection(blockUntilComplete: false); + + var count = 0; + while (count < 200 && !established) + { + Thread.Sleep(20); + count++; + } + Assert.True(count < 200, "SecureConnection took too long."); + Assert.True(established, "SecureConnection not established."); + + var clientMessageData = new List(); + clientMessageData.Add("Hello, I'm the client.".ConvertToBytes()); + clientMessageData.Add("This is my second line.".ConvertToBytes()); + client.Send(clientMessageData); + + count = 0; + while (count < 20 && (!established || !clientReceived || !serverReceived)) + { + Thread.Sleep(20); + count++; + } + Assert.True(count < 20, "Test took too long."); + Assert.True(serverReceived, "Server failed to receive and send."); + Assert.True(clientReceived, "Client failed to receive."); + + count = 0; + while (count < 200 && client.HeartBeatsSentCount < 2 && client.HeartBeatsReceivedCount < 2) + { + Thread.Sleep(500); + count++; + } + + Assert.True(count < 200, "Test took too long."); + var serverKeys = server.GetCurrentSessionKeys(); + Assert.Equal(1, serverKeys.Length); + var session = server.GetSession(serverKeys[0]); + Assert.NotNull(session); + Assert.True("testid" == session.ClientIdentity, "Client identity does not match."); + Assert.True(client.ClientId == session.ClientId, "ClientId does not match."); + Assert.True(session.HeartBeatsReceivedCount > 1, "Server failed to receive heartbeats."); + } + } + } + } +} diff --git a/src/src/MessageWire.Tests/TestZkRepository.cs b/src/MessageWire.Tests/TestZkRepository.cs similarity index 100% rename from src/src/MessageWire.Tests/TestZkRepository.cs rename to src/MessageWire.Tests/TestZkRepository.cs diff --git a/src/MessageWire.sln b/src/MessageWire.sln index c411437..f5fbb88 100644 --- a/src/MessageWire.sln +++ b/src/MessageWire.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26730.8 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B2B7F7CF-457D-4EBE-B34C-7AF0B49C0B54}" EndProject @@ -12,9 +12,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docs", ".docs", "{2A8A94FC ..\README.md = ..\README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageWire", "src\MessageWire\MessageWire.csproj", "{42CEE435-2735-4E99-AF3F-FBB18E55892E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageWire", "MessageWire\MessageWire.csproj", "{42CEE435-2735-4E99-AF3F-FBB18E55892E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageWire.Tests", "src\MessageWire.Tests\MessageWire.Tests.csproj", "{FADEA2FC-B1BC-4218-A64C-C795FE4EDB79}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageWire.Tests", "MessageWire.Tests\MessageWire.Tests.csproj", "{FADEA2FC-B1BC-4218-A64C-C795FE4EDB79}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -38,4 +38,7 @@ Global {42CEE435-2735-4E99-AF3F-FBB18E55892E} = {B2B7F7CF-457D-4EBE-B34C-7AF0B49C0B54} {FADEA2FC-B1BC-4218-A64C-C795FE4EDB79} = {B2B7F7CF-457D-4EBE-B34C-7AF0B49C0B54} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4D78F946-1236-4D86-9C64-1353CC6D049E} + EndGlobalSection EndGlobal diff --git a/src/src/MessageWire/ByteArrayExtensions.cs b/src/MessageWire/ByteArrayExtensions.cs similarity index 100% rename from src/src/MessageWire/ByteArrayExtensions.cs rename to src/MessageWire/ByteArrayExtensions.cs diff --git a/src/src/MessageWire/Client.cs b/src/MessageWire/Client.cs similarity index 86% rename from src/src/MessageWire/Client.cs rename to src/MessageWire/Client.cs index 6d01ac6..6cbad2c 100644 --- a/src/src/MessageWire/Client.cs +++ b/src/MessageWire/Client.cs @@ -1,26 +1,25 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MessageWire - https://github.com/tylerjensen/MessageWire - * + * * The MIT License (MIT) * Copyright (C) 2016-2017 Tyler Jensen - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using MessageWire.Logging; using MessageWire.SecureRemote; @@ -57,20 +56,19 @@ public class Client : IClient, IDisposable /// Client constructor. /// /// Valid NetMQ client socket connection string. - /// Client identifier passed to the server in Zero Knowledge authentication. - /// Null for unsecured hosts. - /// Secret key used by NOT passed to the server in Zero Knowledge authentication - /// but used in memory to validate authentication of the server. Null for - /// unsecured hosts + /// Client identifier passed to the server in Zero Knowledge authentication. + /// Null for unsecured hosts. + /// Secret key used by NOT passed to the server in Zero Knowledge authentication + /// but used in memory to validate authentication of the server. Null for unsecured hosts /// ILogger implementation for logging operations. Null is replaced with NullLogger. /// IStats implementation for logging perf metrics. Null is replaced with NullStats. - /// Number of milliseconds between client sending heartbeat message to the server. + /// Number of milliseconds between client sending heartbeat message to the server. /// Default 30,000 (30 seconds). Min is 1000 (1 second) and max is 600,000 (10 mins). - /// Maximum heartbeat intervals skipped without a heartbeat reply + /// Maximum heartbeat intervals skipped without a heartbeat reply /// from the server before the client begins throwing on Send and returns false for the IsHostAlive property. /// Default is 3. Min is 1 and max is 10. - public Client(string connectionString, string identity = null, string identityKey = null, - ILog logger = null, IStats stats = null, + public Client(string connectionString, string identity = null, string identityKey = null, + ILog logger = null, IStats stats = null, int heartBeatIntervalMs = 30000, int maxSkippedHeartBeatReplies = 3) { _identity = identity; @@ -79,8 +77,8 @@ public Client(string connectionString, string identity = null, string identityKe _logger = logger ?? new NullLogger(); _stats = stats ?? new NullStats(); - _heartBeatMs = (heartBeatIntervalMs < 1000) - ? 1000 + _heartBeatMs = (heartBeatIntervalMs < 1000) + ? 1000 : (heartBeatIntervalMs > 600000) ? 600000 : heartBeatIntervalMs; _maxSkippedHeartBeatReplies = (maxSkippedHeartBeatReplies < 1) @@ -123,12 +121,12 @@ private void HeartBeatTimer_Elapsed(object sender, NetMQTimerEventArgs e) //check for last heartbeat from server, set to throw on new send if exceeds certain threshold if (null !=_session && null != _session.Crypto) { - if ((DateTime.UtcNow - _session.LastHeartBeat).TotalMilliseconds + if ((DateTime.UtcNow - _session.LastHeartBeat).TotalMilliseconds > _heartBeatMs * _maxSkippedHeartBeatReplies) { _throwOnSend = true; //do not allow send _hostDead = true; - _logger.Debug("Heartbeat from server skipped {0} time. Host is dead.", + _logger.Debug("Heartbeat from server skipped {0} time. Host is dead.", _maxSkippedHeartBeatReplies); } else @@ -189,9 +187,9 @@ public DateTime? LastHeartBeatReceivedFromHost { private EventHandler _ecryptionProtocolFailedEvent; /// - /// This event occurs when a message has been received. + /// This event occurs when a message has been received. /// - /// This handler is thread safe occuring on a thread other + /// This handler is thread safe occuring on a thread other /// than the thread sending and receiving messages over the wire. public event EventHandler MessageReceived { add { @@ -203,9 +201,9 @@ public event EventHandler MessageReceived { } /// - /// This event occurs when an invalid protocol message has been received. + /// This event occurs when an invalid protocol message has been received. /// - /// This handler is thread safe occuring on a thread other + /// This handler is thread safe occuring on a thread other /// than the thread sending and receiving messages over the wire. public event EventHandler InvalidMessageReceived { add { @@ -218,9 +216,9 @@ public event EventHandler InvalidMessageReceived { /// /// This event occurs when the client has established a secure connection and - /// messages may be sent without throwing an operation cancelled exception. + /// messages may be sent without throwing an operation cancelled exception. /// - /// This handler is thread safe occuring on a thread other + /// This handler is thread safe occuring on a thread other /// than the thread sending and receiving messages over the wire. public event EventHandler EcryptionProtocolEstablished { add { @@ -233,9 +231,9 @@ public event EventHandler EcryptionProtocolEstablished { /// /// This event occurs when the client failes to establish a secure connection and - /// messages may be sent without throwing an operation cancelled exception. + /// messages may be sent without throwing an operation cancelled exception. /// - /// This handler is thread safe occuring on a thread other + /// This handler is thread safe occuring on a thread other /// than the thread sending and receiving messages over the wire. public event EventHandler EcryptionProtocolFailed { add { @@ -264,68 +262,6 @@ public void Send(List frames) _sendQueue.Enqueue(frames); } - public void Send(IEnumerable frames) - { - Send(frames.ToList()); - } - - public void Send(byte[] frame) - { - Send(new[] { frame }); - } - - public void Send(List frames) - { - Send(frames, Encoding.UTF8); - } - - public void Send(IEnumerable frames) - { - Send(frames, Encoding.UTF8); - } - - public void Send(params string[] frames) - { - Send(Encoding.UTF8, frames); - } - - public void Send(string frame) - { - Send(frame, Encoding.UTF8); - } - - public void Send(List frames, Encoding encoding) - { - Send((from n in frames - select n == null - ? (byte[])null - : encoding.GetBytes(n)).ToList()); - } - - public void Send(IEnumerable frames, Encoding encoding) - { - Send((from n in frames - select n == null - ? (byte[])null - : encoding.GetBytes(n)).ToList()); - } - - public void Send(Encoding encoding, params string[] frames) - { - Send((from n in frames - select n == null - ? (byte[])null - : encoding.GetBytes(n)).ToList()); - } - - public void Send(string frame, Encoding encoding) - { - Send(new[] { frame == null - ? (byte[])null - : encoding.GetBytes(frame) }); - } - - //Executes on same poller thread as dealer socket, so we can send directly private void SendQueue_ReceiveReady(object sender, NetMQQueueEventArgs> e) { @@ -405,7 +341,7 @@ private void ProcessRegularMessage(List frames) frames[i] = _session.Crypto.Decrypt(frames[i]); } } - _logger.Info("Message received Frame count {0}. Total length {1}.", + _logger.Info("Message received Frame count {0}. Total length {1}.", frames.Count, frames.Select(x => x.Length).Sum()); _receivedEvent?.Invoke(this, new MessageEventArgs { @@ -460,13 +396,13 @@ private void ProcessProtocolExchange(List frames) } else { - error = "Protocol proof creation failed."; + error = "Protocol proof creation failed."; _logger.Fatal(error); _ecryptionProtocolFailedEvent?.Invoke(this, new ProtocolFailureEventArgs { Message = error }); } } else if (frames[0][2] == MessageHeader.SM2) - + { if (_session.ProcessProofReply(frames)) //complete proof { @@ -507,7 +443,6 @@ private bool IsHandshakeReply(List frames) && frames[0][3] == MessageHeader.BEL); } - #region IDisposable Members private bool _disposed = false; diff --git a/src/src/MessageWire/Host.cs b/src/MessageWire/Host.cs similarity index 95% rename from src/src/MessageWire/Host.cs rename to src/MessageWire/Host.cs index 68ed21c..c5a857f 100644 --- a/src/src/MessageWire/Host.cs +++ b/src/MessageWire/Host.cs @@ -1,19 +1,19 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MessageWire - https://github.com/tylerjensen/MessageWire - * + * * The MIT License (MIT) * Copyright (C) 2016-2017 Tyler Jensen - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -55,10 +55,10 @@ public class Host : IHost, IDisposable /// External authentication repository. Null creates host with no encryption. /// ILogger implementation for logging operations. Null is replaced with NullLogger. /// IStats implementation for logging perf metrics. Null is replaced with NullStats. - /// Session timout check interval. If no heartbeats or messages - /// received on a given session in this period of time, the session will be removed from memory + /// Session timout check interval. If no heartbeats or messages + /// received on a given session in this period of time, the session will be removed from memory /// and futher attempts from the client will fail. Default is 20 minutes. Min is 1 and Max is 3600. - public Host(string connectionString, IKeyRepository authRepository = null, + public Host(string connectionString, IKeyRepository authRepository = null, ILog logger = null, IStats stats = null, int sessionTimeoutMins = 20) { if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullException("Connection string cannot be null.", nameof(connectionString)); @@ -105,8 +105,7 @@ public Host(string connectionString, IKeyRepository authRepository = null, private EventHandler _sentFailureEvent; private EventHandler _receivedEvent; - private EventHandler _sentEvent; - private EventHandler _zkClientSessionEstablishedEvent; + private EventHandler _secureClientSessionEstablishedEvent; public Guid[] GetCurrentSessionKeys() { @@ -130,7 +129,7 @@ public Session GetSession(Guid key) } /// - /// This event occurs when a message has been received. + /// This event occurs when a message has been received. /// /// This handler will run on a different thread than the socket poller and /// blocking on this thread will not block sending and receiving. @@ -163,12 +162,12 @@ public event EventHandler MessageSentFailure { /// /// This handler will run on a different thread than the socket poller and /// blocking on this thread will not block sending and receiving. - public event EventHandler ZkClientSessionEstablishedEvent { + public event EventHandler SecureClientSessionEstablished { add { - _zkClientSessionEstablishedEvent += value; + _secureClientSessionEstablishedEvent += value; } remove { - _zkClientSessionEstablishedEvent -= value; + _secureClientSessionEstablishedEvent -= value; } } @@ -402,7 +401,7 @@ private void ProcessProtocolExchange(Message message, HostSession session) //if second reply and success, raise event, new client session? if (responseFrames[0].IsEqualTo(MessageHeader.ProofResponseSuccess)) { - _zkClientSessionEstablishedEvent?.Invoke(this, new MessageEventArgs + _secureClientSessionEstablishedEvent?.Invoke(this, new MessageEventArgs { Message = new Message { @@ -443,7 +442,6 @@ private bool IsHandshakeRequest(List frames) && frames[0][3] == MessageHeader.BEL); } - #region IDisposable Members private bool _disposed = false; diff --git a/src/MessageWire/IClient.cs b/src/MessageWire/IClient.cs new file mode 100644 index 0000000..0561278 --- /dev/null +++ b/src/MessageWire/IClient.cs @@ -0,0 +1,42 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MessageWire - https://github.com/tylerjensen/MessageWire + * + * The MIT License (MIT) + * Copyright (C) 2016-2017 Tyler Jensen + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +using System; +using System.Collections.Generic; + +namespace MessageWire +{ + public interface IClient : IDisposable + { + void Send(List frames); + event EventHandler MessageReceived; + event EventHandler InvalidMessageReceived; + + bool SecureConnection(bool blockUntilComplete = true, int timeoutMs = 500); + event EventHandler EcryptionProtocolEstablished; + event EventHandler EcryptionProtocolFailed; + + bool CanSend { get; } + Guid ClientId { get; } + int HeartBeatsReceivedCount { get; } + int HeartBeatsSentCount { get; } + bool IsHostAlive { get; } + DateTime? LastHeartBeatReceivedFromHost { get; } + } +} \ No newline at end of file diff --git a/src/MessageWire/IClientExtensions.cs b/src/MessageWire/IClientExtensions.cs new file mode 100644 index 0000000..2473ce0 --- /dev/null +++ b/src/MessageWire/IClientExtensions.cs @@ -0,0 +1,78 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MessageWire - https://github.com/tylerjensen/MessageWire + * + * The MIT License (MIT) + * Copyright (C) 2016-2017 Tyler Jensen + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MessageWire +{ + public static class IClientExtensions + { + public static void Send(this IClient self, IEnumerable frames) + { + self.Send(frames.ToList()); + } + + public static void Send(this IClient self, byte[] frame) + { + self.Send(new List { frame }); + } + + public static void Send(this IClient self, List frames) + { + self.Send(frames.AsEnumerable(), Encoding.UTF8); + } + + public static void Send(this IClient self, IEnumerable frames) + { + self.Send(frames, Encoding.UTF8); + } + + public static void Send(this IClient self, params string[] frames) + { + self.Send(frames.AsEnumerable(), Encoding.UTF8); + } + + public static void Send(this IClient self, string frame) + { + self.Send(Encoding.UTF8, frame); + } + + public static void Send(this IClient self, List frames, Encoding encoding) + { + self.Send(frames.AsEnumerable(), encoding); + } + + public static void Send(this IClient self, IEnumerable frames, Encoding encoding) + { + self.Send(frames.Select(n => n == null ? null : encoding.GetBytes(n)).ToList()); + } + + public static void Send(this IClient self, Encoding encoding, params string[] frames) + { + self.Send(frames.AsEnumerable(), encoding); + } + + public static void Send(this IClient self, string frame, Encoding encoding) + { + self.Send(encoding, frame); + } + } +} diff --git a/src/MessageWire/IHost.cs b/src/MessageWire/IHost.cs new file mode 100644 index 0000000..5b13eda --- /dev/null +++ b/src/MessageWire/IHost.cs @@ -0,0 +1,36 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MessageWire - https://github.com/tylerjensen/MessageWire + * + * The MIT License (MIT) + * Copyright (C) 2016-2017 Tyler Jensen + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +using System; +using System.Collections.Generic; + +namespace MessageWire +{ + public interface IHost : IDisposable + { + void Send(Guid clientId, List frames); + event EventHandler MessageReceived; + event EventHandler MessageSentFailure; + event EventHandler SecureClientSessionEstablished; + + Session GetSession(Guid key); + Session[] GetCurrentSessions(); + Guid[] GetCurrentSessionKeys(); + } +} \ No newline at end of file diff --git a/src/MessageWire/IHostExtensions.cs b/src/MessageWire/IHostExtensions.cs new file mode 100644 index 0000000..0b2f709 --- /dev/null +++ b/src/MessageWire/IHostExtensions.cs @@ -0,0 +1,79 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MessageWire - https://github.com/tylerjensen/MessageWire + * + * The MIT License (MIT) + * Copyright (C) 2016-2017 Tyler Jensen + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MessageWire +{ + public static class IHostExtensions + { + public static void Send(this IHost self, Guid clientId, IEnumerable frames) + { + self.Send(clientId, frames.ToList()); + } + + public static void Send(this IHost self, Guid clientId, byte[] frame) + { + self.Send(clientId, new List { frame }); + } + + public static void Send(this IHost self, Guid clientId, List frames) + { + self.Send(clientId, frames.AsEnumerable(), Encoding.UTF8); + } + + public static void Send(this IHost self, Guid clientId, IEnumerable frames) + { + self.Send(clientId, frames, Encoding.UTF8); + } + + public static void Send(this IHost self, Guid clientId, params string[] frames) + { + self.Send(clientId, frames.AsEnumerable(), Encoding.UTF8); + } + + public static void Send(this IHost self, Guid clientId, string frame) + { + self.Send(clientId, Encoding.UTF8, frame); + } + + public static void Send(this IHost self, Guid clientId, List frames, Encoding encoding) + { + self.Send(clientId, frames.AsEnumerable(), encoding); + } + + public static void Send(this IHost self, Guid clientId, IEnumerable frames, Encoding encoding) + { + self.Send(clientId, frames.Select(n => n == null ? null : encoding.GetBytes(n)).ToList()); + } + + public static void Send(this IHost self, Guid clientId, Encoding encoding, params string[] frames) + { + self.Send(clientId, frames.AsEnumerable(), encoding); + } + + public static void Send(this IHost self, Guid clientId, string frame, Encoding encoding) + { + self.Send(clientId, encoding, frame); + } + } +} diff --git a/src/src/MessageWire/Logging/ILog.cs b/src/MessageWire/Logging/ILog.cs similarity index 100% rename from src/src/MessageWire/Logging/ILog.cs rename to src/MessageWire/Logging/ILog.cs diff --git a/src/src/MessageWire/Logging/IStats.cs b/src/MessageWire/Logging/IStats.cs similarity index 100% rename from src/src/MessageWire/Logging/IStats.cs rename to src/MessageWire/Logging/IStats.cs diff --git a/src/src/MessageWire/Logging/LogLevel.cs b/src/MessageWire/Logging/LogLevel.cs similarity index 100% rename from src/src/MessageWire/Logging/LogLevel.cs rename to src/MessageWire/Logging/LogLevel.cs diff --git a/src/src/MessageWire/Logging/LogOptions.cs b/src/MessageWire/Logging/LogOptions.cs similarity index 100% rename from src/src/MessageWire/Logging/LogOptions.cs rename to src/MessageWire/Logging/LogOptions.cs diff --git a/src/src/MessageWire/Logging/LogRollOptions.cs b/src/MessageWire/Logging/LogRollOptions.cs similarity index 100% rename from src/src/MessageWire/Logging/LogRollOptions.cs rename to src/MessageWire/Logging/LogRollOptions.cs diff --git a/src/src/MessageWire/Logging/Logger.cs b/src/MessageWire/Logging/Logger.cs similarity index 100% rename from src/src/MessageWire/Logging/Logger.cs rename to src/MessageWire/Logging/Logger.cs diff --git a/src/src/MessageWire/Logging/LoggerBase.cs b/src/MessageWire/Logging/LoggerBase.cs similarity index 93% rename from src/src/MessageWire/Logging/LoggerBase.cs rename to src/MessageWire/Logging/LoggerBase.cs index da8691d..e3111df 100644 --- a/src/src/MessageWire/Logging/LoggerBase.cs +++ b/src/MessageWire/Logging/LoggerBase.cs @@ -1,19 +1,19 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MessageWire - https://github.com/tylerjensen/MessageWire - * + * * The MIT License (MIT) * Copyright (C) 2016-2017 Tyler Jensen - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -115,7 +115,7 @@ private void WriteToFile(string[] lines) File.AppendAllText(fileName, string.Join("\r\n", lines)); #endif } - catch (Exception e) + catch { //todo ? } diff --git a/src/src/MessageWire/Logging/NullLogger.cs b/src/MessageWire/Logging/NullLogger.cs similarity index 100% rename from src/src/MessageWire/Logging/NullLogger.cs rename to src/MessageWire/Logging/NullLogger.cs diff --git a/src/src/MessageWire/Logging/NullStats.cs b/src/MessageWire/Logging/NullStats.cs similarity index 100% rename from src/src/MessageWire/Logging/NullStats.cs rename to src/MessageWire/Logging/NullStats.cs diff --git a/src/src/MessageWire/Logging/Stats.cs b/src/MessageWire/Logging/Stats.cs similarity index 100% rename from src/src/MessageWire/Logging/Stats.cs rename to src/MessageWire/Logging/Stats.cs diff --git a/src/src/MessageWire/Logging/StatsBag.cs b/src/MessageWire/Logging/StatsBag.cs similarity index 100% rename from src/src/MessageWire/Logging/StatsBag.cs rename to src/MessageWire/Logging/StatsBag.cs diff --git a/src/MessageWire/Message.cs b/src/MessageWire/Message.cs new file mode 100644 index 0000000..783cd38 --- /dev/null +++ b/src/MessageWire/Message.cs @@ -0,0 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MessageWire - https://github.com/tylerjensen/MessageWire + * + * The MIT License (MIT) + * Copyright (C) 2016-2017 Tyler Jensen + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +using System; +using System.Collections.Generic; + +namespace MessageWire +{ + public class Message + { + public Guid ClientId { get; set; } + public List Frames { get; set; } + } +} diff --git a/src/MessageWire/MessageExtensions.cs b/src/MessageWire/MessageExtensions.cs new file mode 100644 index 0000000..451e63e --- /dev/null +++ b/src/MessageWire/MessageExtensions.cs @@ -0,0 +1,132 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MessageWire - https://github.com/tylerjensen/MessageWire + * + * The MIT License (MIT) + * Copyright (C) 2016-2017 Tyler Jensen + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +using NetMQ; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MessageWire +{ + internal static class MessageExtensions + { + public static List ConvertToStrings(this Message msg, bool convertEncodingErrorToNull = true) + { + return msg.ConvertToStrings(Encoding.UTF8, convertEncodingErrorToNull); + } + + public static List ConvertToStrings(this Message msg, Encoding encoding, bool convertEncodingErrorToNull = true) + { + var list = new List(); + if (msg.Frames == null || msg.Frames.Count == 0) return list; + foreach (var frame in msg.Frames) + { + try + { + if (frame == null) + list.Add((string)null); + else + list.Add(encoding.GetString(frame)); + } + catch (Exception e) + { + if (convertEncodingErrorToNull) + list.Add((string)null); + else + list.Add($"##EncodingError-Bytes({frame.Length})-{e.Message}-##"); + } + } + return list; + } + + public static Message ToMessageWithoutClientFrame(this NetMQMessage msg, Guid clientId) + { + if (msg == null || msg.FrameCount == 0) return null; + List frames = new List(); + if (msg.FrameCount > 0) + { + frames = (from n in msg where !n.IsEmpty select n.Buffer).ToList(); + } + return new Message + { + ClientId = clientId, + Frames = frames + }; + } + + public static Message ToMessageWithClientFrame(this NetMQMessage msg) + { + if (msg == null || msg.FrameCount == 0) return null; + if (msg[0].BufferSize != 16) return null; //must have a Guid id + var clientId = new Guid(msg[0].Buffer); + List frames = new List(); + if (msg.FrameCount > 1) + { + frames = (from n in msg where !n.IsEmpty select n.Buffer).Skip(1).ToList(); + } + return new Message + { + ClientId = clientId, + Frames = frames + }; + } + + public static NetMQMessage ToNetMQMessage(this Message msg) + { + var message = new NetMQMessage(); + message.Append(msg.ClientId.ToByteArray()); + message.AppendEmptyFrame(); + if (null != msg.Frames) + { + foreach (var frame in msg.Frames) + { + message.Append(frame); + } + } + else + { + message.AppendEmptyFrame(); + } + return message; + } + } + + public class ProtocolFailureEventArgs : EventArgs + { + public string Message { get; set; } + } + + public class MessageEventArgs : EventArgs + { + public Message Message { get; set; } + } + + public class MessageEventFailureArgs : EventArgs + { + public MessageFailure Failure { get; set; } + } + + public class MessageFailure + { + public string ErrorMessage { get; set; } + public string ErrorCode { get; set; } + public Message Message { get; set; } + } +} diff --git a/src/src/MessageWire/MessageWire.csproj b/src/MessageWire/MessageWire.csproj similarity index 85% rename from src/src/MessageWire/MessageWire.csproj rename to src/MessageWire/MessageWire.csproj index cc9d055..9c14883 100644 --- a/src/src/MessageWire/MessageWire.csproj +++ b/src/MessageWire/MessageWire.csproj @@ -5,7 +5,7 @@ MessageWire 1.1.1 Tyler Jensen - netstandard1.3 + netstandard2.0 MessageWire MessageWire TCP;Messaging;Zero-Knowledge-Proof;Secure-Remote-Password-Protocol @@ -13,8 +13,6 @@ http://tsjensen.com/blog/image.axd?picture=MessageWireLogo.png https://github.com/tylerjensen/MessageWire https://opensource.org/licenses/MIT - 1.6.1 - $(PackageTargetFallback);dnxcore50 false false false diff --git a/src/src/MessageWire/Properties/AssemblyInfo.cs b/src/MessageWire/Properties/AssemblyInfo.cs similarity index 100% rename from src/src/MessageWire/Properties/AssemblyInfo.cs rename to src/MessageWire/Properties/AssemblyInfo.cs diff --git a/src/src/MessageWire/SecureRemote/BigInteger.cs b/src/MessageWire/SecureRemote/BigInteger.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/BigInteger.cs rename to src/MessageWire/SecureRemote/BigInteger.cs diff --git a/src/src/MessageWire/SecureRemote/ClientSession.cs b/src/MessageWire/SecureRemote/ClientSession.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/ClientSession.cs rename to src/MessageWire/SecureRemote/ClientSession.cs diff --git a/src/src/MessageWire/SecureRemote/Crypto.cs b/src/MessageWire/SecureRemote/Crypto.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/Crypto.cs rename to src/MessageWire/SecureRemote/Crypto.cs diff --git a/src/src/MessageWire/SecureRemote/HostSession.cs b/src/MessageWire/SecureRemote/HostSession.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/HostSession.cs rename to src/MessageWire/SecureRemote/HostSession.cs diff --git a/src/src/MessageWire/SecureRemote/IKeyRepository.cs b/src/MessageWire/SecureRemote/IKeyRepository.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/IKeyRepository.cs rename to src/MessageWire/SecureRemote/IKeyRepository.cs diff --git a/src/src/MessageWire/SecureRemote/IdentityKeyHash.cs b/src/MessageWire/SecureRemote/IdentityKeyHash.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/IdentityKeyHash.cs rename to src/MessageWire/SecureRemote/IdentityKeyHash.cs diff --git a/src/src/MessageWire/SecureRemote/MessageHeader.cs b/src/MessageWire/SecureRemote/MessageHeader.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/MessageHeader.cs rename to src/MessageWire/SecureRemote/MessageHeader.cs diff --git a/src/src/MessageWire/SecureRemote/Protocol.cs b/src/MessageWire/SecureRemote/Protocol.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/Protocol.cs rename to src/MessageWire/SecureRemote/Protocol.cs diff --git a/src/src/MessageWire/SecureRemote/SafePrimes.cs b/src/MessageWire/SecureRemote/SafePrimes.cs similarity index 100% rename from src/src/MessageWire/SecureRemote/SafePrimes.cs rename to src/MessageWire/SecureRemote/SafePrimes.cs diff --git a/src/src/MessageWire/Session.cs b/src/MessageWire/Session.cs similarity index 100% rename from src/src/MessageWire/Session.cs rename to src/MessageWire/Session.cs diff --git a/src/src/MessageWire/Wire.cs b/src/MessageWire/Wire.cs similarity index 100% rename from src/src/MessageWire/Wire.cs rename to src/MessageWire/Wire.cs diff --git a/src/src/MessageWire.Tests/RouterDealerTests.cs b/src/src/MessageWire.Tests/RouterDealerTests.cs deleted file mode 100644 index b536890..0000000 --- a/src/src/MessageWire.Tests/RouterDealerTests.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using MessageWire; -using System.Text; -using Xunit; -using System.Threading; - -namespace MessageWire.Tests -{ - public class RouterDealerTests : IDisposable - { - public RouterDealerTests() - { - Wire.Linger = new TimeSpan(0, 0, 0, 1); - } - - void IDisposable.Dispose() - { - Wire.Cleanup(); - } - - [Fact] - public void BasicSendReceiveTest() - { - var serverReceived = false; - var clientReceived = false; - var connString = "tcp://127.0.0.1:5800"; - using (var server = new Host(connString)) - { - server.MessageReceived += (s, e) => - { - Assert.Equal("Hello, I'm the client.", e.Message.Frames[0].ConvertToString()); - Assert.Equal("This is my second line.", e.Message.Frames[1].ConvertToString()); - - var replyData = new List(); - replyData.Add("Hello, I'm the server. You sent.".ConvertToBytes()); - replyData.AddRange(e.Message.Frames); - server.Send(e.Message.ClientId, replyData); - serverReceived = true; - Assert.True(e.Message.Frames.Count == 2, "Server received message did not have 2 frames."); - Assert.True(replyData.Count == 3, "Server message did not have 3 frames."); - }; - - using (var client = new Client(connString)) - { - client.MessageReceived += (s, e) => - { - clientReceived = true; - Assert.True(e.Message.Frames.Count == 3, "Received message did not have 3 frames."); - Assert.Equal("Hello, I'm the server. You sent.", e.Message.Frames[0].ConvertToString()); - Assert.Equal("Hello, I'm the client.", e.Message.Frames[1].ConvertToString()); - Assert.Equal("This is my second line.", e.Message.Frames[2].ConvertToString()); - }; - - var clientMessageData = new List(); - clientMessageData.Add("Hello, I'm the client.".ConvertToBytes()); - clientMessageData.Add("This is my second line.".ConvertToBytes()); - client.Send(clientMessageData); - - var count = 0; - while (count < 20 && (!clientReceived || !serverReceived)) - { - Thread.Sleep(20); - count++; - } - Assert.True(count < 100, "Test took too long."); - Assert.True(serverReceived, "Server failed to receive and send."); - Assert.True(clientReceived, "Client failed to receive."); - } - } - } - - [Fact] - public void EncryptedSendReceiveTest() - { - var serverReceived = false; - var clientReceived = false; - var connString = "tcp://127.0.0.1:5800"; - using (var server = new Host(connString, new TestZkRepository())) - { - server.MessageReceived += (s, e) => - { - Assert.Equal("Hello, I'm the client.", e.Message.Frames[0].ConvertToString()); - Assert.Equal("This is my second line.", e.Message.Frames[1].ConvertToString()); - - var replyData = new List(); - replyData.Add("Hello, I'm the server. You sent.".ConvertToBytes()); - replyData.AddRange(e.Message.Frames); - server.Send(e.Message.ClientId, replyData); - serverReceived = true; - Assert.True(e.Message.Frames.Count == 2, "Server received message did not have 2 frames."); - Assert.True(replyData.Count == 3, "Server message did not have 3 frames."); - }; - - using (var client = new Client(connString, "testid", "....++++....")) - { - var established = false; - client.EcryptionProtocolEstablished += (s, e) => - { - established = true; - }; - - client.EcryptionProtocolFailed += (s, e) => - { - Assert.True(false, "Protocol failed."); - }; - - client.MessageReceived += (s, e) => - { - clientReceived = true; - Assert.True(e.Message.Frames.Count == 3, "Received message did not have 3 frames."); - Assert.Equal("Hello, I'm the server. You sent.", e.Message.Frames[0].ConvertToString()); - Assert.Equal("Hello, I'm the client.", e.Message.Frames[1].ConvertToString()); - Assert.Equal("This is my second line.", e.Message.Frames[2].ConvertToString()); - }; - - client.SecureConnection(blockUntilComplete: false); - - var count = 0; - while (count < 20000 && !established) - { - Thread.Sleep(20); - count++; - } - Assert.True(count < 20000, "SecureConnection took too long."); - Assert.True(established, "SecureConnection not established."); - - var clientMessageData = new List(); - clientMessageData.Add("Hello, I'm the client.".ConvertToBytes()); - clientMessageData.Add("This is my second line.".ConvertToBytes()); - client.Send(clientMessageData); - - count = 0; - while (count < 20 && (!established || !clientReceived || !serverReceived)) - { - Thread.Sleep(20); - count++; - } - Assert.True(count < 20, "Test took too long."); - Assert.True(serverReceived, "Server failed to receive and send."); - Assert.True(clientReceived, "Client failed to receive."); - } - } - } - - - [Fact] - public void HeartBeatTest() - { - var serverReceived = false; - var clientReceived = false; - var connString = "tcp://127.0.0.1:5800"; - using (var server = new Host(connString, new TestZkRepository())) - { - server.MessageReceived += (s, e) => - { - Assert.Equal("Hello, I'm the client.", e.Message.Frames[0].ConvertToString()); - Assert.Equal("This is my second line.", e.Message.Frames[1].ConvertToString()); - - var replyData = new List(); - replyData.Add("Hello, I'm the server. You sent.".ConvertToBytes()); - replyData.AddRange(e.Message.Frames); - server.Send(e.Message.ClientId, replyData); - serverReceived = true; - Assert.True(e.Message.Frames.Count == 2, "Server received message did not have 2 frames."); - Assert.True(replyData.Count == 3, "Server message did not have 3 frames."); - }; - - using (var client = new Client(connString, "testid", "....++++....", - heartBeatIntervalMs: 1000, maxSkippedHeartBeatReplies: 4)) - { - var established = false; - client.EcryptionProtocolEstablished += (s, e) => - { - established = true; - }; - - client.EcryptionProtocolFailed += (s, e) => - { - Assert.True(false, "Protocol failed."); - }; - - client.MessageReceived += (s, e) => - { - clientReceived = true; - Assert.True(e.Message.Frames.Count == 3, "Received message did not have 3 frames."); - Assert.Equal("Hello, I'm the server. You sent.", e.Message.Frames[0].ConvertToString()); - Assert.Equal("Hello, I'm the client.", e.Message.Frames[1].ConvertToString()); - Assert.Equal("This is my second line.", e.Message.Frames[2].ConvertToString()); - }; - - client.SecureConnection(blockUntilComplete: false); - - var count = 0; - while (count < 200 && !established) - { - Thread.Sleep(20); - count++; - } - Assert.True(count < 200, "SecureConnection took too long."); - Assert.True(established, "SecureConnection not established."); - - var clientMessageData = new List(); - clientMessageData.Add("Hello, I'm the client.".ConvertToBytes()); - clientMessageData.Add("This is my second line.".ConvertToBytes()); - client.Send(clientMessageData); - - count = 0; - while (count < 20 && (!established || !clientReceived || !serverReceived)) - { - Thread.Sleep(20); - count++; - } - Assert.True(count < 20, "Test took too long."); - Assert.True(serverReceived, "Server failed to receive and send."); - Assert.True(clientReceived, "Client failed to receive."); - - count = 0; - while (count < 200 && client.HeartBeatsSentCount < 2 && client.HeartBeatsReceivedCount < 2) - { - Thread.Sleep(500); - count++; - } - - Assert.True(count < 200, "Test took too long."); - var serverKeys = server.GetCurrentSessionKeys(); - Assert.Equal(1, serverKeys.Length); - var session = server.GetSession(serverKeys[0]); - Assert.NotNull(session); - Assert.True("testid" == session.ClientIdentity, "Client identity does not match."); - Assert.True(client.ClientId == session.ClientId, "ClientId does not match."); - Assert.True(session.HeartBeatsReceivedCount > 1, "Server failed to receive heartbeats."); - } - } - } - } -} diff --git a/src/src/MessageWire/IClient.cs b/src/src/MessageWire/IClient.cs deleted file mode 100644 index 51e25b4..0000000 --- a/src/src/MessageWire/IClient.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace MessageWire -{ - public interface IClient : IDisposable - { - bool CanSend { get; } - Guid ClientId { get; } - int HeartBeatsReceivedCount { get; } - int HeartBeatsSentCount { get; } - bool IsHostAlive { get; } - DateTime? LastHeartBeatReceivedFromHost { get; } - - event EventHandler EcryptionProtocolEstablished; - event EventHandler EcryptionProtocolFailed; - event EventHandler InvalidMessageReceived; - event EventHandler MessageReceived; - - bool SecureConnection(bool blockUntilComplete = true, int timeoutMs = 500); - void Send(List frames); - void Send(IEnumerable frames); - void Send(byte[] frame); - - void Send(List frames); - void Send(IEnumerable frames); - void Send(params string[] frames); - void Send(string frame); - - void Send(List frames, Encoding encoding); - void Send(IEnumerable frames, Encoding encoding); - void Send(Encoding encoding, params string[] frames); - void Send(string frame, Encoding encoding); - } -} \ No newline at end of file diff --git a/src/src/MessageWire/IHost.cs b/src/src/MessageWire/IHost.cs deleted file mode 100644 index 1d293b4..0000000 --- a/src/src/MessageWire/IHost.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace MessageWire -{ - public interface IHost : IDisposable - { - event EventHandler MessageReceived; - event EventHandler MessageSentFailure; - event EventHandler ZkClientSessionEstablishedEvent; - - Guid[] GetCurrentSessionKeys(); - Session[] GetCurrentSessions(); - Session GetSession(Guid key); - void Send(Guid clientId, List frames); - void Send(Guid clientId, IEnumerable frames); - void Send(Guid clientId, byte[] frame); - - void Send(Guid clientId, List frames); - void Send(Guid clientId, IEnumerable frames); - void Send(Guid clientId, params string[] frames); - void Send(Guid clientId, string frame); - - void Send(Guid clientId, List frames, Encoding encoding); - void Send(Guid clientId, IEnumerable frames, Encoding encoding); - void Send(Guid clientId, Encoding encoding, params string[] frames); - void Send(Guid clientId, string frame, Encoding encoding); - } -} \ No newline at end of file diff --git a/src/src/MessageWire/Message.cs b/src/src/MessageWire/Message.cs deleted file mode 100644 index e078578..0000000 --- a/src/src/MessageWire/Message.cs +++ /dev/null @@ -1,138 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * MessageWire - https://github.com/tylerjensen/MessageWire - * - * The MIT License (MIT) - * Copyright (C) 2016-2017 Tyler Jensen - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -using NetMQ; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MessageWire -{ - public class Message - { - public Guid ClientId { get; set; } - public List Frames { get; set; } - } - - internal static class MessageExtensions - { - public static List ConvertToStrings(this Message msg, bool convertEncodingErrorToNull = true) - { - return msg.ConvertToStrings(Encoding.UTF8, convertEncodingErrorToNull); - } - - public static List ConvertToStrings(this Message msg, Encoding encoding, bool convertEncodingErrorToNull = true) - { - var list = new List(); - if (msg.Frames == null || msg.Frames.Count == 0) return list; - foreach (var frame in msg.Frames) - { - try - { - if (frame == null) - list.Add((string)null); - else - list.Add(encoding.GetString(frame)); - } - catch (Exception e) - { - if (convertEncodingErrorToNull) - list.Add((string)null); - else - list.Add($"##EncodingError-Bytes({frame.Length})-{e.Message}-##"); - } - } - return list; - } - - public static Message ToMessageWithoutClientFrame(this NetMQMessage msg, Guid clientId) - { - if (msg == null || msg.FrameCount == 0) return null; - List frames = new List(); - if (msg.FrameCount > 0) - { - frames = (from n in msg where !n.IsEmpty select n.Buffer).ToList(); - } - return new Message - { - ClientId = clientId, - Frames = frames - }; - } - - public static Message ToMessageWithClientFrame(this NetMQMessage msg) - { - if (msg == null || msg.FrameCount == 0) return null; - if (msg[0].BufferSize != 16) return null; //must have a Guid id - var clientId = new Guid(msg[0].Buffer); - List frames = new List(); - if (msg.FrameCount > 1) - { - frames = (from n in msg where !n.IsEmpty select n.Buffer).Skip(1).ToList(); - } - return new Message - { - ClientId = clientId, - Frames = frames - }; - } - - public static NetMQMessage ToNetMQMessage(this Message msg) - { - var message = new NetMQMessage(); - message.Append(msg.ClientId.ToByteArray()); - message.AppendEmptyFrame(); - if (null != msg.Frames) - { - foreach (var frame in msg.Frames) - { - message.Append(frame); - } - } - else - { - message.AppendEmptyFrame(); - } - return message; - } - } - - public class ProtocolFailureEventArgs : EventArgs - { - public string Message { get; set; } - } - - public class MessageEventArgs : EventArgs - { - public Message Message { get; set; } - } - - public class MessageEventFailureArgs : EventArgs - { - public MessageFailure Failure { get; set; } - } - - public class MessageFailure - { - public string ErrorMessage { get; set; } - public string ErrorCode { get; set; } - public Message Message { get; set; } - } -}