From eb323e8bf82e49680f1669ed4538b4551da2d3c2 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sun, 24 May 2026 18:27:22 +0300 Subject: [PATCH] Ver 3 --- Libraries/Esiur/Core/AsyncReply.cs | 31 +- Libraries/Esiur/Data/Codec.cs | 213 ++- Libraries/Esiur/Data/DataDeserializer.cs | 1479 +++++++++++------ Libraries/Esiur/Data/DataSerializer.cs | 355 ++-- Libraries/Esiur/Data/IParseResult.cs | 12 + Libraries/Esiur/Data/IRemoteRecord.cs | 12 + Libraries/Esiur/Data/Map.cs | 6 +- Libraries/Esiur/Data/ParseResult.cs | 18 + Libraries/Esiur/Data/ParsedTdu.cs | 362 +++- Libraries/Esiur/Data/PlainTdu.cs | 155 ++ Libraries/Esiur/Data/Record.cs | 14 +- Libraries/Esiur/Data/Tdu.cs | 80 +- Libraries/Esiur/Data/TduIdentifier.cs | 14 +- Libraries/Esiur/Data/Tru.cs | 1186 ++++++++----- Libraries/Esiur/Data/TruComposite.cs | 125 ++ Libraries/Esiur/Data/TruIdentifier.cs | 16 +- Libraries/Esiur/Data/TruPrimitive.cs | 95 ++ Libraries/Esiur/Data/TruTypeDef.cs | 133 ++ Libraries/Esiur/Data/TypeDefId.cs | 51 + Libraries/Esiur/Data/Types/ArgumentDef.cs | 25 +- Libraries/Esiur/Data/Types/ConstantDef.cs | 31 +- Libraries/Esiur/Data/Types/EventDef.cs | 24 +- Libraries/Esiur/Data/Types/FunctionDef.cs | 42 +- Libraries/Esiur/Data/Types/LocalTypeDef.cs | 579 +++++++ Libraries/Esiur/Data/Types/PropertyDef.cs | 30 +- Libraries/Esiur/Data/Types/RemoteTypeDef.cs | 130 ++ Libraries/Esiur/Data/Types/TypeDef.cs | 686 +------- Libraries/Esiur/Esiur.csproj | 16 +- Libraries/Esiur/Net/Packets/EpAuthPacket.cs | 63 +- .../Esiur/Net/Packets/EpAuthPacketHeader.cs | 2 + .../Esiur/Net/Packets/EpAuthPacketMethod.cs | 41 + Libraries/Esiur/Net/Packets/EpPacket.cs | 22 +- Libraries/Esiur/Protocol/EpConnection.cs | 702 +++++--- .../Esiur/Protocol/EpConnectionConfig.cs | 35 - .../Esiur/Protocol/EpConnectionContext.cs | 58 + .../Esiur/Protocol/EpConnectionProtocol.cs | 1414 +++++++++------- Libraries/Esiur/Protocol/EpResource.cs | 34 +- .../Protocol/EpResourceAttachRequestInfo.cs | 8 +- Libraries/Esiur/Protocol/EpServer.cs | 2 +- Libraries/Esiur/Proxy/ResourceGenerator.cs | 2 +- Libraries/Esiur/Proxy/TypeDefGenerator.cs | 77 +- Libraries/Esiur/Resource/Instance.cs | 12 +- Libraries/Esiur/Resource/RemoteAttribute.cs | 19 + Libraries/Esiur/Resource/ResourceContext.cs | 31 + Libraries/Esiur/Resource/Warehouse.cs | 492 ++++-- .../Security/Authority/Authentication.cs | 64 - .../Authority/AuthenticationContext.cs | 16 +- ...esponder.cs => AuthenticationDirection.cs} | 6 +- .../Authority/AuthenticationHandler.cs | 10 + .../Authority/AuthenticationMaterial.cs | 12 + .../Authority/AuthenticationMaterialType.cs | 14 + .../Authority/AuthenticationResult.cs | 14 +- .../Authority/IAuthenticationHandler.cs | 11 +- .../Authority/IAuthenticationInitiator.cs | 13 - .../IAuthenticationMethodProvider.cs | 16 - .../Authority/IAuthenticationProvider.cs | 14 + .../InitiatorAuthenticationContext.cs | 19 - .../Authority/PpapAuthenticationProvider.cs | 16 + .../PasswordAuthenticationHandler.cs | 606 +++++++ .../PasswordAuthenticationProvider.cs | 39 + .../ResponderAuthenticationContext.cs | 18 - Libraries/Esiur/Security/Authority/Session.cs | 3 +- .../Permissions/IPermissionsManager.cs | 1 + .../Permissions/UserPermissionsManager.cs | 4 +- .../EsiurExtensions.cs | 2 +- .../EsiurProxyRewrite.cs | 4 +- .../MongoDBStoreGeneric.cs | 2 +- Tests/Features/Functional/MyService.cs | 42 +- Tests/Features/Functional/Program.cs | 23 +- 69 files changed, 6532 insertions(+), 3371 deletions(-) create mode 100644 Libraries/Esiur/Data/IParseResult.cs create mode 100644 Libraries/Esiur/Data/IRemoteRecord.cs create mode 100644 Libraries/Esiur/Data/ParseResult.cs create mode 100644 Libraries/Esiur/Data/PlainTdu.cs create mode 100644 Libraries/Esiur/Data/TruComposite.cs create mode 100644 Libraries/Esiur/Data/TruPrimitive.cs create mode 100644 Libraries/Esiur/Data/TruTypeDef.cs create mode 100644 Libraries/Esiur/Data/TypeDefId.cs create mode 100644 Libraries/Esiur/Data/Types/LocalTypeDef.cs create mode 100644 Libraries/Esiur/Data/Types/RemoteTypeDef.cs create mode 100644 Libraries/Esiur/Net/Packets/EpAuthPacketMethod.cs delete mode 100644 Libraries/Esiur/Protocol/EpConnectionConfig.cs create mode 100644 Libraries/Esiur/Protocol/EpConnectionContext.cs create mode 100644 Libraries/Esiur/Resource/RemoteAttribute.cs create mode 100644 Libraries/Esiur/Resource/ResourceContext.cs delete mode 100644 Libraries/Esiur/Security/Authority/Authentication.cs rename Libraries/Esiur/Security/Authority/{IAuthenticationResponder.cs => AuthenticationDirection.cs} (52%) create mode 100644 Libraries/Esiur/Security/Authority/AuthenticationHandler.cs create mode 100644 Libraries/Esiur/Security/Authority/AuthenticationMaterial.cs create mode 100644 Libraries/Esiur/Security/Authority/AuthenticationMaterialType.cs delete mode 100644 Libraries/Esiur/Security/Authority/IAuthenticationInitiator.cs delete mode 100644 Libraries/Esiur/Security/Authority/IAuthenticationMethodProvider.cs create mode 100644 Libraries/Esiur/Security/Authority/IAuthenticationProvider.cs delete mode 100644 Libraries/Esiur/Security/Authority/InitiatorAuthenticationContext.cs create mode 100644 Libraries/Esiur/Security/Authority/PpapAuthenticationProvider.cs create mode 100644 Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationHandler.cs create mode 100644 Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationProvider.cs delete mode 100644 Libraries/Esiur/Security/Authority/ResponderAuthenticationContext.cs diff --git a/Libraries/Esiur/Core/AsyncReply.cs b/Libraries/Esiur/Core/AsyncReply.cs index d2eb82f..30dc518 100644 --- a/Libraries/Esiur/Core/AsyncReply.cs +++ b/Libraries/Esiur/Core/AsyncReply.cs @@ -91,7 +91,7 @@ public class AsyncReply } //int timeoutMilliseconds = 0; - public AsyncReply Timeout(int milliseconds, Action callback = null) + public void Timeout(int milliseconds, Action callback = null) { //timeoutMilliseconds = milliseconds; @@ -107,7 +107,6 @@ public class AsyncReply } }); - return this; } public object Wait(int millisecondsTimeout) @@ -236,7 +235,7 @@ public class AsyncReply return this; } - public AsyncReply Trigger(object result) + public void Trigger(object result) { lock (asyncLock) { @@ -245,13 +244,13 @@ public class AsyncReply //timeout?.Dispose(); if (exception != null) - return this; + return; //if (Debug) // Console.WriteLine($"AsyncReply: {Id} Trigger"); if (resultReady) - return this; + return; this.result = result; @@ -269,15 +268,15 @@ public class AsyncReply } - return this; + return; } - public AsyncReply TriggerError(Exception exception) + public void TriggerError(Exception exception) { //timeout?.Dispose(); if (resultReady) - return this; + return; if (exception is AsyncException) this.exception = exception as AsyncException; @@ -297,10 +296,9 @@ public class AsyncReply mutex?.Set(); - return this; } - public AsyncReply TriggerProgress(ProgressType type, uint value, uint max) + public void TriggerProgress(ProgressType type, uint value, uint max) { //timeout?.Dispose(); @@ -308,10 +306,10 @@ public class AsyncReply foreach (var cb in progressCallbacks) cb(type, value, max); - return this; + return; } - public AsyncReply TriggerWarning(byte level, string message) + public void TriggerWarning(byte level, string message) { //timeout?.Dispose(); @@ -319,11 +317,11 @@ public class AsyncReply foreach (var cb in warningCallbacks) cb(level, message); - return this; + return ; } - public AsyncReply TriggerPropagation(object value) + public void TriggerPropagation(object value) { //timeout?.Dispose(); @@ -331,12 +329,12 @@ public class AsyncReply foreach (var cb in propagationCallbacks) cb(value); - return this; + return; } - public AsyncReply TriggerChunk(object value) + public void TriggerChunk(object value) { //timeout?.Dispose(); @@ -347,7 +345,6 @@ public class AsyncReply cb(value); - return this; } public AsyncAwaiter GetAwaiter() diff --git a/Libraries/Esiur/Data/Codec.cs b/Libraries/Esiur/Data/Codec.cs index 913082e..a9e0dba 100644 --- a/Libraries/Esiur/Data/Codec.cs +++ b/Libraries/Esiur/Data/Codec.cs @@ -100,12 +100,7 @@ public static class Codec static AsyncParser[] TypedAsyncParsers = new AsyncParser[] { - DataDeserializer.RecordParserAsync, - DataDeserializer.TypedListParserAsync, - DataDeserializer.TypedMapParserAsync, - DataDeserializer.TupleParserAsync, - DataDeserializer.EnumParserAsync, - DataDeserializer.ConstantParserAsync, + DataDeserializer.TypedParserAsync, }; static AsyncParser[] ExtendedAsyncParsers = new AsyncParser[] @@ -170,12 +165,13 @@ public static class Codec static SyncParser[] TypedParsers = new SyncParser[] { - DataDeserializer.RecordParser, - DataDeserializer.TypedListParser, - DataDeserializer.TypedMapParser, - DataDeserializer.TupleParser, - DataDeserializer.EnumParser, - DataDeserializer.ConstantParser, + DataDeserializer.TypedParser, + //DataDeserializer.RecordParser, + //DataDeserializer.TypedListParser, + //DataDeserializer.TypedMapParser, + //DataDeserializer.TupleParser, + //DataDeserializer.EnumParser, + //DataDeserializer.ConstantParser, }; static SyncParser[] ExtendedParsers = new SyncParser[] @@ -192,81 +188,197 @@ public static class Codec /// EpConnection is required in case a structure in the array holds items at the other end. /// DataType, in case the data is not prepended with DataType /// Value - public static (uint, object) ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence) + public static AsyncReply> ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence) { + var rt = new AsyncReply>(); - var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length); + ParsedTdu.ParseAsync(data, offset, (uint)data.Length, connection).Then(tdu => + { + if (tdu.Class == TduClass.Invalid) + throw new NullReferenceException("DataType can't be parsed."); - if (tdu.Class == TduClass.Invalid) - throw new NullReferenceException("DataType can't be parsed."); + object result; - if (tdu.Class == TduClass.Fixed) - { - return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence)); - } - else if (tdu.Class == TduClass.Dynamic) - { - return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence)); - } - else if (tdu.Class == TduClass.Typed) - { - return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence)); - } - else // if (tt.Class == TDUClass.Extension) - { - return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence)); + if (tdu.Class == TduClass.Fixed) + { + result = FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence); + + } + else if (tdu.Class == TduClass.Dynamic) + { + result = DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + else if (tdu.Class == TduClass.Typed) + { + result = TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + else // if (tt.Class == TDUClass.Extension) + { + result = ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + + if (result is AsyncReply asyncReply) + { + asyncReply.Then(value => + { + rt.Trigger(new ParseResult(value, (uint)tdu.TotalLength)); + }); + } + else + { + rt.Trigger(new ParseResult(result, (uint)tdu.TotalLength)); + } + }); + + return rt; - } } - public static (uint, object) ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static object ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { if (tdu.Class == TduClass.Invalid) throw new NullReferenceException("DataType can't be parsed."); if (tdu.Class == TduClass.Fixed) { - return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence)); + return FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence); } else if (tdu.Class == TduClass.Dynamic) { - return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence)); + return DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence); } else if (tdu.Class == TduClass.Typed) { - return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence)); + return TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence); } else // if (tt.Class == TDUClass.Extension) { - return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence)); - + return ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence); } } - public static (uint, object) ParseSync(ParsedTdu tdu, Warehouse warehouse) + public static object Parse(PlainTdu plainTdu, EpConnection connection, uint[] requestSequence) { - if (tdu.Class == TduClass.Fixed) + var parsedTdu = ParsedTdu.Parse(plainTdu.Data, plainTdu.TduOffset, (uint)plainTdu.Ends, connection); + + if (parsedTdu is ParsedTdu tdu) { - return ((uint)tdu.TotalLength, FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse)); + if (tdu.Class == TduClass.Invalid) + throw new NullReferenceException("TDU can't be parsed."); + + if (tdu.Class == TduClass.Fixed) + { + return FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence); + } + else if (tdu.Class == TduClass.Dynamic) + { + return DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + else if (tdu.Class == TduClass.Typed) + { + return TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + else // if (tt.Class == TDUClass.Extension) + { + return ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + } + else if (parsedTdu is AsyncReply asyncReply) + { + var rt = new AsyncReply(); + asyncReply.Then(tdu => + { + if (tdu.Class == TduClass.Invalid) + throw new NullReferenceException("TDU can't be parsed."); + + object result; + + if (tdu.Class == TduClass.Fixed) + { + result = FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence); + } + else if (tdu.Class == TduClass.Dynamic) + { + result = DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + else if (tdu.Class == TduClass.Typed) + { + result = TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + else // if (tt.Class == TDUClass.Extension) + { + result = ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence); + } + + if (result is AsyncReply resultReply) + { + resultReply.Then(rt.Trigger) + .Error(rt.TriggerError); + } + else + { + rt.Trigger(result); + } + }); + + return rt; + } + + throw new NotImplementedException(); + } + + public static object ParseSync(PlainTdu plainTdu, Warehouse warehouse) + { + var tdu = ParsedTdu.ParseSync(plainTdu.Data, plainTdu.TduOffset, plainTdu.Ends, warehouse); + + if (tdu.Class == TduClass.Invalid) + { + throw new Exception("Invalid TDU."); + } + else if (tdu.Class == TduClass.Fixed) + { + return FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse); } else if (tdu.Class == TduClass.Dynamic) { - return ((uint)tdu.TotalLength, DynamicParsers[tdu.Index](tdu, warehouse)); + return DynamicParsers[tdu.Index](tdu, warehouse); } else if (tdu.Class == TduClass.Typed) { - return ((uint)tdu.TotalLength, TypedParsers[tdu.Index](tdu, warehouse)); + return TypedParsers[tdu.Index](tdu, warehouse); } else // Extension { - return ((uint)tdu.TotalLength, ExtendedParsers[tdu.Index](tdu, warehouse)); + return ExtendedParsers[tdu.Index](tdu, warehouse); } } + public static object ParseSync(ParsedTdu tdu, Warehouse warehouse) + { + if (tdu.Class == TduClass.Fixed) + { + return FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse); + } + else if (tdu.Class == TduClass.Dynamic) + { + return DynamicParsers[tdu.Index](tdu, warehouse); + } + else if (tdu.Class == TduClass.Typed) + { + return TypedParsers[tdu.Index](tdu, warehouse); + } + else // Extension + { + return ExtendedParsers[tdu.Index](tdu, warehouse); + } + + } + + public static (uint, object) ParseSync(byte[] data, uint offset, Warehouse warehouse) { - var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length); + var tdu = ParsedTdu.ParseSync(data, offset, (uint)data.Length, warehouse); if (tdu.Class == TduClass.Invalid) throw new NullReferenceException("DataType can't be parsed."); @@ -301,7 +413,7 @@ public static class Codec throw new NullReferenceException("Resource is null."); if (resource is EpResource) - if (((EpResource)(resource)).DistributedResourceConnection == connection) + if (((EpResource)(resource)).ResourceConnection == connection) return true; return false; @@ -385,7 +497,7 @@ public static class Codec ComposeInternal(object valueOrSource, Warehouse warehouse, EpConnection connection) { if (valueOrSource == null) - return new Tdu(TduIdentifier.Null, null, 0); + return new Tdu(TduIdentifier.Null, null, 0, null, null); var type = valueOrSource.GetType(); @@ -412,7 +524,7 @@ public static class Codec valueOrSource = ((IUserType)valueOrSource).Get(); if (valueOrSource == null) - return new Tdu(TduIdentifier.Null, null, 0); + return new Tdu(TduIdentifier.Null, null, 0, null, null); type = valueOrSource.GetType(); @@ -488,7 +600,7 @@ public static class Codec } - return new Tdu(TduIdentifier.Null, null, 0); + return new Tdu(TduIdentifier.Null, null, 0, null, null); } @@ -541,6 +653,9 @@ public static class Codec { while (type != null) { + if (type == typeof(object)) + return false; + if (type == iface) return true; diff --git a/Libraries/Esiur/Data/DataDeserializer.cs b/Libraries/Esiur/Data/DataDeserializer.cs index dc78633..faf91f2 100644 --- a/Libraries/Esiur/Data/DataDeserializer.cs +++ b/Libraries/Esiur/Data/DataDeserializer.cs @@ -5,12 +5,14 @@ using Esiur.Data.Types; using Esiur.Misc; using Esiur.Protocol; using Esiur.Resource; +using Esiur.Security.Membership; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.InteropServices.ComTypes; using System.Text; namespace Esiur.Data; @@ -59,206 +61,206 @@ public static class DataDeserializer public static object UInt8ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - return tdu.Data[tdu.Offset]; + return tdu.Data[tdu.PayloadOffset]; } public static object UInt8Parser(ParsedTdu tdu, Warehouse warehouse) { - return tdu.Data[tdu.Offset]; + return tdu.Data[tdu.PayloadOffset]; } public static object Int8ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - return (sbyte)tdu.Data[tdu.Offset]; + return (sbyte)tdu.Data[tdu.PayloadOffset]; } public static object Int8Parser(ParsedTdu tdu, Warehouse warehouse) { - return (sbyte)tdu.Data[tdu.Offset]; + return (sbyte)tdu.Data[tdu.PayloadOffset]; } public static unsafe object Char16ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(char*)ptr; } public static unsafe object Char16Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(char*)ptr; } public static object Char8ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - return (char)tdu.Data[tdu.Offset]; + return (char)tdu.Data[tdu.PayloadOffset]; } public static object Char8Parser(ParsedTdu tdu, Warehouse warehouse) { - return (char)tdu.Data[tdu.Offset]; + return (char)tdu.Data[tdu.PayloadOffset]; } public static unsafe object Int16ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(short*)ptr; } public static unsafe object Int16Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(short*)ptr; } public static unsafe object UInt16ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(ushort*)ptr; } public static unsafe object UInt16Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(ushort*)ptr; } public static unsafe object Int32ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(int*)ptr; } public static unsafe object Int32Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(int*)ptr; } public static unsafe object UInt32ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(uint*)ptr; } public static unsafe object UInt32Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(uint*)ptr; } public static unsafe object Float32ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(float*)ptr; } public static unsafe object Float32Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(float*)ptr; } public static unsafe object Float64ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(double*)ptr; } public static unsafe object Float64Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(double*)ptr; } public static unsafe object Decimal128ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(decimal*)ptr; } public static unsafe object Decimal128Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(decimal*)ptr; } public static unsafe object UUIDParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - return new Uuid(tdu.Data, tdu.Offset); + return new Uuid(tdu.Data, tdu.PayloadOffset); } public static unsafe object UUIDParser(ParsedTdu tdu, Warehouse warehouse) { - return new Uuid(tdu.Data, tdu.Offset); + return new Uuid(tdu.Data, tdu.PayloadOffset); } public static unsafe object Int128ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr1 = &tdu.Data[tdu.Offset]) - fixed (byte* ptr2 = &tdu.Data[tdu.Offset + 8]) + fixed (byte* ptr1 = &tdu.Data[tdu.PayloadOffset]) + fixed (byte* ptr2 = &tdu.Data[tdu.PayloadOffset + 8]) return new Int128(*(ulong*)ptr1, *(ulong*)ptr2); } public static unsafe object Int128Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr1 = &tdu.Data[tdu.Offset]) - fixed (byte* ptr2 = &tdu.Data[tdu.Offset + 8]) + fixed (byte* ptr1 = &tdu.Data[tdu.PayloadOffset]) + fixed (byte* ptr2 = &tdu.Data[tdu.PayloadOffset + 8]) return new Int128(*(ulong*)ptr1, *(ulong*)ptr2); } public static unsafe object UInt128ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr1 = &tdu.Data[tdu.Offset]) - fixed (byte* ptr2 = &tdu.Data[tdu.Offset + 8]) + fixed (byte* ptr1 = &tdu.Data[tdu.PayloadOffset]) + fixed (byte* ptr2 = &tdu.Data[tdu.PayloadOffset + 8]) return new UInt128(*(ulong*)ptr1, *(ulong*)ptr2); } public static unsafe object UInt128Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr1 = &tdu.Data[tdu.Offset]) - fixed (byte* ptr2 = &tdu.Data[tdu.Offset + 8]) + fixed (byte* ptr1 = &tdu.Data[tdu.PayloadOffset]) + fixed (byte* ptr2 = &tdu.Data[tdu.PayloadOffset + 8]) return new UInt128(*(ulong*)ptr1, *(ulong*)ptr2); } public static unsafe object Int64ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(long*)ptr; } public static unsafe object Int64Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(long*)ptr; } public static unsafe object UInt64ParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(ulong*)ptr; } public static unsafe object UInt64Parser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return *(ulong*)ptr; } public static unsafe object DateTimeParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return new DateTime(*(long*)ptr, DateTimeKind.Utc); } public static unsafe object DateTimeParser(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return new DateTime(*(long*)ptr, DateTimeKind.Utc); } @@ -266,7 +268,7 @@ public static class DataDeserializer public static object ResourceLinkParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - var link = tdu.Data.GetString(tdu.Offset, (uint)tdu.ContentLength); + var link = tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength); if (connection == null) { return new ResourceLink(link); @@ -279,55 +281,55 @@ public static class DataDeserializer public static object ResourceLinkParser(ParsedTdu tdu, Warehouse warehouse) { - var link = tdu.Data.GetString(tdu.Offset, (uint)tdu.ContentLength); + var link = tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength); return new ResourceLink(link); } public static unsafe object ResourceParser8Async(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { if (connection == null) - return new ResourceId(false, tdu.Data[tdu.Offset]); + return new ResourceId(false, tdu.Data[tdu.PayloadOffset]); else - return connection.Fetch(tdu.Data[tdu.Offset], requestSequence); + return connection.FetchResource(tdu.Data[tdu.PayloadOffset], requestSequence); } public static unsafe object ResourceParser8(ParsedTdu tdu, Warehouse warehouse) { - return new ResourceId(false, tdu.Data[tdu.Offset]); + return new ResourceId(false, tdu.Data[tdu.PayloadOffset]); } public static unsafe object LocalResourceParser8Async(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { if (connection == null) - return new ResourceId(true, tdu.Data[tdu.Offset]); + return new ResourceId(true, tdu.Data[tdu.PayloadOffset]); else - return connection.Instance.Warehouse.GetById(tdu.Data[tdu.Offset]); + return connection.Instance.Warehouse.GetById(tdu.Data[tdu.PayloadOffset]); } public static unsafe object LocalResourceParser8(ParsedTdu tdu, Warehouse warehouse) { - return new ResourceId(true, tdu.Data[tdu.Offset]); + return new ResourceId(true, tdu.Data[tdu.PayloadOffset]); } public static unsafe object ResourceParser16Async(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) if (connection == null) return new ResourceId(false, *(ushort*)ptr); else - return connection.Fetch(*(ushort*)ptr, requestSequence); + return connection.FetchResource(*(ushort*)ptr, requestSequence); } public static unsafe object ResourceParser16(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return new ResourceId(false, *(ushort*)ptr); } public static unsafe object LocalResourceParser16Async(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) if (connection == null) return new ResourceId(true, *(ushort*)ptr); else @@ -336,29 +338,29 @@ public static class DataDeserializer public static unsafe object LocalResourceParser16(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return new ResourceId(true, *(ushort*)ptr); } public static unsafe object ResourceParser32Async(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) if (connection == null) return new ResourceId(false, *(uint*)ptr); else - return connection.Fetch(*(uint*)ptr, requestSequence); + return connection.FetchResource(*(uint*)ptr, requestSequence); } public static unsafe object ResourceParser32(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return new ResourceId(false, *(uint*)ptr); } public static unsafe object LocalResourceParser32Async(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) if (connection == null) return new ResourceId(true, *(uint*)ptr); else @@ -367,37 +369,44 @@ public static class DataDeserializer public static unsafe object LocalResourceParser32(ParsedTdu tdu, Warehouse warehouse) { - fixed (byte* ptr = &tdu.Data[tdu.Offset]) + fixed (byte* ptr = &tdu.Data[tdu.PayloadOffset]) return new ResourceId(true, *(uint*)ptr); } public static unsafe object RawDataParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - return tdu.Data.Clip(tdu.Offset, (uint)tdu.ContentLength); + return tdu.Data.Clip(tdu.PayloadOffset, (uint)tdu.PayloadLength); } public static unsafe object RawDataParser(ParsedTdu tdu, Warehouse warehouse) { - return tdu.Data.Clip(tdu.Offset, (uint)tdu.ContentLength); + return tdu.Data.Clip(tdu.PayloadOffset, (uint)tdu.PayloadLength); } public static unsafe object StringParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { - return tdu.Data.GetString(tdu.Offset, (uint)tdu.ContentLength); + return tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength); } public static unsafe object StringParser(ParsedTdu tdu, Warehouse warehouse) { - return tdu.Data.GetString(tdu.Offset, (uint)tdu.ContentLength); + return tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength); } - public static unsafe object RecordParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static async AsyncReply RecordParserAsync(ParsedTdu tdu, TypeDef recordTypeDef, EpConnection connection, uint[] requestSequence) { - var typeId = tdu.Metadata.GetUUID(0); - var typeDef = connection.Instance.Warehouse.GetTypeDefById(typeId, - TypeDefKind.Record); + //if (tdu.Metadata.TypeDefId == null) + // throw new Exception("TypeDefId metadata is required for record parsing."); + + //TypeDefId typeDefId = tdu.Metadata.TypeDefId.Value; + //TypeDef typeDef = null; + + + //.GetUInt32(0); + //var typeDef = connection.Instance.Warehouse.GetTypeDefById(tdu.Metadata.TypeDefId, + // TypeDefKind.Record); var rt = new AsyncReply(); @@ -406,140 +415,14 @@ public static class DataDeserializer ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; - var ends = offset + (uint)length; - - var initRecord = (TypeDef typeDef) => - { - for (var i = 0; i < typeDef.Properties.Length; i++) - { - current = ParsedTdu.Parse(tdu.Data, offset, ends); - - if (current.Class == TduClass.Invalid) - throw new Exception("Unknown type."); - - - if (current.Identifier == TduIdentifier.TypeContinuation) - { - current.Class = previous.Value.Class; - current.Identifier = previous.Value.Identifier; - current.Metadata = previous.Value.Metadata; - } - else if (current.Identifier == TduIdentifier.TypeOfTarget) - { - var (idf, mt) = typeDef.Properties[i].ValueType.GetMetadata(); - current.Class = TduClass.Typed; - current.Identifier = idf; - current.Metadata = mt; - current.Index = (int)idf & 0x7; - } - - var (cs, reply) = Codec.ParseAsync(current, connection, requestSequence); - - list.Add(reply); - - if (cs > 0) - { - offset += (uint)current.TotalLength; - length -= (uint)current.TotalLength; - previous = current; - } - else - throw new Exception("Error while parsing structured data"); - - } - - list.Seal(); - - list.Then(results => - { - if (typeDef.DefinedType != null) - { - - var record = Activator.CreateInstance(typeDef.DefinedType) as IRecord; - for (var i = 0; i < typeDef.Properties.Length; i++) - { - try - { - var v = RuntimeCaster.Cast(results[i], typeDef.Properties[i].PropertyInfo.PropertyType); - typeDef.Properties[i].PropertyInfo.SetValue(record, v); - } - catch (Exception ex) - { - Global.Log(ex); - } - } - - rt.Trigger(record); - } - else - { - var record = new Record(); - - for (var i = 0; i < typeDef.Properties.Length; i++) - record.Add(typeDef.Properties[i].Name, results[i]); - - rt.Trigger(record); - } - - }).Error(e => - { - //Console.WriteLine(e); - rt.TriggerError(e); - }); - - }; - - - if (typeDef != null) - { - initRecord(typeDef); - } - else if (connection != null) - { - // try to get the TypeDef from the other end - connection.GetTypeDefById(typeId).Then(tmp => - { - initRecord(tmp); - }).Error(x => rt.TriggerError(x)); - } - else - { - initRecord(null); - } - - return rt; - - - } - - - public static unsafe object RecordParser(ParsedTdu tdu, Warehouse warehouse) - { - var typeId = tdu.Metadata.GetUUID(0); - var typeDef = warehouse.GetTypeDefById(typeId, TypeDefKind.Record); - - if (typeDef == null) - { - // @TODO: add parse if no TypeDef settings - throw new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TypeDefNotFound, - "TypeDef not found for record."); - } - - var list = new List(); - - ParsedTdu current; - ParsedTdu? previous = null; - - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; - for (var i = 0; i < typeDef.Properties.Length; i++) + for (var i = 0; i < recordTypeDef.Properties.Length; i++) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = await ParsedTdu.ParseAsync(tdu.Data, offset, ends, connection); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); @@ -553,18 +436,20 @@ public static class DataDeserializer } else if (current.Identifier == TduIdentifier.TypeOfTarget) { - var (idf, mt) = typeDef.Properties[i].ValueType.GetMetadata(); + //var idf = typeDef.Properties[i].ValueType.GetMetadata(); + var propTru = recordTypeDef.Properties[i].ValueType; + current.Class = TduClass.Typed; - current.Identifier = idf; - current.Metadata = mt; - current.Index = (int)idf & 0x7; + current.Identifier = TduIdentifier.Typed;// propTru.Identifier;// idf; + current.Metadata = propTru;// mt; + current.Index = (int)TduIdentifier.Typed & 0x7;// (int)idf & 0x7; } - var (cs, reply) = Codec.ParseSync(current, warehouse); + var value = Codec.ParseAsync(current, connection, requestSequence); - list.Add(reply); + list.Add(value); - if (cs > 0) + if (current.TotalLength > 0) { offset += (uint)current.TotalLength; length -= (uint)current.TotalLength; @@ -575,16 +460,23 @@ public static class DataDeserializer } - if (typeDef.DefinedType != null) + list.Seal(); + + var results = await list; + + Type recordType = recordTypeDef is LocalTypeDef localTypeDef ? localTypeDef.DefinedType + : recordTypeDef is RemoteTypeDef remoteTypeDef ? remoteTypeDef.ProxyType : null; + + if (recordType != null) { - var record = Activator.CreateInstance(typeDef.DefinedType) as IRecord; - for (var i = 0; i < typeDef.Properties.Length; i++) + var record = Activator.CreateInstance(recordType) as IRecord; + for (var i = 0; i < recordTypeDef.Properties.Length; i++) { try { - var v = RuntimeCaster.Cast(list[i], typeDef.Properties[i].PropertyInfo.PropertyType); - typeDef.Properties[i].PropertyInfo.SetValue(record, v); + var v = RuntimeCaster.Cast(results[i], recordTypeDef.Properties[i].PropertyInfo.PropertyType); + recordTypeDef.Properties[i].PropertyInfo.SetValue(record, v); } catch (Exception ex) { @@ -596,10 +488,154 @@ public static class DataDeserializer } else { - var record = new Record(); + var record = new Record(recordTypeDef); - for (var i = 0; i < typeDef.Properties.Length; i++) - record.Add(typeDef.Properties[i].Name, list[i]); + for (var i = 0; i < recordTypeDef.Properties.Length; i++) + record.Add(recordTypeDef.Properties[i].Name, results[i]); + + return record; + } + + + //list.Then(results => + //{ + // Type recordType = recordTypeDef is LocalTypeDef localTypeDef ? localTypeDef.DefinedType + // : recordTypeDef is RemoteTypeDef remoteTypeDef ? remoteTypeDef.ProxyType : null; + + // if (recordType != null) + // { + + // var record = Activator.CreateInstance(recordType) as IRecord; + // for (var i = 0; i < recordTypeDef.Properties.Length; i++) + // { + // try + // { + // var v = RuntimeCaster.Cast(results[i], recordTypeDef.Properties[i].PropertyInfo.PropertyType); + // recordTypeDef.Properties[i].PropertyInfo.SetValue(record, v); + // } + // catch (Exception ex) + // { + // Global.Log(ex); + // } + // } + + // rt.Trigger(record); + // } + // else + // { + // var record = new Record(recordTypeDef); + + // for (var i = 0; i < recordTypeDef.Properties.Length; i++) + // record.Add(recordTypeDef.Properties[i].Name, results[i]); + + // rt.Trigger(record); + // } + + //}).Error(e => + //{ + // rt.TriggerError(e); + //}); + + //return rt; + } + + + public static unsafe object RecordParser(ParsedTdu tdu, LocalTypeDef recordTypeDef, Warehouse warehouse) + { + + //if (tdu.Metadata is not TypeDefId) + // throw new Exception("Unsupported metadata."); + + + //TypeDefId typeDefId = (TypeDefId)tdu.Metadata; + + //if (typeDefId.Remote) + // throw new Exception("Unsupported in synchronous parsing."); + + //var typeDef = warehouse.GetLocalTypeDefById(recordTypeDef.Value); + + if (recordTypeDef == null) + { + // @TODO: add parse if no TypeDef settings + throw new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TypeDefNotFound, + "TypeDef not found for record."); + } + + var list = new List(); + + ParsedTdu current; + ParsedTdu? previous = null; + + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; + var ends = offset + (uint)length; + + + for (var i = 0; i < recordTypeDef.Properties.Length; i++) + { + current = ParsedTdu.ParseSync(tdu.Data, offset, ends, warehouse); + + if (current.Class == TduClass.Invalid) + throw new Exception("Unknown type."); + + + if (current.Identifier == TduIdentifier.TypeContinuation) + { + current.Class = previous.Value.Class; + current.Identifier = previous.Value.Identifier; + current.Metadata = previous.Value.Metadata; + } + else if (current.Identifier == TduIdentifier.TypeOfTarget) + { + //var (idf, mt) = recordTypeDef.Properties[i].ValueType.GetMetadata(); + + var propTru = recordTypeDef.Properties[i].ValueType; + current.Class = TduClass.Typed; + current.Identifier = TduIdentifier.Typed;// idf; + current.Metadata = propTru;// mt; + current.Index = (int)TduIdentifier.Typed & 0x7; + } + + var reply = Codec.ParseSync(current, warehouse); + + list.Add(reply); + + if (current.TotalLength > 0) + { + offset += (uint)current.TotalLength; + length -= (uint)current.TotalLength; + previous = current; + } + else + throw new Exception("Error while parsing structured data"); + + } + + if (recordTypeDef.DefinedType != null) + { + + var record = Activator.CreateInstance(recordTypeDef.DefinedType) as IRecord; + for (var i = 0; i < recordTypeDef.Properties.Length; i++) + { + try + { + var v = RuntimeCaster.Cast(list[i], recordTypeDef.Properties[i].PropertyInfo.PropertyType); + recordTypeDef.Properties[i].PropertyInfo.SetValue(record, v); + } + catch (Exception ex) + { + Global.Log(ex); + } + } + + return record; + } + else + { + var record = new Record(recordTypeDef); + + for (var i = 0; i < recordTypeDef.Properties.Length; i++) + record.Add(recordTypeDef.Properties[i].Name, list[i]); return record; } @@ -615,72 +651,101 @@ public static class DataDeserializer throw new NotImplementedException(); } - public static unsafe AsyncReply EnumParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static unsafe AsyncReply EnumParserAsync(ParsedTdu tdu, TypeDef enumTypeDef, EpConnection connection, uint[] requestSequence) { + var index = tdu.Data[tdu.PayloadOffset]; + return new AsyncReply(enumTypeDef.Constants[index].Value); - var typeId = tdu.Metadata.GetUUID(0); + //TypeDef typeDef = null; + //if (tdu.Metadata is TypeDefId typeDefId) + //{ + // if (typeDefId.Remote) + // { + // typeDef = connection.Instance.Warehouse.GetRemoteTypeDefById(connection.RemoteDomain, typeDefId.Value); - var index = tdu.Data[tdu.Offset]; + // var index = tdu.Data[tdu.Offset]; - var typeDef = connection.Instance.Warehouse.GetTypeDefById(typeId, - TypeDefKind.Enum); + // if (typeDef == null) + // { + // var reply = new AsyncReply(); - if (typeDef != null) - { - return new AsyncReply(typeDef.Constants[index].Value); - } - else - { - var reply = new AsyncReply(); + // connection.GetTypeDefById(typeDefId.Value).Then(td => + // { + // reply.Trigger(td.Constants[index].Value); + // }).Error(x => reply.TriggerError(x)); - connection.GetTypeDefById(typeId).Then(tmp => - { - reply.Trigger(tmp.Constants[index].Value); - }).Error(x => reply.TriggerError(x)); + // return reply; + // } + // else + // { + // return new AsyncReply(typeDef.Constants[index].Value); + // } + // } + // else + // { + // typeDef = connection.Instance.Warehouse.GetLocalTypeDefById(typeDefId.Value); - return reply; - } + // if (typeDef == null) + // { + // throw new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TypeDefNotFound, + // "TypeDef not found for enum."); + // } + + // var index = tdu.Data[tdu.Offset]; + // return new AsyncReply(typeDef.Constants[index].Value); + + // } + //} + //else + //{ + // throw new Exception("Unsupported metadata."); + //} } - public static unsafe object EnumParser(ParsedTdu tdu, Warehouse warehouse) + public static unsafe object EnumParser(ParsedTdu tdu, TypeDef enumTypeDef, Warehouse warehouse) { + //TypeDef typeDef = null; - var typeId = tdu.Metadata.GetUUID(0); + //if (tdu.Metadata is TypeDefId typeDefId) + //{ + // if (typeDefId.Remote) + // { + // throw new Exception("Unsupported in synchronous parsing."); + // } - var index = tdu.Data[tdu.Offset]; + // typeDef = warehouse.GetLocalTypeDefById(typeDefId.Value); + //} - var typeDef = warehouse.GetTypeDefById(typeId, TypeDefKind.Enum); + //if (typeDef == null) + //{ + // throw new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TypeDefNotFound, + // "TypeDef not found for enum."); + //} - if (typeDef != null) - { - return typeDef.Constants[index].Value; - } - else - { - throw new AsyncException(ErrorType.Management, (ushort)ExceptionCode.TypeDefNotFound, - "TypeDef not found for enum."); - } + var index = tdu.Data[tdu.PayloadOffset]; + return enumTypeDef.Constants[index].Value; } - public static AsyncReply RecordListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static async AsyncReply RecordListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { var rt = new AsyncBag(); - var length = tdu.ContentLength; - var offset = tdu.Offset; + var length = tdu.PayloadLength; + var offset = tdu.PayloadOffset; while (length > 0) { - var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence); + //var (cs, reply) + var pr = await Codec.ParseAsync(tdu.Data, offset, connection, requestSequence); - rt.Add(reply); + rt.Add(pr.Value); - if (cs > 0) + if (pr.Size > 0) { - offset += (uint)cs; - length -= (uint)cs; + offset += (uint)pr.Size; + length -= (uint)pr.Size; } else throw new Exception("Error while parsing structured data"); @@ -688,15 +753,17 @@ public static class DataDeserializer } rt.Seal(); - return rt; + var results = await rt; + + return results; } public static object RecordListParser(ParsedTdu tdu, Warehouse warehouse) { var rt = new List(); - var length = tdu.ContentLength; - var offset = tdu.Offset; + var length = tdu.PayloadLength; + var offset = tdu.PayloadOffset; while (length > 0) { @@ -717,23 +784,24 @@ public static class DataDeserializer return rt.ToArray(); } - public static AsyncReply ResourceListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static async AsyncReply ResourceListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { var rt = new AsyncBag(); - var length = tdu.ContentLength; - var offset = tdu.Offset; + var length = tdu.PayloadLength; + var offset = tdu.PayloadOffset; while (length > 0) { - var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence); + //var (cs, reply) + var pr = await Codec.ParseAsync(tdu.Data, offset, connection, requestSequence); - rt.Add(reply); + rt.Add(pr.Value);// reply); - if (cs > 0) + if (pr.Size > 0) { - offset += (uint)cs; - length -= (uint)cs; + offset += (uint)pr.Size; + length -= (uint)pr.Size; } else throw new Exception("Error while parsing structured data"); @@ -741,7 +809,9 @@ public static class DataDeserializer } rt.Seal(); - return rt; + + var results = await rt; + return results; } @@ -749,8 +819,8 @@ public static class DataDeserializer { var rt = new List(); - var length = tdu.ContentLength; - var offset = tdu.Offset; + var length = tdu.PayloadLength; + var offset = tdu.PayloadOffset; while (length > 0) { @@ -771,7 +841,7 @@ public static class DataDeserializer return rt.ToArray(); } - public static AsyncBag ListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static async AsyncReply ListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) { //var rt = new AsyncBag(); @@ -809,13 +879,13 @@ public static class DataDeserializer ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; while (length > 0) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = await ParsedTdu.ParseAsync(tdu.Data, offset, ends, connection); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); @@ -829,11 +899,13 @@ public static class DataDeserializer } - var (cs, reply) = Codec.ParseAsync(current, connection, requestSequence); + + //var (cs, reply) + var value = Codec.ParseAsync(current, connection, requestSequence); - rt.Add(reply); + rt.Add(value); - if (cs > 0) + if (current.TotalLength > 0) { offset += (uint)current.TotalLength; length -= (uint)current.TotalLength; @@ -846,7 +918,8 @@ public static class DataDeserializer rt.Seal(); - return rt; + var result = await rt; + return result; } @@ -857,13 +930,13 @@ public static class DataDeserializer ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; while (length > 0) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = ParsedTdu.ParseSync(tdu.Data, offset, ends, warehouse); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); @@ -877,11 +950,11 @@ public static class DataDeserializer } - var (cs, reply) = Codec.ParseSync(current, warehouse); + var reply = Codec.ParseSync(current, warehouse); list.Add(reply); - if (cs > 0) + if (current.TotalLength > 0) { offset += (uint)current.TotalLength; length -= (uint)current.TotalLength; @@ -923,45 +996,50 @@ public static class DataDeserializer } - public static AsyncReply TypedMapParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static async AsyncReply TypedMapParserAsync(ParsedTdu tdu, Tru keyTru, Tru valueTru, EpConnection connection, uint[] requestSequence) { var rt = new AsyncReply(); // get key type - var (keyCs, keysTru) = Tru.Parse(tdu.Metadata, 0); - var (valueCs, valuesTru) = Tru.Parse(tdu.Metadata, keyCs); + //var (keyCs, keysTru) = Tru.Parse(tdu.Metadata, 0); + //var (valueCs, valuesTru) = Tru.Parse(tdu.Metadata, keyCs); var map = (IMap)Activator.CreateInstance(typeof(Map<,>).MakeGenericType( - keysTru.GetRuntimeType(connection.Instance.Warehouse), - valuesTru.GetRuntimeType(connection.Instance.Warehouse))); + keyTru.RuntimeType, + valueTru.RuntimeType)); - var keysTdu = ParsedTdu.Parse(tdu.Data, tdu.Offset, - (uint)(tdu.Offset + tdu.ContentLength)); + var keysTdu = await ParsedTdu.ParseAsync(tdu.Data, tdu.PayloadOffset, + (uint)(tdu.PayloadOffset + tdu.PayloadLength), connection); - var valuesTdu = ParsedTdu.Parse(tdu.Data, - (uint)(keysTdu.Offset + keysTdu.ContentLength), - tdu.Ends); + var valuesTdu = await ParsedTdu.ParseAsync(tdu.Data, + (uint)(keysTdu.PayloadOffset + keysTdu.PayloadLength), + tdu.Ends, connection); - var keysReply = TypedArrayParserAsync(keysTdu, keysTru, connection, requestSequence); - var valuesReply = TypedArrayParserAsync(valuesTdu, valuesTru, connection, requestSequence); + //var keysReply = TypedArrayParserAsync(keysTdu, keyTru, connection, requestSequence); + //var valuesReply = TypedArrayParserAsync(valuesTdu, valueTru, connection, requestSequence); + + //keysReply.Then(keys => + //{ + // valuesReply.Then(values => + // { + // for (var i = 0; i < ((Array)keys).Length; i++) + // map.Add(((Array)keys).GetValue(i), ((Array)values).GetValue(i)); + // rt.Trigger(map); + // }).Error(e => rt.TriggerError(e)); + //}).Error(e => rt.TriggerError(e)); + //return rt; - keysReply.Then(keys => - { - valuesReply.Then(values => - { - for (var i = 0; i < ((Array)keys).Length; i++) - map.Add(((Array)keys).GetValue(i), ((Array)values).GetValue(i)); - rt.Trigger(map); - }).Error(e => rt.TriggerError(e)); - }).Error(e => rt.TriggerError(e)); + var keys = await TypedArrayParserAsync2(keysTdu, keyTru, connection, requestSequence); + var values = await TypedArrayParserAsync2(valuesTdu, valueTru, connection, requestSequence); + for (var i = 0; i < ((Array)keys).Length; i++) + map.Add(((Array)keys).GetValue(i), ((Array)values).GetValue(i)); - return rt; - + return map; //// get key type @@ -1011,42 +1089,42 @@ public static class DataDeserializer } - public static Array TypedArrayParser(ParsedTdu tdu, Tru tru, Warehouse warehouse) + public static Array TypedArrayParser(ParsedTdu tdu, Tru elementTru, Warehouse warehouse) { - switch (tru.Identifier) + switch (elementTru.Identifier) { case TruIdentifier.Int32: return GroupInt32Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength)); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength)); case TruIdentifier.Int64: return GroupInt64Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength)); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength)); case TruIdentifier.Int16: return GroupInt16Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength)); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength)); case TruIdentifier.UInt32: return GroupUInt32Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength)); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength)); case TruIdentifier.UInt64: return GroupUInt64Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength)); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength)); case TruIdentifier.UInt16: return GroupUInt16Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength)); - case TruIdentifier.Enum: + (int)tdu.PayloadOffset, (int)tdu.PayloadLength)); + //case TruIdentifier.Enum: - var enumType = tru.GetRuntimeType(warehouse); + // var enumType = tru.GetRuntimeType(warehouse); - var enums = Array.CreateInstance(enumType, (int)tdu.ContentLength); - var enumTypeDef = warehouse.GetTypeDefByType(enumType); + // var enums = Array.CreateInstance(enumType, (int)tdu.ContentLength); + // var enumTypeDef = warehouse.GetTypeDefByType(enumType); - for (var i = 0; i < (int)tdu.ContentLength; i++) - { - var index = tdu.Data[tdu.Offset + i]; - enums.SetValue(enumTypeDef.Constants[index].Value, i); - } + // for (var i = 0; i < (int)tdu.ContentLength; i++) + // { + // var index = tdu.Data[tdu.Offset + i]; + // enums.SetValue(enumTypeDef.Constants[index].Value, i); + // } - return enums; + // return enums; default: @@ -1056,13 +1134,13 @@ public static class DataDeserializer ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; while (length > 0) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = ParsedTdu.ParseSync(tdu.Data, offset, ends, warehouse); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); @@ -1076,18 +1154,18 @@ public static class DataDeserializer } else if (current.Identifier == TduIdentifier.TypeOfTarget) { - var (idf, mt) = tru.GetMetadata(); + //var (idf, mt) = tru.GetMetadata(); current.Class = TduClass.Typed; - current.Identifier = idf; - current.Metadata = mt; - current.Index = (int)idf & 0x7; + current.Identifier = TduIdentifier.Typed; + current.Metadata = elementTru; + current.Index = (int)TduIdentifier.Typed & 0x7; } - var (cs, reply) = Codec.ParseSync(current, warehouse); + var value = Codec.ParseSync(current, warehouse); - list.Add(reply); + list.Add(value); - if (cs > 0) + if (current.TotalLength > 0) { offset += (uint)current.TotalLength; length -= (uint)current.TotalLength; @@ -1098,7 +1176,7 @@ public static class DataDeserializer } - var runtimeType = tru.GetRuntimeType(warehouse); + var runtimeType = elementTru.RuntimeType; var rt = Array.CreateInstance(runtimeType, list.Count); Array.Copy(list.ToArray(), rt, rt.Length); @@ -1107,26 +1185,136 @@ public static class DataDeserializer } - public static object TypedMapParser(ParsedTdu tdu, Warehouse warehouse) + // Non-async/await version of TypedArrayParserAsync using continuations + public static AsyncReply TypedArrayParserAsync2(ParsedTdu tdu, Tru elementTru, EpConnection connection, uint[] requestSequence) + { + switch (elementTru.Identifier) + { + case TruIdentifier.Int32: + return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan( + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); + case TruIdentifier.Int64: + return new AsyncReply(GroupInt64Codec.Decode(tdu.Data.AsSpan( + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); + case TruIdentifier.Int16: + return new AsyncReply(GroupInt16Codec.Decode(tdu.Data.AsSpan( + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); + case TruIdentifier.UInt32: + return new AsyncReply(GroupUInt32Codec.Decode(tdu.Data.AsSpan( + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); + case TruIdentifier.UInt64: + return new AsyncReply(GroupUInt64Codec.Decode(tdu.Data.AsSpan( + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); + case TruIdentifier.UInt16: + return new AsyncReply(GroupUInt16Codec.Decode(tdu.Data.AsSpan( + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); + + default: + var rt = new AsyncReply(); + + var list = new AsyncBag(); + list.ArrayType = elementTru.RuntimeType; + + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; + var ends = offset + (uint)length; + + // Attach error propagation from bag to our reply + list.Error(e => rt.TriggerError(e)); + + // Recursive processor using continuations + Action processNext = null; + + processNext = (curOffset, curLength, previous) => + { + if (curLength == 0) + { + try + { + list.Seal(); + (list as AsyncReply).Then(x => rt.Trigger(x)); + } + catch (Exception ex) + { + rt.TriggerError(ex); + } + return; + } + + ParsedTdu.ParseAsync(tdu.Data, curOffset, ends, connection) + .Then(currentObj => + { + var current = (ParsedTdu)currentObj; + + if (current.Class == TduClass.Invalid) + { + rt.TriggerError(new AsyncException(ErrorType.Management, 0, "Unknown type.")); + return; + } + + if (current.Identifier == TduIdentifier.TypeContinuation) + { + if (previous.HasValue) + { + current.Class = previous.Value.Class; + current.Identifier = previous.Value.Identifier; + current.Metadata = previous.Value.Metadata; + } + } + else if (current.Identifier == TduIdentifier.TypeOfTarget) + { + current.Class = TduClass.Typed; + current.Identifier = TduIdentifier.Typed; + current.Metadata = elementTru; + current.Index = (int)TduIdentifier.Typed & 0x7; + } + + var reply = Codec.ParseAsync(current, connection, requestSequence); + + list.Add(reply); + + if (current.TotalLength > 0) + { + var nextOffset = curOffset + (uint)current.TotalLength; + var nextLength = curLength - (uint)current.TotalLength; + processNext(nextOffset, nextLength, current); + } + else + { + rt.TriggerError(new AsyncException(ErrorType.Management, 0, "Error while parsing structured data")); + return; + } + }) + .Error(e => rt.TriggerError(e)); + }; + + // start processing + processNext(offset, (uint)length, null); + + return rt; + } + } + + public static object TypedMapParser(ParsedTdu tdu, Tru keyTru, Tru valueTru, Warehouse warehouse) { // get key type - var (keyCs, keysTru) = Tru.Parse(tdu.Metadata, 0); - var (valueCs, valuesTru) = Tru.Parse(tdu.Metadata, keyCs); + //var (keyCs, keysTru) = Tru.Parse(tdu.Metadata, 0); + //var (valueCs, valuesTru) = Tru.Parse(tdu.Metadata, keyCs); - var map = (IMap)Activator.CreateInstance(typeof(Map<,>).MakeGenericType(keysTru.GetRuntimeType(warehouse), valuesTru.GetRuntimeType(warehouse))); + var map = (IMap)Activator.CreateInstance(typeof(Map<,>).MakeGenericType(keyTru.RuntimeType, valueTru.RuntimeType)); - var keysTdu = ParsedTdu.Parse(tdu.Data, tdu.Offset, - (uint)(tdu.Offset + tdu.ContentLength)); + var keysTdu = ParsedTdu.ParseSync(tdu.Data, tdu.PayloadOffset, + (uint)(tdu.PayloadOffset + tdu.PayloadLength), warehouse); - var valuesTdu = ParsedTdu.Parse(tdu.Data, - (uint)(keysTdu.Offset + keysTdu.ContentLength), - tdu.Ends); + var valuesTdu = ParsedTdu.ParseSync(tdu.Data, + (uint)(keysTdu.PayloadOffset + keysTdu.PayloadLength), + tdu.Ends, warehouse); - var keys = TypedArrayParser(keysTdu, keysTru, warehouse); - var values = TypedArrayParser(valuesTdu, valuesTru, warehouse); + var keys = TypedArrayParser(keysTdu, keyTru, warehouse); + var values = TypedArrayParser(valuesTdu, valueTru, warehouse); for (var i = 0; i < keys.Length; i++) map.Add(keys.GetValue(i), values.GetValue(i)); @@ -1135,54 +1323,55 @@ public static class DataDeserializer } - public static AsyncReply TupleParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + public static async AsyncReply TupleParserAsync(ParsedTdu tdu, Tru[] subTrus, EpConnection connection, uint[] requestSequence) { var rt = new AsyncReply(); // var tupleSize = tdu.Metadata[0]; - var trus = new List(); + //var trus = new List(); - uint mtOffset = 0; + //uint mtOffset = 0; - while (mtOffset < tdu.Metadata.Length) - { - var (cs, tru) = Tru.Parse(tdu.Metadata, mtOffset); - trus.Add(tru); - mtOffset += cs; - } + //while (mtOffset < tru.Length) + //{ + // var (cs, tru) = Tru.Parse(tdu.Metadata, mtOffset); + // trus.Add(tru); + // mtOffset += cs; + //} var results = new AsyncBag(); - var types = trus.Select(x => x.GetRuntimeType(connection.Instance.Warehouse)).ToArray(); + var subTypes = subTrus.Select(x => x.RuntimeType).ToArray(); ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; - for (var i = 0; i < trus.Count; i++) + for (var i = 0; i < subTypes.Length; i++) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = await ParsedTdu.ParseAsync(tdu.Data, offset, ends, connection); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); if (current.Identifier == TduIdentifier.TypeOfTarget) { - var (idf, mt) = trus[i].GetMetadata(); + //var (idf, mt) = trus[i].GetMetadata(); current.Class = TduClass.Typed; - current.Identifier = idf; - current.Metadata = mt; - current.Index = (int)idf & 0x7; + current.Identifier = TduIdentifier.Typed;// idf; + current.Metadata = subTrus[i];// mt; + current.Index = (int)(TduIdentifier.Typed) & 0x7;// idf & 0x7; } - var (_, reply) = Codec.ParseAsync(current, connection, requestSequence); + // var (_, reply) + var value = Codec.ParseAsync(current, connection, requestSequence); - results.Add(reply); + results.Add(value); if (current.TotalLength > 0) { @@ -1197,85 +1386,121 @@ public static class DataDeserializer results.Seal(); - results.Then(ar => - { - if (ar.Length == 2) - { - var type = typeof(ValueTuple<,>).MakeGenericType(types.ToArray()); - rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1])); - } - else if (ar.Length == 3) - { - var type = typeof(ValueTuple<,,>).MakeGenericType(types.ToArray()); - rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2])); - } - else if (ar.Length == 4) - { - var type = typeof(ValueTuple<,,,>).MakeGenericType(types.ToArray()); - rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3])); - } - else if (ar.Length == 5) - { - var type = typeof(ValueTuple<,,,,>).MakeGenericType(types.ToArray()); - rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4])); - } - else if (ar.Length == 6) - { - var type = typeof(ValueTuple<,,,,,>).MakeGenericType(types.ToArray()); - rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4], ar[5])); - } - else if (ar.Length == 7) - { - var type = typeof(ValueTuple<,,,,,,>).MakeGenericType(types.ToArray()); - rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4], ar[5], ar[6])); - } - }); + var ar = await results; - return rt; + if (ar.Length == 2) + { + var type = typeof(ValueTuple<,>).MakeGenericType(subTypes.ToArray()); + return Activator.CreateInstance(type, ar[0], ar[1]); + } + else if (ar.Length == 3) + { + var type = typeof(ValueTuple<,,>).MakeGenericType(subTypes.ToArray()); + return Activator.CreateInstance(type, ar[0], ar[1], ar[2]); + } + else if (ar.Length == 4) + { + var type = typeof(ValueTuple<,,,>).MakeGenericType(subTypes.ToArray()); + return Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3]); + } + else if (ar.Length == 5) + { + var type = typeof(ValueTuple<,,,,>).MakeGenericType(subTypes.ToArray()); + return Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4]); + } + else if (ar.Length == 6) + { + var type = typeof(ValueTuple<,,,,,>).MakeGenericType(subTypes.ToArray()); + return Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4], ar[5]); + } + else if (ar.Length == 7) + { + var type = typeof(ValueTuple<,,,,,,>).MakeGenericType(subTypes.ToArray()); + return Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4], ar[5], ar[6]); + } + + throw new Exception("Unknown tuple size."); + + + //results.Then(ar => + // { + // if (ar.Length == 2) + // { + // var type = typeof(ValueTuple<,>).MakeGenericType(subTypes.ToArray()); + // rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1])); + // } + // else if (ar.Length == 3) + // { + // var type = typeof(ValueTuple<,,>).MakeGenericType(subTypes.ToArray()); + // rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2])); + // } + // else if (ar.Length == 4) + // { + // var type = typeof(ValueTuple<,,,>).MakeGenericType(subTypes.ToArray()); + // rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3])); + // } + // else if (ar.Length == 5) + // { + // var type = typeof(ValueTuple<,,,,>).MakeGenericType(subTypes.ToArray()); + // rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4])); + // } + // else if (ar.Length == 6) + // { + // var type = typeof(ValueTuple<,,,,,>).MakeGenericType(subTypes.ToArray()); + // rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4], ar[5])); + // } + // else if (ar.Length == 7) + // { + // var type = typeof(ValueTuple<,,,,,,>).MakeGenericType(subTypes.ToArray()); + // rt.Trigger(Activator.CreateInstance(type, ar[0], ar[1], ar[2], ar[3], ar[4], ar[5], ar[6])); + // } + // }); + + //return rt; } - public static object TupleParser(ParsedTdu tdu, Warehouse warehouse) + public static object TupleParser(ParsedTdu tdu, Tru[] subTrus, Warehouse warehouse) { - var tupleSize = tdu.Metadata[0]; + var tupleSize = subTrus.Length; var trus = new List(); - uint mtOffset = 1; - for (var i = 0; i < tupleSize; i++) - { - var (cs, tru) = Tru.Parse(tdu.Metadata, mtOffset); - trus.Add(tru); - mtOffset += cs; - } + //uint mtOffset = 1; + //for (var i = 0; i < tupleSize; i++) + //{ + // var (cs, tru) = Tru.Parse(tdu.Metadata, mtOffset); + // trus.Add(tru); + // mtOffset += cs; + //} var results = new List(); - var types = trus.Select(x => x.GetRuntimeType(warehouse)).ToArray(); + var types = subTrus.Select(x => x.RuntimeType).ToArray(); ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; for (var i = 0; i < tupleSize; i++) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = ParsedTdu.ParseSync(tdu.Data, offset, ends, warehouse); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); if (current.Identifier == TduIdentifier.TypeOfTarget) { - var (idf, mt) = trus[i].GetMetadata(); + //var (idf, mt) = trus[i].GetMetadata(); current.Class = TduClass.Typed; - current.Identifier = idf; - current.Metadata = mt; - current.Index = (int)idf & 0x7; + current.Identifier = TduIdentifier.Typed; + current.Metadata = subTrus[i]; + current.Index = (int)TduIdentifier.Typed & 0x7; } - var (_, reply) = Codec.ParseSync(current, warehouse); + var reply = Codec.ParseSync(current, warehouse); results.Add(reply); @@ -1325,66 +1550,65 @@ public static class DataDeserializer } - public static AsyncReply TypedArrayParserAsync(ParsedTdu tdu, Tru tru, EpConnection connection, uint[] requestSequence) + public static async AsyncReply TypedArrayParserAsyncOld(ParsedTdu tdu, Tru elementTru, EpConnection connection, uint[] requestSequence) { - switch (tru.Identifier) + switch (elementTru.Identifier) { case TruIdentifier.Int32: return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength))); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); case TruIdentifier.Int64: return new AsyncReply(GroupInt64Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength))); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); case TruIdentifier.Int16: return new AsyncReply(GroupInt16Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength))); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); case TruIdentifier.UInt32: return new AsyncReply(GroupUInt32Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength))); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); case TruIdentifier.UInt64: return new AsyncReply(GroupUInt64Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength))); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); case TruIdentifier.UInt16: return new AsyncReply(GroupUInt16Codec.Decode(tdu.Data.AsSpan( - (int)tdu.Offset, (int)tdu.ContentLength))); + (int)tdu.PayloadOffset, (int)tdu.PayloadLength))); - case TruIdentifier.Enum: - var enumType = tru.GetRuntimeType(connection.Instance.Warehouse); + //case TruIdentifier.Enum: + // var enumType = elementTru.GetRuntimeType(connection.Instance.Warehouse); - var rt = Array.CreateInstance(enumType, (int)tdu.ContentLength); - var enumTypeDef = connection.Instance.Warehouse.GetTypeDefByType(enumType); + // var rt = Array.CreateInstance(enumType, (int)tdu.ContentLength); + // var enumTypeDef = connection.Instance.Warehouse.GetTypeDefByType(enumType); - for (var i = 0; i < (int)tdu.ContentLength; i++) - { - var index = tdu.Data[tdu.Offset + i]; + // for (var i = 0; i < (int)tdu.ContentLength; i++) + // { + // var index = tdu.Data[tdu.Offset + i]; - rt.SetValue(Enum.ToObject(enumType, enumTypeDef.Constants[index].Value), i); - } + // rt.SetValue(Enum.ToObject(enumType, enumTypeDef.Constants[index].Value), i); + // } - return new AsyncReply(rt); + // return new AsyncReply(rt); default: var list = new AsyncBag(); - list.ArrayType = tru.GetRuntimeType(connection.Instance.Warehouse); + list.ArrayType = elementTru.RuntimeType; ParsedTdu current; ParsedTdu? previous = null; - var offset = tdu.Offset; - var length = tdu.ContentLength; + var offset = tdu.PayloadOffset; + var length = tdu.PayloadLength; var ends = offset + (uint)length; while (length > 0) { - current = ParsedTdu.Parse(tdu.Data, offset, ends); + current = await ParsedTdu.ParseAsync(tdu.Data, offset, ends, connection); if (current.Class == TduClass.Invalid) throw new Exception("Unknown type."); - if (current.Identifier == TduIdentifier.TypeContinuation) { current.Class = previous.Value.Class; @@ -1393,18 +1617,20 @@ public static class DataDeserializer } else if (current.Identifier == TduIdentifier.TypeOfTarget) { - var (idf, mt) = tru.GetMetadata(); + //var (idf, mt) = tru.GetMetadata(); current.Class = TduClass.Typed; - current.Identifier = idf; - current.Metadata = mt; - current.Index = (int)idf & 0x7; + current.Identifier = TduIdentifier.Typed;// idf; + current.Metadata = elementTru;// mt; + current.Index = (int)TduIdentifier.Typed & 0x7;// (int)idf & 0x7; } - var (cs, reply) = Codec.ParseAsync(current, connection, requestSequence); + //var (cs, reply) + + var reply = Codec.ParseAsync(current, connection, requestSequence); list.Add(reply); - if (cs > 0) + if (current.TotalLength > 0) { offset += (uint)current.TotalLength; length -= (uint)current.TotalLength; @@ -1416,102 +1642,270 @@ public static class DataDeserializer } list.Seal(); - return list; + return await list; } } - public static AsyncReply TypedListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + //public static AsyncReply TypedListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + //{ + // // get the type + // var (hdrCs, tru) = Tru.Parse(tdu.Metadata, 0); + + // return TypedArrayParserAsync(tdu, tru, connection, requestSequence); + + // //switch (rep.Identifier) + // //{ + // // case TRUIdentifier.Int32: + // // return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan( + // // (int)tdu.Offset, (int)tdu.ContentLength))); + // // case TRUIdentifier.Int64: + // // return new AsyncReply(GroupInt64Codec.Decode(tdu.Data.AsSpan( + // // (int)tdu.Offset, (int)tdu.ContentLength))); + // // case TRUIdentifier.Int16: + // // return new AsyncReply(GroupInt16Codec.Decode(tdu.Data.AsSpan( + // // (int)tdu.Offset, (int)tdu.ContentLength))); + // // case TRUIdentifier.UInt32: + // // return new AsyncReply(GroupUInt32Codec.Decode(tdu.Data.AsSpan( + // // (int)tdu.Offset, (int)tdu.ContentLength))); + // // case TRUIdentifier.UInt64: + // // return new AsyncReply(GroupUInt64Codec.Decode(tdu.Data.AsSpan( + // // (int)tdu.Offset, (int)tdu.ContentLength))); + // // case TRUIdentifier.UInt16: + // // return new AsyncReply(GroupUInt16Codec.Decode(tdu.Data.AsSpan( + // // (int)tdu.Offset, (int)tdu.ContentLength))); + // // default: + + // // var rt = new AsyncBag(); + + // // var runtimeType = rep.GetRuntimeType(connection.Instance.Warehouse); + + // // rt.ArrayType = runtimeType; + + // // ParsedTDU current; + // // ParsedTDU? previous = null; + + // // var offset = tdu.Offset; + // // var length = tdu.ContentLength; + // // var ends = offset + (uint)length; + + // // while (length > 0) + // // { + + // // current = ParsedTDU.Parse(tdu.Data, offset, ends); + + // // if (current.Class == TDUClass.Invalid) + // // throw new Exception("Unknown type."); + + + // // if (current.Identifier == TDUIdentifier.TypeContinuation) + // // { + // // current.Class = previous.Value.Class; + // // current.Identifier = previous.Value.Identifier; + // // current.Metadata = previous.Value.Metadata; + // // } + + // // var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence); + + // // rt.Add(reply); + + // // if (cs > 0) + // // { + // // offset += (uint)cs; + // // length -= (uint)cs; + // // } + // // else + // // throw new Exception("Error while parsing structured data"); + + // // } + + // // rt.Seal(); + // // return rt; + // //} + //} + + public static AsyncReply TypedObjectParserAsync(ParsedTdu tdu, TypeDef typeDef, EpConnection connection, uint[] requestSequence) { - // get the type - var (hdrCs, tru) = Tru.Parse(tdu.Metadata, 0); + // get the TypeDef + var warehouse = connection.Instance.Warehouse; + //TypeDef typeDef = null; - return TypedArrayParserAsync(tdu, tru, connection, requestSequence); + var rt = new AsyncReply(); - //switch (rep.Identifier) + if (typeDef.Kind == TypeDefKind.Record) + { + // parse record + RecordParserAsync(tdu, typeDef, connection, requestSequence) + .Then(x => rt.Trigger(x)) + .Error(e => rt.TriggerError(e)); + } + else if (typeDef.Kind == TypeDefKind.Enum) + { + // paese enum + EnumParserAsync(tdu, typeDef, connection, requestSequence) + .Then(x => rt.Trigger(x)) + .Error(e => rt.TriggerError(e)); + + } + else// if (td.Kind == TypeDefKind.Resource) + { + // parse resource : this is kept for future support + throw new Exception("Typed resource parsing is not supported yet."); + } + + + //if (typeDefId.Remote) //{ - // case TRUIdentifier.Int32: - // return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan( - // (int)tdu.Offset, (int)tdu.ContentLength))); - // case TRUIdentifier.Int64: - // return new AsyncReply(GroupInt64Codec.Decode(tdu.Data.AsSpan( - // (int)tdu.Offset, (int)tdu.ContentLength))); - // case TRUIdentifier.Int16: - // return new AsyncReply(GroupInt16Codec.Decode(tdu.Data.AsSpan( - // (int)tdu.Offset, (int)tdu.ContentLength))); - // case TRUIdentifier.UInt32: - // return new AsyncReply(GroupUInt32Codec.Decode(tdu.Data.AsSpan( - // (int)tdu.Offset, (int)tdu.ContentLength))); - // case TRUIdentifier.UInt64: - // return new AsyncReply(GroupUInt64Codec.Decode(tdu.Data.AsSpan( - // (int)tdu.Offset, (int)tdu.ContentLength))); - // case TRUIdentifier.UInt16: - // return new AsyncReply(GroupUInt16Codec.Decode(tdu.Data.AsSpan( - // (int)tdu.Offset, (int)tdu.ContentLength))); - // default: + // typeDef = warehouse.GetRemoteTypeDefById(connection.RemoteDomain, typeDefId.Value); - // var rt = new AsyncBag(); + // if (typeDef == null) + // { + // // get it from the other end + // connection.GetTypeDefById(typeDefId.Value) + // .Then(x => parseTyped(x)) + // .Error(x => rt.TriggerError(x)); - // var runtimeType = rep.GetRuntimeType(connection.Instance.Warehouse); - - // rt.ArrayType = runtimeType; - - // ParsedTDU current; - // ParsedTDU? previous = null; - - // var offset = tdu.Offset; - // var length = tdu.ContentLength; - // var ends = offset + (uint)length; - - // while (length > 0) - // { - - // current = ParsedTDU.Parse(tdu.Data, offset, ends); - - // if (current.Class == TDUClass.Invalid) - // throw new Exception("Unknown type."); - - - // if (current.Identifier == TDUIdentifier.TypeContinuation) - // { - // current.Class = previous.Value.Class; - // current.Identifier = previous.Value.Identifier; - // current.Metadata = previous.Value.Metadata; - // } - - // var (cs, reply) = Codec.ParseAsync(tdu.Data, offset, connection, requestSequence); - - // rt.Add(reply); - - // if (cs > 0) - // { - // offset += (uint)cs; - // length -= (uint)cs; - // } - // else - // throw new Exception("Error while parsing structured data"); - - // } - - // rt.Seal(); - // return rt; + // } //} + //else + //{ + // typeDef = warehouse.GetLocalTypeDefById(typeDefId.Value); + + // if (typeDef == null) + // { + // throw new Exception("Bad TypeDefId."); + // } + + // parseTyped(typeDef); + + //} + + return rt; } - public static object TypedListParser(ParsedTdu tdu, Warehouse warehouse) + + public static object TypedObjectParser(ParsedTdu tdu, TypeDef typeDef, Warehouse warehouse) { - // get the type - var (hdrCs, tru) = Tru.Parse(tdu.Metadata, 0); + // get the TypeDef - return TypedArrayParser(tdu, tru, warehouse); + if (typeDef.Kind == TypeDefKind.Record) + { + // parse record + return RecordParser(tdu, typeDef as LocalTypeDef, warehouse); + } + else if (typeDef.Kind == TypeDefKind.Enum) + { + // paese enum + return EnumParser(tdu, typeDef as LocalTypeDef, warehouse); + } + else// if (td.Kind == TypeDefKind.Resource) + { + // parse resource : this is kept for future support + throw new Exception("Typed resource parsing is not supported yet."); + } } + + + public static AsyncReply TypedParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence) + { + var tru = tdu.Metadata; + + // do we need to get all subtypes typedefs here ? + + if (tru is TruComposite truComposite) + { + return tru.Identifier switch + { + TruIdentifier.TypedList => TypedArrayParserAsync2(tdu, truComposite.SubTypes[0], connection, requestSequence), + TruIdentifier.TypedMap => TypedMapParserAsync(tdu, truComposite.SubTypes[0], truComposite.SubTypes[1], connection, requestSequence), + TruIdentifier.Tuple2 => TupleParserAsync(tdu, truComposite.SubTypes, connection, requestSequence), + TruIdentifier.Tuple3 => TupleParserAsync(tdu, truComposite.SubTypes, connection, requestSequence), + TruIdentifier.Tuple4 => TupleParserAsync(tdu, truComposite.SubTypes, connection, requestSequence), + TruIdentifier.Tuple5 => TupleParserAsync(tdu, truComposite.SubTypes, connection, requestSequence), + TruIdentifier.Tuple6 => TupleParserAsync(tdu, truComposite.SubTypes, connection, requestSequence), + TruIdentifier.Tuple7 => TupleParserAsync(tdu, truComposite.SubTypes, connection, requestSequence), + _ => throw new Exception("Unsupported type for typed parser.") + }; + } + else if (tru is TruTypeDef truTypeDef) + { + return TypedObjectParserAsync(tdu, truTypeDef.TypeDef, connection, requestSequence); +// return tru.Identifier switch +// { +// TruIdentifier.LocalType8 or +// TruIdentifier.LocalType16 or +// TruIdentifier.LocalType32 or +// TruIdentifier.LocalType64 or +// TruIdentifier.RemoteType8 or +// TruIdentifier.RemoteType16 or +// TruIdentifier.RemoteType32 or +// TruIdentifier.RemoteType64 => TypedObjectParserAsync(tdu, truTypeDef.TypeDef, connection, requestSequence); +//, + +// _ => throw new Exception("Unsupported type for typed parser.") +// }; + } + + throw new Exception("Unknown TRU."); + + } + + public static object TypedParser(ParsedTdu tdu, Warehouse warehouse) + { + var tru = tdu.Metadata; + + // do we need to get all subtypes typedefs here ? + + if (tru is TruComposite truComposite) + { + return tru.Identifier switch + { + TruIdentifier.TypedList => TypedArrayParser(tdu, truComposite.SubTypes[0], warehouse), + TruIdentifier.TypedMap => TypedMapParser(tdu, truComposite.SubTypes[0], truComposite.SubTypes[1], warehouse), + TruIdentifier.Tuple2 => TupleParser(tdu, truComposite.SubTypes, warehouse), + TruIdentifier.Tuple3 => TupleParser(tdu, truComposite.SubTypes, warehouse), + TruIdentifier.Tuple4 => TupleParser(tdu, truComposite.SubTypes, warehouse), + TruIdentifier.Tuple5 => TupleParser(tdu, truComposite.SubTypes, warehouse), + TruIdentifier.Tuple6 => TupleParser(tdu, truComposite.SubTypes, warehouse), + TruIdentifier.Tuple7 => TupleParser(tdu, truComposite.SubTypes, warehouse), + _ => throw new Exception("Unsupported type for typed parser.") + }; + } + else if (tru is TruTypeDef truTypeDef) + { + return tru.Identifier switch + { + TruIdentifier.LocalType8 or + TruIdentifier.LocalType16 or + TruIdentifier.LocalType32 or + TruIdentifier.LocalType64 or + TruIdentifier.RemoteType8 or + TruIdentifier.RemoteType16 or + TruIdentifier.RemoteType32 or + TruIdentifier.RemoteType64 => TypedObjectParser(tdu, truTypeDef.TypeDef, warehouse), + _ => throw new Exception("Unsupported type for typed parser.") + }; + + } + + throw new Exception("Unknown TRU."); + } + + //public static object TypedListParser(ParsedTdu tdu, Warehouse warehouse) + //{ + // // get the type + // var (hdrCs, tru) = Tru.Parse(tdu.Metadata, 0); + + // return TypedArrayParser(tdu, tru, warehouse); + //} + public static AsyncBag PropertyValueArrayParserAsync(byte[] data, uint offset, uint length, EpConnection connection, uint[] requestSequence)//, bool ageIncluded = true) { var rt = new AsyncBag(); - ListParserAsync(new ParsedTdu() { Data = data, Offset = offset, ContentLength = length } + ListParserAsync(new ParsedTdu() { Data = data, PayloadOffset = offset, PayloadLength = length } , connection, requestSequence).Then(x => { @@ -1519,7 +1913,7 @@ public static class DataDeserializer var pvs = new List(); for (var i = 0; i < ar.Length; i += 3) - pvs.Add(new PropertyValue(ar[i + 2], Convert.ToUInt64(ar[i]), (DateTime?)ar[i+1])); + pvs.Add(new PropertyValue(ar[i + 2], Convert.ToUInt64(ar[i]), (DateTime?)ar[i + 1])); rt.Trigger(pvs.ToArray()); @@ -1530,9 +1924,9 @@ public static class DataDeserializer } - public static (uint, AsyncReply) PropertyValueParserAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)//, bool ageIncluded = true) + public static async AsyncReply> PropertyValueParserAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)//, bool ageIncluded = true) { - var reply = new AsyncReply(); + // var reply = new AsyncReply(); var age = data.GetUInt64(offset, Endian.Little); offset += 8; @@ -1541,24 +1935,28 @@ public static class DataDeserializer offset += 8; - var (valueSize, results) = Codec.ParseAsync(data, offset, connection, requestSequence); + //var (valueSize, results) = + var pr = await Codec.ParseAsync(data, offset, connection, requestSequence); - if (results is AsyncReply) + if (pr.Value is AsyncReply asycReply) { - (results as AsyncReply).Then(value => - { - reply.Trigger(new PropertyValue(value, age, date)); - }); + var value = await asycReply; + return new ParseResult(new PropertyValue(value, age, date), 16 + pr.Size); + //asycReply.Then(value => + //{ + // reply.Trigger(new PropertyValue(value, age, date)); + //}); } else { - reply.Trigger(new PropertyValue(results, age, date)); + return new ParseResult(new PropertyValue(pr.Value, age, date), 16 + pr.Size); + //reply.Trigger(new PropertyValue(results, age, date)); } - return (16 + valueSize, reply); + // return (16 + valueSize, reply); } - public static AsyncReply> HistoryParserAsync(byte[] data, uint offset, uint length, IResource resource, EpConnection connection, uint[] requestSequence) + public static async AsyncReply> HistoryParserAsync(byte[] data, uint offset, uint length, IResource resource, EpConnection connection, uint[] requestSequence) { //var count = (int)toAge - (int)fromAge; @@ -1577,24 +1975,35 @@ public static class DataDeserializer var cs = data.GetUInt32(offset, Endian.Little); offset += 4; - var (len, pv) = PropertyValueParserAsync(data, offset, connection, requestSequence); + //var (len, pv) = + var pr = await PropertyValueParserAsync(data, offset, connection, requestSequence); - bagOfBags.Add(pv);// ParsePropertyValueArray(data, offset, cs, connection)); - offset += len; + bagOfBags.Add(pr.Value);// ParsePropertyValueArray(data, offset, cs, connection)); + offset += pr.Size; } bagOfBags.Seal(); - bagOfBags.Then(x => - { - for (var i = 0; i < list.Count; i++) - list[list.Keys.ElementAt(i)] = x[i]; + var x = await bagOfBags; - reply.Trigger(list); - }); + for (var i = 0; i < list.Count; i++) + list[list.Keys.ElementAt(i)] = x[i]; - return reply; + return list; + + //bagOfBags.Then(x => + //{ + // for (var i = 0; i < list.Count; i++) + // list[list.Keys.ElementAt(i)] = x[i]; + + // reply.Trigger(list); + //}); + + //return reply; } + + + } diff --git a/Libraries/Esiur/Data/DataSerializer.cs b/Libraries/Esiur/Data/DataSerializer.cs index 07cd493..4779bad 100644 --- a/Libraries/Esiur/Data/DataSerializer.cs +++ b/Libraries/Esiur/Data/DataSerializer.cs @@ -23,7 +23,7 @@ public static class DataSerializer if (v >= sbyte.MinValue && v <= sbyte.MaxValue) { - return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1); + return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null); } else if (v >= short.MinValue && v <= short.MaxValue) { @@ -32,7 +32,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((short*)ptr) = (short)v; - return new Tdu(TduIdentifier.Int16, rt, 2); + return new Tdu(TduIdentifier.Int16, rt, 2, null, null); } else { @@ -40,7 +40,7 @@ public static class DataSerializer var rt = new byte[4]; fixed (byte* ptr = rt) *((int*)ptr) = v; - return new Tdu(TduIdentifier.Int32, rt, 4); + return new Tdu(TduIdentifier.Int32, rt, 4, null, null); } } @@ -51,7 +51,7 @@ public static class DataSerializer if (v <= byte.MaxValue) { // Fits in 1 byte - return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1); + return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null); } else if (v <= ushort.MaxValue) { @@ -60,7 +60,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((ushort*)ptr) = (ushort)v; - return new Tdu(TduIdentifier.UInt16, rt, 2); + return new Tdu(TduIdentifier.UInt16, rt, 2, null, null); } else { @@ -69,7 +69,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((uint*)ptr) = v; - return new Tdu(TduIdentifier.UInt32, rt, 4); + return new Tdu(TduIdentifier.UInt32, rt, 4, null, null); } } @@ -80,7 +80,7 @@ public static class DataSerializer if (v >= sbyte.MinValue && v <= sbyte.MaxValue) { // Fits in 1 byte - return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1); + return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null); } else { @@ -89,7 +89,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((short*)ptr) = v; - return new Tdu(TduIdentifier.Int16, rt, 2); + return new Tdu(TduIdentifier.Int16, rt, 2, null, null); } } @@ -100,7 +100,7 @@ public static class DataSerializer if (v <= byte.MaxValue) { // Fits in 1 byte - return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1); + return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null); } else { @@ -109,7 +109,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((ushort*)ptr) = v; - return new Tdu(TduIdentifier.UInt16, rt, 2); + return new Tdu(TduIdentifier.UInt16, rt, 2, null, null); } } @@ -121,7 +121,7 @@ public static class DataSerializer // Special IEEE-754 values if (float.IsNaN(v) || float.IsInfinity(v)) { - return new Tdu(TduIdentifier.Infinity, new byte[0], 0); + return new Tdu(TduIdentifier.Infinity, new byte[0], 0, null, null); } // If v is an exact integer, prefer smallest signed width up to Int32 @@ -130,7 +130,7 @@ public static class DataSerializer // Note: casts are safe because we check bounds first. if (v >= sbyte.MinValue && v <= sbyte.MaxValue) { - return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1); + return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null); } if (v >= short.MinValue && v <= short.MaxValue) @@ -138,7 +138,7 @@ public static class DataSerializer var rt = new byte[2]; fixed (byte* ptr = rt) *((short*)ptr) = (short)v; - return new Tdu(TduIdentifier.Int16, rt, 2); + return new Tdu(TduIdentifier.Int16, rt, 2, null, null); } } @@ -147,7 +147,7 @@ public static class DataSerializer var rt = new byte[4]; fixed (byte* ptr = rt) *((float*)ptr) = v; - return new Tdu(TduIdentifier.Float32, rt, 4); + return new Tdu(TduIdentifier.Float32, rt, 4, null, null); } } @@ -159,14 +159,14 @@ public static class DataSerializer // Special IEEE-754 values if (double.IsNaN(v) || double.IsInfinity(v)) { - return new Tdu(TduIdentifier.Infinity, new byte[0], 0); + return new Tdu(TduIdentifier.Infinity, new byte[0], 0, null, null); } // If v is an exact integer, choose the smallest signed width if (v == Math.Truncate(v)) { if (v >= sbyte.MinValue && v <= sbyte.MaxValue) - return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1); + return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null); if (v >= short.MinValue && v <= short.MaxValue) { @@ -175,7 +175,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((short*)ptr) = (short)v; - return new Tdu(TduIdentifier.Int16, rt, 2); + return new Tdu(TduIdentifier.Int16, rt, 2, null, null); } if (v >= int.MinValue && v <= int.MaxValue) @@ -185,7 +185,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((int*)ptr) = (int)v; - return new Tdu(TduIdentifier.Int32, rt, 4); + return new Tdu(TduIdentifier.Int32, rt, 4, null, null); } // If it's integral but outside Int64 range, fall through to Float64. @@ -200,7 +200,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((float*)ptr) = f; - return new Tdu(TduIdentifier.Float32, rt, 4); + return new Tdu(TduIdentifier.Float32, rt, 4, null, null); } // Default: Float64 @@ -208,7 +208,7 @@ public static class DataSerializer var rt = new byte[8]; fixed (byte* ptr = rt) *((double*)ptr) = v; - return new Tdu(TduIdentifier.Float64, rt, 8); + return new Tdu(TduIdentifier.Float64, rt, 8, null, null); } } public static unsafe Tdu Int64Composer(object value, Warehouse warehouse, EpConnection connection) @@ -218,7 +218,7 @@ public static class DataSerializer if (v >= sbyte.MinValue && v <= sbyte.MaxValue) { // Fits in 1 byte - return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1); + return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null); } else if (v >= short.MinValue && v <= short.MaxValue) { @@ -227,7 +227,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((short*)ptr) = (short)v; - return new Tdu(TduIdentifier.Int16, rt, 2); + return new Tdu(TduIdentifier.Int16, rt, 2, null, null); } else if (v >= int.MinValue && v <= int.MaxValue) { @@ -236,7 +236,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((int*)ptr) = (int)v; - return new Tdu(TduIdentifier.Int32, rt, 4); + return new Tdu(TduIdentifier.Int32, rt, 4, null, null); } else { @@ -245,7 +245,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((long*)ptr) = v; - return new Tdu(TduIdentifier.Int64, rt, 8); + return new Tdu(TduIdentifier.Int64, rt, 8, null, null); } } @@ -256,7 +256,7 @@ public static class DataSerializer if (v <= byte.MaxValue) { // Fits in 1 byte - return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1); + return new Tdu(TduIdentifier.UInt8, new byte[] { (byte)v }, 1, null, null); } else if (v <= ushort.MaxValue) { @@ -265,7 +265,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((ushort*)ptr) = (ushort)v; - return new Tdu(TduIdentifier.UInt16, rt, 2); + return new Tdu(TduIdentifier.UInt16, rt, 2, null, null); } else if (v <= uint.MaxValue) { @@ -274,7 +274,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((uint*)ptr) = (uint)v; - return new Tdu(TduIdentifier.UInt32, rt, 4); + return new Tdu(TduIdentifier.UInt32, rt, 4, null, null); } else { @@ -283,7 +283,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((ulong*)ptr) = v; - return new Tdu(TduIdentifier.UInt64, rt, 8); + return new Tdu(TduIdentifier.UInt64, rt, 8, null, null); } } @@ -295,7 +295,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((long*)ptr) = v; - return new Tdu(TduIdentifier.DateTime, rt, 8); + return new Tdu(TduIdentifier.DateTime, rt, 8, null, null); } //public static unsafe TDU Decimal128Composer(object value, Warehouse warehouse, EpConnection connection) @@ -320,27 +320,27 @@ public static class DataSerializer if (scale == 0) { if (v >= sbyte.MinValue && v <= sbyte.MaxValue) - return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1); + return new Tdu(TduIdentifier.Int8, new byte[] { (byte)(sbyte)v }, 1, null, null); if (v >= short.MinValue && v <= short.MaxValue) { var b = new byte[2]; BinaryPrimitives.WriteInt16LittleEndian(b, (short)v); - return new Tdu(TduIdentifier.Int16, b, 2); + return new Tdu(TduIdentifier.Int16, b, 2, null, null); } if (v >= int.MinValue && v <= int.MaxValue) { var b = new byte[4]; BinaryPrimitives.WriteInt32LittleEndian(b, (int)v); - return new Tdu(TduIdentifier.Int32, b, 4); + return new Tdu(TduIdentifier.Int32, b, 4, null, null); } if (v >= long.MinValue && v <= long.MaxValue) { var b = new byte[8]; BinaryPrimitives.WriteInt64LittleEndian(b, (long)v); - return new Tdu(TduIdentifier.Int64, b, 8); + return new Tdu(TduIdentifier.Int64, b, 8, null, null); } // else fall through (needs 96+ bits) } @@ -355,7 +355,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((float*)ptr) = f; - return new Tdu(TduIdentifier.Float32, rt, 4); + return new Tdu(TduIdentifier.Float32, rt, 4, null, null); } // Try exact Float64 (8 bytes) @@ -367,7 +367,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((double*)ptr) = d; - return new Tdu(TduIdentifier.Float64, rt, 8); + return new Tdu(TduIdentifier.Float64, rt, 8, null, null); } { @@ -377,7 +377,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((decimal*)ptr) = v; - return new Tdu(TduIdentifier.Decimal128, rt, 16); + return new Tdu(TduIdentifier.Decimal128, rt, 16, null, null); } } @@ -385,58 +385,55 @@ public static class DataSerializer { var b = Encoding.UTF8.GetBytes((string)value); - return new Tdu(TduIdentifier.String, b, (uint)b.Length); + return new Tdu(TduIdentifier.String, b, (uint)b.Length, null, null); } public static Tdu ResourceLinkComposer(object value, Warehouse warehouse, EpConnection connection) { var b = Encoding.UTF8.GetBytes((ResourceLink)value); - return new Tdu(TduIdentifier.ResourceLink, b, (uint)b.Length); + return new Tdu(TduIdentifier.ResourceLink, b, (uint)b.Length, null, null); } public static Tdu EnumComposer(object value, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, null, 0); + return new Tdu(TduIdentifier.Null, null, 0, null, null); - - //var warehouse = connection?.Instance?.Warehouse ?? connection?.Server?.Instance?.Warehouse; - //if (warehouse == null) - // throw new Exception("Warehouse not set."); + var valueType = value.GetType(); - var typeDef = warehouse.GetTypeDefByType(value.GetType()); + var typeDef = warehouse.GetLocalTypeDefByType(valueType); var intVal = Convert.ChangeType(value, (value as Enum).GetTypeCode()); var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal)); if (ct == null) - return new Tdu(TduIdentifier.Null, null, 0); + return new Tdu(TduIdentifier.Null, null, 0, null, null); - //return Codec.ComposeInternal(intVal, warehouse, connection); - return new Tdu(TduIdentifier.TypedEnum, - new byte[] { ct.Index }, 1, typeDef.Id.Data); + return new Tdu(TduIdentifier.Typed, new byte[] { ct.Index }, 1, + Tru.FromType(valueType, warehouse), connection); + } public static Tdu UInt8Composer(object value, Warehouse warehouse, EpConnection connection) { return new Tdu(TduIdentifier.UInt8, - new byte[] { (byte)value }, 1); + new byte[] { (byte)value }, 1, null, null); } public static Tdu Int8Composer(object value, Warehouse warehouse, EpConnection connection) { return new Tdu(TduIdentifier.Int8, - new byte[] { (byte)(sbyte)value }, 1); + new byte[] { (byte)(sbyte)value }, 1, null, null); } public static Tdu Char8Composer(object value, Warehouse warehouse, EpConnection connection) { return new Tdu(TduIdentifier.Int8, - new byte[] { (byte)(char)value }, 1); + new byte[] { (byte)(char)value }, 1, null, null); } public static unsafe Tdu Char16Composer(object value, Warehouse warehouse, EpConnection connection) @@ -446,7 +443,7 @@ public static class DataSerializer fixed (byte* ptr = rt) *((char*)ptr) = v; - return new Tdu(TduIdentifier.Char16, rt, 2); + return new Tdu(TduIdentifier.Char16, rt, 2, null, null); } @@ -454,30 +451,30 @@ public static class DataSerializer { if ((bool)value) { - return new Tdu(TduIdentifier.True, null, 0); + return new Tdu(TduIdentifier.True, null, 0, null, null); } else { - return new Tdu(TduIdentifier.False, null, 0); + return new Tdu(TduIdentifier.False, null, 0, null, null); } } public static Tdu NotModifiedComposer(object value, Warehouse warehouse, EpConnection connection) { - return new Tdu(TduIdentifier.NotModified, null, 0); + return new Tdu(TduIdentifier.NotModified, null, 0, null, null); } public static Tdu RawDataComposerFromArray(object value, Warehouse warehouse, EpConnection connection) { var b = (byte[])value; - return new Tdu(TduIdentifier.RawData, b, (uint)b.Length); + return new Tdu(TduIdentifier.RawData, b, (uint)b.Length, null, null); } public static Tdu RawDataComposerFromList(dynamic value, Warehouse warehouse, EpConnection connection) { var b = value as List; - return new Tdu(TduIdentifier.RawData, b.ToArray(), (uint)b.Count); + return new Tdu(TduIdentifier.RawData, b.ToArray(), (uint)b.Count, null, null); } //public static (TDUIdentifier, byte[]) ListComposerFromArray(dynamic value, EpConnection connection) @@ -497,10 +494,10 @@ public static class DataSerializer var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection); if (composed == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); else { - return new Tdu(TduIdentifier.List, composed, (uint)composed.Length); + return new Tdu(TduIdentifier.List, composed, (uint)composed.Length, null, null); } //if (value == null) @@ -541,7 +538,6 @@ public static class DataSerializer if (value == null) return null; - if (tru.Identifier == TruIdentifier.Int32) { composed = GroupInt32Codec.Encode((IList)value); @@ -566,52 +562,49 @@ public static class DataSerializer { composed = GroupUInt16Codec.Encode((IList)value); } - else if (tru.Identifier == TruIdentifier.Enum) - { + //else if (tru.Identifier == TruIdentifier.Enum) + //{ + // var rt = new List(); + // var typeDef = warehouse.GetTypeDefByType(tru.GetRuntimeType(warehouse)); - var rt = new List(); - var typeDef = warehouse.GetTypeDefByType(tru.GetRuntimeType(warehouse)); + // foreach (var v in value) + // { + // var intVal = Convert.ChangeType(v, (v as Enum).GetTypeCode()); + // var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal)); + // if (ct == null) + // throw new Exception("Unknown Enum."); + // rt.Add(ct.Index); + // } - foreach (var v in value) - { - var intVal = Convert.ChangeType(v, (v as Enum).GetTypeCode()); - var ct = typeDef.Constants.FirstOrDefault(x => x.Value.Equals(intVal)); - if (ct == null) - throw new Exception("Unknown Enum."); - rt.Add(ct.Index); - } - - composed = rt.ToArray(); - } + // composed = rt.ToArray(); + //} else { var rt = new List(); Tdu? previous = null; - var isTyped = tru.IsTyped(); + //var isTyped = tru.TypeDefId != null;// tru.IsTyped(); foreach (var i in value) { var tdu = Codec.ComposeInternal(i, warehouse, connection); - var currentTru = Tru.FromType(i?.GetType()); + var currentTru = Tru.FromType(i?.GetType(), warehouse); - if (isTyped && tru.Match(currentTru)) + if (tdu.Class == TduClass.Typed && tru.Match(currentTru)) { var d = tdu.Composed.Clip(tdu.ContentOffset, (uint)tdu.Composed.Length - tdu.ContentOffset); - var ntd = new Tdu(TduIdentifier.TypeOfTarget, d, (ulong)d.Length); + var ntd = new Tdu(TduIdentifier.TypeOfTarget, d, (ulong)d.Length, null, null); rt.AddRange(ntd.Composed); } - else - - if (previous != null && tdu.MatchType(previous.Value)) + else if (previous != null && tdu.MatchType(previous.Value)) { var d = tdu.Composed.Clip(tdu.ContentOffset, (uint)tdu.Composed.Length - tdu.ContentOffset); - var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length); + var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length, null, null); rt.AddRange(ntd.Composed); } else @@ -632,33 +625,24 @@ public static class DataSerializer public static Tdu TypedListComposer(IEnumerable value, Type type, Warehouse warehouse, EpConnection connection) { - var tru = Tru.FromType(type); + var elementTru = Tru.FromType(type, warehouse); - byte[] composed = TypedArrayComposer(value, tru, warehouse, connection); + byte[] composed = TypedArrayComposer(value, elementTru, warehouse, connection); if (composed == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); - var metadata = tru.Compose(); + var metadata = new TruComposite(TruIdentifier.TypedList, false, + new Tru[] { elementTru }, value.GetType()); - return new Tdu(TduIdentifier.TypedList, composed, (uint)composed.Length, metadata); + return new Tdu(TduIdentifier.Typed, composed, (uint)composed.Length, metadata, connection); } - //public static byte[] PropertyValueComposer(PropertyValue propertyValue, EpConnection connection)//, bool includeAge = true) - //{ - // var rt = new BinaryList(); - - // return - // .AddUInt64(propertyValue.Age) - // .AddDateTime(propertyValue.Date) - // .AddUInt8Array(Codec.Compose(propertyValue.Value, connection)) - // .ToArray(); - //} public static Tdu PropertyValueArrayComposer(object value, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); var rt = new List(); var ar = value as PropertyValue[]; @@ -671,16 +655,16 @@ public static class DataSerializer } return new Tdu(TduIdentifier.RawData, rt.ToArray(), - (uint)rt.Count); + (uint)rt.Count, null, null); } public static Tdu TypedMapComposer(object value, Type keyType, Type valueType, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); - var kt = Tru.FromType(keyType); - var vt = Tru.FromType(valueType); + var kt = Tru.FromType(keyType, warehouse); + var vt = Tru.FromType(valueType, warehouse); //var rt = new List(); @@ -692,34 +676,33 @@ public static class DataSerializer var compsedKeys = TypedArrayComposer(keys, kt, warehouse, connection); var compsedValues = TypedArrayComposer(values, vt, warehouse, connection); - var ktb = kt.Compose(); - var vtb = vt.Compose(); + //var ktb = kt.Compose(); + //var vtb = vt.Compose(); - var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length); + //var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length); - //foreach (var el in map.Serialize()) - // rt.AddRange(Codec.Compose(el, warehouse, connection)); - var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length).Composed; - var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length).Composed; + var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length, null, null).Composed; + var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length, null, null).Composed; var all = DC.Combine(keysTdu, 0, (uint)keysTdu.Length, valuesTdu, 0, (uint)valuesTdu.Length); - return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata); + return new Tdu(TduIdentifier.Typed, all, (uint)all.Length, + new TruComposite(TruIdentifier.TypedMap, false, new Tru[] { kt, vt }, value.GetType()), connection); + + //return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata); - //return new TDU(TDUIdentifier.TypedMap, rt.ToArray(), (uint)rt.Count, - // ); } public static Tdu TypedDictionaryComposer(object value, Type keyType, Type valueType, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); - var kt = Tru.FromType(keyType); - var vt = Tru.FromType(valueType); + var kt = Tru.FromType(keyType, warehouse); + var vt = Tru.FromType(valueType, warehouse); //var rt = new List(); @@ -731,48 +714,18 @@ public static class DataSerializer var compsedKeys = TypedArrayComposer(keys, kt, warehouse, connection); var compsedValues = TypedArrayComposer(values, vt, warehouse, connection); - var ktb = kt.Compose(); - var vtb = vt.Compose(); - var metadata = DC.Combine(ktb, 0, (uint)ktb.Length, vtb, 0, (uint)vtb.Length); - - //foreach (var el in map.Serialize()) - // rt.AddRange(Codec.Compose(el, warehouse, connection)); - - var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length).Composed; - var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length).Composed; + var keysTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedKeys, (uint)compsedKeys.Length, null, null).Composed; + var valuesTdu = new Tdu(TduIdentifier.TypeOfTarget, compsedValues, (uint)compsedValues.Length, null, null).Composed; var all = DC.Combine(keysTdu, 0, (uint)keysTdu.Length, valuesTdu, 0, (uint)valuesTdu.Length); - return new Tdu(TduIdentifier.TypedMap, all, (uint)all.Length, metadata); + return new Tdu(TduIdentifier.Typed, all, (uint)all.Length, + new TruComposite(TruIdentifier.TypedMap, false, new Tru[] { kt, vt }, + value.GetType()), + connection); - //if (value == null) - // return new TDU(TDUIdentifier.Null, null, 0); - - //var kt = TRU.FromType(keyType).Compose(); - //var vt = TRU.FromType(valueType).Compose(); - - //var rt = new List(); - - ////rt.AddRange(kt); - ////rt.AddRange(vt); - - //var dic = (IDictionary)value; - - //var ar = new List(); - //foreach (var k in dic.Keys) - //{ - // ar.Add(k); - // ar.Add(dic[k]); - //} - - //foreach (var el in ar) - // rt.AddRange(Codec.Compose(el, warehouse, connection)); - - - //return new TDU(TDUIdentifier.TypedMap, rt.ToArray(), (uint)rt.Count, - // DC.Combine(kt, 0, (uint)kt.Length, vt, 0, (uint)vt.Length)); } public static byte[] DynamicArrayComposer(IEnumerable value, Warehouse warehouse, EpConnection connection) @@ -792,7 +745,7 @@ public static class DataSerializer var d = tdu.Composed.Clip(tdu.ContentOffset, (uint)tdu.Composed.Length - tdu.ContentOffset); - var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length); + var ntd = new Tdu(TduIdentifier.TypeContinuation, d, (ulong)d.Length, null, null); rt.AddRange(ntd.Composed); } else @@ -809,23 +762,23 @@ public static class DataSerializer public static Tdu ResourceListComposer(object value, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection); return new Tdu(TduIdentifier.ResourceList, composed, - (uint)composed.Length); + (uint)composed.Length, null, null); } public static Tdu RecordListComposer(object value, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); var composed = DynamicArrayComposer((IEnumerable)value, warehouse, connection); return new Tdu(TduIdentifier.RecordList, - composed, (uint)composed.Length); + composed, (uint)composed.Length, null, null); } @@ -835,54 +788,54 @@ public static class DataSerializer if (resource.Instance == null || resource.Instance.IsDestroyed) { - return new Tdu(TduIdentifier.Null, null, 0); + return new Tdu(TduIdentifier.Null, null, 0, null, null); } if (Codec.IsLocalResource(resource, connection)) { - var rid = (resource as EpResource).DistributedResourceInstanceId; + var rid = (resource as EpResource).ResourceInstanceId; if (rid <= 0xFF) - return new Tdu(TduIdentifier.LocalResource8, new byte[] { (byte)rid }, 1); + return new Tdu(TduIdentifier.LocalResource8, new byte[] { (byte)rid }, 1, null, null); else if (rid <= 0xFFFF) { var rt = new byte[2]; fixed (byte* ptr = rt) *((ushort*)ptr) = (ushort)rid; - return new Tdu(TduIdentifier.LocalResource16, rt, 2); + return new Tdu(TduIdentifier.LocalResource16, rt, 2, null, null); } else { var rt = new byte[4]; fixed (byte* ptr = rt) *((uint*)ptr) = rid; - return new Tdu(TduIdentifier.LocalResource32, rt, 4); + return new Tdu(TduIdentifier.LocalResource32, rt, 4, null, null); } } else { - connection.cache.Add(value as IResource, DateTime.UtcNow); + connection._cache.Add(value as IResource, DateTime.UtcNow); var rid = resource.Instance.Id; if (rid <= 0xFF) - return new Tdu(TduIdentifier.RemoteResource8, new byte[] { (byte)rid }, 1); + return new Tdu(TduIdentifier.RemoteResource8, new byte[] { (byte)rid }, 1, null, null); else if (rid <= 0xFFFF) { var rt = new byte[2]; fixed (byte* ptr = rt) *((ushort*)ptr) = (ushort)rid; - return new Tdu(TduIdentifier.RemoteResource16, rt, 2); + return new Tdu(TduIdentifier.RemoteResource16, rt, 2, null, null); } else { var rt = new byte[4]; fixed (byte* ptr = rt) *((uint*)ptr) = rid; - return new Tdu(TduIdentifier.RemoteResource32, rt, 4); + return new Tdu(TduIdentifier.RemoteResource32, rt, 4, null, null); } } } @@ -890,7 +843,7 @@ public static class DataSerializer public static unsafe Tdu MapComposer(object value, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 1); + return new Tdu(TduIdentifier.Null, new byte[0], 1, null, null); var rt = new List(); var map = (IMap)value; @@ -898,12 +851,12 @@ public static class DataSerializer foreach (var el in map.Serialize()) rt.AddRange(Codec.Compose(el, warehouse, connection)); - return new Tdu(TduIdentifier.Map, rt.ToArray(), (uint)rt.Count); + return new Tdu(TduIdentifier.Map, rt.ToArray(), (uint)rt.Count, null, null); } public static unsafe Tdu UUIDComposer(object value, Warehouse warehouse, EpConnection connection) { - return new Tdu(TduIdentifier.UUID, ((Uuid)value).Data, 16); + return new Tdu(TduIdentifier.UUID, ((Uuid)value).Data, 16, null, null); } @@ -912,34 +865,47 @@ public static class DataSerializer var rt = new List(); var record = (IRecord)value; - var typeDef = warehouse.GetTypeDefByType(record.GetType()); + var recordTru = Tru.FromType(value.GetType(), warehouse) ; + TypeDef typeDef = null; + + if (value is Record typedRecord) + { + typeDef = typedRecord.TypeDef; + } + else if (recordTru is TruTypeDef recordTypeDefTru) + { + // @TODO need to enhance performance, maybe cache this in the connection or something + typeDef = recordTypeDefTru.TypeDef;// recordTru.type;// .GetTypeDef(warehouse, connection.RemoteDomain); + } + else + { + throw new Exception("Unsupported."); + } foreach (var pt in typeDef.Properties) { var propValue = pt.PropertyInfo.GetValue(record, null); - //if (propValue == null) - // return TDU(TDUIdentifier.Null, null, 0); - var tru = Tru.FromType(propValue?.GetType()); + var tru = Tru.FromType(propValue?.GetType(), warehouse); var tdu = Codec.ComposeInternal(propValue, warehouse, connection); - if (pt.ValueType.IsTyped() && + if (tdu.Class == TduClass.Typed && // pt.ValueType.IsTyped() && pt.ValueType.Match(tru)) { // strip metadata var len = (uint)tdu.Composed.Length - tdu.ContentOffset; tdu = new Tdu(TduIdentifier.TypeOfTarget, - tdu.Composed.Clip(tdu.ContentOffset, len), len); + tdu.Composed.Clip(tdu.ContentOffset, len), len, null, null); } rt.AddRange(tdu.Composed); } - return new Tdu(TduIdentifier.Record, rt.ToArray(), - (uint)rt.Count, - typeDef.Id.Data); + // @TODO: serialize metadata type Id to byte, ushort or uint depending on size. + return new Tdu(TduIdentifier.Typed, rt.ToArray(), (uint)rt.Count, recordTru, connection); + } public static byte[] HistoryComposer(KeyList history, Warehouse warehouse, @@ -961,18 +927,13 @@ public static class DataSerializer public static Tdu TupleComposer(object value, Warehouse warehouse, EpConnection connection) { if (value == null) - return new Tdu(TduIdentifier.Null, new byte[0], 0); + return new Tdu(TduIdentifier.Null, new byte[0], 0, null, null); var fields = value.GetType().GetFields(); var list = fields.Select(x => x.GetValue(value)).ToArray(); - var trus = fields.Select(x => Tru.FromType(x.FieldType)).ToArray(); + var trus = fields.Select(x => Tru.FromType(x.FieldType, warehouse)).ToArray(); - var metadata = new List(); - - foreach (var t in trus) - metadata.AddRange(t.Compose()); - var rt = new List(); for (var i = 0; i < fields.Length; i++) @@ -982,22 +943,36 @@ public static class DataSerializer var tdu = Codec.ComposeInternal(tupleValue, warehouse, connection); - var valueTru = Tru.FromType(tupleValue?.GetType()); + var valueTru = Tru.FromType(tupleValue?.GetType(), warehouse); - if (targetTru.IsTyped() && + if (tdu.Class == TduClass.Typed && // targetTru.IsTyped() && targetTru.Match(valueTru)) { // strip metadata var len = (uint)tdu.Composed.Length - tdu.ContentOffset; tdu = new Tdu(TduIdentifier.TypeOfTarget, - tdu.Composed.Clip(tdu.ContentOffset, len), len); + tdu.Composed.Clip(tdu.ContentOffset, len), len, null, null); } rt.AddRange(tdu.Composed); } - return new Tdu(TduIdentifier.TypedTuple, rt.ToArray(), - (uint)rt.Count, metadata.ToArray()); + //return new Tdu(TduIdentifier.TypedTuple, rt.ToArray(), + // (uint)rt.Count, metadata.ToArray()); + + var truIdentifier = trus.Length switch + { + 2 => TruIdentifier.Tuple2, + 3 => TruIdentifier.Tuple3, + 4 => TruIdentifier.Tuple4, + 5 => TruIdentifier.Tuple5, + 6 => TruIdentifier.Tuple6, + 7 => TruIdentifier.Tuple7, + _ => throw new NotSupportedException("Tuples with more than 7 or less than 2 elements are not supported.") + }; + + return new Tdu(TduIdentifier.Typed, rt.ToArray(), + (uint)rt.Count, new TruComposite(truIdentifier, false, trus, value.GetType()), connection); } } diff --git a/Libraries/Esiur/Data/IParseResult.cs b/Libraries/Esiur/Data/IParseResult.cs new file mode 100644 index 0000000..702f8b3 --- /dev/null +++ b/Libraries/Esiur/Data/IParseResult.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public interface IParseResult + { + public T Value { get; } + public uint Size { get; } + } +} diff --git a/Libraries/Esiur/Data/IRemoteRecord.cs b/Libraries/Esiur/Data/IRemoteRecord.cs new file mode 100644 index 0000000..ea6c29f --- /dev/null +++ b/Libraries/Esiur/Data/IRemoteRecord.cs @@ -0,0 +1,12 @@ +using Esiur.Data.Types; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public interface IRemoteRecord: IRecord + { + public RemoteTypeDef TypeDef { get; } + } +} diff --git a/Libraries/Esiur/Data/Map.cs b/Libraries/Esiur/Data/Map.cs index cccb312..abd85a8 100644 --- a/Libraries/Esiur/Data/Map.cs +++ b/Libraries/Esiur/Data/Map.cs @@ -96,11 +96,7 @@ public class Map : Dictionary, IMap // IEnumerable $"{x.Key}: {x.Value}")) + "}"; } //public Map(Map source) diff --git a/Libraries/Esiur/Data/ParseResult.cs b/Libraries/Esiur/Data/ParseResult.cs new file mode 100644 index 0000000..c7a0bb9 --- /dev/null +++ b/Libraries/Esiur/Data/ParseResult.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public readonly struct ParseResult: IParseResult + { + public T Value { get; } + public uint Size { get; } + + public ParseResult(T value, uint size) + { + Value = value; + Size = size; + } + } +} \ No newline at end of file diff --git a/Libraries/Esiur/Data/ParsedTdu.cs b/Libraries/Esiur/Data/ParsedTdu.cs index 8c1c1e7..101bbba 100644 --- a/Libraries/Esiur/Data/ParsedTdu.cs +++ b/Libraries/Esiur/Data/ParsedTdu.cs @@ -1,4 +1,7 @@ -using System; +using Esiur.Core; +using Esiur.Protocol; +using Esiur.Resource; +using System; using System.Collections.Generic; using System.Text; @@ -9,16 +12,22 @@ namespace Esiur.Data public TduIdentifier Identifier; public int Index; public TduClass Class; - public uint Offset; - public ulong ContentLength; + public uint PayloadOffset; + public ulong PayloadLength; public byte[] Data; public byte Exponent; public ulong TotalLength; - public byte[] Metadata; + public Tru Metadata; public uint Ends; - public static ParsedTdu Parse(byte[] data, uint offset, uint ends) + public static AsyncReply ParseAsync(byte[] data, EpConnection connection) { + return ParseAsync(data, (uint)0, (uint)data.Length, connection); + } + + public static async AsyncReply ParseAsync(byte[] data, uint offset, uint ends, EpConnection connection) + { + // @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it. var h = data[offset++]; @@ -33,11 +42,11 @@ namespace Esiur.Data { Identifier = (TduIdentifier)h, Data = data, - Offset = offset, + PayloadOffset = offset, Class = cls, Exponent = (byte)exp, Index = (byte)h & 0x7, - ContentLength = 0, + PayloadLength = 0, TotalLength = 1, Ends = ends }; @@ -57,9 +66,9 @@ namespace Esiur.Data { Identifier = (TduIdentifier)h, Data = data, - Offset = offset, + PayloadOffset = offset, Class = cls, - ContentLength = cl, + PayloadLength = cl, TotalLength = 1 + cl, Exponent = (byte)exp, Index = (byte)h & 0x7, @@ -89,20 +98,22 @@ namespace Esiur.Data Class = TduClass.Invalid, }; - var metaData = DC.Clip(data, offset + 1, data[offset]); - offset += data[offset] + (uint)1; + //var metaData = DC.Clip(data, offset + 1, data[offset]); + //offset += data[offset] + (uint)1; + var metaDataTru = await Tru.ParseAsync(data, offset, connection, null); + offset += metaDataTru.Size; return new ParsedTdu() { Identifier = (TduIdentifier)(h & 0xC7), Data = data, - Offset = offset, + PayloadOffset = offset, Class = cls, - ContentLength = cl - 1 - (uint)metaData.Length, + PayloadLength = cl - metaDataTru.Size, TotalLength = 1 + cl + cll, Index = (byte)h & 0x7, - Metadata = metaData, + Metadata = metaDataTru.Value, Ends = ends }; } @@ -135,9 +146,9 @@ namespace Esiur.Data { Identifier = (TduIdentifier)(h & 0xC7), Data = data, - Offset = offset, + PayloadOffset = offset, Class = cls, - ContentLength = cl, + PayloadLength = cl, TotalLength = 1 + cl + cll, Index = (byte)h & 0x7, Ends = ends @@ -145,5 +156,324 @@ namespace Esiur.Data } } + public static object Parse(byte[] data, uint offset, uint ends, EpConnection connection) + { + // @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it. + + var h = data[offset++]; + + var cls = (TduClass)(h >> 6); + + if (cls == TduClass.Fixed) + { + var exp = (h & 0x38) >> 3; + + if (exp == 0) + return new ParsedTdu() + { + Identifier = (TduIdentifier)h, + Data = data, + PayloadOffset = offset, + Class = cls, + Exponent = (byte)exp, + Index = (byte)h & 0x7, + PayloadLength = 0, + TotalLength = 1, + Ends = ends + }; + + ulong cl = (ulong)(1 << (exp - 1)); + + if (ends - offset < cl) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cl - (ends - offset)) + }; + + //offset += (uint)cl; + + return new ParsedTdu() + { + Identifier = (TduIdentifier)h, + Data = data, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl, + Exponent = (byte)exp, + Index = (byte)h & 0x7, + Ends = ends + }; + } + else if (cls == TduClass.Typed) + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cll - (ends - offset)) + }; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return new ParsedTdu() + { + TotalLength = (cl - (ends - offset)), + Class = TduClass.Invalid, + }; + + //var metaData = DC.Clip(data, offset + 1, data[offset]); + //offset += data[offset] + (uint)1; + var rt = new AsyncReply(); + + Tru.ParseAsync(data, offset, connection, null).Then(metaDataTru => + { + offset += metaDataTru.Size; + + rt.Trigger(new ParsedTdu() + { + Identifier = (TduIdentifier)(h & 0xC7), + Data = data, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl - metaDataTru.Size, + TotalLength = 1 + cl + cll, + Index = (byte)h & 0x7, + Metadata = metaDataTru.Value, + Ends = ends + }); + }); + + return rt; + } + else + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cll - (ends - offset)) + }; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cl - (ends - offset)) + }; + + + return + new ParsedTdu() + { + Identifier = (TduIdentifier)(h & 0xC7), + Data = data, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl + cll, + Index = (byte)h & 0x7, + Ends = ends + }; + } + } + + public static byte[] ClipTduData(byte[] data, uint offset, uint ends) + { + var oOffset = (int)offset; + + var h = data[offset++]; + + var cls = (TduClass)(h >> 6); + + if (cls == TduClass.Fixed) + { + var exp = (h & 0x38) >> 3; + + if (exp == 0) + { + return new byte[] { h }; + } + + ulong cl = (ulong)(1 << (exp - 1)); + + if (ends - offset < cl) + { + return null; // failded + } + + var rt = new byte[1 + cl]; + Buffer.BlockCopy(data, oOffset, rt, 0, (int)cl); + return rt; + + } + else + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return null; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return null; + + var rt = new byte[1 + cll + cl]; + + Buffer.BlockCopy(data, oOffset, rt, 0, rt.Length); + + return rt; + } + + } + + public static ParsedTdu ParseSync(byte[] data, uint offset, uint ends, Warehouse warehouse) + { + // @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it. + + var h = data[offset++]; + + var cls = (TduClass)(h >> 6); + + if (cls == TduClass.Fixed) + { + var exp = (h & 0x38) >> 3; + + if (exp == 0) + return new ParsedTdu() + { + Identifier = (TduIdentifier)h, + Data = data, + PayloadOffset = offset, + Class = cls, + Exponent = (byte)exp, + Index = (byte)h & 0x7, + PayloadLength = 0, + TotalLength = 1, + Ends = ends + }; + + ulong cl = (ulong)(1 << (exp - 1)); + + if (ends - offset < cl) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cl - (ends - offset)) + }; + + //offset += (uint)cl; + + return new ParsedTdu() + { + Identifier = (TduIdentifier)h, + Data = data, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl, + Exponent = (byte)exp, + Index = (byte)h & 0x7, + Ends = ends + }; + } + else if (cls == TduClass.Typed) + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cll - (ends - offset)) + }; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return new ParsedTdu() + { + TotalLength = (cl - (ends - offset)), + Class = TduClass.Invalid, + }; + + //var metaData = DC.Clip(data, offset + 1, data[offset]); + //offset += data[offset] + (uint)1; + + var metaDataTru = Tru.Parse(data, offset, warehouse); + offset += metaDataTru.Size; + + return new ParsedTdu() + { + Identifier = (TduIdentifier)(h & 0xC7), + Data = data, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl - metaDataTru.Size, + TotalLength = 1 + cl + cll, + Index = (byte)h & 0x7, + Metadata = metaDataTru.Value, + Ends = ends + }; + } + else + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cll - (ends - offset)) + }; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return new ParsedTdu() + { + Class = TduClass.Invalid, + TotalLength = (cl - (ends - offset)) + }; + + + return + new ParsedTdu() + { + Identifier = (TduIdentifier)(h & 0xC7), + Data = data, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl + cll, + Index = (byte)h & 0x7, + Ends = ends + }; + } + } } } diff --git a/Libraries/Esiur/Data/PlainTdu.cs b/Libraries/Esiur/Data/PlainTdu.cs new file mode 100644 index 0000000..05cc3e2 --- /dev/null +++ b/Libraries/Esiur/Data/PlainTdu.cs @@ -0,0 +1,155 @@ +using Esiur.Core; +using Esiur.Protocol; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public struct PlainTdu + { + public TduIdentifier Identifier; + public int Index; + public TduClass Class; + public uint TduOffset; + public uint PayloadOffset; + public ulong PayloadLength; + public byte[] Data; + public byte Exponent; + public ulong TotalLength; + public uint Ends; + + + + public static PlainTdu Parse(byte[] data, uint offset, uint ends) + { + var oOffset = offset; + + // @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it. + + var h = data[offset++]; + + var cls = (TduClass)(h >> 6); + + if (cls == TduClass.Fixed) + { + var exp = (h & 0x38) >> 3; + + if (exp == 0) + return new PlainTdu() + { + Identifier = (TduIdentifier)h, + Data = data, + TduOffset = oOffset, + PayloadOffset = offset, + Class = cls, + Exponent = (byte)exp, + Index = (byte)h & 0x7, + PayloadLength = 0, + TotalLength = 1, + Ends = ends + }; + + ulong cl = (ulong)(1 << (exp - 1)); + + if (ends - offset < cl) + return new PlainTdu() + { + Class = TduClass.Invalid, + TotalLength = (cl - (ends - offset)) + }; + + //offset += (uint)cl; + + return new PlainTdu() + { + Identifier = (TduIdentifier)h, + Data = data, + TduOffset= oOffset, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl, + Exponent = (byte)exp, + Index = (byte)h & 0x7, + Ends = ends + }; + } + else if (cls == TduClass.Typed) + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return new PlainTdu() + { + Class = TduClass.Invalid, + TotalLength = (cll - (ends - offset)) + }; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return new PlainTdu() + { + TotalLength = (cl - (ends - offset)), + Class = TduClass.Invalid, + }; + + + return new PlainTdu() + { + Identifier = (TduIdentifier)(h & 0xC7), + Data = data, + TduOffset = oOffset, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl + cll, + Index = (byte)h & 0x7, + Ends = ends + }; + } + else + { + ulong cll = (ulong)(h >> 3) & 0x7; + + if (ends - offset < cll) + return new PlainTdu() + { + Class = TduClass.Invalid, + TotalLength = (cll - (ends - offset)) + }; + + ulong cl = 0; + + for (uint i = 0; i < cll; i++) + cl = cl << 8 | data[offset++]; + + if (ends - offset < cl) + return new PlainTdu() + { + Class = TduClass.Invalid, + TotalLength = (cl - (ends - offset)) + }; + + + return + new PlainTdu() + { + Identifier = (TduIdentifier)(h & 0xC7), + Data = data, + TduOffset = oOffset, + PayloadOffset = offset, + Class = cls, + PayloadLength = cl, + TotalLength = 1 + cl + cll, + Index = (byte)h & 0x7, + Ends = ends + }; + } + } + } +} diff --git a/Libraries/Esiur/Data/Record.cs b/Libraries/Esiur/Data/Record.cs index df6e462..366238d 100644 --- a/Libraries/Esiur/Data/Record.cs +++ b/Libraries/Esiur/Data/Record.cs @@ -1,9 +1,21 @@ -using System; +using Esiur.Data.Types; +using System; using System.Collections.Generic; using System.Text; namespace Esiur.Data; + public class Record : KeyList, IRecord { + public TypeDef TypeDef { get; private set; } + public Record(TypeDef typeDef) + { + TypeDef = typeDef; + } + + public override string ToString() + { + return $"Record<{TypeDef.Name}> {{{string.Join(", ", this.Select(x=>x.Key + ": " + x.Value))}}}"; + } } diff --git a/Libraries/Esiur/Data/Tdu.cs b/Libraries/Esiur/Data/Tdu.cs index 1ffffe6..c3834f3 100644 --- a/Libraries/Esiur/Data/Tdu.cs +++ b/Libraries/Esiur/Data/Tdu.cs @@ -1,4 +1,5 @@ -using System; +using Esiur.Protocol; +using System; using System.Collections.Generic; using System.Text; using System.Xml.Linq; @@ -18,7 +19,7 @@ public struct Tdu public byte[] Composed; //public uint Offset; - public byte[] Metadata; + public Tru Metadata; public uint ContentOffset; @@ -64,7 +65,7 @@ public struct Tdu } public Tdu(TduIdentifier identifier, - byte[] data, ulong length, byte[] metadata = null) + byte[] data, ulong length, Tru metadata, EpConnection connection) { Identifier = identifier; //Index = (byte)identifier & 0x7; @@ -174,34 +175,20 @@ public struct Tdu if (metadata == null) throw new Exception("Metadata must be provided for types."); - - if (metadata.Length > 0xFF) - throw new Exception("Metadata can't exceed 255 bytes in length."); - - var metaLen = (byte)metadata.Length; - - var len = 1 + (ulong)metaLen + length; + var metadataData = metadata.Compose(connection); - if (length == 0 && (metadata == null || metadata.Length == 0)) - { - Composed = new byte[1] { (byte)Identifier }; - throw new Exception("Need check"); - } - else if (metadata.Length > 0xFF) - { - throw new Exception("Metadata can't exceed 255 bytes in length."); - } - else if (length <= 0xFF) + var len = (ulong)metadataData.Length + length; + + if (len <= 0xFF) { Composed = new byte[2 + len]; Composed[0] = (byte)((byte)Identifier | 0x8); Composed[1] = (byte)len; - Composed[2] = metaLen; - ContentOffset = metaLen + (uint)3; + ContentOffset = (uint)metadataData.Length + (uint)2; - Buffer.BlockCopy(metadata, 0, Composed, 3, metaLen); - Buffer.BlockCopy(data, 0, Composed, 3 + metaLen, (int)length); + Buffer.BlockCopy(metadataData, 0, Composed, 2, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 2 + metadataData.Length, (int)length); } else if (len <= 0xFF_FF) { @@ -209,11 +196,10 @@ public struct Tdu Composed[0] = (byte)((byte)identifier | 0x10); Composed[1] = (byte)((len >> 8) & 0xFF); Composed[2] = (byte)(len & 0xFF); - Composed[3] = metaLen; - ContentOffset = metaLen + (uint)4; + ContentOffset = (uint)metadataData.Length + (uint)3; - Buffer.BlockCopy(metadata, 0, Composed, 4, metaLen); - Buffer.BlockCopy(data, 0, Composed, 4 + metaLen, (int)length); + Buffer.BlockCopy(metadataData, 0, Composed, 3, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 3 + metadataData.Length, (int)length); } else if (len <= 0xFF_FF_FF) { @@ -222,11 +208,10 @@ public struct Tdu Composed[1] = (byte)((len >> 16) & 0xFF); Composed[2] = (byte)((len >> 8) & 0xFF); Composed[3] = (byte)(len & 0xFF); - Composed[4] = metaLen; - ContentOffset = metaLen + (uint)5; + ContentOffset = (uint)metadataData.Length + (uint)4; - Buffer.BlockCopy(metadata, 0, Composed, 5, metaLen); - Buffer.BlockCopy(data, 0, Composed, 5 + metaLen, (int)length); + Buffer.BlockCopy(metadataData, 0, Composed, 4, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 4 + metadataData.Length, (int)length); } else if (len <= 0xFF_FF_FF_FF) @@ -237,11 +222,10 @@ public struct Tdu Composed[2] = (byte)((len >> 16) & 0xFF); Composed[3] = (byte)((len >> 8) & 0xFF); Composed[4] = (byte)(len & 0xFF); - Composed[5] = metaLen; - ContentOffset = metaLen + (uint)6; + ContentOffset = (uint)metadataData.Length + (uint)5; - Buffer.BlockCopy(metadata, 0, Composed, 6, metaLen); - Buffer.BlockCopy(data, 0, Composed, 6 + metaLen, (int)length); + Buffer.BlockCopy(metadataData, 0, Composed, 5, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 5 + metadataData.Length, (int)length); } else if (len <= 0xFF_FF_FF_FF_FF) { @@ -252,11 +236,10 @@ public struct Tdu Composed[3] = (byte)((len >> 16) & 0xFF); Composed[4] = (byte)((len >> 8) & 0xFF); Composed[5] = (byte)(len & 0xFF); - Composed[6] = metaLen; - ContentOffset = metaLen + (uint)7; + ContentOffset = (uint)metadataData.Length + (uint)6; - Buffer.BlockCopy(metadata, 0, Composed, 7, metaLen); - Buffer.BlockCopy(data, 0, Composed, 7 + metaLen, (int)length); + Buffer.BlockCopy(metadataData, 0, Composed, 6, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 6 + metadataData.Length, (int)length); } else if (len <= 0xFF_FF_FF_FF_FF_FF) { @@ -268,11 +251,10 @@ public struct Tdu Composed[4] = (byte)((len >> 16) & 0xFF); Composed[5] = (byte)((len >> 8) & 0xFF); Composed[6] = (byte)(len & 0xFF); - Composed[7] = metaLen; - ContentOffset = metaLen + (uint)8; + ContentOffset = (uint)metadataData.Length + (uint)7; - Buffer.BlockCopy(metadata, 0, Composed, 8, metaLen); - Buffer.BlockCopy(data, 0, Composed, 8 + metaLen, (int)length); + Buffer.BlockCopy(metadataData, 0, Composed, 7, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 7 + metadataData.Length, (int)length); } else //if (len <= 0xFF_FF_FF_FF_FF_FF_FF) { @@ -285,11 +267,11 @@ public struct Tdu Composed[5] = (byte)((len >> 16) & 0xFF); Composed[6] = (byte)((len >> 8) & 0xFF); Composed[7] = (byte)(len & 0xFF); - Composed[8] = metaLen; - ContentOffset = metaLen + (uint)9; - Buffer.BlockCopy(metadata, 0, Composed, 9, metaLen); - Buffer.BlockCopy(data, 0, Composed, 9 + metaLen, (int)length); + ContentOffset = (uint)metadataData.Length + (uint)8; + + Buffer.BlockCopy(metadataData, 0, Composed, 8, metadataData.Length); + Buffer.BlockCopy(data, 0, Composed, 8 + metadataData.Length, (int)length); } } @@ -305,7 +287,7 @@ public struct Tdu if (Class != TduClass.Typed || with.Class != TduClass.Typed) return false; - if (!Metadata.SequenceEqual(with.Metadata)) + if (!Metadata.Match(with.Metadata)) return false; return true; diff --git a/Libraries/Esiur/Data/TduIdentifier.cs b/Libraries/Esiur/Data/TduIdentifier.cs index e202823..b7dd893 100644 --- a/Libraries/Esiur/Data/TduIdentifier.cs +++ b/Libraries/Esiur/Data/TduIdentifier.cs @@ -50,12 +50,14 @@ namespace Esiur.Data Map = 0x46, MapList = 0x47, - Record = 0x80, - TypedList = 0x81, - TypedMap = 0x82, - TypedTuple = 0x83, - TypedEnum = 0x84, - TypedConstant = 0x85, + Typed = 0x80, + + //Record = 0x80, + //TypedList = 0x81, + //TypedMap = 0x82, + //TypedTuple = 0x83, + //TypedEnum = 0x84, + //TypedConstant = 0x85, TypeContinuation = 0xC0, TypeOfTarget = 0xC1, diff --git a/Libraries/Esiur/Data/Tru.cs b/Libraries/Esiur/Data/Tru.cs index f6a1547..3f4d679 100644 --- a/Libraries/Esiur/Data/Tru.cs +++ b/Libraries/Esiur/Data/Tru.cs @@ -1,5 +1,6 @@ using Esiur.Core; using Esiur.Data.Types; +using Esiur.Protocol; using Esiur.Resource; using Microsoft.CodeAnalysis; using System; @@ -14,10 +15,10 @@ using System.Text; namespace Esiur.Data { - public class Tru + public abstract class Tru { - static TruIdentifier[] refTypes = new TruIdentifier[] + protected static TruIdentifier[] RefTypes = new TruIdentifier[] { TruIdentifier.Dynamic, TruIdentifier.RawData, @@ -34,215 +35,243 @@ namespace Esiur.Data TruIdentifier.Tuple5, TruIdentifier.Tuple6, TruIdentifier.Tuple7, - TruIdentifier.TypedRecord, - TruIdentifier.TypedResource + TruIdentifier.LocalType8, + TruIdentifier.LocalType16, + TruIdentifier.LocalType32, + TruIdentifier.LocalType64, + TruIdentifier.RemoteType8, + TruIdentifier.RemoteType16, + TruIdentifier.RemoteType32, + TruIdentifier.RemoteType64, }; - static Map typesMap = new Map() + protected static Dictionary TypesMapping = new Dictionary() { - [TduIdentifier.UInt8] = TruIdentifier.UInt8, - [TduIdentifier.Int8] = TruIdentifier.Int8, - [TduIdentifier.UInt16] = TruIdentifier.UInt16, - [TduIdentifier.Int16] = TruIdentifier.Int16, - [TduIdentifier.UInt32] = TruIdentifier.UInt32, - [TduIdentifier.Int32] = TruIdentifier.Int32, - [TduIdentifier.UInt64] = TruIdentifier.UInt64, - [TduIdentifier.Int64] = TruIdentifier.Int64, - [TduIdentifier.UInt128] = TruIdentifier.UInt128, - [TduIdentifier.Int128] = TruIdentifier.Int128, - [TduIdentifier.Char8] = TruIdentifier.Char, - [TduIdentifier.DateTime] = TruIdentifier.DateTime, - [TduIdentifier.Float32] = TruIdentifier.Float32, - [TduIdentifier.Float64] = TruIdentifier.Float64, - [TduIdentifier.Decimal128] = TruIdentifier.Decimal, - [TduIdentifier.False] = TruIdentifier.Bool, - [TduIdentifier.True] = TruIdentifier.Bool, - [TduIdentifier.Map] = TruIdentifier.Map, - [TduIdentifier.List] = TruIdentifier.List, - [TduIdentifier.RawData] = TruIdentifier.RawData, - [TduIdentifier.Record] = TruIdentifier.Record, - [TduIdentifier.String] = TruIdentifier.String, + + [TruIdentifier.Void] = typeof(void), + [TruIdentifier.Bool] = typeof(bool), + [TruIdentifier.Char] = typeof(char), + [TruIdentifier.UInt8] = typeof(byte), + [TruIdentifier.Int8] = typeof(sbyte), + [TruIdentifier.Int16] = typeof(short), + [TruIdentifier.UInt16] = typeof(ushort), + [TruIdentifier.Int32] = typeof(int), + [TruIdentifier.UInt32] = typeof(uint), + [TruIdentifier.Int64] = typeof(long), + [TruIdentifier.UInt64] = typeof(ulong), + [TruIdentifier.Float32] = typeof(float), + [TruIdentifier.Float64] = typeof(double), + [TruIdentifier.Decimal] = typeof(decimal), + [TruIdentifier.String] = typeof(string), + [TruIdentifier.DateTime] = typeof(DateTime), + [TruIdentifier.Resource] = typeof(IResource), + [TruIdentifier.Record] = typeof(IRecord), + [TruIdentifier.Dynamic] = typeof(object), + [TruIdentifier.List] = typeof(object[]), + }; + + protected static Dictionary NullableTypesMapping = new Dictionary() + { + + [TruIdentifier.Void] = typeof(void), + [TruIdentifier.Bool] = typeof(bool?), + [TruIdentifier.Char] = typeof(char?), + [TruIdentifier.UInt8] = typeof(byte?), + [TruIdentifier.Int8] = typeof(sbyte?), + [TruIdentifier.Int16] = typeof(short?), + [TruIdentifier.UInt16] = typeof(ushort?), + [TruIdentifier.Int32] = typeof(int?), + [TruIdentifier.UInt32] = typeof(uint?), + [TruIdentifier.Int64] = typeof(long?), + [TruIdentifier.UInt64] = typeof(ulong?), + [TruIdentifier.Float32] = typeof(float?), + [TruIdentifier.Float64] = typeof(double?), + [TruIdentifier.Decimal] = typeof(decimal?), + [TruIdentifier.String] = typeof(string), + [TruIdentifier.DateTime] = typeof(DateTime?), + [TruIdentifier.Resource] = typeof(IResource), + [TruIdentifier.Record] = typeof(IRecord), + [TruIdentifier.Dynamic] = typeof(object), + [TruIdentifier.List] = typeof(object[]), }; - public void SetNull(List flags) - { - if (refTypes.Contains(Identifier)) - { - Nullable = (flags.FirstOrDefault() == 2); - if (flags.Count > 0) - flags.RemoveAt(0); - } - if (SubTypes != null) - foreach (var st in SubTypes) - st.SetNull(flags); + //static Map typesMap = new Map() + //{ + // [TduIdentifier.UInt8] = TruIdentifier.UInt8, + // [TduIdentifier.Int8] = TruIdentifier.Int8, + // [TduIdentifier.UInt16] = TruIdentifier.UInt16, + // [TduIdentifier.Int16] = TruIdentifier.Int16, + // [TduIdentifier.UInt32] = TruIdentifier.UInt32, + // [TduIdentifier.Int32] = TruIdentifier.Int32, + // [TduIdentifier.UInt64] = TruIdentifier.UInt64, + // [TduIdentifier.Int64] = TruIdentifier.Int64, + // [TduIdentifier.UInt128] = TruIdentifier.UInt128, + // [TduIdentifier.Int128] = TruIdentifier.Int128, + // [TduIdentifier.Char8] = TruIdentifier.Char, + // [TduIdentifier.DateTime] = TruIdentifier.DateTime, + // [TduIdentifier.Float32] = TruIdentifier.Float32, + // [TduIdentifier.Float64] = TruIdentifier.Float64, + // [TduIdentifier.Decimal128] = TruIdentifier.Decimal, + // [TduIdentifier.False] = TruIdentifier.Bool, + // [TduIdentifier.True] = TruIdentifier.Bool, + // [TduIdentifier.Map] = TruIdentifier.Map, + // [TduIdentifier.List] = TruIdentifier.List, + // [TduIdentifier.RawData] = TruIdentifier.RawData, + // [TduIdentifier.Record] = TruIdentifier.Record, + // [TduIdentifier.String] = TruIdentifier.String, + //}; + + + public abstract void SetNull(List flags); + public abstract void SetNull(byte flag); + + + public override bool Equals(object obj) + { + // check if the object is a Tru + if (obj is Tru other) + return Match(other); + + return false; } - public void SetNull(byte flag) - { - if (refTypes.Contains(Identifier)) - { - Nullable = (flag == 2); - } + public abstract void SetNotNull(List flags); - if (SubTypes != null) - foreach (var st in SubTypes) - st.SetNull(flag); - } + public abstract void SetNotNull(byte flag); + public abstract Type RuntimeType { get; protected set; } - public void SetNotNull(List flags) - { - if (refTypes.Contains(Identifier)) - { - Nullable = (flags.FirstOrDefault() != 1); - if (flags.Count > 0) - flags.RemoveAt(0); - } + //public Type? GetRuntimeType(Warehouse warehouse, string domain) + //{ + // if (Identifier == TruIdentifier.TypedList) + // { + // var sub = SubTypes?[0].GetRuntimeType(warehouse, domain); + // if (sub == null) + // return null; - if (SubTypes != null) - foreach (var st in SubTypes) - st.SetNotNull(flags); - } + // var rt = sub.MakeArrayType(); + // return rt; + // } + // else if (Identifier == TruIdentifier.TypedMap) + // { + // var subs = SubTypes.Select(x => x.GetRuntimeType(warehouse, domain)).ToArray(); + // var rt = typeof(Map<,>).MakeGenericType(subs); + // return rt; + // } - public override string ToString() - { - if (SubTypes != null && SubTypes.Length > 0) - return Identifier.ToString() + "<" + String.Join(",", SubTypes.Select(x => x.ToString())) + ">" + (Nullable ? "?" : ""); - return Identifier.ToString() + (Nullable ? "?" : ""); - } - public void SetNotNull(byte flag) - { - if (refTypes.Contains(Identifier)) - { - Nullable = (flag != 1); - } + // return Identifier switch + // { + // TruIdentifier.Void => typeof(void), + // TruIdentifier.Dynamic => typeof(object), + // TruIdentifier.Bool => Nullable ? typeof(bool?) : typeof(bool), + // TruIdentifier.Char => Nullable ? typeof(char?) : typeof(char), + // TruIdentifier.UInt8 => Nullable ? typeof(byte?) : typeof(byte), + // TruIdentifier.Int8 => Nullable ? typeof(sbyte?) : typeof(sbyte), + // TruIdentifier.Int16 => Nullable ? typeof(short?) : typeof(short), + // TruIdentifier.UInt16 => Nullable ? typeof(ushort?) : typeof(ushort), + // TruIdentifier.Int32 => Nullable ? typeof(int?) : typeof(int), + // TruIdentifier.UInt32 => Nullable ? typeof(uint?) : typeof(uint), + // TruIdentifier.Int64 => Nullable ? typeof(long?) : typeof(long), + // TruIdentifier.UInt64 => Nullable ? typeof(ulong?) : typeof(ulong), + // TruIdentifier.Float32 => Nullable ? typeof(float?) : typeof(float), + // TruIdentifier.Float64 => Nullable ? typeof(double?) : typeof(double), + // TruIdentifier.Decimal => Nullable ? typeof(decimal?) : typeof(decimal), + // TruIdentifier.String => typeof(string), + // TruIdentifier.DateTime => Nullable ? typeof(DateTime?) : typeof(DateTime), + // TruIdentifier.Resource => typeof(IResource), + // TruIdentifier.Record => typeof(IRecord), - if (SubTypes != null) - foreach (var st in SubTypes) - st.SetNotNull(flag); - } - - public Type? GetRuntimeType(Warehouse warehouse) - { - - if (Identifier == TruIdentifier.TypedList) - { - var sub = SubTypes?[0].GetRuntimeType(warehouse); - if (sub == null) - return null; - - var rt = sub.MakeArrayType(); - - return rt; - } - else if (Identifier == TruIdentifier.TypedMap) - { - var subs = SubTypes.Select(x => x.GetRuntimeType(warehouse)).ToArray(); - var rt = typeof(Map<,>).MakeGenericType(subs); - return rt; - } - - return Identifier switch - { - (TruIdentifier.Void) => typeof(void), - (TruIdentifier.Dynamic) => typeof(object), - (TruIdentifier.Bool) => Nullable ? typeof(bool?) : typeof(bool), - (TruIdentifier.Char) => Nullable ? typeof(char?) : typeof(char), - (TruIdentifier.UInt8) => Nullable ? typeof(byte?) : typeof(byte), - (TruIdentifier.Int8) => Nullable ? typeof(sbyte?) : typeof(sbyte), - (TruIdentifier.Int16) => Nullable ? typeof(short?) : typeof(short), - (TruIdentifier.UInt16) => Nullable ? typeof(ushort?) : typeof(ushort), - (TruIdentifier.Int32) => Nullable ? typeof(int?) : typeof(int), - (TruIdentifier.UInt32) => Nullable ? typeof(uint?) : typeof(uint), - (TruIdentifier.Int64) => Nullable ? typeof(ulong?) : typeof(long), - (TruIdentifier.UInt64) => Nullable ? typeof(ulong?) : typeof(ulong), - (TruIdentifier.Float32) => Nullable ? typeof(float?) : typeof(float), - (TruIdentifier.Float64) => Nullable ? typeof(double?) : typeof(double), - (TruIdentifier.Decimal) => Nullable ? typeof(decimal?) : typeof(decimal), - (TruIdentifier.String) => typeof(string), - (TruIdentifier.DateTime) => Nullable ? typeof(DateTime?) : typeof(DateTime), - (TruIdentifier.Resource) => typeof(IResource), - (TruIdentifier.Record) => typeof(IRecord), - (TruIdentifier.TypedRecord) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Record)?.DefinedType, - (TruIdentifier.TypedResource) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Resource)?.DefinedType, - (TruIdentifier.Enum) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Enum)?.DefinedType, - - _ => null - }; - } + // TruIdentifier.LocalType8 + // or TruIdentifier.LocalType16 + // or TruIdentifier.LocalType32 + // or TruIdentifier.LocalType64 => (TypeDef as LocalTypeDef).DefinedType,// TypeDefId == null ? throw new Exception("TypeDef not set.") + // // : warehouse.GetLocalTypeDefById(TypeDefId.Value.Value).DefinedType, + // TruIdentifier.RemoteType8 + // or TruIdentifier.RemoteType16 + // or TruIdentifier.RemoteType32 + // or TruIdentifier.RemoteType64 => (TypeDef as RemoteTypeDef).ProxyType,// TypeDefId == null ? throw new Exception("TypeDef not set.") + // // : warehouse.GetRemoteTypeDefById(domain, TypeDefId.Value.Value).ProxyType, + // //(TruIdentifier.TypedRecord) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Record)?.DefinedType, + // //(TruIdentifier.TypedResource) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Resource)?.DefinedType, + // //(TruIdentifier.Enum) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Enum)?.DefinedType, + // _ => null + // }; + //} public TruIdentifier Identifier; public bool Nullable; - public Uuid? UUID; + + //public TypeDefId? TypeDefId; + + //public Uuid? UUID; //public RepresentationType? SubType1; // List + Map //public RepresentationType? SubType2; // Map //public RepresentationType? SubType3; // No types yet - public Tru[]? SubTypes = null; - public Tru ToNullable() - { - return new Tru(Identifier, true, UUID, SubTypes); - } + public abstract Tru ToNullable(); + //{ + // //return new Tru(Identifier, true, TypeDefId, TypeDef, SubTypes); + // return new Tru(Identifier, true, TypeDef, SubTypes); + //} - public bool IsTyped() - { - if (Identifier == TruIdentifier.TypedList && SubTypes[0].Identifier == TruIdentifier.UInt8) - return false; + //public bool IsTyped() + //{ + // if (Identifier == TruIdentifier.TypedList && SubTypes[0].Identifier == TruIdentifier.UInt8) + // return false; - if (Identifier == TruIdentifier.TypedResource) - return false; + // if (Identifier == TruIdentifier.TypedResource) + // return false; - return (UUID != null) || (SubTypes != null && SubTypes.Length > 0); - } + // return (TypeDefId != null) || (SubTypes != null && SubTypes.Length > 0); + //} - public bool Match(Tru other) - { - //if (UUID == null && (SubTypes == null || SubTypes.Length == 0)) - // return false; - - if (other.Identifier != Identifier) - return false; - if (other.UUID != UUID) - return false; - - if (other.SubTypes != null) - { - if (other.SubTypes.Length != (SubTypes?.Length ?? -1)) - return false; - - for (var i = 0; i < SubTypes?.Length; i++) - if (!SubTypes[i].Match(other.SubTypes[i])) - return false; - } - - return true; - } + public abstract bool Match(Tru other); - public (TduIdentifier, byte[]) GetMetadata() - { - switch (Identifier) - { - case TruIdentifier.TypedList: - return (TduIdentifier.TypedList, SubTypes[0].Compose()); - case TruIdentifier.TypedRecord: - return (TduIdentifier.Record, UUID?.Data); - case TruIdentifier.TypedMap: - return (TduIdentifier.TypedMap, - SubTypes[0].Compose().Concat(SubTypes[1].Compose()).ToArray()); - case TruIdentifier.Enum: - return (TduIdentifier.TypedEnum, UUID?.Data); + //public TduIdentifier MapToTduIdentifier() + //{ + // switch (Identifier) + // { + // case TruIdentifier.TypedList: + // return TduIdentifier.TypedList;//, SubTypes[0].Compose()); + // case TruIdentifier.LocalType16: + // return TduIdentifier.Record, UUID?.Data; + // case TruIdentifier.TypedMap: + // return (TduIdentifier.TypedMap, + // SubTypes[0].Compose().Concat(SubTypes[1].Compose()).ToArray()); + // case TruIdentifier.Enum: + // return (TduIdentifier.TypedEnum, UUID?.Data); - default: + // default: - throw new NotImplementedException(); - } - } + // throw new NotImplementedException(); + // } + //} + + //public (TduIdentifier, byte[]) GetMetadata() + //{ + // switch (Identifier) + // { + // case TruIdentifier.TypedList: + // return (TduIdentifier.TypedList, SubTypes[0].Compose()); + // case TruIdentifier.TypedRecord: + // return (TduIdentifier.Record, UUID?.Data); + // case TruIdentifier.TypedMap: + // return (TduIdentifier.TypedMap, + // SubTypes[0].Compose().Concat(SubTypes[1].Compose()).ToArray()); + // case TruIdentifier.Enum: + // return (TduIdentifier.TypedEnum, UUID?.Data); + + // default: + + // throw new NotImplementedException(); + // } + //} //public TDUIdentifier GetTDUIdentifer() //{ @@ -267,296 +296,332 @@ namespace Esiur.Data // case TRUIdentifier. } //} - private static Dictionary cache = new Dictionary(); - private static object cacheLook = new object(); + //private static Dictionary cache = new Dictionary(); + //private static object cacheLook = new object(); - public static Tru? FromType(Type type) + public static Tru? FromType(Type type, Warehouse warehouse) { if (type == null) - return new Tru(TruIdentifier.Void, true); + return new TruPrimitive(TruIdentifier.Void, true, typeof(void)); - lock (cacheLook) + + var nullable = false; + + var nullType = System.Nullable.GetUnderlyingType(type); + + if (nullType != null) { - if (cache.ContainsKey(type)) - return cache[type]; + type = nullType; + nullable = true; + } - var nullable = false; + Tru? tru = null; - var nullType = System.Nullable.GetUnderlyingType(type); + if (type == typeof(IResource)) + { + return new TruPrimitive(TruIdentifier.Resource, nullable, typeof(IResource)); + } + else if (type == typeof(IRecord) || type == typeof(Record)) + { + return new TruPrimitive(TruIdentifier.Record, nullable, typeof(IRecord)); + } + else if (type == typeof(Map) + || type == typeof(Dictionary)) + { + return new TruPrimitive(TruIdentifier.Map, nullable, type); + } + else if (Codec.ImplementsInterface(type, typeof(IResource)) + || Codec.ImplementsInterface(type, typeof(IRecord)) + || type.IsEnum) + { - if (nullType != null) + var typeDef = warehouse.GetLocalTypeDefByType(type); + + if (typeDef == null) + throw new Exception("Unregistered type: " + type.FullName + "."); + + return new TruTypeDef(nullable, typeDef); + + } + else if (type.IsGenericType) + { + var genericType = type.GetGenericTypeDefinition(); + + if (genericType == typeof(List<>) + || genericType == typeof(VarList<>) + || genericType == typeof(IList<>)) { - type = nullType; - nullable = true; - } - - Tru? tru = null; - - if (type == typeof(IResource)) - { - return new Tru(TruIdentifier.Resource, nullable); - } - else if (type == typeof(IRecord) || type == typeof(Record)) - { - return new Tru(TruIdentifier.Record, nullable); - } - else if (type == typeof(Map) - || type == typeof(Dictionary)) - { - return new Tru(TruIdentifier.Map, nullable); - } - else if (Codec.ImplementsInterface(type, typeof(IResource))) - { - tru = new Tru( - TruIdentifier.TypedResource, - nullable, - TypeDef.GetTypeUUID(type) - ); - } - else if (Codec.ImplementsInterface(type, typeof(IRecord))) - { - tru = new Tru( - TruIdentifier.TypedRecord, - nullable, - TypeDef.GetTypeUUID(type) - ); - } - else if (type.IsGenericType) - { - var genericType = type.GetGenericTypeDefinition(); - - if (genericType == typeof(List<>) - || genericType == typeof(VarList<>) - || genericType == typeof(IList<>)) + var args = type.GetGenericArguments(); + if (args[0] == typeof(object)) { - var args = type.GetGenericArguments(); - if (args[0] == typeof(object)) - { - tru = new Tru(TruIdentifier.List, nullable); - } - else - { - var subType = FromType(args[0]); - if (subType == null) // unrecongnized type - return null; - - - tru = new Tru(TruIdentifier.TypedList, nullable, null, - new Tru[] { subType }); - - } - } - else if (genericType == typeof(Map<,>) - || genericType == typeof(Dictionary<,>)) - { - var args = type.GetGenericArguments(); - if (args[0] == typeof(object) && args[1] == typeof(object)) - { - tru = new Tru(TruIdentifier.Map, nullable); - } - else - { - var subType1 = FromType(args[0]); - if (subType1 == null) - return null; - - var subType2 = FromType(args[1]); - if (subType2 == null) - return null; - - tru = new Tru(TruIdentifier.TypedMap, nullable, null, - new Tru[] { subType1, subType2 }); - - } - } - else if (genericType == typeof(ResourceLink<>)) - { - var args = type.GetGenericArguments(); - - return FromType(args[0]); - } - else if (genericType == typeof(ValueTuple<,>)) - { - var args = type.GetGenericArguments(); - var subTypes = new Tru[args.Length]; - for (var i = 0; i < args.Length; i++) - { - var t = FromType(args[i]); - if (t == null) - return null; - subTypes[i] = t; - } - - - tru = new Tru(TruIdentifier.Tuple2, nullable, null, subTypes); - - } - else if (genericType == typeof(ValueTuple<,,>)) - { - var args = type.GetGenericArguments(); - var subTypes = new Tru[args.Length]; - for (var i = 0; i < args.Length; i++) - { - var t = FromType(args[i]); - if (t == null) - return null; - subTypes[i] = t; - } - - tru = new Tru(TruIdentifier.Tuple3, nullable, null, subTypes); - - } - else if (genericType == typeof(ValueTuple<,,,>)) - { - - var args = type.GetGenericArguments(); - var subTypes = new Tru[args.Length]; - for (var i = 0; i < args.Length; i++) - { - var t = FromType(args[i]); - if (t == null) - return null; - subTypes[i] = t; - } - - tru = new Tru(TruIdentifier.Tuple4, nullable, null, subTypes); - } - else if (genericType == typeof(ValueTuple<,,,,>)) - { - var args = type.GetGenericArguments(); - var subTypes = new Tru[args.Length]; - for (var i = 0; i < args.Length; i++) - { - var t = FromType(args[i]); - if (t == null) - return null; - subTypes[i] = t; - } - - tru = new Tru(TruIdentifier.Tuple5, nullable, null, subTypes); - } - else if (genericType == typeof(ValueTuple<,,,,,>)) - { - var args = type.GetGenericArguments(); - var subTypes = new Tru[args.Length]; - for (var i = 0; i < args.Length; i++) - { - var t = FromType(args[i]); - if (t == null) - return null; - subTypes[i] = t; - } - - tru = new Tru(TruIdentifier.Tuple6, nullable, null, subTypes); - } - else if (genericType == typeof(ValueTuple<,,,,,,>)) - { - var args = type.GetGenericArguments(); - var subTypes = new Tru[args.Length]; - for (var i = 0; i < args.Length; i++) - { - var t = FromType(args[i]); - if (t == null) - return null; - subTypes[i] = t; - } - - tru = new Tru(TruIdentifier.Tuple7, nullable, null, subTypes); + tru = new TruPrimitive(TruIdentifier.List, nullable, typeof(object[])); } else - return null; + { + var subType = FromType(args[0], warehouse); + + if (subType == null) // unrecongnized type + throw new Exception("Unrecognized type: " + args[0].FullName); + + return new TruComposite(TruIdentifier.TypedList, nullable, + new Tru[] { subType }, type); + + } } - else if (type.IsArray) + else if (genericType == typeof(Map<,>) + || genericType == typeof(Dictionary<,>)) { - var elementType = type.GetElementType(); - if (elementType == typeof(object)) - tru = new Tru(TruIdentifier.List, nullable); + var args = type.GetGenericArguments(); + if (args[0] == typeof(object) && args[1] == typeof(object)) + { + tru = new TruPrimitive(TruIdentifier.Map, nullable, type); + } else { - var subType = FromType(elementType); - - if (subType == null) + var subType1 = FromType(args[0], warehouse); + if (subType1 == null) return null; - tru = new Tru(TruIdentifier.TypedList, nullable, null, - new Tru[] { subType }); + var subType2 = FromType(args[1], warehouse); + if (subType2 == null) + return null; + + tru = new TruComposite(TruIdentifier.TypedMap, nullable, + new Tru[] { subType1, subType2 }, type); } } - else if (type.IsEnum) + else if (genericType == typeof(ResourceLink<>)) { - tru = new Tru(TruIdentifier.Enum, nullable, TypeDef.GetTypeUUID(type)); + var args = type.GetGenericArguments(); + + return FromType(args[0], warehouse); } - else if (type.IsInterface) + else if (genericType == typeof(ValueTuple<,>)) { - return null; // other interfaces are not supported + var args = type.GetGenericArguments(); + var subTypes = new Tru[args.Length]; + for (var i = 0; i < args.Length; i++) + { + var t = FromType(args[i], warehouse); + if (t == null) + return null; + subTypes[i] = t; + } + + + tru = new TruComposite(TruIdentifier.Tuple2, nullable, subTypes, type); + } - - //else if (typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject) => TRUIdentifier.Structure) - //{ - - //} - - if (tru != null) + else if (genericType == typeof(ValueTuple<,,>)) { - cache.Add(type, tru); - return tru; + var args = type.GetGenericArguments(); + var subTypes = new Tru[args.Length]; + for (var i = 0; i < args.Length; i++) + { + var t = FromType(args[i], warehouse); + if (t == null) + return null; + subTypes[i] = t; + } + + tru = new TruComposite(TruIdentifier.Tuple3, nullable, subTypes, type); + } - - // last check - return type switch + else if (genericType == typeof(ValueTuple<,,,>)) { - _ when type == typeof(void) => new Tru(TruIdentifier.Void, nullable), - _ when type == typeof(object) => new Tru(TruIdentifier.Dynamic, nullable), - _ when type == typeof(bool) => new Tru(TruIdentifier.Bool, nullable), - _ when type == typeof(char) => new Tru(TruIdentifier.Char, nullable), - _ when type == typeof(byte) => new Tru(TruIdentifier.UInt8, nullable), - _ when type == typeof(sbyte) => new Tru(TruIdentifier.Int8, nullable), - _ when type == typeof(short) => new Tru(TruIdentifier.Int16, nullable), - _ when type == typeof(ushort) => new Tru(TruIdentifier.UInt16, nullable), - _ when type == typeof(int) => new Tru(TruIdentifier.Int32, nullable), - _ when type == typeof(uint) => new Tru(TruIdentifier.UInt32, nullable), - _ when type == typeof(long) => new Tru(TruIdentifier.Int64, nullable), - _ when type == typeof(ulong) => new Tru(TruIdentifier.UInt64, nullable), - _ when type == typeof(float) => new Tru(TruIdentifier.Float32, nullable), - _ when type == typeof(double) => new Tru(TruIdentifier.Float64, nullable), - _ when type == typeof(decimal) => new Tru(TruIdentifier.Decimal, nullable), - _ when type == typeof(string) => new Tru(TruIdentifier.String, nullable), - _ when type == typeof(DateTime) => new Tru(TruIdentifier.DateTime, nullable), - _ when type == typeof(ResourceLink) => new Tru(TruIdentifier.Resource, nullable), - _ => null - }; + + var args = type.GetGenericArguments(); + var subTypes = new Tru[args.Length]; + for (var i = 0; i < args.Length; i++) + { + var t = FromType(args[i], warehouse); + if (t == null) + return null; + subTypes[i] = t; + } + + tru = new TruComposite(TruIdentifier.Tuple4, nullable, subTypes, type); + } + else if (genericType == typeof(ValueTuple<,,,,>)) + { + var args = type.GetGenericArguments(); + var subTypes = new Tru[args.Length]; + for (var i = 0; i < args.Length; i++) + { + var t = FromType(args[i], warehouse); + if (t == null) + return null; + subTypes[i] = t; + } + + tru = new TruComposite(TruIdentifier.Tuple5, nullable, subTypes, type); + } + else if (genericType == typeof(ValueTuple<,,,,,>)) + { + var args = type.GetGenericArguments(); + var subTypes = new Tru[args.Length]; + for (var i = 0; i < args.Length; i++) + { + var t = FromType(args[i], warehouse); + if (t == null) + return null; + subTypes[i] = t; + } + + tru = new TruComposite(TruIdentifier.Tuple6, nullable, subTypes, type); + } + else if (genericType == typeof(ValueTuple<,,,,,,>)) + { + var args = type.GetGenericArguments(); + var subTypes = new Tru[args.Length]; + for (var i = 0; i < args.Length; i++) + { + var t = FromType(args[i], warehouse); + if (t == null) + return null; + subTypes[i] = t; + } + + tru = new TruComposite(TruIdentifier.Tuple7, nullable, subTypes, type); + } + else + return null; } + else if (type.IsArray) + { + var elementType = type.GetElementType(); + if (elementType == typeof(object)) + tru = new TruPrimitive(TruIdentifier.List, nullable, type); + else + { + var subType = FromType(elementType, warehouse); + + if (subType == null) + return null; + + tru = new TruComposite(TruIdentifier.TypedList, nullable, new Tru[] { subType }, type); + } + } + //else if (type.IsEnum) + //{ + // tru = new Tru(TruIdentifier.Enum, nullable, TypeDef.GetTypeUUID(type)); + //} + else if (type.IsInterface) + { + return null; // other interfaces are not supported + } + + //else if (typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject) => TRUIdentifier.Structure) + //{ + + //} + + if (tru != null) + { + // cache.Add(type, tru); + return tru; + } + + // last check + return type switch + { + _ when type == typeof(void) => new TruPrimitive(TruIdentifier.Void, nullable, type), + _ when type == typeof(object) => new TruPrimitive(TruIdentifier.Dynamic, nullable, type), + _ when type == typeof(bool) => new TruPrimitive(TruIdentifier.Bool, nullable, type), + _ when type == typeof(char) => new TruPrimitive(TruIdentifier.Char, nullable, type), + _ when type == typeof(byte) => new TruPrimitive(TruIdentifier.UInt8, nullable, type), + _ when type == typeof(sbyte) => new TruPrimitive(TruIdentifier.Int8, nullable, type), + _ when type == typeof(short) => new TruPrimitive(TruIdentifier.Int16, nullable, type), + _ when type == typeof(ushort) => new TruPrimitive(TruIdentifier.UInt16, nullable, type), + _ when type == typeof(int) => new TruPrimitive(TruIdentifier.Int32, nullable, type), + _ when type == typeof(uint) => new TruPrimitive(TruIdentifier.UInt32, nullable, type), + _ when type == typeof(long) => new TruPrimitive(TruIdentifier.Int64, nullable, type), + _ when type == typeof(ulong) => new TruPrimitive(TruIdentifier.UInt64, nullable, type), + _ when type == typeof(float) => new TruPrimitive(TruIdentifier.Float32, nullable, type), + _ when type == typeof(double) => new TruPrimitive(TruIdentifier.Float64, nullable, type), + _ when type == typeof(decimal) => new TruPrimitive(TruIdentifier.Decimal, nullable, type), + _ when type == typeof(string) => new TruPrimitive(TruIdentifier.String, nullable, type), + _ when type == typeof(DateTime) => new TruPrimitive(TruIdentifier.DateTime, nullable, type), + _ when type == typeof(ResourceLink) => new TruPrimitive(TruIdentifier.Resource, nullable, type), + _ => null + }; + //} } - public Tru(TruIdentifier identifier, bool nullable, Uuid? uuid = null, Tru[]? subTypes = null) - { - Nullable = nullable; - Identifier = identifier; - UUID = uuid; - SubTypes = subTypes; - } + //public Tru(TruIdentifier identifier, bool nullable, TypeDef? typeDef = null, Tru[]? subTypes = null) + //{ + // Nullable = nullable; + // Identifier = identifier; + // //TypeDefId = typeDefId; + // SubTypes = subTypes; + // TypeDef = typeDef; - public byte[] Compose() - { - var rt = new BinaryList(); + // if (typeDef == null && (identifier >= TruIdentifier.LocalType8 && identifier <= TruIdentifier.RemoteType64))// TypeDefId != null) + // throw new Exception("TypeDef must be provided."); + //} - if (Nullable) - rt.AddUInt8((byte)(0x80 | (byte)Identifier)); - else - rt.AddUInt8((byte)Identifier); + public abstract byte[] Compose(EpConnection connection); - if (UUID != null) - rt.AddUInt8Array(UUID.Value.Data); + //{ + // var rt = new BinaryList(); - if (SubTypes != null) - for (var i = 0; i < SubTypes.Length; i++) - rt.AddUInt8Array(SubTypes[i].Compose()); + // if (Nullable) + // rt.AddUInt8((byte)(0x80 | (byte)Identifier)); + // else + // rt.AddUInt8((byte)Identifier); - return rt.ToArray(); - } + // if (Identifier == TruIdentifier.LocalType8 + // || Identifier == TruIdentifier.RemoteType8) + // { + // if (TypeDef == null) + // { + // throw new Exception("TypeDefId is required for LocalType8 and RemoteType8"); + // } + // else if (TypeDef is LocalTypeDef localTypeDef) + // { + // rt.AddUInt8((byte)localTypeDef.Id); - public static (uint, Tru) Parse(byte[] data, uint offset) + // } + // else if (TypeDef is RemoteTypeDef remoteTypeDef) + // { + // rt.AddUInt8( (byte)remoteTypeDef.Id); + // } + // // rt.AddUInt8((byte)TypeDefId?.Value!); + + // } + // else if (Identifier == TruIdentifier.LocalType16 + // || Identifier == TruIdentifier.RemoteType16) + // { + // if (TypeDefId == null) + // throw new Exception("TypeDefId is required for LocalType16 and RemoteType16"); + + // rt.AddUInt16((ushort)TypeDefId?.Value!); + // } + // else if (Identifier == TruIdentifier.LocalType32 + // || Identifier == TruIdentifier.RemoteType32) + // { + // if (TypeDefId == null) + // throw new Exception("TypeDefId is required for LocalType32 and RemoteType32"); + // rt.AddUInt32((uint)TypeDefId?.Value!); + // } + // else if (Identifier == TruIdentifier.LocalType64 + // || Identifier == TruIdentifier.RemoteType64) + // { + // if (TypeDefId == null) + // throw new Exception("TypeDefId is required for LocalType64 and RemoteType64"); + // rt.AddUInt64((ulong)TypeDefId?.Value!); + // } + + // // TODO: don't rely on SubTypes length, but on the identifier + // if (SubTypes != null) + // for (var i = 0; i < SubTypes.Length; i++) + // rt.AddUInt8Array(SubTypes[i].Compose()); + + // return rt.ToArray(); + //} + + public static IParseResult Parse(byte[] data, uint offset, Warehouse warehouse) { var oOffset = offset; @@ -564,36 +629,261 @@ namespace Esiur.Data bool nullable = (header & 0x80) > 0; var identifier = (TruIdentifier)(header & 0x7F); - if ((header & 0x40) > 0) { - - var hasUUID = (header & 0x4) > 0; var subsCount = (header >> 3) & 0x7; - Uuid? uuid = null; - - if (hasUUID) + if (subsCount == 0) { - uuid = data.GetUUID(offset); - offset += 16; + ulong typeDefId = 0; + bool isRemote = false; + + if (identifier == TruIdentifier.LocalType8) + { + typeDefId = data[offset]; + offset += 1; + } + else if (identifier == TruIdentifier.RemoteType8) + { + typeDefId = data[offset]; + isRemote = true; + offset += 1; + } + else if (identifier == TruIdentifier.LocalType16) + { + typeDefId = data.GetUInt16(offset, Endian.Little); + offset += 2; + } + else if (identifier == TruIdentifier.RemoteType16) + { + typeDefId = data.GetUInt16(offset, Endian.Little); + isRemote = true; + offset += 2; + } + else if (identifier == TruIdentifier.LocalType32) + { + typeDefId = data.GetUInt32(offset, Endian.Little); + offset += 4; + } + else if (identifier == TruIdentifier.RemoteType32) + { + typeDefId = data.GetUInt32(offset, Endian.Little); + offset += 4; + } + else if (identifier == TruIdentifier.LocalType64) + { + typeDefId = data.GetUInt64(offset, Endian.Little); + offset += 8; + } + else if (identifier == TruIdentifier.RemoteType64) + { + typeDefId = data.GetUInt64(offset, Endian.Little); + isRemote = true; + offset += 8; + } + else + { + throw new Exception("Invalid identifier."); + } + + // try to get the typedef from the warehouse. + + if (isRemote) + { + throw new Exception("Unsupported operation."); + } + else + { + var td = warehouse.GetLocalTypeDefById(typeDefId); + + if (td == null) + throw new Exception("TypeDef not found."); + + return new ParseResult( + new TruTypeDef(nullable, td), + offset - oOffset); + } } - - var subs = new Tru[subsCount]; - - for (var i = 0; i < subsCount; i++) + else { - (var len, subs[i]) = Tru.Parse(data, offset); - offset += len; - } + var subTypes = new Tru[subsCount]; + for (var i = 0; i < subsCount; i++) + { + var pr = Tru.Parse(data, offset, warehouse); + subTypes[i] = pr.Value; + offset += pr.Size; + } - return (offset - oOffset, new Tru(identifier, nullable, uuid, subs)); + Type runtimeType = null; + + if (identifier == TruIdentifier.TypedList) + { + runtimeType = subTypes[0].RuntimeType.MakeArrayType(); + } + else if (identifier == TruIdentifier.TypedMap) + { + var subs = subTypes.Select(x => x.RuntimeType).ToArray(); + runtimeType = typeof(Map<,>).MakeGenericType(subs); + } + // @TODO: Need Tuples + + if (runtimeType != null && nullable) + { + runtimeType = typeof(Nullable<>).MakeGenericType(runtimeType); + } + + return new ParseResult( + new TruComposite(identifier, nullable, subTypes, runtimeType), + offset - oOffset); + } } else { - return (1, new Tru(identifier, nullable)); + var runtimeType = nullable ? Tru.NullableTypesMapping[identifier] + : Tru.TypesMapping[identifier]; + return new ParseResult( + new TruPrimitive(identifier, nullable, runtimeType), + 1); } } + + + public static async AsyncReply> ParseAsync(byte[] data, uint offset, EpConnection connection, ulong[] requestSequence) + { + var oOffset = offset; + + var header = data[offset++]; + bool nullable = (header & 0x80) > 0; + var identifier = (TruIdentifier)(header & 0x7F); + + if ((header & 0x40) > 0) + { + var subsCount = (header >> 3) & 0x7; + + if (subsCount == 0) + { + ulong typeDefId = 0; + bool isRemote = false; + + if (identifier == TruIdentifier.LocalType8) + { + typeDefId = data[offset]; + offset += 1; + } + else if (identifier == TruIdentifier.RemoteType8) + { + typeDefId = data[offset]; + isRemote = true; + offset += 1; + } + else if (identifier == TruIdentifier.LocalType16) + { + typeDefId = data.GetUInt16(offset, Endian.Little); + offset += 2; + } + else if (identifier == TruIdentifier.RemoteType16) + { + typeDefId = data.GetUInt16(offset, Endian.Little); + isRemote = true; + offset += 2; + } + else if (identifier == TruIdentifier.LocalType32) + { + typeDefId = data.GetUInt32(offset, Endian.Little); + offset += 4; + } + else if (identifier == TruIdentifier.RemoteType32) + { + typeDefId = data.GetUInt32(offset, Endian.Little); + offset += 4; + } + else if (identifier == TruIdentifier.LocalType64) + { + typeDefId = data.GetUInt64(offset, Endian.Little); + offset += 8; + } + else if (identifier == TruIdentifier.RemoteType64) + { + typeDefId = data.GetUInt64(offset, Endian.Little); + isRemote = true; + offset += 8; + } + else + { + throw new Exception("Invalid identifier."); + } + + // try to get the typedef from the warehouse. + + if (isRemote) + { + if (connection == null) + throw new Exception("Connection is required to resolve remote type definitions."); + + var td = await connection.FetchTypeDef(typeDefId, requestSequence); + + if (td == null) + throw new Exception("TypeDef not found."); + + return new ParseResult( + new TruTypeDef(nullable, td), + offset - oOffset); + } + else + { + var td = connection.Instance.Warehouse.GetLocalTypeDefById(typeDefId); + + if ( td == null) + throw new Exception("TypeDef not found."); + + return new ParseResult( + new TruTypeDef(nullable, td), + offset - oOffset); + } + } + else + { + var subTypes = new Tru[subsCount]; + for (var i = 0; i < subsCount; i++) + { + var pr = await Tru.ParseAsync(data, offset, connection, requestSequence); + subTypes[i] = pr.Value; + offset += pr.Size; + } + + Type runtimeType = null; + + if (identifier == TruIdentifier.TypedList) + { + runtimeType = (subTypes[0].RuntimeType ?? typeof(object)).MakeArrayType(); + } + else if (identifier == TruIdentifier.TypedMap) + { + var subs = subTypes.Select(x => x.RuntimeType).ToArray(); + runtimeType = typeof(Map<,>).MakeGenericType(subs); + } + // @TODO: Need Tuples + + if (runtimeType != null && runtimeType.IsValueType && nullable) + { + //if (runtimeType.IsValueType)// && Nullable.GetUnderlyingType(runtimeType) == null) + runtimeType = typeof(Nullable<>).MakeGenericType(runtimeType); + } + + return new ParseResult( + new TruComposite(identifier, nullable, subTypes, runtimeType), + offset - oOffset); + } + } + else + { + var runtimeType = nullable ? Tru.NullableTypesMapping[identifier] + : Tru.TypesMapping[identifier]; + return new ParseResult( + new TruPrimitive(identifier, nullable, runtimeType), + 1); + } + } } } \ No newline at end of file diff --git a/Libraries/Esiur/Data/TruComposite.cs b/Libraries/Esiur/Data/TruComposite.cs new file mode 100644 index 0000000..aff54f0 --- /dev/null +++ b/Libraries/Esiur/Data/TruComposite.cs @@ -0,0 +1,125 @@ +using Esiur.Data.Types; +using Esiur.Protocol; +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + internal class TruComposite : Tru + { + public Tru[] SubTypes; + + Type _runtimeType; + + public override Type RuntimeType { get; protected set; } + + public TruComposite(TruIdentifier identifier, bool nullable, Tru[] subTypes, Type type) + { + Identifier = identifier; + Nullable = nullable; + SubTypes = subTypes; + RuntimeType = type; + //_runtimeType = typeof(Tuple).MakeGenericType(subTypes.Select(x => x.RuntimeType).ToArray()); + } + + public override void SetNull(List flags) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flags.FirstOrDefault() == 2); + if (flags.Count > 0) + flags.RemoveAt(0); + } + + foreach (var st in SubTypes) + st.SetNull(flags); + } + + + public override void SetNull(byte flag) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flag == 2); + } + + foreach (var st in SubTypes) + st.SetNull(flag); + } + + public override void SetNotNull(List flags) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flags.FirstOrDefault() != 1); + if (flags.Count > 0) + flags.RemoveAt(0); + } + + foreach (var st in SubTypes) + st.SetNotNull(flags); + } + + public override void SetNotNull(byte flag) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flag != 1); + } + + if (SubTypes != null) + foreach (var st in SubTypes) + st.SetNotNull(flag); + } + + public override bool Match(Tru other) + { + + if (other is TruComposite otherComposite) + { + if (other.Identifier != Identifier) + return false; + + + if (otherComposite.SubTypes.Length != (SubTypes?.Length ?? -1)) + return false; + + for (var i = 0; i < SubTypes?.Length; i++) + if (!SubTypes[i].Match(otherComposite.SubTypes[i])) + return false; + + return true; + } + + return false; + } + + public override string ToString() + { + return Identifier.ToString() + "<" + String.Join(",", SubTypes.Select(x => x.ToString())) + ">" + (Nullable ? "?" : ""); + } + + public override byte[] Compose(EpConnection connection) + { + var rt = new BinaryList(); + + if (Nullable) + rt.AddUInt8((byte)(0x80 | (byte)Identifier)); + else + rt.AddUInt8((byte)Identifier); + + + for (var i = 0; i < SubTypes.Length; i++) + rt.AddUInt8Array(SubTypes[i].Compose(connection)); + + return rt.ToArray(); + } + + public override Tru ToNullable() + { + throw new NotImplementedException(); + } + } +} diff --git a/Libraries/Esiur/Data/TruIdentifier.cs b/Libraries/Esiur/Data/TruIdentifier.cs index 874c560..92edd05 100644 --- a/Libraries/Esiur/Data/TruIdentifier.cs +++ b/Libraries/Esiur/Data/TruIdentifier.cs @@ -30,10 +30,20 @@ namespace Esiur.Data Record, List, Map, - Enum = 0x44, - TypedResource = 0x45, // Followed by UUID - TypedRecord = 0x46, // Followed by UUID + //Enum = 0x44, + //TypedResource = 0x45, // Followed by UUID + //TypedRecord = 0x46, // Followed by UUID + LocalType8 = 0x40, + RemoteType8 = 0x41, + LocalType16 = 0x42, + RemoteType16 = 0x43, + LocalType32 = 0x44, + RemoteType32 = 0x45, + LocalType64 = 0x46, + RemoteType64 = 0x47, + TypedList = 0x48, // Followed by element type + Tuple2 = 0x50, // Followed by element type TypedMap = 0x51, // Followed by key type and value type Tuple3 = 0x58, diff --git a/Libraries/Esiur/Data/TruPrimitive.cs b/Libraries/Esiur/Data/TruPrimitive.cs new file mode 100644 index 0000000..d335237 --- /dev/null +++ b/Libraries/Esiur/Data/TruPrimitive.cs @@ -0,0 +1,95 @@ +using Esiur.Data.Types; +using Esiur.Protocol; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public class TruPrimitive:Tru + { + public override Type RuntimeType { get; protected set; } + + public override string ToString() + { + return Identifier.ToString() + (Nullable ? "?" : ""); + } + + public TruPrimitive(TruIdentifier identifier, bool nullable, Type type) + { + Identifier = identifier; + Nullable = nullable; + RuntimeType = type; + } + + public override void SetNull(List flags) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flags.FirstOrDefault() == 2); + if (flags.Count > 0) + flags.RemoveAt(0); + } + } + + + public override void SetNull(byte flag) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flag == 2); + } + } + + public override void SetNotNull(List flags) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flags.FirstOrDefault() != 1); + if (flags.Count > 0) + flags.RemoveAt(0); + } + } + + public override void SetNotNull(byte flag) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flag != 1); + } + } + + public override bool Match(Tru other) + { + + if (other is TruPrimitive otherComposite) + { + if (other.Identifier != Identifier) + return false; + + return true; + } + + return false; + } + + public override byte[] Compose(EpConnection connection) + { + var rt = new BinaryList(); + + if (Nullable) + rt.AddUInt8((byte)(0x80 | (byte)Identifier)); + else + rt.AddUInt8((byte)Identifier); + + return rt.ToArray(); + + } + + public override Tru ToNullable() + { + throw new NotImplementedException(); + } + + } +} diff --git a/Libraries/Esiur/Data/TruTypeDef.cs b/Libraries/Esiur/Data/TruTypeDef.cs new file mode 100644 index 0000000..d10bfaf --- /dev/null +++ b/Libraries/Esiur/Data/TruTypeDef.cs @@ -0,0 +1,133 @@ +using Esiur.Data.Types; +using Esiur.Protocol; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public class TruTypeDef : Tru + { + public TypeDef? TypeDef; + + public override Type RuntimeType { get; protected set; } + + public override void SetNull(List flags) + { + if (RefTypes.Contains(Identifier)) + { + Nullable = (flags.FirstOrDefault() == 2); + if (flags.Count > 0) + flags.RemoveAt(0); + } + } + + public TruTypeDef(bool nullable, TypeDef typeDef) + { + Nullable = nullable; + TypeDef = typeDef; + + if (typeDef is LocalTypeDef localTypeDef) + RuntimeType = localTypeDef.DefinedType; + else if (typeDef is RemoteTypeDef remoteTypeDef) + RuntimeType = remoteTypeDef.ProxyType; + } + + public override void SetNull(byte flag) + { + Nullable = (flag == 2); + } + + public override void SetNotNull(List flags) + { + Nullable = (flags.FirstOrDefault() != 1); + if (flags.Count > 0) + flags.RemoveAt(0); + } + + public override void SetNotNull(byte flag) + { + Nullable = (flag != 1); + } + + public override bool Match(Tru other) + { + if (other is TruTypeDef otherComposite) + { + if (otherComposite.TypeDef != TypeDef) + return false; + + return true; + } + + return false; + } + + public override string ToString() + { + return Identifier.ToString() + (Nullable ? "?" : ""); + } + + public override byte[] Compose(EpConnection connection) + { + var rt = new BinaryList(); + + if (TypeDef is RemoteTypeDef remoteTypeDef) + { + if (connection.RemoteDomain == remoteTypeDef.Domain) + { + // this is local in respect to the connection, send the remote typdef id. + if (Nullable) + rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8); + else + rt.AddUInt8((byte)TruIdentifier.LocalType8); + + rt.AddUInt8((byte)remoteTypeDef.Id); + } + else + { + // this is remote in respect to the connection and the local typedef id is used. + if (Nullable) + rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8); + else + rt.AddUInt8((byte)TruIdentifier.RemoteType8); + + rt.AddUInt8((byte)remoteTypeDef.LocalTypeDefId); + } + } + else if (TypeDef is LocalTypeDef localTypeDef) + { + if (connection == null) + { + // if there is no connection, we assume it's local. + if (Nullable) + rt.AddUInt8(0x80 | (byte)TruIdentifier.LocalType8); + else + rt.AddUInt8((byte)TruIdentifier.LocalType8); + + rt.AddUInt8((byte)localTypeDef.Id); + } + else + { + // this is remote, unless the connection is to self @TODO: solve for this state. + if (Nullable) + rt.AddUInt8(0x80 | (byte)TruIdentifier.RemoteType8); + else + rt.AddUInt8((byte)TruIdentifier.RemoteType8); + + rt.AddUInt8((byte)localTypeDef.Id); + } + } + else + throw new NotImplementedException(); + + return rt.ToArray(); + } + + public override Tru ToNullable() + { + throw new NotImplementedException(); + } + + } +} diff --git a/Libraries/Esiur/Data/TypeDefId.cs b/Libraries/Esiur/Data/TypeDefId.cs new file mode 100644 index 0000000..fca656f --- /dev/null +++ b/Libraries/Esiur/Data/TypeDefId.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Data +{ + public struct TypeDefId + { + public ulong Value; + public bool Remote; + + public TypeDefId(ulong value, bool remote) + { + Value = value; + Remote = remote; + } + + public unsafe override int GetHashCode() + { + // Fallback implementation when System.HashCode is not available. + unchecked + { + int hash = ((int)Value) ^ ((int)(Value >> 32)); + hash = (hash * 397) ^ (Remote ? 1 : 0); + return hash; + } + } + + public override string ToString()=> $"{(Remote ? "Remote" : "Local")}TypeDef{Value}"; + + public override bool Equals(object obj) + { + if (obj is TypeDefId b) + return Value == b.Value && Remote == b.Remote; + + return false; + } + + public static bool operator == (TypeDefId a, TypeDefId b) + { + return a.Equals(b); + } + + public static bool operator !=(TypeDefId a, TypeDefId b) + { + return !(a == b); + } + + } + +} diff --git a/Libraries/Esiur/Data/Types/ArgumentDef.cs b/Libraries/Esiur/Data/Types/ArgumentDef.cs index d34021d..9db51f1 100644 --- a/Libraries/Esiur/Data/Types/ArgumentDef.cs +++ b/Libraries/Esiur/Data/Types/ArgumentDef.cs @@ -1,8 +1,11 @@ -using Esiur.Data; +using Esiur.Core; +using Esiur.Data; +using Esiur.Protocol; using System; using System.Collections.Generic; -using System.Text; +using System.Drawing; using System.Reflection; +using System.Text; namespace Esiur.Data.Types; @@ -20,7 +23,7 @@ public class ArgumentDef public Map Annotations { get; set; } - public static (uint, ArgumentDef) Parse(byte[] data, uint offset, int index) + public static async AsyncReply> ParseAsync(byte[] data, uint offset, int index, EpConnection connection, ulong[] requestSequence) { var optional = (data[offset] & 0x1) == 0x1; var hasAnnotations = (data[offset++] & 0x2) == 0x2; @@ -28,9 +31,9 @@ public class ArgumentDef var cs = (uint)data[offset++]; var name = data.GetString(offset, cs); offset += cs; - var (size, type) = Tru.Parse(data, offset); + var type = await Tru.ParseAsync(data, offset, connection, requestSequence); - offset += size; + offset += type.Size; Map annotations = null; @@ -46,14 +49,14 @@ public class ArgumentDef cs += l; } - return (cs + 2 + size, new ArgumentDef() + return new ParseResult(new ArgumentDef() { Name = name, Index = index, - Type = type, + Type = type.Value, Optional = optional, Annotations = annotations - }); + }, cs + 2 + type.Size); } public ArgumentDef() @@ -70,7 +73,7 @@ public class ArgumentDef return $"{Name}: {Type} "; } - public byte[] Compose() + public byte[] Compose(EpConnection connection) { var name = DC.ToBytes(Name); @@ -80,7 +83,7 @@ public class ArgumentDef .AddUInt8(Optional ? (byte)1 : (byte)0) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(Type.Compose()) + .AddUInt8Array(Type.Compose(connection)) .ToArray(); } else @@ -91,7 +94,7 @@ public class ArgumentDef .AddUInt8((byte)(0x2 | (Optional ? 1 : 0))) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(Type.Compose()) + .AddUInt8Array(Type.Compose(connection)) .AddUInt8Array(exp) .ToArray(); } diff --git a/Libraries/Esiur/Data/Types/ConstantDef.cs b/Libraries/Esiur/Data/Types/ConstantDef.cs index 4bf7be7..3a2a323 100644 --- a/Libraries/Esiur/Data/Types/ConstantDef.cs +++ b/Libraries/Esiur/Data/Types/ConstantDef.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using Esiur.Core; using Esiur.Data; +using Esiur.Protocol; using Esiur.Resource; namespace Esiur.Data.Types; @@ -18,7 +20,7 @@ public class ConstantDef : MemberDef public FieldInfo FieldInfo { get; set; } - public static (uint, ConstantDef) Parse(byte[] data, uint offset, byte index, bool inherited) + public static async AsyncReply< ParseResult> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) { var oOffset = offset; @@ -27,11 +29,16 @@ public class ConstantDef : MemberDef var name = data.GetString(offset + 1, data[offset]); offset += (uint)data[offset] + 1; - var (dts, valueType) = Tru.Parse(data, offset); + //Console.WriteLine("Parsing constantDef " + name); - offset += dts; + //var (dts, valueType) + var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence); - (dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default); + //Console.WriteLine("Parsing constantDef 2 " + name); + + offset += valueType.Size; + + (var dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default); offset += dts; @@ -48,19 +55,19 @@ public class ConstantDef : MemberDef offset += len; } - return (offset - oOffset, new ConstantDef() + return new ParseResult( new ConstantDef() { Index = index, Name = name, Inherited = inherited, - ValueType = valueType, + ValueType = valueType.Value, Value = value, Annotations = annotations - }); + }, offset - oOffset); } - public byte[] Compose() + public byte[] Compose(EpConnection connection) { var name = DC.ToBytes(Name); @@ -75,7 +82,7 @@ public class ConstantDef : MemberDef .AddUInt8(hdr) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose()) + .AddUInt8Array(ValueType.Compose(connection)) .AddUInt8Array(Codec.Compose(Value, null, null)) .AddInt32(exp.Length) .AddUInt8Array(exp) @@ -89,18 +96,18 @@ public class ConstantDef : MemberDef .AddUInt8(hdr) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose()) + .AddUInt8Array(ValueType.Compose(connection)) .AddUInt8Array(Codec.Compose(Value, null, null)) .ToArray(); } } - public static ConstantDef MakeConstantDef(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null) + public static ConstantDef MakeConstantDef(Warehouse warehouse, Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null) { var annotationAttrs = ci.GetCustomAttributes(true); - var valueType = Tru.FromType(ci.FieldType); + var valueType = Tru.FromType(ci.FieldType, warehouse); if (valueType == null) throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`"); diff --git a/Libraries/Esiur/Data/Types/EventDef.cs b/Libraries/Esiur/Data/Types/EventDef.cs index 5a5f0dc..b85dd21 100644 --- a/Libraries/Esiur/Data/Types/EventDef.cs +++ b/Libraries/Esiur/Data/Types/EventDef.cs @@ -1,5 +1,6 @@ using Esiur.Core; using Esiur.Data; +using Esiur.Protocol; using Esiur.Resource; using System; using System.Collections.Generic; @@ -32,7 +33,7 @@ public class EventDef : MemberDef public Tru ArgumentType { get; set; } - public static (uint, EventDef) Parse(byte[] data, uint offset, byte index, bool inherited) + public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) { var oOffset = offset; @@ -42,9 +43,9 @@ public class EventDef : MemberDef var name = data.GetString(offset + 1, data[offset]); offset += (uint)data[offset] + 1; - var (dts, argType) = Tru.Parse(data, offset); + var argType = await Tru.ParseAsync(data, offset, connection, requestSequence); - offset += dts; + offset += argType.Size; // Annotation ? Map annotations = null; @@ -59,18 +60,18 @@ public class EventDef : MemberDef offset += len; } - return (offset - oOffset, new EventDef() + return new ParseResult(new EventDef() { Index = index, Name = name, Inherited = inherited, - ArgumentType = argType, + ArgumentType = argType.Value, Subscribable = subscribable, Annotations = annotations - }); + }, offset - oOffset); } - public byte[] Compose() + public byte[] Compose(EpConnection connection) { var name = Name.ToBytes(); @@ -87,7 +88,7 @@ public class EventDef : MemberDef .AddUInt8(hdr) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ArgumentType.Compose()) + .AddUInt8Array(ArgumentType.Compose(connection)) .AddInt32(exp.Length) .AddUInt8Array(exp) .ToArray(); @@ -99,12 +100,12 @@ public class EventDef : MemberDef .AddUInt8(hdr) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ArgumentType.Compose()) + .AddUInt8Array(ArgumentType.Compose(connection)) .ToArray(); } - public static EventDef MakeEventDef(Type type, EventInfo ei, byte index, string name, TypeDef schema) + public static EventDef MakeEventDef(Warehouse warehouse, Type type, EventInfo ei, byte index, string name, TypeDef schema) { if (!ei.EventHandlerType.IsGenericType) @@ -116,7 +117,8 @@ public class EventDef : MemberDef var argType = ei.EventHandlerType.GenericTypeArguments[0]; - var evtType = Tru.FromType(argType); + // @TODO: need to check if the type is remote + var evtType = Tru.FromType(argType, warehouse); if (evtType == null) throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`"); diff --git a/Libraries/Esiur/Data/Types/FunctionDef.cs b/Libraries/Esiur/Data/Types/FunctionDef.cs index 251034d..c24576b 100644 --- a/Libraries/Esiur/Data/Types/FunctionDef.cs +++ b/Libraries/Esiur/Data/Types/FunctionDef.cs @@ -40,7 +40,7 @@ public class FunctionDef : MemberDef } - public static (uint, FunctionDef) Parse(byte[] data, uint offset, byte index, bool inherited) + public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) { var oOffset = offset; @@ -51,19 +51,21 @@ public class FunctionDef : MemberDef var name = data.GetString(offset + 1, data[offset]); offset += (uint)data[offset] + 1; - // return type - var (rts, returnType) = Tru.Parse(data, offset); - offset += rts; + //Console.WriteLine("Parsing functionDef " + name); + // return type + var returnType = await Tru.ParseAsync(data, offset, connection, requestSequence); + offset += returnType.Size; + // arguments count var argsCount = data[offset++]; List arguments = new(); for (var a = 0; a < argsCount; a++) { - var (cs, argType) = ArgumentDef.Parse(data, offset, a); - arguments.Add(argType); - offset += cs; + var argType = await ArgumentDef.ParseAsync(data, offset, a, connection, requestSequence); + arguments.Add(argType.Value); + offset += argType.Size; } Map annotations = null; @@ -79,7 +81,7 @@ public class FunctionDef : MemberDef offset += len; } - return (offset - oOffset, new FunctionDef() + return new ParseResult( new FunctionDef() { Index = index, Name = name, @@ -87,11 +89,11 @@ public class FunctionDef : MemberDef IsStatic = isStatic, Inherited = inherited, Annotations = annotations, - ReturnType = returnType, - }); + ReturnType = returnType.Value, + }, offset - oOffset); } - public byte[] Compose() + public byte[] Compose(EpConnection connection) { var name = DC.ToBytes(Name); @@ -99,11 +101,11 @@ public class FunctionDef : MemberDef var bl = new BinaryList() .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ReturnType.Compose()) + .AddUInt8Array(ReturnType.Compose(connection)) .AddUInt8((byte)Arguments.Length); for (var i = 0; i < Arguments.Length; i++) - bl.AddUInt8Array(Arguments[i].Compose()); + bl.AddUInt8Array(Arguments[i].Compose(connection)); if (Annotations != null) @@ -119,7 +121,7 @@ public class FunctionDef : MemberDef } - public static FunctionDef MakeFunctionDef(Type type, MethodInfo mi, byte index, string name, TypeDef schema) + public static FunctionDef MakeFunctionDef(Warehouse warehouse, Type type, MethodInfo mi, byte index, string name, TypeDef schema) { var genericRtType = mi.ReturnType.IsGenericType ? mi.ReturnType.GetGenericTypeDefinition() : null; @@ -128,23 +130,23 @@ public class FunctionDef : MemberDef if (genericRtType == typeof(AsyncReply<>)) { - rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]); + rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse); } else if (genericRtType == typeof(Task<>)) { - rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]); + rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse); } else if (genericRtType == typeof(IEnumerable<>) || genericRtType == typeof(IAsyncEnumerable<>)) { // get export - rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]); + rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0], warehouse); } else { if (mi.ReturnType == typeof(Task)) - rtType = Tru.FromType(null); + rtType = Tru.FromType(null, warehouse); else - rtType = Tru.FromType(mi.ReturnType); + rtType = Tru.FromType(mi.ReturnType, warehouse); } if (rtType == null) @@ -214,7 +216,7 @@ public class FunctionDef : MemberDef var arguments = args.Select(x => { - var argType = Tru.FromType(x.ParameterType); + var argType = Tru.FromType(x.ParameterType, warehouse); if (argType == null) throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`"); diff --git a/Libraries/Esiur/Data/Types/LocalTypeDef.cs b/Libraries/Esiur/Data/Types/LocalTypeDef.cs new file mode 100644 index 0000000..e123a41 --- /dev/null +++ b/Libraries/Esiur/Data/Types/LocalTypeDef.cs @@ -0,0 +1,579 @@ +using Esiur.Core; +using Esiur.Data; +using Esiur.Misc; +using Esiur.Protocol; +using Esiur.Proxy; +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; + +namespace Esiur.Data.Types; + + +public class LocalTypeDef:TypeDef +{ + Type _definedType { get; set; } + Type _parentDefinedType { get; set; } + + + public Type DefinedType => _definedType; + public Type ParentDefinedType => _parentDefinedType; + + public TypeDef ParentTypeDef { get; private set; } + + public static Uuid GetTypeUUID(Type type) + { + var attr = type.GetCustomAttribute(); + if (attr != null) + return attr.Id; + + var tn = Encoding.UTF8.GetBytes(GetTypeName(type)); + var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16); + hash[6] = (byte)((hash[6] & 0xF) | 0x80); + hash[8] = (byte)((hash[8] & 0xF) | 0x80); + + var rt = new Uuid(hash); + return rt; + } + + static Type[] GetDistributedTypes(Type type) + { + if (type.IsArray) + return GetDistributedTypes(type.GetElementType()); + else if (type.IsEnum) + return new Type[] { type }; + else if (Codec.ImplementsInterface(type, typeof(IRecord)) + || Codec.ImplementsInterface(type, typeof(IResource))) + { + return new Type[] { type }; + } + else if (type.IsGenericType) + { + var genericType = type.GetGenericTypeDefinition(); + var genericTypeArgs = type.GetGenericArguments(); + + if (genericType == typeof(List<>) + || genericType == typeof(PropertyContext<>) + || genericType == typeof(AsyncReply<>) + || genericType == typeof(ResourceLink<>)) + { + return GetDistributedTypes(genericTypeArgs[0]); + } + else if (genericType == typeof(Tuple<>) + || genericType == typeof(Map<,>)) + { + var rt = new List(); + for (var i = 0; i < genericTypeArgs.Length; i++) + { + var depTypes = GetDistributedTypes(genericTypeArgs[i]); + foreach (var depType in depTypes) + if (!rt.Contains(depType)) + rt.Add(depType); + } + + return rt.ToArray(); + } + } + + + return new Type[0]; + } + + + public static TypeDef[] GetDependencies(LocalTypeDef typeDef, Warehouse warehouse) + { + + var list = new List(); + + // Add self + list.Add(typeDef); + + + Action getDependenciesFunc = null; + + getDependenciesFunc = (LocalTypeDef td) => + { + if (td.DefinedType == null) + return; + + // Add parents + var parentType = td.ParentDefinedType; + + // Get parents + while (parentType != null) + { + var parentTypeDef = warehouse.GetLocalTypeDefByType(parentType); + if (parentTypeDef != null) + { + if (!list.Contains(parentTypeDef)) + { + list.Add(parentTypeDef); + + if (parentTypeDef is LocalTypeDef pltd) + { + parentType = pltd.DefinedType; + } + } + } + } + + // functions + foreach (var f in td._functions) + { + var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType); + + foreach (var functionReturnType in functionReturnTypes) + { + var functionReturnTypeDef = warehouse.GetLocalTypeDefByType(functionReturnType); + if (functionReturnTypeDef != null) + { + if (!list.Contains(functionReturnTypeDef)) + { + list.Add(functionReturnTypeDef); + if (functionReturnTypeDef is LocalTypeDef frtd) + getDependenciesFunc(frtd); + } + } + } + + var args = f.MethodInfo.GetParameters(); + + for (var i = 0; i < args.Length - 1; i++) + { + var fpTypes = GetDistributedTypes(args[i].ParameterType); + + foreach (var fpType in fpTypes) + { + var fpt = warehouse.GetLocalTypeDefByType(fpType); + if (fpt != null) + { + if (!list.Contains(fpt)) + { + list.Add(fpt); + if (fpt is LocalTypeDef ltd) + getDependenciesFunc(ltd); + } + } + } + } + + // skip EpConnection argument + if (args.Length > 0) + { + var last = args.Last(); + if (last.ParameterType != typeof(EpConnection)) + { + + var fpTypes = GetDistributedTypes(last.ParameterType); + + foreach (var fpType in fpTypes) + { + var fpt = warehouse.GetLocalTypeDefByType(fpType); + if (fpt != null) + { + if (!list.Contains(fpt)) + { + list.Add(fpt); + if (fpt is LocalTypeDef ltd) + getDependenciesFunc(ltd); + } + } + } + } + } + + } + + // properties + foreach (var p in td._properties) + { + var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType); + + foreach (var propertyType in propertyTypes) + { + var propertyTypeDef = warehouse.GetLocalTypeDefByType(propertyType); + if (propertyTypeDef != null) + { + if (!list.Contains(propertyTypeDef)) + { + list.Add(propertyTypeDef); + if (propertyTypeDef is LocalTypeDef ltd) + getDependenciesFunc(ltd); + } + } + } + } + + // events + foreach (var e in td._events) + { + var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]); + + foreach (var eventType in eventTypes) + { + var eventTypeDef = warehouse.GetLocalTypeDefByType(eventType); + + if (eventTypeDef != null) + { + if (!list.Contains(eventTypeDef)) + { + list.Add(eventTypeDef); + if (eventTypeDef is LocalTypeDef ltd) + getDependenciesFunc(ltd); + } + } + } + } + }; + + getDependenciesFunc(typeDef); + return list.Distinct().ToArray(); + } + + + public static string GetTypeName(Type type, char separator = '.') + { + + if (type.IsGenericType) + { + var index = type.Name.IndexOf("`"); + var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of"; + foreach (var t in type.GenericTypeArguments) + name += GetTypeName(t, '_'); + + return name; + } + else + return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}"; + } + + public LocalTypeDef(Type type, Warehouse warehouse) + { + if (Codec.ImplementsInterface(type, typeof(IResource))) + _typeDefKind = TypeDefKind.Resource; + else if (Codec.ImplementsInterface(type, typeof(IRecord))) + _typeDefKind = TypeDefKind.Record; + else if (type.IsEnum) + _typeDefKind = TypeDefKind.Enum; + else + throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource."); + + //IsWrapper = Codec.InheritsClass(type, typeof(EpResource)); + + type = ResourceProxy.GetBaseType(type); + + _definedType = type; + + _typeName = GetTypeName(type); + + + warehouse.TryRegisterLocalTypeDef(this); + + var hierarchy = GetHierarchy(type); + + if (hierarchy.ContainsKey(MemberTypes.Field)) + { + foreach (var cd in hierarchy[MemberTypes.Field]) + { + _constants.Add(ConstantDef.MakeConstantDef + (warehouse, type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this)); + } + } + + if (hierarchy.ContainsKey(MemberTypes.Property)) + { + foreach (var pd in hierarchy[MemberTypes.Property]) + { + _properties.Add(PropertyDef.MakePropertyDef + ( warehouse, type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this)); + } + } + + if (_typeDefKind == TypeDefKind.Resource) + { + if (hierarchy.ContainsKey(MemberTypes.Method)) + { + foreach (var fd in hierarchy[MemberTypes.Method]) + { + _functions.Add(FunctionDef.MakeFunctionDef + (warehouse, type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this)); + } + } + + if (hierarchy.ContainsKey(MemberTypes.Event)) + { + foreach (var ed in hierarchy[MemberTypes.Event]) + { + _events.Add(EventDef.MakeEventDef + ( warehouse, type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this)); + } + } + + } + + // add attributes + var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.GetCustomAttribute() != null); + + foreach (var attr in attrs) + { + var attrAttr = attr.GetCustomAttribute(); + + _attributes.Add(AttributeDef + .MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this)); + } + + + + // find the first parent type that implements IResource + var hasParent = HasParent(type); + var classAnnotations = type.GetCustomAttributes(false); + var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0); + + + if (hasParent) + { + // find the first parent type that implements IResource + _parentDefinedType = ResourceProxy.GetBaseType(type.BaseType); + + var parentTypeDef = warehouse.GetLocalTypeDefByType(_parentDefinedType); + + if (parentTypeDef == null) + throw new Exception("Can't find parent TypeDef."); + + ParentTypeDef = parentTypeDef; + } + + if (hasClassAnnotation) + { + Annotations = new Map(); + + foreach (var ann in classAnnotations) + Annotations.Add(ann.Key, ann.Value); + } + + } + + public override byte[] Compose(EpConnection connection) + { + // bake it binarily + var b = new BinaryList(); + + // find the first parent type that implements IResource + + + var hasParent = ParentTypeDef != null; + var hasClassAnnotation = Annotations != null && Annotations.Count() > 0; + + var typeNameBytes = DC.ToBytes(_typeName); + + b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)_typeDefKind)) + .AddUInt64(_typeId) + .AddUInt8((byte)typeNameBytes.Length) + .AddUInt8Array(typeNameBytes); + + if (hasParent) + { + b.AddUInt64(ParentTypeDef.Id); + } + + if (hasClassAnnotation) + { + + //foreach (var ann in Annotations) + // Annotations.Add(ann.Key, ann.Value); + + var classAnnotationBytes = Codec.Compose(Annotations, null, null); + + b.AddUInt8Array(classAnnotationBytes); + + } + + b.AddInt32(_version) + .AddUInt16((ushort)(_functions.Count + _properties.Count + _events.Count + _constants.Count)); + + foreach (var ft in _functions) + b.AddUInt8Array(ft.Compose(connection )); + foreach (var pt in _properties) + b.AddUInt8Array(pt.Compose(connection)); + foreach (var et in _events) + b.AddUInt8Array(et.Compose(connection)); + foreach (var ct in _constants) + b.AddUInt8Array(ct.Compose(connection)); + + return b.ToArray(); + + } + + public static bool HasParent(Type type) + { + var parent = type.BaseType; + + + while (parent != null) + { + if (parent == typeof(Esiur.Resource.Resource) + || parent == typeof(Record) + || parent == typeof(EntryPoint)) + return false; + + if (parent.GetInterfaces().Contains(typeof(IResource)) + || parent.GetInterfaces().Contains(typeof(IRecord))) + return true; + + parent = parent.BaseType; + } + + return false; + } + + + + public static Dictionary> GetHierarchy(Type type) + { + var members = new List(); + + var order = 0; + + while (type != null) + { + var classIsPublic = type.IsEnum || (type.GetCustomAttribute() != null); + + if (classIsPublic) + { + // get public instance members only. + var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance + | BindingFlags.DeclaredOnly | BindingFlags.Static) + .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field + || x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method) + .Where(x => !(x is FieldInfo c && !c.IsStatic)) + .Where(x => x.GetCustomAttribute() == null) + .Where(x => x.Name != "Instance") + .Where(x => x.Name != "Trigger") + .Where(x => !(x is MethodInfo m && m.IsSpecialName)) + .Where(x => !(x is EventInfo e && + !(e.EventHandlerType.IsGenericType && + (e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>) + || e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>)) + ) + )) + .Select(x => new MemberData( + info: x, + order: order + )) + .OrderBy(x => x.Name); + + members.AddRange(mis.ToArray()); + + } + else + { + // allow private and public members that are marked with [Export] attribute. + var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + | BindingFlags.DeclaredOnly | BindingFlags.Static) + .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field + || x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method) + .Where(x => !(x is FieldInfo c && !c.IsStatic)) + .Where(x => x.GetCustomAttribute() != null) + .Where(x => !(x is MethodInfo m && m.IsSpecialName)) + .Select(x => new MemberData( + info: x, + order: order + )) + .OrderBy(x => x.Name); + + members.AddRange(mis.ToArray()); + + } + + type = type.BaseType; + + if (type == null + || type == typeof(Esiur.Resource.Resource) + || type == typeof(Record) + || type == typeof(EntryPoint)) + break; + + if (type.GetInterfaces().Contains(typeof(IResource)) + || type.GetInterfaces().Contains(typeof(IRecord))) + { + order++; + continue; + } + + break; + } + + // round 2: check for duplicates + for (var i = 0; i < members.Count; i++) + { + var mi = members[i]; + for (var j = i + 1; j < members.Count; j++) + { + var pi = members[j]; + if (pi.Info.MemberType != mi.Info.MemberType) + continue; + + //if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order) + // throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'."); + + if (pi.Name == mi.Name) + { + if (pi.Order == mi.Order) + throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'."); + else + { + // @TODO: check for return type and parameters they must match + if (pi.Info.Name != mi.Info.Name) + throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'."); + } + + mi.Parent = pi; + pi.Child = mi; + } + + } + } + + + // assign indexes + var groups = members.Where(x => x.Parent == null) + .OrderBy(x => x.Name).OrderByDescending(x => x.Order) + .GroupBy(x => x.Info.MemberType); + + foreach (var group in groups) + { + byte index = 0; + foreach (var mi in group) + { + //if (mi.Parent == null) + mi.Index = index++; + } + } + + var rt = groups.ToDictionary(g => g.Key, g => g.ToList()); + + return rt; + } + + + //public Map CastProperties(Map properties) + //{ + // var rt = new Map(); + // foreach (var kv in properties) + // { + // var pt = GetPropertyDefByName(kv.Key); + // if (pt == null) continue; + // rt.Add(pt.Index, kv.Value); + // } + + // return rt; + //} +} + diff --git a/Libraries/Esiur/Data/Types/PropertyDef.cs b/Libraries/Esiur/Data/Types/PropertyDef.cs index aad4796..cfd7cfa 100644 --- a/Libraries/Esiur/Data/Types/PropertyDef.cs +++ b/Libraries/Esiur/Data/Types/PropertyDef.cs @@ -1,4 +1,5 @@ -using Esiur.Data; +using Esiur.Core; +using Esiur.Data; using Esiur.Protocol; using Esiur.Resource; using System; @@ -80,7 +81,7 @@ public class PropertyDef : MemberDef return $"{Name}: {ValueType}"; } - public static (uint, PropertyDef) Parse(byte[] data, uint offset, byte index, bool inherited) + public static async AsyncReply> ParseAsync(byte[] data, uint offset, byte index, bool inherited, EpConnection connection, ulong[] requestSequence) { var oOffset = offset; @@ -91,11 +92,13 @@ public class PropertyDef : MemberDef var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3); var name = data.GetString(offset + 1, data[offset]); + //Console.WriteLine("Parsing propdef " + name); + offset += (uint)data[offset] + 1; - var (dts, valueType) = Tru.Parse(data, offset); + var valueType = await Tru.ParseAsync(data, offset, connection, requestSequence); - offset += dts; + offset += valueType.Size; Map annotations = null; @@ -110,20 +113,20 @@ public class PropertyDef : MemberDef offset += len; } - return (offset - oOffset, new PropertyDef() + return new ParseResult(new PropertyDef() { Index = index, Name = name, Inherited = inherited, Permission = permission, HasHistory = hasHistory, - ValueType = valueType, + ValueType = valueType.Value, Annotations = annotations - }); + }, offset - oOffset); } - public byte[] Compose() + public byte[] Compose(EpConnection connection) { var name = DC.ToBytes(Name); @@ -178,7 +181,7 @@ public class PropertyDef : MemberDef .AddUInt8((byte)(0x28 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose()) + .AddUInt8Array(ValueType.Compose(connection)) .AddUInt8Array(rexp) .ToArray(); } @@ -188,20 +191,21 @@ public class PropertyDef : MemberDef .AddUInt8((byte)(0x20 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) - .AddUInt8Array(ValueType.Compose()) + .AddUInt8Array(ValueType.Compose(connection)) .ToArray(); } } - public static PropertyDef MakePropertyDef(Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema) + public static PropertyDef MakePropertyDef(Warehouse warehouse, Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema) { var genericPropType = pi.PropertyType.IsGenericType ? pi.PropertyType.GetGenericTypeDefinition() : null; + // @TODO: need to check if the type is remote var propType = genericPropType == typeof(PropertyContext<>) ? - Tru.FromType(pi.PropertyType.GetGenericArguments()[0]) : - Tru.FromType(pi.PropertyType); + Tru.FromType(pi.PropertyType.GetGenericArguments()[0], warehouse) : + Tru.FromType(pi.PropertyType, warehouse); if (propType == null) throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`"); diff --git a/Libraries/Esiur/Data/Types/RemoteTypeDef.cs b/Libraries/Esiur/Data/Types/RemoteTypeDef.cs new file mode 100644 index 0000000..6a00110 --- /dev/null +++ b/Libraries/Esiur/Data/Types/RemoteTypeDef.cs @@ -0,0 +1,130 @@ +using Esiur.Core; +using Esiur.Data; +using Esiur.Misc; +using Esiur.Protocol; +using Esiur.Proxy; +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; + +namespace Esiur.Data.Types; + + +public class RemoteTypeDef:TypeDef +{ + Type _proxyType { get; set; } + + public Type ProxyType => _proxyType; + + string _domain; + + public string Domain => _domain; + + + public uint _localTypeDefId; + + public uint LocalTypeDefId + { + get => _localTypeDefId; + internal set => _localTypeDefId = value; + } + + public static AsyncReply Parse(RemoteTypeDef od, string domain, byte[] data, EpConnection connection, ulong[] requestSequence) + { + return Parse(od, domain, data, 0, (uint)data.Length, connection, requestSequence); + } + + public static async AsyncReply Parse(RemoteTypeDef od, string domain, byte[] data, uint offset, uint contentLength, EpConnection connection, ulong[] requestSequence) + { + uint ends = offset + contentLength; + + uint oOffset = offset; + + // start parsing... + + + od._domain = domain; + + od._content = data.Clip(offset, contentLength); + + var hasParent = (data[offset] & 0x80) > 0; + var hasClassAnnotation = (data[offset] & 0x40) > 0; + + od._typeDefKind = (TypeDefKind)(data[offset++] & 0xF); + + od._typeId = data.GetUInt64(offset, Endian.Little); + offset += 8; + od._typeName = data.GetString(offset + 1, data[offset]); + offset += (uint)data[offset] + 1; + + + if (hasParent) + { + od._parentTypeId = data.GetUInt64(offset, Endian.Little); + offset += 8; + } + + if (hasClassAnnotation) + { + var (len, anns) = Codec.ParseSync(data, offset, null); + + if (anns is Map annotations) + od.Annotations = annotations; + + offset += len; + } + + od._version = data.GetInt32(offset, Endian.Little); + offset += 4; + + ushort methodsCount = data.GetUInt16(offset, Endian.Little); + offset += 2; + + byte functionIndex = 0; + byte propertyIndex = 0; + byte eventIndex = 0; + byte constantIndex = 0; + + for (int i = 0; i < methodsCount; i++) + { + var inherited = (data[offset] & 0x80) > 0; + var type = (data[offset] >> 5) & 0x3; + + if (type == 0) // function + { + var ft = await FunctionDef.ParseAsync(data, offset, functionIndex++, inherited, connection, requestSequence); + offset += ft.Size; + od._functions.Add(ft.Value); + } + else if (type == 1) // property + { + var pt = await PropertyDef.ParseAsync(data, offset, propertyIndex++, inherited, connection, requestSequence); + offset += pt.Size; + od._properties.Add(pt.Value); + + } + else if (type == 2) // Event + { + var et = await EventDef.ParseAsync(data, offset, eventIndex++, inherited, connection, requestSequence); + offset += et.Size; + od._events.Add(et.Value); + } + // constant + else if (type == 3) + { + var ct = await ConstantDef.ParseAsync(data, offset, constantIndex++, inherited, connection, requestSequence); + offset += ct.Size; + od._constants.Add(ct.Value); + } + } + + return od; + } +} + diff --git a/Libraries/Esiur/Data/Types/TypeDef.cs b/Libraries/Esiur/Data/Types/TypeDef.cs index f8eb566..cd72d03 100644 --- a/Libraries/Esiur/Data/Types/TypeDef.cs +++ b/Libraries/Esiur/Data/Types/TypeDef.cs @@ -18,46 +18,51 @@ namespace Esiur.Data.Types; public class TypeDef { - protected Uuid typeId; - protected Uuid? parentId; + protected ulong _typeId; + protected ulong? _parentTypeId; public Map Annotations { get; set; } - string typeName; - List functions = new List(); - List events = new List(); - List properties = new List(); - List attributes = new List(); - List constants = new(); - int version; - TypeDefKind typeDefKind; + protected string _typeName; + protected List _functions = new List(); + protected List _events = new List(); + protected List _properties = new List(); + protected List _attributes = new List(); + protected List _constants = new(); + protected int _version; + protected TypeDefKind _typeDefKind; + //Type _definedType { get; set; } + //Type _proxyType { get; set; } + //Type _parentDefinedType { get; set; } + + //bool _isLocal; + + //public Type DefinedType => _definedType; + //public Type ParentDefinedType => _parentDefinedType; + + //public Type ProxyType => _proxyType; public override string ToString() { - return typeName; + return _typeName; } + + protected byte[] _content; - protected byte[] content; + public ulong? ParentTypeId => _parentTypeId; - public Uuid? ParentId => parentId; + //public byte[] Content => _content; - public byte[] Content - { - get { return content; } - } - - public TypeDefKind Kind => typeDefKind; + public TypeDefKind Kind => _typeDefKind; - public Type DefinedType { get; set; } - public Type ParentDefinedType { get; set; } public EventDef GetEventDefByName(string eventName) { - foreach (var i in events) + foreach (var i in _events) if (i.Name == eventName) return i; return null; @@ -65,7 +70,7 @@ public class TypeDef public EventDef GetEventDefByIndex(byte index) { - foreach (var i in events) + foreach (var i in _events) if (i.Index == index) return i; return null; @@ -73,14 +78,14 @@ public class TypeDef public FunctionDef GetFunctionDefByName(string functionName) { - foreach (var i in functions) + foreach (var i in _functions) if (i.Name == functionName) return i; return null; } public FunctionDef GetFunctionDefByIndex(byte index) { - foreach (var i in functions) + foreach (var i in _functions) if (i.Index == index) return i; return null; @@ -88,7 +93,7 @@ public class TypeDef public PropertyDef GetPropertyDefByIndex(byte index) { - foreach (var i in properties) + foreach (var i in _properties) if (i.Index == index) return i; return null; @@ -96,7 +101,7 @@ public class TypeDef public PropertyDef GetPropertyDefByName(string propertyName) { - foreach (var i in properties) + foreach (var i in _properties) if (i.Name == propertyName) return i; return null; @@ -104,645 +109,47 @@ public class TypeDef public AttributeDef GetAttributeDef(string attributeName) { - foreach (var i in attributes) + foreach (var i in _attributes) if (i.Name == attributeName) return i; return null; } - public Uuid Id + public ulong Id { - get { return typeId; } + get { return _typeId; } + internal set { _typeId = value; } } + + public string Name { - get { return typeName; } + get { return _typeName; } } public FunctionDef[] Functions { - get { return functions.ToArray(); } + get { return _functions.ToArray(); } } public EventDef[] Events { - get { return events.ToArray(); } + get { return _events.ToArray(); } } public PropertyDef[] Properties { - get { return properties.ToArray(); } + get { return _properties.ToArray(); } } - public ConstantDef[] Constants => constants.ToArray(); + public ConstantDef[] Constants => _constants.ToArray(); public TypeDef() { } - public static Uuid GetTypeUUID(Type type) - { - var attr = type.GetCustomAttribute(); - if (attr != null) - return attr.Id; - - var tn = Encoding.UTF8.GetBytes(GetTypeName(type)); - var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16); - hash[6] = (byte)((hash[6] & 0xF) | 0x80); - hash[8] = (byte)((hash[8] & 0xF) | 0x80); - - var rt = new Uuid(hash); - return rt; - } - - static Type[] GetDistributedTypes(Type type) - { - if (type.IsArray) - return GetDistributedTypes(type.GetElementType()); - else if (type.IsEnum) - return new Type[] { type }; - else if (Codec.ImplementsInterface(type, typeof(IRecord)) - || Codec.ImplementsInterface(type, typeof(IResource))) - { - return new Type[] { type }; - } - else if (type.IsGenericType) - { - var genericType = type.GetGenericTypeDefinition(); - var genericTypeArgs = type.GetGenericArguments(); - - if (genericType == typeof(List<>) - || genericType == typeof(PropertyContext<>) - || genericType == typeof(AsyncReply<>) - || genericType == typeof(ResourceLink<>)) - { - return GetDistributedTypes(genericTypeArgs[0]); - } - else if (genericType == typeof(Tuple<>) - || genericType == typeof(Map<,>)) - { - var rt = new List(); - for (var i = 0; i < genericTypeArgs.Length; i++) - { - var depTypes = GetDistributedTypes(genericTypeArgs[i]); - foreach (var depType in depTypes) - if (!rt.Contains(depType)) - rt.Add(depType); - } - - return rt.ToArray(); - } - } - - - return new Type[0]; - } - - - public static TypeDef[] GetDependencies(TypeDef schema, Warehouse warehouse) - { - - var list = new List(); - - // Add self - list.Add(schema); - - - Action> getDependenciesFunc = null; - - getDependenciesFunc = (TypeDef sch, List bag) => - { - if (schema.DefinedType == null) - return; - - // Add parents - var parentType = sch.ParentDefinedType; - - // Get parents - while (parentType != null) - { - var parentTypeDef = warehouse.GetTypeDefByType(parentType); - if (parentTypeDef != null) - { - list.Add(parentTypeDef); - parentType = parentTypeDef.ParentDefinedType; - } - } - - // functions - foreach (var f in sch.functions) - { - var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType); - - foreach (var functionReturnType in functionReturnTypes) - { - var functionReturnTypeDef = warehouse.GetTypeDefByType(functionReturnType); - if (functionReturnTypeDef != null) - { - if (!bag.Contains(functionReturnTypeDef)) - { - list.Add(functionReturnTypeDef); - getDependenciesFunc(functionReturnTypeDef, bag); - } - } - } - - var args = f.MethodInfo.GetParameters(); - - for (var i = 0; i < args.Length - 1; i++) - { - var fpTypes = GetDistributedTypes(args[i].ParameterType); - - foreach (var fpType in fpTypes) - { - var fpt = warehouse.GetTypeDefByType(fpType); - if (fpt != null) - { - if (!bag.Contains(fpt)) - { - bag.Add(fpt); - getDependenciesFunc(fpt, bag); - } - } - } - } - - // skip EpConnection argument - if (args.Length > 0) - { - var last = args.Last(); - if (last.ParameterType != typeof(EpConnection)) - { - - var fpTypes = GetDistributedTypes(last.ParameterType); - - foreach (var fpType in fpTypes) - { - var fpt = warehouse.GetTypeDefByType(fpType); - if (fpt != null) - { - if (!bag.Contains(fpt)) - { - bag.Add(fpt); - getDependenciesFunc(fpt, bag); - } - } - } - } - } - - } - - // properties - foreach (var p in sch.properties) - { - var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType); - - foreach (var propertyType in propertyTypes) - { - var propertyTypeDef = warehouse.GetTypeDefByType(propertyType); - if (propertyTypeDef != null) - { - if (!bag.Contains(propertyTypeDef)) - { - bag.Add(propertyTypeDef); - getDependenciesFunc(propertyTypeDef, bag); - } - } - } - } - - // events - foreach (var e in sch.events) - { - var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]); - - foreach (var eventType in eventTypes) - { - var eventTypeDef = warehouse.GetTypeDefByType(eventType); - - if (eventTypeDef != null) - { - if (!bag.Contains(eventTypeDef)) - { - bag.Add(eventTypeDef); - getDependenciesFunc(eventTypeDef, bag); - } - } - } - } - }; - - getDependenciesFunc(schema, list); - return list.Distinct().ToArray(); - } - - - public static string GetTypeName(Type type, char separator = '.') - { - - if (type.IsGenericType) - { - var index = type.Name.IndexOf("`"); - var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of"; - foreach (var t in type.GenericTypeArguments) - name += GetTypeName(t, '_'); - - return name; - } - else - return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}"; - } - - - - - - - public bool IsWrapper { get; private set; } - - public TypeDef(Type type, Warehouse warehouse = null) - { - if (Codec.ImplementsInterface(type, typeof(IResource))) - typeDefKind = TypeDefKind.Resource; - else if (Codec.ImplementsInterface(type, typeof(IRecord))) - typeDefKind = TypeDefKind.Record; - else if (type.IsEnum) - typeDefKind = TypeDefKind.Enum; - else - throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource."); - - IsWrapper = Codec.InheritsClass(type, typeof(EpResource)); - - type = ResourceProxy.GetBaseType(type); - - DefinedType = type; - - typeName = GetTypeName(type); - - // set guid - typeId = GetTypeUUID(type); - - if (warehouse != null) - warehouse.RegisterTypeDef(this); - - var hierarchy = GetHierarchy(type); - - if (hierarchy.ContainsKey(MemberTypes.Field)) - { - foreach (var cd in hierarchy[MemberTypes.Field]) - { - constants.Add(ConstantDef.MakeConstantDef - (type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this)); - } - } - - if (hierarchy.ContainsKey(MemberTypes.Property)) - { - foreach (var pd in hierarchy[MemberTypes.Property]) - { - properties.Add(PropertyDef.MakePropertyDef - (type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this)); - } - } - - if (typeDefKind == TypeDefKind.Resource) - { - if (hierarchy.ContainsKey(MemberTypes.Method)) - { - foreach (var fd in hierarchy[MemberTypes.Method]) - { - functions.Add(FunctionDef.MakeFunctionDef - (type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this)); - } - } - - if (hierarchy.ContainsKey(MemberTypes.Event)) - { - foreach (var ed in hierarchy[MemberTypes.Event]) - { - events.Add(EventDef.MakeEventDef - (type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this)); - } - } - - } - - // add attributes - var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(x => x.GetCustomAttribute() != null); - - foreach (var attr in attrs) - { - var attrAttr = attr.GetCustomAttribute(); - - attributes.Add(AttributeDef - .MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this)); - } - - - // bake it binarily - var b = new BinaryList(); - - // find the first parent type that implements IResource - - - var hasParent = HasParent(type); - var classAnnotations = type.GetCustomAttributes(false); - - - var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0); - - var typeNameBytes = DC.ToBytes(typeName); - - b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)typeDefKind)) - .AddUUID(typeId) - .AddUInt8((byte)typeNameBytes.Length) - .AddUInt8Array(typeNameBytes); - - if (hasParent) - { - // find the first parent type that implements IResource - ParentDefinedType = ResourceProxy.GetBaseType(type.BaseType); - var parentId = GetTypeUUID(ParentDefinedType); - b.AddUUID(parentId); - } - - if (hasClassAnnotation) - { - Annotations = new Map(); - - foreach (var ann in classAnnotations) - Annotations.Add(ann.Key, ann.Value); - - var classAnnotationBytes = Codec.Compose (Annotations, null, null); - - b.AddUInt8Array(classAnnotationBytes); - - } - - b.AddInt32(version) - .AddUInt16((ushort)(functions.Count + properties.Count + events.Count + constants.Count)); - - foreach (var ft in functions) - b.AddUInt8Array(ft.Compose()); - foreach (var pt in properties) - b.AddUInt8Array(pt.Compose()); - foreach (var et in events) - b.AddUInt8Array(et.Compose()); - foreach (var ct in constants) - b.AddUInt8Array(ct.Compose()); - - content = b.ToArray(); - - } - - public static bool HasParent(Type type) - { - var parent = type.BaseType; - - - while (parent != null) - { - if (parent == typeof(Esiur.Resource.Resource) - || parent == typeof(Record) - || parent == typeof(EntryPoint)) - return false; - - if (parent.GetInterfaces().Contains(typeof(IResource)) - || parent.GetInterfaces().Contains(typeof(IRecord))) - return true; - - parent = parent.BaseType; - } - - return false; - } - - - - public static Dictionary> GetHierarchy(Type type) - { - var members = new List(); - - var order = 0; - - while (type != null) - { - var classIsPublic = type.IsEnum || (type.GetCustomAttribute() != null); - - if (classIsPublic) - { - // get public instance members only. - var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance - | BindingFlags.DeclaredOnly | BindingFlags.Static) - .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field - || x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method) - .Where(x => !(x is FieldInfo c && !c.IsStatic)) - .Where(x => x.GetCustomAttribute() == null) - .Where(x => x.Name != "Instance") - .Where(x => x.Name != "Trigger") - .Where(x => !(x is MethodInfo m && m.IsSpecialName)) - .Where(x => !(x is EventInfo e && - !(e.EventHandlerType.IsGenericType && - (e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>) - || e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>)) - ) - )) - .Select(x => new MemberData( - info: x, - order: order - )) - .OrderBy(x => x.Name); - - members.AddRange(mis.ToArray()); - - } - else - { - // allow private and public members that are marked with [Export] attribute. - var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance - | BindingFlags.DeclaredOnly | BindingFlags.Static) - .Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field - || x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method) - .Where(x => !(x is FieldInfo c && !c.IsStatic)) - .Where(x => x.GetCustomAttribute() != null) - .Where(x => !(x is MethodInfo m && m.IsSpecialName)) - .Select(x => new MemberData( - info: x, - order: order - )) - .OrderBy(x => x.Name); - - members.AddRange(mis.ToArray()); - - } - - type = type.BaseType; - - if (type == null - || type == typeof(Esiur.Resource.Resource) - || type == typeof(Record) - || type == typeof(EntryPoint)) - break; - - if (type.GetInterfaces().Contains(typeof(IResource)) - || type.GetInterfaces().Contains(typeof(IRecord))) - { - order++; - continue; - } - - break; - } - - // round 2: check for duplicates - for (var i = 0; i < members.Count; i++) - { - var mi = members[i]; - for (var j = i + 1; j < members.Count; j++) - { - var pi = members[j]; - if (pi.Info.MemberType != mi.Info.MemberType) - continue; - - //if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order) - // throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'."); - - if (pi.Name == mi.Name) - { - if (pi.Order == mi.Order) - throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'."); - else - { - // @TODO: check for return type and parameters they must match - if (pi.Info.Name != mi.Info.Name) - throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'."); - } - - mi.Parent = pi; - pi.Child = mi; - } - - } - } - - - // assign indexes - var groups = members.Where(x => x.Parent == null) - .OrderBy(x => x.Name).OrderByDescending(x => x.Order) - .GroupBy(x => x.Info.MemberType); - - foreach (var group in groups) - { - byte index = 0; - foreach (var mi in group) - { - //if (mi.Parent == null) - mi.Index = index++; - } - } - - var rt = groups.ToDictionary(g => g.Key, g => g.ToList()); - - return rt; - } - - - public static TypeDef Parse(byte[] data) - { - return Parse(data, 0, (uint)data.Length); - } - - - public static TypeDef Parse(byte[] data, uint offset, uint contentLength) - { - - uint ends = offset + contentLength; - - uint oOffset = offset; - - // start parsing... - - var od = new TypeDef(); - od.content = data.Clip(offset, contentLength); - - var hasParent = (data[offset] & 0x80) > 0; - var hasClassAnnotation = (data[offset] & 0x40) > 0; - - od.typeDefKind = (TypeDefKind)(data[offset++] & 0xF); - - od.typeId = data.GetUUID(offset); - offset += 16; - od.typeName = data.GetString(offset + 1, data[offset]); - offset += (uint)data[offset] + 1; - - - if (hasParent) - { - od.parentId = data.GetUUID(offset); - offset += 16; - } - - if (hasClassAnnotation) - { - var (len, anns) = Codec.ParseSync(data, offset, null); - - if (anns is Map annotations) - od.Annotations = annotations; - - offset += len; - } - - od.version = data.GetInt32(offset, Endian.Little); - offset += 4; - - ushort methodsCount = data.GetUInt16(offset, Endian.Little); - offset += 2; - - byte functionIndex = 0; - byte propertyIndex = 0; - byte eventIndex = 0; - byte constantIndex = 0; - - for (int i = 0; i < methodsCount; i++) - { - var inherited = (data[offset] & 0x80) > 0; - var type = (data[offset] >> 5) & 0x3; - - if (type == 0) // function - { - var (len, ft) = FunctionDef.Parse(data, offset, functionIndex++, inherited); - offset += len; - od.functions.Add(ft); - } - else if (type == 1) // property - { - var (len, pt) = PropertyDef.Parse(data, offset, propertyIndex++, inherited); - offset += len; - od.properties.Add(pt); - - } - else if (type == 2) // Event - { - var (len, et) = EventDef.Parse(data, offset, eventIndex++, inherited); - offset += len; - od.events.Add(et); - } - // constant - else if (type == 3) - { - var (len, ct) = ConstantDef.Parse(data, offset, constantIndex++, inherited); - offset += len; - od.constants.Add(ct); - } - } - - return od; - } - public Map CastProperties(Map properties) { var rt = new Map(); @@ -755,5 +162,12 @@ public class TypeDef return rt; } + + public virtual byte[] Compose(EpConnection connection) + { + return null; + } + + } diff --git a/Libraries/Esiur/Esiur.csproj b/Libraries/Esiur/Esiur.csproj index 41d23b6..cdefb72 100644 --- a/Libraries/Esiur/Esiur.csproj +++ b/Libraries/Esiur/Esiur.csproj @@ -59,33 +59,37 @@ + + + + + - - - + + + + + - - - diff --git a/Libraries/Esiur/Net/Packets/EpAuthPacket.cs b/Libraries/Esiur/Net/Packets/EpAuthPacket.cs index 36ed262..c96df23 100644 --- a/Libraries/Esiur/Net/Packets/EpAuthPacket.cs +++ b/Libraries/Esiur/Net/Packets/EpAuthPacket.cs @@ -23,6 +23,7 @@ SOFTWARE. */ using Esiur.Data; +using Esiur.Resource; using Esiur.Security.Authority; using Esiur.Security.Cryptography; using System; @@ -38,29 +39,39 @@ namespace Esiur.Net.Packets; public class EpAuthPacket : Packet { + + Warehouse _warehouse; + + public EpAuthPacket(Warehouse warehouse) + { + _warehouse = warehouse; + } + public EpAuthPacketCommand Command { get; set; } - public EpAuthPacketAction Action - { - get; - set; - } + public EpAuthPacketMethod Method { get; set; } - public EpAuthPacketEvent Event - { - get; - set; - } + //public EpAuthPacketAction Action + //{ + // get; + // set; + //} - public EpAuthPacketAcknowledgement Acknowledgement - { - get; - set; - } + //public EpAuthPacketEvent Event + //{ + // get; + // set; + //} + + //public EpAuthPacketAcknowledgement Acknowledgement + //{ + // get; + // set; + //} public AuthenticationMode AuthMode @@ -101,7 +112,7 @@ public class EpAuthPacket : Packet } - public ParsedTdu? Tdu + public PlainTdu? Tdu { get; set; @@ -114,8 +125,6 @@ public class EpAuthPacket : Packet set; } - - private uint dataLengthNeeded; bool NotEnough(uint offset, uint ends, uint needed) @@ -131,7 +140,7 @@ public class EpAuthPacket : Packet public override string ToString() { - return Command.ToString() + " " + Action.ToString(); + return Command.ToString() + " " + Method.ToString(); } public override long Parse(byte[] data, uint offset, uint ends) @@ -146,23 +155,23 @@ public class EpAuthPacket : Packet if (Command == EpAuthPacketCommand.Initialize) { - AuthMode = (AuthenticationMode)(data[offset] >> 3 & 0x7); + AuthMode = (AuthenticationMode)(data[offset] >> 2 & 0x4); EncryptionMode = (EncryptionMode)(data[offset++] & 0x7); } else if (Command == EpAuthPacketCommand.Acknowledge) { - // remove last two reserved LSBs - Acknowledgement = (EpAuthPacketAcknowledgement)(data[offset++]);// & 0xFC); + // remove hasTdu + Method = (EpAuthPacketMethod)(data[offset++] & 0xDF); } else if (Command == EpAuthPacketCommand.Action) { - // remove last two reserved LSBs - Action = (EpAuthPacketAction)(data[offset++]);// & 0xFC); + // remove hasTdu + Method = (EpAuthPacketMethod)(data[offset++] & 0xDF); } else if (Command == EpAuthPacketCommand.Event) { - // remove last two reserved LSBs - Event = (EpAuthPacketEvent)(data[offset++]);// & 0xFC); + // remove hasTdu + Method = (EpAuthPacketMethod)(data[offset++] & 0xDF); } else { @@ -174,7 +183,7 @@ public class EpAuthPacket : Packet if (NotEnough(offset, ends, 1)) return -dataLengthNeeded; - Tdu = ParsedTdu.Parse(data, offset, ends); + Tdu = PlainTdu.Parse(data, offset, ends);//, _warehouse); if (Tdu.Value.Class == TduClass.Invalid) return -(int)Tdu.Value.TotalLength; diff --git a/Libraries/Esiur/Net/Packets/EpAuthPacketHeader.cs b/Libraries/Esiur/Net/Packets/EpAuthPacketHeader.cs index f927cfd..a65fa42 100644 --- a/Libraries/Esiur/Net/Packets/EpAuthPacketHeader.cs +++ b/Libraries/Esiur/Net/Packets/EpAuthPacketHeader.cs @@ -19,6 +19,8 @@ namespace Esiur.Net.Packets Referrer, Time, IPAddress, + Identity, + AuthenticationProtocol, AuthenticationData, } } diff --git a/Libraries/Esiur/Net/Packets/EpAuthPacketMethod.cs b/Libraries/Esiur/Net/Packets/EpAuthPacketMethod.cs new file mode 100644 index 0000000..0870bd0 --- /dev/null +++ b/Libraries/Esiur/Net/Packets/EpAuthPacketMethod.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Net.Packets +{ + public enum EpAuthPacketMethod + { + // Initialize + Initialize = 0x00, + //InitializeEncrypted = 0x01, + //InitializeNoAuth = 0x02, + //InitializeNoAuthEncrypted = 0x3, + + // Actions + Handshake = 0x80, + FinalHandshake = 0x81, + + // Acks + Denied = 0x40, // no reason, terminate connection + NotSupported = 0x41, // auth not supported, terminate connection + TrySupported = 0x42, // auth not supported, but other auth methods in the reply are supported. connection is still open + Retry = 0x43, // try another auth method, connection is still open + ProceedToHandshake = 0x44, // auth method accepted, proceed to handshake, connection is still open + ProceedToFinalHandshake = 0x45, // auth method accepted, proceed to final handshake, connection is still open + ProceedToEstablishSession = 0x46, // auth method accepted, proceed to establish session, connection is still open + SessionEstablished = 0x47, // session established, session Id provided, switch to session mode, connection is still open + + // Events + ErrorTerminate = 0xC0, + ErrorMustEncrypt = 0xC1, + ErrorRetry = 0xC2, + + IndicationEstablished = 0xC8, + + IAuthPlain = 0xD0, + IAuthHashed = 0xD1, + IAuthEncrypted = 0xD2 + + } +} diff --git a/Libraries/Esiur/Net/Packets/EpPacket.cs b/Libraries/Esiur/Net/Packets/EpPacket.cs index 4a27a40..64a211d 100644 --- a/Libraries/Esiur/Net/Packets/EpPacket.cs +++ b/Libraries/Esiur/Net/Packets/EpPacket.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Esiur.Resource; namespace Esiur.Net.Packets; class EpPacket : Packet @@ -44,12 +45,22 @@ class EpPacket : Packet public byte Extension { get; set; } - public ParsedTdu? Tdu { get; set; } + //public ParsedTdu? Tdu { get; set; } + //public byte[] TduPayload { get; set; } + + public PlainTdu? Tdu { get; set; } private uint dataLengthNeeded; private uint originalOffset; + Warehouse _warehouse; + + public EpPacket(Warehouse warehouse) + { + _warehouse = warehouse; + } + public override bool Compose() { return base.Compose(); @@ -124,15 +135,22 @@ class EpPacket : Packet if (NotEnough(offset, ends, 1)) return -dataLengthNeeded; - Tdu = ParsedTdu.Parse(data, offset, ends); + Tdu = PlainTdu.Parse(data, offset, ends); if (Tdu.Value.Class == TduClass.Invalid) return -(int)Tdu.Value.TotalLength; + //Tdu = ParsedTdu.ParseSync(data, offset, ends, _warehouse); + + //if (Tdu.Value.Class == TduClass.Invalid) + // return -(int)Tdu.Value.TotalLength; + + //offset += (uint)Tdu.Value.TotalLength; offset += (uint)Tdu.Value.TotalLength; } else { + //Tdu = null; Tdu = null; } diff --git a/Libraries/Esiur/Protocol/EpConnection.cs b/Libraries/Esiur/Protocol/EpConnection.cs index 10e39b3..1f77594 100644 --- a/Libraries/Esiur/Protocol/EpConnection.cs +++ b/Libraries/Esiur/Protocol/EpConnection.cs @@ -35,6 +35,7 @@ using Esiur.Resource; using Esiur.Security.Authority; using Esiur.Security.Cryptography; using Esiur.Security.Membership; +using Esiur.Security.Permissions; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; @@ -48,7 +49,7 @@ using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Timers; - + namespace Esiur.Protocol; public partial class EpConnection : NetworkConnection, IStore @@ -83,31 +84,30 @@ public partial class EpConnection : NetworkConnection, IStore // Fields - bool invalidCredentials = false; + bool _invalidCredentials = false; - System.Timers.Timer keepAliveTimer; - DateTime? lastKeepAliveSent; - DateTime? lastKeepAliveReceived; + System.Timers.Timer _keepAliveTimer; + DateTime? _lastKeepAliveSent; + DateTime? _lastKeepAliveReceived; - EpPacket packet = new EpPacket(); - EpAuthPacket authPacket = new EpAuthPacket(); + EpPacket _packet; //= new EpPacket(); + EpAuthPacket _authPacket;// = new EpAuthPacket(); - Session session; + Session _session; - AsyncReply openReply; + AsyncReply _openReply; - - //byte[] localPasswordOrToken; - bool authenticated, readyToEstablish; + bool _authenticated, _readyToEstablish; string _hostname; ushort _port; - bool initialPacket = true; - bool isInitiator = false; + bool _initialPacket = true; + AuthenticationDirection _authDirection = AuthenticationDirection.Responder; + Map _remoteTypeDefs = new Map(); // Properties @@ -116,13 +116,26 @@ public partial class EpConnection : NetworkConnection, IStore /// /// Distributed server responsible for this connection, usually for incoming connections. /// - public EpServer Server { get; internal set; } + /// + EpServer _server; + public EpServer Server + { + get => _server; + internal set + { + _server = value; + if (_authPacket == null) + _authPacket = new EpAuthPacket(value.Instance.Warehouse); + if (_packet == null) + _packet = new EpPacket(value.Instance.Warehouse); + } + } /// /// The session related to this connection. /// - public Session Session => session; + public Session Session => _session; [Export] public virtual EpConnectionStatus Status { get; private set; } @@ -167,8 +180,31 @@ public partial class EpConnection : NetworkConnection, IStore //[Attribute] //public ulong TokenIndex { get; set; } - [Attribute] - public string Domain { get; set; } + //[Attribute] + //public string Domain { get; set; } + + string _remoteDomain, _localDomain; + + public string RemoteDomain + { + get => _remoteDomain; + set + { + _remoteDomain = value; + _session.RemoteHeaders[EpAuthPacketHeader.Domain] = value; + } + } + + public string LocalDomain + { + get => _localDomain; + set + { + _localDomain = value; + _session.LocalHeaders[EpAuthPacketHeader.Domain] = value; + } + + } public bool Remove(IResource resource) { @@ -194,7 +230,9 @@ public partial class EpConnection : NetworkConnection, IStore /// Data to send. public override void Send(byte[] data) { - //Console.WriteLine("Client: {0}", Data.Length); + #if VERBOSE + Console.WriteLine("Client: {0}", Data.Length); + #endif Global.Counters["Ep Sent Packets"]++; base.Send(data); @@ -222,11 +260,11 @@ public partial class EpConnection : NetworkConnection, IStore { base.Assign(socket); - session.LocalHeaders[EpAuthPacketHeader.IPAddress] + _session.LocalHeaders[EpAuthPacketHeader.IPAddress] = socket.RemoteEndPoint.Address.GetAddressBytes(); if (socket.State == SocketState.Established && - isInitiator) + _authDirection == AuthenticationDirection.Initiator) { Declare(); } @@ -234,40 +272,32 @@ public partial class EpConnection : NetworkConnection, IStore private void Declare() { - //if (session.KeyExchanger != null) - //{ - // // create key - // var key = session.KeyExchanger.GetPublicKey(); - // session.LocalHeaders[EpAuthPacketHeader.CipherKey] = key; - //} - - - if (!isInitiator) + if (_authDirection != AuthenticationDirection.Initiator) return; - if (session.AuthenticationMode != AuthenticationMode.None) - { - if (session.AuthenticationHandler == null) - throw new Exception("Authentication handler must be assigned for the session."); - - var initAuthData = session.AuthenticationHandler.Initialize(session, null); - - session.LocalHeaders.Add(EpAuthPacketHeader.AuthenticationData, initAuthData); - } - - if (session.EncryptionMode != EncryptionMode.None) - { - // get the handler - } - // change to Map for compatibility - var headers = Codec.Compose(session.LocalHeaders.Select(x => new KeyValuePair((byte)x.Key, x.Value)), this.Instance.Warehouse, this); + var headers = _session.LocalHeaders.Select(x => new KeyValuePair((byte)x.Key, x.Value)); - SendParams() - .AddUInt8((byte)(0x20 | ((byte)session.AuthenticationMode << 2) - | (byte)session.EncryptionMode)) - .AddUInt8Array(headers) - .Done(); + if (_session.AuthenticationMode != AuthenticationMode.None) + { + if (_session.AuthenticationHandler == null) + throw new Exception("Authentication handler must be assigned for the session."); + + var initAuthData = _session.AuthenticationHandler.Process(null); + + headers.Add((byte)EpAuthPacketHeader.AuthenticationProtocol, _session.AuthenticationHandler.Protocol); + headers.Add((byte)EpAuthPacketHeader.AuthenticationData, initAuthData); + } + + if (_session.EncryptionMode != EncryptionMode.None) + { + //@TODO: get the handler + } + + SendAuthHeaders((EpAuthPacketMethod)( + (byte)EpAuthPacketMethod.Initialize + | (byte)(_session.AuthenticationMode) << 2 + | (byte)_session.EncryptionMode), headers); } /// @@ -279,17 +309,17 @@ public partial class EpConnection : NetworkConnection, IStore /// Password. public EpConnection(ISocket socket, IAuthenticationHandler authenticationHandler, Map headers) { - this.session = new Session(); + _session = new Session(); //if (authenticationHandler.Type != AuthenticationType.Initiator) // throw new Exception("" //session.AuthenticationType = AuthenticationMode.Initiator; - session.LocalHeaders = headers; + _session.LocalHeaders = headers; if (authenticationHandler != null) { - session.AuthenticationHandler = authenticationHandler; - session.AuthenticationMode = authenticationHandler.Mode; + _session.AuthenticationHandler = authenticationHandler; + //session.AuthenticationMode = authenticationHandler.Mode; } //this.localPasswordOrToken = DC.ToBytes(password); @@ -322,7 +352,7 @@ public partial class EpConnection : NetworkConnection, IStore /// public EpConnection() { - session = new Session(); + _session = new Session(); //session.AuthenticationType = AuthenticationMode.Responder; //session.AuthenticationResponder = authenticationResponder; @@ -338,7 +368,7 @@ public partial class EpConnection : NetworkConnection, IStore { var r = resource as EpResource; if (r.Instance.Store == this) - return this.Instance.Name + "/" + r.DistributedResourceInstanceId; + return this.Instance.Name + "/" + r.ResourceInstanceId; } return null; @@ -347,15 +377,15 @@ public partial class EpConnection : NetworkConnection, IStore public List> GetFinishedQueue() { - var l = queue.Processed.ToArray().ToList(); - queue.Processed.Clear(); + var l = _queue.Processed.ToArray().ToList(); + _queue.Processed.Clear(); return l; } void init() { //var q = queue; - queue.Then((x) => + _queue.Then((x) => { if (x.Type == EpResourceQueueItem.DistributedResourceQueueItemType.Event) x.Resource._EmitEventByIndex(x.Index, x.Value); @@ -371,8 +401,8 @@ public partial class EpConnection : NetworkConnection, IStore // set local nonce //session.LocalHeaders[EpAuthPacketHeader.Nonce] = Global.GenerateBytes(32); - keepAliveTimer = new System.Timers.Timer(KeepAliveInterval * 1000); - keepAliveTimer.Elapsed += KeepAliveTimer_Elapsed; ; + _keepAliveTimer = new System.Timers.Timer(KeepAliveInterval * 1000); + _keepAliveTimer.Elapsed += KeepAliveTimer_Elapsed; ; } private void KeepAliveTimer_Elapsed(object? sender, ElapsedEventArgs e) @@ -381,27 +411,27 @@ public partial class EpConnection : NetworkConnection, IStore return; - keepAliveTimer.Stop(); + _keepAliveTimer.Stop(); var now = DateTime.UtcNow; - uint interval = lastKeepAliveSent == null ? 0 : - (uint)(now - (DateTime)lastKeepAliveSent).TotalMilliseconds; + uint interval = _lastKeepAliveSent == null ? 0 : + (uint)(now - (DateTime)_lastKeepAliveSent).TotalMilliseconds; - lastKeepAliveSent = now; + _lastKeepAliveSent = now; SendRequest(EpPacketRequest.KeepAlive, now, interval) .Then(x => { Jitter = Convert.ToUInt32(((object[])x)[1]); - keepAliveTimer.Start(); + _keepAliveTimer.Start(); }).Error(ex => { - keepAliveTimer.Stop(); + _keepAliveTimer.Stop(); Close(); }).Timeout((int)(KeepAliveTime * 1000), () => { - keepAliveTimer.Stop(); + _keepAliveTimer.Stop(); Close(); }); @@ -420,9 +450,9 @@ public partial class EpConnection : NetworkConnection, IStore private uint processPacket(byte[] msg, uint offset, uint ends, NetworkBuffer data, int chunkId) { - if (authenticated) + if (_authenticated) { - var rt = packet.Parse(msg, offset, ends); + var rt = _packet.Parse(msg, offset, ends); if (rt <= 0) { @@ -432,160 +462,160 @@ public partial class EpConnection : NetworkConnection, IStore } else { - offset += (uint)rt; - if (packet.Tdu == null) + if (_packet.Tdu == null) return offset; - //Console.WriteLine("Incoming: " + packet + " " + packet.CallbackId); - - if (packet.Method == EpPacketMethod.Notification) +#if VERBOSE + Console.WriteLine("Incoming: " + _packet + " " + _packet.CallbackId); +#endif + if (_packet.Method == EpPacketMethod.Notification) { - var dt = packet.Tdu.Value; + var dt = _packet.Tdu.Value; - switch (packet.Notification) + switch (_packet.Notification) { // Invoke case EpPacketNotification.PropertyModified: EpNotificationPropertyModified(dt); break; case EpPacketNotification.EventOccurred: - EpNotificationEventOccurred(dt, msg); + EpNotificationEventOccurred(dt); break; // Manage case EpPacketNotification.ResourceDestroyed: - EpNotificationResourceDestroyed(dt, msg); + EpNotificationResourceDestroyed(dt); break; case EpPacketNotification.ResourceReassigned: EpNotificationResourceReassigned(dt); break; case EpPacketNotification.ResourceMoved: - EpNotificationResourceMoved(dt, msg); + EpNotificationResourceMoved(dt); break; case EpPacketNotification.SystemFailure: - EpNotificationSystemFailure(dt, msg); + EpNotificationSystemFailure(dt); break; } } - else if (packet.Method == EpPacketMethod.Request) + else if (_packet.Method == EpPacketMethod.Request) { - var dt = packet.Tdu.Value; + var dt = _packet.Tdu.Value; - switch (packet.Request) + switch (_packet.Request) { // Invoke case EpPacketRequest.InvokeFunction: - EpRequestInvokeFunction(packet.CallbackId, dt, msg); + EpRequestInvokeFunction(_packet.CallbackId, dt); break; case EpPacketRequest.SetProperty: - EpRequestSetProperty(packet.CallbackId, dt, msg); + EpRequestSetProperty(_packet.CallbackId, dt); break; case EpPacketRequest.Subscribe: - EpRequestSubscribe(packet.CallbackId, dt, msg); + EpRequestSubscribe(_packet.CallbackId, dt); break; case EpPacketRequest.Unsubscribe: - EpRequestUnsubscribe(packet.CallbackId, dt, msg); + EpRequestUnsubscribe(_packet.CallbackId, dt); break; // Inquire case EpPacketRequest.TypeDefByName: - EpRequestTypeDefByName(packet.CallbackId, dt, msg); + EpRequestTypeDefByName(_packet.CallbackId, dt); break; case EpPacketRequest.TypeDefById: - EpRequestTypeDefById(packet.CallbackId, dt, msg); + EpRequestTypeDefById(_packet.CallbackId, dt); break; case EpPacketRequest.TypeDefByResourceId: - EpRequestTypeDefByResourceId(packet.CallbackId, dt, msg); + EpRequestTypeDefByResourceId(_packet.CallbackId, dt); break; case EpPacketRequest.Query: - EpRequestQueryResources(packet.CallbackId, dt, msg); + EpRequestQueryResources(_packet.CallbackId, dt); break; case EpPacketRequest.LinkTypeDefs: - EpRequestLinkTypeDefs(packet.CallbackId, dt, msg); + EpRequestLinkTypeDefs(_packet.CallbackId, dt); break; case EpPacketRequest.Token: - EpRequestToken(packet.CallbackId, dt, msg); + EpRequestToken(_packet.CallbackId, dt); break; case EpPacketRequest.GetResourceIdByLink: - EpRequestGetResourceIdByLink(packet.CallbackId, dt, msg); + EpRequestGetResourceIdByLink(_packet.CallbackId, dt); break; // Manage case EpPacketRequest.AttachResource: - EpRequestAttachResource(packet.CallbackId, dt, msg); + EpRequestAttachResource(_packet.CallbackId, dt); break; case EpPacketRequest.ReattachResource: - EpRequestReattachResource(packet.CallbackId, dt, msg); + EpRequestReattachResource(_packet.CallbackId, dt); break; case EpPacketRequest.DetachResource: - EpRequestDetachResource(packet.CallbackId, dt, msg); + EpRequestDetachResource(_packet.CallbackId, dt); break; case EpPacketRequest.CreateResource: - EpRequestCreateResource(packet.CallbackId, dt, msg); + EpRequestCreateResource(_packet.CallbackId, dt); break; case EpPacketRequest.DeleteResource: - EpRequestDeleteResource(packet.CallbackId, dt, msg); + EpRequestDeleteResource(_packet.CallbackId, dt); break; case EpPacketRequest.MoveResource: - EpRequestMoveResource(packet.CallbackId, dt, msg); + EpRequestMoveResource(_packet.CallbackId, dt); break; // Static case EpPacketRequest.KeepAlive: - EpRequestKeepAlive(packet.CallbackId, dt, msg); + EpRequestKeepAlive(_packet.CallbackId, dt); break; case EpPacketRequest.ProcedureCall: - EpRequestProcedureCall(packet.CallbackId, dt, msg); + EpRequestProcedureCall(_packet.CallbackId, dt); break; case EpPacketRequest.StaticCall: - EpRequestStaticCall(packet.CallbackId, dt, msg); + EpRequestStaticCall(_packet.CallbackId, dt); break; } } - else if (packet.Method == EpPacketMethod.Reply) + else if (_packet.Method == EpPacketMethod.Reply) { - var dt = packet.Tdu.Value; + var dt = _packet.Tdu.Value; - switch (packet.Reply) + switch (_packet.Reply) { case EpPacketReply.Completed: - EpReplyCompleted(packet.CallbackId, dt); + EpReplyCompleted(_packet.CallbackId, dt); break; case EpPacketReply.Propagated: - EpReplyPropagated(packet.CallbackId, dt, msg); + EpReplyPropagated(_packet.CallbackId, dt); break; case EpPacketReply.PermissionError: - EpReplyError(packet.CallbackId, dt, msg, ErrorType.Management); + EpReplyError(_packet.CallbackId, dt, ErrorType.Management); break; case EpPacketReply.ExecutionError: - EpReplyError(packet.CallbackId, dt, msg, ErrorType.Exception); + EpReplyError(_packet.CallbackId, dt, ErrorType.Exception); break; case EpPacketReply.Progress: - EpReplyProgress(packet.CallbackId, dt, msg); + EpReplyProgress(_packet.CallbackId, dt); break; case EpPacketReply.Chunk: - EpReplyChunk(packet.CallbackId, dt); + EpReplyChunk(_packet.CallbackId, dt); break; case EpPacketReply.Warning: - EpReplyWarning(packet.Extension, dt, msg); + EpReplyWarning(_packet.Extension, dt); break; } } - else if (packet.Method == EpPacketMethod.Extension) + else if (_packet.Method == EpPacketMethod.Extension) { - EpExtensionAction(packet.Extension, packet.Tdu, msg); + EpExtensionAction(_packet.Extension, _packet.Tdu); } } } else { // check if the request through Websockets - if (initialPacket) + if (_initialPacket) { - initialPacket = false; + _initialPacket = false; if (msg.Length > 3 && Encoding.Default.GetString(msg, 0, 3) == "GET") { @@ -633,7 +663,7 @@ public partial class EpConnection : NetworkConnection, IStore } } - var rt = authPacket.Parse(msg, offset, ends); + var rt = _authPacket.Parse(msg, offset, ends); if (rt <= 0) { @@ -644,125 +674,249 @@ public partial class EpConnection : NetworkConnection, IStore { offset += (uint)rt; - //Console.WriteLine($"AuthPacket: RT: {rt} {authPacket.ToString()}"); +#if VERBOSE + Console.WriteLine($"AuthPacket: RT: {rt} {authPacket.ToString()}"); +#endif - if (authPacket.Command == EpAuthPacketCommand.Initialize && isInitiator) + if (_authPacket.Command == EpAuthPacketCommand.Initialize && _authDirection == AuthenticationDirection.Initiator) throw new Exception("Bad authentication packet received. Connection is initiator but received an initialization packet."); - if (authPacket.Command == EpAuthPacketCommand.Acknowledge && !isInitiator) + if (_authPacket.Command == EpAuthPacketCommand.Acknowledge && _authDirection == AuthenticationDirection.Responder) throw new Exception("Bad authentication packet received. Connection is responder but received an acknowledge packet."); - if (authPacket.Command == EpAuthPacketCommand.Initialize) + if (_authPacket.Command == EpAuthPacketCommand.Initialize) { - if (authPacket.Tdu != null) + + var remoteHeaders = new Map(); + object remoteAuthData = null; + + if (_authPacket.Tdu != null) { - var (_, parsed) = Codec.ParseSync(authPacket.Tdu.Value, null); + var parsed = Codec.ParseSync(_authPacket.Tdu.Value, null); if (parsed is Map headers) { - session.RemoteHeaders = headers.Select(x => new KeyValuePair((EpAuthPacketHeader)x.Key, x.Value)); + remoteHeaders = headers; + + foreach (var header in headers) + { + if (header.Key == (byte)EpAuthPacketHeader.AuthenticationData) + remoteAuthData = header.Value; + else + _session.RemoteHeaders.Add((EpAuthPacketHeader)header.Key, header.Value); + } } } - //@TODO: get the authentication handler - if (session.RemoteHeaders.ContainsKey(EpAuthPacketHeader.AuthenticationData)) + var localHeaders = new Map(); + foreach (var header in _session.LocalHeaders) + localHeaders.Add((byte)header.Key, header.Value); + + if (_authPacket.AuthMode == AuthenticationMode.None) { - var authResult = session.AuthenticationHandler.Initialize(session, session.RemoteHeaders[EpAuthPacketHeader.AuthenticationData]); + //@TODO: check if allowed, pass for testing + SendAuthHeaders(EpAuthPacketMethod.SessionEstablished, localHeaders); + AuthenticatonCompleted(null, "guest"); + return offset; } - //@TODO allow all for testing - AuthenticatonCompleted("guest"); - SendParams() - .AddUInt8((byte)EpAuthPacketAcknowledgement.SessionEstablished) - .Done(); + if (!_session.RemoteHeaders.ContainsKey(EpAuthPacketHeader.AuthenticationProtocol)) + { + SendAuthHeaders(EpAuthPacketMethod.NotSupported, localHeaders); + _invalidCredentials = true; + Close(); + return offset; + } + + var provider = Instance.Warehouse.GetAuthenticationProvider(_session.RemoteHeaders[EpAuthPacketHeader.AuthenticationProtocol].ToString()); + + var handler = provider.CreateAuthenticationHandler(new AuthenticationContext() + { + Direction = AuthenticationDirection.Responder, + Mode = _authPacket.AuthMode, + Domain = _session.RemoteHeaders.ContainsKey(EpAuthPacketHeader.Domain) ? _session.RemoteHeaders[EpAuthPacketHeader.Domain].ToString() : null, + Materials = new AuthenticationMaterial[] { new AuthenticationMaterial() { Type = AuthenticationMaterialType.Data, Value = remoteAuthData } } + }); + + if (handler == null) + { + SendAuthHeaders(EpAuthPacketMethod.NotSupported, localHeaders); + _invalidCredentials = true; + Close(); + return offset; + } + + // set auth handler for the session + _session.AuthenticationHandler = handler; + + var authResult = handler.Process(remoteAuthData); + + + // send acknowledgements + + localHeaders.Add(EpAuthPacketHeader.AuthenticationData, + authResult.AuthenticationData); + + if (authResult.Ruling == AuthenticationRuling.Failed) + { + SendAuthHeaders(EpAuthPacketMethod.Denied, localHeaders); + _invalidCredentials = true; + Close(); + } + else if (authResult.Ruling == AuthenticationRuling.InProgress) + { + SendAuthHeaders(EpAuthPacketMethod.ProceedToHandshake, localHeaders); + } + else if (authResult.Ruling == AuthenticationRuling.Succeeded) + { + SendAuthHeaders(EpAuthPacketMethod.SessionEstablished, localHeaders); + AuthenticatonCompleted(authResult.LocalIdentity, authResult.RemoteIdentity); + } } - else if (authPacket.Command == EpAuthPacketCommand.Acknowledge) + else if (_authPacket.Command == EpAuthPacketCommand.Acknowledge) { - //@TODO: get the authentication handler + var remoteHeaders + = new Map(); + object remoteAuthData = null; - if (authPacket.Tdu != null) + if (_authPacket.Tdu != null) { - var (_, parsed) = Codec.ParseSync(authPacket.Tdu.Value, Instance.Warehouse); + var parsed = Codec.ParseSync(_authPacket.Tdu.Value, Instance.Warehouse); if (parsed is Map headers) { - session.RemoteHeaders = headers.Select(x => new KeyValuePair((EpAuthPacketHeader)x.Key, x.Value)); + foreach (var header in headers) + { + if (header.Key == (byte)EpAuthPacketHeader.AuthenticationData) + { + remoteAuthData = header.Value; + } + else + { + remoteHeaders.Add((EpAuthPacketHeader)header.Key, header.Value); + } + } + + _session.RemoteHeaders = remoteHeaders;// headers.Select(x => new KeyValuePair((EpAuthPacketHeader)x.Key, x.Value)); } } - if (session.RemoteHeaders.ContainsKey(EpAuthPacketHeader.AuthenticationData)) + if (_session.AuthenticationMode == AuthenticationMode.None) { - var authResult = session.AuthenticationHandler.Initialize(session, session.RemoteHeaders[EpAuthPacketHeader.AuthenticationData]); + if (_authPacket.Method == EpAuthPacketMethod.SessionEstablished) + { + AuthenticatonCompleted("guest", null); + } + else + { + _invalidCredentials = true; + Close(); + } + + return offset; } - if (authPacket.Acknowledgement == EpAuthPacketAcknowledgement.SessionEstablished) + var authResult = _session.AuthenticationHandler.Process(remoteAuthData); + + if (authResult.Ruling == AuthenticationRuling.Failed) { - // session established, check if authentication is required - AuthenticatonCompleted("guest"); + SendAuth(EpAuthPacketMethod.ErrorTerminate); + _invalidCredentials = true; + Close(); + return offset; + } + else if (authResult.Ruling == AuthenticationRuling.InProgress) + { + if (_authPacket.Method == EpAuthPacketMethod.ProceedToHandshake) + SendAuthData(EpAuthPacketMethod.Handshake, + authResult.AuthenticationData); + else + { + throw new Exception("Bad protocol sequence."); + } + } + else if (authResult.Ruling == AuthenticationRuling.Succeeded) + { + + if (_authPacket.Method == EpAuthPacketMethod.SessionEstablished) + { + AuthenticatonCompleted(authResult.LocalIdentity, authResult.RemoteIdentity); + } + else if (_authPacket.Method == EpAuthPacketMethod.ProceedToEstablishSession) + { + // @TODO: Send establish request + } + + } + + } + else if (_authPacket.Command == EpAuthPacketCommand.Action) + { + + object authData = null; + + if (_authPacket.Tdu != null) + { + var parsed = Codec.ParseSync(_authPacket.Tdu.Value, Instance.Warehouse); + authData = parsed; + } + + if (_authPacket.Method == EpAuthPacketMethod.Handshake) + { + var authResult = _session.AuthenticationHandler.Process(authData); + + if (authResult.Ruling == AuthenticationRuling.Failed) + { + SendAuth(EpAuthPacketMethod.ErrorTerminate); + _invalidCredentials = true; + Close(); + } + else if (authResult.Ruling == AuthenticationRuling.InProgress) + { + SendAuthData(EpAuthPacketMethod.Handshake, authResult.AuthenticationData); + } + else if (authResult.Ruling == AuthenticationRuling.Succeeded) + { + if (authResult.AuthenticationData != null) + { + SendAuthData(EpAuthPacketMethod.FinalHandshake, authResult.AuthenticationData); + } + else + { + SendAuth(EpAuthPacketMethod.SessionEstablished); + AuthenticatonCompleted(authResult.LocalIdentity, authResult.RemoteIdentity); + } + } + } + } + else if (_authPacket.Command == EpAuthPacketCommand.Event) + { + if (_authPacket.Method == EpAuthPacketMethod.ErrorTerminate + || _authPacket.Method == EpAuthPacketMethod.ErrorMustEncrypt + || _authPacket.Method == EpAuthPacketMethod.ErrorRetry) + { + _invalidCredentials = true; + OnError?.Invoke(this, _authPacket.ErrorCode, _authPacket.Message ?? "Authentication error."); + Close(); + } + else if (_authPacket.Method == EpAuthPacketMethod.IndicationEstablished) + { + // @TODO: handle multi-factor authentication indication } } - - - //if (session.AuthenticationMode == AuthenticationMode.None) - //{ - // // establish session without authentication - //} - - //if (session.AuthenticationHandler == null) - //{ - // throw new Exception("No authentication handler assigned for the session."); - //} - - //try - //{ - // var result = session.AuthenticationHandler.Process(authPacket); - // if (result.Ruling == AuthenticationRuling.Succeeded) - // { - // AuthenticatonCompleted(result.Identity); - // } - // else if (result.Ruling == AuthenticationRuling.InProgress) - // { - // SendParams() - // .AddUInt8((byte)EpAuthPacketCommand.Acknowledge) - // .AddUInt8Array(Codec.Compose( - // result.HandshakePayload - // , this.Instance.Warehouse, this)) - // .Done(); - - // } - // else if (result.Ruling == AuthenticationRuling.Failed) - // { - // // Send the server side error - // SendParams() - // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) - // .AddUInt8Array(Codec.Compose( - // new object[] {(ushort)result.ExceptionCode, - // result.ExceptionMessage } - // , this.Instance.Warehouse, this)) - // .Done(); - // } - //} - //catch (Exception ex) - //{ - // // Send the server side error - // SendParams() - // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) - // .AddUInt8Array(Codec.Compose( - // new object[] { (ushort)ExceptionCode.GeneralFailure, - // ex.Message } - // , this.Instance.Warehouse, this)) - // .Done(); - //} - } + } return offset; + } - void AuthenticatonCompleted(string identity) - { + + + void AuthenticatonCompleted(string localIdentity, string remoteIdentity) + { if (this.Instance == null) { @@ -770,32 +924,35 @@ public partial class EpConnection : NetworkConnection, IStore Server.Instance.Link + "/" + this.GetHashCode().ToString().Replace("/", "_"), this) .Then(x => { - session.AuthorizedIdentity = identity; - authenticated = true; + _session.LocalIdentity = localIdentity; + _session.RemoteIdentity = remoteIdentity; + + _authenticated = true; Status = EpConnectionStatus.Connected; - openReply?.Trigger(true); - openReply = null; + _openReply?.Trigger(true); + _openReply = null; OnReady?.Invoke(this); - Server?.Membership?.Login(session); + Server?.Membership?.Login(_session); LoginDate = DateTime.Now; }).Error(x => { - openReply?.TriggerError(x); - openReply = null; + _openReply?.TriggerError(x); + _openReply = null; }); } else { - session.AuthorizedIdentity = identity; - authenticated = true; + _session.LocalIdentity = localIdentity; + _session.RemoteIdentity = remoteIdentity; + _authenticated = true; Status = EpConnectionStatus.Connected; - openReply?.Trigger(true); - openReply = null; + _openReply?.Trigger(true); + _openReply = null; OnReady?.Invoke(this); - Server?.Membership?.Login(session); + Server?.Membership?.Login(_session); } } //private void ProcessClientAuth(byte[] data) @@ -1527,8 +1684,13 @@ public partial class EpConnection : NetworkConnection, IStore /// public AsyncReply Trigger(ResourceTrigger trigger) { + + _authPacket = new EpAuthPacket(Instance.Warehouse); + _packet = new EpPacket(Instance.Warehouse); + if (trigger == ResourceTrigger.Open) { + // @TODO: Need a better way to check for initiator or responder if (this.Server != null) return new AsyncReply(true); @@ -1536,10 +1698,12 @@ public partial class EpConnection : NetworkConnection, IStore var address = host[0]; var port = host.Length > 1 ? ushort.Parse(host[1]) : (ushort)10518; - // assign domain from hostname if not provided - var domain = Domain != null ? Domain : address; - return Connect(null, address, port, domain); + // assign domain from hostname if not provided + if (_remoteDomain == null) + _remoteDomain = address; + + return Connect(null, address, port, _remoteDomain); } return new AsyncReply(true); @@ -1550,23 +1714,26 @@ public partial class EpConnection : NetworkConnection, IStore public AsyncReply Connect(ISocket socket = null, string hostname = null, ushort port = 0, string domain = null) { - if (openReply != null) + if (_openReply != null) throw new AsyncException(ErrorType.Exception, 0, "Connection in progress"); Status = EpConnectionStatus.Connecting; - openReply = new AsyncReply(); + _openReply = new AsyncReply(); + + // set auth direction to initiator + _authDirection = AuthenticationDirection.Initiator; if (hostname != null) { - session = new Session(); - isInitiator = true; - invalidCredentials = false; + _session = new Session(); + _authDirection = AuthenticationDirection.Initiator; + _invalidCredentials = false; - session.LocalHeaders[EpAuthPacketHeader.Domain] = domain; + _session.LocalHeaders[EpAuthPacketHeader.Domain] = domain; } - if (session == null) + if (_session == null) throw new AsyncException(ErrorType.Exception, 0, "Session not initialized"); if (socket == null) @@ -1585,7 +1752,7 @@ public partial class EpConnection : NetworkConnection, IStore connectSocket(socket); - return openReply; + return _openReply; } void connectSocket(ISocket socket) @@ -1602,8 +1769,8 @@ public partial class EpConnection : NetworkConnection, IStore } else { - openReply.TriggerError(x); - openReply = null; + _openReply.TriggerError(x); + _openReply = null; } }); @@ -1620,7 +1787,7 @@ public partial class EpConnection : NetworkConnection, IStore { var toBeRestored = new List(); - foreach (KeyValuePair> kv in suspendedResources) + foreach (KeyValuePair> kv in _suspendedResources) { EpResource r; if (kv.Value.TryGetTarget(out r)) @@ -1630,9 +1797,9 @@ public partial class EpConnection : NetworkConnection, IStore foreach (var r in toBeRestored) { - var link = DC.ToBytes(r.DistributedResourceLink); + var link = DC.ToBytes(r.ResourceLink); - Global.Log("EpConnection", LogType.Debug, "Restoreing " + r.DistributedResourceLink); + Global.Log("EpConnection", LogType.Debug, "Restoreing " + r.ResourceLink); try { @@ -1640,15 +1807,15 @@ public partial class EpConnection : NetworkConnection, IStore // remove from suspended. - suspendedResources.Remove(r.DistributedResourceInstanceId); + _suspendedResources.Remove(r.ResourceInstanceId); // id changed ? - if (id != r.DistributedResourceInstanceId) - r.DistributedResourceInstanceId = id; + if (id != r.ResourceInstanceId) + r.ResourceInstanceId = id; - neededResources[id] = r; + _neededResources[id] = r; - await Fetch(id, null); + await FetchResource(id, null); Global.Log("EpConnection", LogType.Debug, "Restored " + id); @@ -1691,7 +1858,7 @@ public partial class EpConnection : NetworkConnection, IStore public AsyncReply Put(IResource resource, string path) { if (Codec.IsLocalResource(resource, this)) - neededResources.Add((resource as EpResource).DistributedResourceInstanceId, (EpResource)resource); + _neededResources.Add((resource as EpResource).ResourceInstanceId, (EpResource)resource); // else ... send it to the peer return new AsyncReply(true); } @@ -1717,22 +1884,22 @@ public partial class EpConnection : NetworkConnection, IStore protected override void Connected() { - if (isInitiator) + if (_authDirection == AuthenticationDirection.Initiator) Declare(); } protected override void Disconnected() { // clean up - authenticated = false; - readyToEstablish = false; + _authenticated = false; + _readyToEstablish = false; Status = EpConnectionStatus.Closed; - keepAliveTimer.Stop(); + _keepAliveTimer.Stop(); // @TODO: lock requests - foreach (var x in requests.Values) + foreach (var x in _requests.Values) { try { @@ -1744,7 +1911,7 @@ public partial class EpConnection : NetworkConnection, IStore } } - foreach (var x in resourceRequests.Values) + foreach (var x in _resourceRequests.Values) { try { @@ -1756,11 +1923,12 @@ public partial class EpConnection : NetworkConnection, IStore } } - foreach (var x in typeDefsByIdRequests.Values) + + foreach (var x in _typeDefRequests.Values) { try { - x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); + x.Reply.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); } catch (Exception ex) { @@ -1768,58 +1936,60 @@ public partial class EpConnection : NetworkConnection, IStore } } - foreach (var x in typeDefsByNameRequests.Values) - { - try - { - x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); - } - catch (Exception ex) - { - Global.Log(ex); - } - } + //foreach (var x in _typeDefsByNameRequests.Values) + //{ + // try + // { + // x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); + // } + // catch (Exception ex) + // { + // Global.Log(ex); + // } + //} - requests.Clear(); - resourceRequests.Clear(); - typeDefsByIdRequests.Clear(); - typeDefsByNameRequests.Clear(); + _requests.Clear(); + _resourceRequests.Clear(); + _typeDefRequests.Clear(); + + //_typeDefsByIdRequests.Clear(); + //_typeDefsByNameRequests.Clear(); - foreach (var x in attachedResources.Values) + foreach (var x in _attachedResources.Values) { EpResource r; if (x.TryGetTarget(out r)) { r.Suspend(); - suspendedResources[r.DistributedResourceInstanceId] = x; + _suspendedResources[r.ResourceInstanceId] = x; } } if (Server != null) { - suspendedResources.Clear(); + _suspendedResources.Clear(); UnsubscribeAll(); - Instance.Warehouse.Remove(this); + Instance?.Warehouse?.Remove(this); - if (authenticated) - Server.Membership?.Logout(session); + if (_authenticated) + Server.Membership?.Logout(_session); } - else if (AutoReconnect && !invalidCredentials) + else if (AutoReconnect && !_invalidCredentials) { // reconnect Task.Delay((int)ReconnectInterval).ContinueWith((x) => Reconnect()); } else { - suspendedResources.Clear(); + _suspendedResources.Clear(); } - attachedResources.Clear(); + _attachedResources.Clear(); } diff --git a/Libraries/Esiur/Protocol/EpConnectionConfig.cs b/Libraries/Esiur/Protocol/EpConnectionConfig.cs deleted file mode 100644 index cac0576..0000000 --- a/Libraries/Esiur/Protocol/EpConnectionConfig.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Esiur.Core; -using Esiur.Data; -using Esiur.Net.Packets; -using Esiur.Security.Membership; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Esiur.Protocol; - -public class EpConnectionConfig -{ - public ExceptionLevel ExceptionLevel { get; set; } - = ExceptionLevel.Code | ExceptionLevel.Message | ExceptionLevel.Source | ExceptionLevel.Trace; - - public Func> Authenticator { get; set; } - - public bool AutoReconnect { get; set; } = false; - - public uint ReconnectInterval { get; set; } = 5; - - public string Username { get; set; } - - public bool UseWebSocket { get; set; } - - public bool SecureWebSocket { get; set; } - - public string Password { get; set; } - - public string Token { get; set; } - - public ulong TokenIndex { get; set; } - - public string Domain { get; set; } -} diff --git a/Libraries/Esiur/Protocol/EpConnectionContext.cs b/Libraries/Esiur/Protocol/EpConnectionContext.cs new file mode 100644 index 0000000..7937a39 --- /dev/null +++ b/Libraries/Esiur/Protocol/EpConnectionContext.cs @@ -0,0 +1,58 @@ +using Esiur.Core; +using Esiur.Data; +using Esiur.Net.Packets; +using Esiur.Resource; +using Esiur.Security.Membership; +using Esiur.Security.Permissions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Protocol; + +public class EpConnectionContext : ResourceContext +{ + public EpConnectionContext() + : base(0, new Map(), null, null) + { + + } + + public override void Build() + { + Attributes["AutoConnect"] = AutoReconnect; + Attributes["ReconnectInterval"] = ReconnectInterval; + Attributes["UseWebSocket"] = UseWebSocket; + Attributes["SecureWebSocket"] = SecureWebSocket; + Attributes["Domain"] = SecureWebSocket; + Attributes["AuthenticationProtocol"] = SecureWebSocket; + Attributes["Identity"] = SecureWebSocket; + } + + public ExceptionLevel ExceptionLevel { get; set; } + = ExceptionLevel.Code | ExceptionLevel.Message | ExceptionLevel.Source | ExceptionLevel.Trace; + + //public Func> Authenticator { get; set; } + //public Func> Authenticator { get; set; } + + public string Identity { get; set; } + public string AuthenticationProtocol { get; set; } + + public bool AutoReconnect { get; set; } = false; + + public uint ReconnectInterval { get; set; } = 5; + + //public string Username { get; set; } + + public bool UseWebSocket { get; set; } + + public bool SecureWebSocket { get; set; } + + // public string Password { get; set; } + + //public string Token { get; set; } + + //public ulong TokenIndex { get; set; } + + public string Domain { get; set; } +} diff --git a/Libraries/Esiur/Protocol/EpConnectionProtocol.cs b/Libraries/Esiur/Protocol/EpConnectionProtocol.cs index bfcfd7d..ae6e009 100644 --- a/Libraries/Esiur/Protocol/EpConnectionProtocol.cs +++ b/Libraries/Esiur/Protocol/EpConnectionProtocol.cs @@ -26,10 +26,12 @@ using Esiur.Core; using Esiur.Data; using Esiur.Data.Types; using Esiur.Misc; +using Esiur.Net; using Esiur.Net.Packets; using Esiur.Resource; using Esiur.Security.Authority; using Esiur.Security.Permissions; +using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections; using System.Collections.Generic; @@ -45,32 +47,37 @@ namespace Esiur.Protocol; partial class EpConnection { - KeyList neededResources = new KeyList(); - KeyList> attachedResources = new KeyList>(); - KeyList> suspendedResources = new KeyList>(); + KeyList _neededTypeDefs = new KeyList(); + KeyList _cachedTypeDefs = new KeyList(); + KeyList> _typeDefRequests = new KeyList>(); - KeyList resourceRequests = new KeyList(); - KeyList> typeDefsByIdRequests = new KeyList>(); + //KeyList> _typeDefsByIdRequests = new KeyList>(); - KeyList> typeDefsByNameRequests = new KeyList>(); + KeyList _neededResources = new KeyList(); + KeyList> _attachedResources = new KeyList>(); + KeyList> _suspendedResources = new KeyList>(); + KeyList> _resourceRequests = new KeyList>(); + //KeyList> _typeDefsByIdRequests = new KeyList>(); + + //KeyList> _typeDefsByNameRequests = new KeyList>(); - Dictionary typeDefs = new Dictionary(); + //Dictionary typeDefs = new Dictionary(); - object typeDefsLock = new object(); + object _typeDefsLock = new object(); - KeyList requests = new KeyList(); + KeyList _requests = new KeyList(); - volatile int callbackCounter = 0; + volatile int _callbackCounter = 0; - Dictionary> subscriptions = new Dictionary>(); + Dictionary> _subscriptions = new Dictionary>(); // resources might get attached by the client - internal KeyList cache = new(); + internal KeyList _cache = new(); - object subscriptionsLock = new object(); + object _subscriptionsLock = new object(); - AsyncQueue queue = new(); + AsyncQueue _queue = new(); @@ -85,9 +92,9 @@ partial class EpConnection AsyncReply SendRequest(EpPacketRequest action, params object[] args) { var reply = new AsyncReply(); - var c = (uint)Interlocked.Increment(ref callbackCounter); + var c = (uint)Interlocked.Increment(ref _callbackCounter); //callbackCounter++; // avoid thread racing - requests.Add(c, reply); + _requests.Add(c, reply); if (args.Length == 0) { @@ -116,6 +123,68 @@ partial class EpConnection return reply; } + //void SendAuthMaterials(EpAuthPacketMethod method, AuthenticationMaterial[] authenticationMaterials) + //{ + // if (authenticationMaterials != null) + // { + // var authMap = new Map(); + // foreach (var material in authenticationMaterials) + // authMap.Add(material.Type, material.Value); + + // var bl = new BinaryList(); + // bl.AddUInt8((byte)((byte)method | 0x20)); + // bl.AddUInt8Array(Codec.Compose(authMap, Instance.Warehouse, this)); + // Send(bl.ToArray()); + // } + // else + // { + // Send(new byte[] { (byte)method }); + // } + //} + void SendAuthData(EpAuthPacketMethod method, object data) + { + if (data != null) + { + var bl = new BinaryList(); + bl.AddUInt8((byte)((byte)method | 0x20)); + bl.AddUInt8Array(Codec.Compose(data, null, this)); + Send(bl.ToArray()); + } + else + { + Send(new byte[] { (byte)method }); + } + } + + void SendAuth(EpAuthPacketMethod method) + { + Send(new byte[] { (byte)method }); + } + + void SendAuthHeaders(EpAuthPacketMethod method, + Map authHeaders) + { + if (authHeaders != null) + { + //var authMap = new Map(); + + //foreach (var header in authHeaders) + //{ + // authMap.Add(header.Key, header.Value); + //} + + var bl = new BinaryList(); + bl.AddUInt8((byte)((byte)method | 0x20)); + bl.AddUInt8Array(Codec.Compose(authHeaders, null, this)); + Send(bl.ToArray()); + } + else + { + Send(new byte[] { (byte)method }); + } + } + + /// /// Send EP notification. /// @@ -191,7 +260,7 @@ partial class EpConnection } - public AsyncReply StaticCall(Uuid typeId, byte index, object parameters) + public AsyncReply StaticCall(ulong typeId, byte index, object parameters) { return SendRequest(EpPacketRequest.StaticCall, typeId, index, parameters); } @@ -227,15 +296,15 @@ partial class EpConnection { var sendDetach = false; - if (attachedResources.ContainsKey(instanceId)) + if (_attachedResources.ContainsKey(instanceId)) { - attachedResources.Remove(instanceId); + _attachedResources.Remove(instanceId); sendDetach = true; } - if (suspendedResources.ContainsKey(instanceId)) + if (_suspendedResources.ContainsKey(instanceId)) { - suspendedResources.Remove(instanceId); + _suspendedResources.Remove(instanceId); sendDetach = true; } @@ -275,9 +344,9 @@ partial class EpConnection SendReply(EpPacketReply.Chunk, callbackId, chunk); } - void EpReplyCompleted(uint callbackId, ParsedTdu dataType) + void EpReplyCompleted(uint callbackId, PlainTdu tdu) { - var req = requests.Take(callbackId); + var req = _requests.Take(callbackId); //Console.WriteLine("Completed " + callbackId); @@ -287,33 +356,40 @@ partial class EpConnection return; } - var (_, parsed) = Codec.ParseAsync(dataType, this, null); - if (parsed is AsyncReply reply) + var pr = Codec.Parse(tdu, this, null); + + if (pr is AsyncReply asyncReply) { - reply.Then(result => - { - req.Trigger(result); - }) - .Error(e => - { - //Console.WriteLine(callbackId + ": failed"); - req.TriggerError(e); - }); + asyncReply.Then(req.Trigger) + .Error(req.TriggerError); } else { - req.Trigger(parsed); + req.Trigger(pr); } + + //var pr = Codec.ParseAsync(dataType, this, null).Then(pr => + //{ + // if (pr.Value is AsyncReply asyncReply) + // { + // asyncReply.Then(req.Trigger) + // .Error(req.TriggerError); + // } + // else + // { + // req.Trigger(pr.Value); + // } + //}).Error(req.TriggerError); } - void EpExtensionAction(byte actionId, ParsedTdu? dataType, byte[] data) + void EpExtensionAction(byte actionId, PlainTdu? tdu) { // nothing is supported now } - void EpReplyPropagated(uint callbackId, ParsedTdu dataType, byte[] data) + void EpReplyPropagated(uint callbackId, PlainTdu tdu) { - var req = requests[callbackId]; + var req = _requests[callbackId]; if (req == null) { @@ -321,23 +397,35 @@ partial class EpConnection return; } - var (_, parsed) = Codec.ParseAsync(dataType, this, null); - if (parsed is AsyncReply reply) + var value = Codec.Parse(tdu, this, null); + + if (value is AsyncReply reply) { - reply.Then(result => - { - req.TriggerPropagation(result); - }); + reply.Then(req.TriggerPropagation) + .Error(req.TriggerError); } else { - req.TriggerPropagation(parsed); + req.TriggerPropagation(value); } + + //var pr = Codec.ParseAsync(dataType, this, null).Then(pr => + //{ + // if (pr.Value is AsyncReply reply) + // { + // reply.Then(req.TriggerPropagation) + // .Error(req.TriggerError); + // } + // else + // { + // req.TriggerPropagation(pr.Value); + // } + //}).Error(req.TriggerError); } - void EpReplyError(uint callbackId, ParsedTdu dataType, byte[] data, ErrorType type) + void EpReplyError(uint callbackId, PlainTdu plainTdu, ErrorType type) { - var req = requests.Take(callbackId); + var req = _requests.Take(callbackId); if (req == null) { @@ -345,18 +433,19 @@ partial class EpConnection return; } - var args = DataDeserializer.ListParser(dataType, Instance.Warehouse) + var tdu = ParsedTdu.ParseSync(plainTdu.Data, plainTdu.TduOffset, plainTdu.Ends, Instance.Warehouse); + var args = DataDeserializer.ListParser(tdu, Instance.Warehouse) as object[]; - var errorCode =Convert.ToUInt16( args[0]); + var errorCode = Convert.ToUInt16(args[0]); var errorMsg = (string)args[1]; req.TriggerError(new AsyncException(type, errorCode, errorMsg)); } - void EpReplyProgress(uint callbackId, ParsedTdu dataType, byte[] data) + void EpReplyProgress(uint callbackId, PlainTdu plainTdu) { - var req = requests[callbackId]; + var req = _requests[callbackId]; if (req == null) { @@ -364,7 +453,8 @@ partial class EpConnection return; } - var args = DataDeserializer.ListParser(dataType, Instance.Warehouse) + var tdu = ParsedTdu.ParseSync(plainTdu.Data, plainTdu.TduOffset, plainTdu.Ends, Instance.Warehouse); + var args = DataDeserializer.ListParser(tdu, Instance.Warehouse) as object[]; var current = (uint)args[0]; @@ -373,9 +463,9 @@ partial class EpConnection req.TriggerProgress(ProgressType.Execution, current, total); } - void EpReplyWarning(uint callbackId, ParsedTdu dataType, byte[] data) + void EpReplyWarning(uint callbackId, PlainTdu plainTdu) { - var req = requests[callbackId]; + var req = _requests[callbackId]; if (req == null) { @@ -383,7 +473,8 @@ partial class EpConnection return; } - var args = DataDeserializer.ListParser(dataType, Instance.Warehouse) + var tdu = ParsedTdu.ParseSync(plainTdu.Data, plainTdu.TduOffset, plainTdu.Ends, Instance.Warehouse); + var args = DataDeserializer.ListParser(tdu, Instance.Warehouse) as object[]; var level = (byte)args[0]; @@ -394,116 +485,136 @@ partial class EpConnection - void EpReplyChunk(uint callbackId, ParsedTdu dataType) + void EpReplyChunk(uint callbackId, PlainTdu tdu) { - var req = requests[callbackId]; + var req = _requests[callbackId]; if (req == null) return; - var (_, parsed) = Codec.ParseAsync(dataType, this, null); + var value = Codec.Parse(tdu, this, null); - if (parsed is AsyncReply reply) - reply.Then(result => req.TriggerChunk(result)); + if (value is AsyncReply asyncReply) + { + asyncReply.Then(req.TriggerChunk) + .Error(req.TriggerError); + } else - req.TriggerChunk(parsed); + { + req.TriggerChunk(value); + } + + //Codec.ParseAsync(dataType, this, null).Then(pr => + //{ + // if (pr.Value is AsyncReply asyncReply) + // { + // asyncReply.Then(req.TriggerChunk) + // .Error(req.TriggerError); + // } + // else + // { + // req.TriggerChunk(pr.Value); + // } + //}).Error(req.TriggerError); } - void EpNotificationResourceReassigned(ParsedTdu dataType) + void EpNotificationResourceReassigned(PlainTdu dataType) { // uint resourceId, uint newResourceId } - void EpNotificationResourceMoved(ParsedTdu dataType, byte[] data) { } + void EpNotificationResourceMoved(PlainTdu tdu) { } - void EpNotificationSystemFailure(ParsedTdu dataType, byte[] data) { } + void EpNotificationSystemFailure(PlainTdu tdu) { } - void EpNotificationResourceDestroyed(ParsedTdu dataType, byte[] data) + void EpNotificationResourceDestroyed(PlainTdu tdu) { - var (size, rt) = Codec.ParseSync(dataType, Instance.Warehouse); + var (size, rt) = Codec.ParseSync(tdu.Data, tdu.TduOffset, Instance.Warehouse); var resourceId = Convert.ToUInt32(rt); - if (attachedResources.Contains(resourceId)) + if (_attachedResources.Contains(resourceId)) { EpResource r; - if (attachedResources[resourceId].TryGetTarget(out r)) + if (_attachedResources[resourceId].TryGetTarget(out r)) { // remove from attached to avoid sending unnecessary detach request when Destroy() is called - attachedResources.Remove(resourceId); + _attachedResources.Remove(resourceId); r.Destroy(); } else { - attachedResources.Remove(resourceId); + _attachedResources.Remove(resourceId); } } - else if (neededResources.Contains(resourceId)) + else if (_neededResources.Contains(resourceId)) { // @TODO: handle this mess - neededResources.Remove(resourceId); + _neededResources.Remove(resourceId); } } - void EpNotificationPropertyModified(ParsedTdu dataType) + void EpNotificationPropertyModified(PlainTdu tdu) { // resourceId, index, value var (valueOffset, valueSize, args) = - DataDeserializer.LimitedCountListParser(dataType.Data, dataType.Offset, dataType.ContentLength, Instance.Warehouse, 2); + DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, tdu.PayloadLength, Instance.Warehouse, 2); var rid = Convert.ToUInt32(args[0]); var index = (byte)args[1]; - Fetch(rid, null).Then(r => + FetchResource(rid, null).Then(r => { var pt = r.Instance.Definition.GetPropertyDefByIndex(index); if (pt == null) return; - - var (_, parsed) = Codec.ParseAsync(dataType.Data, valueOffset, this, null); - - if (parsed is AsyncReply) + Codec.ParseAsync(tdu.Data, valueOffset, this, null).Then(pr => { - var item = new AsyncReply(); - queue.Add(item); - - (parsed as AsyncReply).Then((result) => + if (pr.Value is AsyncReply asyncReply) { - item.Trigger(new EpResourceQueueItem((EpResource)r, - EpResourceQueueItem.DistributedResourceQueueItemType.Propery, - result, index)); - }); - } - else - { - queue.Add(new AsyncReply(new EpResourceQueueItem((EpResource)r, - EpResourceQueueItem.DistributedResourceQueueItemType.Propery, - parsed, index))); + var item = new AsyncReply(); + _queue.Add(item); + + asyncReply.Then((result) => + { + item.Trigger(new EpResourceQueueItem((EpResource)r, + EpResourceQueueItem.DistributedResourceQueueItemType.Propery, + result, index)); + }); + } + else + { + _queue.Add(new AsyncReply(new EpResourceQueueItem((EpResource)r, + EpResourceQueueItem.DistributedResourceQueueItemType.Propery, + pr.Value, index))); + } + + }).Error((ex) => + { + //.Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); + throw ex; + }); - //item.Trigger(new DistributedResourceQueueItem((DistributedResource)r, - // DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery, - // parsed, index)); - } }); } - void EpNotificationEventOccurred(ParsedTdu dataType, byte[] data) + void EpNotificationEventOccurred(PlainTdu tdu) { // resourceId, index, value var (valueOffset, valueSize, args) = - DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse, 2); + DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse, 2); var resourceId = Convert.ToUInt32(args[0]); var index = (byte)args[1]; - Fetch(resourceId, null).Then(r => + FetchResource(resourceId, null).Then(r => { var et = r.Instance.Definition.GetEventDefByIndex(index); @@ -512,40 +623,44 @@ partial class EpConnection // push to the queue to guarantee serialization var item = new AsyncReply(); - queue.Add(item); + _queue.Add(item); - var (_, parsed) = Codec.ParseAsync(data, valueOffset, this, null); - - if (parsed is AsyncReply) + Codec.ParseAsync(tdu.Data, valueOffset, this, null).Then(pr => { - (parsed as AsyncReply).Then((result) => + if (pr.Value is AsyncReply asyncReply) + { + asyncReply.Then((result) => + { + item.Trigger(new EpResourceQueueItem((EpResource)r, + EpResourceQueueItem.DistributedResourceQueueItemType.Event, result, index)); + }); + } + else { item.Trigger(new EpResourceQueueItem((EpResource)r, - EpResourceQueueItem.DistributedResourceQueueItemType.Event, result, index)); - }); - } - else - { - item.Trigger(new EpResourceQueueItem((EpResource)r, - EpResourceQueueItem.DistributedResourceQueueItemType.Event, parsed, index)); - } + EpResourceQueueItem.DistributedResourceQueueItemType.Event, pr.Value, index)); + } + + }).Error((ex) => throw ex); + // @TODO: Send general error + //.Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); }); } void EpEventRenamed(uint resourceId, string name) { - Fetch(resourceId, null).Then(resource => + FetchResource(resourceId, null).Then(resource => { resource.Instance.Variables["name"] = name; }); } - void EpRequestAttachResource(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestAttachResource(uint callback, PlainTdu tdu) { - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); var resourceId = Convert.ToUInt32(value); @@ -553,7 +668,7 @@ partial class EpConnection { if (res != null) { - if (res.Instance.Applicable(session, ActionType.Attach, null) == Ruling.Denied) + if (res.Instance.Applicable(_session, ActionType.Attach, null) == Ruling.Denied) { SendError(ErrorType.Management, callback, 6); return; @@ -584,12 +699,12 @@ partial class EpConnection }); } - void EpRequestReattachResource(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestReattachResource(uint callback, PlainTdu tdu) { // resourceId, index, value var (valueOffset, valueSize, args) = - DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse, 2); + DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse, 2); var resourceId = Convert.ToUInt32(args[0]); @@ -599,7 +714,7 @@ partial class EpConnection { if (res != null) { - if (res.Instance.Applicable(session, ActionType.Attach, null) == Ruling.Denied) + if (res.Instance.Applicable(_session, ActionType.Attach, null) == Ruling.Denied) { SendError(ErrorType.Management, callback, 6); return; @@ -632,10 +747,9 @@ partial class EpConnection }); } - void EpRequestDetachResource(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestDetachResource(uint callback, PlainTdu tdu) { - - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); var resourceId = Convert.ToUInt32(value); @@ -647,7 +761,7 @@ partial class EpConnection // unsubscribe Unsubscribe(res); // remove from cache - cache.Remove(res); + _cache.Remove(res); // remove from attached resources //attachedResources.Remove(res); @@ -663,71 +777,82 @@ partial class EpConnection }); } - void EpRequestCreateResource(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestCreateResource(uint callback, PlainTdu tdu) { - var (_, parsed) = Codec.ParseAsync(dataType, this, null); - - var args = (object[])parsed; - - var path = (string)args[0]; - - TypeDef type = null; - - if (args[1] is Uuid) - type = Instance.Warehouse.GetTypeDefById((Uuid)args[1]); - else if (args[1] is string) - type = Instance.Warehouse.GetTypeDefByName((string)args[1]); - - if (type == null) + Codec.ParseAsync(tdu.Data, tdu.TduOffset, this, null).Then(pr => { - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ClassNotFound); - return; - } + var args = (object[])pr.Value; - var props = (Map)((object[])args)[2]; - var attrs = (Map)((object[])args)[3]; + var path = (string)args[0]; - // Get store - var sc = path.Split('/'); + TypeDef typeDef = null; - Instance.Warehouse.Get(string.Join("/", sc.Take(sc.Length - 1))) - .Then(r => - { - if (r == null) + if (args[1] is uint || args[1] is byte || args[1] is ushort) // @TODO: this is a mess, we should have a better way to distinguish between type id and name + typeDef = Instance.Warehouse.GetLocalTypeDefById(Convert.ToUInt64(args[1])); + else if (args[1] is string) + typeDef = Instance.Warehouse.GetLocalTypeDefByName((string)args[1]); + + if (typeDef == null || typeDef is not LocalTypeDef) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ClassNotFound); + return; + } + + var localTypeDef = typeDef as LocalTypeDef; + + + var props = (Map)((object[])args)[2]; + var attrs = (Map)((object[])args)[3]; + + // Get store + var sc = path.Split('/'); + + Instance.Warehouse.Get(string.Join("/", sc.Take(sc.Length - 1))) + .Then(r => { - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.StoreNotFound); - return; - } + if (r == null) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.StoreNotFound); + return; + } - var store = r.Instance.Store; + var store = r.Instance.Store; - // check security - if (store.Instance.Applicable(session, ActionType.CreateResource, null) != Ruling.Allowed) - { - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.CreateDenied); - return; - } + // check security + if (store.Instance.Applicable(_session, ActionType.CreateResource, null) != Ruling.Allowed) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.CreateDenied); + return; + } - Instance.Warehouse.New(type.DefinedType, path, null, attrs, props).Then(resource => - { - SendReply(EpPacketReply.Completed, callback, resource.Instance.Id); + Instance.Warehouse.New(localTypeDef.DefinedType, path, + new ResourceContext(0, + attrs, + props.Select(x => new KeyValuePair + (localTypeDef.GetPropertyDefByIndex(x.Key).Name, x.Value)), + null)) + .Then(resource => + { + SendReply(EpPacketReply.Completed, callback, resource.Instance.Id); + + }).Error(e => + { + SendError(e.Type, callback, (ushort)e.Code, e.Message); + }); }).Error(e => { SendError(e.Type, callback, (ushort)e.Code, e.Message); }); - }).Error(e => - { - SendError(e.Type, callback, (ushort)e.Code, e.Message); - }); + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); } - void EpRequestDeleteResource(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestDeleteResource(uint callback, PlainTdu tdu) { - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); var resourceId = Convert.ToUInt32(value); @@ -739,7 +864,7 @@ partial class EpConnection return; } - if (r.Instance.Store.Instance.Applicable(session, ActionType.Delete, null) != Ruling.Allowed) + if (r.Instance.Store.Instance.Applicable(_session, ActionType.Delete, null) != Ruling.Allowed) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.DeleteDenied); return; @@ -753,11 +878,11 @@ partial class EpConnection }); } - void EpRequestMoveResource(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestMoveResource(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse); var resourceId = Convert.ToUInt32(args[0]); @@ -778,7 +903,7 @@ partial class EpConnection return; } - if (resource.Instance.Applicable(this.session, ActionType.Rename, null) != Ruling.Allowed) + if (resource.Instance.Applicable(this._session, ActionType.Rename, null) != Ruling.Allowed) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.RenameDenied); return; @@ -794,14 +919,14 @@ partial class EpConnection - void EpRequestToken(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestToken(uint callback, PlainTdu tdu) { // @TODO: To be implemented } - void EpRequestLinkTypeDefs(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestLinkTypeDefs(uint callback, PlainTdu tdu) { - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); var resourceLink = (string)value; @@ -813,17 +938,24 @@ partial class EpConnection return; } - if (r.Instance.Applicable(session, ActionType.ViewTypeDef, null) == Ruling.Denied) + if (r.Instance.Applicable(_session, ActionType.ViewTypeDef, null) == Ruling.Denied) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAllowed); return; } - var typeDefs = TypeDef.GetDependencies(r.Instance.Definition, Instance.Warehouse); - - // Send - SendReply(EpPacketReply.Completed, callback, typeDefs.Select(x => x.Content).ToArray()); - + // make sure the resource is a local type def. + if (r.Instance.Definition is LocalTypeDef localTypeDef) + { + var typeDefs = LocalTypeDef.GetDependencies(localTypeDef, Instance.Warehouse); + // Send + SendReply(EpPacketReply.Completed, callback, typeDefs.Select(x => x.Compose(this)).ToArray()); + } + else + { + // @TODO: Add support for remote type defs + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotSupported); + } }; if (Server?.EntryPoint != null) @@ -832,17 +964,17 @@ partial class EpConnection Instance.Warehouse.Query(resourceLink).Then(queryCallback); } - void EpRequestTypeDefByName(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestTypeDefByName(uint callback, PlainTdu tdu) { - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); var className = (string)value; - var t = Instance.Warehouse.GetTypeDefByName(className); + var typeDef = Instance.Warehouse.GetRemoteTypeDefByName(_remoteDomain, className); - if (t != null) + if (typeDef != null) { - SendReply(EpPacketReply.Completed, callback, t.Content); + SendReply(EpPacketReply.Completed, callback, typeDef.Compose(this)); } else { @@ -851,18 +983,18 @@ partial class EpConnection } } - void EpRequestTypeDefById(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestTypeDefById(uint callback, PlainTdu tdu) { - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); - var typeId = (Uuid)value; + var typeId = Convert.ToUInt32(value); - var t = Instance.Warehouse.GetTypeDefById(typeId); + var t = Instance.Warehouse.GetLocalTypeDefById(typeId); if (t != null) { - SendReply(EpPacketReply.Completed, callback, t.Content); + SendReply(EpPacketReply.Completed, callback, t.Compose(this)); } else { @@ -873,10 +1005,10 @@ partial class EpConnection - void EpRequestTypeDefByResourceId(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestTypeDefByResourceId(uint callback, PlainTdu tdu) { - var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); + var value = Codec.ParseSync(tdu, Instance.Warehouse); var resourceId = Convert.ToUInt32(value); @@ -884,7 +1016,7 @@ partial class EpConnection { if (r != null) { - SendReply(EpPacketReply.Completed, callback, r.Instance.Definition.Content); + SendReply(EpPacketReply.Completed, callback, r.Instance.Definition.Compose(this)); } else { @@ -896,9 +1028,9 @@ partial class EpConnection - void EpRequestGetResourceIdByLink(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestGetResourceIdByLink(uint callback, PlainTdu tdu) { - var (_, parsed) = Codec.ParseSync(dataType, Instance.Warehouse); + var parsed = Codec.ParseSync(tdu, Instance.Warehouse); var resourceLink = (string)parsed; Action queryCallback = (r) => @@ -907,7 +1039,7 @@ partial class EpConnection SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); else { - if (r.Instance.Applicable(session, ActionType.Attach, null) == Ruling.Denied) + if (r.Instance.Applicable(_session, ActionType.Attach, null) == Ruling.Denied) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); return; @@ -924,9 +1056,9 @@ partial class EpConnection } - void EpRequestQueryResources(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestQueryResources(uint callback, PlainTdu tdu) { - var (_, parsed) = Codec.ParseSync(dataType, Instance.Warehouse); + var parsed = Codec.ParseSync(tdu, Instance.Warehouse); var resourceLink = (string)parsed; @@ -936,7 +1068,7 @@ partial class EpConnection SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); else { - if (r.Instance.Applicable(session, ActionType.Attach, null) == Ruling.Denied) + if (r.Instance.Applicable(_session, ActionType.Attach, null) == Ruling.Denied) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAllowed); return; @@ -944,7 +1076,7 @@ partial class EpConnection r.Instance.Children().Then(children => { - var list = children.Where(x => x.Instance.Applicable(session, ActionType.Attach, null) != Ruling.Denied).ToArray(); + var list = children.Where(x => x.Instance.Applicable(_session, ActionType.Attach, null) != Ruling.Denied).ToArray(); SendReply(EpPacketReply.Completed, callback, list); }).Error(e => { @@ -982,14 +1114,13 @@ partial class EpConnection } - void EpRequestProcedureCall(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestProcedureCall(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse, 1); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse, 1); var procedureCall = (string)args[0]; - if (Server == null) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotSupported); @@ -1004,55 +1135,54 @@ partial class EpConnection return; } - var (_, parsed) = Codec.ParseAsync(data, offset, this, null); - - if (parsed is AsyncReply reply) + Codec.ParseAsync(tdu.Data, offset, this, null).Then(pr => { - reply.Then(results => + if (pr.Value is AsyncReply reply) { - //var arguments = (Map)results; + reply.Then(results => + { + //var arguments = (Map)results; + + // un hold the socket to send data immediately + this.Socket.Unhold(); + + // @TODO: Make managers for procedure calls + //if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied) + //{ + // SendError(ErrorType.Management, callback, + // (ushort)ExceptionCode.InvokeDenied); + // return; + //} + + InvokeFunction(call.Value.Definition, callback, results, EpPacketRequest.ProcedureCall, call.Value.Delegate.Target); + + }).Error(x => + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError); + }); + } + else + { + //var arguments = (Map)parsed; // un hold the socket to send data immediately this.Socket.Unhold(); // @TODO: Make managers for procedure calls - //if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied) - //{ - // SendError(ErrorType.Management, callback, - // (ushort)ExceptionCode.InvokeDenied); - // return; - //} - - InvokeFunction(call.Value.Definition, callback, results, EpPacketRequest.ProcedureCall, call.Value.Delegate.Target); - - }).Error(x => - { - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError); - }); - } - else - { - //var arguments = (Map)parsed; - - // un hold the socket to send data immediately - this.Socket.Unhold(); - - // @TODO: Make managers for procedure calls - InvokeFunction(call.Value.Definition, callback, parsed, EpPacketRequest.ProcedureCall, call.Value.Delegate.Target); - } + InvokeFunction(call.Value.Definition, callback, pr.Value, EpPacketRequest.ProcedureCall, call.Value.Delegate.Target); + } + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); } - void EpRequestStaticCall(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestStaticCall(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse, 2); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse, 2); - var typeId = new Uuid((byte[])args[0]); + var typeId = Convert.ToUInt32(args[0]); var index = (byte)args[1]; - - var typeDef = Instance.Warehouse.GetTypeDefById(typeId); - + var typeDef = Instance.Warehouse.GetLocalTypeDefById(typeId); if (typeDef == null) { @@ -1078,51 +1208,52 @@ partial class EpConnection return; } - var (_, parsed) = Codec.ParseAsync(data, offset, this, null); - - if (parsed is AsyncReply reply) + Codec.ParseAsync(tdu.Data, offset, this, null).Then(pr => { - reply.Then(results => + if (pr.Value is AsyncReply reply) { - //var arguments = (Map)results; + reply.Then(results => + { + //var arguments = (Map)results; + + // un hold the socket to send data immediately + this.Socket.Unhold(); + + + // @TODO: Make managers for static calls + //if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied) + //{ + // SendError(ErrorType.Management, callback, + // (ushort)ExceptionCode.InvokeDenied); + // return; + //} + + InvokeFunction(fd, callback, results, EpPacketRequest.StaticCall, null); + + }).Error(x => + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError); + }); + } + else + { + //var arguments = (Map)parsed; // un hold the socket to send data immediately this.Socket.Unhold(); - // @TODO: Make managers for static calls - //if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied) - //{ - // SendError(ErrorType.Management, callback, - // (ushort)ExceptionCode.InvokeDenied); - // return; - //} - - InvokeFunction(fd, callback, results, EpPacketRequest.StaticCall, null); - - }).Error(x => - { - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError); - }); - } - else - { - //var arguments = (Map)parsed; - - // un hold the socket to send data immediately - this.Socket.Unhold(); - - // @TODO: Make managers for static calls - InvokeFunction(fd, callback, parsed, EpPacketRequest.StaticCall, null); - } + InvokeFunction(fd, callback, pr.Value, EpPacketRequest.StaticCall, null); + } + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); } - void EpRequestInvokeFunction(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestInvokeFunction(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse, 2); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse, 2); var resourceId = Convert.ToUInt32(args[0]); var index = (byte)args[1]; @@ -1145,20 +1276,56 @@ partial class EpConnection return; } - var (_, parsed) = Codec.ParseAsync(data, offset, this, null); - - if (parsed is AsyncReply) + Codec.ParseAsync(tdu.Data, offset, this, null).Then(pr => { - (parsed as AsyncReply).Then(result => + if (pr.Value is AsyncReply asyncReply) { - // var arguments = result; + asyncReply.Then(result => + { + // var arguments = result; + + // un hold the socket to send data immediately + this.Socket.Unhold(); + + if (r is EpResource) + { + var rt = (r as EpResource)._Invoke(index, result); + if (rt != null) + { + rt.Then(res => + { + SendReply(EpPacketReply.Completed, callback, res); + }); + } + else + { + // function not found on a distributed object + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.MethodNotFound); + } + } + else + { + if (r.Instance.Applicable(_session, ActionType.Execute, ft) == Ruling.Denied) + { + SendError(ErrorType.Management, callback, + (ushort)ExceptionCode.InvokeDenied); + return; + } + + InvokeFunction(ft, callback, result, EpPacketRequest.InvokeFunction, r); + } + }); + } + else + { + //var arguments = (Map)parsed; // un hold the socket to send data immediately this.Socket.Unhold(); if (r is EpResource) { - var rt = (r as EpResource)._Invoke(index, result); + var rt = (r as EpResource)._Invoke(index, pr.Value); if (rt != null) { rt.Then(res => @@ -1174,54 +1341,18 @@ partial class EpConnection } else { - if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied) + if (r.Instance.Applicable(_session, ActionType.Execute, ft) == Ruling.Denied) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.InvokeDenied); return; } - InvokeFunction(ft, callback, result, EpPacketRequest.InvokeFunction, r); - } - }); - } - else - { - //var arguments = (Map)parsed; - - // un hold the socket to send data immediately - this.Socket.Unhold(); - - if (r is EpResource) - { - var rt = (r as EpResource)._Invoke(index, parsed); - if (rt != null) - { - rt.Then(res => - { - SendReply(EpPacketReply.Completed, callback, res); - }); - } - else - { - // function not found on a distributed object - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.MethodNotFound); + InvokeFunction(ft, callback, pr.Value, EpPacketRequest.InvokeFunction, r); } } - else - { - if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied) - { - SendError(ErrorType.Management, callback, - (ushort)ExceptionCode.InvokeDenied); - return; - } - - InvokeFunction(ft, callback, parsed, EpPacketRequest.InvokeFunction, r); - } - - } - }); + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); ; + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); ; } @@ -1471,11 +1602,11 @@ partial class EpConnection } } - void EpRequestSubscribe(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestSubscribe(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse); var resourceId = Convert.ToUInt32(args[0]); var index = (byte)args[1]; @@ -1507,21 +1638,21 @@ partial class EpConnection } else { - lock (subscriptionsLock) + lock (_subscriptionsLock) { - if (!subscriptions.ContainsKey(r)) + if (!_subscriptions.ContainsKey(r)) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAttached); return; } - if (subscriptions[r].Contains(index)) + if (_subscriptions[r].Contains(index)) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AlreadyListened); return; } - subscriptions[r].Add(index); + _subscriptions[r].Add(index); SendReply(EpPacketReply.Completed, callback); } @@ -1530,11 +1661,11 @@ partial class EpConnection } - void EpRequestUnsubscribe(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestUnsubscribe(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse); var resourceId = Convert.ToUInt32(args[0]); var index = (byte)args[1]; @@ -1566,21 +1697,21 @@ partial class EpConnection } else { - lock (subscriptionsLock) + lock (_subscriptionsLock) { - if (!subscriptions.ContainsKey(r)) + if (!_subscriptions.ContainsKey(r)) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAttached); return; } - if (!subscriptions[r].Contains(index)) + if (!_subscriptions[r].Contains(index)) { SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AlreadyUnsubscribed); return; } - subscriptions[r].Remove(index); + _subscriptions[r].Remove(index); SendReply(EpPacketReply.Completed, callback); } @@ -1591,11 +1722,11 @@ partial class EpConnection - void EpRequestSetProperty(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestSetProperty(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse, 2); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse, 2); var rid = (uint)args[0]; var index = (byte)args[1]; @@ -1622,23 +1753,37 @@ partial class EpConnection } - if (r is IDynamicResource) + if (r is IDynamicResource dynamicResource) { - var (_, parsed) = Codec.ParseAsync(data, offset, this, null); - if (parsed is AsyncReply) + Codec.ParseAsync(tdu.Data, offset, this, null).Then(pr => { - (parsed as AsyncReply).Then((value) => + if (pr.Value is AsyncReply asyncReply) + { + asyncReply.Then((value) => + { + // propagation + dynamicResource.SetResourcePropertyAsync(index, value).Then((x) => + { + SendReply(EpPacketReply.Completed, callback); + }).Error(x => + { + SendError(x.Type, callback, (ushort)x.Code, x.Message); + }); + }); + } + else { // propagation - (r as IDynamicResource).SetResourcePropertyAsync(index, value).Then((x) => + dynamicResource.SetResourcePropertyAsync(index, pr.Value).Then((x) => { SendReply(EpPacketReply.Completed, callback); }).Error(x => { SendError(x.Type, callback, (ushort)x.Code, x.Message); }); - }); - } + } + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); ; + } else { @@ -1650,7 +1795,7 @@ partial class EpConnection return; } - if (r.Instance.Applicable(session, ActionType.SetProperty, pt, this) == Ruling.Denied) + if (r.Instance.Applicable(_session, ActionType.SetProperty, pt, this) == Ruling.Denied) { SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.SetPropertyDenied); return; @@ -1662,16 +1807,46 @@ partial class EpConnection return; } - var (_, parsed) = Codec.ParseAsync(data, offset, this, null); - - if (parsed is AsyncReply) + Codec.ParseAsync(tdu.Data, offset, this, null).Then(pr => { - (parsed as AsyncReply).Then((value) => + + + + if (pr.Value is AsyncReply asyncReply) { + asyncReply.Then((value) => + { + if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() + == typeof(PropertyContext<>)) + { + value = Activator.CreateInstance(pi.PropertyType, this, value); + } + else + { + // cast new value type to property type + value = RuntimeCaster.Cast(value, pi.PropertyType); + } + + try + { + pi.SetValue(r, value); + SendReply(EpPacketReply.Completed, callback); + } + catch (Exception ex) + { + SendError(ErrorType.Exception, callback, 0, ex.Message); + } + }); + } + else + { + var value = pr.Value; + if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(PropertyContext<>)) { value = Activator.CreateInstance(pi.PropertyType, this, value); + //value = new DistributedPropertyContext(this, value); } else { @@ -1688,105 +1863,85 @@ partial class EpConnection { SendError(ErrorType.Exception, callback, 0, ex.Message); } - }); - } - else - { - if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() - == typeof(PropertyContext<>)) - { - parsed = Activator.CreateInstance(pi.PropertyType, this, parsed); - //value = new DistributedPropertyContext(this, value); } - else - { - // cast new value type to property type - parsed = RuntimeCaster.Cast(parsed, pi.PropertyType); - } - - try - { - pi.SetValue(r, parsed); - SendReply(EpPacketReply.Completed, callback); - } - catch (Exception ex) - { - SendError(ErrorType.Exception, callback, 0, ex.Message); - } - } + }).Error(x => SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ParseError)); } }); } /// - /// Get the TypeSchema for a given type Id. + /// Get the TypeDef for a given type Id. /// /// Type UUID. - /// TypeSchema. - public AsyncReply GetTypeDefById(Uuid typeId) - { - lock (typeDefsLock) - { - if (typeDefs.ContainsKey(typeId)) - return new AsyncReply(typeDefs[typeId]); - else if (typeDefsByIdRequests.ContainsKey(typeId)) - return typeDefsByIdRequests[typeId]; + /// TypeDef. + //public AsyncReply GetTypeDefById(ulong typeId) + //{ + // lock (_typeDefsLock) + // { + // if (_remoteTypeDefs.ContainsKey(typeId)) + // return new AsyncReply(_remoteTypeDefs[typeId]); + // else if (_typeDefsByIdRequests.ContainsKey(typeId)) + // return _typeDefsByIdRequests[typeId]; - var reply = new AsyncReply(); - typeDefsByIdRequests.Add(typeId, reply); + // var reply = new AsyncReply(); + // _typeDefsByIdRequests.Add(typeId, reply); - SendRequest(EpPacketRequest.TypeDefById, typeId) - .Then((result) => - { - var tt = TypeDef.Parse((byte[])result); - typeDefsByIdRequests.Remove(typeId); - typeDefs.Add(tt.Id, tt); - Instance.Warehouse.TryRegisterTypeDef(tt); - reply.Trigger(tt); + // SendRequest(EpPacketRequest.TypeDefById, typeId) + // .Then((result) => + // { + // // @TODO: Solve for dependency deadlock + // RemoteTypeDef.Parse(_remoteDomain, (byte[])result, this).Then(td => + // { + // _typeDefsByIdRequests.Remove(typeId); + // _remoteTypeDefs.Add(td.Id, td); + // // register all remote TypeDefs to warehouse to be used in future parsing before the actual request for them arrives. + // Instance.Warehouse.TryRegisterRemoteTypeDef(_remoteDomain, td); + // reply.Trigger(td); + // }); + // }).Error((ex) => + // { + // reply.TriggerError(ex); + // }); - }).Error((ex) => - { - reply.TriggerError(ex); - }); - - return reply; - } - } + // return reply; + // } + //} - public AsyncReply GetTypeDefByName(string typeName) - { - lock (typeDefsLock) - { - var typeDef = typeDefs.Values.FirstOrDefault(x => x.Name == typeName); - if (typeDef != null) - return new AsyncReply(typeDef); + //public AsyncReply GetTypeDefByName(string typeName) + //{ + // lock (_typeDefsLock) + // { + // var typeDef = _remoteTypeDefs.Values.FirstOrDefault(x => x.Name == typeName); + // if (typeDef != null) + // return new AsyncReply(typeDef); - if (typeDefsByNameRequests.ContainsKey(typeName)) - return typeDefsByNameRequests[typeName]; + // if (_typeDefsByNameRequests.ContainsKey(typeName)) + // return _typeDefsByNameRequests[typeName]; - var reply = new AsyncReply(); - typeDefsByNameRequests.Add(typeName, reply); + // var reply = new AsyncReply(); + // _typeDefsByNameRequests.Add(typeName, reply); + // SendRequest(EpPacketRequest.TypeDefByName, typeName) + // .Then((result) => + // { + // RemoteTypeDef.Parse(_remoteDomain, (byte[])result, this).Then(td => + // { + // _typeDefsByNameRequests.Remove(typeName); + // _remoteTypeDefs.Add(td.Id, td); + // Instance.Warehouse.TryRegisterRemoteTypeDef(_remoteDomain, td); + // reply.Trigger(td); + // }); - SendRequest(EpPacketRequest.TypeDefByName, typeName) - .Then((result) => - { - var tt = TypeDef.Parse((byte[])result); + // }).Error((ex) => + // { + // reply.TriggerError(ex); + // }); - typeDefsByNameRequests.Remove(typeName); - typeDefs.Add(tt.Id, tt); - Instance.Warehouse.RegisterTypeDef(tt); - reply.Trigger(tt); - }).Error((ex) => - { - reply.TriggerError(ex); - }); - - return reply; - } - } + // return reply; + // } + //} // IStore interface /// @@ -1826,30 +1981,32 @@ partial class EpConnection } - public AsyncReply GetLinkDefinitions(string link) + public AsyncReply GetLinkDefinitions(string link) { - var reply = new AsyncReply(); + throw new NotImplementedException(); + + //var reply = new AsyncReply(); - SendRequest(EpPacketRequest.LinkTypeDefs, link) - .Then((result) => - { + //SendRequest(EpPacketRequest.LinkTypeDefs, link) + //.Then((result) => + //{ - var defs = new List(); + // var defs = new List(); - foreach (var def in (byte[][])result) - { - defs.Add(TypeDef.Parse(def)); - } + // foreach (var def in (byte[][])result) + // { + // defs.Add(RemoteTypeDef.Parse(_remoteDomain, def)); + // } - reply.Trigger(defs.ToArray()); + // reply.Trigger(defs.ToArray()); - }).Error((ex) => - { - reply.TriggerError(ex); - }); + //}).Error((ex) => + //{ + // reply.TriggerError(ex); + //}); - return reply; + //return reply; } /// @@ -1858,164 +2015,233 @@ partial class EpConnection /// Resource Id /// DistributedResource /// - object fetchLock = new object(); - public AsyncReply Fetch(uint id, uint[] requestSequence) + //object fetchResourceLock = new object(); + public AsyncReply FetchResource(uint id, uint[] requestSequence) { //lock (fetchLock) //{ - EpResource resource = null; + EpResource resource = null; - attachedResources[id]?.TryGetTarget(out resource); + _attachedResources[id]?.TryGetTarget(out resource); - if (resource != null) - return new AsyncReply(resource); + if (resource != null) + return new AsyncReply(resource); - resource = neededResources[id]; + resource = _neededResources[id]; - var requestInfo = resourceRequests[id]; + var requestInfo = _resourceRequests[id]; - if (requestInfo != null) + if (requestInfo != null) + { + if (resource != null && (requestSequence?.Contains(id) ?? false)) { - if (resource != null && (requestSequence?.Contains(id) ?? false)) - { - // dead lock avoidance for loop reference. - return new AsyncReply(resource); - } - else if (resource != null && requestInfo.RequestSequence.Contains(id)) - { - // dead lock avoidance for dependent reference. - return new AsyncReply(resource); - } - else - { - return requestInfo.Reply; - } - } - else if (resource != null && !resource.DistributedResourceSuspended) - { - // @REVIEW: this should never happen - Global.Log("DCON", LogType.Error, "Resource not moved to attached."); + // dead lock avoidance for loop reference. return new AsyncReply(resource); - } + else if (resource != null && requestInfo.RequestSequence.Contains(id)) + { + // dead lock avoidance for dependent reference. + return new AsyncReply(resource); + } + else + { + return requestInfo.Reply; + } + } + else if (resource != null && !resource.ResourceSuspended) + { + // @REVIEW: this should never happen + Global.Log("DCON", LogType.Error, "Resource not moved to attached."); + return new AsyncReply(resource); - var newSequence = requestSequence != null ? requestSequence.Concat(new uint[] { id }).ToArray() : new uint[] { id }; + } - var reply = new AsyncReply(); - resourceRequests.Add(id, new EpResourceAttachRequestInfo(reply, newSequence)); + var newSequence = requestSequence != null ? requestSequence.Concat(new uint[] { id }).ToArray() : new uint[] { id }; - SendRequest(EpPacketRequest.AttachResource, id) - .Then((result) => + var reply = new AsyncReply(); + _resourceRequests.Add(id, new FetchRequestInfo(reply, newSequence)); + + SendRequest(EpPacketRequest.AttachResource, id) + .Then((result) => + { + if (result == null) { - if (result == null) - { - reply.TriggerError(new AsyncException(ErrorType.Management, + reply.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.ResourceNotFound, "Null response")); - return; - } + return; + } - // TypeId, Age, Link, Hops, PropertyValue[] - var args = (object[])result; - var typeId = (Uuid)args[0]; - var age = Convert.ToUInt64(args[1]); - var link = (string)args[2]; - var hops = (byte)args[3]; - var pvData = (byte[])args[4]; + // TypeId, Age, Link, Hops, PropertyValue[] + var args = (object[])result; + var typeId = Convert.ToUInt32(args[0]); + var age = Convert.ToUInt64(args[1]); + var link = (string)args[2]; + var hops = (byte)args[3]; + var pvData = (byte[])args[4]; - EpResource dr; - TypeDef typeDef = null; + var typeDef = resource != null ? + resource.Instance.Definition as RemoteTypeDef + : Instance.Warehouse.GetRemoteTypeDefById( + _remoteDomain, + typeId + ); - if (resource == null) + + var initResource = (EpResource dr) => + { + var parsedReply = DataDeserializer.PropertyValueArrayParserAsync(pvData, 0, (uint)pvData.Length, this, newSequence); + + parsedReply.Then(results => { - typeDef = Instance.Warehouse.GetTypeDefById(typeId, TypeDefKind.Resource); - if (typeDef?.DefinedType != null && typeDef.IsWrapper) - dr = Activator.CreateInstance(typeDef.DefinedType, this, id, Convert.ToUInt64(args[1]), (string)args[2]) as EpResource; - else - dr = new EpResource(this, id, Convert.ToUInt64(args[1]), (string)args[2]); - } - else - { - dr = resource; - typeDef = resource.Instance.Definition; - } - - - var initResource = (EpResource ok) => - { - var parsedReply = DataDeserializer.PropertyValueArrayParserAsync(pvData, 0, (uint)pvData.Length, this, newSequence);// Codec.proper (content, 0, this, newSequence, transmissionType); - - - parsedReply.Then(results => - { - var pvs = results as PropertyValue[]; - - //var pvs = new List(); - - //for (var i = 0; i < ar.Length; i += 3) - // pvs.Add(new PropertyValue(ar[i + 2], Convert.ToUInt64(ar[i]), (DateTime)ar[i + 1])); - - dr._Attach(pvs); - resourceRequests.Remove(id); - // move from needed to attached. - neededResources.Remove(id); - attachedResources[id] = new WeakReference(dr); - reply.Trigger(dr); - }).Error(ex => reply.TriggerError(ex)); - - - }; - - if (typeDef == null) - { - GetTypeDefById(typeId).Then((tmp) => - { - // typeId, ResourceAge, ResourceLink, Content - if (resource == null) - { - dr.ResourceDefinition = tmp; - - Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), dr) - .Then(initResource) - .Error(ex => reply.TriggerError(ex)); - } - else - { - initResource(resource); - } - }).Error((ex) => - { - reply.TriggerError(ex); - }); - - } - else + var pvs = results as PropertyValue[]; + + dr._Attach(pvs); + _resourceRequests.Remove(id); + // move from needed to attached. + _neededResources.Remove(id); + _attachedResources[id] = new WeakReference(dr); + reply.Trigger(dr); + }).Error(ex => reply.TriggerError(ex)); + }; + + if (typeDef == null) + { + FetchTypeDef(typeId, null).Then((td) => { + // typeId, ResourceAge, ResourceLink, Content if (resource == null) { - dr.ResourceDefinition = typeDef; + if (td.ProxyType != null) + resource = Activator.CreateInstance(td.ProxyType, this, id, Convert.ToUInt64(args[1]), (string)args[2]) as EpResource; + else + resource = new EpResource(this, id, Convert.ToUInt64(args[1]), (string)args[2]); + + resource.ResourceDefinition = td; + typeDef = td; + Instance.Warehouse.Put(Instance.Link + "/" + id.ToString(), resource) + .Then(initResource) + .Error(ex => reply.TriggerError(ex)); - Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), dr) - .Then(initResource).Error((ex) => reply.TriggerError(ex)); } else { initResource(resource); } - - } - - }).Error((ex) => + }).Error((ex) => + { + reply.TriggerError(ex); + }); + } + else { - reply.TriggerError(ex); - }); + if (resource == null) + { + if (typeDef.ProxyType != null) + resource = Activator.CreateInstance(typeDef.ProxyType, this, id, Convert.ToUInt64(args[1]), (string)args[2]) as EpResource; + else + resource = new EpResource(this, id, Convert.ToUInt64(args[1]), (string)args[2]); + + resource.ResourceDefinition = typeDef; + + Instance.Warehouse.Put(this.Instance.Link + "/" + id.ToString(), resource) + .Then(initResource).Error((ex) => reply.TriggerError(ex)); + } + else + { + initResource(resource); + } + } + + }).Error((ex) => + { + reply.TriggerError(ex); + }); - return reply; + return reply; //} } + + /// + /// Fetch a resource from the other end + /// + /// Resource Id + /// DistributedResource + /// + //object fetchResourceLock = new object(); + public AsyncReply FetchTypeDef(ulong id, ulong[] requestSequence) + { + //Console.WriteLine($"Fetching typedef {id}"); + + RemoteTypeDef typeDef = _cachedTypeDefs[id]; + + if (typeDef != null) + return new AsyncReply(typeDef); + + typeDef = _neededTypeDefs[id]; + + var requestInfo = _typeDefRequests[id]; + + if (requestInfo != null) + { + if (typeDef != null && (requestSequence?.Contains(id) ?? false)) + { + // dead lock avoidance for loop reference. + return new AsyncReply(typeDef); + } + else if (typeDef != null && requestInfo.RequestSequence.Contains(id)) + { + // dead lock avoidance for dependent reference. + return new AsyncReply(typeDef); + } + else + { + return requestInfo.Reply; + } + } + + var newSequence = requestSequence != null ? requestSequence.Concat(new ulong[] { id }).ToArray() : new ulong[] { id }; + + var reply = new AsyncReply(); + _typeDefRequests.Add(id, new FetchRequestInfo(reply, newSequence)); + + SendRequest(EpPacketRequest.TypeDefById, id) + .Then((result) => + { + if (result == null) + { + reply.TriggerError(new AsyncException(ErrorType.Management, + (ushort)ExceptionCode.ResourceNotFound, "Null response")); + return; + } + + // TypeDef Data + //var args = (object[])result; + var typeDefData = (byte[])result; + + var od = new RemoteTypeDef(); + _neededTypeDefs[id] = od; + + RemoteTypeDef.Parse(od, this.RemoteDomain, typeDefData, this, newSequence).Then(td => + { + _typeDefRequests.Remove(id); + // move from needed to attached. + _neededTypeDefs.Remove(id); + _cachedTypeDefs[id] = td; + + reply.Trigger(td); + + }).Error(reply.TriggerError); + + }).Error(reply.TriggerError); + + return reply; + + } + /// /// Query resources at specific link. /// @@ -2050,28 +2276,28 @@ partial class EpConnection SendRequest(EpPacketRequest.CreateResource, path, type.Id, type.CastProperties(properties), attributes) .Then(r => reply.Trigger((EpResource)r)) - .Error(e => reply.TriggerError(e)) - .Warning((l, m) => reply.TriggerWarning(l, m)); + .Warning((l, m) => reply.TriggerWarning(l, m)) + .Error(e => reply.TriggerError(e)); return reply; } private void Subscribe(IResource resource) { - lock (subscriptionsLock) + lock (_subscriptionsLock) { resource.Instance.EventOccurred += Instance_EventOccurred; resource.Instance.CustomEventOccurred += Instance_CustomEventOccurred; resource.Instance.PropertyModified += Instance_PropertyModified; resource.Instance.Destroyed += Instance_ResourceDestroyed; - subscriptions.Add(resource, new List()); + _subscriptions.Add(resource, new List()); } } private void Unsubscribe(IResource resource) { - lock (subscriptionsLock) + lock (_subscriptionsLock) { // do something with the list... resource.Instance.EventOccurred -= Instance_EventOccurred; @@ -2079,16 +2305,16 @@ partial class EpConnection resource.Instance.PropertyModified -= Instance_PropertyModified; resource.Instance.Destroyed -= Instance_ResourceDestroyed; - subscriptions.Remove(resource); + _subscriptions.Remove(resource); } } private void UnsubscribeAll() { - lock (subscriptionsLock) + lock (_subscriptionsLock) { - foreach (var resource in subscriptions.Keys) + foreach (var resource in _subscriptions.Keys) { resource.Instance.EventOccurred -= Instance_EventOccurred; resource.Instance.CustomEventOccurred -= Instance_CustomEventOccurred; @@ -2096,7 +2322,7 @@ partial class EpConnection resource.Instance.Destroyed -= Instance_ResourceDestroyed; } - subscriptions.Clear(); + _subscriptions.Clear(); } } @@ -2121,21 +2347,21 @@ partial class EpConnection { if (info.EventDef.Subscribable) { - lock (subscriptionsLock) + lock (_subscriptionsLock) { // check the client requested listen - if (!subscriptions.ContainsKey(info.Resource)) + if (!_subscriptions.ContainsKey(info.Resource)) return; - if (!subscriptions[info.Resource].Contains(info.EventDef.Index)) + if (!_subscriptions[info.Resource].Contains(info.EventDef.Index)) return; } } - if (!info.Receivers(this.session)) + if (!info.Receivers(_session)) return; - if (info.Resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, info.EventDef, info.Issuer) == Ruling.Denied) + if (info.Resource.Instance.Applicable(_session, ActionType.ReceiveEvent, info.EventDef, info.Issuer) == Ruling.Denied) return; @@ -2150,18 +2376,18 @@ partial class EpConnection { if (info.Definition.Subscribable) { - lock (subscriptionsLock) + lock (_subscriptionsLock) { // check the client requested listen - if (!subscriptions.ContainsKey(info.Resource)) + if (!_subscriptions.ContainsKey(info.Resource)) return; - if (!subscriptions[info.Resource].Contains(info.Definition.Index)) + if (!_subscriptions[info.Resource].Contains(info.Definition.Index)) return; } } - if (info.Resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, info.Definition, null) == Ruling.Denied) + if (info.Resource.Instance.Applicable(_session, ActionType.ReceiveEvent, info.Definition, null) == Ruling.Denied) return; // compose the packet @@ -2173,11 +2399,11 @@ partial class EpConnection - void EpRequestKeepAlive(uint callback, ParsedTdu dataType, byte[] data) + void EpRequestKeepAlive(uint callback, PlainTdu tdu) { - var (offset, length, args) = DataDeserializer.LimitedCountListParser(data, dataType.Offset, - dataType.ContentLength, Instance.Warehouse); + var (offset, length, args) = DataDeserializer.LimitedCountListParser(tdu.Data, tdu.PayloadOffset, + tdu.PayloadLength, Instance.Warehouse); var peerTime = (DateTime)args[0]; var interval = Convert.ToUInt32(args[1]); @@ -2186,14 +2412,14 @@ partial class EpConnection var now = DateTime.UtcNow; - if (lastKeepAliveReceived != null) + if (_lastKeepAliveReceived != null) { - var diff = (uint)(now - (DateTime)lastKeepAliveReceived).TotalMilliseconds; + var diff = (uint)(now - (DateTime)_lastKeepAliveReceived).TotalMilliseconds; jitter = (uint)Math.Abs((int)diff - (int)interval); } SendReply(EpPacketReply.Completed, callback, now, jitter); - lastKeepAliveReceived = now; + _lastKeepAliveReceived = now; } } diff --git a/Libraries/Esiur/Protocol/EpResource.cs b/Libraries/Esiur/Protocol/EpResource.cs index a84004e..d5c42d4 100644 --- a/Libraries/Esiur/Protocol/EpResource.cs +++ b/Libraries/Esiur/Protocol/EpResource.cs @@ -81,7 +81,7 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn /// /// Connection responsible for the distributed resource. /// - public EpConnection DistributedResourceConnection + public EpConnection ResourceConnection { get { return connection; } } @@ -89,7 +89,7 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn /// /// Resource link /// - public string DistributedResourceLink + public string ResourceLink { get { return link; } } @@ -97,7 +97,7 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn /// /// Instance Id given by the other end. /// - public uint DistributedResourceInstanceId + public uint ResourceInstanceId { get { return instanceId; } internal set { instanceId = value; } @@ -128,9 +128,9 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn /// /// Resource is attached when all its properties are received. /// - public bool DistributedResourceAttached => attached; + public bool ResourceAttached => attached; - public bool DistributedResourceSuspended => suspended; + public bool ResourceSuspended => suspended; // public DistributedResourceStack Stack @@ -216,10 +216,18 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn public AsyncReply Subscribe(EventDef et) { if (et == null) - return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); + { + var rt = new AsyncReply(); + rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); + return rt; + } if (!et.Subscribable) - return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, "")); + { + var rt = new AsyncReply(); + rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, "")); + return rt; + } return connection.SendSubscribeRequest(instanceId, et.Index); } @@ -235,10 +243,18 @@ public class EpResource : DynamicObject, IResource, INotifyPropertyChanged, IDyn public AsyncReply Unsubscribe(EventDef et) { if (et == null) - return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); + { + var rt = new AsyncReply(); + rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); + return rt; + } if (!et.Subscribable) - return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, "")); + { + var rt = new AsyncReply(); + rt.TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotSubscribable, "")); + return rt; + } return connection.SendUnsubscribeRequest(instanceId, et.Index); } diff --git a/Libraries/Esiur/Protocol/EpResourceAttachRequestInfo.cs b/Libraries/Esiur/Protocol/EpResourceAttachRequestInfo.cs index ada422c..da010ee 100644 --- a/Libraries/Esiur/Protocol/EpResourceAttachRequestInfo.cs +++ b/Libraries/Esiur/Protocol/EpResourceAttachRequestInfo.cs @@ -6,12 +6,12 @@ using System.Text; namespace Esiur.Protocol; -internal class EpResourceAttachRequestInfo +internal class FetchRequestInfo { - public AsyncReply Reply { get; set; } - public uint[] RequestSequence { get; set; } + public AsyncReply Reply { get; set; } + public TId[] RequestSequence { get; set; } - public EpResourceAttachRequestInfo(AsyncReply reply, uint[] requestSequence) + public FetchRequestInfo(AsyncReply reply, TId[] requestSequence) { Reply = reply; RequestSequence = requestSequence; diff --git a/Libraries/Esiur/Protocol/EpServer.cs b/Libraries/Esiur/Protocol/EpServer.cs index 92796b9..e828ca3 100644 --- a/Libraries/Esiur/Protocol/EpServer.cs +++ b/Libraries/Esiur/Protocol/EpServer.cs @@ -184,7 +184,7 @@ public class EpServer : NetworkServer, IResource public EpServer MapCall(string call, Delegate handler) { - var fd = FunctionDef.MakeFunctionDef(null, handler.Method, 0, call, null); + var fd = FunctionDef.MakeFunctionDef(Instance.Warehouse, null, handler.Method, 0, call, null); Calls.Add(call, new CallInfo() { Delegate = handler, Definition = fd }); return this; } diff --git a/Libraries/Esiur/Proxy/ResourceGenerator.cs b/Libraries/Esiur/Proxy/ResourceGenerator.cs index b790c49..5803d32 100644 --- a/Libraries/Esiur/Proxy/ResourceGenerator.cs +++ b/Libraries/Esiur/Proxy/ResourceGenerator.cs @@ -250,7 +250,7 @@ namespace Esiur.Proxy } // === Emission helpers (ported from your original generator) === - private static void EmitTypeDefs(SourceProductionContext spc, TypeDef[] typeDefs) + private static void EmitTypeDefs(SourceProductionContext spc, RemoteTypeDef[] typeDefs) { foreach (var typeDef in typeDefs) { diff --git a/Libraries/Esiur/Proxy/TypeDefGenerator.cs b/Libraries/Esiur/Proxy/TypeDefGenerator.cs index f8bc579..e8ceb62 100644 --- a/Libraries/Esiur/Proxy/TypeDefGenerator.cs +++ b/Libraries/Esiur/Proxy/TypeDefGenerator.cs @@ -61,7 +61,7 @@ public static class TypeDefGenerator } - internal static string GenerateRecord(TypeDef typeDef, TypeDef[] typeDefs) + internal static string GenerateRecord(RemoteTypeDef typeDef, TypeDef[] typeDefs) { var cls = typeDef.Name.Split('.'); @@ -82,7 +82,8 @@ public static class TypeDefGenerator } } - rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]"); + //rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]"); + rt.AppendLine($"[Remote(\"{typeDef.Name}\", \"{typeDef.Domain}\")]"); rt.AppendLine($"[Export] public class {className} : IRecord {{"); @@ -109,7 +110,7 @@ public static class TypeDefGenerator return rt.ToString(); } - internal static string GenerateEnum(TypeDef typeDef, TypeDef[] typeDefs) + internal static string GenerateEnum(RemoteTypeDef typeDef, TypeDef[] typeDefs) { var cls = typeDef.Name.Split('.'); @@ -129,7 +130,9 @@ public static class TypeDefGenerator } } - rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]"); + //rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]"); + rt.AppendLine($"[Remote(\"{typeDef.Name}\", \"{typeDef.Domain}\")]"); + rt.AppendLine($"[Export] public enum {className} {{"); rt.AppendLine(String.Join(",\r\n", typeDef.Constants.Select(x => $"{x.Name}={x.Value}"))); @@ -142,28 +145,36 @@ public static class TypeDefGenerator static string GetTypeName(Tru tru, TypeDef[] typeDefs) { - string name; + string name = null; - if (tru.Identifier == TruIdentifier.TypedResource)// == DataType.Resource) - name = typeDefs.First(x => x.Id == tru.UUID && (x.Kind == TypeDefKind.Resource)).Name; - else if (tru.Identifier == TruIdentifier.TypedRecord) - name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Record).Name; - else if (tru.Identifier == TruIdentifier.Enum) - name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Enum).Name; - else if (tru.Identifier == TruIdentifier.TypedList) - name = GetTypeName(tru.SubTypes[0], typeDefs) + "[]"; - else if (tru.Identifier == TruIdentifier.TypedMap) - name = "Map<" + GetTypeName(tru.SubTypes[0], typeDefs) - + "," + GetTypeName(tru.SubTypes[1], typeDefs) - + ">"; - else if (tru.Identifier == TruIdentifier.Tuple2 || - tru.Identifier == TruIdentifier.Tuple3 || - tru.Identifier == TruIdentifier.Tuple4 || - tru.Identifier == TruIdentifier.Tuple5 || - tru.Identifier == TruIdentifier.Tuple6 || - tru.Identifier == TruIdentifier.Tuple7) - name = "(" + String.Join(",", tru.SubTypes.Select(x => GetTypeName(x, typeDefs))) - + ")"; + if (tru is TruTypeDef truTypeDef) + { + name = truTypeDef.TypeDef.Name;// typeDefs.First(x => x.Id == tru.TypeDef.Value.Value).Name; + } + //if (tru.Identifier == TruIdentifier.TypedResource)// == DataType.Resource) + // name = typeDefs.First(x => x.Id == tru.UUID && (x.Kind == TypeDefKind.Resource)).Name; + //else if (tru.Identifier == TruIdentifier.TypedRecord) + // name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Record).Name; + //else if (tru.Identifier == TruIdentifier.Enum) + // name = typeDefs.First(x => x.Id == tru.UUID && x.Kind == TypeDefKind.Enum).Name; + + else if (tru is TruComposite truComposite) + { + if (tru.Identifier == TruIdentifier.TypedList) + name = GetTypeName(truComposite.SubTypes[0], typeDefs) + "[]"; + else if (tru.Identifier == TruIdentifier.TypedMap) + name = "Map<" + GetTypeName(truComposite.SubTypes[0], typeDefs) + + "," + GetTypeName(truComposite.SubTypes[1], typeDefs) + + ">"; + else if (tru.Identifier == TruIdentifier.Tuple2 || + tru.Identifier == TruIdentifier.Tuple3 || + tru.Identifier == TruIdentifier.Tuple4 || + tru.Identifier == TruIdentifier.Tuple5 || + tru.Identifier == TruIdentifier.Tuple6 || + tru.Identifier == TruIdentifier.Tuple7) + name = "(" + String.Join(",", truComposite.SubTypes.Select(x => GetTypeName(x, typeDefs))) + + ")"; + } else { @@ -193,6 +204,8 @@ public static class TypeDefGenerator }; } + if (name == null) return "object"; + return (tru.Nullable) ? name + "?" : name; } @@ -205,8 +218,9 @@ public static class TypeDefGenerator throw new Exception("Invalid EP URL"); var path = urlRegex.Split(url); - var con = Warehouse.Default.Get(path[1] + "://" + path[2], - !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) ? new { Username = username, Password = password } : null + var con = Warehouse.Default.Get(path[1] + "://" + path[2], new ResourceContext(0, + new Map { ["username"] = username, ["password"] = password }, null, null) + // !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) ? new { Username = username, Password = password } : null ).Wait(20000); if (con == null) @@ -279,7 +293,7 @@ public static class TypeDefGenerator } } - internal static string GenerateClass(TypeDef typeDef, TypeDef[] typeDefs, bool asyncSetters) + internal static string GenerateClass(RemoteTypeDef typeDef, TypeDef[] typeDefs, bool asyncSetters) { var cls = typeDef.Name.Split('.'); @@ -302,13 +316,14 @@ public static class TypeDefGenerator } - rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]"); + rt.AppendLine($"[Remote(\"{typeDef.Name}\", \"{typeDef.Domain}\")]"); + //rt.AppendLine($"[TypeId(\"{typeDef.Id.Data.ToHex(0, 16, null)}\")]"); // extends - if (typeDef.ParentId == null) + if (typeDef.ParentTypeId == null) rt.AppendLine($"public class {className} : EpResource {{"); else - rt.AppendLine($"public class {className} : {typeDefs.First(x => x.Id == typeDef.ParentId && x.Kind == TypeDefKind.Resource).Name} {{"); + rt.AppendLine($"public class {className} : {typeDefs.First(x => x.Id == typeDef.ParentTypeId && x.Kind == TypeDefKind.Resource).Name} {{"); rt.AppendLine($"public {className}(EpConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}"); diff --git a/Libraries/Esiur/Resource/Instance.cs b/Libraries/Esiur/Resource/Instance.cs index 734a638..ea1b9a6 100644 --- a/Libraries/Esiur/Resource/Instance.cs +++ b/Libraries/Esiur/Resource/Instance.cs @@ -710,7 +710,15 @@ public class Instance } - return Ruling.DontCare; + // Apply default permissions if no manager is applicable or if the resource is not available. + if (action == ActionType.GetProperty + || action == ActionType.ViewTypeDef + || action == ActionType.ReceiveEvent + || action == ActionType.Attach + || action == ActionType.Execute) + return Ruling.Allowed; + else + return Ruling.Denied; } @@ -753,7 +761,7 @@ public class Instance } else { - this.definition = warehouse.GetTypeDefByType(resource.GetType()); + this.definition = warehouse.GetLocalTypeDefByType(resource.GetType()); } // set ages diff --git a/Libraries/Esiur/Resource/RemoteAttribute.cs b/Libraries/Esiur/Resource/RemoteAttribute.cs new file mode 100644 index 0000000..595a18b --- /dev/null +++ b/Libraries/Esiur/Resource/RemoteAttribute.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Class, Inherited = false)] + public class RemoteAttribute:Attribute + { + public string Domain { get; private set; } + public string FullName { get; private set; } + + public RemoteAttribute(string domain, string fullName) + { + Domain = domain; + FullName = fullName; + } + } +} diff --git a/Libraries/Esiur/Resource/ResourceContext.cs b/Libraries/Esiur/Resource/ResourceContext.cs new file mode 100644 index 0000000..2e69373 --- /dev/null +++ b/Libraries/Esiur/Resource/ResourceContext.cs @@ -0,0 +1,31 @@ +using Esiur.Data; +using Esiur.Security.Authority; +using Esiur.Security.Permissions; +using System; +using System.Collections.Generic; +using System.Security.Principal; +using System.Text; + +namespace Esiur.Resource +{ + public class ResourceContext + { + public ulong Age { get; } + public Map Attributes { get; } + public Map Properties { get; } + public IPermissionsManager PermissionsManager { get; } + + public ResourceContext(ulong age, Map attributes, Map properties, IPermissionsManager permissionsManager) + { + Age = age; + Attributes = attributes; + Properties = properties; + PermissionsManager = permissionsManager; + } + + public virtual void Build() + { + // update the context based on the current state of the resource and its environment + } + } +} diff --git a/Libraries/Esiur/Resource/Warehouse.cs b/Libraries/Esiur/Resource/Warehouse.cs index f538a12..a4e5400 100644 --- a/Libraries/Esiur/Resource/Warehouse.cs +++ b/Libraries/Esiur/Resource/Warehouse.cs @@ -29,13 +29,16 @@ using Esiur.Misc; using Esiur.Net.Packets; using Esiur.Protocol; using Esiur.Proxy; +using Esiur.Security.Authority; using Esiur.Security.Permissions; +using Org.BouncyCastle.Asn1.Cms; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Reflection; using System.Reflection.Metadata; using System.Text; using System.Text.RegularExpressions; @@ -51,47 +54,83 @@ public class Warehouse //static byte prefixCounter; - //static AutoList stores = new AutoList(null); - ConcurrentDictionary> resources = new ConcurrentDictionary>(); - ConcurrentDictionary>> stores = new ConcurrentDictionary>>(); + ConcurrentDictionary> _resources = new ConcurrentDictionary>(); + ConcurrentDictionary>> _stores = new ConcurrentDictionary>>(); + + volatile int _resourceCounter = 0; + volatile int _typeDefsCounter = 0; - uint resourceCounter = 0; + //KeyList> _localTypeDefs + // = new KeyList>() + // { + // [TypeDefKind.Resource] = new KeyList(), + // [TypeDefKind.Record] = new KeyList(), + // [TypeDefKind.Enum] = new KeyList(), + // }; + + KeyList _localTypeDefs + = new KeyList(); - KeyList> typeDefs - = new KeyList>() - { - [TypeDefKind.Resource] = new KeyList(), - [TypeDefKind.Record] = new KeyList(), - [TypeDefKind.Enum] = new KeyList(), - }; + KeyList> _remoteTypeDefs + = new KeyList>(); - object typeDefsLock = new object(); + //KeyList>> _remoteTypeDefs + // = new KeyList>>(); - bool warehouseIsOpen = false; + + Map _authenticationProviders = new Map(); + List _permissionsManagers = new List(); + + + object _typeDefsLock = new object(); + + bool _warehouseIsOpen = false; public delegate void StoreEvent(IStore store); public event StoreEvent StoreConnected; public event StoreEvent StoreDisconnected; - public delegate AsyncReply ProtocolInstance(string name, object properties); + public delegate AsyncReply ProtocolInstance(string name, ResourceContext resourceContext); public KeyList Protocols { get; } = new KeyList(); private Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)"); + public void RegisterAuthenticationProvider(IAuthenticationProvider provider) + { + RegisterAuthenticationProvider(provider.DefaultName, provider); + } + + public void RegisterAuthenticationProvider(string name, IAuthenticationProvider provider) + { + _authenticationProviders.Add(name, provider); + } + + + public void RegisterPermissionsManager(IPermissionsManager manager) + { + _permissionsManagers.Add(manager); + } + + public IAuthenticationProvider GetAuthenticationProvider(string name) + { + if (_authenticationProviders.ContainsKey(name)) + return _authenticationProviders[name]; + throw new Exception("Authentication provider not found."); + } + public Warehouse() { Protocols.Add("EP", - async (name, attributes) - => await New(name, null, attributes)); + async (name, context) + => await New(name, context)); - new TypeDef(typeof(EpAuthPacketIAuthHeader), this); - - new TypeDef(typeof(EpAuthPacketIAuthDestination), this); - new TypeDef(typeof(EpAuthPacketIAuthFormat), this); + //new LocalTypeDef(typeof(EpAuthPacketIAuthHeader), this); + //new LocalTypeDef(typeof(EpAuthPacketIAuthDestination), this); + //new LocalTypeDef(typeof(EpAuthPacketIAuthFormat), this); } @@ -102,13 +141,13 @@ public class Warehouse /// public IStore GetStore(string name) { - foreach (var s in stores) + foreach (var s in _stores) if (s.Key.Instance.Name == name) return s.Key; return null; } - public WeakReference[] Resources => resources.Values.ToArray(); + public WeakReference[] Resources => _resources.Values.ToArray(); /// /// Get a resource by instance Id. @@ -117,10 +156,10 @@ public class Warehouse /// public AsyncReply GetById(uint id) { - if (resources.ContainsKey(id)) + if (_resources.ContainsKey(id)) { IResource r; - if (resources[id].TryGetTarget(out r)) + if (_resources[id].TryGetTarget(out r)) return new AsyncReply(r); else return new AsyncReply(null); @@ -129,33 +168,33 @@ public class Warehouse return new AsyncReply(null); } - void LoadGenerated() - { - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - var generatedType = assembly.GetType("Esiur.Generated"); - if (generatedType != null) - { - var resourceTypes = (Type[])generatedType.GetProperty("Resources").GetValue(null); - foreach (var t in resourceTypes) - { - RegisterTypeDef(new TypeDef(t)); - } + //void LoadGenerated() + //{ + // foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + // { + // var generatedType = assembly.GetType("Esiur.Generated"); + // if (generatedType != null) + // { + // var resourceTypes = (Type[])generatedType.GetProperty("Resources").GetValue(null); + // foreach (var t in resourceTypes) + // { + // RegisterTypeDef(new TypeDef(t)); + // } - var recordTypes = (Type[])generatedType.GetProperty("Records").GetValue(null); - foreach (var t in recordTypes) - { - RegisterTypeDef(new TypeDef(t)); - } + // var recordTypes = (Type[])generatedType.GetProperty("Records").GetValue(null); + // foreach (var t in recordTypes) + // { + // RegisterTypeDef(new TypeDef(t)); + // } - var enumsTypes = (Type[])generatedType.GetProperty("Enums").GetValue(null); - foreach (var t in enumsTypes) - { - RegisterTypeDef(new TypeDef(t)); - } - } - } - } + // var enumsTypes = (Type[])generatedType.GetProperty("Enums").GetValue(null); + // foreach (var t in enumsTypes) + // { + // RegisterTypeDef(new TypeDef(t)); + // } + // } + // } + //} /// /// Open the warehouse. @@ -164,16 +203,16 @@ public class Warehouse /// True, if no problem occurred. public async AsyncReply Open() { - if (warehouseIsOpen) + if (_warehouseIsOpen) return false; // Load generated models - LoadGenerated(); + //LoadGenerated(); - warehouseIsOpen = true; + _warehouseIsOpen = true; - var resSnap = resources.Select(x => + var resSnap = _resources.Select(x => { IResource r; if (x.Value.TryGetTarget(out r)) @@ -222,7 +261,7 @@ public class Warehouse var bag = new AsyncBag(); - foreach (var resource in resources.Values) + foreach (var resource in _resources.Values) { IResource r; if (resource.TryGetTarget(out r)) @@ -233,11 +272,11 @@ public class Warehouse } } - foreach (var store in stores) + foreach (var store in _stores) bag.Add(store.Key.Trigger(ResourceTrigger.Terminate)); - foreach (var resource in resources.Values) + foreach (var resource in _resources.Values) { IResource r; if (resource.TryGetTarget(out r)) @@ -248,7 +287,7 @@ public class Warehouse } - foreach (var store in stores) + foreach (var store in _stores) bag.Add(store.Key.Trigger(ResourceTrigger.SystemTerminated)); bag.Seal(); @@ -274,7 +313,7 @@ public class Warehouse { var p = path.Trim().TrimStart('/').Split('/'); - foreach (var store in stores.Keys) + foreach (var store in _stores.Keys) { if (p[0] == store.Instance.Name) { @@ -298,7 +337,7 @@ public class Warehouse /// /// /// Resource instance. - public async AsyncReply Get(string path, object attributes = null, IResource parent = null, IPermissionsManager manager = null) + public async AsyncReply Get(string path, ResourceContext resourceContext = null) where T : IResource { @@ -309,11 +348,11 @@ public class Warehouse if (Protocols.ContainsKey(url[1])) { - if (!warehouseIsOpen) + if (!_warehouseIsOpen) await Open(); var handler = Protocols[url[1]]; - var store = await handler(url[2], attributes); + var store = await handler(url[2], resourceContext); try { @@ -346,7 +385,7 @@ public class Warehouse /// Resource instance. /// IStore that manages the resource. Can be null if the resource is a store. /// Parent resource. if not presented the store becomes the parent for the resource. - public async AsyncReply Put(string path, T resource, ulong age = 0, IPermissionsManager manager = null, object attributes = null) where T : IResource + public async AsyncReply Put(string path, T resource, ResourceContext resourceContext = null) where T : IResource { if (resource.Instance != null) throw new Exception("Resource already initialized."); @@ -385,18 +424,17 @@ public class Warehouse var resourceReference = new WeakReference(resource); - resource.Instance = new Instance(this, resourceCounter++, instanceName, resource, store, age); + var resourceId = (uint)Interlocked.Increment(ref _resourceCounter); - if (attributes != null) - if (attributes is Map attrs) - resource.Instance.SetAttributes(attrs); - else - resource.Instance.SetAttributes(Map.FromObject(attributes)); + resource.Instance = new Instance(this, resourceId, instanceName, resource, store, resourceContext?.Age ?? 0); + + if (resourceContext?.Attributes != null) + resource.Instance.SetAttributes(resourceContext.Attributes); try { if (resource is IStore) - stores.TryAdd(resource as IStore, new List>()); + _stores.TryAdd(resource as IStore, new List>()); else if ((IResource)resource != store) { if (!await store.Put(resource, string.Join("/", location.Skip(1).ToArray()))) @@ -406,9 +444,9 @@ public class Warehouse var t = resource.GetType(); Global.Counters["T-" + t.Namespace + "." + t.Name]++; - resources.TryAdd(resource.Instance.Id, resourceReference); - - if (warehouseIsOpen) + _resources.TryAdd(resource.Instance.Id, resourceReference); + + if (_warehouseIsOpen) { await resource.Trigger(ResourceTrigger.Initialize); if (resource is IStore) @@ -429,60 +467,81 @@ public class Warehouse } - public T Create(object properties = null) + public T Create(Map properties = null) { return (T)Create(typeof(T), properties); } - public IResource Create(Type type, object properties = null) + public IResource CreateFromIndexedProperties(Type type, Map properties) { type = ResourceProxy.GetProxy(type); - var res = Activator.CreateInstance(type) as IResource; - if (properties != null) { - if (properties is Map map) + var typeDef = GetLocalTypeDefByType(type); + foreach (var p in properties) { - var typeDef = GetTypeDefByType(type); - foreach (var kvp in map) - typeDef.GetPropertyDefByIndex(kvp.Key).PropertyInfo.SetValue(res, kvp.Value); - } - else - { - var ps = Map.FromObject(properties); + var pi = typeDef.GetPropertyDefByIndex(p.Key).PropertyInfo; - foreach (var p in ps) + if (pi != null) { - var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (pi != null) + if (pi.CanWrite) { - if (pi.CanWrite) + try { - try - { - pi.SetValue(res, p.Value); - } - catch (Exception ex) - { - Global.Log(ex); - } + pi.SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); } } - else + } + } + } + + + return res; + } + + public IResource Create(Type type, Map properties) + { + type = ResourceProxy.GetProxy(type); + var res = Activator.CreateInstance(type) as IResource; + + if (properties != null) + { + + foreach (var p in properties) + { + var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (pi != null) + { + if (pi.CanWrite) { - var fi = type.GetField(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (fi != null) + try { - try - { - fi.SetValue(res, p.Value); - } - catch (Exception ex) - { - Global.Log(ex); - } + pi.SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); + } + } + } + else + { + var fi = type.GetField(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (fi != null) + { + try + { + fi.SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); } } } @@ -492,59 +551,130 @@ public class Warehouse return res; } - public async AsyncReply New(Type type, string path, IPermissionsManager manager = null, object attributes = null, object properties = null) + public async AsyncReply New(Type type, string path, ResourceContext resourceContext) { - var res = Create(type, properties); - return await Put(path, res, 0, manager, attributes); + var res = Create(type, resourceContext?.Properties); + return await Put(path, res, resourceContext); } - public async AsyncReply New(string path, IPermissionsManager manager = null, object attributes = null, object properties = null) + public async AsyncReply New(string path, ResourceContext resourceContext = null) where T : IResource { - return (T)(await New(typeof(T), path, manager, attributes, properties)); + return (T)(await New(typeof(T), path, resourceContext)); + } + + + public void IsProxyType(Type type) + { + + } + + public void RegisterProxyType(Type type) + { + } /// - /// Put a resource schema in the schemas warehouse. + /// Register TypeDef. /// /// Resource type definition. - public void RegisterTypeDef(TypeDef typeDef) + public uint RegisterLocalTypeDef(LocalTypeDef typeDef) { - lock (typeDefsLock) + lock (_typeDefsLock) { - if (typeDefs[typeDef.Kind].ContainsKey(typeDef.Id)) - throw new Exception($"TypeDef with same class Id already exists. {typeDefs[typeDef.Kind][typeDef.Id].Name} -> {typeDef.Name}"); + //if (_localTypeDefs[typeDef.Kind].ContainsKey(typeDef.Id)) + if (_localTypeDefs.ContainsKey(typeDef.Id)) + throw new Exception($"TypeDef with same class Id already exists. {_localTypeDefs[typeDef.Id].Name} -> {typeDef.Name}"); + //throw new Exception($"TypeDef with same class Id already exists. {_localTypeDefs[typeDef.Kind][typeDef.Id].Name} -> {typeDef.Name}"); - typeDefs[typeDef.Kind][typeDef.Id] = typeDef; + var typeDefId = (uint)Interlocked.Increment(ref _typeDefsCounter); + + typeDef.Id = typeDefId; + + //_localTypeDefs[typeDef.Kind][typeDef.Id] = typeDef; + _localTypeDefs[typeDef.Id] = typeDef; + + return typeDefId; } } - public bool TryRegisterTypeDef(TypeDef typeDef) + public bool TryRegisterLocalTypeDef(LocalTypeDef typeDef) { - lock (typeDefsLock) + lock (_typeDefsLock) { - if (typeDefs[typeDef.Kind].ContainsKey(typeDef.Id)) + if (_localTypeDefs.ContainsKey(typeDef.Id)) return false; - typeDefs[typeDef.Kind][typeDef.Id] = typeDef; + var typeDefId = (uint)Interlocked.Increment(ref _typeDefsCounter); + typeDef.Id = typeDefId; + + _localTypeDefs[typeDef.Id] = typeDef; return true; } } + public bool TryRegisterRemoteTypeDef(string domain, RemoteTypeDef typeDef) + { + lock (_typeDefsLock) + { + if (!_remoteTypeDefs.ContainsKey(domain)) + { + _remoteTypeDefs.Add(domain, new KeyList()); + } + + if (_remoteTypeDefs[domain].ContainsKey(typeDef.Id)) + return false; + + // @TODO: Try to find a proxy type for the remote type def, if not found, create a new proxy type and register it in the warehouse. + _remoteTypeDefs[domain][typeDef.Id] = typeDef; + + var localTypeDefId = (uint)Interlocked.Increment(ref _typeDefsCounter); + typeDef.LocalTypeDefId = localTypeDefId; + _localTypeDefs[localTypeDefId] = typeDef; + + return true; + } + } + + + //public TypeDef FindTypeDefByType(Type type) + //{ + // var remote = type.GetCustomAttribute(); + + // if (remote != null) + // { + // return GetRemoteTypeDefByName(remote.Domain, remote.FullName); + // } + // else + // { + // return GetLocalTypeDefByType(type); + // } + //} + + //public TypeDef FindTypeDefByTypeDefId(TypeDefId typeDefId, string domain) + //{ + // if (typeDefId.Remote) + // { + // return GetRemoteTypeDefById(domain, typeDefId.Value); + // } + // else + // { + // return GetLocalTypeDefById(typeDefId.Value); + // } + //} + /// /// Get a TypeDef by type from the warehouse. If not in the warehouse, a new TypeDef is created and added to the warehouse. /// /// .Net type. /// Resource TypeDef. - public TypeDef GetTypeDefByType(Type type) + public TypeDef GetLocalTypeDefByType(Type type) { - - if (!(type.IsClass || type.IsEnum)) - return null; + //if (!(type.IsClass || type.IsEnum)) + // return null; - var baseType = ResourceProxy.GetBaseType(type); if (baseType == typeof(IResource) @@ -561,16 +691,33 @@ public class Warehouse else return null; - lock (typeDefsLock) + lock (_typeDefsLock) { - var typeDef = typeDefs[typeDefKind].Values.FirstOrDefault(x => x.DefinedType == baseType); - if (typeDef != null) - return typeDef; + //var typeDef = _localTypeDefs.Values.FirstOrDefault(x => x is LocalTypeDef ltd + // && ltd.DefinedType == baseType); + + foreach (var td in _localTypeDefs.Values) + { + if (td is LocalTypeDef ltd && ltd.DefinedType == baseType) + { + return td; + } + else if (td is RemoteTypeDef rtd && rtd.ProxyType == baseType) + { + return td; + } + } + + //if (typeDef != null) + // return typeDef; // create new TypeDef for type - typeDef = new TypeDef(baseType, this); - TypeDef.GetDependencies(typeDef, this); - return typeDef; + + //Console.WriteLine($"Creating {baseType.Name}"); + + var ntd = new LocalTypeDef(baseType, this); + //LocalTypeDef.GetDependencies(ntd, this); + return ntd; } } @@ -579,71 +726,56 @@ public class Warehouse /// /// typeId. /// TypeDef. - public TypeDef GetTypeDefById(Uuid typeId, TypeDefKind? typeDefKind = null) + public TypeDef GetLocalTypeDefById(ulong typeId) { - if (typeDefKind == null) - { - // look into resources - var typeDef = typeDefs[TypeDefKind.Resource][typeId]; - if (typeDef != null) - return typeDef; + return _localTypeDefs[typeId]; + } - // look into records - typeDef = typeDefs[TypeDefKind.Record][typeId]; - if (typeDef != null) - return typeDef; - // look into enums - typeDef = typeDefs[TypeDefKind.Enum][typeId]; - return typeDef; - - } - else - return typeDefs[typeDefKind.Value][typeId]; + public RemoteTypeDef GetRemoteTypeDefById(string domain, ulong typeId) + { + if (string.IsNullOrEmpty(domain) || !_remoteTypeDefs.ContainsKey(domain)) + return null; + return _remoteTypeDefs[domain][typeId]; } + public TypeDef GetRemoteTypeDefByName(string domain, string typeName, TypeDefKind? typeDefKind = null) + { + if (!string.IsNullOrEmpty(domain) || !_remoteTypeDefs.ContainsKey(domain)) + return null; + + return _remoteTypeDefs[domain].Values.FirstOrDefault(x => x.Name == typeName); + } + + public TypeDef GetRemoteTypeDefByType(Type type) + { + var remoteAttr = type.GetCustomAttribute(); + + if (remoteAttr == null) return null; + + return GetRemoteTypeDefByName(remoteAttr.Domain, remoteAttr.FullName); + } + /// /// Get a TypeDef by type name . If not in the warehouse, a new TypeDef is created and added to the warehouse. /// /// Class full name. /// TypeDef. - public TypeDef GetTypeDefByName(string typeName, TypeDefKind? typeDefKind = null) + public TypeDef GetLocalTypeDefByName(string typeName) { - if (typeDefKind == null) - { - // look into resources - var typeDef = typeDefs[TypeDefKind.Resource].Values.FirstOrDefault(x => x.Name == typeName); - if (typeDef != null) - return typeDef; - - // look into records - typeDef = typeDefs[TypeDefKind.Record].Values.FirstOrDefault(x => x.Name == typeName); - if (typeDef != null) - return typeDef; - - // look into enums - typeDef = typeDefs[TypeDefKind.Enum].Values.FirstOrDefault(x => x.Name == typeName); - return typeDef; - - } - else - { - return typeDefs[typeDefKind.Value].Values.FirstOrDefault(x => x.Name == typeName); - } + return _localTypeDefs.Values.FirstOrDefault(x => x.Name == typeName); } public bool Remove(IResource resource) { - if (resource.Instance == null) return false; - WeakReference resourceReference; - if (resources.ContainsKey(resource.Instance.Id)) - resources.TryRemove(resource.Instance.Id, out resourceReference); + if (_resources.ContainsKey(resource.Instance.Id)) + _resources.TryRemove(resource.Instance.Id, out resourceReference); else return false; @@ -651,7 +783,7 @@ public class Warehouse if (resource != resource.Instance.Store) { List> list; - if (stores.TryGetValue(resource.Instance.Store, out list)) + if (_stores.TryGetValue(resource.Instance.Store, out list)) { lock (((ICollection)list).SyncRoot) @@ -665,7 +797,7 @@ public class Warehouse List> toBeRemoved; - stores.TryRemove(store, out toBeRemoved); + _stores.TryRemove(store, out toBeRemoved); foreach (var o in toBeRemoved) diff --git a/Libraries/Esiur/Security/Authority/Authentication.cs b/Libraries/Esiur/Security/Authority/Authentication.cs deleted file mode 100644 index a065377..0000000 --- a/Libraries/Esiur/Security/Authority/Authentication.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - -Copyright (c) 2017 Ahmed Kh. Zamil - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Esiur.Security.Authority; - -public class Authentication -{ - AuthenticationMode type; - - public AuthenticationMethod Method { get; set; } - - public ulong TokenIndex { get; set; } - - public string Username { get; set; } - public Certificate Certificate { get; set; } - public string Domain { get; set; } - - public string FullName => Username + "@" + Domain; - - public Source Source { get; } = new Source(); - - public AuthenticationState State - { - get; - set; - } - - public AuthenticationMode Type - { - get => type; - } - - public Authentication(AuthenticationMode type) - { - this.type = type; - } -} diff --git a/Libraries/Esiur/Security/Authority/AuthenticationContext.cs b/Libraries/Esiur/Security/Authority/AuthenticationContext.cs index 72195e2..da91374 100644 --- a/Libraries/Esiur/Security/Authority/AuthenticationContext.cs +++ b/Libraries/Esiur/Security/Authority/AuthenticationContext.cs @@ -6,16 +6,14 @@ namespace Esiur.Security.Authority { public class AuthenticationContext { - public AuthenticationMode Mode { get; } + public AuthenticationDirection Direction { get; set; } + public AuthenticationMode Mode { get; set; } - public string? LocalDomain { get; } - public string? RemoteDomain { get; } - - public string? LocalHost { get; } - public string? RemoteHost { get; } - - //public AuthenticationComponentContext LocalToRemote { get; } = new(); - //public AuthenticationComponentContext RemoteToLocal { get; } = new(); + public string Domain { get; set; } + public string? InitiatorIdentity { get; set; } + public string? ResponderIdentity { get; set; } + public AuthenticationMaterial[] Materials { get; set; } + public string? HostName { get; set; } } } diff --git a/Libraries/Esiur/Security/Authority/IAuthenticationResponder.cs b/Libraries/Esiur/Security/Authority/AuthenticationDirection.cs similarity index 52% rename from Libraries/Esiur/Security/Authority/IAuthenticationResponder.cs rename to Libraries/Esiur/Security/Authority/AuthenticationDirection.cs index e52d0a8..7cc7070 100644 --- a/Libraries/Esiur/Security/Authority/IAuthenticationResponder.cs +++ b/Libraries/Esiur/Security/Authority/AuthenticationDirection.cs @@ -4,9 +4,9 @@ using System.Text; namespace Esiur.Security.Authority { - public interface IAuthenticationResponder + public enum AuthenticationDirection { - public AuthenticationResult Process(Session session); - + Initiator, + Responder } } diff --git a/Libraries/Esiur/Security/Authority/AuthenticationHandler.cs b/Libraries/Esiur/Security/Authority/AuthenticationHandler.cs new file mode 100644 index 0000000..1280d32 --- /dev/null +++ b/Libraries/Esiur/Security/Authority/AuthenticationHandler.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Security.Authority +{ + internal class AuthenticationHandler + { + } +} diff --git a/Libraries/Esiur/Security/Authority/AuthenticationMaterial.cs b/Libraries/Esiur/Security/Authority/AuthenticationMaterial.cs new file mode 100644 index 0000000..a37e970 --- /dev/null +++ b/Libraries/Esiur/Security/Authority/AuthenticationMaterial.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Security.Authority +{ + public class AuthenticationMaterial + { + public AuthenticationMaterialType Type { get; set; } + public object Value { get; set; } + } +} diff --git a/Libraries/Esiur/Security/Authority/AuthenticationMaterialType.cs b/Libraries/Esiur/Security/Authority/AuthenticationMaterialType.cs new file mode 100644 index 0000000..49787b8 --- /dev/null +++ b/Libraries/Esiur/Security/Authority/AuthenticationMaterialType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Security.Authority +{ + public enum AuthenticationMaterialType: byte + { + Secret, + Key, + Identity, + Data, + } +} diff --git a/Libraries/Esiur/Security/Authority/AuthenticationResult.cs b/Libraries/Esiur/Security/Authority/AuthenticationResult.cs index 677226d..444bc06 100644 --- a/Libraries/Esiur/Security/Authority/AuthenticationResult.cs +++ b/Libraries/Esiur/Security/Authority/AuthenticationResult.cs @@ -8,20 +8,24 @@ namespace Esiur.Security.Authority public class AuthenticationResult { public AuthenticationRuling Ruling { get; internal set; } - public string Identity { get; internal set; } + public string LocalIdentity { get; internal set; } + public string RemoteIdentity { get; internal set; } - public object HandshakePayload { get; internal set; } + //public object HandshakePayload { get; internal set; } public byte[] SessionKey { get; internal set; } public ExceptionCode? ExceptionCode { get; internal set; } public string ExceptionMessage { get; internal set; } - public AuthenticationResult(AuthenticationRuling ruling, string identity, object handshakePayload, byte[] sessionKey) + public object AuthenticationData { get; internal set; } + + public AuthenticationResult(AuthenticationRuling ruling, object authenticationData, string localIdentity = null, string remoteIdentity = null, byte[] sessionKey = null) { Ruling = ruling; - Identity = identity; - HandshakePayload = handshakePayload; + LocalIdentity = localIdentity; + RemoteIdentity = remoteIdentity; + AuthenticationData = authenticationData; SessionKey = sessionKey; } } diff --git a/Libraries/Esiur/Security/Authority/IAuthenticationHandler.cs b/Libraries/Esiur/Security/Authority/IAuthenticationHandler.cs index 201e353..72684b5 100644 --- a/Libraries/Esiur/Security/Authority/IAuthenticationHandler.cs +++ b/Libraries/Esiur/Security/Authority/IAuthenticationHandler.cs @@ -8,13 +8,12 @@ namespace Esiur.Security.Authority public interface IAuthenticationHandler { - public AuthenticationMode Mode { get; } - public AuthenticationResult Initialize(Session session, object authenticationData); + public string Protocol { get; } + //public AuthenticationMode Mode { get; } + //public AuthenticationResult Initialize(object authData); - public AuthenticationResult Process(object authenticationData); + public AuthenticationResult Process(object authData); - public void Terminate(Session session); - - public void Update(Session session, object authData); + //public AuthenticationResult? Result { get; } } } diff --git a/Libraries/Esiur/Security/Authority/IAuthenticationInitiator.cs b/Libraries/Esiur/Security/Authority/IAuthenticationInitiator.cs deleted file mode 100644 index a926b50..0000000 --- a/Libraries/Esiur/Security/Authority/IAuthenticationInitiator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Esiur.Security.Authority -{ - public interface IAuthenticationInitiator - { - public AuthenticationResult Initiate(Session session); - - public AuthenticationResult Process(object handshakePayload); - } -} diff --git a/Libraries/Esiur/Security/Authority/IAuthenticationMethodProvider.cs b/Libraries/Esiur/Security/Authority/IAuthenticationMethodProvider.cs deleted file mode 100644 index 90a9ca1..0000000 --- a/Libraries/Esiur/Security/Authority/IAuthenticationMethodProvider.cs +++ /dev/null @@ -1,16 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.Text; - -//namespace Esiur.Security.Authority -//{ -// public interface IAuthenticationMethodProvider -// { -// string Method { get; } - -// bool CanHandle(AuthenticationCreationContext context); - -// IAuthenticator CreateAuthenticator(AuthenticationCreationContext context); - -// } -//} diff --git a/Libraries/Esiur/Security/Authority/IAuthenticationProvider.cs b/Libraries/Esiur/Security/Authority/IAuthenticationProvider.cs new file mode 100644 index 0000000..778ecd0 --- /dev/null +++ b/Libraries/Esiur/Security/Authority/IAuthenticationProvider.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Security.Authority +{ + public interface IAuthenticationProvider + { + public IAuthenticationHandler + CreateAuthenticationHandler(AuthenticationContext context); + + public string DefaultName { get; } + } +} diff --git a/Libraries/Esiur/Security/Authority/InitiatorAuthenticationContext.cs b/Libraries/Esiur/Security/Authority/InitiatorAuthenticationContext.cs deleted file mode 100644 index e3c6038..0000000 --- a/Libraries/Esiur/Security/Authority/InitiatorAuthenticationContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Esiur.Security.Authority -{ - public sealed class InitiatorAuthenticationContext - { - public string LocalIdentity { get; } = string.Empty; - public string RemoteIdentity { get; } = string.Empty; - - public string? RemoteDomain { get; } - public string? LocalDomain { get; } - - public string? RemoteIpAddress { get; } - - public AuthenticationMode Mode { get; } - } -} diff --git a/Libraries/Esiur/Security/Authority/PpapAuthenticationProvider.cs b/Libraries/Esiur/Security/Authority/PpapAuthenticationProvider.cs new file mode 100644 index 0000000..e15d307 --- /dev/null +++ b/Libraries/Esiur/Security/Authority/PpapAuthenticationProvider.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Security.Authority +{ + internal class PpapAuthenticationProvider : IAuthenticationProvider + { + public string DefaultName => "PPAP"; + + public IAuthenticationHandler CreateAuthenticationHandler(AuthenticationContext context) + { + throw new NotImplementedException(); + } + } +} diff --git a/Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationHandler.cs b/Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationHandler.cs new file mode 100644 index 0000000..498c6ff --- /dev/null +++ b/Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationHandler.cs @@ -0,0 +1,606 @@ +using Esiur.Misc; +using Esiur.Security.Permissions; +using Org.BouncyCastle.Crypto.Digests; +using System; +using System.Collections.Generic; +using System.Text; +using Esiur.Data; +using Esiur.Data.Types; + +namespace Esiur.Security.Authority.Providers +{ + internal class PasswordAuthenticationHandler : IAuthenticationHandler + { + public string Protocol => "hash"; + + byte[] localNonce, remoteNonce; + byte[] localSalt, remoteSalt; + + string initiatorIdentity, responderIdentity; + + byte[] initiatorPassword, responderPassword; + + string hostName, domain; + + int step = 0; + + AuthenticationMode mode; + AuthenticationDirection direction; + + PasswordAuthenticationProvider provider; + + + public byte[] ComputeSha3(byte[] data, int bitLength = 256) + { + // 1. Initialize the digest (supports 224, 256, 384, 512) + var digest = new Sha3Digest(bitLength); + + // 3. Update the digest with data + digest.BlockUpdate(data, 0, data.Length); + + // 4. Retrieve the final hash + byte[] result = new byte[digest.GetDigestSize()]; + digest.DoFinal(result, 0); + + return result; + } + + public AuthenticationResult Process(object authData) + { + var remoteAuthData = (object[])authData; + var localAuthData = new List(); + + if (direction == AuthenticationDirection.Initiator) + { + if (mode == AuthenticationMode.None) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + else if (mode == AuthenticationMode.InitializerIdentity) + { + if (step == 0) + { + // step 0: send local nonce and initiator identity. + if (initiatorIdentity == null) + (initiatorIdentity, initiatorPassword) = provider.GetSelfIdentityAndCredential(domain, hostName); + else + initiatorPassword = provider.GetSelfCredential(initiatorIdentity, domain, hostName); + + if (initiatorPassword == null || initiatorIdentity == null) + return new AuthenticationResult(AuthenticationRuling.Failed, null); + + // send local nonce and initiator identity + localAuthData.Add(localNonce); + localAuthData.Add(initiatorIdentity); + + return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData); + + } + else if (step == 1) + { + // expect remote nonce, salt and challenge. + remoteNonce = (byte[])remoteAuthData[0]; + remoteSalt = (byte[])remoteAuthData[1]; + var remoteChallenge = (byte[])remoteAuthData[2]; + + // prevent reply attack by checking if remote nonce is same as local nonce. + if (remoteNonce.SequenceEqual(localNonce)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // make salted hash of password. + var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray()); + + + var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword) + .Concat(localNonce) + .ToArray()); + + // compare remote challenge + if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // make hash challenge response. + var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword) + .Concat(remoteNonce) + .ToArray()); + + localAuthData.Add(localChallenge); + step = -1; + + // derive a session key from nonces and password. + // initiator identity + initiator password + initiator nonce + responder nonce + + var sessionKey = ComputeSha3(initiatorIdentity.ToBytes() + .Concat(hashedPassword) + .Concat(localNonce) + .Concat(remoteNonce) + .ToArray(), 512); + + return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, initiatorIdentity, null, sessionKey); + } + else + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + } + else if (mode == AuthenticationMode.ResponderIdentity) + { + if (step == 0) + { + // just send local nonce. + localAuthData.Add(localNonce); + return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData); + } + else if (step == 1) + { + // expect responder identity and nonce. + remoteNonce = (byte[])remoteAuthData[0]; + responderIdentity = (string)remoteAuthData[1]; + + // prevent reply attack by checking if remote nonce is same as local nonce. + if (remoteNonce.SequenceEqual(localNonce)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // check if responder identity is valid and get password. + (localSalt, responderPassword) = provider.GetHostedAccountCredential(responderIdentity, domain); + + if (responderPassword == null) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // make hash challenge response. + var localChallenge = ComputeSha3(localNonce.Concat(responderPassword) + .Concat(remoteNonce) + .ToArray()); + // send localSalt and challenge + localAuthData.Add(localSalt); + localAuthData.Add(localChallenge); + step = 2; + + return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData); + + } + else if (step == 2) + { + // expect remote challenge. + var remoteChallenge = (byte[])remoteAuthData[0]; + + // compare remote challenge + + var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(responderPassword) + .Concat(localNonce) + .ToArray()); + + if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // derive a session key from nonces and password. + // responder identity + responder hashed password + initiator nonce + responder nonce + + var sessionKey = ComputeSha3(responderIdentity.ToBytes() + .Concat(responderPassword) + .Concat(localNonce) + .Concat(remoteNonce) + .ToArray(), 512); + + step = -1; + return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey); + + } + else + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + } + else if (mode == AuthenticationMode.DualIdentity) + { + if (step == 0) + { + // step 0: send local nonce and initiator identity. + if (initiatorIdentity == null) + (initiatorIdentity, initiatorPassword) = provider.GetSelfIdentityAndCredential(domain, hostName); + else + initiatorPassword = provider.GetSelfCredential(initiatorIdentity, domain, hostName); + + if (initiatorPassword == null || initiatorIdentity == null) + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + localAuthData.Add(localNonce); + localAuthData.Add(initiatorIdentity); + return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData); + + } + else if (step == 1) + { + // expect responder identity, nonce and salt. + remoteNonce = (byte[])remoteAuthData[0]; + responderIdentity = (string)remoteAuthData[1]; + remoteSalt = (byte[])remoteAuthData[2]; + + // prevent reply attack by checking if remote nonce is same as local nonce. + if (remoteNonce.SequenceEqual(localNonce)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // check if responder identity is valid and get password. + (localSalt, responderPassword) = provider.GetHostedAccountCredential(responderIdentity, domain); + + if (responderPassword == null) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // make salted hash of password. + var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray()); + + // make hash challenge response. + var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword) + .Concat(responderPassword) + .Concat(remoteNonce) + .ToArray()); + + // send localSalt and challenge + localAuthData.Add(localSalt); + localAuthData.Add(localChallenge); + step = 2; + + return new AuthenticationResult(AuthenticationRuling.InProgress, localAuthData); + + } + else if (step == 2) + { + // expect remote challenge. + var remoteChallenge = (byte[])remoteAuthData[0]; + + // make salted hash of password. + var hashedPassword = ComputeSha3(initiatorPassword.Concat(remoteSalt).ToArray()); + + // compare remote challenge + var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword) + .Concat(responderPassword) + .Concat(localNonce) + .ToArray()); + + if (!remoteChallenge.SequenceEqual(expectedRemoteChallenge)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // derive a session key from nonces and password. + // responder identity + responder password + initiator nonce + responder nonce + + var sessionKey = ComputeSha3(initiatorIdentity.ToBytes() + .Concat(responderIdentity.ToBytes()) + .Concat(hashedPassword) + .Concat(responderPassword) + .Concat(localNonce) + .Concat(remoteNonce) + .ToArray(), 512); + + step = -1; + return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey); + + } + else + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + } + } + else if (direction == AuthenticationDirection.Responder) + { + if (mode == AuthenticationMode.None) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + else if (mode == AuthenticationMode.InitializerIdentity) + { + if (step == 0) + { + if (remoteAuthData.Length < 2) + return new AuthenticationResult(AuthenticationRuling.Failed, null); + + // step 0: expect remote nonce and initiator identity. + remoteNonce = (byte[])remoteAuthData[0]; + initiatorIdentity = (string)remoteAuthData[1]; + + // prevent reply attack by checking if remote nonce is same as local nonce. + // @TODO: We can change our localNonce then send it + if (remoteNonce.SequenceEqual(localNonce)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // get initiator password from provider. + (localSalt, initiatorPassword) = provider.GetHostedAccountCredential(initiatorIdentity, domain); + + // account not found or no password for this account. + if (initiatorPassword == null || initiatorIdentity == null) + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + var localChallenge = ComputeSha3(localNonce.Concat(initiatorPassword) + .Concat(remoteNonce) + .ToArray()); + + // send local nonce, salt and challenge. + localAuthData.Add(localNonce); + localAuthData.Add(localSalt); + localAuthData.Add(localChallenge); + + step = 1; + return new AuthenticationResult(AuthenticationRuling.InProgress, + localAuthData); + } + else if (step == 1) + { + // expect challenge response. + var remoteChallenge = (byte[])remoteAuthData[0]; + + var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(initiatorPassword) + .Concat(localNonce) + .ToArray()); + // compare remote challenge + if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // compute session key. + + // derive a session key from nonces and password. + // initiator identity + initiator password + initiator nonce + responder nonce + + var sessionKey = ComputeSha3(initiatorIdentity.ToBytes() + .Concat(initiatorPassword) + .Concat(remoteNonce) + .Concat(localNonce) + .ToArray(), 512); + + step = -1; + return new AuthenticationResult(AuthenticationRuling.Succeeded, null, initiatorIdentity, responderIdentity, sessionKey); + + } + else + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + } + else if (mode == AuthenticationMode.ResponderIdentity) + { + if (step == 0) + { + if (remoteAuthData.Length < 1) + return new AuthenticationResult(AuthenticationRuling.Failed, null); + + // step 0: receive remote nonce. + remoteNonce = (byte[])remoteAuthData[0]; + + // prevent reply attack by checking if remote nonce is same as local nonce. + // @TODO: We can change our localNonce then send it + if (remoteNonce.SequenceEqual(localNonce)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // get responder identity from provider. + if (responderIdentity == null) + (responderIdentity, responderPassword) = provider.GetSelfIdentityAndCredential(domain, hostName); + else + responderPassword = provider.GetSelfCredential(responderIdentity, domain, hostName); + + if (responderPassword == null || responderIdentity == null) + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + localAuthData.Add(localNonce); + localAuthData.Add(responderIdentity); + + step = 1; + // send local nonce and identity. + return new AuthenticationResult(AuthenticationRuling.InProgress, + localAuthData + ); + } + else if (step == 1) + { + // expect remote salt and challenge. + remoteSalt = (byte[])remoteAuthData[0]; + var remoteChallenge = (byte[])remoteAuthData[1]; + + // compute expected challenge response. + var hashedPassword = ComputeSha3(responderPassword.Concat(remoteSalt).ToArray()); + + var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(hashedPassword) + .Concat(localNonce) + .ToArray()); + + // compare remote challenge + if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // compute our challenge response. + var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword) + .Concat(remoteNonce) + .ToArray()); + + // derive a session key from nonces and password. + // responder identity + responder hashed password + initiator nonce + responder nonce + + var sessionKey = ComputeSha3(responderIdentity.ToBytes() + .Concat(hashedPassword) + .Concat(remoteNonce) + .Concat(localNonce) + .ToArray(), 512); + + localAuthData.Add(localChallenge); + + step = -1; + return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, responderIdentity, null, sessionKey); + } + else + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + } + else if (mode == AuthenticationMode.DualIdentity) + { + if (step == 0) + { + if (remoteAuthData.Length < 2) + return new AuthenticationResult(AuthenticationRuling.Failed, null); + + // step 0: receive remote nonce and initiator identity. + remoteNonce = (byte[])remoteAuthData[0]; + initiatorIdentity = (string)remoteAuthData[1]; + + // prevent reply attack by checking if remote nonce is same as local nonce. + // @TODO: We can change our localNonce then send it + if (remoteNonce.SequenceEqual(localNonce)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // get responder identity from provider. + if (responderIdentity == null) + (responderIdentity, responderPassword) = provider.GetSelfIdentityAndCredential(domain, hostName); + else + responderPassword = provider.GetSelfCredential(responderIdentity, domain, hostName); + + if (responderPassword == null || responderIdentity == null) + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // get initiator password from provider. + (localSalt, initiatorPassword) = provider.GetHostedAccountCredential(initiatorIdentity, domain); + + // account not found or no password for this account. + if (initiatorPassword == null || initiatorIdentity == null) + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // send local nonce, salt and responder identity. + localAuthData.Add(localNonce); + localAuthData.Add(localSalt); + localAuthData.Add(responderIdentity); + + step = 1; + // send local nonce and identity. + return new AuthenticationResult(AuthenticationRuling.InProgress, + localAuthData + ); + + } + else if (step == 1) + { + // expect initiator salt and challenge. + var remoteSalt = (byte[])remoteAuthData[0]; + var remoteChallenge = (byte[])remoteAuthData[1]; + + // compute expected challenge response. + var hashedPassword = ComputeSha3(responderPassword.Concat(remoteSalt).ToArray()); + + // compare remote challenge + var expectedRemoteChallenge = ComputeSha3(remoteNonce.Concat(initiatorPassword) + .Concat(hashedPassword) + .Concat(localNonce) + .ToArray()); + + // compare remote challenge + if (!expectedRemoteChallenge.SequenceEqual(remoteChallenge)) + { + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + // compute our challenge + var localChallenge = ComputeSha3(localNonce.Concat(hashedPassword) + .Concat(initiatorPassword) + .Concat(remoteNonce) + .ToArray()); + + localAuthData.Add(localChallenge); + + // derive a session key from nonces and password. + // responder identity + responder password + initiator nonce + responder nonce + + var sessionKey = ComputeSha3(initiatorIdentity.ToBytes() + .Concat(responderIdentity.ToBytes()) + .Concat(initiatorPassword) + .Concat(hashedPassword) + .Concat(remoteNonce) + .Concat(localNonce) + .ToArray(), 512); + + step = -1; + return new AuthenticationResult(AuthenticationRuling.Succeeded, localAuthData, initiatorIdentity, responderIdentity, sessionKey); + + } + else + { + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + } + } + + + step = -1; + return new AuthenticationResult(AuthenticationRuling.Failed, null); + } + + public PasswordAuthenticationHandler(AuthenticationMode mode, + AuthenticationDirection direction, + string initiatorIdentity, + string responderIdentity, + string hostName, + string domain, + PasswordAuthenticationProvider provider) + { + localNonce = Global.GenerateBytes(20); + + this.provider = provider; + this.initiatorIdentity = initiatorIdentity; + this.responderIdentity = responderIdentity; + this.mode = mode; + this.direction = direction; + this.domain = domain; + this.hostName = hostName; + } + } +} diff --git a/Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationProvider.cs b/Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationProvider.cs new file mode 100644 index 0000000..61753f2 --- /dev/null +++ b/Libraries/Esiur/Security/Authority/Providers/PasswordAuthenticationProvider.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Security.Authority.Providers +{ + internal class PasswordAuthenticationProvider : IAuthenticationProvider + { + public string DefaultName => "hash"; + + public IAuthenticationHandler CreateAuthenticationHandler(AuthenticationContext context) + { + var authHandler = new PasswordAuthenticationHandler(context.Mode, + context.Direction, + context.InitiatorIdentity, + context.ResponderIdentity, + context.HostName, + context.Domain, + this); + + return authHandler; + } + + public virtual (byte[], byte[]) GetHostedAccountCredential(string identity, string domain) + { + return (null, null); + } + + public virtual (string, byte[]) GetSelfIdentityAndCredential(string domain, string hostname) + { + return (null, null); + } + + public virtual byte[] GetSelfCredential(string identity, string domain, string hostname) + { + return null; + } + } +} diff --git a/Libraries/Esiur/Security/Authority/ResponderAuthenticationContext.cs b/Libraries/Esiur/Security/Authority/ResponderAuthenticationContext.cs deleted file mode 100644 index 0d565fb..0000000 --- a/Libraries/Esiur/Security/Authority/ResponderAuthenticationContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Esiur.Security.Authority -{ - public sealed class ResponderAuthenticationContext - { - public string? RemoteIpAddress { get; } - - public string? LocalDomain { get; } - - public AuthenticationMode Mode { get; } - - public IReadOnlyDictionary Headers { get; } - = new Dictionary(StringComparer.OrdinalIgnoreCase); - } -} diff --git a/Libraries/Esiur/Security/Authority/Session.cs b/Libraries/Esiur/Security/Authority/Session.cs index c804c0e..28171cd 100644 --- a/Libraries/Esiur/Security/Authority/Session.cs +++ b/Libraries/Esiur/Security/Authority/Session.cs @@ -59,5 +59,6 @@ public class Session public IAuthenticationHandler AuthenticationHandler { get; set; } //public IAuthenticationHandler AuthenticationResponder { get; set; } - public string AuthorizedIdentity { get; set; } + public string LocalIdentity { get; set; } + public string RemoteIdentity { get; set; } } diff --git a/Libraries/Esiur/Security/Permissions/IPermissionsManager.cs b/Libraries/Esiur/Security/Permissions/IPermissionsManager.cs index a7dec3e..c45ef80 100644 --- a/Libraries/Esiur/Security/Permissions/IPermissionsManager.cs +++ b/Libraries/Esiur/Security/Permissions/IPermissionsManager.cs @@ -52,4 +52,5 @@ public interface IPermissionsManager bool Initialize(Map settings, IResource resource); Map Settings { get; } + } diff --git a/Libraries/Esiur/Security/Permissions/UserPermissionsManager.cs b/Libraries/Esiur/Security/Permissions/UserPermissionsManager.cs index 1f4484f..e2c4122 100644 --- a/Libraries/Esiur/Security/Permissions/UserPermissionsManager.cs +++ b/Libraries/Esiur/Security/Permissions/UserPermissionsManager.cs @@ -44,8 +44,8 @@ public class UserPermissionsManager : IPermissionsManager { Map userPermissions = null; - if (settings.ContainsKey(session.AuthorizedIdentity)) - userPermissions = settings[session.AuthorizedIdentity] as Map; + if (settings.ContainsKey(session.RemoteIdentity)) + userPermissions = settings[session.RemoteIdentity] as Map; else if (settings.ContainsKey("public")) userPermissions = settings["public"] as Map; else diff --git a/Stores/Esiur.Stores.EntityCore/EsiurExtensions.cs b/Stores/Esiur.Stores.EntityCore/EsiurExtensions.cs index 4db4441..b906607 100644 --- a/Stores/Esiur.Stores.EntityCore/EsiurExtensions.cs +++ b/Stores/Esiur.Stores.EntityCore/EsiurExtensions.cs @@ -131,7 +131,7 @@ public static class EsiurExtensions var id = store.TypesByType[typeof(T)].PrimaryKey.GetValue(resource); - await options.Warehouse.Put($"{store.Instance.Name}/{typeof(T).Name}/{id}", res, 0, manager); + await options.Warehouse.Put($"{store.Instance.Name}/{typeof(T).Name}/{id}", res, new ResourceContext(0, null, null, manager)); return (T)res; } diff --git a/Stores/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs b/Stores/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs index 6c1cef5..b2519e7 100644 --- a/Stores/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs +++ b/Stores/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs @@ -69,9 +69,9 @@ public class EsiurProxyRewrite : IModelFinalizingConvention if (Codec.ImplementsInterface(entityType.ClrType, typeof(IResource))) { // check if the object exists - var obj = options.Warehouse.Create(entityType.ClrType) as IResource; + var obj = options.Warehouse.Create(entityType.ClrType, null) as IResource; options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id); - options.Warehouse.Put($"{options.Store.Instance.Name}/{entityType.ClrType.Name}/{id}", obj, 0, manager).Wait(); + options.Warehouse.Put($"{options.Store.Instance.Name}/{entityType.ClrType.Name}/{id}", obj, new ResourceContext(0, null, null, manager)).Wait(); return obj; } else diff --git a/Stores/Esiur.Stores.MongoDB/MongoDBStoreGeneric.cs b/Stores/Esiur.Stores.MongoDB/MongoDBStoreGeneric.cs index 18d4c79..97c5a82 100644 --- a/Stores/Esiur.Stores.MongoDB/MongoDBStoreGeneric.cs +++ b/Stores/Esiur.Stores.MongoDB/MongoDBStoreGeneric.cs @@ -36,7 +36,7 @@ namespace Esiur.Stores.MongoDB public class MongoDBStore : MongoDBStore where T:IResource { [Export] - public async AsyncReply New(string name = null, object properties = null) + public async AsyncReply New(string name = null, Map properties = null) { var resource = Instance.Warehouse.Create(properties); await Instance.Warehouse.Put(this.Instance.Name + "/" + name, resource); diff --git a/Tests/Features/Functional/MyService.cs b/Tests/Features/Functional/MyService.cs index d20bac9..f3cf53b 100644 --- a/Tests/Features/Functional/MyService.cs +++ b/Tests/Features/Functional/MyService.cs @@ -33,13 +33,6 @@ public partial class MyService [Export] bool boolean = true; [Export] bool[] booleanArray = new bool[] { true, false, true, false, true }; - [Export] - public MyGenericRecord GetGenericRecord() - { - return new MyGenericRecord() { Needed = 3, Start = 10, Results = new MyResource[0], Total = 102 }; - } - - [Export] public static string staticFunction(string name) => $"Hello {name}"; [Export] byte uInt8Test = 8; [Export] byte? uInt8Null = null; @@ -85,25 +78,33 @@ public partial class MyService [Export] DateTime time = DateTime.Now; + [Export] public const double PI = Math.PI; + + [Export] public MyService Me => this; + + [Export] int PrivateInt32 { get; set; } = 99; + + [Export("Object")] object objectTest = "String as object"; + + [Export] object[] objectArray = new object[] { 1, 1.2f, Math.PI, "Hello World" }; + [Export] Map stringMap = new Map() { - ["int"] = 33, - ["string"] = "Hello World" + ["Sequence"] = 123, + ["Message"] = "Hello World" }; [Export] Map intStringMap = new() { [4] = "Abcd", - [44] = "EfG" + [3] = "EfG" }; - [Export("Object")] object objectTest = "object"; - [Export] object[] objectArray = new object[] { 1, 1.2f, Math.PI, "Hello World" }; [Export] public PropertyContext PropertyContext @@ -119,10 +120,10 @@ public partial class MyService int MyPasscode = 2025; public PropertyContext Passcode { - get => new((sender) => sender.Session.AuthorizedIdentity == "alice" ? MyPasscode : 0); + get => new((sender) => sender.Session.RemoteIdentity == "alice" ? MyPasscode : 0); set { - if (value.Connection.Session.AuthorizedIdentity != "alice") + if (value.Connection.Session.RemoteIdentity != "alice") throw new Exception("Only Alice is allowed."); MyPasscode = value.Value; } @@ -159,9 +160,6 @@ public partial class MyService [Export] public void InvokeEvents(string msg, InvocationContext context) { - //if (context.Connection.Session.AuthorizedAccount != "Alice") - // throw new Exception("Only Alice is allowed."); - StringEvent?.Invoke(msg); ArrayEvent?.Invoke(new object[] { DateTime.UtcNow, "Event", msg }); } @@ -207,9 +205,13 @@ public partial class MyService return record; } - [Export] public const double PI = Math.PI; - [Export] public MyService Me => this; + [Export] + public MyGenericRecord GetGenericRecord() + { + return new MyGenericRecord() { Needed = 3, Start = 10, Results = new MyResource[0], Total = 102 }; + } + + [Export] public static string staticFunction(string name) => $"Hello {name}"; - [Export] int PrivateInt32 { get; set; } = 99; } diff --git a/Tests/Features/Functional/Program.cs b/Tests/Features/Functional/Program.cs index e766586..092a732 100644 --- a/Tests/Features/Functional/Program.cs +++ b/Tests/Features/Functional/Program.cs @@ -88,7 +88,7 @@ class Program //TestSerialization(10.1d); //TestSerialization((byte)1); //TestSerialization((byte)2); - TestSerialization(new int[] { 1, 2, 3, 4 }); + //TestSerialization(new int[] { 1, 2, 3, 4 }); //var x = LogLevel.Warning; //TestSerialization(LogLevel.Warning); @@ -213,18 +213,22 @@ class Program private static async void TestClient(IResource local) { - var con = await new Warehouse().Get("EP://localhost", new EpConnectionConfig + var con = await new Warehouse().Get("EP://localhost", new EpConnectionContext { AutoReconnect = true, - Username = "admin", - Password = "admin", - Authenticator = Authenticator + //Username = "admin", + //Password = "admin", + Identity = "demo", + AuthenticationProtocol = "hash" }); - dynamic remote = await con.Get("sys/service"); - var gr = await remote.GetGenericRecord(); - Console.WriteLine(gr); + dynamic remote = await con.Get("sys/service"); + + TestObjectProps(local, remote); + + //return; + //return; Console.WriteLine("OK"); @@ -236,11 +240,8 @@ class Program var temp = await con.Call("temp"); Console.WriteLine("Temp: " + temp.GetHashCode()); - //var template = await con.GetTemplateByClassName("Test.MyResource"); - TestObjectProps(local, remote); - var opt = await remote.Optional(new { a1 = 22, a2 = 33, a4 = "What?" }); Console.WriteLine(opt);