From 592b17ad16dabeebab19b79412672b8d39446992 Mon Sep 17 00:00:00 2001 From: Snarly Narwhal Date: Sat, 23 Mar 2019 15:59:16 -0400 Subject: [PATCH] Prototype code for packet handler decorators --- .../AuthorizationHandlerDecorator.cs | 35 ++++++++++++ Demos/Demo.Decorators/AuthorizeAttribute.cs | 10 ++++ Demos/Demo.Decorators/Demo.Decorators.csproj | 12 ++++ Demos/Demo.Decorators/LogAttribute.cs | 10 ++++ .../LoggingHandlerDecorator.cs | 23 ++++++++ .../MeasureExecutionAttribute.cs | 10 ++++ .../MeasureExecutionHandlerDecorator.cs | 27 +++++++++ Demos/Demo.Decorators/Program.cs | 28 ++++++++++ Demos/Demo.Decorators/RequireRoleAttribute.cs | 14 +++++ Demos/Demo.Decorators/Role.cs | 12 ++++ Demos/Demo.Decorators/SomePacket.cs | 7 +++ Demos/Demo.Decorators/SomePacketHandler.cs | 41 ++++++++++++++ .../SomePacketHandlerModule.cs | 13 +++++ Networker.sln | 7 +++ Networker/Common/DecorateAttribute.cs | 16 ++++++ Networker/Common/DecoratorAttribute.cs | 9 +++ Networker/Common/PacketHandlerDecorator.cs | 55 +++++++++++++++++++ 17 files changed, 329 insertions(+) create mode 100644 Demos/Demo.Decorators/AuthorizationHandlerDecorator.cs create mode 100644 Demos/Demo.Decorators/AuthorizeAttribute.cs create mode 100644 Demos/Demo.Decorators/Demo.Decorators.csproj create mode 100644 Demos/Demo.Decorators/LogAttribute.cs create mode 100644 Demos/Demo.Decorators/LoggingHandlerDecorator.cs create mode 100644 Demos/Demo.Decorators/MeasureExecutionAttribute.cs create mode 100644 Demos/Demo.Decorators/MeasureExecutionHandlerDecorator.cs create mode 100644 Demos/Demo.Decorators/Program.cs create mode 100644 Demos/Demo.Decorators/RequireRoleAttribute.cs create mode 100644 Demos/Demo.Decorators/Role.cs create mode 100644 Demos/Demo.Decorators/SomePacket.cs create mode 100644 Demos/Demo.Decorators/SomePacketHandler.cs create mode 100644 Demos/Demo.Decorators/SomePacketHandlerModule.cs create mode 100644 Networker/Common/DecorateAttribute.cs create mode 100644 Networker/Common/DecoratorAttribute.cs create mode 100644 Networker/Common/PacketHandlerDecorator.cs diff --git a/Demos/Demo.Decorators/AuthorizationHandlerDecorator.cs b/Demos/Demo.Decorators/AuthorizationHandlerDecorator.cs new file mode 100644 index 0000000..bc04ec7 --- /dev/null +++ b/Demos/Demo.Decorators/AuthorizationHandlerDecorator.cs @@ -0,0 +1,35 @@ +using Networker.Common; +using Networker.Common.Abstractions; +using System.Reflection; +using System.Threading.Tasks; + +namespace Demo.Decorators +{ + // TODO: Test and make sure works with inheritance and multiple methods + // Aka replace SomePacket with PacketBase and test on SomePacket and SomeOtherPacket + public class AuthorizationHandlerDecorator : PacketHandlerDecorator + { + public override async Task Process(SomePacket packet, IPacketContext context) + { + Role userRole = Role.AuthenticatedUser; // Get from service + Role minimumRequiredRole = GetMinimumRequiredRole(packet); + if (userRole >= minimumRequiredRole) + { + await ContinueProcessing(packet, context); + } + } + + private Role GetMinimumRequiredRole(SomePacket packet) + { + RequireRoleAttribute authorize = GetProcessMethodInfo(packet).GetCustomAttribute(); + if (authorize == null) + { + return Role.GuestUser; // No authorization required + } + else + { + return authorize.Role; + } + } + } +} \ No newline at end of file diff --git a/Demos/Demo.Decorators/AuthorizeAttribute.cs b/Demos/Demo.Decorators/AuthorizeAttribute.cs new file mode 100644 index 0000000..cecfcfc --- /dev/null +++ b/Demos/Demo.Decorators/AuthorizeAttribute.cs @@ -0,0 +1,10 @@ +using Networker.Common; +using System; + +namespace Demo.Decorators +{ + public class AuthorizeAttribute : DecoratorAttribute + { + public override Type[] Types => new Type[] { typeof(AuthorizationHandlerDecorator) }; + } +} diff --git a/Demos/Demo.Decorators/Demo.Decorators.csproj b/Demos/Demo.Decorators/Demo.Decorators.csproj new file mode 100644 index 0000000..87028bb --- /dev/null +++ b/Demos/Demo.Decorators/Demo.Decorators.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.2 + + + + + + + diff --git a/Demos/Demo.Decorators/LogAttribute.cs b/Demos/Demo.Decorators/LogAttribute.cs new file mode 100644 index 0000000..b11d303 --- /dev/null +++ b/Demos/Demo.Decorators/LogAttribute.cs @@ -0,0 +1,10 @@ +using Networker.Common; +using System; + +namespace Demo.Decorators +{ + public class LogAttribute : DecoratorAttribute + { + public override Type[] Types => new Type[] { typeof(LoggingHandlerDecorator) }; + } +} diff --git a/Demos/Demo.Decorators/LoggingHandlerDecorator.cs b/Demos/Demo.Decorators/LoggingHandlerDecorator.cs new file mode 100644 index 0000000..ab1e00b --- /dev/null +++ b/Demos/Demo.Decorators/LoggingHandlerDecorator.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.Logging; +using Networker.Common; +using Networker.Common.Abstractions; +using System.Threading.Tasks; + +namespace Demo.Decorators +{ + public class LoggingHandlerDecorator : PacketHandlerDecorator + { + private readonly ILogger logger; + + public LoggingHandlerDecorator(ILogger logger) + { + this.logger = logger; + } + + public override async Task Process(SomePacket packet, IPacketContext context) + { + logger.LogInformation("Logging to fake database: SomePacket received, SomePacket.SomeString = " + packet.SomeString); + await ContinueProcessing(packet, context); + } + } +} \ No newline at end of file diff --git a/Demos/Demo.Decorators/MeasureExecutionAttribute.cs b/Demos/Demo.Decorators/MeasureExecutionAttribute.cs new file mode 100644 index 0000000..1d8dc7b --- /dev/null +++ b/Demos/Demo.Decorators/MeasureExecutionAttribute.cs @@ -0,0 +1,10 @@ +using Networker.Common; +using System; + +namespace Demo.Decorators +{ + public class MeasureExecutionAttribute : DecoratorAttribute + { + public override Type[] Types => new Type[] { typeof(MeasureExecutionHandlerDecorator) }; + } +} diff --git a/Demos/Demo.Decorators/MeasureExecutionHandlerDecorator.cs b/Demos/Demo.Decorators/MeasureExecutionHandlerDecorator.cs new file mode 100644 index 0000000..a703a29 --- /dev/null +++ b/Demos/Demo.Decorators/MeasureExecutionHandlerDecorator.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Networker.Common; +using Networker.Common.Abstractions; + +namespace Demo.Decorators +{ + public class MeasureExecutionHandlerDecorator : PacketHandlerDecorator + { + private readonly ILogger logger; + + public MeasureExecutionHandlerDecorator(ILogger logger) + { + this.logger = logger; + } + + public override async Task Process(SomePacket packet, IPacketContext context) + { + Stopwatch stopwatch = Stopwatch.StartNew(); + await ContinueProcessing(packet, context); + stopwatch.Stop(); + long elapsedMs = stopwatch.ElapsedMilliseconds; + logger.LogInformation("SomePacket took " + elapsedMs + " to process."); + } + } +} \ No newline at end of file diff --git a/Demos/Demo.Decorators/Program.cs b/Demos/Demo.Decorators/Program.cs new file mode 100644 index 0000000..3650f06 --- /dev/null +++ b/Demos/Demo.Decorators/Program.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Logging; +using Networker.Server; +using System; + +namespace Demo.Decorators +{ + class Program + { + static void Main(string[] args) + { + var server = new ServerBuilder(). + UseTcp(1000). + SetMaximumConnections(6000). + UseUdp(5000). + ConfigureLogging(loggingBuilder => + { + loggingBuilder.SetMinimumLevel( + LogLevel.Debug); + }). + RegisterPacketHandlerModule(). + Build(); + + server.Start(); + + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/Demos/Demo.Decorators/RequireRoleAttribute.cs b/Demos/Demo.Decorators/RequireRoleAttribute.cs new file mode 100644 index 0000000..60ad016 --- /dev/null +++ b/Demos/Demo.Decorators/RequireRoleAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Demo.Decorators +{ + public class RequireRoleAttribute : Attribute + { + public Role Role { get; private set; } + + public RequireRoleAttribute(Role role) + { + Role = role; + } + } +} diff --git a/Demos/Demo.Decorators/Role.cs b/Demos/Demo.Decorators/Role.cs new file mode 100644 index 0000000..1360efd --- /dev/null +++ b/Demos/Demo.Decorators/Role.cs @@ -0,0 +1,12 @@ +namespace Demo.Decorators +{ + public enum Role + { + GuestUser, + AuthenticatedUser, + QATester, + Moderator, + Developer, + Administrator + } +} diff --git a/Demos/Demo.Decorators/SomePacket.cs b/Demos/Demo.Decorators/SomePacket.cs new file mode 100644 index 0000000..2f4f6c7 --- /dev/null +++ b/Demos/Demo.Decorators/SomePacket.cs @@ -0,0 +1,7 @@ +namespace Demo.Decorators +{ + public class SomePacket + { + public string SomeString { get; set; } + } +} \ No newline at end of file diff --git a/Demos/Demo.Decorators/SomePacketHandler.cs b/Demos/Demo.Decorators/SomePacketHandler.cs new file mode 100644 index 0000000..f2ee140 --- /dev/null +++ b/Demos/Demo.Decorators/SomePacketHandler.cs @@ -0,0 +1,41 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Networker.Common; +using Networker.Common.Abstractions; + +namespace Demo.Decorators +{ + // Build in attribute + [Decorate( + typeof(AuthorizationHandlerDecorator), + typeof(LoggingHandlerDecorator), + typeof(MeasureExecutionHandlerDecorator))] + + // -- or -- + + // Custom attributes + [Authorize, Log, MeasureExecution] + public class SomePacketHandler : PacketHandlerBase + { + private readonly ILogger logger; + + public SomePacketHandler(ILogger logger) + { + this.logger = logger; + } + + [RequireRole(Role.Administrator)] + public override Task Process(SomePacket packet, IPacketContext context) + { + Thread.Sleep(1000); // To test measure execution time decorator + logger.LogInformation("Processing SomePacket."); + return Task.CompletedTask; + } + + public string GetLog(SomePacket packet) + { + return "Packet received with data: " + packet.SomeString; + } + } +} \ No newline at end of file diff --git a/Demos/Demo.Decorators/SomePacketHandlerModule.cs b/Demos/Demo.Decorators/SomePacketHandlerModule.cs new file mode 100644 index 0000000..f890f4f --- /dev/null +++ b/Demos/Demo.Decorators/SomePacketHandlerModule.cs @@ -0,0 +1,13 @@ +using System; +using Networker.Common; + +namespace Demo.Decorators +{ + public class SomePacketHandlerModule : PacketHandlerModuleBase + { + public SomePacketHandlerModule() + { + this.AddPacketHandler(); + } + } +} \ No newline at end of file diff --git a/Networker.sln b/Networker.sln index 0a7dc03..5409a8b 100644 --- a/Networker.sln +++ b/Networker.sln @@ -35,6 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tutorial.Client", "Demos\Tu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tutorial.Common", "Demos\Tutorial.Common\Tutorial.Common.csproj", "{D6542750-89CB-4D09-9E7B-EAAA392D4E5D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo.Decorators", "Demos\Demo.Decorators\Demo.Decorators.csproj", "{AE162773-D7F0-44D7-A732-8743D08C9CC1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -93,6 +95,10 @@ Global {D6542750-89CB-4D09-9E7B-EAAA392D4E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D6542750-89CB-4D09-9E7B-EAAA392D4E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D6542750-89CB-4D09-9E7B-EAAA392D4E5D}.Release|Any CPU.Build.0 = Release|Any CPU + {AE162773-D7F0-44D7-A732-8743D08C9CC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE162773-D7F0-44D7-A732-8743D08C9CC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE162773-D7F0-44D7-A732-8743D08C9CC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE162773-D7F0-44D7-A732-8743D08C9CC1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -110,6 +116,7 @@ Global {6ADC9B53-4FB7-487A-8D61-CC4981C30269} = {84C88291-7FC8-47AC-84FA-9CF5DF39028C} {F7226539-2B8F-492F-B9BD-DA81E298AE67} = {84C88291-7FC8-47AC-84FA-9CF5DF39028C} {D6542750-89CB-4D09-9E7B-EAAA392D4E5D} = {84C88291-7FC8-47AC-84FA-9CF5DF39028C} + {AE162773-D7F0-44D7-A732-8743D08C9CC1} = {84C88291-7FC8-47AC-84FA-9CF5DF39028C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A022B3C8-E866-480E-82A5-17935BA812CC} diff --git a/Networker/Common/DecorateAttribute.cs b/Networker/Common/DecorateAttribute.cs new file mode 100644 index 0000000..2c66e97 --- /dev/null +++ b/Networker/Common/DecorateAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Networker.Common +{ + public class DecorateAttribute : DecoratorAttribute + { + public override Type[] Types => types; + + private readonly Type[] types; + + public DecorateAttribute(params Type[] types) + { + this.types = types; + } + } +} diff --git a/Networker/Common/DecoratorAttribute.cs b/Networker/Common/DecoratorAttribute.cs new file mode 100644 index 0000000..de747a7 --- /dev/null +++ b/Networker/Common/DecoratorAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Networker.Common +{ + public abstract class DecoratorAttribute : Attribute + { + public abstract Type[] Types { get; } + } +} \ No newline at end of file diff --git a/Networker/Common/PacketHandlerDecorator.cs b/Networker/Common/PacketHandlerDecorator.cs new file mode 100644 index 0000000..bff9c4c --- /dev/null +++ b/Networker/Common/PacketHandlerDecorator.cs @@ -0,0 +1,55 @@ +using Networker.Common.Abstractions; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; + +namespace Networker.Common +{ + public abstract class PacketHandlerDecorator : PacketHandlerBase where T: class + { + private PacketHandlerBase decoratedHandler; + private Dictionary processMethodInfoCache = new Dictionary(); + + public void Decorate(PacketHandlerBase handler) + { + decoratedHandler = handler; + } + + protected Task ContinueProcessing(T packet, IPacketContext context) + { + return decoratedHandler.Process(packet, context); + } + + protected MethodInfo GetProcessMethodInfo(T packet) + { + return GetProcessMethodInfo(packet.GetType()); + } + + protected MethodInfo GetProcessMethodInfo() where S : T + { + return GetProcessMethodInfo(typeof(S)); + } + + protected MethodInfo GetProcessMethodInfo(Type packetType) + { + if (processMethodInfoCache.ContainsKey(packetType)) + { + return processMethodInfoCache[packetType]; + } + + MethodInfo processMethodInfo = null; + if (decoratedHandler is PacketHandlerDecorator decoratorHandler) + { + processMethodInfo = decoratorHandler.GetProcessMethodInfo(packetType); + } + else + { + Type[] processParameterTypes = new Type[] { packetType, typeof(IPacketContext) }; + processMethodInfo = GetType().GetMethod("Process", processParameterTypes); + } + processMethodInfoCache.Add(packetType, processMethodInfo); + return processMethodInfo; + } + } +} \ No newline at end of file