From 394f9ab908be238984f89050ce24d378de93054d Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 16 Jul 2020 12:47:18 +0400 Subject: [PATCH] Implement running websocket server and add handling messages in ConnectionHandler --- .vscode/launch.json | 4 +- sample_config.toml | 8 +++- src/Zirconium/Core/App.cs | 14 +++++++ src/Zirconium/Core/Config.cs | 18 +++++++-- src/Zirconium/Core/ConnectionHandler.cs | 49 ++++++++++++++++++++---- src/Zirconium/Core/Main.cs | 10 +++++ src/Zirconium/Core/Models/BaseMessage.cs | 37 ++++++++++-------- src/Zirconium/Core/Router.cs | 24 ++++++------ src/Zirconium/Utils/OtherUtils.cs | 4 ++ 9 files changed, 125 insertions(+), 43 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3878828..00ff003 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,11 +11,11 @@ "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceFolder}/src/Zirconium/bin/Debug/netcoreapp3.1/Zirconium.dll", - "args": [], + "args": ["--config", "${workspaceFolder}/.config.toml"], "cwd": "${workspaceFolder}/src/Zirconium", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console": "internalConsole", - "stopAtEntry": false + "stopAtEntry": false, }, { "name": ".NET Core Attach", diff --git a/sample_config.toml b/sample_config.toml index 3595e06..7e34df3 100644 --- a/sample_config.toml +++ b/sample_config.toml @@ -8,4 +8,10 @@ ServerDomains = [] PluginsDirPath = "" # ID of this server in terms of Cadmium federation network. WARNING! This ID should be maximally unique. -ServerID = "" \ No newline at end of file +ServerID = "" + +# Websocket server settings +[Websocket] +Host = "localhost" +Port = 8000 +Endpoint = "" \ No newline at end of file diff --git a/src/Zirconium/Core/App.cs b/src/Zirconium/Core/App.cs index 5ca92ce..b970ce4 100644 --- a/src/Zirconium/Core/App.cs +++ b/src/Zirconium/Core/App.cs @@ -1,6 +1,7 @@ using Zirconium.Core.Modules; using Zirconium.Core.Modules.Interfaces; using Zirconium.Core.Logging; +using WebSocketSharp.Server; namespace Zirconium.Core { @@ -12,15 +13,28 @@ namespace Zirconium.Core public ModuleManager ModuleManager { get; } public IHostModuleAPI HostModuleAPI { get; } public AuthManager AuthManager { get; } + private WebSocketServer _websocketServer; public App(Config config) { Config = config; + _websocketServer = new WebSocketServer($"ws://{config.Websocket.Host}:{config.Websocket.Port}"); + _websocketServer.AddWebSocketService(config.Websocket.Endpoint, () => new ConnectionHandler(this)); SessionManager = new SessionManager(); Router = new Router(this); HostModuleAPI = new HostModuleAPI(this, Router); AuthManager = new AuthManager(this); Log.Info("Zirconium is initialized successfully"); } + + public void Run() + { + _websocketServer.Start(); + } + + public void Destroy() { + Log.Info("Shutting down Zirconium..."); + _websocketServer.Stop(); + } } } \ No newline at end of file diff --git a/src/Zirconium/Core/Config.cs b/src/Zirconium/Core/Config.cs index a6c1c6b..8d20496 100644 --- a/src/Zirconium/Core/Config.cs +++ b/src/Zirconium/Core/Config.cs @@ -3,15 +3,25 @@ namespace Zirconium.Core public class Config { // A list of enabled plugins (or extensions) in server - public string[] EnabledPlugins {get; set;} + public string[] EnabledPlugins { get; set; } // Server domain names (e.g. example.com) - public string[] ServerDomains {get; set;} + public string[] ServerDomains { get; set; } // Path to directory with plugin assemblies - public string PluginsDirPath {get; set;} + public string PluginsDirPath { get; set; } // ID of this server in terms of Cadmium federation network - public string ServerID {get; set;} + public string ServerID { get; set; } + + // Websocket server settings + public Websocket Websocket { get; set; } + } + + public class Websocket + { + public string Host { get; set; } + public int Port { get; set; } + public string Endpoint { get; set; } } } \ No newline at end of file diff --git a/src/Zirconium/Core/ConnectionHandler.cs b/src/Zirconium/Core/ConnectionHandler.cs index e029233..45beae8 100644 --- a/src/Zirconium/Core/ConnectionHandler.cs +++ b/src/Zirconium/Core/ConnectionHandler.cs @@ -1,9 +1,11 @@ -using System.Threading.Tasks; using System; +using System.Threading.Tasks; using WebSocketSharp.Server; using WebSocketSharp; using Zirconium.Core.Models; using Zirconium.Utils; +using Newtonsoft.Json; +using System.Collections.Generic; namespace Zirconium.Core { @@ -19,18 +21,49 @@ namespace Zirconium.Core protected override void OnClose(CloseEventArgs e) { _app.SessionManager.DeleteSession(ID); - Console.WriteLine($"Connection {ID} was closed (reason: {e.Reason})"); // TODO implement normal logging + Logging.Log.Info($"Connection {ID} was closed (reason: {e.Reason})"); // TODO implement closing connection } protected override void OnError(ErrorEventArgs e) { - Console.WriteLine($"Error occurred: {e.Exception}"); // TODO implement normal logging + Logging.Log.Error($"Error occurred: {e.Exception}"); } protected override void OnMessage(MessageEventArgs e) { - // TODO implement message parsing and routing + try + { + if (e.IsText) + { + var msg = JsonConvert.DeserializeObject(e.Data); + _app.Router.RouteMessage(_app.SessionManager.GetConnectionInfo(ID), msg); + } + else + { + var errMsg = OtherUtils.GenerateProtocolError( + null, + "parseError", + $"Server cannot parse this message yet because it is not JSON", + new Dictionary() + ); + errMsg.From = _app.Config.ServerID; + var msgStr = JsonConvert.SerializeObject(errMsg); + this.SendMessage(msgStr); + } + } + catch (Exception ex) + { + var errMsg = OtherUtils.GenerateProtocolError( + null, + "parseError", + $"Server cannot parse this message! {ex.Message}", + new Dictionary() + ); + errMsg.From = _app.Config.ServerID; + var msgStr = JsonConvert.SerializeObject(errMsg); + this.SendMessage(msgStr); + } } protected override void OnOpen() @@ -40,12 +73,12 @@ namespace Zirconium.Core connInfo.ClientAddress = ip; connInfo.ConnectionHandler = this; _app.SessionManager.AddSession(ID, connInfo); - Console.WriteLine($"Connection {ID} was created"); // TODO implement normal logging + Logging.Log.Info($"Connection {ID} was created"); } - public Task SendMessage(string message) { - this.Send(message.ToByteArray()); - return Task.CompletedTask; + public void SendMessage(string message) + { + this.Send(message); } } } diff --git a/src/Zirconium/Core/Main.cs b/src/Zirconium/Core/Main.cs index ea6ed26..5182fde 100644 --- a/src/Zirconium/Core/Main.cs +++ b/src/Zirconium/Core/Main.cs @@ -12,6 +12,8 @@ namespace Zirconium.Core } class Program { + private static bool keepRunning = true; + static void Main(string[] args) { string configPath = null; @@ -38,6 +40,14 @@ namespace Zirconium.Core Log.Fatal($"Error occured when parsing config - {e.Message}"); } App app = new App(config); + app.Run(); + Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) + { + e.Cancel = true; + app.Destroy(); + keepRunning = false; + }; + while(keepRunning) {} } } } diff --git a/src/Zirconium/Core/Models/BaseMessage.cs b/src/Zirconium/Core/Models/BaseMessage.cs index da5803a..88f52fd 100644 --- a/src/Zirconium/Core/Models/BaseMessage.cs +++ b/src/Zirconium/Core/Models/BaseMessage.cs @@ -11,16 +11,16 @@ namespace Zirconium.Core.Models [JsonProperty("type")] public string MessageType { get; set; } - [JsonProperty("from")] + [JsonProperty("from", NullValueHandling = NullValueHandling.Ignore)] public string From { get; set; } - [JsonProperty("to")] + [JsonProperty("to", NullValueHandling = NullValueHandling.Ignore)] public string To { get; set; } [JsonProperty("ok")] public bool Ok { get; set; } - [JsonProperty("authToken")] + [JsonProperty("authToken", NullValueHandling = NullValueHandling.Ignore)] public string AuthToken { get; set; } [JsonProperty("payload")] @@ -30,22 +30,25 @@ namespace Zirconium.Core.Models public BaseMessage(BaseMessage message, bool reply) { - ID = message.ID; - MessageType = message.MessageType; - if (reply) + if (message != null) { - From = message.To; - To = message.From; - } - else - { - From = message.From; - To = message.To; - } + ID = message.ID; + MessageType = message.MessageType; + if (reply) + { + From = message.To; + To = message.From; + } + else + { + From = message.From; + To = message.To; + } - Ok = message.Ok; - AuthToken = message.AuthToken; - Payload = message.Payload; + Ok = message.Ok; + AuthToken = message.AuthToken; + Payload = message.Payload; + } } } } \ No newline at end of file diff --git a/src/Zirconium/Core/Router.cs b/src/Zirconium/Core/Router.cs index a89eb1a..53c4a5a 100644 --- a/src/Zirconium/Core/Router.cs +++ b/src/Zirconium/Core/Router.cs @@ -25,18 +25,18 @@ namespace Zirconium.Core public void RouteMessage(ConnectionInfo connInfo, BaseMessage message) { - var handlers = _c2sMessageHandlers[message.MessageType]; + var handlers = _c2sMessageHandlers.GetValueOrDefault(message.MessageType, null); if (handlers == null) { - Log.Warning($"Drop message with type {message.MessageType} because server hasn't proper handlers"); - var serializedMsg = JsonConvert.SerializeObject( - OtherUtils.GenerateProtocolError( - message, - "unhandled", - $"Server doesn't implement message type {message.MessageType}", - new Dictionary() - ) + Log.Warning($"Drop message with type \"{message.MessageType}\" because server hasn't proper handlers"); + var msg = OtherUtils.GenerateProtocolError( + message, + "unhandled", + $"Server doesn't implement message type \"{message.MessageType}\"", + new Dictionary() ); + msg.From = _app.Config.ServerID; + var serializedMsg = JsonConvert.SerializeObject(msg); connInfo.ConnectionHandler.SendMessage(serializedMsg); return; } @@ -113,7 +113,8 @@ namespace Zirconium.Core public void RemoveC2SHandler(string messageType, IC2SMessageHandler handler) { - if (!this._c2sMessageHandlers[messageType].Remove(handler)) { + if (!this._c2sMessageHandlers[messageType].Remove(handler)) + { Log.Warning("attempt to remove c2s handler which doesn't exist in router"); } } @@ -129,7 +130,8 @@ namespace Zirconium.Core public void RemoveCoreEventHandler(string eventType, ICoreEventHandler handler) { - if (!this._coreEventsHandlers[eventType].Remove(handler)) { + if (!this._coreEventsHandlers[eventType].Remove(handler)) + { Log.Warning("attempt to remove core handler which doesn't exist in router"); } } diff --git a/src/Zirconium/Utils/OtherUtils.cs b/src/Zirconium/Utils/OtherUtils.cs index 25201b2..acf7f30 100644 --- a/src/Zirconium/Utils/OtherUtils.cs +++ b/src/Zirconium/Utils/OtherUtils.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Zirconium.Core.Models; @@ -13,6 +14,9 @@ namespace Zirconium.Utils err.ErrPayload = errPayload; BaseMessage msg = new BaseMessage(parentMessage, true); + if (parentMessage == null) { + msg.ID = Guid.NewGuid().ToString(); + } msg.Ok = false; msg.Payload = err.ToDictionary(); return msg;