This repository has been archived by the owner on Feb 25, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 77
Prototype code for packet handler decorators #40
Draft
snarlynarwhal
wants to merge
1
commit into
markiodev:master
Choose a base branch
from
snarlynarwhal:feature/packet-handler-decorators
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<SomePacket> | ||
{ | ||
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<RequireRoleAttribute>(); | ||
if (authorize == null) | ||
{ | ||
return Role.GuestUser; // No authorization required | ||
} | ||
else | ||
{ | ||
return authorize.Role; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using Networker.Common; | ||
using System; | ||
|
||
namespace Demo.Decorators | ||
{ | ||
public class AuthorizeAttribute : DecoratorAttribute | ||
{ | ||
public override Type[] Types => new Type[] { typeof(AuthorizationHandlerDecorator) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>netcoreapp2.2</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\Networker\Networker.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using Networker.Common; | ||
using System; | ||
|
||
namespace Demo.Decorators | ||
{ | ||
public class LogAttribute : DecoratorAttribute | ||
{ | ||
public override Type[] Types => new Type[] { typeof(LoggingHandlerDecorator) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<SomePacket> | ||
{ | ||
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using Networker.Common; | ||
using System; | ||
|
||
namespace Demo.Decorators | ||
{ | ||
public class MeasureExecutionAttribute : DecoratorAttribute | ||
{ | ||
public override Type[] Types => new Type[] { typeof(MeasureExecutionHandlerDecorator) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<SomePacket> | ||
{ | ||
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."); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<SomePacketHandlerModule>(). | ||
Build(); | ||
|
||
server.Start(); | ||
|
||
Console.ReadLine(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace Demo.Decorators | ||
{ | ||
public enum Role | ||
{ | ||
GuestUser, | ||
AuthenticatedUser, | ||
QATester, | ||
Moderator, | ||
Developer, | ||
Administrator | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Demo.Decorators | ||
{ | ||
public class SomePacket | ||
{ | ||
public string SomeString { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<SomePacket> | ||
{ | ||
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
using Networker.Common; | ||
|
||
namespace Demo.Decorators | ||
{ | ||
public class SomePacketHandlerModule : PacketHandlerModuleBase | ||
{ | ||
public SomePacketHandlerModule() | ||
{ | ||
this.AddPacketHandler<SomePacket, SomePacketHandler>(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System; | ||
|
||
namespace Networker.Common | ||
{ | ||
public abstract class DecoratorAttribute : Attribute | ||
{ | ||
public abstract Type[] Types { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<T> : PacketHandlerBase<T> where T: class | ||
{ | ||
private PacketHandlerBase<T> decoratedHandler; | ||
private Dictionary<Type, MethodInfo> processMethodInfoCache = new Dictionary<Type, MethodInfo>(); | ||
|
||
public void Decorate(PacketHandlerBase<T> 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<S>() 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<T> decoratorHandler) | ||
{ | ||
processMethodInfo = decoratorHandler.GetProcessMethodInfo(packetType); | ||
} | ||
else | ||
{ | ||
Type[] processParameterTypes = new Type[] { packetType, typeof(IPacketContext) }; | ||
processMethodInfo = GetType().GetMethod("Process", processParameterTypes); | ||
} | ||
processMethodInfoCache.Add(packetType, processMethodInfo); | ||
return processMethodInfo; | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This particular way of doing it would work well as an extension project, but I would like to avoid the dependency on PacketHandlerBase as you are able to use just an IPacketHandler now.
Would this also mean you'd need a new PacketHandlerDecorator for each packet type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah implementing IPacketHandler makes more sense. The decorator has helper methods to get the target handler method info based on the packet type. This assumes the handler methods accept a base packet type though. I definitely need to think more about this.
Where should I wire up the decorators? Once I get it to a testable state, I can play around with ideas to make sure a single decorator can handle multiple packet types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got a new way of registering packets + handlers coming in soon which might be the best place to wire up the decorator, I'll let you know when it's ready.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright sounds good! Also, it might be preferable to assign decorators using the builder instead of attributes. That approach would require a fluent interface instead of method chaining, which I can add later on if you think it's a good idea.