diff --git a/EonaCat.Network.Tester/EonaCat.Network.Tester.csproj b/EonaCat.Network.Tester/EonaCat.Network.Tester.csproj
new file mode 100644
index 0000000..3b0107d
--- /dev/null
+++ b/EonaCat.Network.Tester/EonaCat.Network.Tester.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/EonaCat.Network.Tester/Program.cs b/EonaCat.Network.Tester/Program.cs
new file mode 100644
index 0000000..88cd9bb
--- /dev/null
+++ b/EonaCat.Network.Tester/Program.cs
@@ -0,0 +1,188 @@
+// This file is part of the EonaCat project(s) which is released under the Apache License.
+// See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+using System.Net.Security;
+using System.Reflection;
+using System.Security.Authentication;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using EonaCat.WebSockets;
+
+internal class Program
+{
+ private static AsyncWebSocketClient _client;
+ private static AsyncWebSocketServer _server;
+
+ private static async Task Main(string[] args)
+ {
+ // Menu loop
+ while (true)
+ {
+ Console.WriteLine("0. Create a HTTPS certificate");
+ Console.WriteLine("1. Start the server and client");
+ Console.WriteLine("2. Send Message");
+ Console.WriteLine("3. Quit");
+
+ var choice = Console.ReadLine();
+
+ switch (choice)
+ {
+ case "0":
+ CreateCertificate();
+ break;
+
+ case "1":
+ await CreateServerAndClientAsync().ConfigureAwait(false);
+ break;
+
+ case "2":
+ if (_client != null)
+ {
+ Console.Write("Enter message: ");
+ var message = Console.ReadLine();
+ await _client.SendTextAsync(message).ConfigureAwait(false);
+ }
+
+ break;
+
+ case "3":
+ _client?.CloseAsync().ConfigureAwait(false);
+ return;
+
+ default:
+ Console.WriteLine("Invalid choice. Try again.");
+ break;
+ }
+ }
+ }
+
+ private static async Task CreateServerAndClientAsync()
+ {
+ var serverUri = "wss://localhost:8443";
+ var clientUri = "wss://localhost:8443/Welcome";
+ var certificatePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "localhost.pfx");
+ var certificatePassword = "";
+ var requiredPassword = "";
+
+ // Start the server
+ StartServer(serverUri, certificatePath, certificatePassword, requiredPassword);
+
+ // Start the client in the main thread
+ await StartClientAsync(clientUri, certificatePath, certificatePassword).ConfigureAwait(false);
+ Console.WriteLine("Connected to the server.");
+ }
+
+ private static async Task StartClientAsync(string clientUri, string certificatePath, string certificatePassword)
+ {
+ var configuration = new AsyncWebSocketClientConfiguration();
+ configuration.SslTargetHost = "localhost";
+ configuration.SslEncryptionPolicy = EncryptionPolicy.RequireEncryption;
+ configuration.SslCheckCertificateRevocation = false;
+ configuration.SslPolicyErrorsBypassed = true;
+ configuration.SslEnabledProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
+ configuration.SslClientCertificates = new X509CertificateCollection { new X509Certificate2(certificatePath, certificatePassword) };
+
+ _client = new AsyncWebSocketClient(new Uri(clientUri), OnServerTextReceived, OnServerBinaryReceived, OnServerConnected, OnServerDisconnected, OnServerFragmentationStreamOpened, OnServerFragmentationStreamOpened, OnServerFragmentationStreamClosed, configuration);
+ await _client.ConnectAsync().ConfigureAwait(false);
+ }
+
+ private static Task OnServerFragmentationStreamClosed(AsyncWebSocketClient arg1, byte[] arg2, int arg3, int arg4)
+ {
+ // Do nothing
+ return Task.CompletedTask;
+ }
+
+ private static Task OnServerFragmentationStreamOpened(AsyncWebSocketClient arg1, byte[] arg2, int arg3, int arg4)
+ {
+ // Do nothing
+ return Task.CompletedTask;
+ }
+
+ private static Task OnServerDisconnected(AsyncWebSocketClient arg)
+ {
+ Console.WriteLine("Disconnected from server.");
+ return Task.CompletedTask;
+ }
+
+ private static Task OnServerConnected(AsyncWebSocketClient arg)
+ {
+ Console.WriteLine("Connected to server.");
+ return Task.CompletedTask;
+ }
+
+ private static Task OnServerBinaryReceived(AsyncWebSocketClient arg1, byte[] bytes, int arg3, int arg4)
+ {
+ Console.WriteLine($"Received binary {bytes} {arg3} {arg4}");
+ return Task.CompletedTask;
+ }
+
+ private static Task OnServerTextReceived(AsyncWebSocketClient arg1, string data)
+ {
+ Console.WriteLine($"Received message from server: {data}");
+ return Task.CompletedTask;
+ }
+
+ private static void CreateCertificate()
+ {
+ Console.Write("Enter hostname: (default: localhost) ");
+ var hostname = Console.ReadLine();
+
+ if (string.IsNullOrWhiteSpace(hostname))
+ {
+ hostname = "localhost";
+ }
+
+ var days = 30;
+ Console.Write("Enter days until expiration: (default: 30 days) ");
+ if (int.TryParse(Console.ReadLine(), out var givenDays))
+ {
+ days = givenDays;
+ }
+
+ Console.Write("Enter password, enter to skip: ");
+ var password = Console.ReadLine();
+
+ var rsa = RSA.Create();
+
+ // Create a certificate request with the specified subject and key pair
+ var request = new CertificateRequest(
+ $"CN={hostname}",
+ rsa,
+ HashAlgorithmName.SHA256,
+ RSASignaturePadding.Pkcs1);
+
+ // Create a self-signed certificate from the certificate request
+ var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(days));
+
+ // Export the certificate to a file with password
+ var certBytes = string.IsNullOrEmpty(password)
+ ? certificate.Export(X509ContentType.Pfx)
+ : certificate.Export(X509ContentType.Pfx, password);
+ File.WriteAllBytes($"{hostname}.pfx", certBytes);
+
+ Console.WriteLine(
+ $"Certificate for {hostname} created successfully and will expire on {certificate.NotAfter}.");
+ Console.WriteLine($"Path: {Path.Combine(AppContext.BaseDirectory, hostname)}.pfx");
+ }
+
+ private static void StartServer(string serverUri, string certificatePath, string certificatePassword,
+ string requiredPassword)
+ {
+ // split the server uri to get the port
+ var uri = new Uri(serverUri);
+ var port = uri.Port;
+
+ var test = new AsyncWebSocketServerModuleCatalog();
+ test.RegisterModule(new TestModule());
+ var configuration = new AsyncWebSocketServerConfiguration();
+ configuration.SslEnabled = true;
+ configuration.SslEncryptionPolicy = EncryptionPolicy.RequireEncryption;
+ configuration.SslCheckCertificateRevocation = false;
+ configuration.SslPolicyErrorsBypassed = true;
+ configuration.SslClientCertificateRequired = true;
+ configuration.SslEnabledProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
+ configuration.SslServerCertificate = new X509Certificate2(certificatePath, certificatePassword);
+ _server = new AsyncWebSocketServer(port, test, configuration);
+ _server.Listen();
+ }
+}
\ No newline at end of file
diff --git a/EonaCat.Network.Tester/TestModule.cs b/EonaCat.Network.Tester/TestModule.cs
new file mode 100644
index 0000000..13d372b
--- /dev/null
+++ b/EonaCat.Network.Tester/TestModule.cs
@@ -0,0 +1,16 @@
+// This file is part of the EonaCat project(s) which is released under the Apache License.
+// See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+using EonaCat.WebSockets;
+
+internal class TestModule : AsyncWebSocketServerModule
+{
+ public TestModule()
+ : base("/Welcome")
+ {
+ }
+ public override async Task OnSessionTextReceived(AsyncWebSocketSession session, string text)
+ {
+ await session.SendTextAsync(text);
+ }
+}
\ No newline at end of file
diff --git a/JsonTester/JsonTester.csproj b/JsonTester/JsonTester.csproj
index d729163..bcc0f02 100644
--- a/JsonTester/JsonTester.csproj
+++ b/JsonTester/JsonTester.csproj
@@ -8,9 +8,14 @@
-
+
+
+ ..\..\EonaCat.Json\EonaCat.Json\bin\Release\netstandard2.1\EonaCat.Json.dll
+
+
+