diff --git a/Example.Backend/PagesList.cs b/Example.Backend/PagesList.cs index 1b195c9..35db8f1 100644 --- a/Example.Backend/PagesList.cs +++ b/Example.Backend/PagesList.cs @@ -8,7 +8,7 @@ namespace Example.Backend { public class PagesList : RemoteObjectBase, IPagesList { - public PagesList(int id, IRepresentationModule representationModule) : base(id, representationModule) + public PagesList(int id, IRepresentationModule representationModule,IEndPointContext context) : base(id, representationModule, context) { } diff --git a/Example.Backend/Printer.cs b/Example.Backend/Printer.cs index 87958c9..9e9bb07 100644 --- a/Example.Backend/Printer.cs +++ b/Example.Backend/Printer.cs @@ -10,6 +10,17 @@ namespace Example.Backend { public string Name; + public async Task SomeoneIsApproaching(string humanName) + { + Console.WriteLine(humanName + " is approaching"); + } + + public Task SetFingerPrint(int[] fingerPrint) + { + Console.WriteLine(fingerPrint.Length); + return Task.CompletedTask; + } + public void OnPrintExternal(IPage p0, RequestContext ro) { OnPrint?.Invoke(p0, ro); diff --git a/Example.Backend/Program.cs b/Example.Backend/Program.cs index 1e12d48..15e0604 100644 --- a/Example.Backend/Program.cs +++ b/Example.Backend/Program.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Net; using Example.Backend; using mROA.Abstract; @@ -7,7 +8,6 @@ using mROA.Codegen; using mROA.Implementation; using mROA.Implementation.Backend; using mROA.Implementation.Bootstrap; -using mROA.Implementation.Frontend; class Program { @@ -19,21 +19,22 @@ class Program builder.Modules.Add(new BackendIdentityGenerator()); // builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), // builder.GetModule()!); - builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), + var listening = new IPEndPoint(IPAddress.Loopback, 4567); + builder.UseNetworkGateway(listening, typeof(ChannelInteractionModule), builder.GetModule()!); - + builder.Modules.Add(new UdpGateway(listening)); builder.Modules.Add(new ConnectionHub()); - builder.Modules.Add(new HubRequestExtractor(typeof(RequestExtractor))); + builder.Modules.Add(new HubRequestExtractor()); builder.UseBasicExecution(); builder.Modules.Add(new CreativeRepresentationModuleProducer( - new IInjectableModule[] { builder.GetModule()! }, + new IInjectableModule[] { builder.GetModule()! }, typeof(RepresentationModule))); - builder.Modules.Add(new RemoteContextRepository()); + builder.Modules.Add(new RemoteInstanceRepository()); // builder.UseCollectableContextRepository(typeof(PrinterFactory).Assembly); - builder.Modules.Add(new MultiClientContextRepository(i => + builder.Modules.Add(new MultiClientInstanceRepository(i => { - var repo = new ContextRepository(); + var repo = new InstanceRepository(); repo.FillSingletons(typeof(PrinterFactory).Assembly); repo.Inject(builder.Modules.OfType().First()); return repo; @@ -45,12 +46,11 @@ class Program builder.Build(); new RemoteTypeBinder(); - TransmissionConfig.RealContextRepository = builder.GetModule(); - TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); - TransmissionConfig.OwnershipRepository = new MultiClientOwnershipRepository(); + _ = builder.GetModule()!.Start(); var gateway = builder.GetModule(); - gateway.Run(); + + Console.ReadLine(); } } \ No newline at end of file diff --git a/Example.Frontend/ClientBasedPrinter.cs b/Example.Frontend/ClientBasedPrinter.cs index 6afc7a3..1c4f0cb 100644 --- a/Example.Frontend/ClientBasedPrinter.cs +++ b/Example.Frontend/ClientBasedPrinter.cs @@ -8,6 +8,16 @@ namespace Example.Frontend { public class ClientBasedPrinter : IPrinter { + public Task SomeoneIsApproaching(string humanName) + { + return Task.CompletedTask; + } + + public Task SetFingerPrint(int[] fingerPrint) + { + return Task.CompletedTask; + } + public void OnPrintExternal(IPage p0, RequestContext ro) { } diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index 8ecc3f4..5e1b7a7 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Net; using System.Text; using System.Threading; @@ -15,17 +16,19 @@ using mROA.Implementation.Frontend; class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { var builder = new FullMixBuilder(); new RemoteTypeBinder(); - // builder.Modules.Add(new JsonSerializationToolkit()); - builder.Modules.Add(new CborSerializationToolkit()); - builder.Modules.Add(new RemoteContextRepository()); - builder.Modules.Add(new NextGenerationInteractionModule()); + builder.Modules.Add(new CborSerializationToolkit()); + builder.Modules.Add(new EndPointContext()); + builder.Modules.Add(new RemoteInstanceRepository()); + builder.Modules.Add(new ChannelInteractionModule()); + builder.Modules.Add(new UdpUntrustedInteraction()); builder.Modules.Add(new RepresentationModule()); - builder.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Loopback, 4567))); + var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 4567); + builder.Modules.Add(new NetworkFrontendBridge(serverEndPoint)); builder.Modules.Add(new StaticRepresentationModuleProducer()); builder.Modules.Add(new RequestExtractor()); builder.Modules.Add(new BasicExecutionModule()); @@ -34,25 +37,29 @@ class Program builder.Modules.Add(new CancellationRepository()); builder.Build(); - - - TransmissionConfig.RealContextRepository = builder.GetModule(); - TransmissionConfig.RemoteEndpointContextRepository = builder.GetModule(); - + var frontendBridge = builder.GetModule()!; - frontendBridge.Connect(); + await frontendBridge.Connect(); _ = builder.GetModule()!.StartExtraction(); - Console.WriteLine(TransmissionConfig.OwnershipRepository.GetOwnershipId()); - var context = builder.GetModule(); + _ = builder.GetModule().Start(serverEndPoint); + Console.WriteLine(builder.GetModule().HostId); + var context = builder.GetModule(); - var factory = context.GetSingleObject(typeof(IPrinterFactory), 0) as IPrinterFactory; + var factory = + context.GetSingletonObject( + builder.GetModule()); using (var disposingPrinter = factory.Create("Test")) { + disposingPrinter.SetFingerPrint(new[] { 1, 2, 3 }).ContinueWith(r => + { + Console.WriteLine(r.Status); + }); + DemoCheck.CreatingPrinter = true; disposingPrinter.OnPrint += (_, _) => { - Console.WriteLine("New page creater. Called from event!!!"); + Console.WriteLine("New page created. Called from event!!!"); DemoCheck.EventCallback = true; }; Console.WriteLine("Printer created"); @@ -65,7 +72,11 @@ class Program Thread.Sleep(100); + disposingPrinter.SomeoneIsApproaching("Mikhail"); + Console.WriteLine("Approaching detected"); + factory.Register(new ClientBasedPrinter()); + factory.Register(disposingPrinter); DemoCheck.ClientBasedImplementation = true; Console.WriteLine("Registered printer"); Thread.Sleep(100); @@ -80,10 +91,9 @@ class Program var names = factory.CollectAllNames(); Thread.Sleep(100); - Console.WriteLine(string.Join(", ", names)); + Console.WriteLine("Names: " + string.Join(", ", names)); - var page = disposingPrinter.Print("Test Page", false, default, CancellationToken.None).GetAwaiter() - .GetResult(); + var page = await disposingPrinter.Print("Test Page", false, default, CancellationToken.None); Console.WriteLine("Page printed"); DemoCheck.TaskExecution = true; Console.WriteLine(page.ToString()); @@ -101,10 +111,11 @@ class Program Console.WriteLine("Dispose printer"); } + DemoCheck.Dispose = true; - var loadSingleton = context.GetSingleObject(typeof(ILoadTest), 0) as ILoadTest; + var loadSingleton = context.GetSingletonObject(builder.GetModule()); var cts = new CancellationTokenSource(); @@ -115,23 +126,26 @@ class Program cts.Cancel(); Console.WriteLine($"Token state {cts.Token.IsCancellationRequested}"); DemoCheck.TaskCancelation = true; + + const int iterations = 10000; + var timer = Stopwatch.StartNew(); + var x = 0; + for (int i = 0; i < iterations; i++) + { + x = loadSingleton.Next(x); + } + + timer.Stop(); + Console.WriteLine("X is {0}", x); + Console.WriteLine("Time : {0}", timer.Elapsed.TotalMilliseconds); + Console.WriteLine($"Time per call: {timer.Elapsed.TotalMilliseconds / iterations} ms"); frontendBridge.Disconnect(); - + DemoCheck.Show(); Console.ReadKey(); - // - // const int iterations = 10000; - // var timer = Stopwatch.StartNew(); - // var x = 0; - // for (int i = 0; i < iterations; i++) - // { - // x = loadSingleton.Next(x); - // } - // - // timer.Stop(); - // Console.WriteLine("X is {0}", x); - // Console.WriteLine("Time : {0}", timer.Elapsed.TotalMilliseconds); - // Console.WriteLine($"Time per call: {timer.Elapsed.TotalMilliseconds / iterations} ms"); + + + } } \ No newline at end of file diff --git a/Example.Shared/IPrinter.cs b/Example.Shared/IPrinter.cs index f5b25f0..e34b889 100644 --- a/Example.Shared/IPrinter.cs +++ b/Example.Shared/IPrinter.cs @@ -13,5 +13,8 @@ namespace Example.Shared string GetName(); Task Print(string text, bool someParameter, RequestContext context, CancellationToken cancellationToken); event Action OnPrint; + [Untrusted] + Task SomeoneIsApproaching(string humanName); + Task SetFingerPrint(int[] fingerPrint); } } \ No newline at end of file diff --git a/mROA.Cbor/CborSerializationToolkit.cs b/mROA.Cbor/CborSerializationToolkit.cs index 47d1fc0..5fc81a5 100644 --- a/mROA.Cbor/CborSerializationToolkit.cs +++ b/mROA.Cbor/CborSerializationToolkit.cs @@ -213,13 +213,10 @@ namespace mROA.Cbor if (obj is IShared) { - var generic = obj.GetType().GetInterfaces().FirstOrDefault(i => typeof(IShared).IsAssignableFrom(i)); - var sharedShell = typeof(SharedObjectShellShell<>).MakeGenericType(generic); + var sharedShell = typeof(SharedObjectShellShell); var so = - Activator.CreateInstance(sharedShell, obj) as + Activator.CreateInstance(sharedShell, obj, context) as ISharedObjectShell; - if (context != null) - so.EndPointContext = context; writer.WriteStartArray(1); writer.WriteUInt64(so.Identifier.Flat); diff --git a/mROA.Cbor/PreParsedValue.cs b/mROA.Cbor/PreParsedValue.cs index 9737b7e..c1b1575 100644 --- a/mROA.Cbor/PreParsedValue.cs +++ b/mROA.Cbor/PreParsedValue.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using mROA.Abstract; using mROA.Implementation; @@ -34,6 +36,19 @@ namespace mROA.Cbor return so.UniversalValue; } + + if (type is { IsArray: true }) + { + var elementType = type.GetElementType(); + var array = Array.CreateInstance(elementType, _properties.Count); + Array.Copy(_properties.Select(i => Convert.ChangeType(i,elementType)).ToArray(), array, _properties.Count); + return array; + } + + + if (typeof(IList).IsAssignableFrom(type)) + return Convert.ChangeType(_properties.Select(i => Convert.ChangeType(i, type.GetElementType())).ToList(), type); + var instance = Activator.CreateInstance(type); if (instance == null) return null; diff --git a/mROA.Cbor/mROA.Cbor.csproj b/mROA.Cbor/mROA.Cbor.csproj index a2dbaf9..1950f08 100644 --- a/mROA.Cbor/mROA.Cbor.csproj +++ b/mROA.Cbor/mROA.Cbor.csproj @@ -3,6 +3,9 @@ netstandard2.1 enable + true + 2.0.2 + 9 diff --git a/mROA.Codegen/MethodRepo.cstmpl b/mROA.Codegen/MethodRepo.cstmpl index b1c4a93..3860e42 100644 --- a/mROA.Codegen/MethodRepo.cstmpl +++ b/mROA.Codegen/MethodRepo.cstmpl @@ -17,6 +17,7 @@ namespace mROA.Codegen new mROA.Implementation.AsyncMethodInvoker { IsVoid = , + IsTrusted = , ReturnType = typeof(), ParameterTypes = new Type[] { }, SuitableType = typeof(), @@ -26,6 +27,7 @@ namespace mROA.Codegen new mROA.Implementation.MethodInvoker { IsVoid = , + IsTrusted = , ReturnType = typeof(), ParameterTypes = new Type[] { }, SuitableType = typeof(), diff --git a/mROA.Codegen/RemoteEndpoint.cstmpl b/mROA.Codegen/RemoteEndpoint.cstmpl index 8a2541a..0ee69af 100644 --- a/mROA.Codegen/RemoteEndpoint.cstmpl +++ b/mROA.Codegen/RemoteEndpoint.cstmpl @@ -10,8 +10,8 @@ namespace partial class : RemoteObjectBase, { - public (int id, IRepresentationModule representationModule) - : base(id, representationModule) + public (int id, IRepresentationModule representationModule, IEndPointContext context) + : base(id, representationModule, context) { } diff --git a/mROA.Codegen/RemoteTypeBinder.cstmpl b/mROA.Codegen/RemoteTypeBinder.cstmpl index be2bf42..69eff9c 100644 --- a/mROA.Codegen/RemoteTypeBinder.cstmpl +++ b/mROA.Codegen/RemoteTypeBinder.cstmpl @@ -11,11 +11,11 @@ namespace mROA.Codegen public sealed class RemoteTypeBinder { static RemoteTypeBinder(){ - RemoteContextRepository.RemoteTypes = new Dictionary { + RemoteInstanceRepository.RemoteTypes = new Dictionary { , }; - ContextRepository.EventBinders = new object[] { + InstanceRepository.EventBinders = new object[] { new EventBinder<> @@ -39,7 +39,7 @@ namespace mROA.Codegen Parameters = new object[] { } }; - module.PostCallMessageAsync(request.Id, EMessageType.EventRequest, request); + module.PostCallMessageAsync(request.Id, EMessageType.EventRequest, request, context); }; } diff --git a/mROA.Codegen/mROA.Codegen.csproj b/mROA.Codegen/mROA.Codegen.csproj index 765f637..0b91fce 100644 --- a/mROA.Codegen/mROA.Codegen.csproj +++ b/mROA.Codegen/mROA.Codegen.csproj @@ -17,7 +17,7 @@ https://github.com/YaslePoy/mROA git True - 2.0.1 + 2.0.3 diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index 6329ab2..8e3fafc 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -291,23 +291,38 @@ namespace mROA.Codegen $"{method.ReturnType.ToUnityString()} {method.Name}({string.Join(", ", method.Parameters.Select(ToFullString))}){{"); + var isUntrusted = method.GetAttributes().Any(i => i.AttributeClass.Name == "UntrustedAttribute"); + var prefix = isAsync ? "await " : ""; var postfix = !isAsync ? isVoid ? ".Wait()" : ".GetAwaiter().GetResult()" : ""; var parameterLink = isParametrized ? ", new System.Object[] { " + string.Join(", ", parameters.Select(i => i.Name)) + " }" : string.Empty; - var tokenInsert = isAsync && method.Parameters.FirstOrDefault(i => i.Type.Name == "CancellationToken") is - { } tokenSymbol - ? ", cancellationToken : " + tokenSymbol.Name - : string.Empty; - var caller = isVoid - ? $"CallAsync({index}{parameterLink}{tokenInsert})" - : isAsync - ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" - : $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"; - if (!isVoid) - prefix = "return " + prefix; + string caller; + + if (isUntrusted) + { + caller = $"CallUntrustedAsync({index}{parameterLink})"; + } + else + { + var tokenInsert = isAsync && + method.Parameters.FirstOrDefault(i => i.Type.Name == "CancellationToken") is + { } tokenSymbol + ? ", cancellationToken : " + tokenSymbol.Name + : string.Empty; + + caller = isVoid + ? $"CallAsync({index}{parameterLink}{tokenInsert})" + : isAsync + ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" + : $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"; + + if (!isVoid) + prefix = "return " + prefix; + + } sb.AppendLine("\t\t\t" + prefix + caller + postfix + ";"); @@ -365,6 +380,7 @@ namespace mROA.Codegen invokerTemplate.AddDefine("parametersType", parameterTypes); invokerTemplate.AddDefine("suitableType", baseInterace.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", funcInvoking); + invokerTemplate.AddDefine("isTrusted", (!isUntrusted).ToString().ToLower()); backend = invokerTemplate.Compile(); } else @@ -375,6 +391,7 @@ namespace mROA.Codegen invokerTemplate.AddDefine("parametersType", parameterTypes); invokerTemplate.AddDefine("suitableType", baseInterace.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", funcInvoking); + invokerTemplate.AddDefine("isTrusted", (!isUntrusted).ToString().ToLower()); backend = invokerTemplate.Compile(); } @@ -394,9 +411,11 @@ namespace mROA.Codegen var parametersDeclaration = string.Join(", ", JoinWithComa(Enumerable.Range(0, parameters.Count).Select(i => "p" + i))); + + int pi = 0; var transferParameters = - JoinWithComa(parameters.Where(i => !ParameterFilterForType(i)) - .Select(i => "p" + parameters.IndexOf(i))); + JoinWithComa(parameters.Select(i => (i, pi++)).Where(i => !ParameterFilterForType(i.i)) + .Select(i => "p" + i.Item2)); var requestIndex = parameters.FindIndex(i => i.Name == "RequestContext"); if (requestIndex != -1) @@ -423,15 +442,16 @@ namespace mROA.Codegen { var level = "\t\t\t"; - var parameters = ((INamedTypeSymbol)eventSymbol.Type).TypeArguments; - var parsingParameters = parameters.RemoveAll(ParameterFilterForType).ToList(); + int pi = 0; + var parameters = ((INamedTypeSymbol)eventSymbol.Type).TypeArguments.Select(i => (i, pi++)).ToImmutableArray(); + var parsingParameters = parameters.RemoveAll(i => ParameterFilterForType(i.i)).ToList(); var parameterTypes = string.Join(", ", - $"{string.Join(", ", parsingParameters.Select(p => $"typeof({p.ToUnityString()})"))}"); + $"{string.Join(", ", parsingParameters.Select(p => $"typeof({p.i.ToUnityString()})"))}"); var parametersInsertList = new List(); foreach (var parameter in parameters) - switch (parameter.Name) + switch (parameter.i.Name) { case "CancellationToken": parametersInsertList.Add("(CancellationToken)special[1]"); @@ -440,8 +460,8 @@ namespace mROA.Codegen parametersInsertList.Add("special[0] as RequestContext"); break; default: - parametersInsertList.Add(Caster(parameter, - $"parameters[{parameters.IndexOf(parameter)}]")); + parametersInsertList.Add(Caster(parameter.i, + $"parameters[{parameter.Item2}]")); break; } @@ -459,6 +479,7 @@ namespace mROA.Codegen invokerTemplate.AddDefine("parametersType", parameterTypes); invokerTemplate.AddDefine("suitableType", baseInterface.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", funcInvoking); + invokerTemplate.AddDefine("isTrusted", "true"); var backend = invokerTemplate.Compile(); _methodRepoTemplate.Insert("invoker", backend); @@ -482,8 +503,8 @@ namespace mROA.Codegen var parameterTypes = string.Join(", ", $"{string.Join(", ", method.Parameters.Select(p => "typeof(" + p.Type.ToUnityString() + ")"))}"); var parameterInserts = string.Join(", ", - method.Parameters.Select( - p => Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"))); + method.Parameters.Select(p => + Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"))); var invokerTemplate = (TemplateDocument)_methodInvokerOriginal.Clone(); invokerTemplate.AddDefine("isVoid", "false"); @@ -492,6 +513,8 @@ namespace mROA.Codegen invokerTemplate.AddDefine("suitableType", baseInterace.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", $"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]"); + invokerTemplate.AddDefine("isTrusted", "true"); + backend = invokerTemplate.Compile(); } else @@ -502,6 +525,8 @@ namespace mROA.Codegen invokerTemplate.AddDefine("suitableType", baseInterace.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", $"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}"); + invokerTemplate.AddDefine("isTrusted", "true"); + backend = invokerTemplate.Compile(); } @@ -532,6 +557,8 @@ namespace mROA.Codegen invokerTemplate.AddDefine("suitableType", baseInterace.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", $"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}] = {valueInsert}"); + invokerTemplate.AddDefine("isTrusted", "true"); + backend = invokerTemplate.Compile(); } else @@ -545,6 +572,8 @@ namespace mROA.Codegen invokerTemplate.AddDefine("suitableType", baseInterace.ToUnityString()); invokerTemplate.AddDefine("funcInvoking", $"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")}"); + invokerTemplate.AddDefine("isTrusted", "true"); + backend = invokerTemplate.Compile(); } @@ -612,7 +641,6 @@ namespace mROA.Codegen return parts.ToUnityString(); return type.ToDisplayString(); - } public static string ToUnityString(this IParameterSymbol parameter) diff --git a/mROA.Test/CborTest.cs b/mROA.Test/CborTest.cs deleted file mode 100644 index cfa7d20..0000000 --- a/mROA.Test/CborTest.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using mROA.Cbor; - -namespace mROA.Test; - -public class CborTest -{ - private ComplexTestObject _complexTestObject; - private IContextualSerializationToolKit _serializationToolKit; - private BasicCollectionElement _basicCollectionElement; - - [SetUp] - public void Setup() - { - _basicCollectionElement = new() { A = 567565, B = "test text", C = 2.781f }; - - _complexTestObject = new ComplexTestObject - { - IntValue = 123, - DoubleValue = 3.14159, - StringValue = "abc", - EnumValue = TestEnum.X, - CollectionElements = - [ - _basicCollectionElement, - new BasicCollectionElement { A = 8_000_000, B = "Fi number", C = 1.618f } - ], - IntArray = [1, 4, 8, 16, 87] - }; - _serializationToolKit = new CborSerializationToolkit(); - } - - [Test] - public void BasicOnly() - { - var value = 123; - var data = _serializationToolKit.Serialize(value, null); - - var deserialize = _serializationToolKit.Deserialize(data, null); - Assert.That(value, Is.EqualTo(deserialize)); - } - - [Test] - public void ComplexFlat() - { - var value = _basicCollectionElement; - var data = _serializationToolKit.Serialize(value, null); - var deserialize = _serializationToolKit.Deserialize(data, null); - Assert.That(value, Is.EqualTo(deserialize)); - } - - [Test] - public void ComplexFull() - { - var value = _complexTestObject; - var data = _serializationToolKit.Serialize(value, null); - var deserialize = _serializationToolKit.Deserialize(data, null); - Assert.That(value, Is.EqualTo(deserialize)); - } - - public void SharedObject() - { - } - - - private class ComplexTestObject - { - public int IntValue { get; set; } - public double DoubleValue { get; set; } - public string StringValue { get; set; } - public TestEnum EnumValue { get; set; } - public int[] IntArray { get; set; } - public List CollectionElements { get; set; } - - protected bool Equals(ComplexTestObject other) - { - return IntValue == other.IntValue && DoubleValue.Equals(other.DoubleValue) && StringValue == other.StringValue && IntArray.SequenceEqual(other.IntArray) && CollectionElements.SequenceEqual(other.CollectionElements); - } - - public override bool Equals(object? obj) - { - if (obj is null) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((ComplexTestObject)obj); - } - - public override int GetHashCode() - { - return HashCode.Combine(IntValue, DoubleValue, StringValue, IntArray, CollectionElements); - } - } - - private class BasicCollectionElement - { - public int A { get; set; } - public string B { get; set; } - public float C { get; set; } - - protected bool Equals(BasicCollectionElement other) - { - return A == other.A && B == other.B && C.Equals(other.C); - } - - public override bool Equals(object? obj) - { - if (obj is null) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((BasicCollectionElement)obj); - } - - public override int GetHashCode() - { - return HashCode.Combine(A, B, C); - } - } - - public enum TestEnum - { - X = -5, Y, Z - } -} \ No newline at end of file diff --git a/mROA.Test/FrontendFinalTest.cs b/mROA.Test/FrontendFinalTest.cs deleted file mode 100644 index a765343..0000000 --- a/mROA.Test/FrontendFinalTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -// using System.Net; -// using System.Net.Sockets; -// using System.Reflection; -// using mROA.Abstract; -// using mROA.Codegen; -// using Example.Shared; -// using mROA.Implementation; -// -// namespace mROA.Test; -// -// public class FrontendFinalTest -// { -// private StreamBasedInteractionModule _interactionModule; -// private StreamBasedFrontendInteractionModule _frontendInteractionModule; -// private JsonFrontendSerialisationModule _frontendSerialisationModule; -// private ISerialisationModule _serialisationModule; -// private IExecuteModule _executeModule; -// private IMethodRepository _methodRepository; -// private IContextRepository _contextRepository; -// bool isTestNotFinished = true; -// private IContextRepository _frontendContextRepository; -// -// [SetUp] -// public void Setup() -// { -// _methodRepository = new CoCodegenMethodRepository(); -// var repo2 = new ContextRepository(); -// repo2.FillSingletons(typeof(ITestController).Assembly); -// _contextRepository = repo2; -// -// _interactionModule = new StreamBasedInteractionModule(); -// -// _serialisationModule = new JsonSerialisationModule(); -// -// _executeModule = new BasicExecutionModule(); -// -// IInjectableModule[] backendModules = -// [_methodRepository, _contextRepository, _interactionModule, _serialisationModule, _executeModule]; -// -// foreach (var backendModule in backendModules) -// foreach (var injection in backendModules) -// backendModule.Inject(injection); -// -// _frontendInteractionModule = new StreamBasedFrontendInteractionModule(); -// _frontendSerialisationModule = new JsonFrontendSerialisationModule(); -// _frontendContextRepository = new FrontendContextRepository(); -// -// IInjectableModule[] frontendModules = -// [_frontendInteractionModule, _frontendSerialisationModule, _frontendContextRepository]; -// -// foreach (var backendModule in frontendModules) -// foreach (var injection in frontendModules) -// backendModule.Inject(injection); -// -// Task.Run(() => -// { -// TcpListener listener = new TcpListener(IPAddress.Loopback, 4567); -// listener.Start(); -// -// var stream = listener.AcceptTcpClient().GetStream(); -// -// Console.WriteLine("Client connected"); -// -// _interactionModule.RegisterSourse(stream); -// -// while (isTestNotFinished) ; -// }); -// -// var tcpClient = new TcpClient(); -// tcpClient.Connect(IPAddress.Loopback, 4567); -// _frontendInteractionModule.ServerStream = tcpClient.GetStream(); -// } -// -// [Test] -// public void CallTest() -// { -// var singleton = _frontendContextRepository.GetSingleObject(typeof(ITestController)) as ITestController; -// -// var x = singleton.B(); -// Console.WriteLine(x); -// } -// -// [Test] -// public void TransmittionTest() -// { -// var singleton = _frontendContextRepository.GetSingleObject(typeof(ITestController)) as ITestController; -// -// var next = singleton.SharedObjectTransmitionTest().Value; -// var parameter = singleton.GetTestParameter().Value; -// var x = next.Parametrized(new TestParameter { A = 100, LinkedObject = new(parameter!) }); -// } -// } \ No newline at end of file diff --git a/mROA.Test/Identifier.cs b/mROA.Test/Identifier.cs new file mode 100644 index 0000000..6b929bc --- /dev/null +++ b/mROA.Test/Identifier.cs @@ -0,0 +1,23 @@ +using mROA.Implementation; + +namespace mROA.Test; + +[TestFixture] +public class Identifier +{ + [Test] + public void TestParse() + { + var id = new ComplexObjectIdentifier(-1, -1); + var flat = id.Flat; + var next = new ComplexObjectIdentifier { Flat = flat }; + if (id.Equals(next)) + { + Assert.Pass(); + } + else + { + Assert.Fail(); + } + } +} \ No newline at end of file diff --git a/mROA.Test/NextGenTest.cs b/mROA.Test/NextGenTest.cs deleted file mode 100644 index 0b708a7..0000000 --- a/mROA.Test/NextGenTest.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; -using mROA.Implementation; - -namespace mROA.Test -{ - public class NextGenTest - { - private TcpListener _listener; - private NextGenerationInteractionModule _interactionModuleA; - private NextGenerationInteractionModule _interactionModuleB; - private Guid[] guids = [Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()]; - - [SetUp] - public void Setup() - { - _listener = new TcpListener(IPAddress.Loopback, 4567); - _interactionModuleA = new NextGenerationInteractionModule(); - _interactionModuleA.Inject(new JsonSerializationToolkit()); - _interactionModuleB = new NextGenerationInteractionModule(); - _interactionModuleB.Inject(new JsonSerializationToolkit()); - - } - - [Test] - public void MultithreadedTest() - { - - Task.Run(() => - { - _listener.Start(); - _interactionModuleB.BaseStream = _listener.AcceptTcpClient().GetStream(); - - foreach (var guid in guids) - { - _interactionModuleB.PostMessageAsync(new NetworkMessageHeader { Id = guid, Data = "Hello user"u8.ToArray() }); - } - }); - - var client = new TcpClient(); - client.Connect(IPAddress.Loopback, 4567); - _interactionModuleA.BaseStream = client.GetStream(); - - var tasks = guids.Select(ReadStream); - - Task.WaitAll(tasks.ToArray()); - Assert.Pass(); - - } - - private async Task ReadStream(Guid current) - { - var msg = await _interactionModuleA.GetNextMessageReceiving(); - Console.WriteLine( - $"{Environment.CurrentManagedThreadId} Received message: {Encoding.Default.GetString(new JsonSerializationToolkit().Serialize(msg))}"); - while (msg.Id != current) - { - msg = await _interactionModuleA.GetNextMessageReceiving(); - Console.WriteLine( - $"{Environment.CurrentManagedThreadId} Received message: {Encoding.Default.GetString(new JsonSerializationToolkit().Serialize(msg))}"); - } - - Console.WriteLine($"{Environment.CurrentManagedThreadId} Good message received"); - } - - [TearDown] - public void TearDown() - { - _listener.Stop(); - _listener.Dispose(); - _interactionModuleA.Dispose(); - _interactionModuleB.Dispose(); - } - } -} \ No newline at end of file diff --git a/mROA.Test/StreamTest.cs b/mROA.Test/StreamTest.cs deleted file mode 100644 index 6a10e72..0000000 --- a/mROA.Test/StreamTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -// using System.Net; -// using System.Net.Sockets; -// using System.Text.Json; -// using mROA.Implementation; -// -// namespace mROA.Test; -// -// public class StreamTest -// { -// private StreamBasedInteractionModule _interactionModule; -// private StreamBasedFrontendInteractionModule _frontendInteractionModule; -// private JsonFrontendSerialisationModule _frontendSerialisationModule; -// private ISerialisationModule _serialisationModule; -// private IExecuteModule _executeModule; -// bool isTestNotFinished = true; -// -// [SetUp] -// public void Setup() -// { -// _interactionModule = new StreamBasedInteractionModule(); -// -// _serialisationModule = new JsonSerialisationModule(_interactionModule, new MockMethodRepository()); -// -// _executeModule = new MockExecModule(); -// _serialisationModule.SetExecuteModule(_executeModule); -// -// _frontendInteractionModule = new StreamBasedFrontendInteractionModule(); -// _frontendSerialisationModule = new JsonFrontendSerialisationModule(_frontendInteractionModule); -// -// Task.Run(() => -// { -// TcpListener listener = new TcpListener(IPAddress.Loopback, 4567); -// listener.Start(); -// -// var stream = listener.AcceptTcpClient().GetStream(); -// -// Console.WriteLine("Client connected"); -// -// _interactionModule.RegisterSourse(stream); -// -// while (isTestNotFinished) ; -// }); -// } -// -// [Test] -// public void StreamingTest() -// { -// var tcpClient = new TcpClient(); -// tcpClient.Connect(IPAddress.Loopback, 4567); -// _frontendInteractionModule.ServerStream = tcpClient.GetStream(); -// -// var req = new DefaultCallRequest { CommandId = 1, ObjectId = -1 }; -// _frontendSerialisationModule.PostCallRequest(req); -// var res = ((JsonElement)_frontendSerialisationModule -// .GetNextCommandExecution(req.CallRequestId).GetAwaiter().GetResult().Result!) -// .Deserialize(); -// isTestNotFinished = false; -// Assert.That(res.A == "wqer" && res.B == 5); -// } -// -// [Test] -// public void RemoteObjectTest() -// { -// } -// } \ No newline at end of file diff --git a/mROA.Test/UnSOization.cs b/mROA.Test/UnSOization.cs deleted file mode 100644 index d30c025..0000000 --- a/mROA.Test/UnSOization.cs +++ /dev/null @@ -1,26 +0,0 @@ -using mROA.Implementation; - -namespace mROA.Test; - -public class UnSOization -{ - private ComplexObjectIdentifier _uoi; - - [SetUp] - public void Setup() - { - _uoi = new ComplexObjectIdentifier - { - ContextId = -123, OwnerId = 123 - }; - } - - [Test] - public void FlatTest() - { - var flat = _uoi.Flat; - var next = new ComplexObjectIdentifier { Flat = flat }; - - Assert.That(_uoi, Is.EqualTo(next)); - } -} \ No newline at end of file diff --git a/mROA.Test/UnitTest1.cs b/mROA.Test/UnitTest1.cs deleted file mode 100644 index 78a3e38..0000000 --- a/mROA.Test/UnitTest1.cs +++ /dev/null @@ -1,154 +0,0 @@ -// using System.Diagnostics; -// using System.Reflection; -// using System.Text; -// using System.Text.Json; -// using Example.Shared; -// using mROA.Implementation; -// using Newtonsoft.Json; -// using JsonSerializer = System.Text.Json.JsonSerializer; -// -// namespace mROA.Test; -// -// public class Tests -// { -// private ProgramlyInteractionChanel _interactionModule; -// private ISerialisationModule _serialisationModule; -// private IExecuteModule _executeModule; -// private IMethodRepository _methodRepository; -// private IContextRepository _contextRepository; -// -// private ITestController _testController; -// -// [SetUp] -// public void Setup() -// { -// _interactionModule = new ProgramlyInteractionChanel(); -// var repo = new MethodRepository(); -// repo.CollectForAssembly(Assembly.GetExecutingAssembly()); -// _methodRepository = repo; -// var repo2 = new ContextRepository(); -// repo2.FillSingletons(Assembly.GetExecutingAssembly()); -// _contextRepository = repo2; -// _serialisationModule = new JsonSerialisationModule(_interactionModule, _methodRepository); -// -// _executeModule = new LaunchReadyExecutionModule(_methodRepository, _serialisationModule, _contextRepository); -// TransmissionConfig.DefaultContextRepository = _contextRepository; -// } -// -// [Test] -// public void CommandPipelineTest() -// { -// var sw = Stopwatch.StartNew(); -// -// _interactionModule.PassCommand(132, """ -// { -// "RequestTypeId": 0, -// "CommandId": 2 -// } -// """u8.ToArray()); -// Assert.Pass(_interactionModule.OutputBuffer.Last()); -// } -// -// [Test] -// public void CommandPipelineTestAsync() -// { -// var sw = Stopwatch.StartNew(); -// _interactionModule.PassCommand(132, """ -// { -// "RequestTypeId": 0, -// "CommandId": 3 -// } -// """u8.ToArray()); -// while (_interactionModule.OutputBuffer.Count != 2) ; -// -// Assert.Pass(_interactionModule.OutputBuffer.Last()); -// } -// -// [Test] -// public void MethodRegistrationTest() -// { -// var repo = new MethodRepository(); -// repo.CollectForAssembly(Assembly.GetExecutingAssembly()); -// Assert.That(repo.GetMethods().ToList().Count == 8); -// } -// -// [Test] -// public void ContextSupplyTest() -// { -// var repo = new ContextRepository(); -// repo.FillSingletons(Assembly.GetExecutingAssembly()); -// var singleObject = repo.GetSingleObject(typeof(ITestController)) as ITestController; -// singleObject.B(); -// Assert.That(singleObject.B() == 6); -// } -// -// [Test] -// public void TransmissionTest() -// { -// _interactionModule.PassCommand(132, """ -// { -// "CommandId": 4 -// } -// """u8.ToArray()); -// var response = -// JsonSerializer.Deserialize>( -// JsonSerializer.Deserialize(_interactionModule.OutputBuffer.Last()) -// ?.Result.ToString() -// ); -// -// _interactionModule.PassCommand(132, Encoding.UTF8.GetBytes( -// JsonSerializer.Serialize(new DefaultCallRequest { CommandId = 2, ObjectId = response.ContextId }))); -// -// var firstFull = JsonDocument.Parse(_interactionModule.OutputBuffer.Last()).RootElement.GetProperty("Result") -// .GetInt32(); -// -// _interactionModule.PassCommand(132, Encoding.UTF8.GetBytes( -// JsonSerializer.Serialize(new DefaultCallRequest { CommandId = 4, ObjectId = response.ContextId }))); -// -// response = -// JsonSerializer.Deserialize>( -// JsonSerializer.Deserialize(_interactionModule.OutputBuffer.Last()) -// ?.Result.ToString() -// ); -// -// _interactionModule.PassCommand(132, Encoding.UTF8.GetBytes( -// JsonSerializer.Serialize(new DefaultCallRequest { CommandId = 2, ObjectId = response.ContextId }))); -// _interactionModule.PassCommand(132, Encoding.UTF8.GetBytes( -// JsonSerializer.Serialize(new DefaultCallRequest { CommandId = 2, ObjectId = response.ContextId }))); -// -// var secondFull = JsonDocument.Parse(_interactionModule.OutputBuffer.Last()).RootElement.GetProperty("Result") -// .GetInt32(); -// -// Assert.That(firstFull == 456789 && secondFull == 6); -// } -// -// [Test] -// public void LinkedObjectsAndParametersTest() -// { -// _interactionModule.PassCommand(132, """ -// { -// "CommandId": 6 -// } -// """u8.ToArray()); -// var response = -// JsonSerializer.Deserialize>( -// JsonSerializer.Deserialize(_interactionModule.OutputBuffer.Last()) -// ?.Result.ToString() -// ); -// var x = response.ContextId; -// _interactionModule.PassCommand(132,Encoding.UTF8.GetBytes( -// JsonSerializer.Serialize(new DefaultCallRequest -// { -// CommandId = 5, -// Parameter = new TestParameter -// { -// A = 10, -// LinkedObject = new TransmittedSharedObject { ContextId = x } -// } -// }))); -// -// var finalResponse = JsonDocument.Parse(_interactionModule.OutputBuffer.Last()).RootElement.GetProperty("Result").GetInt32(); -// -// Assert.That(finalResponse, Is.EqualTo(20)); -// } -// } \ No newline at end of file diff --git a/mROA.sln b/mROA.sln index 2c090e8..40a367e 100644 --- a/mROA.sln +++ b/mROA.sln @@ -23,8 +23,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Cbor", "mROA.Cbor\mROA EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TotalDemo", "TotalDemo", "{FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Codegen", "Codegen", "{3DB22457-E65B-426F-B3DD-08C615132B3E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -72,6 +70,5 @@ Global {A9BB364E-0BA6-40B9-A293-757BC48EFC06} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} {9BD25A13-3165-47C0-9EAA-5C59EC490E32} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} {E7703F5B-F2A6-4A88-AA57-EBB1E38D533E} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} - {3DB22457-E65B-426F-B3DD-08C615132B3E} = {EAE92F5A-664C-41AB-8811-5885524B5347} EndGlobalSection EndGlobal diff --git a/mROA/Abstract/IChannelInteractionModule.cs b/mROA/Abstract/IChannelInteractionModule.cs new file mode 100644 index 0000000..2c59de3 --- /dev/null +++ b/mROA/Abstract/IChannelInteractionModule.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Channels; +using System.Threading.Tasks; +using mROA.Implementation; + +namespace mROA.Abstract +{ + public interface IChannelInteractionModule : IInjectableModule, IDisposable + { + int ConnectionId { get; set; } + Channel ReceiveChanel { get; } + ChannelReader TrustedPostChanel { get; } + ChannelReader UntrustedPostChanel { get; } + Func IsConnected { get; set; } + ValueTask GetNextMessageReceiving(bool infinite = true); + Task PostMessageAsync(NetworkMessageHeader messageHeader); + Task PostMessageUntrustedAsync(NetworkMessageHeader messageHeader); + event Action OnDisconnected; + Task Restart(bool sendRecovery); + } +} \ No newline at end of file diff --git a/mROA/Abstract/IConnectionHub.cs b/mROA/Abstract/IConnectionHub.cs index 3c6afd4..01f60b9 100644 --- a/mROA/Abstract/IConnectionHub.cs +++ b/mROA/Abstract/IConnectionHub.cs @@ -6,8 +6,8 @@ public interface IConnectionHub : IInjectableModule { - void RegisterInteraction(INextGenerationInteractionModule interaction); - INextGenerationInteractionModule GetInteraction(int id); + void RegisterInteraction(IChannelInteractionModule interaction); + IChannelInteractionModule GetInteraction(int id); event ConnectionHandler? OnConnected; event DisconnectionHandler? OnDisconnected; } diff --git a/mROA/Abstract/IContextRepository.cs b/mROA/Abstract/IContextRepository.cs deleted file mode 100644 index 920a317..0000000 --- a/mROA/Abstract/IContextRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using mROA.Implementation; - -namespace mROA.Abstract -{ - public interface IContextRepository : IInjectableModule - { - int HostId { get; set; } - int ResisterObject(object o, IEndPointContext context); - void ClearObject(ComplexObjectIdentifier id); - T GetObject(ComplexObjectIdentifier id); - object GetSingleObject(Type type, int ownerId); - int GetObjectIndex(object o, IEndPointContext context); - } -} \ No newline at end of file diff --git a/mROA/Abstract/IContextRepositoryHub.cs b/mROA/Abstract/IContextRepositoryHub.cs index 19a7987..ca2e205 100644 --- a/mROA/Abstract/IContextRepositoryHub.cs +++ b/mROA/Abstract/IContextRepositoryHub.cs @@ -2,7 +2,7 @@ namespace mROA.Abstract { public interface IContextRepositoryHub { - IContextRepository GetRepository(int clientId); + IInstanceRepository GetRepository(int clientId); void FreeRepository(int clientId); } } \ No newline at end of file diff --git a/mROA.Cbor/IContextualSerializationToolKit.cs b/mROA/Abstract/IContextualSerializationToolKit.cs similarity index 85% rename from mROA.Cbor/IContextualSerializationToolKit.cs rename to mROA/Abstract/IContextualSerializationToolKit.cs index 957ca29..6cfa9ac 100644 --- a/mROA.Cbor/IContextualSerializationToolKit.cs +++ b/mROA/Abstract/IContextualSerializationToolKit.cs @@ -1,9 +1,8 @@ using System; -using mROA.Abstract; -namespace mROA.Cbor +namespace mROA.Abstract { - public interface IContextualSerializationToolKit : ISerializationToolkit + public interface IContextualSerializationToolKit : IInjectableModule { byte[] Serialize(object objectToSerialize, IEndPointContext? context); void Serialize(object objectToSerialize, Span destination, IEndPointContext? context); diff --git a/mROA/Abstract/IEndPointContext.cs b/mROA/Abstract/IEndPointContext.cs index 0267321..8b5db20 100644 --- a/mROA/Abstract/IEndPointContext.cs +++ b/mROA/Abstract/IEndPointContext.cs @@ -1,10 +1,10 @@ namespace mROA.Abstract { - public interface IEndPointContext + public interface IEndPointContext : IInjectableModule { - IContextRepository RealRepository { get; } - IContextRepository RemoteRepository { get; } - int HostId { get; } - int OwnerId { get; } + IInstanceRepository RealRepository { get; } + IInstanceRepository RemoteRepository { get; } + int HostId { get; set; } + int OwnerId { get; set; } } } \ No newline at end of file diff --git a/mROA/Abstract/IEventBinder.cs b/mROA/Abstract/IEventBinder.cs index 737bfd2..51b11a5 100644 --- a/mROA/Abstract/IEventBinder.cs +++ b/mROA/Abstract/IEventBinder.cs @@ -1,8 +1,20 @@ namespace mROA.Abstract { - public interface IEventBinder + public interface IEventBinder : IEventBinder { public void BindEvents(T source, IEndPointContext context, IRepresentationModuleProducer representationModuleProducer, int index); + + void IEventBinder.BindEvents(object source, IEndPointContext context, IRepresentationModuleProducer representationModuleProducer, + int index) + { + BindEvents((T)source, context, representationModuleProducer, index); + } + } + + public interface IEventBinder + { + public void BindEvents(object source, IEndPointContext context, + IRepresentationModuleProducer representationModuleProducer, int index); } } \ No newline at end of file diff --git a/mROA/Abstract/IExecuteModule.cs b/mROA/Abstract/IExecuteModule.cs index 3ae2d13..ea87f0d 100644 --- a/mROA/Abstract/IExecuteModule.cs +++ b/mROA/Abstract/IExecuteModule.cs @@ -4,7 +4,7 @@ namespace mROA.Abstract { public interface IExecuteModule : IInjectableModule { - ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository, - IRepresentationModule representationModule); + ICommandExecution Execute(ICallRequest command, IInstanceRepository instanceRepository, + IRepresentationModule representationModule, IEndPointContext context); } } \ No newline at end of file diff --git a/mROA/Abstract/IFrontendBridge.cs b/mROA/Abstract/IFrontendBridge.cs index 53217d7..7ce7460 100644 --- a/mROA/Abstract/IFrontendBridge.cs +++ b/mROA/Abstract/IFrontendBridge.cs @@ -1,10 +1,11 @@ using System; +using System.Threading.Tasks; namespace mROA.Abstract { public interface IFrontendBridge : IInjectableModule, IDisposable { - void Connect(); + Task Connect(); void Obstacle(); void Disconnect(); } diff --git a/mROA/Abstract/IInstanceRepository.cs b/mROA/Abstract/IInstanceRepository.cs new file mode 100644 index 0000000..53d949d --- /dev/null +++ b/mROA/Abstract/IInstanceRepository.cs @@ -0,0 +1,15 @@ +using System; +using mROA.Implementation; + +namespace mROA.Abstract +{ + public interface IInstanceRepository : IInjectableModule + { + int ResisterObject(object o, IEndPointContext context); + void ClearObject(ComplexObjectIdentifier id, IEndPointContext context); + T GetObject(ComplexObjectIdentifier id, IEndPointContext context); + T GetSingletonObject(IEndPointContext context) where T : class, IShared; + object GetSingletonObject(Type type, IEndPointContext context); + int GetObjectIndex(object o, IEndPointContext context); + } +} \ No newline at end of file diff --git a/mROA/Abstract/IInteractionModule.cs b/mROA/Abstract/IInteractionModule.cs deleted file mode 100644 index d797ae0..0000000 --- a/mROA/Abstract/IInteractionModule.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using mROA.Implementation; - -namespace mROA.Abstract -{ - public interface INextGenerationInteractionModule : IInjectableModule, IDisposable - { - int ConnectionId { get; set; } - public Stream? BaseStream { get; set; } - Task GetNextMessageReceiving(bool infinite = true); - Task PostMessageAsync(NetworkMessageHeader messageHeader); - void HandleMessage(NetworkMessageHeader messageHeader); - NetworkMessageHeader[] UnhandledMessages { get; } - NetworkMessageHeader? FirstByFilter(Predicate predicate); - event Action OnDisconected; - Task Restart(bool sendRecovery); - } -} \ No newline at end of file diff --git a/mROA/Abstract/IMethodInvoker.cs b/mROA/Abstract/IMethodInvoker.cs index dcb04db..f151cae 100644 --- a/mROA/Abstract/IMethodInvoker.cs +++ b/mROA/Abstract/IMethodInvoker.cs @@ -5,6 +5,7 @@ namespace mROA.Abstract public interface IMethodInvoker { bool IsVoid { get; } + bool IsTrusted { get; } Type[] ParameterTypes { get; } Type? ReturnType { get; } Type SuitableType { get; } diff --git a/mROA/Abstract/IRemoteObjectFactory.cs b/mROA/Abstract/IRemoteObjectFactory.cs index e68fb0f..1ae127f 100644 --- a/mROA/Abstract/IRemoteObjectFactory.cs +++ b/mROA/Abstract/IRemoteObjectFactory.cs @@ -4,6 +4,6 @@ namespace mROA.Abstract { public interface IRemoteObjectFactory : IInjectableModule { - T Produce(ComplexObjectIdentifier id); + T Produce(ComplexObjectIdentifier id, IEndPointContext context); } } \ No newline at end of file diff --git a/mROA/Abstract/IRepresentationModule.cs b/mROA/Abstract/IRepresentationModule.cs new file mode 100644 index 0000000..101b399 --- /dev/null +++ b/mROA/Abstract/IRepresentationModule.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using mROA.Implementation; + +namespace mROA.Abstract +{ + public interface IRepresentationModule : IInjectableModule + { + int Id { get; } + + Task<(object? Deserialized, EMessageType MessageType)> GetSingle(Predicate rule, + IEndPointContext? context, CancellationToken token = default, + params Func[] converter); + + IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream(Predicate rule, + IEndPointContext? context, CancellationToken token = default, + params Func[] converter); + + Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) + where T : notnull; + + void PostCallMessage(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) + where T : notnull; + + Task PostCallMessageUntrustedAsync(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) + where T : notnull; + } +} \ No newline at end of file diff --git a/mROA/Abstract/ISerialisationModule.cs b/mROA/Abstract/ISerialisationModule.cs deleted file mode 100644 index 4cea03f..0000000 --- a/mROA/Abstract/ISerialisationModule.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using mROA.Implementation; - -namespace mROA.Abstract -{ - public interface IRepresentationModule : IInjectableModule - { - int Id { get; } - - Task GetMessageAsync(Guid? requestId = null, EMessageType? messageType = null, - CancellationToken token = default); - - T GetMessage(Guid? requestId = null, EMessageType? messageType = null); - - Task GetRawMessage(Guid? requestId = null, EMessageType? messageType = null, - CancellationToken token = default); - - Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull; - Task PostCallMessageAsync(Guid id, EMessageType eMessageType, object payload, Type payloadType); - void PostCallMessage(Guid id, EMessageType eMessageType, T payload) where T : notnull; - void PostCallMessage(Guid id, EMessageType eMessageType, object payload, Type payloadType); - } -} \ No newline at end of file diff --git a/mROA/Abstract/ISerializationToolkit.cs b/mROA/Abstract/ISerializationToolkit.cs index 497c953..6761e10 100644 --- a/mROA/Abstract/ISerializationToolkit.cs +++ b/mROA/Abstract/ISerializationToolkit.cs @@ -1,16 +1,14 @@ -using System; - -namespace mROA.Abstract +namespace mROA.Abstract { - public interface ISerializationToolkit : IInjectableModule - { - byte[] Serialize(T objectToSerialize); - byte[] Serialize(object objectToSerialize, Type type); - T? Deserialize(byte[] rawData); - object? Deserialize(byte[] rawData, Type type); - T? Deserialize(Span rawData); - object? Deserialize(Span rawData, Type type); - T? Cast(object? nonCasted); - object? Cast(object? nonCasted, Type type); - } + // public interface IContextualSerializationToolKit : IInjectableModule + // { + // byte[] Serialize(T objectToSerialize); + // byte[] Serialize(object objectToSerialize, Type type); + // T? Deserialize(byte[] rawData); + // object? Deserialize(byte[] rawData, Type type); + // T? Deserialize(Span rawData); + // object? Deserialize(Span rawData, Type type); + // T? Cast(object? nonCasted); + // object? Cast(object? nonCasted, Type type); + // } } \ No newline at end of file diff --git a/mROA/Abstract/IUntrustedGateway.cs b/mROA/Abstract/IUntrustedGateway.cs new file mode 100644 index 0000000..1c6f25c --- /dev/null +++ b/mROA/Abstract/IUntrustedGateway.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace mROA.Abstract +{ + public interface IUntrustedGateway : IInjectableModule, IDisposable + { + Task Start(); + } +} \ No newline at end of file diff --git a/mROA/Abstract/IUntrustedInteractionModule.cs b/mROA/Abstract/IUntrustedInteractionModule.cs new file mode 100644 index 0000000..151902b --- /dev/null +++ b/mROA/Abstract/IUntrustedInteractionModule.cs @@ -0,0 +1,11 @@ +using System; +using System.Net; +using System.Threading.Tasks; + +namespace mROA.Abstract +{ + public interface IUntrustedInteractionModule : IInjectableModule, IDisposable + { + Task Start(IPEndPoint endpoint); + } +} \ No newline at end of file diff --git a/mROA/Implementation/Attributes/UntrustedAttribute.cs b/mROA/Implementation/Attributes/UntrustedAttribute.cs new file mode 100644 index 0000000..2441590 --- /dev/null +++ b/mROA/Implementation/Attributes/UntrustedAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace mROA.Implementation.Attributes +{ + public class UntrustedAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/mROA/Implementation/Backend/BackendIdentityGenerator.cs b/mROA/Implementation/Backend/BackendIdentityGenerator.cs index dce88be..bf9fcdb 100644 --- a/mROA/Implementation/Backend/BackendIdentityGenerator.cs +++ b/mROA/Implementation/Backend/BackendIdentityGenerator.cs @@ -8,7 +8,7 @@ namespace mROA.Implementation.Backend public int GetNextIdentity() { - return ++_currentId; + return -++_currentId; } public void Inject(T dependency) diff --git a/mROA/Implementation/Backend/BasicConfigurationExtensions.cs b/mROA/Implementation/Backend/BasicConfigurationExtensions.cs index b8e0f7c..a10bdd3 100644 --- a/mROA/Implementation/Backend/BasicConfigurationExtensions.cs +++ b/mROA/Implementation/Backend/BasicConfigurationExtensions.cs @@ -21,9 +21,8 @@ namespace mROA.Implementation.Backend public static void UseCollectableContextRepository(this FullMixBuilder builder, params Assembly[] assemblies) { - var repo = new ContextRepository(); + var repo = new InstanceRepository(); repo.FillSingletons(assemblies); - TransmissionConfig.RealContextRepository = repo; builder.Modules.Add(repo); } diff --git a/mROA/Implementation/Backend/BasicExecutionModule.cs b/mROA/Implementation/Backend/BasicExecutionModule.cs index f184d20..0387bf1 100644 --- a/mROA/Implementation/Backend/BasicExecutionModule.cs +++ b/mROA/Implementation/Backend/BasicExecutionModule.cs @@ -9,7 +9,7 @@ namespace mROA.Implementation.Backend { private ICancellationRepository? _cancellationRepo; private IMethodRepository? _methodRepo; - private ISerializationToolkit? _serialization; + private IContextualSerializationToolKit? _serialization; public void Inject(T dependency) { @@ -21,27 +21,21 @@ namespace mROA.Implementation.Backend case ICancellationRepository cancellationRepo: _cancellationRepo = cancellationRepo; break; - case ISerializationToolkit serializationToolkit: + case IContextualSerializationToolKit serializationToolkit: _serialization = serializationToolkit; break; } } - public ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository, - IRepresentationModule representationModule) + public ICommandExecution Execute(ICallRequest command, IInstanceRepository instanceRepository, + IRepresentationModule representationModule, IEndPointContext endPointContext) { -#if TRACE - Console.WriteLine(command.GetType().Name); -#endif try { - ThrowIfNotInjected(contextRepository); + ThrowIfNotInjected(instanceRepository); if (command is CancelRequest) { -#if TRACE - Console.WriteLine("Final cancelling request"); -#endif return CancelExecution(command); } @@ -49,7 +43,7 @@ namespace mROA.Implementation.Backend if (invoker == null) throw new Exception($"Command {command.CommandId} not found"); - var context = GetContext(command, contextRepository, invoker); + var context = GetContext(command, instanceRepository, invoker, endPointContext); if (context == null) throw new NullReferenceException("Instance can't be null"); @@ -58,7 +52,7 @@ namespace mROA.Implementation.Backend object?[]? castedParams = null; if (invoker.ParameterTypes.Length != 0) - castedParams = CastedParams(command, invoker); + castedParams = CastedParams(command, invoker, endPointContext); var execContext = new RequestContext(command.Id, representationModule.Id); @@ -68,18 +62,15 @@ namespace mROA.Implementation.Backend case AsyncMethodInvoker { IsVoid: false } asyncNonVoidMethodInvoker: return TypedExecuteAsync(asyncNonVoidMethodInvoker, context, castedParams, command, _cancellationRepo!, - representationModule, execContext); + representationModule, execContext, endPointContext); case AsyncMethodInvoker asyncMethodInvoker: return ExecuteAsync(asyncMethodInvoker, context, castedParams, command, _cancellationRepo!, - representationModule, execContext); + representationModule, execContext, endPointContext); default: var result = Execute((invoker as MethodInvoker)!, context, castedParams!, command, execContext); if (command.CommandId == -1) { -#if TRACE - Console.WriteLine("Disposing object"); -#endif - contextRepository.ClearObject(command.ObjectId); + instanceRepository.ClearObject(command.ObjectId, endPointContext); } return result; @@ -95,26 +86,27 @@ namespace mROA.Implementation.Backend } } - private static object GetContext(ICallRequest command, IContextRepository contextRepository, IMethodInvoker invoker) + private static object GetContext(ICallRequest command, IInstanceRepository instanceRepository, + IMethodInvoker invoker, IEndPointContext endPointContext) { var context = command.ObjectId.ContextId != -1 - ? contextRepository.GetObject(command.ObjectId) - : contextRepository.GetSingleObject(invoker.SuitableType, command.ObjectId.OwnerId); + ? instanceRepository.GetObject(command.ObjectId, endPointContext) + : instanceRepository.GetSingletonObject(invoker.SuitableType, endPointContext); return context; } - private object?[] CastedParams(ICallRequest command, IMethodInvoker invoker) + private object?[] CastedParams(ICallRequest command, IMethodInvoker invoker, IEndPointContext context) { object?[] castedParams = new object[invoker.ParameterTypes.Length]; for (var i = 0; i < castedParams.Length; i++) { - castedParams[i] = _serialization!.Cast(command.Parameters![i], invoker.ParameterTypes[i]); + castedParams[i] = _serialization!.Cast(command.Parameters![i], invoker.ParameterTypes[i], context); } return castedParams; } - private void ThrowIfNotInjected(IContextRepository contextRepository) + private void ThrowIfNotInjected(IInstanceRepository instanceRepository) { if (_cancellationRepo is null) throw new NullReferenceException("Method repository was not defined"); @@ -122,7 +114,7 @@ namespace mROA.Implementation.Backend if (_methodRepo is null) throw new NullReferenceException("Method repository was not defined"); - if (contextRepository is null) + if (instanceRepository is null) throw new NullReferenceException("Context repository was not defined"); } @@ -147,6 +139,11 @@ namespace mROA.Implementation.Backend { var finalResult = invoker.Invoke(instance, parameter, new object[] { executionContext }); + if (!invoker.IsTrusted) + { + return new AsyncCommandExecution(); + } + if (invoker.IsVoid) { return new FinalCommandExecution @@ -163,24 +160,27 @@ namespace mROA.Implementation.Backend } catch (Exception e) { - return new ExceptionCommandExecution + if (invoker.IsTrusted) + return new ExceptionCommandExecution + { + Id = command.Id, + Exception = e.ToString() + }; + return new AsyncCommandExecution { - Id = command.Id, - Exception = e.ToString() + Id = command.Id }; } } private ICommandExecution ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, ICallRequest command, ICancellationRepository cancellationRepository, - IRepresentationModule representationModule, RequestContext executionContext) + IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context) { var tokenSource = new CancellationTokenSource(); cancellationRepository.RegisterCancellation(command.Id, tokenSource); var token = tokenSource.Token; -#if TRACE - token.Register(() => Console.WriteLine($"Cancellation requested check {command.Id}")); -#endif + try { invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ => @@ -194,12 +194,10 @@ namespace mROA.Implementation.Backend }; _cancellationRepo?.FreeCancelation(command.Id); - var multiClientOwnershipRepository = - TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; - - multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); - representationModule.PostCallMessage(command.Id, EMessageType.FinishedCommandExecution, payload); - multiClientOwnershipRepository?.FreeOwnership(); + + if (invoker.IsTrusted) + representationModule.PostCallMessage(command.Id, EMessageType.FinishedCommandExecution, + payload, context); }); return new AsyncCommandExecution @@ -209,17 +207,22 @@ namespace mROA.Implementation.Backend } catch (Exception e) { - return new ExceptionCommandExecution + if (invoker.IsTrusted) + return new ExceptionCommandExecution + { + Id = command.Id, + Exception = e.ToString() + }; + return new AsyncCommandExecution { - Id = command.Id, - Exception = e.ToString() + Id = command.Id }; } } private ICommandExecution TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, ICallRequest command, ICancellationRepository cancellationRepository, - IRepresentationModule representationModule, RequestContext executionContext) + IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context) { var tokenSource = new CancellationTokenSource(); cancellationRepository.RegisterCancellation(command.Id, tokenSource); @@ -236,13 +239,9 @@ namespace mROA.Implementation.Backend Result = finalResult }; _cancellationRepo!.FreeCancelation(command.Id); - - var multiClientOwnershipRepository = - TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; - multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); + representationModule.PostCallMessage(command.Id, EMessageType.FinishedCommandExecution, - payload); - multiClientOwnershipRepository?.FreeOwnership(); + payload, context); }); return new AsyncCommandExecution diff --git a/mROA/Implementation/Backend/ConnectionHub.cs b/mROA/Implementation/Backend/ConnectionHub.cs index de302b0..ddaf694 100644 --- a/mROA/Implementation/Backend/ConnectionHub.cs +++ b/mROA/Implementation/Backend/ConnectionHub.cs @@ -6,10 +6,10 @@ namespace mROA.Implementation.Backend { public class ConnectionHub : IConnectionHub { - private readonly Dictionary _connections = new(); - private ISerializationToolkit? _serializationToolkit; + private readonly Dictionary _connections = new(); + private IContextualSerializationToolKit? _serializationToolkit; - public void RegisterInteraction(INextGenerationInteractionModule interaction) + public void RegisterInteraction(IChannelInteractionModule interaction) { if (_serializationToolkit is null) throw new NullReferenceException("Serialization toolkit is null"); @@ -21,9 +21,9 @@ namespace mROA.Implementation.Backend OnConnected?.Invoke(module); } - public INextGenerationInteractionModule GetInteraction(int id) + public IChannelInteractionModule GetInteraction(int id) { - return _connections!.GetValueOrDefault(id, null) ?? throw new Exception("No connection found"); + return _connections!.GetValueOrDefault(id, null) ?? _connections!.GetValueOrDefault(-id, null) ?? throw new Exception("No connection found"); } public event ConnectionHandler? OnConnected; @@ -31,7 +31,7 @@ namespace mROA.Implementation.Backend public void Inject(T dependency) { - if (dependency is ISerializationToolkit serializationToolkit) + if (dependency is IContextualSerializationToolKit serializationToolkit) _serializationToolkit = serializationToolkit; } } diff --git a/mROA/Implementation/Backend/HubRequestExtractor.cs b/mROA/Implementation/Backend/HubRequestExtractor.cs index 71171de..7a51339 100644 --- a/mROA/Implementation/Backend/HubRequestExtractor.cs +++ b/mROA/Implementation/Backend/HubRequestExtractor.cs @@ -1,5 +1,5 @@ -using System; using mROA.Abstract; +using mROA.Implementation.Frontend; namespace mROA.Implementation.Backend { @@ -7,17 +7,11 @@ namespace mROA.Implementation.Backend { private IConnectionHub? _hub; - private IContextRepository? _contextRepository; - private IContextRepository? _remoteContextRepository; + private IInstanceRepository? _contextRepository; + private IInstanceRepository? _remoteContextRepository; private IMethodRepository? _methodRepository; - private ISerializationToolkit? _serializationToolkit; + private IContextualSerializationToolKit? _serializationToolkit; private IExecuteModule? _executeModule; - private readonly Type _extractorType; - - public HubRequestExtractor(Type extractorType) - { - _extractorType = extractorType; - } public void Inject(T dependency) { @@ -27,17 +21,17 @@ namespace mROA.Implementation.Backend _hub = connectionHub; _hub.OnConnected += HubOnOnConnected; break; - case MultiClientContextRepository: - case ContextRepository: - _contextRepository = dependency as IContextRepository; + case MultiClientInstanceRepository: + case InstanceRepository: + _contextRepository = dependency as IInstanceRepository; break; - case RemoteContextRepository remoteContextRepository: + case RemoteInstanceRepository remoteContextRepository: _remoteContextRepository = remoteContextRepository; break; case IMethodRepository methodRepository: _methodRepository = methodRepository; break; - case ISerializationToolkit serializationToolkit: + case IContextualSerializationToolKit serializationToolkit: _serializationToolkit = serializationToolkit; break; case IExecuteModule executeModule: @@ -49,7 +43,7 @@ namespace mROA.Implementation.Backend private void HubOnOnConnected(IRepresentationModule interaction) { var extractor = CreateExtractor(interaction); - extractor.StartExtraction().ContinueWith(t => OnDisconnected(interaction)); + extractor.StartExtraction().ContinueWith(_ => OnDisconnected(interaction)); } private void OnDisconnected(IRepresentationModule representationModule) @@ -60,16 +54,23 @@ namespace mROA.Implementation.Backend private IRequestExtractor CreateExtractor(IRepresentationModule interaction) { - var extractor = (IRequestExtractor)Activator.CreateInstance(_extractorType)!; + var extractor = new RequestExtractor(); + var context = new EndPointContext + { + HostId = 0, OwnerId = -interaction.Id + }; extractor.Inject(interaction); if (_contextRepository is IContextRepositoryHub contextHub) - extractor.Inject(contextHub.GetRepository(interaction.Id)); + context.RealRepository = contextHub.GetRepository(interaction.Id); else - extractor.Inject(interaction); + context.RealRepository = _contextRepository!; + + context.RemoteRepository = _remoteContextRepository!; + + extractor.Inject(context); extractor.Inject(_methodRepository); extractor.Inject(_serializationToolkit); extractor.Inject(_executeModule); - extractor.Inject(_remoteContextRepository); return extractor; } } diff --git a/mROA/Implementation/Backend/ContextRepository.cs b/mROA/Implementation/Backend/InstanceRepository.cs similarity index 67% rename from mROA/Implementation/Backend/ContextRepository.cs rename to mROA/Implementation/Backend/InstanceRepository.cs index b4ecc98..dbece06 100644 --- a/mROA/Implementation/Backend/ContextRepository.cs +++ b/mROA/Implementation/Backend/InstanceRepository.cs @@ -2,13 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Threading.Tasks; using mROA.Abstract; using mROA.Implementation.Attributes; namespace mROA.Implementation.Backend { - public class ContextRepository : IContextRepository + public class InstanceRepository : IInstanceRepository { public static object[] EventBinders = { }; @@ -22,7 +21,7 @@ namespace mROA.Implementation.Backend private IStorage _storage; - public ContextRepository() + public InstanceRepository() { _storage = new ExtensibleStorage(); } @@ -33,18 +32,24 @@ namespace mROA.Implementation.Backend { var last = _storage.Place(o); - EventBinders.OfType>().FirstOrDefault() - ?.BindEvents((T)o, context, _representationModuleProducer!, last); + var sharedType = typeof(IShared); + var interfaces = o.GetType().GetInterfaces(); + var generic = interfaces.Where(i => sharedType.IsAssignableFrom(i) && i != sharedType) + .Select(i => typeof(IEventBinder<>).MakeGenericType(i)); + + var binders = EventBinders.Where(i => generic.Any(g => g.IsAssignableFrom(i.GetType()))); + foreach (var binder in binders) + ((IEventBinder)binder).BindEvents(o, context, _representationModuleProducer!, last); return last; } - public void ClearObject(ComplexObjectIdentifier id) + public void ClearObject(ComplexObjectIdentifier id, IEndPointContext context) { _storage.Free(id.ContextId); } - public T GetObject(ComplexObjectIdentifier id) + public T GetObject(ComplexObjectIdentifier id, IEndPointContext context) { var value = _storage.GetValue(id.ContextId); @@ -56,7 +61,13 @@ namespace mROA.Implementation.Backend return (T)value; } - public object GetSingleObject(Type type, int ownerId) + public T GetSingletonObject(IEndPointContext context) where T : class, IShared + { + return GetSingletonObject(typeof(T), context) as T ?? + throw new ArgumentException("Unregistered singleton type"); + } + + public object GetSingletonObject(Type type, IEndPointContext context) { return _singletons.GetValueOrDefault(type.GetHashCode()) ?? throw new ArgumentException("Unregistered singleton type"); diff --git a/mROA/Implementation/Backend/MultiClientContextRepository.cs b/mROA/Implementation/Backend/MultiClientContextRepository.cs deleted file mode 100644 index a0b3893..0000000 --- a/mROA/Implementation/Backend/MultiClientContextRepository.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using mROA.Abstract; - -namespace mROA.Implementation.Backend -{ - public class MultiClientContextRepository : IContextRepository, IContextRepositoryHub - { - private readonly Func _produceRepository; - private readonly Dictionary _repositories = new(); - - public MultiClientContextRepository(Func produceRepository) - { - _produceRepository = produceRepository; - } - - public void Inject(T dependency) - { - } - - public int HostId { get; set; } - - public int ResisterObject(object o, IEndPointContext context) - { - var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); - return repository.ResisterObject(o, context); - } - - public void ClearObject(ComplexObjectIdentifier id) - { - var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); - repository.ClearObject(id); - } - - public T GetObject(ComplexObjectIdentifier id) - { - var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); - return repository.GetObject(id); - } - - public object GetSingleObject(Type type, int ownerId) - { - var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); - return repository.GetSingleObject(type, ownerId); - } - - public int GetObjectIndex(object o, IEndPointContext context) - { - var repository = GetRepositoryByClientId(TransmissionConfig.OwnershipRepository.GetOwnershipId()); - return repository.GetObjectIndex(o, context); - } - - public IContextRepository GetRepository(int clientId) - { - var repository = GetRepositoryByClientId(clientId); - return repository; - } - - public void FreeRepository(int clientId) - { - _repositories.Remove(clientId); - } - - private IContextRepository GetRepositoryByClientId(int clientId) - { - if (_repositories.TryGetValue(clientId, out var repository)) - return repository; - - var created = _produceRepository(clientId); - _repositories.Add(clientId, created); - return created; - } - } -} \ No newline at end of file diff --git a/mROA/Implementation/Backend/MultiClientInstanceRepository.cs b/mROA/Implementation/Backend/MultiClientInstanceRepository.cs new file mode 100644 index 0000000..8cfc9d2 --- /dev/null +++ b/mROA/Implementation/Backend/MultiClientInstanceRepository.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using mROA.Abstract; + +namespace mROA.Implementation.Backend +{ + public class MultiClientInstanceRepository : IInstanceRepository, IContextRepositoryHub + { + private readonly Func _produceRepository; + private readonly Dictionary _repositories = new(); + + public MultiClientInstanceRepository(Func produceRepository) + { + _produceRepository = produceRepository; + } + + public void Inject(T dependency) + { + } + + public int HostId { get; set; } + + public int ResisterObject(object o, IEndPointContext context) + { + var repository = GetRepositoryByClientId(context.OwnerId); + return repository.ResisterObject(o, context); + } + + public void ClearObject(ComplexObjectIdentifier id, IEndPointContext context) + { + var repository = GetRepositoryByClientId(context.OwnerId); + repository.ClearObject(id, context); + } + + public T GetObject(ComplexObjectIdentifier id, IEndPointContext context) + { + var repository = GetRepositoryByClientId(context.OwnerId); + return repository.GetObject(id, context); + } + + public T GetSingletonObject(IEndPointContext context) where T : class, IShared + { + var repository = GetRepositoryByClientId(context.OwnerId); + return repository.GetSingletonObject(context); + } + + public object GetSingletonObject(Type type, IEndPointContext context) + { + var repository = GetRepositoryByClientId(context.OwnerId); + return repository.GetSingletonObject(type, context); + } + + public int GetObjectIndex(object o, IEndPointContext context) + { + var repository = GetRepositoryByClientId(context.OwnerId); + return repository.GetObjectIndex(o, context); + } + + public IInstanceRepository GetRepository(int clientId) + { + var repository = GetRepositoryByClientId(clientId); + return repository; + } + + public void FreeRepository(int clientId) + { + _repositories.Remove(clientId); + } + + private IInstanceRepository GetRepositoryByClientId(int clientId) + { + if (_repositories.TryGetValue(clientId, out var repository)) + return repository; + + var created = _produceRepository(clientId); + _repositories.Add(clientId, created); + return created; + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs b/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs index 56cd389..5bb5197 100644 --- a/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs +++ b/mROA/Implementation/Backend/MultiClientOwnershipRepository.cs @@ -13,10 +13,8 @@ namespace mROA.Implementation.Backend return _ownerships.GetValueOrDefault(Environment.CurrentManagedThreadId, 0); } - public int GetHostOwnershipId() - { - return 0; - } + public int GetHostOwnershipId() => 0; + public void RegisterOwnership(int ownershipId) { diff --git a/mROA/Implementation/Backend/NetworkGatewayModule.cs b/mROA/Implementation/Backend/NetworkGatewayModule.cs index a3236a8..f4e9d3c 100644 --- a/mROA/Implementation/Backend/NetworkGatewayModule.cs +++ b/mROA/Implementation/Backend/NetworkGatewayModule.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Net; using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; using mROA.Abstract; @@ -12,7 +14,8 @@ namespace mROA.Implementation.Backend private readonly Type? _interactionModuleType; private readonly TcpListener _tcpListener; private IConnectionHub? _hub; - private ISerializationToolkit? _serialization; + private IContextualSerializationToolKit? _serialization; + private Dictionary _extractorsCTS = new(); public NetworkGatewayModule(IPEndPoint endpoint, Type interactionModuleType, IInjectableModule[] injectableModules) @@ -29,15 +32,6 @@ namespace mROA.Implementation.Backend Console.WriteLine("Enter Backspace to stop"); Task.Run(HandleIncomingConnections); - - while (true) - { - var key = Console.ReadKey(); - if (key.Key == ConsoleKey.Backspace) - break; - } - - Console.WriteLine("Stopping"); } public void Dispose() @@ -52,49 +46,73 @@ namespace mROA.Implementation.Backend case IConnectionHub interactionModule: _hub = interactionModule; break; - case ISerializationToolkit serializationToolkit: + case IContextualSerializationToolKit serializationToolkit: _serialization = serializationToolkit; break; } } - private void HandleIncomingConnections() + private async Task HandleIncomingConnections() { ThrowIfNotInjected(); while (true) { - var client = _tcpListener.AcceptTcpClient(); + var client = await _tcpListener.AcceptTcpClientAsync(); Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}"); - var interaction = Activator.CreateInstance(_interactionModuleType!) as INextGenerationInteractionModule; - + var interaction = Activator.CreateInstance(_interactionModuleType!) as IChannelInteractionModule; + foreach (var injectableModule in _injectableModules!) interaction!.Inject(injectableModule); interaction!.Inject(_serialization); - interaction.BaseStream = client.GetStream(); - var connectionRequest = interaction.GetNextMessageReceiving(false) - .GetAwaiter().GetResult()!; + + //TODO сделать контекст + var context = new EndPointContext(); + + var streamExtractor = + new ChannelInteractionModule.StreamExtractor(client.GetStream(), _serialization, context); + interaction.IsConnected = () => streamExtractor.IsConnected; + streamExtractor.MessageReceived = async message => + { + await interaction.ReceiveChanel.Writer.WriteAsync(message); + }; + Task.Run(() => streamExtractor.SingleReceive()); + var connectionRequest = await interaction.ReceiveChanel.Reader.ReadAsync(); + var cts = new CancellationTokenSource(); switch (connectionRequest.MessageType) { case EMessageType.ClientConnect: + context.HostId = 0; + context.OwnerId = -interaction.ConnectionId; + Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token)); + _ = streamExtractor.SendFromChannel(interaction.TrustedPostChanel, cts.Token); interaction.PostMessageAsync(new NetworkMessageHeader(_serialization!, - new IdAssignment { Id = -interaction.ConnectionId })); + new IdAssignment { Id = interaction.ConnectionId }, null)); + _extractorsCTS[interaction.ConnectionId] = cts; _hub!.RegisterInteraction(interaction); Console.WriteLine("Client registered"); break; case EMessageType.ClientRecovery: { - - interaction.BaseStream = null; - var recoveryRequest = _serialization!.Deserialize(connectionRequest.Data)!; + var recoveryRequest = _serialization!.Deserialize(connectionRequest.Data, null); var recoveryInteraction = _hub.GetInteraction(recoveryRequest.Id); - recoveryInteraction.BaseStream = client.GetStream(); - + + _extractorsCTS[-recoveryRequest.Id].Cancel(); + + recoveryInteraction.IsConnected = () => streamExtractor.IsConnected; + streamExtractor.MessageReceived = message => + { + recoveryInteraction.ReceiveChanel.Writer.WriteAsync(message); + }; + _ = streamExtractor.SendFromChannel(recoveryInteraction.TrustedPostChanel, cts.Token); + + Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token)); + + recoveryInteraction.Restart(false); - Console.WriteLine("Connection recovery for client {0} finished", recoveryRequest.Id); break; } default: diff --git a/mROA/Implementation/Backend/UdpGateway.cs b/mROA/Implementation/Backend/UdpGateway.cs new file mode 100644 index 0000000..1945e0f --- /dev/null +++ b/mROA/Implementation/Backend/UdpGateway.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using mROA.Abstract; +using static mROA.Implementation.EMessageType; + +namespace mROA.Implementation.Backend +{ + public class UdpGateway : IUntrustedGateway + { + private IConnectionHub _hub; + private UdpClient _client; + private Dictionary _reservedPorts = new(); + private CancellationTokenSource _tokenSource = new(); + private IContextualSerializationToolKit _serializationToolkit; + private IEndPointContext _context; + public UdpGateway(IPEndPoint listeningEndpoint) + { + _client = new UdpClient(listeningEndpoint); + } + + + public void Inject(T dependency) + { + switch (dependency) + { + case IConnectionHub hub: + _hub = hub; + break; + case IContextualSerializationToolKit serializationToolkit: + _serializationToolkit = serializationToolkit; + break; + case IEndPointContext context: + _context = context; + break; + } + } + + public void Dispose() + { + _tokenSource.Cancel(); + _client.Close(); + } + + public Task Start() + { + var token = _tokenSource.Token; + return Task.Run(async () => + { + while (token.IsCancellationRequested == false) + { + var incoming = await _client.ReceiveAsync(); + var parsed = _serializationToolkit.Deserialize(incoming.Buffer, _context); + try + { + int channelId; + switch (parsed.MessageType) + { + case UntrustedConnect: + channelId = BitConverter.ToInt32(parsed.Data); + _reservedPorts[incoming.RemoteEndPoint] = channelId; + _ = UntrustedSend(_hub.GetInteraction(channelId), incoming.RemoteEndPoint); + break; + default: + channelId = _reservedPorts[incoming.RemoteEndPoint]; + var interaction = _hub.GetInteraction(channelId); + await interaction.ReceiveChanel.Writer.WriteAsync(parsed, token); + break; + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + }, token); + } + + private Task UntrustedSend(IChannelInteractionModule interaction, IPEndPoint endpoint) + { + return Task.Run(async () => + { + await foreach (var post in interaction.UntrustedPostChanel.ReadAllAsync()) + { + if (post.MessageType is not (CallRequest or EMessageType.CancelRequest + or EventRequest)) + continue; + + var parsed = _serializationToolkit.Serialize(post, _context); + await _client.SendAsync(parsed, parsed.Length, endpoint); + } + } + ); + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/ChannelInteractionModule.cs b/mROA/Implementation/ChannelInteractionModule.cs new file mode 100644 index 0000000..fcec7bd --- /dev/null +++ b/mROA/Implementation/ChannelInteractionModule.cs @@ -0,0 +1,232 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class ChannelInteractionModule : IChannelInteractionModule + { + private readonly ChannelReader _receiveReader; + private readonly ChannelWriter _trustedWriter; + private readonly ChannelWriter _untrustedWriter; + private readonly Channel _outputTrustedChannel; + private readonly Channel _outputUntrustedChannel; + private IContextualSerializationToolKit? _serialization; + private bool _isConnected = true; + private bool _isActive = true; + private TaskCompletionSource _reconnection; + private IEndPointContext? _context; + + public ChannelInteractionModule() + { + ReceiveChanel = Channel.CreateUnbounded(new UnboundedChannelOptions + { + SingleReader = false, + SingleWriter = false, + }); + _receiveReader = ReceiveChanel.Reader; + _outputTrustedChannel = Channel.CreateBounded(new BoundedChannelOptions(1) + { + SingleReader = true, + SingleWriter = true, + }); + _trustedWriter = _outputTrustedChannel.Writer; + _outputUntrustedChannel = Channel.CreateUnbounded(new UnboundedChannelOptions + { + SingleReader = true, + SingleWriter = true, + }); + _untrustedWriter = _outputUntrustedChannel.Writer; + _reconnection = new TaskCompletionSource(); + } + + public int ConnectionId { get; set; } + + public Channel ReceiveChanel { get; } + + public ChannelReader TrustedPostChanel => _outputTrustedChannel.Reader; + public ChannelReader UntrustedPostChanel => _outputUntrustedChannel.Reader; + public Func IsConnected { get; set; } = () => false; + + public void Inject(T dependency) + { + switch (dependency) + { + case IContextualSerializationToolKit toolkit: + _serialization = toolkit; + break; + case IIdentityGenerator identityGenerator: + ConnectionId = identityGenerator.GetNextIdentity(); + break; + case IEndPointContext endpointContext: + _context = endpointContext; + break; + } + } + + public ValueTask GetNextMessageReceiving(bool infinite = true) + { + return _receiveReader.ReadAsync(); + // if (_currentReceiving != null) return _currentReceiving; + // _currentReceiving = Task.Run(async () => await GetNextMessage()); + // return _currentReceiving; + } +#pragma warning disable CS8602 // Dereference of a possibly null reference. + private async ValueTask PostMessageInternal(NetworkMessageHeader messageHeader) + { + if (!IsConnected()) + { + return false; + } + + await _trustedWriter.WriteAsync(messageHeader); + return true; + } +#pragma warning restore CS8602 // Dereference of a possibly null reference. + + + public async Task PostMessageAsync(NetworkMessageHeader messageHeader) + { + if (_serialization == null) + throw new NullReferenceException("Serialization toolkit is not initialized"); + + while (true) + { + if (await PostMessageInternal(messageHeader)) + break; + + if (!_isActive) + { + return; + } + + _isConnected = false; + await MakeRecovery(); + } + } + + public async Task PostMessageUntrustedAsync(NetworkMessageHeader messageHeader) + { + await _untrustedWriter.WriteAsync(messageHeader); + } + + public event Action? OnDisconnected; + + public async Task Restart(bool sendRecovery) + { + if (sendRecovery) + { + await PostMessageAsync( + new NetworkMessageHeader(_serialization!, new ClientRecovery(Math.Abs(ConnectionId)), _context)); + await ReceiveChanel.Reader.ReadAsync(); + } + else + { + await _trustedWriter.WriteAsync(new NetworkMessageHeader()); + } + + _reconnection.TrySetResult(Stream.Null); + _isConnected = true; + _reconnection = new TaskCompletionSource(); + } + + private async Task MakeRecovery() + { + lock (_reconnection) + { + OnDisconnected?.Invoke(ConnectionId); + } + + if (!_reconnection.Task.IsCompleted && !_isConnected) + { + await _reconnection.Task; + } + } + + public void Dispose() + { + _isActive = false; + } + + public class StreamExtractor + { + private readonly Stream _ioStream; + private readonly IContextualSerializationToolKit _serializationToolkit; + private const int BufferSize = ushort.MaxValue; + private readonly Memory _buffer = new byte[BufferSize]; + private bool _manualConnectionState = true; + private readonly IEndPointContext _context; + + public StreamExtractor(Stream ioStream, IContextualSerializationToolKit serializationToolkit, + IEndPointContext context) + { + _ioStream = ioStream; + _serializationToolkit = serializationToolkit; + _context = context; + } + + public Action MessageReceived = _ => { }; + + private ushort ReadMessageLength() + { + var firstBit = _ioStream.ReadByte(); + if (firstBit == -1) + { + _manualConnectionState = false; + throw new EndOfStreamException(); + } + + _manualConnectionState = true; + var secondBit = (byte)_ioStream.ReadByte(); + + var len = BitConverter.ToUInt16(new[] { (byte)firstBit, secondBit }); + + return len; + } + + public async Task SingleReceive(CancellationToken token = default) + { + var len = ReadMessageLength(); + var localSpan = _buffer[..len]; + + await _ioStream.ReadExactlyAsync(localSpan, cancellationToken: token); + + var message = _serializationToolkit.Deserialize(localSpan, _context); + MessageReceived(message); + } + + public async Task LoopedReceive(CancellationToken token = default) + { + while (token.IsCancellationRequested == false && IsConnected) + { + await SingleReceive(token); + } + } + + private async Task Send(NetworkMessageHeader message, CancellationToken token = default) + { + var rawMessage = _serializationToolkit.Serialize(message, _context); + var header = BitConverter.GetBytes((ushort)rawMessage.Length).AsMemory(0, sizeof(ushort)); + + await _ioStream.WriteAsync(header, token); + await _ioStream.WriteAsync(rawMessage, token); + } + + public async Task SendFromChannel(ChannelReader channel, + CancellationToken token = default) + { + while (token.IsCancellationRequested == false && IsConnected) + { + var message = await channel.ReadAsync(token); + await Send(message, token); + } + } + + + public bool IsConnected => _ioStream is { CanRead: true, CanWrite: true } && _manualConnectionState; + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/ComplexContextRepository.cs b/mROA/Implementation/ComplexContextRepository.cs deleted file mode 100644 index 8f44c78..0000000 --- a/mROA/Implementation/ComplexContextRepository.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using mROA.Abstract; - -namespace mROA.Implementation -{ - public class ComplexContextRepository : IContextRepository - { - private List>> _storages = new(); - public static object[] EventBinders = { }; - - private IRemoteObjectFactory? _remoteObjectFactory; - private IRepresentationModuleProducer? _representationModuleProducer; - - public void Inject(T dependency) - { - if (dependency is IRemoteObjectFactory remoteObjectFactory) - { - _remoteObjectFactory = remoteObjectFactory; - } - - if (dependency is IRepresentationModuleProducer moduleProducer) - { - _representationModuleProducer = moduleProducer; - } - } - - public int HostId { get; set; } - - public int ResisterObject(object o, IEndPointContext context) - { - var storageIndex = _storages.FindIndex(i => i.Key == context.OwnerId); - if (storageIndex == -1) - { - _storages.Add( - new KeyValuePair>(context.OwnerId, new ExtensibleStorage())); - storageIndex = _storages.Count - 1; - } - - var storage = _storages[storageIndex].Value; - - var placedIndex = storage.Place(o); - EventBinders.OfType>().FirstOrDefault() - ?.BindEvents((T)o, context, _representationModuleProducer!, placedIndex); - - return placedIndex; - } - - public void ClearObject(ComplexObjectIdentifier id) - { - _storages.Find(i => i.Key == id.OwnerId).Value.Free(id.ContextId); - } - - public T GetObject(ComplexObjectIdentifier id) - { - throw new NotImplementedException(); - } - - public object GetSingleObject(Type type, int ownerId) - { - throw new NotImplementedException(); - } - - public int GetObjectIndex(object o, IEndPointContext context) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/mROA/Implementation/ComplexObjectIdentifier.cs b/mROA/Implementation/ComplexObjectIdentifier.cs index d95d378..ae2e4e0 100644 --- a/mROA/Implementation/ComplexObjectIdentifier.cs +++ b/mROA/Implementation/ComplexObjectIdentifier.cs @@ -32,7 +32,7 @@ namespace mROA.Implementation public ulong Flat { - get => (ulong)OwnerId << 32 | (uint)ContextId; + get => (ulong)((long)OwnerId << 32 | (uint)ContextId); set { OwnerId = (int)(value >> 32); diff --git a/mROA/Implementation/EMessageType.cs b/mROA/Implementation/EMessageType.cs index bfb6304..d53aa72 100644 --- a/mROA/Implementation/EMessageType.cs +++ b/mROA/Implementation/EMessageType.cs @@ -12,5 +12,6 @@ namespace mROA.Implementation ClientRecovery, ClientConnect, ClientDisconnect, + UntrustedConnect, } } \ No newline at end of file diff --git a/mROA/Implementation/EndPointContext.cs b/mROA/Implementation/EndPointContext.cs index e6351e9..5694614 100644 --- a/mROA/Implementation/EndPointContext.cs +++ b/mROA/Implementation/EndPointContext.cs @@ -1,20 +1,27 @@ -using System; using mROA.Abstract; +using mROA.Implementation.Backend; namespace mROA.Implementation { public class EndPointContext : IEndPointContext { - public Func OwnerFunc; - public IContextRepository RealRepository { get; set; } - public IContextRepository RemoteRepository { get; set; } + public IInstanceRepository RealRepository { get; set; } + public IInstanceRepository RemoteRepository { get; set; } public int HostId { get; set; } - public int OwnerId + public int OwnerId { get; set; } + + public void Inject(T dependency) { - get => OwnerFunc(); - // ReSharper disable once UnusedMember.Global - set { OwnerFunc = () => value; } + switch (dependency) + { + case RemoteInstanceRepository remoteRepository: + RemoteRepository = remoteRepository; + break; + case InstanceRepository realRepository: + RealRepository = realRepository; + break; + } } } } \ No newline at end of file diff --git a/mROA/Implementation/EventBinder.cs b/mROA/Implementation/EventBinder.cs index 8354c4f..429a882 100644 --- a/mROA/Implementation/EventBinder.cs +++ b/mROA/Implementation/EventBinder.cs @@ -1,10 +1,11 @@ using System; +using mROA.Abstract; -namespace mROA.Abstract +namespace mROA.Implementation { public class EventBinder : IEventBinder { - public Action BindAction { get; set; } + public Action BindAction { get; set; } = (_, _, _, _) => { }; public void BindEvents(T source, IEndPointContext context, IRepresentationModuleProducer representationModuleProducer, int index) diff --git a/mROA/Implementation/Frontend/NetworkFrontendBridge.cs b/mROA/Implementation/Frontend/NetworkFrontendBridge.cs index ca32354..8611418 100644 --- a/mROA/Implementation/Frontend/NetworkFrontendBridge.cs +++ b/mROA/Implementation/Frontend/NetworkFrontendBridge.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; using mROA.Abstract; using Exception = System.Exception; @@ -11,28 +12,35 @@ namespace mROA.Implementation.Frontend { private readonly IPEndPoint _serverEndPoint; private TcpClient _tcpClient = new(); - private NextGenerationInteractionModule? _interactionModule; - private ISerializationToolkit? _serialization; + private IChannelInteractionModule? _interactionModule; + private IContextualSerializationToolKit? _serialization; + private ChannelInteractionModule.StreamExtractor _currentExtractor; + private CancellationTokenSource _rawExtractorCancellation; + private IEndPointContext _context; public NetworkFrontendBridge(IPEndPoint serverEndPoint) { _serverEndPoint = serverEndPoint; + _rawExtractorCancellation = new CancellationTokenSource(); } public void Inject(T dependency) { switch (dependency) { - case NextGenerationInteractionModule interactionModule: + case ChannelInteractionModule interactionModule: _interactionModule = interactionModule; break; - case ISerializationToolkit toolkit: + case IContextualSerializationToolKit toolkit: _serialization = toolkit; break; + case IEndPointContext endPointContext: + _context = endPointContext; + break; } } - public void Connect() + public async Task Connect() { if (_interactionModule is null) throw new Exception("Interaction module was not injected"); @@ -41,15 +49,16 @@ namespace mROA.Implementation.Frontend _tcpClient.Connect(_serverEndPoint); - _interactionModule.BaseStream = _tcpClient.GetStream(); + PrepareExtractor(); + _interactionModule.IsConnected = () => _currentExtractor.IsConnected; + _interactionModule.OnDisconnected += _ => { Reconnect(); }; + + _interactionModule.PostMessageAsync(new NetworkMessageHeader(_serialization, new ClientConnect(), _context)) + .Wait(); + + _currentExtractor.SingleReceive(); + var idMessage = await _interactionModule.GetNextMessageReceiving(false); - _interactionModule.OnDisconected += id => - { - Reconnect(); - }; - - _interactionModule.PostMessageAsync(new NetworkMessageHeader(_serialization, new ClientConnect())).Wait(); - var idMessage = _interactionModule.GetNextMessageReceiving(false).GetAwaiter().GetResult(); if (idMessage.MessageType != EMessageType.IdAssigning) { throw new Exception( @@ -57,28 +66,51 @@ namespace mROA.Implementation.Frontend } - var assignment = _serialization.Deserialize(idMessage.Data)!; + Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token)); + + var assignment = _serialization.Deserialize(idMessage.Data, _context); _interactionModule.ConnectionId = -assignment.Id; - TransmissionConfig.OwnershipRepository = new StaticOwnershipRepository(assignment.Id); + _context.HostId = assignment.Id; + _context.OwnerId = assignment.Id; + } + + private void PrepareExtractor() + { + _currentExtractor = + new ChannelInteractionModule.StreamExtractor(_tcpClient.GetStream(), _serialization!, _context); + + _ = _currentExtractor.SendFromChannel(_interactionModule!.TrustedPostChanel, + _rawExtractorCancellation.Token); + _currentExtractor.MessageReceived = message => + { + _interactionModule.ReceiveChanel.Writer.WriteAsync(message); + }; } private async Task Reconnect() { _tcpClient = new TcpClient(); _tcpClient.Connect(_serverEndPoint); - _interactionModule.BaseStream = _tcpClient.GetStream(); + + _rawExtractorCancellation.Cancel(); + _rawExtractorCancellation = new CancellationTokenSource(); + + PrepareExtractor(); + + Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token)); + await _interactionModule.Restart(true); } public void Obstacle() { - _interactionModule!.BaseStream!.Dispose(); _tcpClient.Dispose(); } public void Disconnect() { - _ = _interactionModule!.PostMessageAsync(new NetworkMessageHeader(_serialization!, new ClientDisconnect())); + _ = _interactionModule!.PostMessageAsync(new NetworkMessageHeader(_serialization!, new ClientDisconnect(), + _context)); _interactionModule.Dispose(); _tcpClient.Dispose(); } diff --git a/mROA/Implementation/Frontend/RequestExtractor.cs b/mROA/Implementation/Frontend/RequestExtractor.cs index fbe9539..3edccaf 100644 --- a/mROA/Implementation/Frontend/RequestExtractor.cs +++ b/mROA/Implementation/Frontend/RequestExtractor.cs @@ -1,10 +1,7 @@ using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using mROA.Abstract; -using mROA.Implementation.Backend; -using mROA.Implementation.CommandExecution; // ReSharper disable MethodHasAsyncOverload @@ -13,11 +10,12 @@ namespace mROA.Implementation.Frontend public class RequestExtractor : IRequestExtractor { private IExecuteModule? _executeModule; + private IMethodRepository? _methodRepository; - private IContextRepository? _realContextRepository; - private IContextRepository? _remoteContextRepository; + private IRepresentationModule? _representationModule; - private ISerializationToolkit? _serializationToolkit; + private IContextualSerializationToolKit? _serializationToolkit; + private IEndPointContext _context; public void Inject(T dependency) { @@ -26,93 +24,64 @@ namespace mROA.Implementation.Frontend case IExecuteModule executeModule: _executeModule = executeModule; break; - case MultiClientContextRepository: - case ContextRepository: - _realContextRepository = dependency as IContextRepository; - break; - case RemoteContextRepository remoteContextRepository: - _remoteContextRepository = remoteContextRepository; - break; case IMethodRepository methodRepository: _methodRepository = methodRepository; break; case IRepresentationModule representationModule: _representationModule = representationModule; break; - case ISerializationToolkit serializationToolkit: + case IContextualSerializationToolKit serializationToolkit: _serializationToolkit = serializationToolkit; break; + case IEndPointContext remoteContext: + _context = remoteContext; + break; } } - public Task StartExtraction() + public async Task StartExtraction() { - return Task.Run(() => - { - ThrowIfNotInjected(); - var multiClientOwnershipRepository = - TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; - multiClientOwnershipRepository?.RegisterOwnership(_representationModule.Id); + ThrowIfNotInjected(); - try + + try + { + + var streamTokenSource = new CancellationTokenSource(); + + var query = _representationModule!.GetStream(m => + m.MessageType is EMessageType.CallRequest or EMessageType.CancelRequest + or EMessageType.EventRequest or EMessageType.ClientDisconnect, _context, + streamTokenSource.Token, + m => m.MessageType == EMessageType.CallRequest ? typeof(DefaultCallRequest) : null, + m => m.MessageType == EMessageType.CancelRequest ? typeof(CancelRequest) : null, + m => m.MessageType == EMessageType.EventRequest ? typeof(DefaultCallRequest) : null, + m => m.MessageType == EMessageType.ClientDisconnect ? typeof(ClientDisconnect) : null); + + + await foreach (var command in query) { -#if TRACE - var sw = new Stopwatch(); -#endif - while (true) + switch (command.originalType) { -#if TRACE - Console.WriteLine("Waiting for request..."); - if (sw.IsRunning) - { - sw.Stop(); - Console.WriteLine($"Request handling took {Math.Round(sw.Elapsed.TotalMilliseconds * 1000.0)} microseconds."); - } -#endif - var tokenSource = new CancellationTokenSource(); - var token = tokenSource.Token; - var defaultRequest = - _representationModule!.GetMessageAsync( - messageType: EMessageType.CallRequest, token: token); - var cancelRequest = - _representationModule!.GetMessageAsync( - messageType: EMessageType.CancelRequest, token: token); - var eventRequest = - _representationModule!.GetMessageAsync( - messageType: EMessageType.EventRequest, token: token); - var disconnectRequest = - _representationModule!.GetMessageAsync( - messageType: EMessageType.ClientDisconnect, token:token); - Task.WaitAny(defaultRequest, cancelRequest, eventRequest, disconnectRequest); -#if TRACE - Console.WriteLine("Request received"); - sw.Restart(); -#endif - if (cancelRequest.IsCompleted) - { -#if TRACE - Console.WriteLine("Cancelling request"); -#endif - HandleCancelRequest(tokenSource, cancelRequest.Result); - } - else if (defaultRequest.IsCompleted) - { - HandleCallRequest(tokenSource, defaultRequest.Result); - } - else if(eventRequest.IsCompleted) - { - HandleEventRequest(tokenSource, eventRequest.Result); - }else if (disconnectRequest.IsCompleted) - { + case EMessageType.CallRequest: + HandleCallRequest((command.parced as DefaultCallRequest)!); break; - } + case EMessageType.ClientDisconnect: + return; + case EMessageType.EventRequest: + HandleEventRequest((command.parced as DefaultCallRequest)!); + break; + case EMessageType.CancelRequest: + HandleCancelRequest((command.parced as CancelRequest)!); + break; + default: + continue; } } - catch - { - multiClientOwnershipRepository?.FreeOwnership(); - } - }); + } + catch + { + } } private void ThrowIfNotInjected() @@ -121,40 +90,34 @@ namespace mROA.Implementation.Frontend throw new NullReferenceException("Serializing toolkit is null."); if (_executeModule == null) throw new NullReferenceException("Execute module is null."); - if (_realContextRepository == null) - throw new NullReferenceException("Context repository is null."); if (_representationModule == null) throw new NullReferenceException("Representation module is null."); if (_methodRepository == null) throw new NullReferenceException("Method repository is null."); } - private void HandleCancelRequest(CancellationTokenSource tokenSource, CancelRequest req) + private void HandleCancelRequest(CancelRequest req) { - tokenSource.Cancel(); - _executeModule!.Execute(req, _realContextRepository!, _representationModule!); + _executeModule!.Execute(req, _context.RealRepository, _representationModule!, _context); } - private void HandleCallRequest(CancellationTokenSource tokenSource, DefaultCallRequest request) + private void HandleCallRequest(DefaultCallRequest request) { - tokenSource.Cancel(); - - var result = _executeModule!.Execute(request, _realContextRepository!, _representationModule!); + var result = _executeModule!.Execute(request, _context.RealRepository, _representationModule!, _context); var resultType = result.MessageType; - + if (resultType == EMessageType.Unknown) { return; } - _representationModule!.PostCallMessage(request.Id, resultType, result, result.GetType()); + _representationModule!.PostCallMessage(request.Id, resultType, result, _context); } - private void HandleEventRequest(CancellationTokenSource tokenSource, DefaultCallRequest request) + private void HandleEventRequest(DefaultCallRequest request) { - tokenSource.Cancel(); - _executeModule!.Execute(request, _remoteContextRepository!, _representationModule!); + _executeModule!.Execute(request, _context.RemoteRepository, _representationModule!, _context); } } } \ No newline at end of file diff --git a/mROA/Implementation/Frontend/UdpUntrustedInteraction.cs b/mROA/Implementation/Frontend/UdpUntrustedInteraction.cs new file mode 100644 index 0000000..5d4d4cf --- /dev/null +++ b/mROA/Implementation/Frontend/UdpUntrustedInteraction.cs @@ -0,0 +1,85 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using mROA.Abstract; + +namespace mROA.Implementation.Frontend +{ + public class UdpUntrustedInteraction : IUntrustedInteractionModule + { + private IContextualSerializationToolKit _serializationToolkit; + private IChannelInteractionModule _channelInteractionModule; + private CancellationTokenSource _tokenSource = new(); + private IEndPointContext _context; + public void Dispose() + { + _tokenSource.Cancel(); + } + + public Task Start(IPEndPoint endpoint) + { + return Task.Run(() => + { + var client = new UdpClient(); + client.Connect(endpoint); + Listening(client, _tokenSource.Token); + Posting(client, _tokenSource.Token); + }, _tokenSource.Token); + } + + private async Task Listening(UdpClient udpClient, CancellationToken token) + { + var writer = _channelInteractionModule.ReceiveChanel.Writer; + while (token.IsCancellationRequested == false) + { + var message = new Memory((await udpClient.ReceiveAsync()).Buffer); + var parsed = _serializationToolkit.Deserialize(message, _context); + + await writer.WriteAsync(parsed, token); + } + } + + private async Task Posting(UdpClient udpClient, CancellationToken token) + { + var initMessage = new NetworkMessageHeader + { + MessageType = EMessageType.UntrustedConnect, Id = Guid.NewGuid(), + Data = BitConverter.GetBytes(_channelInteractionModule.ConnectionId) + }; + + var initParsed = _serializationToolkit.Serialize(initMessage, _context); + + await udpClient.SendAsync(initParsed, initParsed.Length); + + await foreach (var post in _channelInteractionModule.UntrustedPostChanel.ReadAllAsync(token)) + { + if (post.MessageType is not (EMessageType.CallRequest or EMessageType.CancelRequest + or EMessageType.EventRequest)) + continue; + + var serialized = _serializationToolkit.Serialize(post, _context); + + await udpClient.SendAsync(serialized, serialized.Length); + + } + } + + public void Inject(T dependency) + { + switch (dependency) + { + case IChannelInteractionModule channelModule: + _channelInteractionModule = channelModule; + break; + case IContextualSerializationToolKit serializationToolkit: + _serializationToolkit = serializationToolkit; + break; + case IEndPointContext endPointContext: + _context = endPointContext; + break; + } + } + } +} \ No newline at end of file diff --git a/mROA/Implementation/JsonSerializationToolkit.cs b/mROA/Implementation/JsonSerializationToolkit.cs deleted file mode 100644 index 61a3c7e..0000000 --- a/mROA/Implementation/JsonSerializationToolkit.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Text.Json; -using mROA.Abstract; - -namespace mROA.Implementation -{ - public class JsonSerializationToolkit : ISerializationToolkit - { - public byte[] Serialize(T objectToSerialize) - { - return JsonSerializer.SerializeToUtf8Bytes(objectToSerialize); - } - - public byte[] Serialize(object objectToSerialize, Type type) - { - return JsonSerializer.SerializeToUtf8Bytes(objectToSerialize, type); - } - - public T? Deserialize(byte[] rawData) - { - return JsonSerializer.Deserialize(rawData); - } - - public object? Deserialize(byte[] rawData, Type type) - { - return JsonSerializer.Deserialize(rawData, type); - } - - public T? Deserialize(Span rawData) - { - return JsonSerializer.Deserialize(rawData); - } - - public object? Deserialize(Span rawData, Type type) - { - return JsonSerializer.Deserialize(rawData, type); - } - - public T Cast(object nonCasted) - { - return nonCasted switch - { - JsonElement jsonElement => jsonElement.Deserialize()!, - T casted => casted, - _ => throw new JsonException("Cannot cast object to type " + typeof(T).FullName) - }; - } - - public object Cast(object nonCasted, Type type) - { - if (nonCasted is JsonElement jsonElement) - return jsonElement.Deserialize(type)!; - - throw new JsonException("Cannot cast object to type " + type.FullName); - } - - public void Inject(T dependency) - { - } - } -} \ No newline at end of file diff --git a/mROA/Implementation/MethodInvoker.cs b/mROA/Implementation/MethodInvoker.cs index 59efb7d..4f1e26c 100644 --- a/mROA/Implementation/MethodInvoker.cs +++ b/mROA/Implementation/MethodInvoker.cs @@ -6,6 +6,7 @@ namespace mROA.Implementation public class MethodInvoker : IMethodInvoker { public bool IsVoid { get; set; } + public bool IsTrusted { get; set; } = true; public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; public Type? ReturnType { get; set; } public Func Invoking { get; set; } = (_, _, _) => null; @@ -32,9 +33,10 @@ namespace mROA.Implementation public class AsyncMethodInvoker : IMethodInvoker { public bool IsVoid { get; set; } + public bool IsTrusted { get; set; } = true; public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; public Type? ReturnType { get; set; } - public Type SuitableType { get; set; } + public Type SuitableType { get; set; } = typeof(object); public Action> Invoking { get; set; } = (_, _, _, post) => { post.Invoke(null); }; diff --git a/mROA/Implementation/NetworkMessageHeader.cs b/mROA/Implementation/NetworkMessageHeader.cs index a75cdb0..0a0a271 100644 --- a/mROA/Implementation/NetworkMessageHeader.cs +++ b/mROA/Implementation/NetworkMessageHeader.cs @@ -1,5 +1,4 @@ using System; -using System.Text.Json.Serialization; using mROA.Abstract; // ReSharper disable UnusedMember.Global @@ -8,6 +7,19 @@ namespace mROA.Implementation { public class NetworkMessageHeader { + private bool Equals(NetworkMessageHeader other) + { + return Id.Equals(other.Id) && MessageType == other.MessageType; + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((NetworkMessageHeader)obj); + } + public static readonly NetworkMessageHeader Null = new(); public NetworkMessageHeader() { @@ -15,15 +27,15 @@ namespace mROA.Implementation MessageType = EMessageType.Unknown; Data = Array.Empty(); } - public NetworkMessageHeader(ISerializationToolkit serializationToolkit, INetworkMessage networkMessage) + public NetworkMessageHeader(IContextualSerializationToolKit serializationToolkit, + INetworkMessage networkMessage, IEndPointContext? context) { MessageType = networkMessage.MessageType; - Data = serializationToolkit.Serialize(networkMessage); + Data = serializationToolkit.Serialize(networkMessage, context); Id = Guid.NewGuid(); } public Guid Id { get; set; } - [JsonConverter(typeof(JsonStringEnumConverter))] public EMessageType MessageType { get; set; } public byte[] Data { get; set; } diff --git a/mROA/Implementation/NextGenerationInteractionModule.cs b/mROA/Implementation/NextGenerationInteractionModule.cs deleted file mode 100644 index 57ba7dd..0000000 --- a/mROA/Implementation/NextGenerationInteractionModule.cs +++ /dev/null @@ -1,269 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using mROA.Abstract; - -namespace mROA.Implementation -{ - public class NextGenerationInteractionModule : INextGenerationInteractionModule - { - private int DebugId = new Random().Next(); - private const int BufferSize = ushort.MaxValue; - private readonly Memory _buffer = new byte[BufferSize]; - private readonly List _messageBuffer = new(128); - private Task? _currentReceiving; - private ISerializationToolkit? _serialization; - private Stream? _baseStream; - private bool _isConnected = true; - private bool _isInReconnectionState; - private bool _isActive = true; - private TaskCompletionSource _reconnection; - - public NextGenerationInteractionModule() - { - _reconnection = new TaskCompletionSource(); - } - - public int ConnectionId { get; set; } - - public Stream? BaseStream - { - get => _baseStream; set => _baseStream = value; - } - - - public void Inject(T dependency) - { - switch (dependency) - { - case ISerializationToolkit toolkit: - _serialization = toolkit; - break; - case IIdentityGenerator identityGenerator: - ConnectionId = identityGenerator.GetNextIdentity(); - break; - } - } - - public Task GetNextMessageReceiving(bool infinite = true) - { - if (!infinite) return Receive().AsTask(); - if (_currentReceiving != null) return _currentReceiving; - _currentReceiving = Task.Run(async () => await GetNextMessage()); - return _currentReceiving; - - } -#pragma warning disable CS8602 // Dereference of a possibly null reference. - private async ValueTask PostMessageInternal(NetworkMessageHeader messageHeader) - { -#if TRACE - Console.WriteLine( - $"{DateTime.Now.TimeOfDay} Posting message: {messageHeader.Id} - {messageHeader.MessageType} to {ConnectionId}"); -#endif - - var rawMessage = _serialization.Serialize(messageHeader); - var header = BitConverter.GetBytes((ushort)rawMessage.Length).AsMemory(0, sizeof(ushort)); - - if (!_baseStream.CanWrite) - return false; - - await BaseStream.WriteAsync(header); - await BaseStream.WriteAsync(rawMessage); - return true; - } -#pragma warning restore CS8602 // Dereference of a possibly null reference. - - - public async Task PostMessageAsync(NetworkMessageHeader messageHeader) - { - if (BaseStream == null) - throw new NullReferenceException("BaseStream is null"); - - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); - - // Console.WriteLine("Sending {0}", JsonSerializer.Serialize(message)); - - bool withError = false; - while (true) - { - if (withError) - { - Console.WriteLine("Post again"); - } - - if (await PostMessageInternal(messageHeader)) - break; - - if (!_isActive) - { - return; - } - _isConnected = false; - withError = true; - await MakeRecovery("OUT"); - } - } - - public void HandleMessage(NetworkMessageHeader messageHeader) - { - _messageBuffer.Remove(messageHeader); - } - - public NetworkMessageHeader[] UnhandledMessages => _messageBuffer.ToArray(); - - public NetworkMessageHeader? FirstByFilter(Predicate predicate) - { - return _messageBuffer.FirstOrDefault(m => predicate(m)); - } - - public event Action? OnDisconected; - - private async Task GetNextMessage() - { - if (BaseStream == null) - throw new NullReferenceException("BaseStream is null"); - - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is null"); - - bool withError = false; - - while (true) - { - if (withError) - { - Console.WriteLine("Receive again"); - } - - try - { - var message = await Receive(); - _currentReceiving = Task.Run(async () => await GetNextMessage()); - - return message; - } - catch (Exception ex) - { - if (!_isActive) - { - return NetworkMessageHeader.Null; - } - withError = true; - await MakeRecovery("IN"); - } - } - } - - private ushort ReadMessageLength() - { - var firstBit = BaseStream.ReadByte(); - if (firstBit == -1) - { - _isConnected = false; - throw new EndOfStreamException(); - } - - _isConnected = true; - var secondBit = (byte)BaseStream.ReadByte(); - - var len = BitConverter.ToUInt16(new[] { (byte)firstBit, secondBit }); - - return len; - } - - private async ValueTask Receive() - { - var len = ReadMessageLength(); - var localSpan = _buffer[..len]; - - await BaseStream.ReadExactlyAsync(localSpan); - - var message = _serialization.Deserialize(localSpan.Span); -#if TRACE - Console.WriteLine($"{DateTime.Now.TimeOfDay} Received Message {message.Id} - {message.MessageType}"); - TransmissionConfig.TotalTransmittedBytes += len; - Console.WriteLine($"Total received bytes are {TransmissionConfig.TotalTransmittedBytes}"); -#endif - _messageBuffer.Add(message); - return message; - } - - public async Task Restart(bool sendRecovery) - { - if (sendRecovery) - { - await PostMessageAsync( - new NetworkMessageHeader(_serialization!, new ClientRecovery(Math.Abs(ConnectionId)))); - var iTest = _baseStream.ReadByte(); - var bTest = (byte)iTest; - _baseStream.WriteByte(bTest); - } - else - { - const byte confirmByte = 128; - _baseStream.WriteByte(confirmByte); - var iPong = _baseStream.ReadByte(); - var bPong = (byte)iPong; - if (confirmByte != bPong) - { - Console.WriteLine("Incorrect byte"); - } - } - - Console.WriteLine("Setting result for reconnection"); - var setting = _reconnection.TrySetResult(BaseStream); - _isInReconnectionState = false; - _isConnected = true; - Console.WriteLine($"Set result for reconnection {setting}"); - - _reconnection = new TaskCompletionSource(); - } - - private async Task MakeRecovery(string source) - { - Console.WriteLine("Staring recovery from {0}", source); - - lock (_reconnection) - { - Console.WriteLine("Got lock from {0}", source); - if (_isConnected || _isInReconnectionState) - { - Console.WriteLine( - $"{source} {_isConnected} {_isInReconnectionState} {!_baseStream.CanRead} {!_baseStream.CanWrite}"); - return; - } - - Console.WriteLine("Call OnDisconnected from {0}", source); - _isInReconnectionState = true; - OnDisconected?.Invoke(ConnectionId); - } - - Console.WriteLine("Waiting for reconnect from {0}", source); - if (!_reconnection.Task.IsCompleted && !_isConnected) - { - Console.WriteLine("Current connection state {0} from {1}", _isConnected, source); - await _reconnection.Task; - } - - Console.WriteLine("Reconnect finished from {0}", source); - lock (_reconnection) - { - _isInReconnectionState = false; - } - } - - public void Dispose() - { - Console.WriteLine("Interaction module disposed"); - _isActive = false; - if (_currentReceiving is { IsCompleted: true }) - { - _currentReceiving?.Dispose(); - } - _baseStream?.Dispose(); - } - } -} \ No newline at end of file diff --git a/mROA/Implementation/RemoteContextRepository.cs b/mROA/Implementation/RemoteInstanceRepository.cs similarity index 73% rename from mROA/Implementation/RemoteContextRepository.cs rename to mROA/Implementation/RemoteInstanceRepository.cs index 8c8ded5..39ae9ef 100644 --- a/mROA/Implementation/RemoteContextRepository.cs +++ b/mROA/Implementation/RemoteInstanceRepository.cs @@ -5,7 +5,7 @@ using mROA.Abstract; namespace mROA.Implementation { - public class RemoteContextRepository : IContextRepository + public class RemoteInstanceRepository : IInstanceRepository { private List _producedRemoteEndpoints = new(); public static Dictionary RemoteTypes = new(); @@ -18,12 +18,12 @@ namespace mROA.Implementation throw new NotSupportedException(); } - public void ClearObject(ComplexObjectIdentifier id) + public void ClearObject(ComplexObjectIdentifier id, IEndPointContext context) { throw new NotSupportedException(); } - public T GetObject(ComplexObjectIdentifier id) + public T GetObject(ComplexObjectIdentifier id, IEndPointContext context) { var index = _producedRemoteEndpoints.Find(i => i.Identifier.Equals(id)); if (index is not null) @@ -33,25 +33,30 @@ namespace mROA.Implementation if (!RemoteTypes.TryGetValue(typeof(T), out var remoteType)) throw new NotSupportedException(); var representationModule = - _representationProducer.Produce(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + _representationProducer.Produce(context.OwnerId); var remote = (T)Activator.CreateInstance(remoteType, id.ContextId, - representationModule)!; + representationModule, context)!; _producedRemoteEndpoints.Add((remote as RemoteObjectBase)!); return remote; } - public object GetSingleObject(Type type, int ownerId) + public T GetSingletonObject(IEndPointContext context) where T : class, IShared + { + return GetSingletonObject(typeof(T), context) as T; + } + + public object GetSingletonObject(Type type, IEndPointContext context) { if (_representationProducer == null) throw new NullReferenceException("representation producer is not initialized"); var representationModule = - _representationProducer.Produce(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + _representationProducer.Produce(context.OwnerId); _producedRemoteEndpoints.Add((Activator.CreateInstance(RemoteTypes[type], -1, - representationModule) as RemoteObjectBase)!); + representationModule, context) as RemoteObjectBase)!); return _producedRemoteEndpoints.Last(); } diff --git a/mROA/Implementation/RemoteObjectBase.cs b/mROA/Implementation/RemoteObjectBase.cs index 7f2be6f..3410e97 100644 --- a/mROA/Implementation/RemoteObjectBase.cs +++ b/mROA/Implementation/RemoteObjectBase.cs @@ -10,7 +10,9 @@ namespace mROA.Implementation { public abstract class RemoteObjectBase : IDisposable { - protected bool Equals(RemoteObjectBase other) + private readonly IEndPointContext _context; + + public bool Equals(RemoteObjectBase other) { return _identifier.Equals(other._identifier); } @@ -31,10 +33,11 @@ namespace mROA.Implementation private readonly ComplexObjectIdentifier _identifier; private readonly IRepresentationModule _representationModule; - protected RemoteObjectBase(int id, IRepresentationModule representationModule) + protected RemoteObjectBase(int id, IRepresentationModule representationModule, IEndPointContext context) { _identifier = new ComplexObjectIdentifier { ContextId = id, OwnerId = representationModule.Id }; _representationModule = representationModule; + _context = context; } public int Id => _identifier.ContextId; @@ -56,44 +59,40 @@ namespace mROA.Implementation CommandId = methodId, ObjectId = _identifier, Parameters = parameters }; - await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request); + await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request, _context); var localTokenSource = new CancellationTokenSource(); - var successResponse = - _representationModule.GetMessageAsync>(request.Id, - EMessageType.FinishedCommandExecution, - localTokenSource.Token); - var errorResponse = - _representationModule.GetMessageAsync(requestId: request.Id, - EMessageType.ExceptionCommandExecution, localTokenSource.Token); + var responseRequestTask = _representationModule.GetSingle( + m => m.MessageType is EMessageType.FinishedCommandExecution or EMessageType.ExceptionCommandExecution, _context, + localTokenSource.Token, + m => m.MessageType is EMessageType.FinishedCommandExecution ? typeof(FinalCommandExecution) : null, + m => m.MessageType is EMessageType.ExceptionCommandExecution + ? typeof(ExceptionCommandExecution) + : null); - cancellationToken.Register(async () => + cancellationToken.Register(() => { #if TRACE Console.WriteLine("Cancelling task"); #endif - await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CancelRequest, + _representationModule.PostCallMessageAsync(request.Id, EMessageType.CancelRequest, new CancelRequest { Id = request.Id - }); - localTokenSource.Cancel(); + }, _context).ContinueWith(_ => localTokenSource.Cancel()); }); - Task.WaitAny(new Task[] - { - successResponse, errorResponse - }, cancellationToken); + var response = await responseRequestTask; - if (successResponse.IsCompletedSuccessfully) + if (response.Deserialized is FinalCommandExecution successResponse) { localTokenSource.Cancel(); - return successResponse.Result.Result!; + return successResponse.Result!; } localTokenSource.Cancel(); - throw errorResponse.Result.GetException(); + throw (response.Deserialized as ExceptionCommandExecution)!.GetException(); } protected async Task CallAsync(int methodId, object?[]? parameters = null, @@ -103,44 +102,52 @@ namespace mROA.Implementation { CommandId = methodId, ObjectId = _identifier, Parameters = parameters }; - await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request); + await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request, _context); var localTokenSource = new CancellationTokenSource(); - var successResponse = - _representationModule.GetMessageAsync(request.Id, - EMessageType.FinishedCommandExecution, - localTokenSource.Token); - var errorResponse = - _representationModule.GetMessageAsync(requestId: request.Id, - EMessageType.ExceptionCommandExecution, localTokenSource.Token); + var responseRequestTask = _representationModule.GetSingle( + m => m.MessageType is EMessageType.FinishedCommandExecution or EMessageType.ExceptionCommandExecution, _context, + localTokenSource.Token, + m => m.MessageType is EMessageType.FinishedCommandExecution ? typeof(FinalCommandExecution) : null, + m => m.MessageType is EMessageType.ExceptionCommandExecution + ? typeof(ExceptionCommandExecution) + : null); - cancellationToken.Register(async () => + + cancellationToken.Register(() => { #if TRACE Console.WriteLine("Cancelling task"); #endif - await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CancelRequest, + _representationModule.PostCallMessageAsync(request.Id, EMessageType.CancelRequest, new CancelRequest { Id = request.Id - }); - localTokenSource.Cancel(); + }, _context).ContinueWith(_ => localTokenSource.Cancel()); }); - Task.WaitAny(new Task[] - { - errorResponse, successResponse - }, cancellationToken); - + var responseRequest = await responseRequestTask; #if TRACE Console.WriteLine($"Handling message"); #endif - if (successResponse.IsCompletedSuccessfully) - return; + switch (responseRequest.MessageType) + { + case EMessageType.FinishedCommandExecution: + return; + case EMessageType.ExceptionCommandExecution: + throw (responseRequest.Deserialized as ExceptionCommandExecution)!.GetException(); + } + } - if (errorResponse.IsCompletedSuccessfully) - throw errorResponse.Result.GetException(); + protected async Task CallUntrustedAsync(int methodId, object?[]? parameters = null) + { + var request = new DefaultCallRequest + { + CommandId = methodId, ObjectId = _identifier, Parameters = parameters + }; + await _representationModule.PostCallMessageUntrustedAsync(request.Id, EMessageType.CallRequest, request, + _context); } public override string ToString() diff --git a/mROA/Implementation/RemoteObjectFactory.cs b/mROA/Implementation/RemoteObjectFactory.cs index fad21e3..8fa0873 100644 --- a/mROA/Implementation/RemoteObjectFactory.cs +++ b/mROA/Implementation/RemoteObjectFactory.cs @@ -9,14 +9,14 @@ namespace mROA.Implementation public static Dictionary RemoteTypes = new(); private IRepresentationModuleProducer? _representationProducer; - public T Produce(ComplexObjectIdentifier id) + public T Produce(ComplexObjectIdentifier id, IEndPointContext context) { if (_representationProducer == null) throw new NullReferenceException("representation producer is not initialized"); if (!RemoteTypes.TryGetValue(typeof(T), out var remoteType)) throw new NotSupportedException(); var representationModule = - _representationProducer.Produce(TransmissionConfig.OwnershipRepository.GetOwnershipId()); + _representationProducer.Produce(context.OwnerId); var remote = (T)Activator.CreateInstance(remoteType, id.ContextId, representationModule)!; return remote; diff --git a/mROA/Implementation/RepresentationModule.cs b/mROA/Implementation/RepresentationModule.cs index 6ea6fcd..d406d59 100644 --- a/mROA/Implementation/RepresentationModule.cs +++ b/mROA/Implementation/RepresentationModule.cs @@ -1,111 +1,110 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using mROA.Abstract; +#pragma warning disable CS8602 // Dereference of a possibly null reference. namespace mROA.Implementation { public class RepresentationModule : IRepresentationModule { - private INextGenerationInteractionModule? _interaction; - private ISerializationToolkit? _serialization; + private IChannelInteractionModule? _interaction; + private IContextualSerializationToolKit? _serialization; public void Inject(T dependency) { switch (dependency) { - case ISerializationToolkit toolkit: + case IContextualSerializationToolKit toolkit: _serialization = toolkit; break; - case INextGenerationInteractionModule interactionModule: + case IChannelInteractionModule interactionModule: _interaction = interactionModule; break; } } - - + public int Id => (_interaction ?? throw new NullReferenceException("Interaction is not initialized")) .ConnectionId; - public async Task GetMessageAsync(Guid? requestId, EMessageType? messageType, - CancellationToken token = default) + public async Task<(object? Deserialized, EMessageType MessageType)> GetSingle( + Predicate rule, IEndPointContext? context, + CancellationToken token = default, params Func[] converter) { - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); + var writer = _interaction.ReceiveChanel.Writer; + var reader = _interaction.ReceiveChanel.Reader; - var rawMessage = await GetRawMessage(requestId, messageType, token); - return _serialization.Deserialize(rawMessage)!; - } - public T GetMessage(Guid? requestId = null, EMessageType? messageType = null) - { - if (_serialization == null) - throw new NullReferenceException("Serialization toolkit is not initialized"); - - var rawMessage = GetRawMessage(requestId, messageType).GetAwaiter().GetResult(); - return _serialization.Deserialize(rawMessage)!; - } - - public async Task GetRawMessage(Guid? requestId = null, EMessageType? messageType = null, - CancellationToken token = default) - { - if (_interaction == null) - throw new NullReferenceException("Interaction toolkit is not initialized"); - - var fromBuffer = - _interaction.FirstByFilter(message => - (requestId is null || message.Id == requestId) && - (messageType is null || message.MessageType == messageType)); - - if (fromBuffer == null) + await foreach (var message in reader.ReadAllAsync(token)) { - while (token.IsCancellationRequested == false) + if (!rule(message)) { - var message = await _interaction.GetNextMessageReceiving(); - if ((requestId is not null && message.Id != requestId) || - (messageType is not null && message.MessageType != messageType)) - continue; - - _interaction.HandleMessage(message); - return message.Data; + await writer.WriteAsync(message, token); + continue; } + + var type = converter.Select(i => i(message)).First(i => i != null)!; + var deserialized = _serialization.Deserialize(message.Data, type, context); + return (deserialized, message.MessageType); } - if (fromBuffer == null) - { - return Array.Empty(); - } - - _interaction.HandleMessage(fromBuffer); - return fromBuffer.Data; + return (null, EMessageType.Unknown); } - public async Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull + public async IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream( + Predicate rule, IEndPointContext? context, + [EnumeratorCancellation] CancellationToken token = default, + params Func[] converter) { - await PostCallMessageAsync(id, eMessageType, payload, typeof(T)); - } + var writer = _interaction?.ReceiveChanel.Writer; + await foreach (var message in _interaction.ReceiveChanel.Reader.ReadAllAsync(token)) + { + if (!rule(message)) + { + await writer.WriteAsync(message, token); + continue; + } - public async Task PostCallMessageAsync(Guid id, EMessageType eMessageType, object payload, Type payloadType) + var type = converter.Select(i => i(message)).First(i => i != null)!; + var deserialized = _serialization.Deserialize(message.Data, type, context); + yield return (deserialized, message.MessageType)!; + } + } + + // public async Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload, + // IEndPointContext? context) where T : notnull + // { + // await this.PostCallMessageAsync(id, eMessageType, payload, context); + // } + + public async Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) where T : notnull { if (_interaction == null) throw new NullReferenceException("Interaction toolkit is not initialized"); if (_serialization == null) throw new NullReferenceException("Serialization toolkit is not initialized"); - var serialized = _serialization.Serialize(payload, payloadType); + var serialized = _serialization.Serialize(payload, context); await _interaction.PostMessageAsync(new NetworkMessageHeader { Id = id, MessageType = eMessageType, Data = serialized }); } - public void PostCallMessage(Guid id, EMessageType eMessageType, T payload) where T : notnull + public void PostCallMessage(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) + where T : notnull { - PostCallMessageAsync(id, eMessageType, payload).GetAwaiter().GetResult(); + PostCallMessageAsync(id, eMessageType, payload, context).GetAwaiter().GetResult(); } - public void PostCallMessage(Guid id, EMessageType eMessageType, object payload, Type payloadType) + public async Task PostCallMessageUntrustedAsync(Guid id, EMessageType eMessageType, T payload, + IEndPointContext? context) where T : notnull { - PostCallMessageAsync(id, eMessageType, payload, payloadType).GetAwaiter().GetResult(); + var serialized = _serialization.Serialize(payload, context); + await _interaction.PostMessageUntrustedAsync(new NetworkMessageHeader + { Id = id, MessageType = eMessageType, Data = serialized }); } } } \ No newline at end of file diff --git a/mROA/Implementation/SharedObjectShell.cs b/mROA/Implementation/SharedObjectShell.cs index 0fe0450..e6783c6 100644 --- a/mROA/Implementation/SharedObjectShell.cs +++ b/mROA/Implementation/SharedObjectShell.cs @@ -4,7 +4,7 @@ using mROA.Abstract; using mROA.Implementation.Attributes; // ReSharper disable UnusedMember.Global -#pragma warning disable CS8618, CS9264 +// #pragma warning disable CS8618, CS9264 namespace mROA.Implementation { @@ -20,18 +20,21 @@ namespace mROA.Implementation { private ComplexObjectIdentifier _identifier = ComplexObjectIdentifier.Null; - private T _value; + private T? _value; // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once UnusedMember.Global public SharedObjectShellShell() { + _value = default; + EndPointContext = new EndPointContext(); } // ReSharper disable once UnusedMember.Global // ReSharper disable once MemberCanBePrivate.Global - public SharedObjectShellShell(T value) + public SharedObjectShellShell(T value, IEndPointContext endPointContext) { + EndPointContext = endPointContext; Value = value; } @@ -40,7 +43,7 @@ namespace mROA.Implementation // ReSharper disable once MemberCanBePrivate.Global public T Value { - get => _value; + get => _value!; set { _value = value; @@ -57,15 +60,7 @@ namespace mROA.Implementation } } - [SerializationIgnore] - [JsonIgnore] - public IEndPointContext EndPointContext { get; set; } = new EndPointContext - { - RealRepository = TransmissionConfig.RealContextRepository, - RemoteRepository = TransmissionConfig.RemoteEndpointContextRepository, - HostId = TransmissionConfig.OwnershipRepository.GetHostOwnershipId(), - OwnerFunc = TransmissionConfig.OwnershipRepository.GetOwnershipId - }; + [SerializationIgnore] [JsonIgnore] public IEndPointContext EndPointContext { get; set; } public ComplexObjectIdentifier Identifier { @@ -77,18 +72,18 @@ namespace mROA.Implementation set { _identifier = value; - Value = GetDefaultContextRepository().GetObject(Identifier); + Value = GetDefaultContextRepository().GetObject(Identifier, EndPointContext); } } public object UniversalValue { - get => _value; + get => _value!; set => _value = (T)value; } - private IContextRepository GetDefaultContextRepository() => - (_identifier.OwnerId == EndPointContext.HostId + private IInstanceRepository GetDefaultContextRepository() => + (_identifier.OwnerId == EndPointContext.OwnerId ? EndPointContext.RealRepository : EndPointContext.RemoteRepository) ?? throw new NullReferenceException( @@ -96,7 +91,7 @@ namespace mROA.Implementation public static implicit operator T(SharedObjectShellShell value) => value.Value; - public static implicit operator SharedObjectShellShell(T value) => - new(value); + // public static implicit operator SharedObjectShellShell(T value) => + // new(value); } } \ No newline at end of file diff --git a/mROA/Implementation/TransmissionConfig.cs b/mROA/Implementation/TransmissionConfig.cs deleted file mode 100644 index 5302f9c..0000000 --- a/mROA/Implementation/TransmissionConfig.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using mROA.Abstract; - -namespace mROA.Implementation -{ -#pragma warning disable CS8618, CS9264 - public static class TransmissionConfig - { -#if TRACE - public static int TotalTransmittedBytes { get; set; } -#endif - private static IContextRepository? _realContextRepository; - private static IContextRepository? _remoteEndpointContextRepository; - private static IOwnershipRepository? _ownershipRepository; - - public static IContextRepository RealContextRepository - { - get => _realContextRepository ?? throw new NullReferenceException("RealContextRepository is null"); - set => _realContextRepository = value; - } - - public static IContextRepository RemoteEndpointContextRepository - { - get => _remoteEndpointContextRepository ?? - throw new NullReferenceException("RemoteEndpointContextRepository is null"); - set => _remoteEndpointContextRepository = value; - } - - public static IOwnershipRepository OwnershipRepository - { - get => _ownershipRepository ?? throw new NullReferenceException("OwnershipRepository is null"); - set => _ownershipRepository = value; - } - } -} \ No newline at end of file diff --git a/mROA/mROA.csproj b/mROA/mROA.csproj index 5c9aea9..6c53c78 100644 --- a/mROA/mROA.csproj +++ b/mROA/mROA.csproj @@ -4,7 +4,7 @@ netstandard2.1 enable mROA - 2.0.0 + 2.0.1 YaslePoy Fast and easy RPC with contex https://github.com/YaslePoy/mROA @@ -26,6 +26,7 @@ +