mirror of
				https://github.com/esiur/esiur-dotnet.git
				synced 2025-11-04 01:11:35 +00:00 
			
		
		
		
	.Net 6 Upgrade
This commit is contained in:
		@@ -32,44 +32,42 @@ using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
 | 
			
		||||
public class EntityResource : IResource
 | 
			
		||||
{
 | 
			
		||||
    
 | 
			
		||||
    public class EntityResource : IResource
 | 
			
		||||
    //[NotMapped]
 | 
			
		||||
    //internal object _PrimaryId;
 | 
			
		||||
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
    //public event PropertyChangedEventHandler PropertyChanged;
 | 
			
		||||
 | 
			
		||||
    [NotMapped]
 | 
			
		||||
    public Instance Instance { get; set; }
 | 
			
		||||
 | 
			
		||||
    public EntityResource()
 | 
			
		||||
    {
 | 
			
		||||
        //[NotMapped]
 | 
			
		||||
        //internal object _PrimaryId;
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        //public event PropertyChangedEventHandler PropertyChanged;
 | 
			
		||||
 | 
			
		||||
        [NotMapped]
 | 
			
		||||
        public Instance Instance { get; set; }
 | 
			
		||||
 | 
			
		||||
        public EntityResource()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        protected virtual void Create()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
                Create();
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected virtual void Create()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            Create();
 | 
			
		||||
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -34,209 +34,207 @@ using System.Linq;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Metadata;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
public class EntityStore : IStore
 | 
			
		||||
{
 | 
			
		||||
    public class EntityStore : IStore
 | 
			
		||||
    public Instance Instance { get; set; }
 | 
			
		||||
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    Dictionary<Type, Dictionary<object, WeakReference>> DB = new Dictionary<Type, Dictionary<object, WeakReference>>();
 | 
			
		||||
    object DBLock = new object();
 | 
			
		||||
 | 
			
		||||
    Dictionary<string, EntityTypeInfo> TypesByName = new Dictionary<string, EntityTypeInfo>();
 | 
			
		||||
    internal Dictionary<Type, EntityTypeInfo> TypesByType = new Dictionary<Type, EntityTypeInfo>();
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public Func<DbContext> Getter { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<IResource> Get(string path)
 | 
			
		||||
    {
 | 
			
		||||
        public Instance Instance { get; set; }
 | 
			
		||||
        var p = path.Split('/');
 | 
			
		||||
        var ti = TypesByName[p[0]];
 | 
			
		||||
        var id = Convert.ChangeType(p[1], ti.PrimaryKey.PropertyType);
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        // Get db
 | 
			
		||||
        var db = Getter();
 | 
			
		||||
        var res = db.Find(ti.Type.ClrType, id);
 | 
			
		||||
 | 
			
		||||
        Dictionary<Type, Dictionary<object, WeakReference>> DB = new Dictionary<Type, Dictionary<object, WeakReference>>();
 | 
			
		||||
        object DBLock = new object();
 | 
			
		||||
        // load navigation properties
 | 
			
		||||
        var ent = db.Entry(res);
 | 
			
		||||
        foreach (var rf in ent.References)
 | 
			
		||||
            rf.Load();
 | 
			
		||||
 | 
			
		||||
        Dictionary<string, EntityTypeInfo> TypesByName = new Dictionary<string, EntityTypeInfo>();
 | 
			
		||||
        internal Dictionary<Type, EntityTypeInfo> TypesByType = new Dictionary<Type, EntityTypeInfo>();
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public Func<DbContext> Getter { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<IResource> Get(string path)
 | 
			
		||||
        {
 | 
			
		||||
            var p = path.Split('/');
 | 
			
		||||
            var ti = TypesByName[p[0]];
 | 
			
		||||
            var id = Convert.ChangeType(p[1], ti.PrimaryKey.PropertyType);
 | 
			
		||||
 | 
			
		||||
            // Get db
 | 
			
		||||
            var db = Getter();
 | 
			
		||||
            var res = db.Find(ti.Type.ClrType, id);
 | 
			
		||||
 | 
			
		||||
            // load navigation properties
 | 
			
		||||
            var ent = db.Entry(res);
 | 
			
		||||
            foreach (var rf in ent.References)
 | 
			
		||||
                rf.Load();
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<IResource>(res as IResource);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Put(IResource resource)
 | 
			
		||||
        {
 | 
			
		||||
            if (resource == this)
 | 
			
		||||
                return new AsyncReply<bool>(true);
 | 
			
		||||
 | 
			
		||||
            var type = ResourceProxy.GetBaseType(resource);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var eid = TypesByType[type].PrimaryKey.GetValue(resource);
 | 
			
		||||
 | 
			
		||||
            lock (DBLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (DB[type].ContainsKey(eid))
 | 
			
		||||
                    DB[type].Remove(eid);
 | 
			
		||||
 | 
			
		||||
                DB[type].Add(eid, new WeakReference(resource));
 | 
			
		||||
            }
 | 
			
		||||
        return new AsyncReply<IResource>(res as IResource);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Put(IResource resource)
 | 
			
		||||
    {
 | 
			
		||||
        if (resource == this)
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
 | 
			
		||||
        var type = ResourceProxy.GetBaseType(resource);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var eid = TypesByType[type].PrimaryKey.GetValue(resource);
 | 
			
		||||
 | 
			
		||||
        lock (DBLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (DB[type].ContainsKey(eid))
 | 
			
		||||
                DB[type].Remove(eid);
 | 
			
		||||
 | 
			
		||||
            DB[type].Add(eid, new WeakReference(resource));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IResource GetById(Type type, object id)
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IResource GetById(Type type, object id)
 | 
			
		||||
    {
 | 
			
		||||
        lock (DBLock)
 | 
			
		||||
        {
 | 
			
		||||
            lock (DBLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (!DB[type].ContainsKey(id))
 | 
			
		||||
                    return null;
 | 
			
		||||
            if (!DB[type].ContainsKey(id))
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
                if (!DB[type][id].IsAlive)
 | 
			
		||||
                    return null;
 | 
			
		||||
            if (!DB[type][id].IsAlive)
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
                return DB[type][id].Target as IResource;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //public T CreateDB()
 | 
			
		||||
        //{
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //DbContext dbContext;
 | 
			
		||||
        //[Attribute]
 | 
			
		||||
        //public DbContext DbContext { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Link(IResource resource)
 | 
			
		||||
        {
 | 
			
		||||
            var type = ResourceProxy.GetBaseType(resource.GetType());
 | 
			
		||||
 | 
			
		||||
            var id = TypesByType[type].PrimaryKey.GetValue(resource);
 | 
			
		||||
            //DbContext.Model.FindEntityType(type).DisplayName();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //            DbContext.Model.FindEntityType(type).DisplayName
 | 
			
		||||
            //var entityType = DbContext.Model.FindEntityType(type);
 | 
			
		||||
            //var id = entityType.FindPrimaryKey().Properties
 | 
			
		||||
            //            .FirstOrDefault()?.PropertyInfo
 | 
			
		||||
            //            .GetValue(resource);
 | 
			
		||||
            //        var id = Types
 | 
			
		||||
 | 
			
		||||
            if (id != null)
 | 
			
		||||
                return this.Instance.Name + "/" + type.Name + "/" + id.ToString();
 | 
			
		||||
            else
 | 
			
		||||
                return this.Instance.Name + "/" + type.Name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
            //throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
            //throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Remove(IResource resource)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> AddChild(IResource parent, IResource child)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> RemoveChild(IResource parent, IResource child)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> AddParent(IResource child, IResource parent)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> RemoveParent(IResource child, IResource parent)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal DbContextOptions Options { get; set; }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)// SystemInitialized && DbContext != null)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (Getter == null)
 | 
			
		||||
                    throw new Exception("Getter is not set for the store.");
 | 
			
		||||
                  //  DbContextProvider = () => Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                ReloadModel();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ReloadModel()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            TypesByName.Clear();
 | 
			
		||||
            TypesByType.Clear();
 | 
			
		||||
 | 
			
		||||
            var context = Getter();
 | 
			
		||||
 | 
			
		||||
            var types = context.Model.GetEntityTypes();
 | 
			
		||||
            foreach (var t in types)
 | 
			
		||||
            {
 | 
			
		||||
                var ti = new EntityTypeInfo()
 | 
			
		||||
                {
 | 
			
		||||
                    Name = t.ClrType.Name,
 | 
			
		||||
                    PrimaryKey = t.FindPrimaryKey().Properties.FirstOrDefault()?.PropertyInfo,
 | 
			
		||||
                    Type = t,
 | 
			
		||||
                    //Getter = getter
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                TypesByName.Add(t.ClrType.Name, ti);
 | 
			
		||||
                TypesByType.Add(t.ClrType, ti);
 | 
			
		||||
 | 
			
		||||
                if (!DB.ContainsKey(t.ClrType))
 | 
			
		||||
                    DB.Add(t.ClrType, new Dictionary<object, WeakReference>());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
            return DB[type][id].Target as IResource;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //public T CreateDB()
 | 
			
		||||
    //{
 | 
			
		||||
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //DbContext dbContext;
 | 
			
		||||
    //[Attribute]
 | 
			
		||||
    //public DbContext DbContext { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string Link(IResource resource)
 | 
			
		||||
    {
 | 
			
		||||
        var type = ResourceProxy.GetBaseType(resource.GetType());
 | 
			
		||||
 | 
			
		||||
        var id = TypesByType[type].PrimaryKey.GetValue(resource);
 | 
			
		||||
        //DbContext.Model.FindEntityType(type).DisplayName();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //            DbContext.Model.FindEntityType(type).DisplayName
 | 
			
		||||
        //var entityType = DbContext.Model.FindEntityType(type);
 | 
			
		||||
        //var id = entityType.FindPrimaryKey().Properties
 | 
			
		||||
        //            .FirstOrDefault()?.PropertyInfo
 | 
			
		||||
        //            .GetValue(resource);
 | 
			
		||||
        //        var id = Types
 | 
			
		||||
 | 
			
		||||
        if (id != null)
 | 
			
		||||
            return this.Instance.Name + "/" + type.Name + "/" + id.ToString();
 | 
			
		||||
        else
 | 
			
		||||
            return this.Instance.Name + "/" + type.Name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
        //throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
        //throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Remove(IResource resource)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> AddChild(IResource parent, IResource child)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> RemoveChild(IResource parent, IResource child)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> AddParent(IResource child, IResource parent)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> RemoveParent(IResource child, IResource parent)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal DbContextOptions Options { get; set; }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)// SystemInitialized && DbContext != null)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (Getter == null)
 | 
			
		||||
                throw new Exception("Getter is not set for the store.");
 | 
			
		||||
            //  DbContextProvider = () => Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            ReloadModel();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void ReloadModel()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        TypesByName.Clear();
 | 
			
		||||
        TypesByType.Clear();
 | 
			
		||||
 | 
			
		||||
        var context = Getter();
 | 
			
		||||
 | 
			
		||||
        var types = context.Model.GetEntityTypes();
 | 
			
		||||
        foreach (var t in types)
 | 
			
		||||
        {
 | 
			
		||||
            var ti = new EntityTypeInfo()
 | 
			
		||||
            {
 | 
			
		||||
                Name = t.ClrType.Name,
 | 
			
		||||
                PrimaryKey = t.FindPrimaryKey().Properties.FirstOrDefault()?.PropertyInfo,
 | 
			
		||||
                Type = t,
 | 
			
		||||
                //Getter = getter
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            TypesByName.Add(t.ClrType.Name, ti);
 | 
			
		||||
            TypesByType.Add(t.ClrType, ti);
 | 
			
		||||
 | 
			
		||||
            if (!DB.ContainsKey(t.ClrType))
 | 
			
		||||
                DB.Add(t.ClrType, new Dictionary<object, WeakReference>());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,12 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
 | 
			
		||||
struct EntityTypeInfo
 | 
			
		||||
{
 | 
			
		||||
    struct EntityTypeInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Name;
 | 
			
		||||
        public IEntityType Type;
 | 
			
		||||
        public PropertyInfo PrimaryKey;
 | 
			
		||||
       // public Func<DbContext> Getter;
 | 
			
		||||
    }
 | 
			
		||||
    public string Name;
 | 
			
		||||
    public IEntityType Type;
 | 
			
		||||
    public PropertyInfo PrimaryKey;
 | 
			
		||||
    // public Func<DbContext> Getter;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>netstandard2.1</TargetFramework>
 | 
			
		||||
    <TargetFramework>net6.0</TargetFramework>
 | 
			
		||||
    <AssemblyName>Esiur.Stores.EntityCore</AssemblyName>
 | 
			
		||||
    <Authors>Ahmed Kh. Zamil</Authors>
 | 
			
		||||
    <Company>Esiur Foundation</Company>
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
			
		||||
    <PackageId>Esiur.Stores.EntityCore</PackageId>
 | 
			
		||||
    <Version>1.2.5</Version>
 | 
			
		||||
	<LangVersion>latest</LangVersion>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
@@ -21,7 +22,7 @@
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.6" />
 | 
			
		||||
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
 | 
			
		||||
    <PackageReference Include="System.Collections" Version="4.3.0" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,79 +36,83 @@ using System.Reflection;
 | 
			
		||||
using Esiur.Proxy;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
 | 
			
		||||
public class EsiurExtensionOptions : IDbContextOptionsExtension
 | 
			
		||||
{
 | 
			
		||||
    public class EsiurExtensionOptions : IDbContextOptionsExtension 
 | 
			
		||||
 | 
			
		||||
    //public Dictionary<Type, PropertyInfo> Cache { get; } = new Dictionary<Type, PropertyInfo>();
 | 
			
		||||
    //public void AddType(IEntityType type)
 | 
			
		||||
    //{
 | 
			
		||||
    //    if (!Cache.ContainsKey(type.ClrType))
 | 
			
		||||
    //        Cache.Add(type.ClrType, type.FindPrimaryKey().Properties[0].PropertyInfo);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private DbContextOptionsExtensionInfo _info;
 | 
			
		||||
    EntityStore _store;
 | 
			
		||||
 | 
			
		||||
    public DbContextOptionsExtensionInfo Info => _info;
 | 
			
		||||
 | 
			
		||||
    public EntityStore Store => _store;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void ApplyServices(IServiceCollection services)
 | 
			
		||||
    {
 | 
			
		||||
        // services.AddEntityFrameworkProxies();
 | 
			
		||||
 | 
			
		||||
        new EntityFrameworkServicesBuilder(services)
 | 
			
		||||
            .TryAdd<IConventionSetPlugin, EsiurPlugin>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Validate(IDbContextOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
 | 
			
		||||
        if (internalServiceProvider != null)
 | 
			
		||||
        {
 | 
			
		||||
            var scope = internalServiceProvider.CreateScope();
 | 
			
		||||
            var conventionPlugins = scope.ServiceProvider.GetService<IEnumerable<IConventionSetPlugin>>();
 | 
			
		||||
            if (conventionPlugins?.Any(s => s is EsiurPlugin) == false)
 | 
			
		||||
            {
 | 
			
		||||
                throw new InvalidOperationException("");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public EsiurExtensionOptions(EntityStore store)
 | 
			
		||||
    {
 | 
			
		||||
        _info = new ExtensionInfo(this);
 | 
			
		||||
        _store = store;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        //public Dictionary<Type, PropertyInfo> Cache { get; } = new Dictionary<Type, PropertyInfo>();
 | 
			
		||||
        //public void AddType(IEntityType type)
 | 
			
		||||
        //{
 | 
			
		||||
        //    if (!Cache.ContainsKey(type.ClrType))
 | 
			
		||||
        //        Cache.Add(type.ClrType, type.FindPrimaryKey().Properties[0].PropertyInfo);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        private DbContextOptionsExtensionInfo _info;
 | 
			
		||||
        EntityStore _store;
 | 
			
		||||
 | 
			
		||||
        public DbContextOptionsExtensionInfo Info => _info;
 | 
			
		||||
 | 
			
		||||
        public EntityStore Store => _store;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void ApplyServices(IServiceCollection services)
 | 
			
		||||
        public ExtensionInfo(IDbContextOptionsExtension extension)
 | 
			
		||||
            : base(extension)
 | 
			
		||||
        {
 | 
			
		||||
           // services.AddEntityFrameworkProxies();
 | 
			
		||||
 | 
			
		||||
            new EntityFrameworkServicesBuilder(services)
 | 
			
		||||
                .TryAdd<IConventionSetPlugin, EsiurPlugin>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Validate(IDbContextOptions options)
 | 
			
		||||
        {
 | 
			
		||||
            var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
 | 
			
		||||
            if (internalServiceProvider != null)
 | 
			
		||||
            {
 | 
			
		||||
                var scope = internalServiceProvider.CreateScope();
 | 
			
		||||
                var conventionPlugins = scope.ServiceProvider.GetService<IEnumerable<IConventionSetPlugin>>();
 | 
			
		||||
                if (conventionPlugins?.Any(s => s is EsiurPlugin) == false)
 | 
			
		||||
                {
 | 
			
		||||
                    throw new InvalidOperationException("");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        private new EsiurExtensionOptions Extension
 | 
			
		||||
            => (EsiurExtensionOptions)base.Extension;
 | 
			
		||||
 | 
			
		||||
        public EsiurExtensionOptions(EntityStore store)
 | 
			
		||||
        {
 | 
			
		||||
            _info = new ExtensionInfo(this);
 | 
			
		||||
            _store = store;
 | 
			
		||||
        }
 | 
			
		||||
        public override bool IsDatabaseProvider => false;
 | 
			
		||||
 | 
			
		||||
        public override string LogFragment => "Esiur";
 | 
			
		||||
 | 
			
		||||
        private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
 | 
			
		||||
        public override int GetServiceProviderHashCode() => 2312;
 | 
			
		||||
 | 
			
		||||
        public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            public ExtensionInfo(IDbContextOptionsExtension extension)
 | 
			
		||||
                : base(extension)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            private new EsiurExtensionOptions Extension
 | 
			
		||||
                => (EsiurExtensionOptions)base.Extension;
 | 
			
		||||
 | 
			
		||||
            public override bool IsDatabaseProvider => false;
 | 
			
		||||
 | 
			
		||||
            public override string LogFragment => "Esiur";
 | 
			
		||||
 | 
			
		||||
            public override long GetServiceProviderHashCode() => 2312;
 | 
			
		||||
 | 
			
		||||
            public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,74 +37,62 @@ using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
 | 
			
		||||
public static class EsiurExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static class EsiurExtensions
 | 
			
		||||
    //public static T CreateResource<T>(this DbContext dbContext, object properties = null) where T:class,IResource
 | 
			
		||||
    //{
 | 
			
		||||
    //    return dbContext.GetInfrastructure().CreateResource<T>(properties);
 | 
			
		||||
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    public static T AddResource<T>(this DbSet<T> dbSet, T resource) where T : class, IResource
 | 
			
		||||
        => AddResourceAsync(dbSet, resource).Wait();
 | 
			
		||||
 | 
			
		||||
    public static async AsyncReply<T> AddResourceAsync<T>(this DbSet<T> dbSet, T resource) where T : class, IResource
 | 
			
		||||
    {
 | 
			
		||||
        //public static T CreateResource<T>(this DbContext dbContext, object properties = null) where T:class,IResource
 | 
			
		||||
        //{
 | 
			
		||||
        //    return dbContext.GetInfrastructure().CreateResource<T>(properties);
 | 
			
		||||
        var store = dbSet.GetInfrastructure().GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions>().Store;
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        public static T AddResource<T>(this DbSet<T> dbSet, T resource) where T : class, IResource
 | 
			
		||||
            => AddResourceAsync(dbSet, resource).Wait();
 | 
			
		||||
        var manager = store.Instance.Managers.FirstOrDefault();// > 0 ? store.Instance.Managers.First() : null;
 | 
			
		||||
 | 
			
		||||
        public static async AsyncReply<T> AddResourceAsync<T>(this DbSet<T> dbSet, T resource) where T : class, IResource
 | 
			
		||||
        //var db = dbSet.GetService<ICurrentDbContext>().Context;
 | 
			
		||||
 | 
			
		||||
        //var resource = dbSet.GetInfrastructure().CreateResource<T>(properties);
 | 
			
		||||
        //var resource = Warehouse.New<T>("", options.Store, null, null, null, properties);
 | 
			
		||||
 | 
			
		||||
        var resType = typeof(T);
 | 
			
		||||
        var proxyType = ResourceProxy.GetProxy(resType);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        IResource res;
 | 
			
		||||
 | 
			
		||||
        if (proxyType == resType)
 | 
			
		||||
        {
 | 
			
		||||
            var store = dbSet.GetInfrastructure().GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions>().Store;
 | 
			
		||||
            res = resource;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            res = Activator.CreateInstance(proxyType) as IResource;
 | 
			
		||||
            var ps = Structure.FromObject(resource);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var manager = store.Instance.Managers.FirstOrDefault();// > 0 ? store.Instance.Managers.First() : null;
 | 
			
		||||
 | 
			
		||||
            //var db = dbSet.GetService<ICurrentDbContext>().Context;
 | 
			
		||||
 | 
			
		||||
            //var resource = dbSet.GetInfrastructure().CreateResource<T>(properties);
 | 
			
		||||
            //var resource = Warehouse.New<T>("", options.Store, null, null, null, properties);
 | 
			
		||||
 | 
			
		||||
            var resType = typeof(T);
 | 
			
		||||
            var proxyType = ResourceProxy.GetProxy(resType);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            IResource res;
 | 
			
		||||
 | 
			
		||||
            if (proxyType == resType)
 | 
			
		||||
            foreach (var p in ps)
 | 
			
		||||
            {
 | 
			
		||||
                res = resource;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                res = Activator.CreateInstance(proxyType) as IResource;
 | 
			
		||||
                var ps = Structure.FromObject(resource);
 | 
			
		||||
 | 
			
		||||
                foreach (var p in ps)
 | 
			
		||||
                var mi = resType.GetMember(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
 | 
			
		||||
                                .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
                if (mi != null)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    var mi = resType.GetMember(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
 | 
			
		||||
                                    .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
                    if (mi != null)
 | 
			
		||||
                    if (mi is PropertyInfo)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (mi is PropertyInfo)
 | 
			
		||||
                        {
 | 
			
		||||
                            var pi = mi as PropertyInfo;
 | 
			
		||||
                            if (pi.CanWrite)
 | 
			
		||||
                            {
 | 
			
		||||
                                try
 | 
			
		||||
                                {
 | 
			
		||||
                                    pi.SetValue(res, p.Value);
 | 
			
		||||
                                }
 | 
			
		||||
                                catch (Exception ex)
 | 
			
		||||
                                {
 | 
			
		||||
                                    Global.Log(ex);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (mi is FieldInfo)
 | 
			
		||||
                        var pi = mi as PropertyInfo;
 | 
			
		||||
                        if (pi.CanWrite)
 | 
			
		||||
                        {
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                (mi as FieldInfo).SetValue(res, p.Value);
 | 
			
		||||
                                pi.SetValue(res, p.Value);
 | 
			
		||||
                            }
 | 
			
		||||
                            catch (Exception ex)
 | 
			
		||||
                            {
 | 
			
		||||
@@ -112,90 +100,101 @@ namespace Esiur.Stores.EntityCore
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (mi is FieldInfo)
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            (mi as FieldInfo).SetValue(res, p.Value);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex)
 | 
			
		||||
                        {
 | 
			
		||||
                            Global.Log(ex);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //await Warehouse.Put<T>("", null, null, null, null, properties);
 | 
			
		||||
            var entity = dbSet.Add((T)res);
 | 
			
		||||
            await entity.Context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
            var id = store.TypesByType[typeof(T)].PrimaryKey.GetValue(resource);
 | 
			
		||||
 | 
			
		||||
            await Warehouse.Put(id.ToString(), res, store, null, null, 0, manager);
 | 
			
		||||
 | 
			
		||||
            return (T)res;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //public static async AsyncReply<T> CreateResourceAsync<T>(this IServiceProvider serviceProvider, T properties = null) where T : class, IResource
 | 
			
		||||
        //{
 | 
			
		||||
        //    var options = serviceProvider.GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions<T>>();
 | 
			
		||||
        //await Warehouse.Put<T>("", null, null, null, null, properties);
 | 
			
		||||
        var entity = dbSet.Add((T)res);
 | 
			
		||||
        await entity.Context.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
        //    var resource = await Warehouse.New<T>("", options.Store, null, null, null, properties);
 | 
			
		||||
        var id = store.TypesByType[typeof(T)].PrimaryKey.GetValue(resource);
 | 
			
		||||
 | 
			
		||||
        //    resource.Instance.Managers.AddRange(options.Store.Instance.Managers.ToArray());
 | 
			
		||||
        await Warehouse.Put(id.ToString(), res, store, null, null, 0, manager);
 | 
			
		||||
 | 
			
		||||
        //    return resource;
 | 
			
		||||
        //}
 | 
			
		||||
        return (T)res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        //public static T CreateResource<T>(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource
 | 
			
		||||
        //    => CreateResourceAsync<T>(serviceProvider, properties).Wait();
 | 
			
		||||
    //public static async AsyncReply<T> CreateResourceAsync<T>(this IServiceProvider serviceProvider, T properties = null) where T : class, IResource
 | 
			
		||||
    //{
 | 
			
		||||
    //    var options = serviceProvider.GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions<T>>();
 | 
			
		||||
 | 
			
		||||
        public static DbContextOptionsBuilder UseEsiur(this DbContextOptionsBuilder optionsBuilder,
 | 
			
		||||
                                                        EntityStore store,
 | 
			
		||||
                                                        Func<DbContext> getter = null
 | 
			
		||||
    //    var resource = await Warehouse.New<T>("", options.Store, null, null, null, properties);
 | 
			
		||||
 | 
			
		||||
            //IServiceCollection services = null
 | 
			
		||||
            //string name = null,
 | 
			
		||||
            //IResource parent = null,
 | 
			
		||||
            //IPermissionsManager manager = null,
 | 
			
		||||
            //Func<DbContext> dbContextProvider = null
 | 
			
		||||
            )
 | 
			
		||||
    //    resource.Instance.Managers.AddRange(options.Store.Instance.Managers.ToArray());
 | 
			
		||||
 | 
			
		||||
    //    return resource;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public static T CreateResource<T>(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource
 | 
			
		||||
    //    => CreateResourceAsync<T>(serviceProvider, properties).Wait();
 | 
			
		||||
 | 
			
		||||
    public static DbContextOptionsBuilder UseEsiur(this DbContextOptionsBuilder optionsBuilder,
 | 
			
		||||
                                                    EntityStore store,
 | 
			
		||||
                                                    Func<DbContext> getter = null
 | 
			
		||||
 | 
			
		||||
        //IServiceCollection services = null
 | 
			
		||||
        //string name = null,
 | 
			
		||||
        //IResource parent = null,
 | 
			
		||||
        //IPermissionsManager manager = null,
 | 
			
		||||
        //Func<DbContext> dbContextProvider = null
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
 | 
			
		||||
        if (extension == null)
 | 
			
		||||
        {
 | 
			
		||||
            var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
 | 
			
		||||
            if (extension == null)
 | 
			
		||||
            {
 | 
			
		||||
                if (store == null)
 | 
			
		||||
                    return optionsBuilder;
 | 
			
		||||
 | 
			
		||||
                store.Options = optionsBuilder.Options;
 | 
			
		||||
                extension = new EsiurExtensionOptions(store);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
 | 
			
		||||
 | 
			
		||||
            return optionsBuilder;
 | 
			
		||||
            if (store == null)
 | 
			
		||||
                return optionsBuilder;
 | 
			
		||||
 | 
			
		||||
            store.Options = optionsBuilder.Options;
 | 
			
		||||
            extension = new EsiurExtensionOptions(store);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //public static DbContextOptionsBuilder<TContext> UseEsiur<TContext>(
 | 
			
		||||
        //                                     this DbContextOptionsBuilder<TContext> optionsBuilder,
 | 
			
		||||
        //                                    //DbContext context,
 | 
			
		||||
        //                                    string name = null,
 | 
			
		||||
        //                                    IResource parent = null,
 | 
			
		||||
        //                                    IPermissionsManager manager = null,
 | 
			
		||||
        //                                    Func<DbContext> dbContextProvider = null)
 | 
			
		||||
        //                                    where TContext : DbContext
 | 
			
		||||
        //{
 | 
			
		||||
        ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //    var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
 | 
			
		||||
        //    if (extension == null)
 | 
			
		||||
        //    {
 | 
			
		||||
        //        var store = Warehouse.New<EntityStore>(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait();
 | 
			
		||||
        //        extension = new EsiurExtensionOptions(store);
 | 
			
		||||
        //        //store.Options = optionsBuilder;
 | 
			
		||||
        //        //store.Options = extension;
 | 
			
		||||
        //        //store.DbContext = context;
 | 
			
		||||
        //    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //    ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
 | 
			
		||||
 | 
			
		||||
        //    return optionsBuilder;
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
        return optionsBuilder;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //public static DbContextOptionsBuilder<TContext> UseEsiur<TContext>(
 | 
			
		||||
    //                                     this DbContextOptionsBuilder<TContext> optionsBuilder,
 | 
			
		||||
    //                                    //DbContext context,
 | 
			
		||||
    //                                    string name = null,
 | 
			
		||||
    //                                    IResource parent = null,
 | 
			
		||||
    //                                    IPermissionsManager manager = null,
 | 
			
		||||
    //                                    Func<DbContext> dbContextProvider = null)
 | 
			
		||||
    //                                    where TContext : DbContext
 | 
			
		||||
    //{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //    var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
 | 
			
		||||
    //    if (extension == null)
 | 
			
		||||
    //    {
 | 
			
		||||
    //        var store = Warehouse.New<EntityStore>(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait();
 | 
			
		||||
    //        extension = new EsiurExtensionOptions(store);
 | 
			
		||||
    //        //store.Options = optionsBuilder;
 | 
			
		||||
    //        //store.Options = extension;
 | 
			
		||||
    //        //store.DbContext = context;
 | 
			
		||||
    //    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //    ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
 | 
			
		||||
 | 
			
		||||
    //    return optionsBuilder;
 | 
			
		||||
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,31 +30,29 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
public class EsiurPlugin : IConventionSetPlugin
 | 
			
		||||
{
 | 
			
		||||
    public class EsiurPlugin : IConventionSetPlugin
 | 
			
		||||
    private readonly IDbContextOptions _options;
 | 
			
		||||
    private readonly ProviderConventionSetBuilderDependencies _conventionSetBuilderDependencies;
 | 
			
		||||
 | 
			
		||||
    public EsiurPlugin(
 | 
			
		||||
         IDbContextOptions options,
 | 
			
		||||
        ProviderConventionSetBuilderDependencies conventionSetBuilderDependencies)
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IDbContextOptions _options;
 | 
			
		||||
        private readonly ProviderConventionSetBuilderDependencies _conventionSetBuilderDependencies;
 | 
			
		||||
 | 
			
		||||
        public EsiurPlugin(
 | 
			
		||||
             IDbContextOptions options,
 | 
			
		||||
            ProviderConventionSetBuilderDependencies conventionSetBuilderDependencies)
 | 
			
		||||
        {
 | 
			
		||||
            _options = options;
 | 
			
		||||
            _conventionSetBuilderDependencies = conventionSetBuilderDependencies;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
     
 | 
			
		||||
        public ConventionSet ModifyConventions(ConventionSet conventionSet)
 | 
			
		||||
        {
 | 
			
		||||
            var extension = _options.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
            conventionSet.ModelFinalizingConventions.Add(new EsiurProxyRewrite(
 | 
			
		||||
                    extension,
 | 
			
		||||
                    _conventionSetBuilderDependencies));
 | 
			
		||||
            return conventionSet;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        _options = options;
 | 
			
		||||
        _conventionSetBuilderDependencies = conventionSetBuilderDependencies;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public ConventionSet ModifyConventions(ConventionSet conventionSet)
 | 
			
		||||
    {
 | 
			
		||||
        var extension = _options.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
        conventionSet.ModelFinalizingConventions.Add(new EsiurProxyRewrite(
 | 
			
		||||
                extension,
 | 
			
		||||
                _conventionSetBuilderDependencies));
 | 
			
		||||
        return conventionSet;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,135 +39,157 @@ using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Stores.EntityCore
 | 
			
		||||
namespace Esiur.Stores.EntityCore;
 | 
			
		||||
 | 
			
		||||
public class EsiurProxyRewrite : IModelFinalizingConvention
 | 
			
		||||
{
 | 
			
		||||
    public class EsiurProxyRewrite : IModelFinalizingConvention 
 | 
			
		||||
    private static readonly MethodInfo _createInstance
 | 
			
		||||
= typeof(EsiurProxyRewrite).GetTypeInfo().GetDeclaredMethod(nameof(EsiurProxyRewrite.CreateInstance));
 | 
			
		||||
 | 
			
		||||
    private readonly ConstructorBindingConvention _directBindingConvention;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static object CreateInstance(IDbContextOptions dbContextOptions,
 | 
			
		||||
                                        IEntityType entityType,
 | 
			
		||||
                                        object[] properties)
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly MethodInfo _createInstance
 | 
			
		||||
    = typeof(EsiurProxyRewrite).GetTypeInfo().GetDeclaredMethod(nameof(EsiurProxyRewrite.CreateInstance));
 | 
			
		||||
        var id = properties.First();
 | 
			
		||||
 | 
			
		||||
        private readonly ConstructorBindingConvention _directBindingConvention;
 | 
			
		||||
        var options = dbContextOptions.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
        var manager = options.Store.Instance.Managers.Count > 0 ? options.Store.Instance.Managers.First() : null;
 | 
			
		||||
 | 
			
		||||
        //public static object CreateInstance(IDbContextOptions dbContextOptions, IEntityType entityType,
 | 
			
		||||
        //                                    object[] constructorArguments, DbContext context, long id)
 | 
			
		||||
        //{
 | 
			
		||||
        //    return CreateInstance(dbContextOptions, entityType,
 | 
			
		||||
        //                                     constructorArguments,  context,  id);
 | 
			
		||||
        //}
 | 
			
		||||
        var cache = options.Store.GetById(entityType.ClrType, id);
 | 
			
		||||
 | 
			
		||||
        public static object CreateInstance(
 | 
			
		||||
                                            IDbContextOptions dbContextOptions,
 | 
			
		||||
                                            IEntityType entityType,
 | 
			
		||||
                                            //object id
 | 
			
		||||
                                            object[] properties
 | 
			
		||||
        if (cache != null)
 | 
			
		||||
            return cache;
 | 
			
		||||
 | 
			
		||||
        // ILazyLoader loader,
 | 
			
		||||
        // object[] constructorArguments,
 | 
			
		||||
        //DbContext context,
 | 
			
		||||
        )
 | 
			
		||||
        if (Codec.ImplementsInterface(entityType.ClrType, typeof(IResource)))
 | 
			
		||||
        {
 | 
			
		||||
            var id = properties.First();
 | 
			
		||||
 | 
			
		||||
            var options = dbContextOptions.FindExtension<EsiurExtensionOptions>();
 | 
			
		||||
            var manager = options.Store.Instance.Managers.Count > 0 ? options.Store.Instance.Managers.First() : null;
 | 
			
		||||
 | 
			
		||||
            var cache = options.Store.GetById(entityType.ClrType, id);
 | 
			
		||||
 | 
			
		||||
            if (cache != null)
 | 
			
		||||
                return cache;
 | 
			
		||||
 | 
			
		||||
            if (Codec.ImplementsInterface(entityType.ClrType, typeof(IResource)))
 | 
			
		||||
            {
 | 
			
		||||
                // check if the object exists
 | 
			
		||||
                var obj = Warehouse.New(entityType.ClrType).Wait() as IResource;
 | 
			
		||||
                options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
 | 
			
		||||
                Warehouse.Put(id.ToString(), obj, options.Store, null, null, 0, manager).Wait();
 | 
			
		||||
                return obj;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // record
 | 
			
		||||
                var obj = Activator.CreateInstance(entityType.ClrType);
 | 
			
		||||
                options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
 | 
			
		||||
 | 
			
		||||
                return obj;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public EsiurProxyRewrite(EsiurExtensionOptions ext, ProviderConventionSetBuilderDependencies conventionSetBuilderDependencies)
 | 
			
		||||
        {
 | 
			
		||||
            _directBindingConvention = new ConstructorBindingConvention(conventionSetBuilderDependencies);
 | 
			
		||||
            // check if the object exists
 | 
			
		||||
            var obj = Warehouse.New(entityType.ClrType).Wait() as IResource;
 | 
			
		||||
            options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
 | 
			
		||||
            Warehouse.Put(id.ToString(), obj, options.Store, null, null, 0, manager).Wait();
 | 
			
		||||
            return obj;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
 | 
			
		||||
            // record
 | 
			
		||||
            var obj = Activator.CreateInstance(entityType.ClrType);
 | 
			
		||||
            options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
 | 
			
		||||
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public EsiurProxyRewrite(EsiurExtensionOptions ext, ProviderConventionSetBuilderDependencies conventionSetBuilderDependencies)
 | 
			
		||||
    {
 | 
			
		||||
        _directBindingConvention = new ConstructorBindingConvention(conventionSetBuilderDependencies);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (!Codec.ImplementsInterface(entityType.ClrType, typeof(IResource)))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            var proxyType = ResourceProxy.GetProxy(entityType.ClrType);
 | 
			
		||||
 | 
			
		||||
            // var ann = entityType.GetAnnotation(CoreAnnotationNames.ConstructorBinding);
 | 
			
		||||
 | 
			
		||||
#pragma warning disable EF1001 // Internal EF Core API usage.
 | 
			
		||||
            var binding = ((EntityType)entityType).ConstructorBinding;// (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding];
 | 
			
		||||
#pragma warning restore EF1001 // Internal EF Core API usage.
 | 
			
		||||
            if (binding == null)
 | 
			
		||||
            {
 | 
			
		||||
                _directBindingConvention.ProcessModelFinalizing(modelBuilder, context);
 | 
			
		||||
 | 
			
		||||
#pragma warning disable EF1001 // Internal EF Core API usage.
 | 
			
		||||
                binding = ((EntityType)entityType).ConstructorBinding; // (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding];
 | 
			
		||||
#pragma warning restore EF1001 // Internal EF Core API usage.
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (!Codec.ImplementsInterface(entityType.ClrType, typeof(IResource)))
 | 
			
		||||
                var key = entityType.FindPrimaryKey().Properties.First();
 | 
			
		||||
                if (key == null)
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                var proxyType = ResourceProxy.GetProxy(entityType.ClrType);
 | 
			
		||||
 | 
			
		||||
                // var ann = entityType.GetAnnotation(CoreAnnotationNames.ConstructorBinding);
 | 
			
		||||
                ((EntityType)entityType).SetConstructorBinding(
 | 
			
		||||
                    UpdateConstructorBindings(key, proxyType),
 | 
			
		||||
                    ConfigurationSource.Convention);
 | 
			
		||||
 | 
			
		||||
#pragma warning disable EF1001 // Internal EF Core API usage.
 | 
			
		||||
                var binding = (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding];
 | 
			
		||||
#pragma warning restore EF1001 // Internal EF Core API usage.
 | 
			
		||||
                if (binding == null)
 | 
			
		||||
                    _directBindingConvention.ProcessModelFinalizing(modelBuilder, context);
 | 
			
		||||
 | 
			
		||||
#pragma warning disable EF1001 // Internal EF Core API usage.
 | 
			
		||||
                binding = (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding];
 | 
			
		||||
#pragma warning restore EF1001 // Internal EF Core API usage.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                binding = ((EntityType)entityType).ServiceOnlyConstructorBinding;
 | 
			
		||||
                if (binding != null)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    var key = entityType.FindPrimaryKey().Properties.First();
 | 
			
		||||
                    if (key == null)
 | 
			
		||||
                        continue;
 | 
			
		||||
 | 
			
		||||
                    //var keys = entityType.FindPrimaryKey().Properties.Select(x=>new PropertyParameterBinding(x));
 | 
			
		||||
 | 
			
		||||
                    entityType.SetAnnotation(
 | 
			
		||||
#pragma warning disable EF1001 // Internal EF Core API usage.
 | 
			
		||||
                        CoreAnnotationNames.ConstructorBinding,
 | 
			
		||||
#pragma warning restore EF1001 // Internal EF Core API usage.
 | 
			
		||||
                        new FactoryMethodBinding(
 | 
			
		||||
                            _createInstance,
 | 
			
		||||
                            new List<ParameterBinding>
 | 
			
		||||
                                {
 | 
			
		||||
                                new DependencyInjectionParameterBinding(typeof(IDbContextOptions), typeof(IDbContextOptions)),
 | 
			
		||||
                                new EntityTypeParameterBinding(),
 | 
			
		||||
                                //new PropertyParameterBinding(key)
 | 
			
		||||
                                // constructor arguments 
 | 
			
		||||
                                //new ObjectArrayParameterBinding(binding.ParameterBindings),
 | 
			
		||||
                                 //new ContextParameterBinding(typeof(DbContext)),
 | 
			
		||||
                                 //new ObjectArrayParameterBinding(entityType.FindPrimaryKey().Properties.Select(x=>new PropertyParameterBinding(x)).ToArray())
 | 
			
		||||
                                 new ObjectArrayParameterBinding(new ParameterBinding[]{
 | 
			
		||||
                                            new PropertyParameterBinding(key) })
 | 
			
		||||
                                 //})
 | 
			
		||||
                                // new Microsoft.EntityFrameworkCore.Metadata.ObjectArrayParameterBinding(),
 | 
			
		||||
                                 //new ObjectArrayParameterBinding() 
 | 
			
		||||
 | 
			
		||||
                                },
 | 
			
		||||
                            proxyType));
 | 
			
		||||
 | 
			
		||||
                    ((EntityType)entityType).SetServiceOnlyConstructorBinding(
 | 
			
		||||
                        UpdateConstructorBindings(key, proxyType),
 | 
			
		||||
                        ConfigurationSource.Convention);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                //                entityType.SetAnnotation(
 | 
			
		||||
                //#pragma warning disable EF1001 // Internal EF Core API usage.
 | 
			
		||||
                //                        CoreAnnotationNames.ConstructorBinding,
 | 
			
		||||
                //#pragma warning restore EF1001 // Internal EF Core API usage.
 | 
			
		||||
                //                        new FactoryMethodBinding(
 | 
			
		||||
                //                        _createInstance,
 | 
			
		||||
                //                        new List<ParameterBinding>
 | 
			
		||||
                //                            {
 | 
			
		||||
                //                                new DependencyInjectionParameterBinding(typeof(IDbContextOptions), typeof(IDbContextOptions)),
 | 
			
		||||
                //                                new EntityTypeParameterBinding(),
 | 
			
		||||
                //                                //new PropertyParameterBinding(key)
 | 
			
		||||
                //                                // constructor arguments 
 | 
			
		||||
                //                                //new ObjectArrayParameterBinding(binding.ParameterBindings),
 | 
			
		||||
                //                                 //new ContextParameterBinding(typeof(DbContext)),
 | 
			
		||||
                //                                 //new ObjectArrayParameterBinding(entityType.FindPrimaryKey().Properties.Select(x=>new PropertyParameterBinding(x)).ToArray())
 | 
			
		||||
                //                                 new ObjectArrayParameterBinding(new ParameterBinding[]{
 | 
			
		||||
                //                                            new PropertyParameterBinding(key) })
 | 
			
		||||
                //                            //})
 | 
			
		||||
                //                            // new Microsoft.EntityFrameworkCore.Metadata.ObjectArrayParameterBinding(),
 | 
			
		||||
                //                            //new ObjectArrayParameterBinding() 
 | 
			
		||||
 | 
			
		||||
                //                        },
 | 
			
		||||
                //                        proxyType));
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private InstantiationBinding UpdateConstructorBindings(
 | 
			
		||||
    IConventionProperty key,
 | 
			
		||||
    Type proxyType)
 | 
			
		||||
    {
 | 
			
		||||
        return new FactoryMethodBinding(
 | 
			
		||||
                        _createInstance,
 | 
			
		||||
                        new List<ParameterBinding>
 | 
			
		||||
                            {
 | 
			
		||||
                                new DependencyInjectionParameterBinding(typeof(IDbContextOptions), typeof(IDbContextOptions)),
 | 
			
		||||
                                new EntityTypeParameterBinding(),
 | 
			
		||||
                                 new ObjectArrayParameterBinding(new ParameterBinding[]{
 | 
			
		||||
                                            new PropertyParameterBinding((IProperty)key) })
 | 
			
		||||
 | 
			
		||||
                        },
 | 
			
		||||
                        proxyType);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
 | 
			
		||||
    <Version>1.5.2</Version>
 | 
			
		||||
    <PackageId>Esiur.Stores.MongoDB</PackageId>
 | 
			
		||||
	<LangVersion>latest</LangVersion>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4,50 +4,49 @@ using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncAwaiter : INotifyCompletion
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncAwaiter : INotifyCompletion
 | 
			
		||||
    Action callback = null;
 | 
			
		||||
 | 
			
		||||
    AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
    object result;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncAwaiter(AsyncReply reply)
 | 
			
		||||
    {
 | 
			
		||||
        Action callback = null;
 | 
			
		||||
 | 
			
		||||
        AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
        object result;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncAwaiter(AsyncReply reply)
 | 
			
		||||
        reply.Then(x =>
 | 
			
		||||
        {
 | 
			
		||||
            reply.Then(x =>
 | 
			
		||||
            {
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.result = x;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            }).Error(x =>
 | 
			
		||||
            {
 | 
			
		||||
                exception = x;
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object GetResult()
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.result = x;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        }).Error(x =>
 | 
			
		||||
        {
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
                throw exception;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public void OnCompleted(Action continuation)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsCompleted)
 | 
			
		||||
                continuation?.Invoke();
 | 
			
		||||
            else
 | 
			
		||||
                // Continue....
 | 
			
		||||
                callback = continuation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            exception = x;
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public object GetResult()
 | 
			
		||||
    {
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            throw exception;
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public void OnCompleted(Action continuation)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsCompleted)
 | 
			
		||||
            continuation?.Invoke();
 | 
			
		||||
        else
 | 
			
		||||
            // Continue....
 | 
			
		||||
            callback = continuation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,50 +4,48 @@ using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncAwaiter<T> : INotifyCompletion
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncAwaiter<T> : INotifyCompletion
 | 
			
		||||
    Action callback = null;
 | 
			
		||||
 | 
			
		||||
    AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
    T result;
 | 
			
		||||
 | 
			
		||||
    public AsyncAwaiter(AsyncReply<T> reply)
 | 
			
		||||
    {
 | 
			
		||||
        Action callback = null;
 | 
			
		||||
 | 
			
		||||
        AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
        T result;
 | 
			
		||||
 | 
			
		||||
        public AsyncAwaiter(AsyncReply<T> reply)
 | 
			
		||||
        reply.Then(x =>
 | 
			
		||||
        {
 | 
			
		||||
            reply.Then(x =>
 | 
			
		||||
            {
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.result = (T)x;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            }).Error(x =>
 | 
			
		||||
            {
 | 
			
		||||
                exception = x;
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T GetResult()
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.result = (T)x;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        }).Error(x =>
 | 
			
		||||
        {
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
               throw exception;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public void OnCompleted(Action continuation)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsCompleted)
 | 
			
		||||
                continuation?.Invoke();
 | 
			
		||||
            else
 | 
			
		||||
                // Continue....
 | 
			
		||||
                callback = continuation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            exception = x;
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public T GetResult()
 | 
			
		||||
    {
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            throw exception;
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public void OnCompleted(Action continuation)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsCompleted)
 | 
			
		||||
            continuation?.Invoke();
 | 
			
		||||
        else
 | 
			
		||||
            // Continue....
 | 
			
		||||
            callback = continuation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,104 +28,103 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncBag : AsyncReply
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncBag: AsyncReply
 | 
			
		||||
 | 
			
		||||
    protected List<AsyncReply> replies = new List<AsyncReply>();
 | 
			
		||||
    List<object> results = new List<object>();
 | 
			
		||||
 | 
			
		||||
    int count = 0;
 | 
			
		||||
    bool sealedBag = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Type ArrayType { get; set; }
 | 
			
		||||
 | 
			
		||||
    public AsyncBag Then(Action<object[]> callback)
 | 
			
		||||
    {
 | 
			
		||||
        base.Then(new Action<object>(o => callback((object[])o)));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new AsyncBagAwaiter GetAwaiter()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncBagAwaiter(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new object[] Wait()
 | 
			
		||||
    {
 | 
			
		||||
        return (object[])base.Wait();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new object[] Wait(int timeout)
 | 
			
		||||
    {
 | 
			
		||||
        return (object[])base.Wait(timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Seal()
 | 
			
		||||
    {
 | 
			
		||||
        if (sealedBag)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        sealedBag = true;
 | 
			
		||||
 | 
			
		||||
        if (results.Count == 0)
 | 
			
		||||
            Trigger(new object[0]);
 | 
			
		||||
 | 
			
		||||
        for (var i = 0; i < results.Count; i++)
 | 
			
		||||
        //foreach(var reply in results.Keys)
 | 
			
		||||
        {
 | 
			
		||||
            var k = replies[i];// results.Keys.ElementAt(i);
 | 
			
		||||
            var index = i;
 | 
			
		||||
 | 
			
		||||
            k.Then((r) =>
 | 
			
		||||
            {
 | 
			
		||||
                results[index] = r;
 | 
			
		||||
                count++;
 | 
			
		||||
                if (count == results.Count)
 | 
			
		||||
                {
 | 
			
		||||
                    if (ArrayType != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var ar = Array.CreateInstance(ArrayType, count);
 | 
			
		||||
                        for (var i = 0; i < count; i++)
 | 
			
		||||
                            ar.SetValue(results[i], i);
 | 
			
		||||
                        Trigger(ar);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                        Trigger(results.ToArray());
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Add(AsyncReply reply)
 | 
			
		||||
    {
 | 
			
		||||
        if (!sealedBag)
 | 
			
		||||
        {
 | 
			
		||||
            results.Add(null);
 | 
			
		||||
            replies.Add(reply);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AddBag(AsyncBag bag)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var r in bag.replies)
 | 
			
		||||
            Add(r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncBag()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        protected List<AsyncReply> replies = new List<AsyncReply>();
 | 
			
		||||
        List<object> results = new List<object>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        int count = 0;
 | 
			
		||||
        bool sealedBag = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public Type ArrayType { get; set; }
 | 
			
		||||
 | 
			
		||||
        public AsyncBag Then(Action<object[]> callback)
 | 
			
		||||
        {
 | 
			
		||||
            base.Then(new Action<object>(o => callback((object[])o)));
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new AsyncBagAwaiter GetAwaiter()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncBagAwaiter(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new object[] Wait()
 | 
			
		||||
        {
 | 
			
		||||
            return (object[])base.Wait();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new object[] Wait(int timeout)
 | 
			
		||||
        {
 | 
			
		||||
            return (object[])base.Wait(timeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Seal()
 | 
			
		||||
        {
 | 
			
		||||
            if (sealedBag)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            sealedBag = true;
 | 
			
		||||
 | 
			
		||||
            if (results.Count == 0)
 | 
			
		||||
                Trigger(new object[0]);
 | 
			
		||||
 | 
			
		||||
            for (var i = 0; i < results.Count; i++)
 | 
			
		||||
            //foreach(var reply in results.Keys)
 | 
			
		||||
            {
 | 
			
		||||
                var k = replies[i];// results.Keys.ElementAt(i);
 | 
			
		||||
                var index = i;
 | 
			
		||||
 | 
			
		||||
                k.Then((r) =>
 | 
			
		||||
                {
 | 
			
		||||
                    results[index] = r;
 | 
			
		||||
                    count++;
 | 
			
		||||
                    if (count == results.Count)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (ArrayType != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            var ar = Array.CreateInstance(ArrayType, count);
 | 
			
		||||
                            for (var i = 0; i < count; i++)
 | 
			
		||||
                                ar.SetValue(results[i], i);
 | 
			
		||||
                            Trigger(ar);
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                            Trigger(results.ToArray());
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Add(AsyncReply reply)
 | 
			
		||||
        {
 | 
			
		||||
            if (!sealedBag)
 | 
			
		||||
            {
 | 
			
		||||
                results.Add(null);
 | 
			
		||||
                replies.Add(reply);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AddBag(AsyncBag bag)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var r in bag.replies)
 | 
			
		||||
                Add(r);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
        public AsyncBag()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncBag(object[] results)
 | 
			
		||||
            : base(results)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    public AsyncBag(object[] results)
 | 
			
		||||
        : base(results)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,50 +4,48 @@ using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncBagAwaiter : INotifyCompletion
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncBagAwaiter : INotifyCompletion
 | 
			
		||||
    Action callback = null;
 | 
			
		||||
 | 
			
		||||
    AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
    object[] result;
 | 
			
		||||
 | 
			
		||||
    public AsyncBagAwaiter(AsyncBag reply)
 | 
			
		||||
    {
 | 
			
		||||
        Action callback = null;
 | 
			
		||||
 | 
			
		||||
        AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
        object[] result;
 | 
			
		||||
 | 
			
		||||
        public AsyncBagAwaiter(AsyncBag reply)
 | 
			
		||||
        reply.Then(x =>
 | 
			
		||||
        {
 | 
			
		||||
            reply.Then(x =>
 | 
			
		||||
            {
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.result = x;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            }).Error(x =>
 | 
			
		||||
            {
 | 
			
		||||
                exception = x;
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object[] GetResult()
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.result = x;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        }).Error(x =>
 | 
			
		||||
        {
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
                throw exception;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public void OnCompleted(Action continuation)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsCompleted)
 | 
			
		||||
                continuation?.Invoke();
 | 
			
		||||
            else
 | 
			
		||||
                // Continue....
 | 
			
		||||
                callback = continuation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            exception = x;
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public object[] GetResult()
 | 
			
		||||
    {
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            throw exception;
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public void OnCompleted(Action continuation)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsCompleted)
 | 
			
		||||
            continuation?.Invoke();
 | 
			
		||||
        else
 | 
			
		||||
            // Continue....
 | 
			
		||||
            callback = continuation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,50 +4,48 @@ using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncBagAwaiter<T> : INotifyCompletion
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncBagAwaiter<T> : INotifyCompletion
 | 
			
		||||
    Action callback = null;
 | 
			
		||||
 | 
			
		||||
    AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
    T[] result;
 | 
			
		||||
 | 
			
		||||
    public AsyncBagAwaiter(AsyncBag<T> reply)
 | 
			
		||||
    {
 | 
			
		||||
        Action callback = null;
 | 
			
		||||
 | 
			
		||||
        AsyncException exception = null;
 | 
			
		||||
 | 
			
		||||
        T[] result;
 | 
			
		||||
 | 
			
		||||
        public AsyncBagAwaiter(AsyncBag<T> reply)
 | 
			
		||||
        reply.Then(x =>
 | 
			
		||||
        {
 | 
			
		||||
            reply.Then(x =>
 | 
			
		||||
            {
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.result = x;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            }).Error(x =>
 | 
			
		||||
            {
 | 
			
		||||
                exception = x;
 | 
			
		||||
                this.IsCompleted = true;
 | 
			
		||||
                this.callback?.Invoke();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T[] GetResult()
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.result = x;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        }).Error(x =>
 | 
			
		||||
        {
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
                throw exception;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public void OnCompleted(Action continuation)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsCompleted)
 | 
			
		||||
                continuation?.Invoke();
 | 
			
		||||
            else
 | 
			
		||||
                // Continue....
 | 
			
		||||
                callback = continuation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            exception = x;
 | 
			
		||||
            this.IsCompleted = true;
 | 
			
		||||
            this.callback?.Invoke();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public T[] GetResult()
 | 
			
		||||
    {
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            throw exception;
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsCompleted { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public void OnCompleted(Action continuation)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsCompleted)
 | 
			
		||||
            continuation?.Invoke();
 | 
			
		||||
        else
 | 
			
		||||
            // Continue....
 | 
			
		||||
            callback = continuation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,48 +28,47 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncBag<T> : AsyncBag
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncBag<T>: AsyncBag
 | 
			
		||||
    {     
 | 
			
		||||
        public AsyncBag<T> Then(Action<T[]> callback)
 | 
			
		||||
        {
 | 
			
		||||
            base.Then(new Action<object>((o) => callback(((object[])o).Select(x=>(T)x).ToArray())));
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        public void Add(AsyncReply<T> reply)
 | 
			
		||||
        {
 | 
			
		||||
            base.Add(reply);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AddBag(AsyncBag<T> bag)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var r in bag.replies)
 | 
			
		||||
                Add(r);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public new AsyncBagAwaiter<T> GetAwaiter()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncBagAwaiter<T>(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new T[] Wait()
 | 
			
		||||
        {
 | 
			
		||||
            return base.Wait().Select(x => (T)x).ToArray();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncBag()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncBag(T[] results)
 | 
			
		||||
            : base(results.Select(x=>(object)x).ToArray())
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    public AsyncBag<T> Then(Action<T[]> callback)
 | 
			
		||||
    {
 | 
			
		||||
        base.Then(new Action<object>((o) => callback(((object[])o).Select(x => (T)x).ToArray())));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Add(AsyncReply<T> reply)
 | 
			
		||||
    {
 | 
			
		||||
        base.Add(reply);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AddBag(AsyncBag<T> bag)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var r in bag.replies)
 | 
			
		||||
            Add(r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public new AsyncBagAwaiter<T> GetAwaiter()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncBagAwaiter<T>(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new T[] Wait()
 | 
			
		||||
    {
 | 
			
		||||
        return base.Wait().Select(x => (T)x).ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncBag()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncBag(T[] results)
 | 
			
		||||
        : base(results.Select(x => (object)x).ToArray())
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -26,32 +26,31 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncException : Exception
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncException : Exception
 | 
			
		||||
    public readonly ErrorType Type;
 | 
			
		||||
    public readonly ExceptionCode Code;
 | 
			
		||||
 | 
			
		||||
    public AsyncException(Exception exception) : base(exception.Message, exception)
 | 
			
		||||
    {
 | 
			
		||||
        public readonly ErrorType Type;
 | 
			
		||||
        public readonly ExceptionCode Code;
 | 
			
		||||
        Type = ErrorType.Exception;
 | 
			
		||||
        Code = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public AsyncException(Exception exception) :base(exception.Message, exception)
 | 
			
		||||
        {
 | 
			
		||||
            Type = ErrorType.Exception;
 | 
			
		||||
            Code = 0;
 | 
			
		||||
        }
 | 
			
		||||
    public override string StackTrace => InnerException != null && Type == ErrorType.Exception ? InnerException.StackTrace : base.StackTrace;
 | 
			
		||||
 | 
			
		||||
        public override string StackTrace => InnerException != null && Type == ErrorType.Exception ? InnerException.StackTrace : base.StackTrace;
 | 
			
		||||
    public AsyncException(ErrorType type, ushort code, string message)
 | 
			
		||||
        : base(type == ErrorType.Management ? ((ExceptionCode)code).ToString() : message)
 | 
			
		||||
    {
 | 
			
		||||
        this.Type = type;
 | 
			
		||||
        this.Code = (ExceptionCode)code;
 | 
			
		||||
 | 
			
		||||
        public AsyncException(ErrorType type, ushort code, string message)
 | 
			
		||||
            : base(type == ErrorType.Management ? ((ExceptionCode)code).ToString() : message)
 | 
			
		||||
        {
 | 
			
		||||
            this.Type = type;
 | 
			
		||||
            this.Code = (ExceptionCode)code;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            return Code.ToString() + ": " + Message;
 | 
			
		||||
        }
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return Code.ToString() + ": " + Message;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,57 +28,56 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncQueue<T> : AsyncReply<T>
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncQueue<T> : AsyncReply<T>
 | 
			
		||||
    List<AsyncReply<T>> list = new List<AsyncReply<T>>();
 | 
			
		||||
    //Action<T> callback;
 | 
			
		||||
    object queueLock = new object();
 | 
			
		||||
 | 
			
		||||
    //public AsyncQueue<T> Then(Action<T> callback)
 | 
			
		||||
    //{
 | 
			
		||||
    //  base.Then(new Action<object>(o => callback((T)o)));
 | 
			
		||||
 | 
			
		||||
    //return this;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    public void Add(AsyncReply<T> reply)
 | 
			
		||||
    {
 | 
			
		||||
        List<AsyncReply<T>> list = new List<AsyncReply<T>>();
 | 
			
		||||
        //Action<T> callback;
 | 
			
		||||
        object queueLock = new object();
 | 
			
		||||
        lock (queueLock)
 | 
			
		||||
            list.Add(reply);
 | 
			
		||||
 | 
			
		||||
        //public AsyncQueue<T> Then(Action<T> callback)
 | 
			
		||||
        //{
 | 
			
		||||
          //  base.Then(new Action<object>(o => callback((T)o)));
 | 
			
		||||
        resultReady = false;
 | 
			
		||||
        reply.Then(processQueue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            //return this;
 | 
			
		||||
        //}
 | 
			
		||||
    public void Remove(AsyncReply<T> reply)
 | 
			
		||||
    {
 | 
			
		||||
        lock (queueLock)
 | 
			
		||||
            list.Remove(reply);
 | 
			
		||||
        processQueue(default(T));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Add(AsyncReply<T> reply)
 | 
			
		||||
        {
 | 
			
		||||
            lock (queueLock)
 | 
			
		||||
                list.Add(reply);
 | 
			
		||||
    void processQueue(T o)
 | 
			
		||||
    {
 | 
			
		||||
        lock (queueLock)
 | 
			
		||||
            for (var i = 0; i < list.Count; i++)
 | 
			
		||||
                if (list[i].Ready)
 | 
			
		||||
                {
 | 
			
		||||
                    Trigger(list[i].Result);
 | 
			
		||||
                    resultReady = false;
 | 
			
		||||
                    list.RemoveAt(i);
 | 
			
		||||
                    i--;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
            resultReady = false;
 | 
			
		||||
            reply.Then(processQueue);
 | 
			
		||||
        }
 | 
			
		||||
        resultReady = (list.Count == 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Remove(AsyncReply<T> reply)
 | 
			
		||||
        {
 | 
			
		||||
            lock (queueLock)
 | 
			
		||||
                list.Remove(reply);
 | 
			
		||||
            processQueue(default(T));
 | 
			
		||||
        }
 | 
			
		||||
    public AsyncQueue()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        void processQueue(T o)
 | 
			
		||||
        {
 | 
			
		||||
            lock (queueLock)
 | 
			
		||||
                for (var i = 0; i < list.Count; i++)
 | 
			
		||||
                    if (list[i].Ready)
 | 
			
		||||
                    {
 | 
			
		||||
                        Trigger(list[i].Result);
 | 
			
		||||
                        resultReady = false;
 | 
			
		||||
                        list.RemoveAt(i);
 | 
			
		||||
                        i--;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
            resultReady = (list.Count == 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncQueue()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,328 +33,327 @@ using System.Threading;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
[AsyncMethodBuilder(typeof(AsyncReplyBuilder))]
 | 
			
		||||
public class AsyncReply
 | 
			
		||||
{
 | 
			
		||||
    [AsyncMethodBuilder(typeof(AsyncReplyBuilder))]
 | 
			
		||||
    public class AsyncReply 
 | 
			
		||||
    public bool Debug = false;
 | 
			
		||||
 | 
			
		||||
    protected List<Action<object>> callbacks = new List<Action<object>>();
 | 
			
		||||
    protected object result;
 | 
			
		||||
 | 
			
		||||
    protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
 | 
			
		||||
 | 
			
		||||
    protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
 | 
			
		||||
 | 
			
		||||
    protected List<Action<object>> chunkCallbacks = new List<Action<object>>();
 | 
			
		||||
 | 
			
		||||
    //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>();
 | 
			
		||||
 | 
			
		||||
    object asyncLock = new object();
 | 
			
		||||
 | 
			
		||||
    //public Timer timeout;// = new Timer()
 | 
			
		||||
    protected bool resultReady = false;
 | 
			
		||||
    AsyncException exception;
 | 
			
		||||
    // StackTrace trace;
 | 
			
		||||
    AutoResetEvent mutex = new AutoResetEvent(false);
 | 
			
		||||
 | 
			
		||||
    public static int MaxId;
 | 
			
		||||
 | 
			
		||||
    public int Id;
 | 
			
		||||
 | 
			
		||||
    public bool Ready
 | 
			
		||||
    {
 | 
			
		||||
        public bool Debug = false;
 | 
			
		||||
 | 
			
		||||
        protected List<Action<object>> callbacks = new List<Action<object>>();
 | 
			
		||||
        protected object result;
 | 
			
		||||
 | 
			
		||||
        protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
 | 
			
		||||
 | 
			
		||||
        protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
 | 
			
		||||
 | 
			
		||||
        protected List<Action<object>> chunkCallbacks = new List<Action<object>>();
 | 
			
		||||
 | 
			
		||||
        //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>();
 | 
			
		||||
 | 
			
		||||
        object asyncLock = new object();
 | 
			
		||||
 | 
			
		||||
        //public Timer timeout;// = new Timer()
 | 
			
		||||
        protected bool resultReady = false;
 | 
			
		||||
        AsyncException exception;
 | 
			
		||||
        // StackTrace trace;
 | 
			
		||||
        AutoResetEvent mutex = new AutoResetEvent(false);
 | 
			
		||||
 | 
			
		||||
        public static int MaxId;
 | 
			
		||||
 | 
			
		||||
        public int Id;
 | 
			
		||||
 | 
			
		||||
        public bool Ready
 | 
			
		||||
        {
 | 
			
		||||
            get { return resultReady; }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public object Wait()
 | 
			
		||||
        {
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return result;
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Wait");
 | 
			
		||||
 | 
			
		||||
            //mutex = new AutoResetEvent(false);
 | 
			
		||||
            mutex.WaitOne();
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Wait ended");
 | 
			
		||||
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
                throw exception;
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public object Wait(int millisecondsTimeout)
 | 
			
		||||
        {
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return result;
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Wait");
 | 
			
		||||
 | 
			
		||||
            if (!mutex.WaitOne(millisecondsTimeout))
 | 
			
		||||
            {
 | 
			
		||||
                var e = new Exception("AsyncReply timeout");
 | 
			
		||||
                TriggerError(e);
 | 
			
		||||
                throw e;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Wait ended");
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object Result
 | 
			
		||||
        {
 | 
			
		||||
            get { return result; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Then(Action<object> callback)
 | 
			
		||||
        {
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            lock (asyncLock)
 | 
			
		||||
            {
 | 
			
		||||
                //  trace = new StackTrace();
 | 
			
		||||
 | 
			
		||||
                if (resultReady)
 | 
			
		||||
                {
 | 
			
		||||
                    if (Debug)
 | 
			
		||||
                        Console.WriteLine($"AsyncReply: {Id} Then ready");
 | 
			
		||||
 | 
			
		||||
                    callback(result);
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                //timeout = new Timer(x =>
 | 
			
		||||
                //{
 | 
			
		||||
                //    // Get calling method name
 | 
			
		||||
                //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name);
 | 
			
		||||
 | 
			
		||||
                //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name));
 | 
			
		||||
                //    timeout.Dispose();
 | 
			
		||||
 | 
			
		||||
                //    tr = trace.ToString();
 | 
			
		||||
                //    throw new Exception("Request timeout " + Id);
 | 
			
		||||
                //}, null, 15000, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Then pending");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                callbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
                return this;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Error(Action<AsyncException> callback)
 | 
			
		||||
        {
 | 
			
		||||
            // lock (callbacksLock)
 | 
			
		||||
            //  {
 | 
			
		||||
            errorCallbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
                callback(exception);
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
        {
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            progressCallbacks.Add(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Chunk(Action<object> callback)
 | 
			
		||||
        {
 | 
			
		||||
            // lock (callbacksLock)
 | 
			
		||||
            // {
 | 
			
		||||
            chunkCallbacks.Add(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
            // }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Trigger(object result)
 | 
			
		||||
        {
 | 
			
		||||
            lock (asyncLock)
 | 
			
		||||
            {
 | 
			
		||||
                //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Trigger");
 | 
			
		||||
 | 
			
		||||
                if (resultReady)
 | 
			
		||||
                    return this;
 | 
			
		||||
 | 
			
		||||
                this.result = result;
 | 
			
		||||
 | 
			
		||||
                resultReady = true;
 | 
			
		||||
 | 
			
		||||
                //if (mutex != null)
 | 
			
		||||
                mutex.Set();
 | 
			
		||||
 | 
			
		||||
                foreach (var cb in callbacks)
 | 
			
		||||
                    cb(result);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Trigger ended");
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply TriggerError(Exception exception)
 | 
			
		||||
        {
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return this;
 | 
			
		||||
 | 
			
		||||
            if (exception is AsyncException)
 | 
			
		||||
                this.exception = exception as AsyncException;
 | 
			
		||||
            else
 | 
			
		||||
                this.exception = new AsyncException(exception);
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            // lock (callbacksLock)
 | 
			
		||||
            // {
 | 
			
		||||
            foreach (var cb in errorCallbacks)
 | 
			
		||||
                cb(this.exception);
 | 
			
		||||
            //  }
 | 
			
		||||
 | 
			
		||||
            mutex?.Set();
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply TriggerProgress(ProgressType type, int value, int max)
 | 
			
		||||
        {
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            foreach (var cb in progressCallbacks)
 | 
			
		||||
                cb(type, value, max);
 | 
			
		||||
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply TriggerChunk(object value)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            foreach (var cb in chunkCallbacks)
 | 
			
		||||
                cb(value);
 | 
			
		||||
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncAwaiter GetAwaiter()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncAwaiter(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply()
 | 
			
		||||
        {
 | 
			
		||||
            //   this.Debug = true;
 | 
			
		||||
            Id = MaxId++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply(object result)
 | 
			
		||||
        {
 | 
			
		||||
            //   this.Debug = true;
 | 
			
		||||
            resultReady = true;
 | 
			
		||||
            this.result = result;
 | 
			
		||||
 | 
			
		||||
            Id = MaxId++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
    public AsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
        {
 | 
			
		||||
           base.Then(new Action<object>(o => callback((T)o)));
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Trigger(T result)
 | 
			
		||||
        {
 | 
			
		||||
            Trigger((object)result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task<bool> MoveNext(CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply()
 | 
			
		||||
        {
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new Task<T> Task
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return base.Task.ContinueWith<T>((t) =>
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
                    return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
 | 
			
		||||
#else
 | 
			
		||||
                    return (T)t.GetType().GetProperty("Result").GetValue(t);
 | 
			
		||||
#endif
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T Current => throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
        public AsyncReply(T result)
 | 
			
		||||
            : base(result)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        get { return resultReady; }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public object Wait()
 | 
			
		||||
    {
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return result;
 | 
			
		||||
 | 
			
		||||
        if (Debug)
 | 
			
		||||
            Console.WriteLine($"AsyncReply: {Id} Wait");
 | 
			
		||||
 | 
			
		||||
        //mutex = new AutoResetEvent(false);
 | 
			
		||||
        mutex.WaitOne();
 | 
			
		||||
 | 
			
		||||
        if (Debug)
 | 
			
		||||
            Console.WriteLine($"AsyncReply: {Id} Wait ended");
 | 
			
		||||
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            throw exception;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public object Wait(int millisecondsTimeout)
 | 
			
		||||
    {
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return result;
 | 
			
		||||
 | 
			
		||||
        if (Debug)
 | 
			
		||||
            Console.WriteLine($"AsyncReply: {Id} Wait");
 | 
			
		||||
 | 
			
		||||
        if (!mutex.WaitOne(millisecondsTimeout))
 | 
			
		||||
        {
 | 
			
		||||
            var e = new Exception("AsyncReply timeout");
 | 
			
		||||
            TriggerError(e);
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Debug)
 | 
			
		||||
            Console.WriteLine($"AsyncReply: {Id} Wait ended");
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public object Result
 | 
			
		||||
    {
 | 
			
		||||
        get { return result; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Then(Action<object> callback)
 | 
			
		||||
    {
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        lock (asyncLock)
 | 
			
		||||
        {
 | 
			
		||||
            //  trace = new StackTrace();
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
            {
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Then ready");
 | 
			
		||||
 | 
			
		||||
                callback(result);
 | 
			
		||||
                return this;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //timeout = new Timer(x =>
 | 
			
		||||
            //{
 | 
			
		||||
            //    // Get calling method name
 | 
			
		||||
            //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name);
 | 
			
		||||
 | 
			
		||||
            //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name));
 | 
			
		||||
            //    timeout.Dispose();
 | 
			
		||||
 | 
			
		||||
            //    tr = trace.ToString();
 | 
			
		||||
            //    throw new Exception("Request timeout " + Id);
 | 
			
		||||
            //}, null, 15000, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Then pending");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            callbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Error(Action<AsyncException> callback)
 | 
			
		||||
    {
 | 
			
		||||
        // lock (callbacksLock)
 | 
			
		||||
        //  {
 | 
			
		||||
        errorCallbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            callback(exception);
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
    {
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        progressCallbacks.Add(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Chunk(Action<object> callback)
 | 
			
		||||
    {
 | 
			
		||||
        // lock (callbacksLock)
 | 
			
		||||
        // {
 | 
			
		||||
        chunkCallbacks.Add(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
        // }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Trigger(object result)
 | 
			
		||||
    {
 | 
			
		||||
        lock (asyncLock)
 | 
			
		||||
        {
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Trigger");
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return this;
 | 
			
		||||
 | 
			
		||||
            this.result = result;
 | 
			
		||||
 | 
			
		||||
            resultReady = true;
 | 
			
		||||
 | 
			
		||||
            //if (mutex != null)
 | 
			
		||||
            mutex.Set();
 | 
			
		||||
 | 
			
		||||
            foreach (var cb in callbacks)
 | 
			
		||||
                cb(result);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Trigger ended");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply TriggerError(Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return this;
 | 
			
		||||
 | 
			
		||||
        if (exception is AsyncException)
 | 
			
		||||
            this.exception = exception as AsyncException;
 | 
			
		||||
        else
 | 
			
		||||
            this.exception = new AsyncException(exception);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // lock (callbacksLock)
 | 
			
		||||
        // {
 | 
			
		||||
        foreach (var cb in errorCallbacks)
 | 
			
		||||
            cb(this.exception);
 | 
			
		||||
        //  }
 | 
			
		||||
 | 
			
		||||
        mutex?.Set();
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply TriggerProgress(ProgressType type, int value, int max)
 | 
			
		||||
    {
 | 
			
		||||
        //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        foreach (var cb in progressCallbacks)
 | 
			
		||||
            cb(type, value, max);
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply TriggerChunk(object value)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        foreach (var cb in chunkCallbacks)
 | 
			
		||||
            cb(value);
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncAwaiter GetAwaiter()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncAwaiter(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply()
 | 
			
		||||
    {
 | 
			
		||||
        //   this.Debug = true;
 | 
			
		||||
        Id = MaxId++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply(object result)
 | 
			
		||||
    {
 | 
			
		||||
        //   this.Debug = true;
 | 
			
		||||
        resultReady = true;
 | 
			
		||||
        this.result = result;
 | 
			
		||||
 | 
			
		||||
        Id = MaxId++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
public AsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
    {
 | 
			
		||||
       base.Then(new Action<object>(o => callback((T)o)));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Trigger(T result)
 | 
			
		||||
    {
 | 
			
		||||
        Trigger((object)result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<bool> MoveNext(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new Task<T> Task
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return base.Task.ContinueWith<T>((t) =>
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
                return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
 | 
			
		||||
#else
 | 
			
		||||
                return (T)t.GetType().GetProperty("Result").GetValue(t);
 | 
			
		||||
#endif
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T Current => throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
    public AsyncReply(T result)
 | 
			
		||||
        : base(result)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,66 +3,65 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncReplyBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncReplyBuilder
 | 
			
		||||
    AsyncReply reply;
 | 
			
		||||
 | 
			
		||||
    AsyncReplyBuilder(AsyncReply reply)
 | 
			
		||||
    {
 | 
			
		||||
        AsyncReply reply;
 | 
			
		||||
 | 
			
		||||
        AsyncReplyBuilder(AsyncReply reply)
 | 
			
		||||
        {
 | 
			
		||||
            this.reply = reply;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static AsyncReplyBuilder Create()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncReplyBuilder(new AsyncReply());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Start<TStateMachine>(ref TStateMachine stateMachine)
 | 
			
		||||
            where TStateMachine : IAsyncStateMachine
 | 
			
		||||
        {
 | 
			
		||||
            stateMachine.MoveNext();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetStateMachine(IAsyncStateMachine stateMachine)
 | 
			
		||||
        {
 | 
			
		||||
            Console.WriteLine("SetStateMachine");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetException(Exception exception)
 | 
			
		||||
        {
 | 
			
		||||
            reply.TriggerError(exception);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetResult()
 | 
			
		||||
        {
 | 
			
		||||
            reply.Trigger(null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AwaitOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
            ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
            where TAwaiter : INotifyCompletion
 | 
			
		||||
            where TStateMachine : IAsyncStateMachine
 | 
			
		||||
        {
 | 
			
		||||
            awaiter.OnCompleted(stateMachine.MoveNext);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
            ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
            where TAwaiter : ICriticalNotifyCompletion
 | 
			
		||||
            where TStateMachine : IAsyncStateMachine
 | 
			
		||||
        {
 | 
			
		||||
            awaiter.UnsafeOnCompleted(stateMachine.MoveNext);            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Task
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return reply;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.reply = reply;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static AsyncReplyBuilder Create()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncReplyBuilder(new AsyncReply());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Start<TStateMachine>(ref TStateMachine stateMachine)
 | 
			
		||||
        where TStateMachine : IAsyncStateMachine
 | 
			
		||||
    {
 | 
			
		||||
        stateMachine.MoveNext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetStateMachine(IAsyncStateMachine stateMachine)
 | 
			
		||||
    {
 | 
			
		||||
        Console.WriteLine("SetStateMachine");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetException(Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        reply.TriggerError(exception);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetResult()
 | 
			
		||||
    {
 | 
			
		||||
        reply.Trigger(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
        where TAwaiter : INotifyCompletion
 | 
			
		||||
        where TStateMachine : IAsyncStateMachine
 | 
			
		||||
    {
 | 
			
		||||
        awaiter.OnCompleted(stateMachine.MoveNext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
        where TAwaiter : ICriticalNotifyCompletion
 | 
			
		||||
        where TStateMachine : IAsyncStateMachine
 | 
			
		||||
    {
 | 
			
		||||
        awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Task
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return reply;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,65 +3,65 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncReplyBuilder<T>
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncReplyBuilder<T>
 | 
			
		||||
    AsyncReply<T> reply;
 | 
			
		||||
 | 
			
		||||
    AsyncReplyBuilder(AsyncReply<T> reply)
 | 
			
		||||
    {
 | 
			
		||||
        AsyncReply<T> reply;
 | 
			
		||||
 | 
			
		||||
        AsyncReplyBuilder(AsyncReply<T> reply)
 | 
			
		||||
        {
 | 
			
		||||
            this.reply = reply;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static AsyncReplyBuilder<T> Create()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncReplyBuilder<T>(new AsyncReply<T>());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Start<TStateMachine>(ref TStateMachine stateMachine)
 | 
			
		||||
            where TStateMachine : IAsyncStateMachine
 | 
			
		||||
        {
 | 
			
		||||
            stateMachine.MoveNext();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetStateMachine(IAsyncStateMachine stateMachine)
 | 
			
		||||
        {
 | 
			
		||||
            Console.WriteLine("SetStateMachine");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetException(Exception exception)
 | 
			
		||||
        {
 | 
			
		||||
            reply.TriggerError(exception);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetResult(T result)
 | 
			
		||||
        {
 | 
			
		||||
            reply.Trigger(result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AwaitOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
            ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
            where TAwaiter : INotifyCompletion
 | 
			
		||||
            where TStateMachine : IAsyncStateMachine
 | 
			
		||||
        {
 | 
			
		||||
            awaiter.OnCompleted(stateMachine.MoveNext);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
            ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
            where TAwaiter : ICriticalNotifyCompletion
 | 
			
		||||
            where TStateMachine : IAsyncStateMachine
 | 
			
		||||
        {
 | 
			
		||||
            awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<T> Task
 | 
			
		||||
        {
 | 
			
		||||
            get {
 | 
			
		||||
                return reply;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.reply = reply;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static AsyncReplyBuilder<T> Create()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncReplyBuilder<T>(new AsyncReply<T>());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Start<TStateMachine>(ref TStateMachine stateMachine)
 | 
			
		||||
        where TStateMachine : IAsyncStateMachine
 | 
			
		||||
    {
 | 
			
		||||
        stateMachine.MoveNext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetStateMachine(IAsyncStateMachine stateMachine)
 | 
			
		||||
    {
 | 
			
		||||
        Console.WriteLine("SetStateMachine");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetException(Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        reply.TriggerError(exception);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void SetResult(T result)
 | 
			
		||||
    {
 | 
			
		||||
        reply.Trigger(result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
        where TAwaiter : INotifyCompletion
 | 
			
		||||
        where TStateMachine : IAsyncStateMachine
 | 
			
		||||
    {
 | 
			
		||||
        awaiter.OnCompleted(stateMachine.MoveNext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
 | 
			
		||||
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
 | 
			
		||||
        where TAwaiter : ICriticalNotifyCompletion
 | 
			
		||||
        where TStateMachine : IAsyncStateMachine
 | 
			
		||||
    {
 | 
			
		||||
        awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<T> Task
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return reply;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,347 +33,346 @@ using System.Threading;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
[AsyncMethodBuilder(typeof(AsyncReplyBuilder<>))]
 | 
			
		||||
public class AsyncReply<T> : AsyncReply
 | 
			
		||||
{
 | 
			
		||||
    [AsyncMethodBuilder(typeof(AsyncReplyBuilder<>))]
 | 
			
		||||
    public class AsyncReply<T> : AsyncReply
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
    {
 | 
			
		||||
        base.Then((x) => callback((T)x));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new AsyncReply<T> Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
    {
 | 
			
		||||
        base.Progress(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<T> Chunk(Action<T> callback)
 | 
			
		||||
    {
 | 
			
		||||
        chunkCallbacks.Add((x) => callback((T)x));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply(T result)
 | 
			
		||||
       : base(result)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
        {
 | 
			
		||||
            base.Then((x)=>callback((T)x));
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new AsyncReply<T> Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
        {
 | 
			
		||||
            base.Progress(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<T> Chunk(Action<T> callback)
 | 
			
		||||
        {
 | 
			
		||||
            chunkCallbacks.Add((x)=>callback((T)x));
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply(T result)
 | 
			
		||||
           : base(result)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply()
 | 
			
		||||
            :base()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new AsyncAwaiter<T> GetAwaiter()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncAwaiter<T>(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new T Wait()
 | 
			
		||||
        {
 | 
			
		||||
            return (T)base.Wait();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new T Wait(int millisecondsTimeout)
 | 
			
		||||
        {
 | 
			
		||||
            return (T)base.Wait(millisecondsTimeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        protected new List<Action> callbacks = new List<Action>();
 | 
			
		||||
        protected new object result;
 | 
			
		||||
 | 
			
		||||
        protected new List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
 | 
			
		||||
 | 
			
		||||
        protected new List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
 | 
			
		||||
 | 
			
		||||
        protected new List<Action> chunkCallbacks = new List<Action>();
 | 
			
		||||
 | 
			
		||||
        //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>();
 | 
			
		||||
 | 
			
		||||
        object asyncLock = new object();
 | 
			
		||||
 | 
			
		||||
        //public Timer timeout;// = new Timer()
 | 
			
		||||
 | 
			
		||||
        AsyncException exception;
 | 
			
		||||
        // StackTrace trace;
 | 
			
		||||
        AutoResetEvent mutex = new AutoResetEvent(false);
 | 
			
		||||
 | 
			
		||||
        public static int MaxId;
 | 
			
		||||
 | 
			
		||||
        public int Id;
 | 
			
		||||
 | 
			
		||||
        public bool Ready
 | 
			
		||||
        {
 | 
			
		||||
            get { return resultReady; }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public T Wait()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return result;
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Wait");
 | 
			
		||||
 | 
			
		||||
            //mutex = new AutoResetEvent(false);
 | 
			
		||||
            mutex.WaitOne();
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Wait ended");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public object Result
 | 
			
		||||
        {
 | 
			
		||||
            get { return result; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public IAsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
        {
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            lock (asyncLock)
 | 
			
		||||
            {
 | 
			
		||||
                //  trace = new StackTrace();
 | 
			
		||||
 | 
			
		||||
                if (resultReady)
 | 
			
		||||
                {
 | 
			
		||||
                    if (Debug)
 | 
			
		||||
                        Console.WriteLine($"AsyncReply: {Id} Then ready");
 | 
			
		||||
 | 
			
		||||
                    callback(result);
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                //timeout = new Timer(x =>
 | 
			
		||||
                //{
 | 
			
		||||
                //    // Get calling method name
 | 
			
		||||
                //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name);
 | 
			
		||||
 | 
			
		||||
                //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name));
 | 
			
		||||
                //    timeout.Dispose();
 | 
			
		||||
 | 
			
		||||
                //    tr = trace.ToString();
 | 
			
		||||
                //    throw new Exception("Request timeout " + Id);
 | 
			
		||||
                //}, null, 15000, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Then pending");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                callbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
                return this;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public IAsyncReply<T> Error(Action<AsyncException> callback)
 | 
			
		||||
        {
 | 
			
		||||
            // lock (callbacksLock)
 | 
			
		||||
            //  {
 | 
			
		||||
            errorCallbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
                callback(exception);
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IAsyncReply<T> Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
        {
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            progressCallbacks.Add(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public IAsyncReply<T> Chunk(Action<T> callback)
 | 
			
		||||
        {
 | 
			
		||||
            // lock (callbacksLock)
 | 
			
		||||
            // {
 | 
			
		||||
            chunkCallbacks.Add(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
            // }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Trigger(object result)
 | 
			
		||||
        {
 | 
			
		||||
            lock (asyncLock)
 | 
			
		||||
            {
 | 
			
		||||
                //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Trigger");
 | 
			
		||||
 | 
			
		||||
                if (resultReady)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                this.result = (T)result;
 | 
			
		||||
 | 
			
		||||
                resultReady = true;
 | 
			
		||||
 | 
			
		||||
                //if (mutex != null)
 | 
			
		||||
                mutex.Set();
 | 
			
		||||
 | 
			
		||||
                foreach (var cb in callbacks)
 | 
			
		||||
                    cb((T)result);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Trigger ended");
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerError(Exception exception)
 | 
			
		||||
        {
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (exception is AsyncException)
 | 
			
		||||
                this.exception = exception as AsyncException;
 | 
			
		||||
            else
 | 
			
		||||
                this.exception = new AsyncException(ErrorType.Management, 0, exception.Message);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // lock (callbacksLock)
 | 
			
		||||
            // {
 | 
			
		||||
            foreach (var cb in errorCallbacks)
 | 
			
		||||
                cb(this.exception);
 | 
			
		||||
            //  }
 | 
			
		||||
 | 
			
		||||
            mutex?.Set();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerProgress(ProgressType type, int value, int max)
 | 
			
		||||
        {
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            foreach (var cb in progressCallbacks)
 | 
			
		||||
                cb(type, value, max);
 | 
			
		||||
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void TriggerChunk(object value)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            //lock (callbacksLock)
 | 
			
		||||
            //{
 | 
			
		||||
            foreach (var cb in chunkCallbacks)
 | 
			
		||||
                cb((T)value);
 | 
			
		||||
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncAwaiter<T> GetAwaiter()
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncAwaiter<T>(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply()
 | 
			
		||||
        {
 | 
			
		||||
            //   this.Debug = true;
 | 
			
		||||
            Id = MaxId++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply(T result)
 | 
			
		||||
        {
 | 
			
		||||
            //   this.Debug = true;
 | 
			
		||||
            resultReady = true;
 | 
			
		||||
            this.result = result;
 | 
			
		||||
 | 
			
		||||
            Id = MaxId++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
    public AsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
        {
 | 
			
		||||
           base.Then(new Action<object>(o => callback((T)o)));
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Trigger(T result)
 | 
			
		||||
        {
 | 
			
		||||
            Trigger((object)result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task<bool> MoveNext(CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply()
 | 
			
		||||
        {
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public new Task<T> Task
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return base.Task.ContinueWith<T>((t) =>
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
                    return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
 | 
			
		||||
#else
 | 
			
		||||
                    return (T)t.GetType().GetProperty("Result").GetValue(t);
 | 
			
		||||
#endif
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T Current => throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
       
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply()
 | 
			
		||||
        : base()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new AsyncAwaiter<T> GetAwaiter()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncAwaiter<T>(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new T Wait()
 | 
			
		||||
    {
 | 
			
		||||
        return (T)base.Wait();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new T Wait(int millisecondsTimeout)
 | 
			
		||||
    {
 | 
			
		||||
        return (T)base.Wait(millisecondsTimeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    protected new List<Action> callbacks = new List<Action>();
 | 
			
		||||
    protected new object result;
 | 
			
		||||
 | 
			
		||||
    protected new List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
 | 
			
		||||
 | 
			
		||||
    protected new List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
 | 
			
		||||
 | 
			
		||||
    protected new List<Action> chunkCallbacks = new List<Action>();
 | 
			
		||||
 | 
			
		||||
    //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>();
 | 
			
		||||
 | 
			
		||||
    object asyncLock = new object();
 | 
			
		||||
 | 
			
		||||
    //public Timer timeout;// = new Timer()
 | 
			
		||||
 | 
			
		||||
    AsyncException exception;
 | 
			
		||||
    // StackTrace trace;
 | 
			
		||||
    AutoResetEvent mutex = new AutoResetEvent(false);
 | 
			
		||||
 | 
			
		||||
    public static int MaxId;
 | 
			
		||||
 | 
			
		||||
    public int Id;
 | 
			
		||||
 | 
			
		||||
    public bool Ready
 | 
			
		||||
    {
 | 
			
		||||
        get { return resultReady; }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public T Wait()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return result;
 | 
			
		||||
 | 
			
		||||
        if (Debug)
 | 
			
		||||
            Console.WriteLine($"AsyncReply: {Id} Wait");
 | 
			
		||||
 | 
			
		||||
        //mutex = new AutoResetEvent(false);
 | 
			
		||||
        mutex.WaitOne();
 | 
			
		||||
 | 
			
		||||
        if (Debug)
 | 
			
		||||
            Console.WriteLine($"AsyncReply: {Id} Wait ended");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public object Result
 | 
			
		||||
    {
 | 
			
		||||
        get { return result; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public IAsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
    {
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        lock (asyncLock)
 | 
			
		||||
        {
 | 
			
		||||
            //  trace = new StackTrace();
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
            {
 | 
			
		||||
                if (Debug)
 | 
			
		||||
                    Console.WriteLine($"AsyncReply: {Id} Then ready");
 | 
			
		||||
 | 
			
		||||
                callback(result);
 | 
			
		||||
                return this;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //timeout = new Timer(x =>
 | 
			
		||||
            //{
 | 
			
		||||
            //    // Get calling method name
 | 
			
		||||
            //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name);
 | 
			
		||||
 | 
			
		||||
            //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name));
 | 
			
		||||
            //    timeout.Dispose();
 | 
			
		||||
 | 
			
		||||
            //    tr = trace.ToString();
 | 
			
		||||
            //    throw new Exception("Request timeout " + Id);
 | 
			
		||||
            //}, null, 15000, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Then pending");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            callbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public IAsyncReply<T> Error(Action<AsyncException> callback)
 | 
			
		||||
    {
 | 
			
		||||
        // lock (callbacksLock)
 | 
			
		||||
        //  {
 | 
			
		||||
        errorCallbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
            callback(exception);
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IAsyncReply<T> Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
    {
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        progressCallbacks.Add(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public IAsyncReply<T> Chunk(Action<T> callback)
 | 
			
		||||
    {
 | 
			
		||||
        // lock (callbacksLock)
 | 
			
		||||
        // {
 | 
			
		||||
        chunkCallbacks.Add(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
        // }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Trigger(object result)
 | 
			
		||||
    {
 | 
			
		||||
        lock (asyncLock)
 | 
			
		||||
        {
 | 
			
		||||
            //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Trigger");
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            this.result = (T)result;
 | 
			
		||||
 | 
			
		||||
            resultReady = true;
 | 
			
		||||
 | 
			
		||||
            //if (mutex != null)
 | 
			
		||||
            mutex.Set();
 | 
			
		||||
 | 
			
		||||
            foreach (var cb in callbacks)
 | 
			
		||||
                cb((T)result);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (Debug)
 | 
			
		||||
                Console.WriteLine($"AsyncReply: {Id} Trigger ended");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void TriggerError(Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (exception is AsyncException)
 | 
			
		||||
            this.exception = exception as AsyncException;
 | 
			
		||||
        else
 | 
			
		||||
            this.exception = new AsyncException(ErrorType.Management, 0, exception.Message);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // lock (callbacksLock)
 | 
			
		||||
        // {
 | 
			
		||||
        foreach (var cb in errorCallbacks)
 | 
			
		||||
            cb(this.exception);
 | 
			
		||||
        //  }
 | 
			
		||||
 | 
			
		||||
        mutex?.Set();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void TriggerProgress(ProgressType type, int value, int max)
 | 
			
		||||
    {
 | 
			
		||||
        //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        foreach (var cb in progressCallbacks)
 | 
			
		||||
            cb(type, value, max);
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void TriggerChunk(object value)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        //timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        //lock (callbacksLock)
 | 
			
		||||
        //{
 | 
			
		||||
        foreach (var cb in chunkCallbacks)
 | 
			
		||||
            cb((T)value);
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncAwaiter<T> GetAwaiter()
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncAwaiter<T>(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply()
 | 
			
		||||
    {
 | 
			
		||||
        //   this.Debug = true;
 | 
			
		||||
        Id = MaxId++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply(T result)
 | 
			
		||||
    {
 | 
			
		||||
        //   this.Debug = true;
 | 
			
		||||
        resultReady = true;
 | 
			
		||||
        this.result = result;
 | 
			
		||||
 | 
			
		||||
        Id = MaxId++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
public AsyncReply<T> Then(Action<T> callback)
 | 
			
		||||
    {
 | 
			
		||||
       base.Then(new Action<object>(o => callback((T)o)));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Trigger(T result)
 | 
			
		||||
    {
 | 
			
		||||
        Trigger((object)result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<bool> MoveNext(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public new Task<T> Task
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return base.Task.ContinueWith<T>((t) =>
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
                return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
 | 
			
		||||
#else
 | 
			
		||||
                return (T)t.GetType().GetProperty("Result").GetValue(t);
 | 
			
		||||
#endif
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T Current => throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,158 +28,157 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public class AsyncReply
 | 
			
		||||
{
 | 
			
		||||
    public class AsyncReply
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected List<Action<object>> callbacks = new List<Action<object>>();
 | 
			
		||||
    protected object result;
 | 
			
		||||
 | 
			
		||||
    protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
 | 
			
		||||
 | 
			
		||||
    protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
 | 
			
		||||
 | 
			
		||||
    protected List<Action<object>> chunkCallbacks = new List<Action<object>>();
 | 
			
		||||
 | 
			
		||||
    object callbacksLock = new object();
 | 
			
		||||
 | 
			
		||||
    protected bool resultReady = false;
 | 
			
		||||
    AsyncException exception;
 | 
			
		||||
 | 
			
		||||
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool Ready
 | 
			
		||||
    {
 | 
			
		||||
   
 | 
			
		||||
        get { return resultReady; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public object Result
 | 
			
		||||
    {
 | 
			
		||||
        get { return result; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Then(Action<object> callback)
 | 
			
		||||
    {
 | 
			
		||||
        callbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
        protected List<Action<object>> callbacks = new List<Action<object>>();
 | 
			
		||||
        protected object result;
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            callback(result);
 | 
			
		||||
 | 
			
		||||
        protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
 | 
			
		||||
    public AsyncReply Error(Action<AsyncException> callback)
 | 
			
		||||
    {
 | 
			
		||||
        errorCallbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
        protected List<Action<object>> chunkCallbacks = new List<Action<object>>();
 | 
			
		||||
 | 
			
		||||
        object callbacksLock = new object();
 | 
			
		||||
 | 
			
		||||
        protected bool resultReady = false;
 | 
			
		||||
        AsyncException exception;
 | 
			
		||||
 | 
			
		||||
        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool Ready
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
        {
 | 
			
		||||
            get { return resultReady; }
 | 
			
		||||
            callback(exception);
 | 
			
		||||
            tcs.SetException(exception);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object Result
 | 
			
		||||
        {
 | 
			
		||||
            get { return result; }
 | 
			
		||||
        }
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Then(Action<object> callback)
 | 
			
		||||
        {
 | 
			
		||||
            callbacks.Add(callback);
 | 
			
		||||
    public AsyncReply Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
    {
 | 
			
		||||
        progressCallbacks.Add(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                callback(result);
 | 
			
		||||
    public AsyncReply Chunk(Action<object> callback)
 | 
			
		||||
    {
 | 
			
		||||
        chunkCallbacks.Add(callback);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
    public void Trigger(object result)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Error(Action<AsyncException> callback)
 | 
			
		||||
        {
 | 
			
		||||
            errorCallbacks.Add(callback);
 | 
			
		||||
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
            {
 | 
			
		||||
                callback(exception);
 | 
			
		||||
                tcs.SetException(exception);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Progress(Action<ProgressType, int, int> callback)
 | 
			
		||||
        {
 | 
			
		||||
            progressCallbacks.Add(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Chunk(Action<object> callback)
 | 
			
		||||
        {
 | 
			
		||||
            chunkCallbacks.Add(callback);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Trigger(object result)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            lock (callbacksLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (resultReady)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                this.result = result;
 | 
			
		||||
                resultReady = true;
 | 
			
		||||
 | 
			
		||||
                foreach (var cb in callbacks)
 | 
			
		||||
                    cb(result);
 | 
			
		||||
 | 
			
		||||
                tcs.TrySetResult(result);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerError(AsyncException exception)
 | 
			
		||||
        lock (callbacksLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            this.exception = exception;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            lock (callbacksLock)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var cb in errorCallbacks)
 | 
			
		||||
                    cb(exception);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            tcs.TrySetException(exception);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerProgress(ProgressType type, int value, int max)
 | 
			
		||||
        {
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            lock (callbacksLock)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var cb in progressCallbacks)
 | 
			
		||||
                    cb(type, value, max);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerChunk(object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (resultReady)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            lock (callbacksLock)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var cb in chunkCallbacks)
 | 
			
		||||
                    cb(value);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public Task Task
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return tcs.Task;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply(object result)
 | 
			
		||||
        {
 | 
			
		||||
            resultReady = true;
 | 
			
		||||
            tcs.SetResult(result);
 | 
			
		||||
            this.result = result;
 | 
			
		||||
            resultReady = true;
 | 
			
		||||
 | 
			
		||||
            foreach (var cb in callbacks)
 | 
			
		||||
                cb(result);
 | 
			
		||||
 | 
			
		||||
            tcs.TrySetResult(result);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void TriggerError(AsyncException exception)
 | 
			
		||||
    {
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.exception = exception;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        lock (callbacksLock)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var cb in errorCallbacks)
 | 
			
		||||
                cb(exception);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tcs.TrySetException(exception);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void TriggerProgress(ProgressType type, int value, int max)
 | 
			
		||||
    {
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        lock (callbacksLock)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var cb in progressCallbacks)
 | 
			
		||||
                cb(type, value, max);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void TriggerChunk(object value)
 | 
			
		||||
    {
 | 
			
		||||
        if (resultReady)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        lock (callbacksLock)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var cb in chunkCallbacks)
 | 
			
		||||
                cb(value);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Task Task
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return tcs.Task;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply(object result)
 | 
			
		||||
    {
 | 
			
		||||
        resultReady = true;
 | 
			
		||||
        tcs.SetResult(result);
 | 
			
		||||
        this.result = result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,10 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public enum ErrorType
 | 
			
		||||
{
 | 
			
		||||
    public enum ErrorType
 | 
			
		||||
    {
 | 
			
		||||
        Management,
 | 
			
		||||
        Exception
 | 
			
		||||
    }
 | 
			
		||||
    Management,
 | 
			
		||||
    Exception
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,44 +2,43 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public enum ExceptionCode : ushort
 | 
			
		||||
{
 | 
			
		||||
    public enum ExceptionCode : ushort
 | 
			
		||||
    {
 | 
			
		||||
        HostNotReachable,
 | 
			
		||||
        AccessDenied,
 | 
			
		||||
        UserOrTokenNotFound,
 | 
			
		||||
        ChallengeFailed,
 | 
			
		||||
        ResourceNotFound,
 | 
			
		||||
        AttachDenied,
 | 
			
		||||
        InvalidMethod,
 | 
			
		||||
        InvokeDenied,
 | 
			
		||||
        CreateDenied,
 | 
			
		||||
        AddParentDenied,
 | 
			
		||||
        AddChildDenied,
 | 
			
		||||
        ViewAttributeDenied,
 | 
			
		||||
        UpdateAttributeDenied,
 | 
			
		||||
        StoreNotFound,
 | 
			
		||||
        ParentNotFound,
 | 
			
		||||
        ChildNotFound,
 | 
			
		||||
        ResourceIsNotStore,
 | 
			
		||||
        DeleteDenied,
 | 
			
		||||
        DeleteFailed,
 | 
			
		||||
        UpdateAttributeFailed,
 | 
			
		||||
        GetAttributesFailed,
 | 
			
		||||
        ClearAttributesFailed,
 | 
			
		||||
        TemplateNotFound,
 | 
			
		||||
        RenameDenied,
 | 
			
		||||
        ClassNotFound,
 | 
			
		||||
        MethodNotFound,
 | 
			
		||||
        PropertyNotFound,
 | 
			
		||||
        SetPropertyDenied,
 | 
			
		||||
        ReadOnlyProperty,
 | 
			
		||||
        GeneralFailure,
 | 
			
		||||
        AddToStoreFailed,
 | 
			
		||||
        NotAttached,
 | 
			
		||||
        AlreadyListened,
 | 
			
		||||
        AlreadyUnlistened,
 | 
			
		||||
        NotListenable
 | 
			
		||||
    }
 | 
			
		||||
    HostNotReachable,
 | 
			
		||||
    AccessDenied,
 | 
			
		||||
    UserOrTokenNotFound,
 | 
			
		||||
    ChallengeFailed,
 | 
			
		||||
    ResourceNotFound,
 | 
			
		||||
    AttachDenied,
 | 
			
		||||
    InvalidMethod,
 | 
			
		||||
    InvokeDenied,
 | 
			
		||||
    CreateDenied,
 | 
			
		||||
    AddParentDenied,
 | 
			
		||||
    AddChildDenied,
 | 
			
		||||
    ViewAttributeDenied,
 | 
			
		||||
    UpdateAttributeDenied,
 | 
			
		||||
    StoreNotFound,
 | 
			
		||||
    ParentNotFound,
 | 
			
		||||
    ChildNotFound,
 | 
			
		||||
    ResourceIsNotStore,
 | 
			
		||||
    DeleteDenied,
 | 
			
		||||
    DeleteFailed,
 | 
			
		||||
    UpdateAttributeFailed,
 | 
			
		||||
    GetAttributesFailed,
 | 
			
		||||
    ClearAttributesFailed,
 | 
			
		||||
    TemplateNotFound,
 | 
			
		||||
    RenameDenied,
 | 
			
		||||
    ClassNotFound,
 | 
			
		||||
    MethodNotFound,
 | 
			
		||||
    PropertyNotFound,
 | 
			
		||||
    SetPropertyDenied,
 | 
			
		||||
    ReadOnlyProperty,
 | 
			
		||||
    GeneralFailure,
 | 
			
		||||
    AddToStoreFailed,
 | 
			
		||||
    NotAttached,
 | 
			
		||||
    AlreadyListened,
 | 
			
		||||
    AlreadyUnlistened,
 | 
			
		||||
    NotListenable
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,12 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public enum ExceptionLevel
 | 
			
		||||
{
 | 
			
		||||
    public enum ExceptionLevel
 | 
			
		||||
    {
 | 
			
		||||
        Code = 0x1,
 | 
			
		||||
        Message = 0x2,
 | 
			
		||||
        Source = 0x4,
 | 
			
		||||
        Trace = 0x8
 | 
			
		||||
    }
 | 
			
		||||
    Code = 0x1,
 | 
			
		||||
    Message = 0x2,
 | 
			
		||||
    Source = 0x4,
 | 
			
		||||
    Trace = 0x8
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,19 +3,18 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
{
 | 
			
		||||
    public interface IAsyncReply<out T>//IAsyncEnumerator<T> 
 | 
			
		||||
    {   
 | 
			
		||||
        IAsyncReply<T> Then(Action<T> callback);
 | 
			
		||||
        IAsyncReply<T> Error(Action<AsyncException> callback);
 | 
			
		||||
        IAsyncReply<T> Progress(Action<ProgressType, int, int> callback);
 | 
			
		||||
        IAsyncReply<T> Chunk(Action<T> callback);
 | 
			
		||||
        void Trigger(object result);
 | 
			
		||||
        void TriggerError(Exception exception);
 | 
			
		||||
        void TriggerProgress(ProgressType type, int value, int max);
 | 
			
		||||
        void TriggerChunk(object value);
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
        T Wait();
 | 
			
		||||
    }
 | 
			
		||||
public interface IAsyncReply<out T>//IAsyncEnumerator<T> 
 | 
			
		||||
{
 | 
			
		||||
    IAsyncReply<T> Then(Action<T> callback);
 | 
			
		||||
    IAsyncReply<T> Error(Action<AsyncException> callback);
 | 
			
		||||
    IAsyncReply<T> Progress(Action<ProgressType, int, int> callback);
 | 
			
		||||
    IAsyncReply<T> Chunk(Action<T> callback);
 | 
			
		||||
    void Trigger(object result);
 | 
			
		||||
    void TriggerError(Exception exception);
 | 
			
		||||
    void TriggerProgress(ProgressType type, int value, int max);
 | 
			
		||||
    void TriggerChunk(object value);
 | 
			
		||||
 | 
			
		||||
    T Wait();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,13 +27,12 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
{
 | 
			
		||||
    public delegate void DestroyedEvent(object sender);
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
    public interface IDestructible
 | 
			
		||||
    {
 | 
			
		||||
        event DestroyedEvent OnDestroy;
 | 
			
		||||
        void Destroy();
 | 
			
		||||
    }
 | 
			
		||||
public delegate void DestroyedEvent(object sender);
 | 
			
		||||
 | 
			
		||||
public interface IDestructible
 | 
			
		||||
{
 | 
			
		||||
    event DestroyedEvent OnDestroy;
 | 
			
		||||
    void Destroy();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,12 +29,11 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public enum LogType
 | 
			
		||||
{
 | 
			
		||||
    public enum LogType
 | 
			
		||||
    {
 | 
			
		||||
        Debug,
 | 
			
		||||
        Warning,
 | 
			
		||||
        Error,
 | 
			
		||||
    }
 | 
			
		||||
    Debug,
 | 
			
		||||
    Warning,
 | 
			
		||||
    Error,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,10 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Core
 | 
			
		||||
namespace Esiur.Core;
 | 
			
		||||
 | 
			
		||||
public enum ProgressType
 | 
			
		||||
{
 | 
			
		||||
    public enum ProgressType
 | 
			
		||||
    {
 | 
			
		||||
        Execution,
 | 
			
		||||
        Network,
 | 
			
		||||
    }
 | 
			
		||||
    Execution,
 | 
			
		||||
    Network,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,285 +30,284 @@ using System.Collections;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
 | 
			
		||||
{
 | 
			
		||||
    public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
 | 
			
		||||
 | 
			
		||||
    private readonly object syncRoot = new object();
 | 
			
		||||
    private List<T> list = new List<T>();
 | 
			
		||||
 | 
			
		||||
    public delegate void Modified(ST sender, int index, T oldValue, T newValue);
 | 
			
		||||
    public delegate void Added(ST sender, T value);
 | 
			
		||||
    public delegate void Removed(ST sender, T value);
 | 
			
		||||
    public delegate void Cleared(ST sender);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public event Modified OnModified;
 | 
			
		||||
    public event Removed OnRemoved;
 | 
			
		||||
    public event Cleared OnCleared;
 | 
			
		||||
    public event Added OnAdd;
 | 
			
		||||
 | 
			
		||||
    bool removableList;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public ST State { get; set; }
 | 
			
		||||
    /*
 | 
			
		||||
    IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
 | 
			
		||||
    {
 | 
			
		||||
        return list.OrderBy<T,TK>(keySelector);
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
        private readonly object syncRoot = new object();
 | 
			
		||||
        private List<T> list = new List<T>();
 | 
			
		||||
 | 
			
		||||
        public delegate void Modified(ST sender, int index, T oldValue, T newValue);
 | 
			
		||||
        public delegate void Added(ST sender, T value);
 | 
			
		||||
        public delegate void Removed(ST sender, T value);
 | 
			
		||||
        public delegate void Cleared(ST sender);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public event Modified OnModified;
 | 
			
		||||
        public event Removed OnRemoved;
 | 
			
		||||
        public event Cleared OnCleared;
 | 
			
		||||
        public event Added OnAdd;
 | 
			
		||||
 | 
			
		||||
        bool removableList;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public ST State { get; set; }
 | 
			
		||||
        /*
 | 
			
		||||
        IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
 | 
			
		||||
        {
 | 
			
		||||
            return list.OrderBy<T,TK>(keySelector);
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public void Sort()
 | 
			
		||||
        {
 | 
			
		||||
            lock(syncRoot)
 | 
			
		||||
    public void Sort()
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Sort();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Sort(IComparer<T> comparer)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Sort(comparer);
 | 
			
		||||
        }
 | 
			
		||||
    public void Sort(IComparer<T> comparer)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Sort(comparer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Sort(Comparison<T> comparison)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Sort(comparison);
 | 
			
		||||
        }
 | 
			
		||||
    public void Sort(Comparison<T> comparison)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Sort(comparison);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<T> Where(Func<T, bool> predicate)
 | 
			
		||||
        {
 | 
			
		||||
            return list.Where(predicate);
 | 
			
		||||
        }
 | 
			
		||||
    public IEnumerable<T> Where(Func<T, bool> predicate)
 | 
			
		||||
    {
 | 
			
		||||
        return list.Where(predicate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Convert AutoList to array
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>Array</returns>
 | 
			
		||||
        public T[] ToArray()
 | 
			
		||||
        {
 | 
			
		||||
            //    list.OrderBy()
 | 
			
		||||
            return list.ToArray();
 | 
			
		||||
        }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Convert AutoList to array
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>Array</returns>
 | 
			
		||||
    public T[] ToArray()
 | 
			
		||||
    {
 | 
			
		||||
        //    list.OrderBy()
 | 
			
		||||
        return list.ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create a new instance of AutoList
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="state">State object to be included when an event is raised.</param>
 | 
			
		||||
        public AutoList(ST state)
 | 
			
		||||
        {
 | 
			
		||||
            State = state;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new instance of AutoList
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="state">State object to be included when an event is raised.</param>
 | 
			
		||||
    public AutoList(ST state)
 | 
			
		||||
    {
 | 
			
		||||
        State = state;
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
            removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
 | 
			
		||||
        removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
 | 
			
		||||
#else
 | 
			
		||||
                        removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create a new instance of AutoList
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="values">Populate the list with items</param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public AutoList(ST state, T[] values)
 | 
			
		||||
        {
 | 
			
		||||
            State = state;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new instance of AutoList
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="values">Populate the list with items</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public AutoList(ST state, T[] values)
 | 
			
		||||
    {
 | 
			
		||||
        State = state;
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
            removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
 | 
			
		||||
        removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
 | 
			
		||||
#else
 | 
			
		||||
                                        removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
            AddRange(values);
 | 
			
		||||
        }
 | 
			
		||||
        AddRange(values);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Synchronization lock of the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public object SyncRoot
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Synchronization lock of the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object SyncRoot
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return syncRoot;
 | 
			
		||||
            }
 | 
			
		||||
            return syncRoot;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// First item in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public T First()
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// First item in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public T First()
 | 
			
		||||
    {
 | 
			
		||||
        return list.First();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get an item at a specified index
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public T this[int index]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return list.First();
 | 
			
		||||
            return list[index];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Get an item at a specified index
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public T this[int index]
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return list[index];
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                var oldValue = list[index];
 | 
			
		||||
            var oldValue = list[index];
 | 
			
		||||
 | 
			
		||||
                if (removableList)
 | 
			
		||||
                {
 | 
			
		||||
                    if (oldValue != null)
 | 
			
		||||
                        ((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
 | 
			
		||||
                    if (value != null)
 | 
			
		||||
                        ((IDestructible)value).OnDestroy += ItemDestroyed;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                lock (syncRoot)
 | 
			
		||||
                    list[index] = value;
 | 
			
		||||
 | 
			
		||||
                OnModified?.Invoke(State, index, oldValue, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Add item to the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Add(T value)
 | 
			
		||||
        {
 | 
			
		||||
            if (removableList)
 | 
			
		||||
            {
 | 
			
		||||
                if (oldValue != null)
 | 
			
		||||
                    ((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
 | 
			
		||||
                if (value != null)
 | 
			
		||||
                    ((IDestructible)value).OnDestroy += ItemDestroyed;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Add(value);
 | 
			
		||||
                list[index] = value;
 | 
			
		||||
 | 
			
		||||
            OnAdd?.Invoke(State, value);
 | 
			
		||||
            OnModified?.Invoke(State, index, oldValue, value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Add an array of items to the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void AddRange(T[] values)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var v in values)
 | 
			
		||||
                Add(v);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ItemDestroyed(object sender)
 | 
			
		||||
        {
 | 
			
		||||
            Remove((T)sender);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Clear the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Clear()
 | 
			
		||||
        {
 | 
			
		||||
            if (removableList)
 | 
			
		||||
                foreach (IDestructible v in list)
 | 
			
		||||
                    if (v != null)
 | 
			
		||||
                        v.OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Clear();
 | 
			
		||||
 | 
			
		||||
            OnCleared?.Invoke(State);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Remove an item from the list
 | 
			
		||||
        /// <param name="value">Item to remove</param>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool Remove(T value)
 | 
			
		||||
        {
 | 
			
		||||
            if (!list.Contains(value))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (removableList)
 | 
			
		||||
                if (value != null)
 | 
			
		||||
                    ((IDestructible)value).OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Remove(value);
 | 
			
		||||
 | 
			
		||||
            OnRemoved?.Invoke(State, value);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Number of items in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int Count
 | 
			
		||||
        {
 | 
			
		||||
            get { return list.Count; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsSynchronized => (list as ICollection).IsSynchronized;
 | 
			
		||||
 | 
			
		||||
        public bool IsReadOnly => false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if an item exists in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="value">Item to check if exists</param>
 | 
			
		||||
        public bool Contains(T value)
 | 
			
		||||
        {
 | 
			
		||||
            return list.Contains(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if any item of the given array is in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="values">Array of items</param>
 | 
			
		||||
        public bool ContainsAny(T[] values)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var v in values)
 | 
			
		||||
                if (list.Contains(v))
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if any item of the given list is in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="values">List of items</param>
 | 
			
		||||
        public bool ContainsAny(AutoList<T, ST> values)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var v in values)
 | 
			
		||||
                if (list.Contains((T)v))
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerator<T> GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void CopyTo(Array array, int index)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                (list as ICollection).CopyTo(array, index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void CopyTo(T[] array, int arrayIndex)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.CopyTo(array, arrayIndex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //bool ICollection<T>.Remove(T item)
 | 
			
		||||
        //{
 | 
			
		||||
        //    lock(syncRoot)
 | 
			
		||||
        //        return Remove(item);
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Add item to the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Add(T value)
 | 
			
		||||
    {
 | 
			
		||||
        if (removableList)
 | 
			
		||||
            if (value != null)
 | 
			
		||||
                ((IDestructible)value).OnDestroy += ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Add(value);
 | 
			
		||||
 | 
			
		||||
        OnAdd?.Invoke(State, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Add an array of items to the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void AddRange(T[] values)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in values)
 | 
			
		||||
            Add(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ItemDestroyed(object sender)
 | 
			
		||||
    {
 | 
			
		||||
        Remove((T)sender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Clear the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Clear()
 | 
			
		||||
    {
 | 
			
		||||
        if (removableList)
 | 
			
		||||
            foreach (IDestructible v in list)
 | 
			
		||||
                if (v != null)
 | 
			
		||||
                    v.OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Clear();
 | 
			
		||||
 | 
			
		||||
        OnCleared?.Invoke(State);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Remove an item from the list
 | 
			
		||||
    /// <param name="value">Item to remove</param>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool Remove(T value)
 | 
			
		||||
    {
 | 
			
		||||
        if (!list.Contains(value))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (removableList)
 | 
			
		||||
            if (value != null)
 | 
			
		||||
                ((IDestructible)value).OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Remove(value);
 | 
			
		||||
 | 
			
		||||
        OnRemoved?.Invoke(State, value);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Number of items in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Count
 | 
			
		||||
    {
 | 
			
		||||
        get { return list.Count; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsSynchronized => (list as ICollection).IsSynchronized;
 | 
			
		||||
 | 
			
		||||
    public bool IsReadOnly => false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Check if an item exists in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="value">Item to check if exists</param>
 | 
			
		||||
    public bool Contains(T value)
 | 
			
		||||
    {
 | 
			
		||||
        return list.Contains(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Check if any item of the given array is in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="values">Array of items</param>
 | 
			
		||||
    public bool ContainsAny(T[] values)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in values)
 | 
			
		||||
            if (list.Contains(v))
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Check if any item of the given list is in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="values">List of items</param>
 | 
			
		||||
    public bool ContainsAny(AutoList<T, ST> values)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in values)
 | 
			
		||||
            if (list.Contains((T)v))
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IEnumerator<T> GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void CopyTo(Array array, int index)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            (list as ICollection).CopyTo(array, index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void CopyTo(T[] array, int arrayIndex)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.CopyTo(array, arrayIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //bool ICollection<T>.Remove(T item)
 | 
			
		||||
    //{
 | 
			
		||||
    //    lock(syncRoot)
 | 
			
		||||
    //        return Remove(item);
 | 
			
		||||
    //}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2874
									
								
								Esiur/Data/Codec.cs
									
									
									
									
									
								
							
							
						
						
									
										2874
									
								
								Esiur/Data/Codec.cs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -28,92 +28,90 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
public enum DataType : byte
 | 
			
		||||
{
 | 
			
		||||
    public enum DataType : byte
 | 
			
		||||
    {
 | 
			
		||||
        Void = 0x0,
 | 
			
		||||
        //Variant,
 | 
			
		||||
        Bool,
 | 
			
		||||
        Int8,
 | 
			
		||||
        UInt8,
 | 
			
		||||
        Char,
 | 
			
		||||
        Int16,
 | 
			
		||||
        UInt16,
 | 
			
		||||
        Int32,
 | 
			
		||||
        UInt32,
 | 
			
		||||
        Int64,
 | 
			
		||||
        UInt64,
 | 
			
		||||
        Float32,
 | 
			
		||||
        Float64,
 | 
			
		||||
        Decimal,
 | 
			
		||||
        DateTime,
 | 
			
		||||
        Resource,
 | 
			
		||||
        DistributedResource,
 | 
			
		||||
        ResourceLink,
 | 
			
		||||
        String,
 | 
			
		||||
        Structure,
 | 
			
		||||
        Record,
 | 
			
		||||
        //Stream,
 | 
			
		||||
        //Array = 0x80,
 | 
			
		||||
        VarArray = 0x80,
 | 
			
		||||
        BoolArray,
 | 
			
		||||
        Int8Array,
 | 
			
		||||
        UInt8Array,
 | 
			
		||||
        CharArray,
 | 
			
		||||
        Int16Array,
 | 
			
		||||
        UInt16Array,
 | 
			
		||||
        Int32Array,
 | 
			
		||||
        UInt32Array,
 | 
			
		||||
        Int64Array,
 | 
			
		||||
        UInt64Array,
 | 
			
		||||
        Float32Array,
 | 
			
		||||
        Float64Array,
 | 
			
		||||
        DecimalArray,
 | 
			
		||||
        DateTimeArray,
 | 
			
		||||
        ResourceArray,
 | 
			
		||||
        DistributedResourceArray,
 | 
			
		||||
        ResourceLinkArray,
 | 
			
		||||
        StringArray,
 | 
			
		||||
        StructureArray,
 | 
			
		||||
        RecordArray,
 | 
			
		||||
        NotModified = 0x7f,
 | 
			
		||||
        Unspecified = 0xff,
 | 
			
		||||
    }
 | 
			
		||||
    Void = 0x0,
 | 
			
		||||
    //Variant,
 | 
			
		||||
    Bool,
 | 
			
		||||
    Int8,
 | 
			
		||||
    UInt8,
 | 
			
		||||
    Char,
 | 
			
		||||
    Int16,
 | 
			
		||||
    UInt16,
 | 
			
		||||
    Int32,
 | 
			
		||||
    UInt32,
 | 
			
		||||
    Int64,
 | 
			
		||||
    UInt64,
 | 
			
		||||
    Float32,
 | 
			
		||||
    Float64,
 | 
			
		||||
    Decimal,
 | 
			
		||||
    DateTime,
 | 
			
		||||
    Resource,
 | 
			
		||||
    DistributedResource,
 | 
			
		||||
    ResourceLink,
 | 
			
		||||
    String,
 | 
			
		||||
    Structure,
 | 
			
		||||
    Record,
 | 
			
		||||
    //Stream,
 | 
			
		||||
    //Array = 0x80,
 | 
			
		||||
    VarArray = 0x80,
 | 
			
		||||
    BoolArray,
 | 
			
		||||
    Int8Array,
 | 
			
		||||
    UInt8Array,
 | 
			
		||||
    CharArray,
 | 
			
		||||
    Int16Array,
 | 
			
		||||
    UInt16Array,
 | 
			
		||||
    Int32Array,
 | 
			
		||||
    UInt32Array,
 | 
			
		||||
    Int64Array,
 | 
			
		||||
    UInt64Array,
 | 
			
		||||
    Float32Array,
 | 
			
		||||
    Float64Array,
 | 
			
		||||
    DecimalArray,
 | 
			
		||||
    DateTimeArray,
 | 
			
		||||
    ResourceArray,
 | 
			
		||||
    DistributedResourceArray,
 | 
			
		||||
    ResourceLinkArray,
 | 
			
		||||
    StringArray,
 | 
			
		||||
    StructureArray,
 | 
			
		||||
    RecordArray,
 | 
			
		||||
    NotModified = 0x7f,
 | 
			
		||||
    Unspecified = 0xff,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public static class DataTypeExpansions
 | 
			
		||||
public static class DataTypeExpansions
 | 
			
		||||
{
 | 
			
		||||
    public static int Size(this DataType t)
 | 
			
		||||
    {
 | 
			
		||||
        public static int Size(this DataType t)
 | 
			
		||||
        switch (t)
 | 
			
		||||
        {
 | 
			
		||||
            switch (t)
 | 
			
		||||
            {
 | 
			
		||||
                case DataType.Void:
 | 
			
		||||
                case DataType.NotModified:
 | 
			
		||||
                    return 0;
 | 
			
		||||
                case DataType.Bool:
 | 
			
		||||
                case DataType.UInt8:
 | 
			
		||||
                case DataType.Int8:
 | 
			
		||||
                    return 1;
 | 
			
		||||
                case DataType.Char:
 | 
			
		||||
                case DataType.UInt16:
 | 
			
		||||
                case DataType.Int16:
 | 
			
		||||
                    return 2;
 | 
			
		||||
                case DataType.Int32:
 | 
			
		||||
                case DataType.UInt32:
 | 
			
		||||
                case DataType.Float32:
 | 
			
		||||
                case DataType.Resource:
 | 
			
		||||
                    return 4;
 | 
			
		||||
                case DataType.Int64:
 | 
			
		||||
                case DataType.UInt64:
 | 
			
		||||
                case DataType.Float64:
 | 
			
		||||
                case DataType.DateTime:
 | 
			
		||||
                    return 8;
 | 
			
		||||
                case DataType.DistributedResource:
 | 
			
		||||
                    return 4;
 | 
			
		||||
            case DataType.Void:
 | 
			
		||||
            case DataType.NotModified:
 | 
			
		||||
                return 0;
 | 
			
		||||
            case DataType.Bool:
 | 
			
		||||
            case DataType.UInt8:
 | 
			
		||||
            case DataType.Int8:
 | 
			
		||||
                return 1;
 | 
			
		||||
            case DataType.Char:
 | 
			
		||||
            case DataType.UInt16:
 | 
			
		||||
            case DataType.Int16:
 | 
			
		||||
                return 2;
 | 
			
		||||
            case DataType.Int32:
 | 
			
		||||
            case DataType.UInt32:
 | 
			
		||||
            case DataType.Float32:
 | 
			
		||||
            case DataType.Resource:
 | 
			
		||||
                return 4;
 | 
			
		||||
            case DataType.Int64:
 | 
			
		||||
            case DataType.UInt64:
 | 
			
		||||
            case DataType.Float64:
 | 
			
		||||
            case DataType.DateTime:
 | 
			
		||||
                return 8;
 | 
			
		||||
            case DataType.DistributedResource:
 | 
			
		||||
                return 4;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    return -1;
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,9 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
{
 | 
			
		||||
    public interface IRecord
 | 
			
		||||
    {
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public interface IRecord
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,10 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
public interface IUserType
 | 
			
		||||
{
 | 
			
		||||
    public interface IUserType
 | 
			
		||||
    {
 | 
			
		||||
        object Get();
 | 
			
		||||
        void Set(object value);
 | 
			
		||||
    }
 | 
			
		||||
    object Get();
 | 
			
		||||
    void Set(object value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,212 +33,210 @@ using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
 | 
			
		||||
{
 | 
			
		||||
    private readonly object syncRoot = new object();
 | 
			
		||||
    private Dictionary<KT, T> dic;
 | 
			
		||||
 | 
			
		||||
    public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
 | 
			
		||||
    public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender);
 | 
			
		||||
    public delegate void Added(T value, KeyList<KT, T> sender);
 | 
			
		||||
    public delegate void Removed(KT key, T value, KeyList<KT, T> sender);
 | 
			
		||||
    public delegate void Cleared(KeyList<KT, T> sender);
 | 
			
		||||
 | 
			
		||||
    public event Modified OnModified;
 | 
			
		||||
    public event Removed OnRemoved;
 | 
			
		||||
    public event Cleared OnCleared;
 | 
			
		||||
    public event Added OnAdd;
 | 
			
		||||
 | 
			
		||||
    bool removableList;
 | 
			
		||||
 | 
			
		||||
    public object SyncRoot
 | 
			
		||||
    {
 | 
			
		||||
        private readonly object syncRoot = new object();
 | 
			
		||||
        private Dictionary<KT, T> dic;
 | 
			
		||||
 | 
			
		||||
        public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender);
 | 
			
		||||
        public delegate void Added(T value, KeyList<KT, T> sender);
 | 
			
		||||
        public delegate void Removed(KT key, T value, KeyList<KT, T> sender);
 | 
			
		||||
        public delegate void Cleared(KeyList<KT, T> sender);
 | 
			
		||||
 | 
			
		||||
        public event Modified OnModified;
 | 
			
		||||
        public event Removed OnRemoved;
 | 
			
		||||
        public event Cleared OnCleared;
 | 
			
		||||
        public event Added OnAdd;
 | 
			
		||||
 | 
			
		||||
        bool removableList;
 | 
			
		||||
 | 
			
		||||
        public object SyncRoot
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return syncRoot;
 | 
			
		||||
            }
 | 
			
		||||
            return syncRoot;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public T Take(KT key)
 | 
			
		||||
    public T Take(KT key)
 | 
			
		||||
    {
 | 
			
		||||
        if (dic.ContainsKey(key))
 | 
			
		||||
        {
 | 
			
		||||
            var v = dic[key];
 | 
			
		||||
            Remove(key);
 | 
			
		||||
            return v;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return default(T);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
 | 
			
		||||
    {
 | 
			
		||||
        dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T[] ToArray()
 | 
			
		||||
    {
 | 
			
		||||
        var a = new T[Count];
 | 
			
		||||
        dic.Values.CopyTo(a, 0);
 | 
			
		||||
        return a;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Add(KT key, T value)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            if (removableList)
 | 
			
		||||
                if (value != null)
 | 
			
		||||
                    ((IDestructible)value).OnDestroy += ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
            if (dic.ContainsKey(key))
 | 
			
		||||
            {
 | 
			
		||||
                var v = dic[key];
 | 
			
		||||
                Remove(key);
 | 
			
		||||
                return v;
 | 
			
		||||
                var oldValue = dic[key];
 | 
			
		||||
                if (removableList)
 | 
			
		||||
                    if (oldValue != null)
 | 
			
		||||
                        ((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
 | 
			
		||||
                dic[key] = value;
 | 
			
		||||
                if (OnModified != null)
 | 
			
		||||
                    OnModified(key, oldValue, value, this);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                dic.Add(key, value);
 | 
			
		||||
 | 
			
		||||
                if (OnAdd != null)
 | 
			
		||||
                    OnAdd(value, this);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ItemDestroyed(object sender)
 | 
			
		||||
    {
 | 
			
		||||
        RemoveValue((T)sender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RemoveValue(T value)
 | 
			
		||||
    {
 | 
			
		||||
        var toRemove = new List<KT>();
 | 
			
		||||
        foreach (var kv in dic)
 | 
			
		||||
            if (kv.Value.Equals(value))
 | 
			
		||||
                toRemove.Add(kv.Key);
 | 
			
		||||
 | 
			
		||||
        foreach (var k in toRemove)
 | 
			
		||||
            Remove(k);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T this[KT key]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            if (dic.ContainsKey(key))
 | 
			
		||||
                return dic[key];
 | 
			
		||||
            else
 | 
			
		||||
                return default(T);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value);
 | 
			
		||||
            Add(key, value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public T[] ToArray()
 | 
			
		||||
 | 
			
		||||
    public IEnumerator<KeyValuePair<KT, T>> GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return dic.GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return dic.GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Clear()
 | 
			
		||||
    {
 | 
			
		||||
        if (removableList)
 | 
			
		||||
            foreach (IDestructible v in dic.Values)
 | 
			
		||||
                if (v != null)
 | 
			
		||||
                    v.OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            dic.Clear();
 | 
			
		||||
 | 
			
		||||
        if (OnCleared != null)
 | 
			
		||||
            OnCleared(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Dictionary<KT, T>.KeyCollection Keys
 | 
			
		||||
    {
 | 
			
		||||
        get { return dic.Keys; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Dictionary<KT, T>.ValueCollection Values
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            var a = new T[Count];
 | 
			
		||||
            dic.Values.CopyTo(a, 0);
 | 
			
		||||
            return a;
 | 
			
		||||
            return dic.Values;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Add(KT key, T value)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
            {
 | 
			
		||||
                if (removableList)
 | 
			
		||||
                    if (value != null)
 | 
			
		||||
                        ((IDestructible)value).OnDestroy += ItemDestroyed;
 | 
			
		||||
    public void Remove(KT key)
 | 
			
		||||
    {
 | 
			
		||||
        if (!dic.ContainsKey(key))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
                if (dic.ContainsKey(key))
 | 
			
		||||
                {
 | 
			
		||||
                    var oldValue = dic[key];
 | 
			
		||||
                    if (removableList)
 | 
			
		||||
                        if (oldValue != null)
 | 
			
		||||
                            ((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
 | 
			
		||||
                    dic[key] = value;
 | 
			
		||||
                    if (OnModified != null)
 | 
			
		||||
                        OnModified(key, oldValue, value, this);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    dic.Add(key, value);
 | 
			
		||||
        var value = dic[key];
 | 
			
		||||
 | 
			
		||||
                    if (OnAdd != null)
 | 
			
		||||
                        OnAdd(value, this);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (removableList)
 | 
			
		||||
            if (value != null)
 | 
			
		||||
                ((IDestructible)value).OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
        private void ItemDestroyed(object sender)
 | 
			
		||||
        {
 | 
			
		||||
            RemoveValue((T)sender);
 | 
			
		||||
        }
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            dic.Remove(key);
 | 
			
		||||
 | 
			
		||||
        public void RemoveValue(T value)
 | 
			
		||||
        {
 | 
			
		||||
            var toRemove = new List<KT>();
 | 
			
		||||
            foreach (var kv in dic)
 | 
			
		||||
                if (kv.Value.Equals(value))
 | 
			
		||||
                    toRemove.Add(kv.Key);
 | 
			
		||||
        if (OnRemoved != null)
 | 
			
		||||
            OnRemoved(key, value, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            foreach (var k in toRemove)
 | 
			
		||||
                Remove(k);
 | 
			
		||||
        }
 | 
			
		||||
    public object Owner
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public T this[KT key]
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (dic.ContainsKey(key))
 | 
			
		||||
                    return dic[key];
 | 
			
		||||
                else
 | 
			
		||||
                    return default(T);
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                 Add(key, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
     
 | 
			
		||||
        public IEnumerator<KeyValuePair<KT, T>> GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return dic.GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return dic.GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
    public int Count
 | 
			
		||||
    {
 | 
			
		||||
        get { return dic.Count; }
 | 
			
		||||
    }
 | 
			
		||||
    public bool Contains(KT Key)
 | 
			
		||||
    {
 | 
			
		||||
        return dic.ContainsKey(Key);
 | 
			
		||||
    }
 | 
			
		||||
    public bool ContainsKey(KT Key)
 | 
			
		||||
    {
 | 
			
		||||
        return dic.ContainsKey(Key);
 | 
			
		||||
    }
 | 
			
		||||
    public bool ContainsValue(T Value)
 | 
			
		||||
    {
 | 
			
		||||
        return dic.ContainsValue(Value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Clear()
 | 
			
		||||
        {
 | 
			
		||||
            if (removableList)
 | 
			
		||||
                foreach (IDestructible v in dic.Values)
 | 
			
		||||
                    if (v != null)
 | 
			
		||||
                        v.OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                dic.Clear();
 | 
			
		||||
 | 
			
		||||
            if (OnCleared != null)
 | 
			
		||||
                OnCleared(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Dictionary<KT, T>.KeyCollection Keys
 | 
			
		||||
        {
 | 
			
		||||
            get { return dic.Keys; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Dictionary<KT, T>.ValueCollection Values
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return dic.Values;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Remove(KT key)
 | 
			
		||||
        {
 | 
			
		||||
            if (!dic.ContainsKey(key))
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var value = dic[key];
 | 
			
		||||
 | 
			
		||||
            if (removableList)
 | 
			
		||||
                if (value != null)
 | 
			
		||||
                    ((IDestructible)value).OnDestroy -= ItemDestroyed;
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                dic.Remove(key);
 | 
			
		||||
 | 
			
		||||
            if (OnRemoved != null)
 | 
			
		||||
                OnRemoved(key, value, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object Owner
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Count
 | 
			
		||||
        {
 | 
			
		||||
            get { return dic.Count; }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Contains(KT Key)
 | 
			
		||||
        {
 | 
			
		||||
            return dic.ContainsKey(Key);
 | 
			
		||||
        }
 | 
			
		||||
        public bool ContainsKey(KT Key)
 | 
			
		||||
        {
 | 
			
		||||
            return dic.ContainsKey(Key);
 | 
			
		||||
        }
 | 
			
		||||
        public bool ContainsValue(T Value)
 | 
			
		||||
        {
 | 
			
		||||
            return dic.ContainsValue(Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        public KeyList(object owner = null)
 | 
			
		||||
        {
 | 
			
		||||
            #if NETSTANDARD
 | 
			
		||||
                        removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
 | 
			
		||||
    public KeyList(object owner = null)
 | 
			
		||||
    {
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
        removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
 | 
			
		||||
#else
 | 
			
		||||
                        removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
            this.Owner = owner;
 | 
			
		||||
        this.Owner = owner;
 | 
			
		||||
 | 
			
		||||
            if (typeof(KT) == typeof(string))
 | 
			
		||||
                dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
            else
 | 
			
		||||
                dic = new Dictionary<KT, T>();
 | 
			
		||||
        }
 | 
			
		||||
        if (typeof(KT) == typeof(string))
 | 
			
		||||
            dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        else
 | 
			
		||||
            dic = new Dictionary<KT, T>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,10 +28,8 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
public class NotModified
 | 
			
		||||
{
 | 
			
		||||
    public class NotModified
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,34 +2,33 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
{
 | 
			
		||||
    public class PropertyValue
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Get or set the value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public object Value { get; set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Get or set date of modification or occurrence.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DateTime Date { get; set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Get or set property age.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ulong Age { get; set; }
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create an instance of PropertyValue.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="value">Value.</param>
 | 
			
		||||
        /// <param name="age">Age.</param>
 | 
			
		||||
        /// <param name="date">Date.</param>
 | 
			
		||||
        public PropertyValue(object value, ulong age, DateTime date)
 | 
			
		||||
        {
 | 
			
		||||
            Value = value;
 | 
			
		||||
            Age = age;
 | 
			
		||||
            Date = date;
 | 
			
		||||
        }
 | 
			
		||||
public class PropertyValue
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get or set the value.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object Value { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get or set date of modification or occurrence.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime Date { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get or set property age.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong Age { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create an instance of PropertyValue.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="value">Value.</param>
 | 
			
		||||
    /// <param name="age">Age.</param>
 | 
			
		||||
    /// <param name="date">Date.</param>
 | 
			
		||||
    public PropertyValue(object value, ulong age, DateTime date)
 | 
			
		||||
    {
 | 
			
		||||
        Value = value;
 | 
			
		||||
        Age = age;
 | 
			
		||||
        Date = date;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,8 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
public class Record : KeyList<string, object>, IRecord
 | 
			
		||||
{
 | 
			
		||||
    public class Record: KeyList<string, object>, IRecord
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,12 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public enum RecordComparisonResult : byte
 | 
			
		||||
{
 | 
			
		||||
    public enum RecordComparisonResult : byte
 | 
			
		||||
    {
 | 
			
		||||
        Null,
 | 
			
		||||
        Record,
 | 
			
		||||
        RecordSameType,
 | 
			
		||||
        Same
 | 
			
		||||
    }
 | 
			
		||||
    Null,
 | 
			
		||||
    Record,
 | 
			
		||||
    RecordSameType,
 | 
			
		||||
    Same
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,11 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public enum ResourceArrayType
 | 
			
		||||
{
 | 
			
		||||
    public enum ResourceArrayType
 | 
			
		||||
    {
 | 
			
		||||
        Dynamic = 0x0,
 | 
			
		||||
        Static = 0x10,
 | 
			
		||||
        Wrapper = 0x20,
 | 
			
		||||
    }
 | 
			
		||||
    Dynamic = 0x0,
 | 
			
		||||
    Static = 0x10,
 | 
			
		||||
    Wrapper = 0x20,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,12 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public enum ResourceComparisonResult
 | 
			
		||||
{
 | 
			
		||||
    public enum ResourceComparisonResult
 | 
			
		||||
    {
 | 
			
		||||
        Null, // null
 | 
			
		||||
        Distributed, // resource is distributed
 | 
			
		||||
        Local, // resource is local
 | 
			
		||||
        Same, // Same as previous
 | 
			
		||||
    }
 | 
			
		||||
    Null, // null
 | 
			
		||||
    Distributed, // resource is distributed
 | 
			
		||||
    Local, // resource is local
 | 
			
		||||
    Same, // Same as previous
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using Esiur.Net.IIP;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
Copyright (c) 2017-2021 Ahmed Kh. Zamil
 | 
			
		||||
@@ -24,76 +23,77 @@ SOFTWARE.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
using Esiur.Net.IIP;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
class ResourceJsonConverter : JsonConverter<IResource>
 | 
			
		||||
{
 | 
			
		||||
    class ResourceJsonConverter : JsonConverter<IResource>
 | 
			
		||||
    public override IResource Read(
 | 
			
		||||
        ref Utf8JsonReader reader,
 | 
			
		||||
        Type typeToConvert,
 | 
			
		||||
        JsonSerializerOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        public override IResource Read(
 | 
			
		||||
            ref Utf8JsonReader reader,
 | 
			
		||||
            Type typeToConvert,
 | 
			
		||||
            JsonSerializerOptions options)
 | 
			
		||||
        {
 | 
			
		||||
            return (IResource)JsonSerializer.Deserialize(ref reader,typeof(IResource), options);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public override void Write(
 | 
			
		||||
            Utf8JsonWriter writer,
 | 
			
		||||
            IResource resource,
 | 
			
		||||
            JsonSerializerOptions options)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            writer.WriteStartObject();
 | 
			
		||||
 | 
			
		||||
            foreach (var pt in resource.Instance.Template.Properties)
 | 
			
		||||
            {
 | 
			
		||||
                var rt = pt.PropertyInfo.GetValue(resource, null);
 | 
			
		||||
                if (rt is DistributedPropertyContext)
 | 
			
		||||
                    continue;
 | 
			
		||||
                
 | 
			
		||||
                writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name);
 | 
			
		||||
 | 
			
		||||
                if (rt is IResource)
 | 
			
		||||
                    JsonSerializer.Serialize(writer, (IResource) rt, options);
 | 
			
		||||
                else
 | 
			
		||||
                    JsonSerializer.Serialize(writer, rt, options);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            writer.WriteEndObject();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return (IResource)JsonSerializer.Deserialize(ref reader, typeof(IResource), options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public class DoubleJsonConverter : JsonConverter<double>
 | 
			
		||||
    public override void Write(
 | 
			
		||||
        Utf8JsonWriter writer,
 | 
			
		||||
        IResource resource,
 | 
			
		||||
        JsonSerializerOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 | 
			
		||||
        {
 | 
			
		||||
            if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
 | 
			
		||||
            {
 | 
			
		||||
                return double.NaN;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number
 | 
			
		||||
        }
 | 
			
		||||
        writer.WriteStartObject();
 | 
			
		||||
 | 
			
		||||
        public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
 | 
			
		||||
        foreach (var pt in resource.Instance.Template.Properties)
 | 
			
		||||
        {
 | 
			
		||||
            if (double.IsNaN(value))
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteStringValue("NaN");
 | 
			
		||||
            }
 | 
			
		||||
            var rt = pt.PropertyInfo.GetValue(resource, null);
 | 
			
		||||
            if (rt is DistributedPropertyContext)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name);
 | 
			
		||||
 | 
			
		||||
            if (rt is IResource)
 | 
			
		||||
                JsonSerializer.Serialize(writer, (IResource)rt, options);
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteNumberValue(value);
 | 
			
		||||
            }
 | 
			
		||||
                JsonSerializer.Serialize(writer, rt, options);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        writer.WriteEndObject();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class DoubleJsonConverter : JsonConverter<double>
 | 
			
		||||
{
 | 
			
		||||
    public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
 | 
			
		||||
        {
 | 
			
		||||
            return double.NaN;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        if (double.IsNaN(value))
 | 
			
		||||
        {
 | 
			
		||||
            writer.WriteStringValue("NaN");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            writer.WriteNumberValue(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,245 +30,244 @@ using System.Collections;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
 | 
			
		||||
{
 | 
			
		||||
    public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
 | 
			
		||||
 | 
			
		||||
    private readonly object syncRoot = new object();
 | 
			
		||||
    private List<T> list = new List<T>();
 | 
			
		||||
 | 
			
		||||
    public delegate void Modified(ST sender, int index, T oldValue, T newValue);
 | 
			
		||||
    public delegate void Added(ST sender, T value);
 | 
			
		||||
    public delegate void Removed(ST sender, int index, T value);
 | 
			
		||||
    public delegate void Cleared(ST sender);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public event Modified OnModified;
 | 
			
		||||
    public event Removed OnRemoved;
 | 
			
		||||
    public event Cleared OnCleared;
 | 
			
		||||
    public event Added OnAdd;
 | 
			
		||||
 | 
			
		||||
    ST state;
 | 
			
		||||
 | 
			
		||||
    public void Sort()
 | 
			
		||||
    {
 | 
			
		||||
        list.Sort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        private readonly object syncRoot = new object();
 | 
			
		||||
        private List<T> list = new List<T>();
 | 
			
		||||
    public void Sort(IComparer<T> comparer)
 | 
			
		||||
    {
 | 
			
		||||
        list.Sort(comparer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public delegate void Modified(ST sender, int index, T oldValue, T newValue);
 | 
			
		||||
        public delegate void Added(ST sender, T value);
 | 
			
		||||
        public delegate void Removed(ST sender, int index, T value);
 | 
			
		||||
        public delegate void Cleared(ST sender);
 | 
			
		||||
    public void Sort(Comparison<T> comparison)
 | 
			
		||||
    {
 | 
			
		||||
        list.Sort(comparison);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IEnumerable<T> Where(Func<T, bool> predicate)
 | 
			
		||||
    {
 | 
			
		||||
        return list.Where(predicate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public event Modified OnModified;
 | 
			
		||||
        public event Removed OnRemoved;
 | 
			
		||||
        public event Cleared OnCleared;
 | 
			
		||||
        public event Added OnAdd;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Convert AutoList to array
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>Array</returns>
 | 
			
		||||
    public T[] ToArray()
 | 
			
		||||
    {
 | 
			
		||||
        //    list.OrderBy()
 | 
			
		||||
        return list.ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        ST state;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new instance of AutoList
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="state">State object to be included when an event is raised.</param>
 | 
			
		||||
    public ResourceList(ST state)
 | 
			
		||||
    {
 | 
			
		||||
        this.state = state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Sort()
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new instance of AutoList
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="values">Populate the list with items</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public ResourceList(ST state, T[] values)
 | 
			
		||||
    {
 | 
			
		||||
        this.state = state;
 | 
			
		||||
        AddRange(values);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Synchronization lock of the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object SyncRoot
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            list.Sort();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Sort(IComparer<T> comparer)
 | 
			
		||||
        {
 | 
			
		||||
            list.Sort(comparer);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Sort(Comparison<T> comparison)
 | 
			
		||||
        {
 | 
			
		||||
            list.Sort(comparison);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<T> Where(Func<T, bool> predicate)
 | 
			
		||||
        {
 | 
			
		||||
            return list.Where(predicate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Convert AutoList to array
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>Array</returns>
 | 
			
		||||
        public T[] ToArray()
 | 
			
		||||
        {
 | 
			
		||||
            //    list.OrderBy()
 | 
			
		||||
            return list.ToArray();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create a new instance of AutoList
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="state">State object to be included when an event is raised.</param>
 | 
			
		||||
        public ResourceList(ST state)
 | 
			
		||||
        {
 | 
			
		||||
            this.state = state;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create a new instance of AutoList
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="values">Populate the list with items</param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public ResourceList(ST state, T[] values)
 | 
			
		||||
        {
 | 
			
		||||
            this.state = state;
 | 
			
		||||
            AddRange(values);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Synchronization lock of the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public object SyncRoot
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return syncRoot;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// First item in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public T First()
 | 
			
		||||
        {
 | 
			
		||||
            return list.First();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Get an item at a specified index
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public T this[int index]
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return list[index];
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                var oldValue = list[index];
 | 
			
		||||
 | 
			
		||||
                lock (syncRoot)
 | 
			
		||||
                    list[index] = value;
 | 
			
		||||
 | 
			
		||||
                OnModified?.Invoke(state, index, oldValue, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Add item to the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Add(T value)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Add(value);
 | 
			
		||||
 | 
			
		||||
            OnAdd?.Invoke(state, value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Add an array of items to the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void AddRange(T[] values)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var v in values)
 | 
			
		||||
                Add(v);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ItemDestroyed(object sender)
 | 
			
		||||
        {
 | 
			
		||||
            Remove((T)sender);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Clear the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Clear()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list.Clear();
 | 
			
		||||
 | 
			
		||||
            OnCleared?.Invoke(state);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Remove an item from the list
 | 
			
		||||
        /// <param name="value">Item to remove</param>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Remove(T value)
 | 
			
		||||
        {
 | 
			
		||||
            var index = 0;
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
            {
 | 
			
		||||
                index = list.IndexOf(value);
 | 
			
		||||
 | 
			
		||||
                if (index == -1)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                list.RemoveAt(index);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            OnRemoved?.Invoke(state, index, value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Number of items in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int Count
 | 
			
		||||
        {
 | 
			
		||||
            get { return list.Count; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsSynchronized => (list as ICollection).IsSynchronized;
 | 
			
		||||
 | 
			
		||||
        public bool IsReadOnly => throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if an item exists in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="value">Item to check if exists</param>
 | 
			
		||||
        public bool Contains(T value)
 | 
			
		||||
        {
 | 
			
		||||
            return list.Contains(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if any item of the given array is in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="values">Array of items</param>
 | 
			
		||||
        public bool ContainsAny(T[] values)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var v in values)
 | 
			
		||||
                if (list.Contains(v))
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if any item of the given list is in the list
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="values">List of items</param>
 | 
			
		||||
        public bool ContainsAny(AutoList<T, ST> values)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var v in values)
 | 
			
		||||
                if (list.Contains((T)v))
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerator<T> GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void CopyTo(Array array, int index)
 | 
			
		||||
        {
 | 
			
		||||
            (list as ICollection).CopyTo(array, index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void CopyTo(T[] array, int arrayIndex)
 | 
			
		||||
        {
 | 
			
		||||
            list.CopyTo(array, arrayIndex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool ICollection<T>.Remove(T item)
 | 
			
		||||
        {
 | 
			
		||||
            return list.Remove(item);
 | 
			
		||||
            return syncRoot;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// First item in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public T First()
 | 
			
		||||
    {
 | 
			
		||||
        return list.First();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get an item at a specified index
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public T this[int index]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return list[index];
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            var oldValue = list[index];
 | 
			
		||||
 | 
			
		||||
            lock (syncRoot)
 | 
			
		||||
                list[index] = value;
 | 
			
		||||
 | 
			
		||||
            OnModified?.Invoke(state, index, oldValue, value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Add item to the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Add(T value)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Add(value);
 | 
			
		||||
 | 
			
		||||
        OnAdd?.Invoke(state, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Add an array of items to the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void AddRange(T[] values)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in values)
 | 
			
		||||
            Add(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ItemDestroyed(object sender)
 | 
			
		||||
    {
 | 
			
		||||
        Remove((T)sender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Clear the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Clear()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
            list.Clear();
 | 
			
		||||
 | 
			
		||||
        OnCleared?.Invoke(state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Remove an item from the list
 | 
			
		||||
    /// <param name="value">Item to remove</param>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Remove(T value)
 | 
			
		||||
    {
 | 
			
		||||
        var index = 0;
 | 
			
		||||
 | 
			
		||||
        lock (syncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            index = list.IndexOf(value);
 | 
			
		||||
 | 
			
		||||
            if (index == -1)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            list.RemoveAt(index);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        OnRemoved?.Invoke(state, index, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Number of items in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Count
 | 
			
		||||
    {
 | 
			
		||||
        get { return list.Count; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsSynchronized => (list as ICollection).IsSynchronized;
 | 
			
		||||
 | 
			
		||||
    public bool IsReadOnly => throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Check if an item exists in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="value">Item to check if exists</param>
 | 
			
		||||
    public bool Contains(T value)
 | 
			
		||||
    {
 | 
			
		||||
        return list.Contains(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Check if any item of the given array is in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="values">Array of items</param>
 | 
			
		||||
    public bool ContainsAny(T[] values)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in values)
 | 
			
		||||
            if (list.Contains(v))
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Check if any item of the given list is in the list
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="values">List of items</param>
 | 
			
		||||
    public bool ContainsAny(AutoList<T, ST> values)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var v in values)
 | 
			
		||||
            if (list.Contains((T)v))
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IEnumerator<T> GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return ((IEnumerable<T>)list).GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void CopyTo(Array array, int index)
 | 
			
		||||
    {
 | 
			
		||||
        (list as ICollection).CopyTo(array, index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void CopyTo(T[] array, int arrayIndex)
 | 
			
		||||
    {
 | 
			
		||||
        list.CopyTo(array, arrayIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool ICollection<T>.Remove(T item)
 | 
			
		||||
    {
 | 
			
		||||
        return list.Remove(item);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,190 +31,189 @@ using System.Text;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class StringKeyList : IEnumerable<KeyValuePair<string, string>>
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class StringKeyList : IEnumerable<KeyValuePair<string, string>>
 | 
			
		||||
    //private List<string> m_keys = new List<string>();
 | 
			
		||||
    //private List<string> m_values = new List<string>();
 | 
			
		||||
 | 
			
		||||
    private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>();
 | 
			
		||||
 | 
			
		||||
    private bool allowMultiple;
 | 
			
		||||
 | 
			
		||||
    public delegate void Modified(string Key, string NewValue);
 | 
			
		||||
    public event Modified OnModified;
 | 
			
		||||
 | 
			
		||||
    public StringKeyList(bool AllowMultipleValues = false)
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
        //private List<string> m_keys = new List<string>();
 | 
			
		||||
        //private List<string> m_values = new List<string>();
 | 
			
		||||
        allowMultiple = AllowMultipleValues;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>();
 | 
			
		||||
    public void Add(string Key, string Value)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnModified != null)
 | 
			
		||||
            OnModified(Key, Value);
 | 
			
		||||
 | 
			
		||||
        private bool allowMultiple;
 | 
			
		||||
        var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
        public delegate void Modified(string Key, string NewValue);
 | 
			
		||||
        public event Modified OnModified;
 | 
			
		||||
 | 
			
		||||
        public StringKeyList(bool AllowMultipleValues = false)
 | 
			
		||||
        if (!allowMultiple)
 | 
			
		||||
        {
 | 
			
		||||
            allowMultiple = AllowMultipleValues;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Add(string Key, string Value)
 | 
			
		||||
        {
 | 
			
		||||
            if (OnModified != null)
 | 
			
		||||
                OnModified(Key, Value);
 | 
			
		||||
 | 
			
		||||
            var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
            if (!allowMultiple)
 | 
			
		||||
            {
 | 
			
		||||
                foreach(var kv in m_Variables)
 | 
			
		||||
                {
 | 
			
		||||
                    if (kv.Key.ToLower() == key)
 | 
			
		||||
                    {
 | 
			
		||||
                        m_Variables.Remove(kv);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            m_Variables.Add(new KeyValuePair<string, string>(Key, Value));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string this[string Key]
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var key = Key.ToLower();
 | 
			
		||||
                foreach (var kv in m_Variables)
 | 
			
		||||
                    if (kv.Key.ToLower() == key)
 | 
			
		||||
                        return kv.Value;
 | 
			
		||||
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
                var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray();
 | 
			
		||||
 | 
			
		||||
                foreach (var item in toRemove)
 | 
			
		||||
                    m_Variables.Remove(item);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                m_Variables.Add(new KeyValuePair<string, string>(Key, value));
 | 
			
		||||
 | 
			
		||||
                OnModified?.Invoke(Key, value);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            //return m_keys.GetEnumerator();
 | 
			
		||||
            return m_Variables.GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return m_Variables.GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Clear()
 | 
			
		||||
        {
 | 
			
		||||
            if (OnModified != null)
 | 
			
		||||
                OnModified(null, null);
 | 
			
		||||
 | 
			
		||||
            m_Variables.Clear();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public string[] Keys
 | 
			
		||||
        {
 | 
			
		||||
            get 
 | 
			
		||||
            { 
 | 
			
		||||
                return m_keys.ToArray();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //public Dictionary<string, string>.ValueCollection Values
 | 
			
		||||
        public string[] Values
 | 
			
		||||
        {
 | 
			
		||||
            get 
 | 
			
		||||
            { 
 | 
			
		||||
                //return m_Variables.Values; 
 | 
			
		||||
                return m_values.ToArray();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public List<string> GetValues(string Key)
 | 
			
		||||
        {
 | 
			
		||||
            var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
            List<string> values = new List<string>();
 | 
			
		||||
 | 
			
		||||
            foreach (var kv in m_Variables)
 | 
			
		||||
                if (kv.Key.ToLower() == key)
 | 
			
		||||
                    values.Add(kv.Value);
 | 
			
		||||
            
 | 
			
		||||
            return values;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void RemoveAll(string Key)
 | 
			
		||||
        {
 | 
			
		||||
            while (Remove(Key)){}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Remove(string Key)
 | 
			
		||||
        {
 | 
			
		||||
            var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
            foreach(var kv in m_Variables)
 | 
			
		||||
            {
 | 
			
		||||
                if (kv.Key.ToLower() == key)
 | 
			
		||||
                {
 | 
			
		||||
                    if (OnModified != null)
 | 
			
		||||
                        OnModified(Key, null);
 | 
			
		||||
                    m_Variables.Remove(kv);
 | 
			
		||||
                    return true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Count
 | 
			
		||||
        {
 | 
			
		||||
            get { return m_Variables.Count; }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public bool ContainsKey(string Key)
 | 
			
		||||
        m_Variables.Add(new KeyValuePair<string, string>(Key, Value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string this[string Key]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            var key = Key.ToLower();
 | 
			
		||||
            foreach (var kv in m_Variables)
 | 
			
		||||
                if (kv.Key.ToLower() == key)
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
                    return kv.Value;
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public bool ContainsKey(string Key)
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            //return m_Variables.ContainsKey(Key);
 | 
			
		||||
            return m_keys.Contains(Key.ToLower());
 | 
			
		||||
        }
 | 
			
		||||
         */
 | 
			
		||||
            var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
        public bool ContainsValue(string Value)
 | 
			
		||||
        {
 | 
			
		||||
            var value = Value.ToLower();
 | 
			
		||||
            foreach (var kv in m_Variables)
 | 
			
		||||
                if (kv.Value.ToLower() == value)
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
            var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray();
 | 
			
		||||
 | 
			
		||||
        //internal KeyList()
 | 
			
		||||
        //{
 | 
			
		||||
        //    m_Session = Session;
 | 
			
		||||
        //    m_Server = Server;
 | 
			
		||||
        //}
 | 
			
		||||
            foreach (var item in toRemove)
 | 
			
		||||
                m_Variables.Remove(item);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            m_Variables.Add(new KeyValuePair<string, string>(Key, value));
 | 
			
		||||
 | 
			
		||||
            OnModified?.Invoke(Key, value);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        //return m_keys.GetEnumerator();
 | 
			
		||||
        return m_Variables.GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return m_Variables.GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Clear()
 | 
			
		||||
    {
 | 
			
		||||
        if (OnModified != null)
 | 
			
		||||
            OnModified(null, null);
 | 
			
		||||
 | 
			
		||||
        m_Variables.Clear();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public string[] Keys
 | 
			
		||||
    {
 | 
			
		||||
        get 
 | 
			
		||||
        { 
 | 
			
		||||
            return m_keys.ToArray();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //public Dictionary<string, string>.ValueCollection Values
 | 
			
		||||
    public string[] Values
 | 
			
		||||
    {
 | 
			
		||||
        get 
 | 
			
		||||
        { 
 | 
			
		||||
            //return m_Variables.Values; 
 | 
			
		||||
            return m_values.ToArray();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public List<string> GetValues(string Key)
 | 
			
		||||
    {
 | 
			
		||||
        var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
        List<string> values = new List<string>();
 | 
			
		||||
 | 
			
		||||
        foreach (var kv in m_Variables)
 | 
			
		||||
            if (kv.Key.ToLower() == key)
 | 
			
		||||
                values.Add(kv.Value);
 | 
			
		||||
 | 
			
		||||
        return values;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RemoveAll(string Key)
 | 
			
		||||
    {
 | 
			
		||||
        while (Remove(Key)) { }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Remove(string Key)
 | 
			
		||||
    {
 | 
			
		||||
        var key = Key.ToLower();
 | 
			
		||||
 | 
			
		||||
        foreach (var kv in m_Variables)
 | 
			
		||||
        {
 | 
			
		||||
            if (kv.Key.ToLower() == key)
 | 
			
		||||
            {
 | 
			
		||||
                if (OnModified != null)
 | 
			
		||||
                    OnModified(Key, null);
 | 
			
		||||
                m_Variables.Remove(kv);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Count
 | 
			
		||||
    {
 | 
			
		||||
        get { return m_Variables.Count; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool ContainsKey(string Key)
 | 
			
		||||
    {
 | 
			
		||||
        var key = Key.ToLower();
 | 
			
		||||
        foreach (var kv in m_Variables)
 | 
			
		||||
            if (kv.Key.ToLower() == key)
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public bool ContainsKey(string Key)
 | 
			
		||||
    {
 | 
			
		||||
        //return m_Variables.ContainsKey(Key);
 | 
			
		||||
        return m_keys.Contains(Key.ToLower());
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    public bool ContainsValue(string Value)
 | 
			
		||||
    {
 | 
			
		||||
        var value = Value.ToLower();
 | 
			
		||||
        foreach (var kv in m_Variables)
 | 
			
		||||
            if (kv.Value.ToLower() == value)
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //internal KeyList()
 | 
			
		||||
    //{
 | 
			
		||||
    //    m_Session = Session;
 | 
			
		||||
    //    m_Server = Server;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -34,148 +34,147 @@ using Esiur.Core;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Dynamic;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public class Structure : IEnumerable<KeyValuePair<string, object>>
 | 
			
		||||
{
 | 
			
		||||
    public class Structure : IEnumerable<KeyValuePair<string, object>>
 | 
			
		||||
 | 
			
		||||
    public struct StructureMetadata
 | 
			
		||||
    {
 | 
			
		||||
        public string[] Keys;
 | 
			
		||||
        public DataType[] Types;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
    private object syncRoot = new object();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool ContainsKey(string key)
 | 
			
		||||
    {
 | 
			
		||||
        return dic.ContainsKey(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        var rt = "";
 | 
			
		||||
        foreach (var kv in dic)
 | 
			
		||||
            rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
 | 
			
		||||
 | 
			
		||||
        return rt.TrimEnd('\r', '\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Structure(Structure source)
 | 
			
		||||
    {
 | 
			
		||||
        dic = source.dic;
 | 
			
		||||
    }
 | 
			
		||||
    public Structure()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public struct StructureMetadata
 | 
			
		||||
        {
 | 
			
		||||
            public string[] Keys;
 | 
			
		||||
            public DataType[] Types;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        private object syncRoot = new object();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool ContainsKey(string key)
 | 
			
		||||
        {
 | 
			
		||||
            return dic.ContainsKey(key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            var rt = "";
 | 
			
		||||
            foreach (var kv in dic)
 | 
			
		||||
                rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
 | 
			
		||||
 | 
			
		||||
            return rt.TrimEnd('\r', '\n');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Structure(Structure source)
 | 
			
		||||
        {
 | 
			
		||||
            dic = source.dic;
 | 
			
		||||
        }
 | 
			
		||||
        public Structure()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Structure FromStructure(Structure source, Type destinationType)
 | 
			
		||||
        {
 | 
			
		||||
            var rt = Activator.CreateInstance(destinationType) as Structure;
 | 
			
		||||
            rt.dic = source.dic;
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static T FromStructure<T>(Structure source) where T : Structure
 | 
			
		||||
        {
 | 
			
		||||
            var rt = Activator.CreateInstance<T>();
 | 
			
		||||
            rt.dic = source.dic;
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public static explicit operator Structure(ExpandoObject obj) => FromDynamic(obj);
 | 
			
		||||
 | 
			
		||||
        public static Structure FromDynamic(ExpandoObject obj)
 | 
			
		||||
        {
 | 
			
		||||
            var rt = new Structure();
 | 
			
		||||
            foreach (var kv in obj)
 | 
			
		||||
                rt[kv.Key] = kv.Value;
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Structure FromObject(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            var type = obj.GetType();
 | 
			
		||||
 | 
			
		||||
            if (obj is Structure)
 | 
			
		||||
                return obj as Structure;
 | 
			
		||||
            else //if (Codec.IsAnonymous(type))
 | 
			
		||||
            {
 | 
			
		||||
                var st = new Structure();
 | 
			
		||||
 | 
			
		||||
                var pi = type.GetTypeInfo().GetProperties().Where(x=>x.CanRead);
 | 
			
		||||
                foreach (var p in pi)
 | 
			
		||||
                    st[p.Name] = p.GetValue(obj);
 | 
			
		||||
 | 
			
		||||
                var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic);
 | 
			
		||||
                foreach (var f in fi)
 | 
			
		||||
                    st[f.Name] = f.GetValue(obj);
 | 
			
		||||
 | 
			
		||||
                return st;
 | 
			
		||||
            }
 | 
			
		||||
            //else
 | 
			
		||||
              //  return null;
 | 
			
		||||
        }
 | 
			
		||||
        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return dic.GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
        {
 | 
			
		||||
            return dic.GetEnumerator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Length
 | 
			
		||||
        {
 | 
			
		||||
            get { return dic.Count; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public KeyValuePair<string, object> At(int index)
 | 
			
		||||
        {
 | 
			
		||||
            return dic.ElementAt(index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object SyncRoot
 | 
			
		||||
        {
 | 
			
		||||
            get { return syncRoot; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string[] GetKeys() => dic.Keys.ToArray();//GetKeys()
 | 
			
		||||
        //{
 | 
			
		||||
          //  return dic.Keys.ToArray();
 | 
			
		||||
        //}
 | 
			
		||||
        
 | 
			
		||||
        public Structure Add(string key, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (dic.ContainsKey(key))
 | 
			
		||||
                dic[key] = value;
 | 
			
		||||
            else
 | 
			
		||||
                dic.Add(key, value);
 | 
			
		||||
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public object this[string index]
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (dic.ContainsKey(index))
 | 
			
		||||
                    return dic[index];
 | 
			
		||||
                else
 | 
			
		||||
                    return null;
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                if (dic.ContainsKey(index))
 | 
			
		||||
                    dic[index] = value;
 | 
			
		||||
                else
 | 
			
		||||
                    dic.Add(index, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Structure FromStructure(Structure source, Type destinationType)
 | 
			
		||||
    {
 | 
			
		||||
        var rt = Activator.CreateInstance(destinationType) as Structure;
 | 
			
		||||
        rt.dic = source.dic;
 | 
			
		||||
        return rt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static T FromStructure<T>(Structure source) where T : Structure
 | 
			
		||||
    {
 | 
			
		||||
        var rt = Activator.CreateInstance<T>();
 | 
			
		||||
        rt.dic = source.dic;
 | 
			
		||||
        return rt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static explicit operator Structure(ExpandoObject obj) => FromDynamic(obj);
 | 
			
		||||
 | 
			
		||||
    public static Structure FromDynamic(ExpandoObject obj)
 | 
			
		||||
    {
 | 
			
		||||
        var rt = new Structure();
 | 
			
		||||
        foreach (var kv in obj)
 | 
			
		||||
            rt[kv.Key] = kv.Value;
 | 
			
		||||
        return rt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Structure FromObject(object obj)
 | 
			
		||||
    {
 | 
			
		||||
        var type = obj.GetType();
 | 
			
		||||
 | 
			
		||||
        if (obj is Structure)
 | 
			
		||||
            return obj as Structure;
 | 
			
		||||
        else //if (Codec.IsAnonymous(type))
 | 
			
		||||
        {
 | 
			
		||||
            var st = new Structure();
 | 
			
		||||
 | 
			
		||||
            var pi = type.GetTypeInfo().GetProperties().Where(x => x.CanRead);
 | 
			
		||||
            foreach (var p in pi)
 | 
			
		||||
                st[p.Name] = p.GetValue(obj);
 | 
			
		||||
 | 
			
		||||
            var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic);
 | 
			
		||||
            foreach (var f in fi)
 | 
			
		||||
                st[f.Name] = f.GetValue(obj);
 | 
			
		||||
 | 
			
		||||
            return st;
 | 
			
		||||
        }
 | 
			
		||||
        //else
 | 
			
		||||
        //  return null;
 | 
			
		||||
    }
 | 
			
		||||
    public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return dic.GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        return dic.GetEnumerator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Length
 | 
			
		||||
    {
 | 
			
		||||
        get { return dic.Count; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public KeyValuePair<string, object> At(int index)
 | 
			
		||||
    {
 | 
			
		||||
        return dic.ElementAt(index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public object SyncRoot
 | 
			
		||||
    {
 | 
			
		||||
        get { return syncRoot; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string[] GetKeys() => dic.Keys.ToArray();//GetKeys()
 | 
			
		||||
                                                    //{
 | 
			
		||||
                                                    //  return dic.Keys.ToArray();
 | 
			
		||||
                                                    //}
 | 
			
		||||
 | 
			
		||||
    public Structure Add(string key, object value)
 | 
			
		||||
    {
 | 
			
		||||
        if (dic.ContainsKey(key))
 | 
			
		||||
            dic[key] = value;
 | 
			
		||||
        else
 | 
			
		||||
            dic.Add(key, value);
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public object this[string index]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            if (dic.ContainsKey(index))
 | 
			
		||||
                return dic[index];
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (dic.ContainsKey(index))
 | 
			
		||||
                dic[index] = value;
 | 
			
		||||
            else
 | 
			
		||||
                dic.Add(index, value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,13 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Data
 | 
			
		||||
namespace Esiur.Data;
 | 
			
		||||
 | 
			
		||||
public enum StructureComparisonResult : byte
 | 
			
		||||
{
 | 
			
		||||
    public enum StructureComparisonResult : byte
 | 
			
		||||
    {
 | 
			
		||||
        Null,
 | 
			
		||||
        Structure,
 | 
			
		||||
        StructureSameKeys,
 | 
			
		||||
        StructureSameTypes,
 | 
			
		||||
        Same
 | 
			
		||||
    }
 | 
			
		||||
    Null,
 | 
			
		||||
    Structure,
 | 
			
		||||
    StructureSameKeys,
 | 
			
		||||
    StructureSameTypes,
 | 
			
		||||
    Same
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,20 +4,20 @@
 | 
			
		||||
    <TargetFramework>netstandard2.0</TargetFramework>
 | 
			
		||||
    <Description>Distributed Resources Platform</Description>
 | 
			
		||||
    <Copyright>Ahmed Kh. Zamil</Copyright>
 | 
			
		||||
    <PackageLicenseUrl>https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE</PackageLicenseUrl>
 | 
			
		||||
    <PackageProjectUrl>http://www.esiur.com</PackageProjectUrl>
 | 
			
		||||
     <PackageProjectUrl>http://www.esiur.com</PackageProjectUrl>
 | 
			
		||||
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
			
		||||
    <Version>1.8.8</Version>
 | 
			
		||||
    <Version>2.0.0-alpha</Version>
 | 
			
		||||
    <RepositoryUrl>https://github.com/esiur/esiur-dotnet</RepositoryUrl>
 | 
			
		||||
    <Authors>Ahmed Kh. Zamil</Authors>
 | 
			
		||||
    <AssemblyVersion>1.8.8.0</AssemblyVersion>
 | 
			
		||||
    <AssemblyVersion>2.0.0.0</AssemblyVersion>
 | 
			
		||||
    <Company>Esiur Foundation</Company>
 | 
			
		||||
    <FileVersion>1.8.8.0</FileVersion>
 | 
			
		||||
    <FileVersion>2.0.0.0</FileVersion>
 | 
			
		||||
    <AssemblyName>Esiur</AssemblyName>
 | 
			
		||||
    <RootNamespace>Esiur</RootNamespace>
 | 
			
		||||
    <PackageId>Esiur</PackageId>
 | 
			
		||||
    <Product>Esiur</Product>
 | 
			
		||||
    <LangVersion>9.0</LangVersion>
 | 
			
		||||
    <LangVersion>latest</LangVersion>
 | 
			
		||||
    <PackageLicenseFile>LICENSE</PackageLicenseFile>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
 | 
			
		||||
@@ -79,10 +79,13 @@
 | 
			
		||||
    <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
 | 
			
		||||
    <!-- Package the Newtonsoft.Json dependency alongside the generator assembly -->
 | 
			
		||||
    <None Include="$(PkgSystem_Text_Json)\lib\netstandard2.0\*.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <None Include="Tools/*" Pack="true" PackagePath="tools/" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <None Include="LICENSE" Pack="true" PackagePath="">
 | 
			
		||||
    </None>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project> 
 | 
			
		||||
							
								
								
									
										21
									
								
								Esiur/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Esiur/LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2017-2021 Esiur Foundation, 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.
 | 
			
		||||
@@ -42,449 +42,448 @@ using System.Text.Json;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Misc
 | 
			
		||||
namespace Esiur.Misc;
 | 
			
		||||
public static class Global
 | 
			
		||||
{
 | 
			
		||||
    public static class Global
 | 
			
		||||
    private static KeyList<string, object> variables = new KeyList<string, object>();
 | 
			
		||||
    // private static Hashtable m_Cached = new Hashtable();
 | 
			
		||||
    //internal static bool SystemIsWorking = false;
 | 
			
		||||
 | 
			
		||||
    private static Random rand = new Random(System.Environment.TickCount);
 | 
			
		||||
 | 
			
		||||
    //public static Encoding DefaultEncoding =  Encoding.GetEncoding(1252);// .GetEncoding("windows-1252");
 | 
			
		||||
 | 
			
		||||
    public static KeyList<string, long> Counters = new KeyList<string, long>();
 | 
			
		||||
 | 
			
		||||
    public delegate void LogEvent(string service, LogType type, string message);
 | 
			
		||||
 | 
			
		||||
    public static event LogEvent SystemLog;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static string ToJson(this IResource resource)
 | 
			
		||||
    {
 | 
			
		||||
        private static KeyList<string, object> variables = new KeyList<string, object>();
 | 
			
		||||
        // private static Hashtable m_Cached = new Hashtable();
 | 
			
		||||
        //internal static bool SystemIsWorking = false;
 | 
			
		||||
 | 
			
		||||
        private static Random rand = new Random(System.Environment.TickCount);
 | 
			
		||||
 | 
			
		||||
        //public static Encoding DefaultEncoding =  Encoding.GetEncoding(1252);// .GetEncoding("windows-1252");
 | 
			
		||||
 | 
			
		||||
        public static KeyList<string, long> Counters = new KeyList<string, long>();
 | 
			
		||||
 | 
			
		||||
        public delegate void LogEvent(string service, LogType type, string message);
 | 
			
		||||
 | 
			
		||||
        public static event LogEvent SystemLog;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public static string ToJson(this IResource resource)
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                return JsonSerializer.Serialize(resource, Global.SerializeOptions);
 | 
			
		||||
            }catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine(ex.ToString());
 | 
			
		||||
                return "{}";
 | 
			
		||||
            }
 | 
			
		||||
            return JsonSerializer.Serialize(resource, Global.SerializeOptions);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static JsonSerializerOptions SerializeOptions = new JsonSerializerOptions
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            ReferenceHandler = ReferenceHandler.Preserve,
 | 
			
		||||
            WriteIndented = true,
 | 
			
		||||
            Converters =
 | 
			
		||||
            Console.WriteLine(ex.ToString());
 | 
			
		||||
            return "{}";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JsonSerializerOptions SerializeOptions = new JsonSerializerOptions
 | 
			
		||||
    {
 | 
			
		||||
        ReferenceHandler = ReferenceHandler.Preserve,
 | 
			
		||||
        WriteIndented = true,
 | 
			
		||||
        Converters =
 | 
			
		||||
            {
 | 
			
		||||
                new ResourceJsonConverter(),
 | 
			
		||||
                new DoubleJsonConverter()
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public static string Version { get; }= FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion;
 | 
			
		||||
    public static string Version { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion;
 | 
			
		||||
 | 
			
		||||
        //FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
 | 
			
		||||
        //      string version = fvi.FileVersion;
 | 
			
		||||
    //FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
 | 
			
		||||
    //      string version = fvi.FileVersion;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static char GetDirectorySeparator()
 | 
			
		||||
        {
 | 
			
		||||
            return System.IO.Path.DirectorySeparatorChar;
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public static void Log(Exception ex, params object[] arguments)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                 
 | 
			
		||||
                var stack = new StackTrace(ex, true);
 | 
			
		||||
                var frame = stack.GetFrames().First();
 | 
			
		||||
                var method = frame.GetMethod();
 | 
			
		||||
                var parameters = method.GetParameters();
 | 
			
		||||
                var service = method.DeclaringType.Name;
 | 
			
		||||
                var message = "";
 | 
			
		||||
 | 
			
		||||
                if (arguments.Length > 0 && parameters.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    message = "Arguments ( ";
 | 
			
		||||
 | 
			
		||||
                    for (int i = 0; i < parameters.Length && i < arguments.Length; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        message += parameters[i].Name + ": " + arguments[i].ToString() + " ";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    message += ")" + Environment.NewLine + "------------------------------------------------";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                                message += ex.ToString();
 | 
			
		||||
 | 
			
		||||
                Log(service, LogType.Error, message);
 | 
			
		||||
 | 
			
		||||
              
 | 
			
		||||
 | 
			
		||||
                Log(service, LogType.Error, ex.ToString());
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void Log(string service, LogType type, string message, bool appendHeader = true)
 | 
			
		||||
        {
 | 
			
		||||
            //if (type != LogType.Debug)
 | 
			
		||||
            Console.WriteLine(service + " " + message);
 | 
			
		||||
 | 
			
		||||
            SystemLog?.Invoke(service, type, message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static string GetTempPath()
 | 
			
		||||
        {
 | 
			
		||||
            return System.IO.Path.GetTempPath();
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public static string RemoveControlCharacters(string inString)
 | 
			
		||||
        {
 | 
			
		||||
            if (inString == null) return null;
 | 
			
		||||
 | 
			
		||||
            StringBuilder newString = new StringBuilder();
 | 
			
		||||
            char ch;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < inString.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                ch = inString[i];
 | 
			
		||||
 | 
			
		||||
                if (!char.IsControl(ch))
 | 
			
		||||
                {
 | 
			
		||||
                    newString.Append(ch);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return newString.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void PrintCounters()
 | 
			
		||||
        {
 | 
			
		||||
            string[] keys = new string[Counters.Keys.Count];
 | 
			
		||||
            Counters.Keys.CopyTo(keys, 0);
 | 
			
		||||
 | 
			
		||||
            foreach (string k in keys)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine(k + ":" + Counters[k]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
//        Encoding ANSI = Encoding.GetEncoding(1252);
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
        public static Hashtable Cached
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return m_Cached;
 | 
			
		||||
            }
 | 
			
		||||
        }*/
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static string ByteArrayToMAC(byte[] array)
 | 
			
		||||
        {
 | 
			
		||||
            string rt="";
 | 
			
		||||
 | 
			
		||||
            if (array == null)
 | 
			
		||||
                return "00:00:00:00:00:00";
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                //for (int i = 0; i < array.Length - 1; i++)
 | 
			
		||||
                //    rt += Convert.ToString(array[i], 16) + ":";
 | 
			
		||||
                
 | 
			
		||||
                //rt += Convert.ToString(array[array.Length - 1], 16);
 | 
			
		||||
 | 
			
		||||
                rt = BitConverter.ToString(array);
 | 
			
		||||
                rt = rt.Replace('-', ':');
 | 
			
		||||
                return rt;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static string IPAddressFromInt32(UInt32 IP)
 | 
			
		||||
        {
 | 
			
		||||
            //var dIP = DC.ToBytes(IP);
 | 
			
		||||
 | 
			
		||||
            return (IP >> 24) + "." + ((IP >> 16) & 0xFF) + "." + ((IP >> 8) & 0xFF) + "." + (IP & 0xFF);
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public static KeyList<string, object> Variables
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return variables;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public static uint CurrentUnixTime()
 | 
			
		||||
        {
 | 
			
		||||
            return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void SetConsoleColors(ConsoleColor ForegroundColor, ConsoleColor BackgroundColor)
 | 
			
		||||
        {
 | 
			
		||||
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
 | 
			
		||||
            {
 | 
			
		||||
                switch (ForegroundColor)
 | 
			
		||||
                {
 | 
			
		||||
                    case ConsoleColor.Black:
 | 
			
		||||
                        Console.Write("\u001B[30m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Blue:
 | 
			
		||||
                        Console.Write("\u001B[1;34m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Cyan:
 | 
			
		||||
                        Console.Write("\u001B[1;36m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Gray:
 | 
			
		||||
                    case ConsoleColor.DarkGray:
 | 
			
		||||
                        Console.Write("\u001B[1;30m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Green:
 | 
			
		||||
                        Console.Write("\u001B[1;32m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Magenta:
 | 
			
		||||
                        Console.Write("\u001B[1;35m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Red:
 | 
			
		||||
                        Console.Write("\u001B[1;31m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.White:
 | 
			
		||||
                        Console.Write("\u001B[1;37m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Yellow:
 | 
			
		||||
                        Console.Write("\u001B[1;33m");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case ConsoleColor.DarkBlue:
 | 
			
		||||
                        Console.Write("\u001B[34m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkCyan:
 | 
			
		||||
                        Console.Write("\u001B[36m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkGreen:
 | 
			
		||||
                        Console.Write("\u001B[32m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkMagenta:
 | 
			
		||||
                        Console.Write("\u001B[35m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkRed:
 | 
			
		||||
                        Console.Write("\u001B[31m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkYellow:
 | 
			
		||||
                        Console.Write("\u001B[33m");
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                switch (BackgroundColor)
 | 
			
		||||
                {
 | 
			
		||||
                    case ConsoleColor.Black:
 | 
			
		||||
                        Console.Write("\u001B[40m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Blue:
 | 
			
		||||
                        Console.Write("\u001B[1;44m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Cyan:
 | 
			
		||||
                        Console.Write("\u001B[1;46m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Gray:
 | 
			
		||||
                    case ConsoleColor.DarkGray:
 | 
			
		||||
                        Console.Write("\u001B[1;40m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Green:
 | 
			
		||||
                        Console.Write("\u001B[1;42m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Magenta:
 | 
			
		||||
                        Console.Write("\u001B[1;45m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Red:
 | 
			
		||||
                        Console.Write("\u001B[1;41m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.White:
 | 
			
		||||
                        Console.Write("\u001B[1;47m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.Yellow:
 | 
			
		||||
                        Console.Write("\u001B[1;43m");
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case ConsoleColor.DarkBlue:
 | 
			
		||||
                        Console.Write("\u001B[44m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkCyan:
 | 
			
		||||
                        Console.Write("\u001B[46m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkGreen:
 | 
			
		||||
                        Console.Write("\u001B[42m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkMagenta:
 | 
			
		||||
                        Console.Write("\u001B[45m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkRed:
 | 
			
		||||
                        Console.Write("\u001B[41m");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case ConsoleColor.DarkYellow:
 | 
			
		||||
                        Console.Write("\u001B[43m");
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                Console.ForegroundColor = ForegroundColor;
 | 
			
		||||
                Console.BackgroundColor = BackgroundColor;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetUserPart(string strAddress)
 | 
			
		||||
        {
 | 
			
		||||
            return strAddress.Substring(0, strAddress.IndexOf("@", 0));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static byte[][] GetBytesFromChunk(byte[] Data, int ChunkSize)
 | 
			
		||||
        {
 | 
			
		||||
            if (ChunkSize == 1)
 | 
			
		||||
            {
 | 
			
		||||
                byte[][] ar = new byte[0][];
 | 
			
		||||
                int ptr = 0;
 | 
			
		||||
                while (ptr < Data.Length)
 | 
			
		||||
                {
 | 
			
		||||
                    Array.Resize<byte[]>(ref ar, ar.Length + 1);
 | 
			
		||||
                    ar[ar.Length - 1] = new byte[Data[ptr]];
 | 
			
		||||
                    Buffer.BlockCopy(Data, ++ptr, ar[ar.Length - 1], 0, Data[ptr]);
 | 
			
		||||
                    ptr += Data[ptr] + 1;
 | 
			
		||||
                }
 | 
			
		||||
                return ar;
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
        
 | 
			
		||||
 
 | 
			
		||||
        public static string GetFileTitle(string Filename)
 | 
			
		||||
        {
 | 
			
		||||
            string[] s = Filename.Split(Path.DirectorySeparatorChar);
 | 
			
		||||
            return s[s.Length - 1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetNewFileName(string FileDir)
 | 
			
		||||
        {
 | 
			
		||||
            string tempGetNewFileName = null;
 | 
			
		||||
            short i = 0;
 | 
			
		||||
            string NewFile = null;
 | 
			
		||||
            NewFile = FileDir;
 | 
			
		||||
        Begin:
 | 
			
		||||
            FileInfo FF = new FileInfo(NewFile);
 | 
			
		||||
            if (FF.Exists)
 | 
			
		||||
            {
 | 
			
		||||
                //If FSO.FileExists(NewFile) Then
 | 
			
		||||
                i++; //= i + 1;
 | 
			
		||||
                NewFile = FileDir.Substring(0, FileDir.Length - 4) + "_" + i + "." + FileDir.Substring(FileDir.Length - 3);
 | 
			
		||||
                goto Begin;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                tempGetNewFileName = NewFile;
 | 
			
		||||
            }
 | 
			
		||||
            return tempGetNewFileName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /////////////////////////////////////
 | 
			
		||||
        public static string TrimEx(string strIn)
 | 
			
		||||
        {
 | 
			
		||||
            return strIn.Replace("\r", "").Replace("\n", "");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static bool IsUnix()
 | 
			
		||||
        {
 | 
			
		||||
            // Linux OSs under Mono 1.2 uses unknown integer numbers so this should identify all non windows as unix
 | 
			
		||||
            return (Environment.OSVersion.Platform != PlatformID.Win32NT
 | 
			
		||||
                && Environment.OSVersion.Platform != PlatformID.Win32Windows); // || Environment.OSVersion.Platform == PlatformID.Linux; 
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public static string GenerateCode()
 | 
			
		||||
        {
 | 
			
		||||
            return GenerateCode(16);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public static byte[] GenerateBytes(int length)
 | 
			
		||||
        {
 | 
			
		||||
            var b  = new byte[length];
 | 
			
		||||
            rand.NextBytes(b);
 | 
			
		||||
            return b;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GenerateCode(int length)
 | 
			
		||||
        {
 | 
			
		||||
            return GenerateCode(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");// ~!@#$%^&*()_-+=\\?/");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GenerateCode(int length, string chars)
 | 
			
		||||
        //public static string GenerateCode(int Length)
 | 
			
		||||
        {
 | 
			
		||||
            //var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_-+=\\?/";
 | 
			
		||||
            var result = new string(
 | 
			
		||||
                Enumerable.Repeat(chars, length)
 | 
			
		||||
                          .Select(s => s[rand.Next(s.Length)])
 | 
			
		||||
                          .ToArray());
 | 
			
		||||
            //if (result.Length < length)
 | 
			
		||||
              //  Console.WriteLine();
 | 
			
		||||
            return result;
 | 
			
		||||
            /*
 | 
			
		||||
            int len = 0;
 | 
			
		||||
            string code = "";
 | 
			
		||||
 | 
			
		||||
            while(len < Length)
 | 
			
		||||
            {
 | 
			
		||||
                var c = Convert.ToChar((byte)(rand.NextDouble() * 255));
 | 
			
		||||
                if (Char.IsLetterOrDigit(c))
 | 
			
		||||
                {
 | 
			
		||||
                    code += c;
 | 
			
		||||
                    len++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return code;
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string ReplaceOnce(string Expression, string Find, string Replacement)
 | 
			
		||||
        {
 | 
			
		||||
            int pos = Expression.IndexOf(Find);
 | 
			
		||||
            if (pos != -1)
 | 
			
		||||
                return Expression.Substring(0, pos) + Replacement + Expression.Substring(pos + Find.Length);
 | 
			
		||||
            else
 | 
			
		||||
                return Expression;
 | 
			
		||||
        }
 | 
			
		||||
        //public void Replace(string Expression, string Find, string Replacement, int Start, int Count)
 | 
			
		||||
        //{
 | 
			
		||||
        //    Expression.IndexOf(
 | 
			
		||||
        //}
 | 
			
		||||
    /*
 | 
			
		||||
    public static char GetDirectorySeparator()
 | 
			
		||||
    {
 | 
			
		||||
        return System.IO.Path.DirectorySeparatorChar;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public static void Log(Exception ex, params object[] arguments)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var stack = new StackTrace(ex, true);
 | 
			
		||||
            var frame = stack.GetFrames().First();
 | 
			
		||||
            var method = frame.GetMethod();
 | 
			
		||||
            var parameters = method.GetParameters();
 | 
			
		||||
            var service = method.DeclaringType.Name;
 | 
			
		||||
            var message = "";
 | 
			
		||||
 | 
			
		||||
            if (arguments.Length > 0 && parameters.Length > 0)
 | 
			
		||||
            {
 | 
			
		||||
                message = "Arguments ( ";
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < parameters.Length && i < arguments.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    message += parameters[i].Name + ": " + arguments[i].ToString() + " ";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                message += ")" + Environment.NewLine + "------------------------------------------------";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            message += ex.ToString();
 | 
			
		||||
 | 
			
		||||
            Log(service, LogType.Error, message);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Log(service, LogType.Error, ex.ToString());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void Log(string service, LogType type, string message, bool appendHeader = true)
 | 
			
		||||
    {
 | 
			
		||||
        //if (type != LogType.Debug)
 | 
			
		||||
        Console.WriteLine(service + " " + message);
 | 
			
		||||
 | 
			
		||||
        SystemLog?.Invoke(service, type, message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public static string GetTempPath()
 | 
			
		||||
    {
 | 
			
		||||
        return System.IO.Path.GetTempPath();
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public static string RemoveControlCharacters(string inString)
 | 
			
		||||
    {
 | 
			
		||||
        if (inString == null) return null;
 | 
			
		||||
 | 
			
		||||
        StringBuilder newString = new StringBuilder();
 | 
			
		||||
        char ch;
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < inString.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            ch = inString[i];
 | 
			
		||||
 | 
			
		||||
            if (!char.IsControl(ch))
 | 
			
		||||
            {
 | 
			
		||||
                newString.Append(ch);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return newString.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void PrintCounters()
 | 
			
		||||
    {
 | 
			
		||||
        string[] keys = new string[Counters.Keys.Count];
 | 
			
		||||
        Counters.Keys.CopyTo(keys, 0);
 | 
			
		||||
 | 
			
		||||
        foreach (string k in keys)
 | 
			
		||||
        {
 | 
			
		||||
            Console.WriteLine(k + ":" + Counters[k]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    //        Encoding ANSI = Encoding.GetEncoding(1252);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
public static Hashtable Cached
 | 
			
		||||
{
 | 
			
		||||
    get
 | 
			
		||||
    {
 | 
			
		||||
        return m_Cached;
 | 
			
		||||
    }
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public static string ByteArrayToMAC(byte[] array)
 | 
			
		||||
    {
 | 
			
		||||
        string rt="";
 | 
			
		||||
 | 
			
		||||
        if (array == null)
 | 
			
		||||
            return "00:00:00:00:00:00";
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            //for (int i = 0; i < array.Length - 1; i++)
 | 
			
		||||
            //    rt += Convert.ToString(array[i], 16) + ":";
 | 
			
		||||
 | 
			
		||||
            //rt += Convert.ToString(array[array.Length - 1], 16);
 | 
			
		||||
 | 
			
		||||
            rt = BitConverter.ToString(array);
 | 
			
		||||
            rt = rt.Replace('-', ':');
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public static string IPAddressFromInt32(UInt32 IP)
 | 
			
		||||
    {
 | 
			
		||||
        //var dIP = DC.ToBytes(IP);
 | 
			
		||||
 | 
			
		||||
        return (IP >> 24) + "." + ((IP >> 16) & 0xFF) + "." + ((IP >> 8) & 0xFF) + "." + (IP & 0xFF);
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public static KeyList<string, object> Variables
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return variables;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static uint CurrentUnixTime()
 | 
			
		||||
    {
 | 
			
		||||
        return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void SetConsoleColors(ConsoleColor ForegroundColor, ConsoleColor BackgroundColor)
 | 
			
		||||
    {
 | 
			
		||||
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
 | 
			
		||||
        {
 | 
			
		||||
            switch (ForegroundColor)
 | 
			
		||||
            {
 | 
			
		||||
                case ConsoleColor.Black:
 | 
			
		||||
                    Console.Write("\u001B[30m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Blue:
 | 
			
		||||
                    Console.Write("\u001B[1;34m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Cyan:
 | 
			
		||||
                    Console.Write("\u001B[1;36m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Gray:
 | 
			
		||||
                case ConsoleColor.DarkGray:
 | 
			
		||||
                    Console.Write("\u001B[1;30m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Green:
 | 
			
		||||
                    Console.Write("\u001B[1;32m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Magenta:
 | 
			
		||||
                    Console.Write("\u001B[1;35m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Red:
 | 
			
		||||
                    Console.Write("\u001B[1;31m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.White:
 | 
			
		||||
                    Console.Write("\u001B[1;37m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Yellow:
 | 
			
		||||
                    Console.Write("\u001B[1;33m");
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case ConsoleColor.DarkBlue:
 | 
			
		||||
                    Console.Write("\u001B[34m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkCyan:
 | 
			
		||||
                    Console.Write("\u001B[36m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkGreen:
 | 
			
		||||
                    Console.Write("\u001B[32m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkMagenta:
 | 
			
		||||
                    Console.Write("\u001B[35m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkRed:
 | 
			
		||||
                    Console.Write("\u001B[31m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkYellow:
 | 
			
		||||
                    Console.Write("\u001B[33m");
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            switch (BackgroundColor)
 | 
			
		||||
            {
 | 
			
		||||
                case ConsoleColor.Black:
 | 
			
		||||
                    Console.Write("\u001B[40m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Blue:
 | 
			
		||||
                    Console.Write("\u001B[1;44m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Cyan:
 | 
			
		||||
                    Console.Write("\u001B[1;46m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Gray:
 | 
			
		||||
                case ConsoleColor.DarkGray:
 | 
			
		||||
                    Console.Write("\u001B[1;40m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Green:
 | 
			
		||||
                    Console.Write("\u001B[1;42m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Magenta:
 | 
			
		||||
                    Console.Write("\u001B[1;45m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Red:
 | 
			
		||||
                    Console.Write("\u001B[1;41m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.White:
 | 
			
		||||
                    Console.Write("\u001B[1;47m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.Yellow:
 | 
			
		||||
                    Console.Write("\u001B[1;43m");
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case ConsoleColor.DarkBlue:
 | 
			
		||||
                    Console.Write("\u001B[44m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkCyan:
 | 
			
		||||
                    Console.Write("\u001B[46m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkGreen:
 | 
			
		||||
                    Console.Write("\u001B[42m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkMagenta:
 | 
			
		||||
                    Console.Write("\u001B[45m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkRed:
 | 
			
		||||
                    Console.Write("\u001B[41m");
 | 
			
		||||
                    break;
 | 
			
		||||
                case ConsoleColor.DarkYellow:
 | 
			
		||||
                    Console.Write("\u001B[43m");
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            Console.ForegroundColor = ForegroundColor;
 | 
			
		||||
            Console.BackgroundColor = BackgroundColor;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string GetUserPart(string strAddress)
 | 
			
		||||
    {
 | 
			
		||||
        return strAddress.Substring(0, strAddress.IndexOf("@", 0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static byte[][] GetBytesFromChunk(byte[] Data, int ChunkSize)
 | 
			
		||||
    {
 | 
			
		||||
        if (ChunkSize == 1)
 | 
			
		||||
        {
 | 
			
		||||
            byte[][] ar = new byte[0][];
 | 
			
		||||
            int ptr = 0;
 | 
			
		||||
            while (ptr < Data.Length)
 | 
			
		||||
            {
 | 
			
		||||
                Array.Resize<byte[]>(ref ar, ar.Length + 1);
 | 
			
		||||
                ar[ar.Length - 1] = new byte[Data[ptr]];
 | 
			
		||||
                Buffer.BlockCopy(Data, ++ptr, ar[ar.Length - 1], 0, Data[ptr]);
 | 
			
		||||
                ptr += Data[ptr] + 1;
 | 
			
		||||
            }
 | 
			
		||||
            return ar;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static string GetFileTitle(string Filename)
 | 
			
		||||
    {
 | 
			
		||||
        string[] s = Filename.Split(Path.DirectorySeparatorChar);
 | 
			
		||||
        return s[s.Length - 1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string GetNewFileName(string FileDir)
 | 
			
		||||
    {
 | 
			
		||||
        string tempGetNewFileName = null;
 | 
			
		||||
        short i = 0;
 | 
			
		||||
        string NewFile = null;
 | 
			
		||||
        NewFile = FileDir;
 | 
			
		||||
    Begin:
 | 
			
		||||
        FileInfo FF = new FileInfo(NewFile);
 | 
			
		||||
        if (FF.Exists)
 | 
			
		||||
        {
 | 
			
		||||
            //If FSO.FileExists(NewFile) Then
 | 
			
		||||
            i++; //= i + 1;
 | 
			
		||||
            NewFile = FileDir.Substring(0, FileDir.Length - 4) + "_" + i + "." + FileDir.Substring(FileDir.Length - 3);
 | 
			
		||||
            goto Begin;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            tempGetNewFileName = NewFile;
 | 
			
		||||
        }
 | 
			
		||||
        return tempGetNewFileName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /////////////////////////////////////
 | 
			
		||||
    public static string TrimEx(string strIn)
 | 
			
		||||
    {
 | 
			
		||||
        return strIn.Replace("\r", "").Replace("\n", "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public static bool IsUnix()
 | 
			
		||||
    {
 | 
			
		||||
        // Linux OSs under Mono 1.2 uses unknown integer numbers so this should identify all non windows as unix
 | 
			
		||||
        return (Environment.OSVersion.Platform != PlatformID.Win32NT
 | 
			
		||||
            && Environment.OSVersion.Platform != PlatformID.Win32Windows); // || Environment.OSVersion.Platform == PlatformID.Linux; 
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public static string GenerateCode()
 | 
			
		||||
    {
 | 
			
		||||
        return GenerateCode(16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static byte[] GenerateBytes(int length)
 | 
			
		||||
    {
 | 
			
		||||
        var b = new byte[length];
 | 
			
		||||
        rand.NextBytes(b);
 | 
			
		||||
        return b;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string GenerateCode(int length)
 | 
			
		||||
    {
 | 
			
		||||
        return GenerateCode(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");// ~!@#$%^&*()_-+=\\?/");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string GenerateCode(int length, string chars)
 | 
			
		||||
    //public static string GenerateCode(int Length)
 | 
			
		||||
    {
 | 
			
		||||
        //var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_-+=\\?/";
 | 
			
		||||
        var result = new string(
 | 
			
		||||
            Enumerable.Repeat(chars, length)
 | 
			
		||||
                      .Select(s => s[rand.Next(s.Length)])
 | 
			
		||||
                      .ToArray());
 | 
			
		||||
        //if (result.Length < length)
 | 
			
		||||
        //  Console.WriteLine();
 | 
			
		||||
        return result;
 | 
			
		||||
        /*
 | 
			
		||||
        int len = 0;
 | 
			
		||||
        string code = "";
 | 
			
		||||
 | 
			
		||||
        while(len < Length)
 | 
			
		||||
        {
 | 
			
		||||
            var c = Convert.ToChar((byte)(rand.NextDouble() * 255));
 | 
			
		||||
            if (Char.IsLetterOrDigit(c))
 | 
			
		||||
            {
 | 
			
		||||
                code += c;
 | 
			
		||||
                len++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return code;
 | 
			
		||||
        */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string ReplaceOnce(string Expression, string Find, string Replacement)
 | 
			
		||||
    {
 | 
			
		||||
        int pos = Expression.IndexOf(Find);
 | 
			
		||||
        if (pos != -1)
 | 
			
		||||
            return Expression.Substring(0, pos) + Replacement + Expression.Substring(pos + Find.Length);
 | 
			
		||||
        else
 | 
			
		||||
            return Expression;
 | 
			
		||||
    }
 | 
			
		||||
    //public void Replace(string Expression, string Find, string Replacement, int Start, int Count)
 | 
			
		||||
    //{
 | 
			
		||||
    //    Expression.IndexOf(
 | 
			
		||||
    //}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,26 +31,24 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Net.Packets;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.DataLink
 | 
			
		||||
namespace Esiur.Net.DataLink;
 | 
			
		||||
public abstract class PacketFilter : IResource
 | 
			
		||||
{
 | 
			
		||||
    public abstract class PacketFilter : IResource
 | 
			
		||||
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
    public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
    public abstract bool Execute(Packet packet);
 | 
			
		||||
 | 
			
		||||
        public abstract bool Execute(Packet packet);
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,91 +32,89 @@ using System.Runtime.InteropServices;
 | 
			
		||||
using Esiur.Net.Packets;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.DataLink
 | 
			
		||||
namespace Esiur.Net.DataLink;
 | 
			
		||||
public class PacketServer : IResource
 | 
			
		||||
{
 | 
			
		||||
    public class PacketServer:IResource
 | 
			
		||||
    List<PacketSource> sources = new List<PacketSource>();
 | 
			
		||||
    List<PacketFilter> filters = new List<PacketFilter>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    [Storable]
 | 
			
		||||
    public string Mode
 | 
			
		||||
    {
 | 
			
		||||
        List<PacketSource> sources = new List<PacketSource>();
 | 
			
		||||
        List<PacketFilter> filters = new List<PacketFilter>();
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        [Storable]
 | 
			
		||||
        public string Mode
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<PacketSource> Sources
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            return sources;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public List<PacketSource> Sources
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            /*
 | 
			
		||||
            foreach (var resource in Instance.Children<IResource>())
 | 
			
		||||
            {
 | 
			
		||||
                return sources;
 | 
			
		||||
 | 
			
		||||
                if (resource is PacketFilter)
 | 
			
		||||
                {
 | 
			
		||||
                    filters.Add(resource as PacketFilter);
 | 
			
		||||
                }
 | 
			
		||||
                else if (resource is PacketSource)
 | 
			
		||||
                {
 | 
			
		||||
                    sources.Add(resource as PacketSource);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
            foreach (var src in sources)
 | 
			
		||||
            {
 | 
			
		||||
                src.OnNewPacket += PacketReceived;
 | 
			
		||||
                src.Open();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
        {
 | 
			
		||||
            //            foreach (var src in sources)
 | 
			
		||||
            //              src.Close();
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var src in sources)
 | 
			
		||||
            {
 | 
			
		||||
                src.Close();
 | 
			
		||||
                src.Open();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
    void PacketReceived(Packet Packet)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var f in filters)
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            if (f.Execute(Packet))
 | 
			
		||||
            {
 | 
			
		||||
                /*
 | 
			
		||||
                foreach (var resource in Instance.Children<IResource>())
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    if (resource is PacketFilter)
 | 
			
		||||
                    {
 | 
			
		||||
                        filters.Add(resource as PacketFilter);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (resource is PacketSource)
 | 
			
		||||
                    {
 | 
			
		||||
                        sources.Add(resource as PacketSource);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                */
 | 
			
		||||
                foreach (var src in sources)
 | 
			
		||||
                {
 | 
			
		||||
                    src.OnNewPacket += PacketReceived;
 | 
			
		||||
                    src.Open();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
            {
 | 
			
		||||
    //            foreach (var src in sources)
 | 
			
		||||
      //              src.Close();
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var src in sources)
 | 
			
		||||
                {
 | 
			
		||||
                    src.Close();
 | 
			
		||||
                    src.Open();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>( true);
 | 
			
		||||
        }        
 | 
			
		||||
 | 
			
		||||
        void PacketReceived(Packet Packet)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var f in filters)
 | 
			
		||||
            {                
 | 
			
		||||
                if (f.Execute(Packet))
 | 
			
		||||
                {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,66 +30,64 @@ using System.Text;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.DataLink
 | 
			
		||||
namespace Esiur.Net.DataLink;
 | 
			
		||||
public abstract class PacketSource : IResource
 | 
			
		||||
{
 | 
			
		||||
    public abstract class PacketSource: IResource
 | 
			
		||||
    public delegate void NewPacket(Packet Packet);
 | 
			
		||||
    public abstract event NewPacket OnNewPacket;
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        public delegate void NewPacket(Packet Packet);
 | 
			
		||||
        public abstract event NewPacket OnNewPacket;
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
 | 
			
		||||
    public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public abstract bool RawMode
 | 
			
		||||
    {
 | 
			
		||||
        set;
 | 
			
		||||
        get;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //public PacketSource(PacketServer Server, bool RawMode)
 | 
			
		||||
    //{
 | 
			
		||||
    //  this.RawMode = RawMode;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public abstract bool Open();
 | 
			
		||||
 | 
			
		||||
    public abstract bool Close();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public abstract bool Write(Packet packet);
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public virtual string TypeName
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public abstract bool RawMode
 | 
			
		||||
        {
 | 
			
		||||
            set;
 | 
			
		||||
            get;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //public PacketSource(PacketServer Server, bool RawMode)
 | 
			
		||||
        //{
 | 
			
		||||
        //  this.RawMode = RawMode;
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public abstract bool Open();
 | 
			
		||||
 | 
			
		||||
        public abstract bool Close();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public abstract bool Write(Packet packet);
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public virtual string TypeName
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return "Raw";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public abstract byte[] Address
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public abstract string DeviceId
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            return "Raw";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public abstract byte[] Address
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract string DeviceId
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,400 +38,399 @@ using Esiur.Misc;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.HTTP
 | 
			
		||||
namespace Esiur.Net.HTTP;
 | 
			
		||||
public class HTTPConnection : NetworkConnection
 | 
			
		||||
{
 | 
			
		||||
    public class HTTPConnection : NetworkConnection
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool WSMode { get; internal set; }
 | 
			
		||||
    public HTTPServer Server { get; internal set; }
 | 
			
		||||
 | 
			
		||||
    public WebsocketPacket WSRequest { get; set; }
 | 
			
		||||
    public HTTPRequestPacket Request { get; set; }
 | 
			
		||||
    public HTTPResponsePacket Response { get; } = new HTTPResponsePacket();
 | 
			
		||||
 | 
			
		||||
    HTTPSession session;
 | 
			
		||||
 | 
			
		||||
    public KeyList<string, object> Variables { get; } = new KeyList<string, object>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    internal long Parse(byte[] data)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        public bool WSMode { get; internal set; }
 | 
			
		||||
        public HTTPServer Server { get; internal set; }
 | 
			
		||||
 | 
			
		||||
        public WebsocketPacket WSRequest { get; set; }
 | 
			
		||||
        public HTTPRequestPacket Request { get; set; }
 | 
			
		||||
        public HTTPResponsePacket Response { get; } = new HTTPResponsePacket();
 | 
			
		||||
 | 
			
		||||
        HTTPSession session;
 | 
			
		||||
 | 
			
		||||
        public KeyList<string, object> Variables { get; } = new KeyList<string, object>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        internal long Parse(byte[] data)
 | 
			
		||||
        if (WSMode)
 | 
			
		||||
        {
 | 
			
		||||
            if (WSMode)
 | 
			
		||||
            // now parse WS protocol
 | 
			
		||||
            WebsocketPacket ws = new WebsocketPacket();
 | 
			
		||||
 | 
			
		||||
            var pSize = ws.Parse(data, 0, (uint)data.Length);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (pSize > 0)
 | 
			
		||||
            {
 | 
			
		||||
                // now parse WS protocol
 | 
			
		||||
                WebsocketPacket ws = new WebsocketPacket();
 | 
			
		||||
 | 
			
		||||
                var pSize = ws.Parse(data, 0, (uint)data.Length);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (pSize > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    WSRequest = ws;
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return pSize;
 | 
			
		||||
                }
 | 
			
		||||
                WSRequest = ws;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var rp = new HTTPRequestPacket();
 | 
			
		||||
                var pSize = rp.Parse(data, 0, (uint)data.Length);
 | 
			
		||||
                if (pSize > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    Request = rp;
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return pSize;
 | 
			
		||||
                }
 | 
			
		||||
                return pSize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Flush()
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // close the connection
 | 
			
		||||
            if (Request.Headers["connection"].ToLower() != "keep-alive" & IsConnected)
 | 
			
		||||
                Close();
 | 
			
		||||
            var rp = new HTTPRequestPacket();
 | 
			
		||||
            var pSize = rp.Parse(data, 0, (uint)data.Length);
 | 
			
		||||
            if (pSize > 0)
 | 
			
		||||
            {
 | 
			
		||||
                Request = rp;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return pSize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Flush()
 | 
			
		||||
    {
 | 
			
		||||
        // close the connection
 | 
			
		||||
        if (Request.Headers["connection"].ToLower() != "keep-alive" & IsConnected)
 | 
			
		||||
            Close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Upgrade()
 | 
			
		||||
    {
 | 
			
		||||
        if (IsWebsocketRequest())
 | 
			
		||||
        {
 | 
			
		||||
            string magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 | 
			
		||||
            string ret = Request.Headers["Sec-WebSocket-Key"] + magicString;
 | 
			
		||||
            // Compute the SHA1 hash
 | 
			
		||||
            SHA1 sha = SHA1.Create();
 | 
			
		||||
            byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
 | 
			
		||||
            Response.Headers["Upgrade"] = Request.Headers["Upgrade"];
 | 
			
		||||
            Response.Headers["Connection"] = Request.Headers["Connection"];// "Upgrade";
 | 
			
		||||
            Response.Headers["Sec-WebSocket-Accept"] = Convert.ToBase64String(sha1Hash);
 | 
			
		||||
 | 
			
		||||
            if (Request.Headers.ContainsKey("Sec-WebSocket-Protocol"))
 | 
			
		||||
                Response.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Response.Number = HTTPResponsePacket.ResponseCode.Switching;
 | 
			
		||||
            Response.Text = "Switching Protocols";
 | 
			
		||||
            WSMode = true;
 | 
			
		||||
 | 
			
		||||
            Send();
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Upgrade()
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public HTTPServer Parent
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            if (IsWebsocketRequest())
 | 
			
		||||
            return Server;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Send(WebsocketPacket packet)
 | 
			
		||||
    {
 | 
			
		||||
        if (packet.Data != null)
 | 
			
		||||
            base.Send(packet.Data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Send(string data)
 | 
			
		||||
    {
 | 
			
		||||
        Response.Message = Encoding.UTF8.GetBytes(data);
 | 
			
		||||
        Send();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Send(byte[] msg, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        Response.Message = DC.Clip(msg, (uint)offset, (uint)length);
 | 
			
		||||
        Send();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Send(byte[] message)
 | 
			
		||||
    {
 | 
			
		||||
        Response.Message = message;
 | 
			
		||||
        Send();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Send(HTTPResponsePacket.ComposeOptions Options = HTTPResponsePacket.ComposeOptions.AllCalculateLength)
 | 
			
		||||
    {
 | 
			
		||||
        if (Response.Handled)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Response.Compose(Options);
 | 
			
		||||
            base.Send(Response.Data);
 | 
			
		||||
 | 
			
		||||
            // Refresh the current session
 | 
			
		||||
            if (session != null)
 | 
			
		||||
                session.Refresh();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                string magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 | 
			
		||||
                string ret = Request.Headers["Sec-WebSocket-Key"] + magicString;
 | 
			
		||||
                // Compute the SHA1 hash
 | 
			
		||||
                SHA1 sha = SHA1.Create();
 | 
			
		||||
                byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
 | 
			
		||||
                Response.Headers["Upgrade"] = Request.Headers["Upgrade"];
 | 
			
		||||
                Response.Headers["Connection"] = Request.Headers["Connection"];// "Upgrade";
 | 
			
		||||
                Response.Headers["Sec-WebSocket-Accept"] = Convert.ToBase64String(sha1Hash);
 | 
			
		||||
                Close();
 | 
			
		||||
            }
 | 
			
		||||
            finally { }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
                if (Request.Headers.ContainsKey("Sec-WebSocket-Protocol"))
 | 
			
		||||
                    Response.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                Response.Number = HTTPResponsePacket.ResponseCode.Switching;
 | 
			
		||||
                Response.Text = "Switching Protocols";
 | 
			
		||||
                WSMode = true;
 | 
			
		||||
    public void CreateNewSession()
 | 
			
		||||
    {
 | 
			
		||||
        if (session == null)
 | 
			
		||||
        {
 | 
			
		||||
            // Create a new one
 | 
			
		||||
            session = Server.CreateSession(Global.GenerateCode(12), 60 * 20);
 | 
			
		||||
 | 
			
		||||
                Send();
 | 
			
		||||
            HTTPResponsePacket.HTTPCookie cookie = new HTTPResponsePacket.HTTPCookie("SID", session.Id);
 | 
			
		||||
            cookie.Expires = DateTime.MaxValue;
 | 
			
		||||
            cookie.Path = "/";
 | 
			
		||||
            cookie.HttpOnly = true;
 | 
			
		||||
 | 
			
		||||
            Response.Cookies.Add(cookie);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool IsWebsocketRequest()
 | 
			
		||||
    {
 | 
			
		||||
        if (Request.Headers.ContainsKey("connection")
 | 
			
		||||
            && Request.Headers["connection"].ToLower().Contains("upgrade")
 | 
			
		||||
            && Request.Headers.ContainsKey("upgrade")
 | 
			
		||||
            && Request.Headers["upgrade"].ToLower() == "websocket"
 | 
			
		||||
            && Request.Headers.ContainsKey("Sec-WebSocket-Version")
 | 
			
		||||
            && Request.Headers["Sec-WebSocket-Version"] == "13"
 | 
			
		||||
            && Request.Headers.ContainsKey("Sec-WebSocket-Key"))
 | 
			
		||||
        //&& Request.Headers.ContainsKey("Sec-WebSocket-Protocol"))
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void DataReceived(NetworkBuffer data)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        byte[] msg = data.Read();
 | 
			
		||||
 | 
			
		||||
        var BL = Parse(msg);
 | 
			
		||||
 | 
			
		||||
        if (BL == 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (Request.Method == HTTPRequestPacket.HTTPMethod.UNKNOWN)
 | 
			
		||||
            {
 | 
			
		||||
                Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (Request.URL == "")
 | 
			
		||||
            {
 | 
			
		||||
                Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (BL == -1)
 | 
			
		||||
        {
 | 
			
		||||
            data.HoldForNextWrite(msg);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        else if (BL < 0)
 | 
			
		||||
        {
 | 
			
		||||
            data.HoldFor(msg, (uint)(msg.Length - BL));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        else if (BL > 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (BL > Server.MaxPost)
 | 
			
		||||
            {
 | 
			
		||||
                Send(
 | 
			
		||||
                    "<html><body>POST method content is larger than "
 | 
			
		||||
                    + Server.MaxPost
 | 
			
		||||
                    + " bytes.</body></html>");
 | 
			
		||||
 | 
			
		||||
                Close();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                data.HoldFor(msg, (uint)(msg.Length + BL));
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        else if (BL < 0) // for security
 | 
			
		||||
        {
 | 
			
		||||
            Close();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (IsWebsocketRequest() & !WSMode)
 | 
			
		||||
        {
 | 
			
		||||
            Upgrade();
 | 
			
		||||
            //return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //return;
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (!Server.Execute(this))
 | 
			
		||||
            {
 | 
			
		||||
                Response.Number = HTTPResponsePacket.ResponseCode.InternalServerError;
 | 
			
		||||
                Send("Bad Request");
 | 
			
		||||
                Close();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            if (ex.Message != "Thread was being aborted.")
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                Global.Log("HTTPServer", LogType.Error, ex.ToString());
 | 
			
		||||
 | 
			
		||||
                //Console.WriteLine(ex.ToString());
 | 
			
		||||
                //EventLog.WriteEntry("HttpServer", ex.ToString(), EventLogEntryType.Error);
 | 
			
		||||
                Send(Error500(ex.Message));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private string Error500(string msg)
 | 
			
		||||
    {
 | 
			
		||||
        return "<html><head><title>500 Internal Server Error</title></head><br>\r\n"
 | 
			
		||||
                 + "<body><br>\r\n"
 | 
			
		||||
                 + "<b>500</b> Internal Server Error<br>" + msg + "\r\n"
 | 
			
		||||
                 + "</body><br>\r\n"
 | 
			
		||||
                 + "</html><br>\r\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async AsyncReply<bool> SendFile(string filename)
 | 
			
		||||
    {
 | 
			
		||||
        if (Response.Handled == true)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //HTTP/1.1 200 OK
 | 
			
		||||
            //Server: Microsoft-IIS/5.0
 | 
			
		||||
            //Content-Location: http://127.0.0.1/index.html
 | 
			
		||||
            //Date: Wed, 10 Dec 2003 19:10:25 GMT
 | 
			
		||||
            //Content-Type: text/html
 | 
			
		||||
            //Accept-Ranges: bytes
 | 
			
		||||
            //Last-Modified: Mon, 22 Sep 2003 22:36:56 GMT
 | 
			
		||||
            //Content-Length: 1957
 | 
			
		||||
 | 
			
		||||
            if (!File.Exists(filename))
 | 
			
		||||
            {
 | 
			
		||||
                Response.Number = HTTPResponsePacket.ResponseCode.NotFound;
 | 
			
		||||
                Send("File Not Found");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public HTTPServer Parent
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return Server;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Send(WebsocketPacket packet)
 | 
			
		||||
        {
 | 
			
		||||
            if (packet.Data != null)
 | 
			
		||||
                base.Send(packet.Data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Send(string data)
 | 
			
		||||
        {
 | 
			
		||||
            Response.Message = Encoding.UTF8.GetBytes(data);
 | 
			
		||||
            Send();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Send(byte[] msg, int offset, int length)
 | 
			
		||||
        {
 | 
			
		||||
            Response.Message = DC.Clip(msg, (uint)offset, (uint)length);
 | 
			
		||||
            Send();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Send(byte[] message)
 | 
			
		||||
        {
 | 
			
		||||
            Response.Message = message;
 | 
			
		||||
            Send();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Send(HTTPResponsePacket.ComposeOptions Options = HTTPResponsePacket.ComposeOptions.AllCalculateLength)
 | 
			
		||||
        {
 | 
			
		||||
            if (Response.Handled)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                Response.Compose(Options);
 | 
			
		||||
                base.Send(Response.Data);
 | 
			
		||||
 | 
			
		||||
                // Refresh the current session
 | 
			
		||||
                if (session != null)
 | 
			
		||||
                    session.Refresh();
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            var fileEditTime = File.GetLastWriteTime(filename).ToUniversalTime();
 | 
			
		||||
            if (Request.Headers.ContainsKey("if-modified-since"))
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Close();
 | 
			
		||||
                    var ims = DateTime.Parse(Request.Headers["if-modified-since"]);
 | 
			
		||||
                    if ((fileEditTime - ims).TotalSeconds < 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        Response.Number = HTTPResponsePacket.ResponseCode.NotModified;
 | 
			
		||||
                        Response.Headers.Clear();
 | 
			
		||||
                        //Response.Text = "Not Modified";
 | 
			
		||||
                        Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally { }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Response.Number = HTTPResponsePacket.ResponseCode.OK;
 | 
			
		||||
            // Fri, 30 Oct 2007 14:19:41 GMT
 | 
			
		||||
            Response.Headers["Last-Modified"] = fileEditTime.ToString("ddd, dd MMM yyyy HH:mm:ss");
 | 
			
		||||
            FileInfo fi = new FileInfo(filename);
 | 
			
		||||
            Response.Headers["Content-Length"] = fi.Length.ToString();
 | 
			
		||||
            Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly);
 | 
			
		||||
 | 
			
		||||
            //var fd = File.ReadAllBytes(filename);
 | 
			
		||||
 | 
			
		||||
            //base.Send(fd);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                var buffer = new byte[60000];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                while (true)
 | 
			
		||||
                {
 | 
			
		||||
                    var n = fs.Read(buffer, 0, 60000);
 | 
			
		||||
 | 
			
		||||
                    if (n <= 0)
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    //Thread.Sleep(50);
 | 
			
		||||
                    await base.SendAsync(buffer, 0, n);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                Close();
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void CreateNewSession()
 | 
			
		||||
        {
 | 
			
		||||
            if (session == null)
 | 
			
		||||
            {
 | 
			
		||||
                // Create a new one
 | 
			
		||||
                session = Server.CreateSession(Global.GenerateCode(12), 60 * 20);
 | 
			
		||||
 | 
			
		||||
                HTTPResponsePacket.HTTPCookie cookie = new HTTPResponsePacket.HTTPCookie("SID", session.Id);
 | 
			
		||||
                cookie.Expires = DateTime.MaxValue;
 | 
			
		||||
                cookie.Path = "/";
 | 
			
		||||
                cookie.HttpOnly = true;
 | 
			
		||||
 | 
			
		||||
                Response.Cookies.Add(cookie);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool IsWebsocketRequest()
 | 
			
		||||
        {
 | 
			
		||||
            if (Request.Headers.ContainsKey("connection")
 | 
			
		||||
                && Request.Headers["connection"].ToLower().Contains("upgrade")
 | 
			
		||||
                && Request.Headers.ContainsKey("upgrade")
 | 
			
		||||
                && Request.Headers["upgrade"].ToLower() == "websocket"
 | 
			
		||||
                && Request.Headers.ContainsKey("Sec-WebSocket-Version")
 | 
			
		||||
                && Request.Headers["Sec-WebSocket-Version"] == "13"
 | 
			
		||||
                && Request.Headers.ContainsKey("Sec-WebSocket-Key"))
 | 
			
		||||
            //&& Request.Headers.ContainsKey("Sec-WebSocket-Protocol"))
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void DataReceived(NetworkBuffer data)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            byte[] msg = data.Read();
 | 
			
		||||
 | 
			
		||||
            var BL = Parse(msg);
 | 
			
		||||
 | 
			
		||||
            if (BL == 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (Request.Method == HTTPRequestPacket.HTTPMethod.UNKNOWN)
 | 
			
		||||
                {
 | 
			
		||||
                    Close();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (Request.URL == "")
 | 
			
		||||
                {
 | 
			
		||||
                    Close();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (BL == -1)
 | 
			
		||||
            {
 | 
			
		||||
                data.HoldForNextWrite(msg);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else if (BL < 0)
 | 
			
		||||
            {
 | 
			
		||||
                data.HoldFor(msg, (uint)(msg.Length - BL));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else if (BL > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (BL > Server.MaxPost)
 | 
			
		||||
                {
 | 
			
		||||
                    Send(
 | 
			
		||||
                        "<html><body>POST method content is larger than "
 | 
			
		||||
                        + Server.MaxPost
 | 
			
		||||
                        + " bytes.</body></html>");
 | 
			
		||||
 | 
			
		||||
                    Close();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    data.HoldFor(msg, (uint)(msg.Length + BL));
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else if (BL < 0) // for security
 | 
			
		||||
            {
 | 
			
		||||
                Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (IsWebsocketRequest() & !WSMode)
 | 
			
		||||
            {
 | 
			
		||||
                Upgrade();
 | 
			
		||||
                //return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //return;
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (!Server.Execute(this))
 | 
			
		||||
                {
 | 
			
		||||
                    Response.Number = HTTPResponsePacket.ResponseCode.InternalServerError;
 | 
			
		||||
                    Send("Bad Request");
 | 
			
		||||
                    Close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                if (ex.Message != "Thread was being aborted.")
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    Global.Log("HTTPServer", LogType.Error, ex.ToString());
 | 
			
		||||
 | 
			
		||||
                    //Console.WriteLine(ex.ToString());
 | 
			
		||||
                    //EventLog.WriteEntry("HttpServer", ex.ToString(), EventLogEntryType.Error);
 | 
			
		||||
                    Send(Error500(ex.Message));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string Error500(string msg)
 | 
			
		||||
        {
 | 
			
		||||
            return "<html><head><title>500 Internal Server Error</title></head><br>\r\n"
 | 
			
		||||
                     + "<body><br>\r\n"
 | 
			
		||||
                     + "<b>500</b> Internal Server Error<br>" + msg + "\r\n"
 | 
			
		||||
                     + "</body><br>\r\n"
 | 
			
		||||
                     + "</html><br>\r\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async AsyncReply<bool> SendFile(string filename)
 | 
			
		||||
        {
 | 
			
		||||
            if (Response.Handled == true)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                //HTTP/1.1 200 OK
 | 
			
		||||
                //Server: Microsoft-IIS/5.0
 | 
			
		||||
                //Content-Location: http://127.0.0.1/index.html
 | 
			
		||||
                //Date: Wed, 10 Dec 2003 19:10:25 GMT
 | 
			
		||||
                //Content-Type: text/html
 | 
			
		||||
                //Accept-Ranges: bytes
 | 
			
		||||
                //Last-Modified: Mon, 22 Sep 2003 22:36:56 GMT
 | 
			
		||||
                //Content-Length: 1957
 | 
			
		||||
 | 
			
		||||
                if (!File.Exists(filename))
 | 
			
		||||
                {
 | 
			
		||||
                    Response.Number = HTTPResponsePacket.ResponseCode.NotFound;
 | 
			
		||||
                    Send("File Not Found");
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                var fileEditTime = File.GetLastWriteTime(filename).ToUniversalTime();
 | 
			
		||||
                if (Request.Headers.ContainsKey("if-modified-since"))
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        var ims = DateTime.Parse(Request.Headers["if-modified-since"]);
 | 
			
		||||
                        if ((fileEditTime - ims).TotalSeconds < 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            Response.Number = HTTPResponsePacket.ResponseCode.NotModified;
 | 
			
		||||
                            Response.Headers.Clear();
 | 
			
		||||
                            //Response.Text = "Not Modified";
 | 
			
		||||
                            Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                Response.Number = HTTPResponsePacket.ResponseCode.OK;
 | 
			
		||||
                // Fri, 30 Oct 2007 14:19:41 GMT
 | 
			
		||||
                Response.Headers["Last-Modified"] = fileEditTime.ToString("ddd, dd MMM yyyy HH:mm:ss");
 | 
			
		||||
                FileInfo fi = new FileInfo(filename);
 | 
			
		||||
                Response.Headers["Content-Length"] = fi.Length.ToString();
 | 
			
		||||
                Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly);
 | 
			
		||||
 | 
			
		||||
                //var fd = File.ReadAllBytes(filename);
 | 
			
		||||
 | 
			
		||||
                //base.Send(fd);
 | 
			
		||||
 | 
			
		||||
                
 | 
			
		||||
                using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    var buffer = new byte[60000];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    while (true)
 | 
			
		||||
                    {
 | 
			
		||||
                        var n = fs.Read(buffer, 0, 60000);
 | 
			
		||||
 | 
			
		||||
                        if (n <= 0)
 | 
			
		||||
                            break;
 | 
			
		||||
 | 
			
		||||
                        //Thread.Sleep(50);
 | 
			
		||||
                        await base.SendAsync(buffer, 0, n);
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
               
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Close();
 | 
			
		||||
                }
 | 
			
		||||
                finally { 
 | 
			
		||||
                
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Connected()
 | 
			
		||||
        {
 | 
			
		||||
            // do nothing
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Disconencted()
 | 
			
		||||
        {
 | 
			
		||||
            // do nothing
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    protected override void Connected()
 | 
			
		||||
    {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void Disconencted()
 | 
			
		||||
    {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,48 +35,46 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.HTTP
 | 
			
		||||
namespace Esiur.Net.HTTP;
 | 
			
		||||
 | 
			
		||||
public abstract class HTTPFilter : IResource
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public abstract class HTTPFilter : IResource
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
       public Instance Instance
 | 
			
		||||
       {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
       }
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public virtual void SessionModified(HTTPSession session, string key, object oldValue, object newValue)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void SessionExpired(HTTPSession session)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<bool> Execute(HTTPConnection sender);
 | 
			
		||||
 | 
			
		||||
        public virtual void ClientConnected(HTTPConnection HTTP)
 | 
			
		||||
        {
 | 
			
		||||
            //return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void ClientDisconnected(HTTPConnection HTTP)
 | 
			
		||||
        {
 | 
			
		||||
            //return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public virtual void SessionModified(HTTPSession session, string key, object oldValue, object newValue)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void SessionExpired(HTTPSession session)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public abstract AsyncReply<bool> Execute(HTTPConnection sender);
 | 
			
		||||
 | 
			
		||||
    public virtual void ClientConnected(HTTPConnection HTTP)
 | 
			
		||||
    {
 | 
			
		||||
        //return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void ClientDisconnected(HTTPConnection HTTP)
 | 
			
		||||
    {
 | 
			
		||||
        //return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,262 +39,260 @@ using Esiur.Net.Packets;
 | 
			
		||||
using System.Security.Cryptography.X509Certificates;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.HTTP
 | 
			
		||||
namespace Esiur.Net.HTTP;
 | 
			
		||||
public class HTTPServer : NetworkServer<HTTPConnection>, IResource
 | 
			
		||||
{
 | 
			
		||||
    public class HTTPServer : NetworkServer<HTTPConnection>, IResource
 | 
			
		||||
    Dictionary<string, HTTPSession> sessions = new Dictionary<string, HTTPSession>();
 | 
			
		||||
    HTTPFilter[] filters = new HTTPFilter[0];
 | 
			
		||||
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        Dictionary<string, HTTPSession> sessions= new Dictionary<string, HTTPSession>();
 | 
			
		||||
        HTTPFilter[] filters = new HTTPFilter[0];
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public virtual string IP
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public virtual ushort Port
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //[Attribute]
 | 
			
		||||
    //public virtual uint Timeout
 | 
			
		||||
    //{
 | 
			
		||||
    //    get;
 | 
			
		||||
    //    set;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //[Attribute]
 | 
			
		||||
    //public virtual uint Clock
 | 
			
		||||
    //{
 | 
			
		||||
    //    get;
 | 
			
		||||
    //    set;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public virtual uint MaxPost
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public virtual bool SSL
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public virtual string Certificate
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public HTTPSession CreateSession(string id, int timeout)
 | 
			
		||||
    {
 | 
			
		||||
        var s = new HTTPSession();
 | 
			
		||||
 | 
			
		||||
        s.Set(id, timeout);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        sessions.Add(id, s);
 | 
			
		||||
 | 
			
		||||
        return s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string MakeCookie(string Item, string Value, DateTime Expires, string Domain, string Path, bool HttpOnly)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/
 | 
			
		||||
        //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/
 | 
			
		||||
        string Cookie = Item + "=" + Value;
 | 
			
		||||
 | 
			
		||||
        if (Expires.Ticks != 0)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            Cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public virtual string IP
 | 
			
		||||
        if (Domain != null)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            Cookie += "; domain=" + Domain;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public virtual ushort Port
 | 
			
		||||
        if (Path != null)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            Cookie += "; path=" + Path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //[Attribute]
 | 
			
		||||
        //public virtual uint Timeout
 | 
			
		||||
        //{
 | 
			
		||||
        //    get;
 | 
			
		||||
        //    set;
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //[Attribute]
 | 
			
		||||
        //public virtual uint Clock
 | 
			
		||||
        //{
 | 
			
		||||
        //    get;
 | 
			
		||||
        //    set;
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public virtual uint MaxPost
 | 
			
		||||
        if (HttpOnly)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            Cookie += "; HttpOnly";
 | 
			
		||||
        }
 | 
			
		||||
        return Cookie;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public virtual bool SSL
 | 
			
		||||
    protected override void ClientDisconnected(HTTPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var filter in filters)
 | 
			
		||||
            filter.ClientDisconnected(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    internal bool Execute(HTTPConnection sender)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var resource in filters)
 | 
			
		||||
            if (resource.Execute(sender).Wait(30000))
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    protected override void SessionEnded(NetworkSession session)
 | 
			
		||||
    {
 | 
			
		||||
        // verify wether there are no active connections related to the session
 | 
			
		||||
 | 
			
		||||
        foreach (HTTPConnection c in Connections)//.Values)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public virtual string Certificate
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
         
 | 
			
		||||
   
 | 
			
		||||
       public HTTPSession CreateSession(string id, int timeout)
 | 
			
		||||
       {
 | 
			
		||||
            var s = new HTTPSession();
 | 
			
		||||
 | 
			
		||||
            s.Set(id, timeout);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            sessions.Add(id, s);
 | 
			
		||||
 | 
			
		||||
            return s;
 | 
			
		||||
       }
 | 
			
		||||
 | 
			
		||||
        public static string MakeCookie(string Item, string Value, DateTime Expires, string Domain, string Path, bool HttpOnly)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/
 | 
			
		||||
            //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/
 | 
			
		||||
            string Cookie = Item + "=" + Value;
 | 
			
		||||
 | 
			
		||||
            if (Expires.Ticks != 0)
 | 
			
		||||
            if (c.Session == session)
 | 
			
		||||
            {
 | 
			
		||||
                Cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT";
 | 
			
		||||
            }
 | 
			
		||||
            if (Domain != null)
 | 
			
		||||
            {
 | 
			
		||||
                Cookie += "; domain=" + Domain;
 | 
			
		||||
            }
 | 
			
		||||
            if (Path != null)
 | 
			
		||||
            {
 | 
			
		||||
                Cookie += "; path=" + Path;
 | 
			
		||||
            }
 | 
			
		||||
            if (HttpOnly)
 | 
			
		||||
            {
 | 
			
		||||
                Cookie += "; HttpOnly";
 | 
			
		||||
            }
 | 
			
		||||
            return Cookie;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ClientDisconnected(HTTPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var filter in filters)
 | 
			
		||||
                filter.ClientDisconnected(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        internal bool Execute(HTTPConnection sender)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var resource in filters)
 | 
			
		||||
                if (resource.Execute(sender).Wait(30000))
 | 
			
		||||
                    return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        protected override void SessionEnded(NetworkSession session)
 | 
			
		||||
        {
 | 
			
		||||
            // verify wether there are no active connections related to the session
 | 
			
		||||
 | 
			
		||||
            foreach (HTTPConnection c in Connections)//.Values)
 | 
			
		||||
            {
 | 
			
		||||
                if (c.Session == session)
 | 
			
		||||
                {
 | 
			
		||||
                    session.Refresh();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (Instance instance in Instance.Children)
 | 
			
		||||
            {
 | 
			
		||||
                var f = (HTTPFilter)instance.Resource;
 | 
			
		||||
                f.SessionExpired((HTTPSession)session);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            base.SessionEnded((HTTPSession)session);
 | 
			
		||||
            //Sessions.Remove(Session.ID);
 | 
			
		||||
            //Session.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public int TTL
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return Timeout;// mTimeout;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public async AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            {
 | 
			
		||||
                //var ip = (IPAddress)Instance.Attributes["ip"];
 | 
			
		||||
                //var port = (int)Instance.Attributes["port"];
 | 
			
		||||
                //var ssl = (bool)Instance.Attributes["ssl"];
 | 
			
		||||
                //var cert = (string)Instance.Attributes["certificate"];
 | 
			
		||||
 | 
			
		||||
                //if (ip == null) ip = IPAddress.Any;
 | 
			
		||||
 | 
			
		||||
                Sockets.ISocket listener;
 | 
			
		||||
                IPAddress ipAdd;
 | 
			
		||||
 | 
			
		||||
                if (IP == null)
 | 
			
		||||
                    ipAdd = IPAddress.Any;
 | 
			
		||||
                else
 | 
			
		||||
                    ipAdd = IPAddress.Parse(IP);
 | 
			
		||||
 | 
			
		||||
                if (SSL)
 | 
			
		||||
                   listener = new SSLSocket(new IPEndPoint(ipAdd, Port), new X509Certificate2(Certificate));
 | 
			
		||||
                else
 | 
			
		||||
                    listener = new TCPSocket(new IPEndPoint(ipAdd, Port));
 | 
			
		||||
 | 
			
		||||
                Start(listener);
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
            {
 | 
			
		||||
                Stop();
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
            {
 | 
			
		||||
                await Trigger(ResourceTrigger.Terminate);
 | 
			
		||||
                await Trigger(ResourceTrigger.Initialize);
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemInitialized)
 | 
			
		||||
            {
 | 
			
		||||
                filters = await Instance.Children<HTTPFilter>();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public override void Add(HTTPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Server = this;
 | 
			
		||||
            base.Add(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Remove(HTTPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Server = null;
 | 
			
		||||
            base.Remove(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ClientConnected(HTTPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            if (filters.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                connection.Close();
 | 
			
		||||
                session.Refresh();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var resource in filters)
 | 
			
		||||
            {
 | 
			
		||||
                resource.ClientConnected(connection);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
		public int LocalPort
 | 
			
		||||
		{
 | 
			
		||||
			get 
 | 
			
		||||
			{
 | 
			
		||||
				return cServer.LocalPort;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /* 
 | 
			
		||||
        public HTTPServer(int Port)
 | 
			
		||||
        foreach (Instance instance in Instance.Children)
 | 
			
		||||
        {
 | 
			
		||||
            cServer = new TServer();
 | 
			
		||||
            cServer.LocalPort = Port;
 | 
			
		||||
            cServer.StartServer();
 | 
			
		||||
            cServer.ClientConnected += new TServer.eClientConnected(ClientConnected);
 | 
			
		||||
            cServer.ClientDisConnected += new TServer.eClientDisConnected(ClientDisConnected);
 | 
			
		||||
            cServer.ClientIsSwitching += new TServer.eClientIsSwitching(ClientIsSwitching);
 | 
			
		||||
            cServer.DataReceived += new TServer.eDataReceived(DataReceived);
 | 
			
		||||
            var f = (HTTPFilter)instance.Resource;
 | 
			
		||||
            f.SessionExpired((HTTPSession)session);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        }*/
 | 
			
		||||
 | 
			
		||||
        //~HTTPServer()
 | 
			
		||||
        //{
 | 
			
		||||
        //    cServer.StopServer();
 | 
			
		||||
        //}
 | 
			
		||||
        base.SessionEnded((HTTPSession)session);
 | 
			
		||||
        //Sessions.Remove(Session.ID);
 | 
			
		||||
        //Session.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public int TTL
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return Timeout;// mTimeout;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public async AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
        {
 | 
			
		||||
            //var ip = (IPAddress)Instance.Attributes["ip"];
 | 
			
		||||
            //var port = (int)Instance.Attributes["port"];
 | 
			
		||||
            //var ssl = (bool)Instance.Attributes["ssl"];
 | 
			
		||||
            //var cert = (string)Instance.Attributes["certificate"];
 | 
			
		||||
 | 
			
		||||
            //if (ip == null) ip = IPAddress.Any;
 | 
			
		||||
 | 
			
		||||
            Sockets.ISocket listener;
 | 
			
		||||
            IPAddress ipAdd;
 | 
			
		||||
 | 
			
		||||
            if (IP == null)
 | 
			
		||||
                ipAdd = IPAddress.Any;
 | 
			
		||||
            else
 | 
			
		||||
                ipAdd = IPAddress.Parse(IP);
 | 
			
		||||
 | 
			
		||||
            if (SSL)
 | 
			
		||||
                listener = new SSLSocket(new IPEndPoint(ipAdd, Port), new X509Certificate2(Certificate));
 | 
			
		||||
            else
 | 
			
		||||
                listener = new TCPSocket(new IPEndPoint(ipAdd, Port));
 | 
			
		||||
 | 
			
		||||
            Start(listener);
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
        {
 | 
			
		||||
            Stop();
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
        {
 | 
			
		||||
            await Trigger(ResourceTrigger.Terminate);
 | 
			
		||||
            await Trigger(ResourceTrigger.Initialize);
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemInitialized)
 | 
			
		||||
        {
 | 
			
		||||
            filters = await Instance.Children<HTTPFilter>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public override void Add(HTTPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        connection.Server = this;
 | 
			
		||||
        base.Add(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Remove(HTTPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        connection.Server = null;
 | 
			
		||||
        base.Remove(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void ClientConnected(HTTPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        if (filters.Length == 0)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Close();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach (var resource in filters)
 | 
			
		||||
        {
 | 
			
		||||
            resource.ClientConnected(connection);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public int LocalPort
 | 
			
		||||
    {
 | 
			
		||||
        get 
 | 
			
		||||
        {
 | 
			
		||||
            return cServer.LocalPort;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /* 
 | 
			
		||||
    public HTTPServer(int Port)
 | 
			
		||||
    {
 | 
			
		||||
        cServer = new TServer();
 | 
			
		||||
        cServer.LocalPort = Port;
 | 
			
		||||
        cServer.StartServer();
 | 
			
		||||
        cServer.ClientConnected += new TServer.eClientConnected(ClientConnected);
 | 
			
		||||
        cServer.ClientDisConnected += new TServer.eClientDisConnected(ClientDisConnected);
 | 
			
		||||
        cServer.ClientIsSwitching += new TServer.eClientIsSwitching(ClientIsSwitching);
 | 
			
		||||
        cServer.DataReceived += new TServer.eDataReceived(DataReceived);
 | 
			
		||||
 | 
			
		||||
    }*/
 | 
			
		||||
 | 
			
		||||
    //~HTTPServer()
 | 
			
		||||
    //{
 | 
			
		||||
    //    cServer.StopServer();
 | 
			
		||||
    //}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,96 +35,94 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Misc;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.HTTP
 | 
			
		||||
namespace Esiur.Net.HTTP;
 | 
			
		||||
public class HTTPSession : IDestructible //<T> where T : TClient
 | 
			
		||||
{
 | 
			
		||||
    public class HTTPSession : IDestructible //<T> where T : TClient
 | 
			
		||||
    public delegate void SessionModifiedEvent(HTTPSession session, string key, object oldValue, object newValue);
 | 
			
		||||
    public delegate void SessionEndedEvent(HTTPSession session);
 | 
			
		||||
 | 
			
		||||
    private string id;
 | 
			
		||||
    private Timer timer;
 | 
			
		||||
    private int timeout;
 | 
			
		||||
    DateTime creation;
 | 
			
		||||
    DateTime lastAction;
 | 
			
		||||
 | 
			
		||||
    private KeyList<string, object> variables;
 | 
			
		||||
 | 
			
		||||
    public event SessionEndedEvent OnEnd;
 | 
			
		||||
    public event SessionModifiedEvent OnModify;
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    public KeyList<string, object> Variables
 | 
			
		||||
    {
 | 
			
		||||
        public delegate void SessionModifiedEvent(HTTPSession session, string key, object oldValue, object newValue);
 | 
			
		||||
        public delegate void SessionEndedEvent(HTTPSession session);
 | 
			
		||||
        get { return variables; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        private string id;
 | 
			
		||||
        private Timer timer;
 | 
			
		||||
        private int timeout;
 | 
			
		||||
        DateTime creation;
 | 
			
		||||
        DateTime lastAction;
 | 
			
		||||
    public HTTPSession()
 | 
			
		||||
    {
 | 
			
		||||
        variables = new KeyList<string, object>();
 | 
			
		||||
        variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
 | 
			
		||||
        creation = DateTime.Now;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        private KeyList<string, object> variables;
 | 
			
		||||
    internal void Set(string id, int timeout)
 | 
			
		||||
    {
 | 
			
		||||
        //modified = sessionModifiedEvent;
 | 
			
		||||
        //ended = sessionEndEvent;
 | 
			
		||||
        this.id = id;
 | 
			
		||||
 | 
			
		||||
        public event SessionEndedEvent OnEnd;
 | 
			
		||||
        public event SessionModifiedEvent OnModify;
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public KeyList<string, object> Variables
 | 
			
		||||
        if (this.timeout != 0)
 | 
			
		||||
        {
 | 
			
		||||
            get { return variables; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public HTTPSession()
 | 
			
		||||
        {
 | 
			
		||||
            variables = new KeyList<string, object>();
 | 
			
		||||
            variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
 | 
			
		||||
            this.timeout = timeout;
 | 
			
		||||
            timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
            creation = DateTime.Now;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal void Set(string id, int timeout)
 | 
			
		||||
        {
 | 
			
		||||
            //modified = sessionModifiedEvent;
 | 
			
		||||
            //ended = sessionEndEvent;
 | 
			
		||||
            this.id = id;
 | 
			
		||||
 | 
			
		||||
            if (this.timeout != 0)
 | 
			
		||||
            {
 | 
			
		||||
                this.timeout = timeout;
 | 
			
		||||
                timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
                creation = DateTime.Now;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnSessionEndTimerCallback(object o)
 | 
			
		||||
        {
 | 
			
		||||
            OnEnd?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender)
 | 
			
		||||
        {
 | 
			
		||||
            OnModify?.Invoke(this, key, oldValue, newValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
            timer.Dispose();
 | 
			
		||||
            timer = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal void Refresh()
 | 
			
		||||
        {
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
            timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Timeout // Seconds
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return timeout;
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                timeout = value;
 | 
			
		||||
                Refresh();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Id
 | 
			
		||||
        {
 | 
			
		||||
            get { return id; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DateTime LastAction
 | 
			
		||||
        {
 | 
			
		||||
            get { return lastAction; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
    private void OnSessionEndTimerCallback(object o)
 | 
			
		||||
    {
 | 
			
		||||
        OnEnd?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender)
 | 
			
		||||
    {
 | 
			
		||||
        OnModify?.Invoke(this, key, oldValue, newValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
        timer.Dispose();
 | 
			
		||||
        timer = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal void Refresh()
 | 
			
		||||
    {
 | 
			
		||||
        lastAction = DateTime.Now;
 | 
			
		||||
        timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Timeout // Seconds
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return timeout;
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            timeout = value;
 | 
			
		||||
            Refresh();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string Id
 | 
			
		||||
    {
 | 
			
		||||
        get { return id; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DateTime LastAction
 | 
			
		||||
    {
 | 
			
		||||
        get { return lastAction; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,34 +6,32 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.HTTP
 | 
			
		||||
namespace Esiur.Net.HTTP;
 | 
			
		||||
public class IIPoHTTP : HTTPFilter
 | 
			
		||||
{
 | 
			
		||||
    public class IIPoHTTP : HTTPFilter
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    EntryPoint EntryPoint { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override AsyncReply<bool> Execute(HTTPConnection sender)
 | 
			
		||||
    {
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        EntryPoint EntryPoint { get; set; }
 | 
			
		||||
        if (sender.Request.URL != "iip")
 | 
			
		||||
            return new AsyncReply<bool>(false);
 | 
			
		||||
 | 
			
		||||
        public override AsyncReply<bool> Execute(HTTPConnection sender)
 | 
			
		||||
        IIPPacket.IIPPacketAction action = (IIPPacket.IIPPacketAction)Convert.ToByte(sender.Request.Query["a"]);
 | 
			
		||||
 | 
			
		||||
        if (action == IIPPacket.IIPPacketAction.QueryLink)
 | 
			
		||||
        {
 | 
			
		||||
            if (sender.Request.URL != "iip")
 | 
			
		||||
                return new AsyncReply<bool>(false);
 | 
			
		||||
 | 
			
		||||
            IIPPacket.IIPPacketAction action = (IIPPacket.IIPPacketAction)Convert.ToByte(sender.Request.Query["a"]);
 | 
			
		||||
 | 
			
		||||
            if (action == IIPPacket.IIPPacketAction.QueryLink)
 | 
			
		||||
            EntryPoint.Query(sender.Request.Query["l"], null).Then(x =>
 | 
			
		||||
            {
 | 
			
		||||
                EntryPoint.Query(sender.Request.Query["l"], null).Then(x =>
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,94 +32,91 @@ using Esiur.Net.IIP;
 | 
			
		||||
using Esiur.Net.Sockets;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.HTTP
 | 
			
		||||
namespace Esiur.Net.HTTP;
 | 
			
		||||
public class IIPoWS : HTTPFilter
 | 
			
		||||
{
 | 
			
		||||
    public class IIPoWS: HTTPFilter
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public DistributedServer Server
 | 
			
		||||
    {
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public DistributedServer Server
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override AsyncReply<bool> Execute(HTTPConnection sender)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (sender.IsWebsocketRequest())
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
            if (Server == null)
 | 
			
		||||
                return new AsyncReply<bool>(false);
 | 
			
		||||
 | 
			
		||||
        public override AsyncReply<bool> Execute(HTTPConnection sender)
 | 
			
		||||
        {
 | 
			
		||||
            var tcpSocket = sender.Unassign();
 | 
			
		||||
 | 
			
		||||
            if (sender.IsWebsocketRequest())
 | 
			
		||||
            {
 | 
			
		||||
                if (Server == null)
 | 
			
		||||
                    return new AsyncReply<bool>(false);
 | 
			
		||||
            if (tcpSocket == null)
 | 
			
		||||
                return new AsyncReply<bool>(false);
 | 
			
		||||
 | 
			
		||||
                var tcpSocket = sender.Unassign();
 | 
			
		||||
            var httpServer = sender.Parent;
 | 
			
		||||
            var wsSocket = new WSocket(tcpSocket);
 | 
			
		||||
            httpServer.Remove(sender);
 | 
			
		||||
 | 
			
		||||
                if (tcpSocket == null)
 | 
			
		||||
                    return new AsyncReply<bool>(false);
 | 
			
		||||
            var iipConnection = new DistributedConnection();
 | 
			
		||||
 | 
			
		||||
                var httpServer = sender.Parent;
 | 
			
		||||
                var wsSocket = new WSocket(tcpSocket);
 | 
			
		||||
                httpServer.Remove(sender);
 | 
			
		||||
            Server.Add(iipConnection);
 | 
			
		||||
            iipConnection.Assign(wsSocket);
 | 
			
		||||
            wsSocket.Begin();
 | 
			
		||||
 | 
			
		||||
                var iipConnection = new DistributedConnection();
 | 
			
		||||
 | 
			
		||||
                Server.Add(iipConnection);
 | 
			
		||||
                iipConnection.Assign(wsSocket);
 | 
			
		||||
                wsSocket.Begin();
 | 
			
		||||
 | 
			
		||||
                return new AsyncReply<bool>(true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>( false);
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            if (sender.Request.Filename.StartsWith("/iip/"))
 | 
			
		||||
            {
 | 
			
		||||
                // find the service
 | 
			
		||||
                var path = sender.Request.Filename.Substring(5);// sender.Request.Query["path"];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                Warehouse.Get(path).Then((r) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (r is DistributedServer)
 | 
			
		||||
                    {
 | 
			
		||||
                        var httpServer = sender.Parent;
 | 
			
		||||
                        var iipServer = r as DistributedServer;
 | 
			
		||||
                        var tcpSocket = sender.Unassign();
 | 
			
		||||
                        if (tcpSocket == null)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var wsSocket = new WSSocket(tcpSocket);
 | 
			
		||||
                        httpServer.RemoveConnection(sender);
 | 
			
		||||
 | 
			
		||||
                        //httpServer.Connections.Remove(sender);
 | 
			
		||||
                        var iipConnection = new DistributedConnection();
 | 
			
		||||
  //                      iipConnection.OnReady += IipConnection_OnReady;
 | 
			
		||||
//                        iipConnection.Server = iipServer;
 | 
			
		||||
    //                    iipConnection.Assign(wsSocket);
 | 
			
		||||
 | 
			
		||||
                        iipServer.AddConnection(iipConnection);
 | 
			
		||||
                        iipConnection.Assign(wsSocket);
 | 
			
		||||
                        wsSocket.Begin();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void IipConnection_OnReady(DistributedConnection sender)
 | 
			
		||||
        {
 | 
			
		||||
            Warehouse.Put(sender.RemoteUsername, sender, null, sender.Server).Wait();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new AsyncReply<bool>(false);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        if (sender.Request.Filename.StartsWith("/iip/"))
 | 
			
		||||
        {
 | 
			
		||||
            // find the service
 | 
			
		||||
            var path = sender.Request.Filename.Substring(5);// sender.Request.Query["path"];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Warehouse.Get(path).Then((r) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (r is DistributedServer)
 | 
			
		||||
                {
 | 
			
		||||
                    var httpServer = sender.Parent;
 | 
			
		||||
                    var iipServer = r as DistributedServer;
 | 
			
		||||
                    var tcpSocket = sender.Unassign();
 | 
			
		||||
                    if (tcpSocket == null)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    var wsSocket = new WSSocket(tcpSocket);
 | 
			
		||||
                    httpServer.RemoveConnection(sender);
 | 
			
		||||
 | 
			
		||||
                    //httpServer.Connections.Remove(sender);
 | 
			
		||||
                    var iipConnection = new DistributedConnection();
 | 
			
		||||
//                      iipConnection.OnReady += IipConnection_OnReady;
 | 
			
		||||
//                        iipConnection.Server = iipServer;
 | 
			
		||||
//                    iipConnection.Assign(wsSocket);
 | 
			
		||||
 | 
			
		||||
                    iipServer.AddConnection(iipConnection);
 | 
			
		||||
                    iipConnection.Assign(wsSocket);
 | 
			
		||||
                    wsSocket.Begin();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void IipConnection_OnReady(DistributedConnection sender)
 | 
			
		||||
    {
 | 
			
		||||
        Warehouse.Put(sender.RemoteUsername, sender, null, sender.Server).Wait();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -2,26 +2,24 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
public class DistributedPropertyContext
 | 
			
		||||
{
 | 
			
		||||
    public class DistributedPropertyContext
 | 
			
		||||
    public object Value { get; private set; }
 | 
			
		||||
    public DistributedConnection Connection { get; private set; }
 | 
			
		||||
    public Func<DistributedConnection, object> Method { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public DistributedPropertyContext(DistributedConnection connection, object value)
 | 
			
		||||
    {
 | 
			
		||||
        public object Value { get; private set; }
 | 
			
		||||
        public DistributedConnection Connection { get; private set; }
 | 
			
		||||
        public Func<DistributedConnection, object> Method { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public DistributedPropertyContext(DistributedConnection connection, object value)
 | 
			
		||||
        {
 | 
			
		||||
            this.Value = value;
 | 
			
		||||
            this.Connection = connection;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DistributedPropertyContext(Func<DistributedConnection, object> method)
 | 
			
		||||
        {
 | 
			
		||||
            this.Method = method;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static implicit operator DistributedPropertyContext(Func<DistributedConnection, object> method)
 | 
			
		||||
                                        => new DistributedPropertyContext(method);
 | 
			
		||||
        this.Value = value;
 | 
			
		||||
        this.Connection = connection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DistributedPropertyContext(Func<DistributedConnection, object> method)
 | 
			
		||||
    {
 | 
			
		||||
        this.Method = method;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static implicit operator DistributedPropertyContext(Func<DistributedConnection, object> method)
 | 
			
		||||
                                    => new DistributedPropertyContext(method);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,465 +42,463 @@ using System.Threading.Tasks;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using Esiur.Resource.Template;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
 | 
			
		||||
//[System.Runtime.InteropServices.ComVisible(true)]
 | 
			
		||||
public class DistributedResource : DynamicObject, IResource
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    //[System.Runtime.InteropServices.ComVisible(true)]
 | 
			
		||||
    public class DistributedResource : DynamicObject, IResource
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Raised when the distributed resource is destroyed.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
    public event Instance.ResourceModifiedEvent OnModified;
 | 
			
		||||
    uint instanceId;
 | 
			
		||||
    DistributedConnection connection;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    bool attached = false;
 | 
			
		||||
    bool destroyed = false;
 | 
			
		||||
    bool suspended = false;
 | 
			
		||||
 | 
			
		||||
    //Structure properties = new Structure();
 | 
			
		||||
 | 
			
		||||
    string link;
 | 
			
		||||
    //ulong age;
 | 
			
		||||
    //ulong[] ages;
 | 
			
		||||
    protected object[] properties;
 | 
			
		||||
    internal List<DistributedResource> parents = new List<DistributedResource>();
 | 
			
		||||
    internal List<DistributedResource> children = new List<DistributedResource>();
 | 
			
		||||
 | 
			
		||||
    DistributedResourceEvent[] events;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Resource template for the remotely located resource.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    //public ResourceTemplate Template
 | 
			
		||||
    //{
 | 
			
		||||
    //    get { return template; }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Connection responsible for the distributed resource.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DistributedConnection Connection
 | 
			
		||||
    {
 | 
			
		||||
        get { return connection; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Resource link
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Link
 | 
			
		||||
    {
 | 
			
		||||
        get { return link; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Instance Id given by the other end.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public uint Id
 | 
			
		||||
    {
 | 
			
		||||
        get { return instanceId; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// IDestructible interface.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        destroyed = true;
 | 
			
		||||
        attached = false;
 | 
			
		||||
        connection.SendDetachRequest(instanceId);
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Suspend resource
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 | 
			
		||||
    internal void Suspend()
 | 
			
		||||
    {
 | 
			
		||||
        suspended = true;
 | 
			
		||||
        attached = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Resource is attached when all its properties are received.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal bool Attached => attached;
 | 
			
		||||
 | 
			
		||||
    internal bool Suspended => suspended;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // public DistributedResourceStack Stack
 | 
			
		||||
    //{
 | 
			
		||||
    //     get { return stack; }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new distributed resource.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="connection">Connection responsible for the distributed resource.</param>
 | 
			
		||||
    /// <param name="template">Resource template.</param>
 | 
			
		||||
    /// <param name="instanceId">Instance Id given by the other end.</param>
 | 
			
		||||
    /// <param name="age">Resource age.</param>
 | 
			
		||||
    public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link)
 | 
			
		||||
    {
 | 
			
		||||
        this.link = link;
 | 
			
		||||
        this.connection = connection;
 | 
			
		||||
        this.instanceId = instanceId;
 | 
			
		||||
 | 
			
		||||
        //this.Instance.Template = template;
 | 
			
		||||
        //this.Instance.Age = age;
 | 
			
		||||
        //this.template = template;
 | 
			
		||||
        //this.age = age;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Export all properties with ResourceProperty attributed as bytes array.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal PropertyValue[] _Serialize()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Raised when the distributed resource is destroyed.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        public event Instance.ResourceModifiedEvent OnModified;
 | 
			
		||||
        uint instanceId;
 | 
			
		||||
        DistributedConnection connection;
 | 
			
		||||
        var props = new PropertyValue[properties.Length];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        bool attached = false;
 | 
			
		||||
        bool destroyed = false;
 | 
			
		||||
        bool suspended = false;
 | 
			
		||||
        for (byte i = 0; i < properties.Length; i++)
 | 
			
		||||
            props[i] = new PropertyValue(properties[i], Instance.GetAge(i), Instance.GetModificationDate(i));
 | 
			
		||||
 | 
			
		||||
        //Structure properties = new Structure();
 | 
			
		||||
        return props;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        string link;
 | 
			
		||||
        //ulong age;
 | 
			
		||||
        //ulong[] ages;
 | 
			
		||||
        protected object[] properties;
 | 
			
		||||
        internal List<DistributedResource> parents = new List<DistributedResource>();
 | 
			
		||||
        internal List<DistributedResource> children = new List<DistributedResource>();
 | 
			
		||||
 | 
			
		||||
        DistributedResourceEvent[] events;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Resource template for the remotely located resource.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        //public ResourceTemplate Template
 | 
			
		||||
        //{
 | 
			
		||||
        //    get { return template; }
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Connection responsible for the distributed resource.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DistributedConnection Connection
 | 
			
		||||
    internal bool _Attach(PropertyValue[] properties)
 | 
			
		||||
    {
 | 
			
		||||
        if (attached)
 | 
			
		||||
            return false;
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            get { return connection; }
 | 
			
		||||
        }
 | 
			
		||||
            suspended = false;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Resource link
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Link
 | 
			
		||||
        {
 | 
			
		||||
            get { return link; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Instance Id given by the other end.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public uint Id
 | 
			
		||||
        {
 | 
			
		||||
            get { return instanceId; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// IDestructible interface.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            destroyed = true;
 | 
			
		||||
            attached = false;
 | 
			
		||||
            connection.SendDetachRequest(instanceId);
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Suspend resource
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 | 
			
		||||
        internal void Suspend()
 | 
			
		||||
        {
 | 
			
		||||
            suspended = true;
 | 
			
		||||
            attached = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Resource is attached when all its properties are received.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        internal bool Attached => attached;
 | 
			
		||||
 | 
			
		||||
        internal bool Suspended => suspended;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // public DistributedResourceStack Stack
 | 
			
		||||
        //{
 | 
			
		||||
        //     get { return stack; }
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create a new distributed resource.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="connection">Connection responsible for the distributed resource.</param>
 | 
			
		||||
        /// <param name="template">Resource template.</param>
 | 
			
		||||
        /// <param name="instanceId">Instance Id given by the other end.</param>
 | 
			
		||||
        /// <param name="age">Resource age.</param>
 | 
			
		||||
        public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link)
 | 
			
		||||
        {
 | 
			
		||||
            this.link = link;
 | 
			
		||||
            this.connection = connection;
 | 
			
		||||
            this.instanceId = instanceId;
 | 
			
		||||
 | 
			
		||||
            //this.Instance.Template = template;
 | 
			
		||||
            //this.Instance.Age = age;
 | 
			
		||||
            //this.template = template;
 | 
			
		||||
            //this.age = age;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Export all properties with ResourceProperty attributed as bytes array.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        internal PropertyValue[] _Serialize()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            var props = new PropertyValue[properties.Length];
 | 
			
		||||
            this.properties = new object[properties.Length];
 | 
			
		||||
 | 
			
		||||
            this.events = new DistributedResourceEvent[Instance.Template.Events.Length];
 | 
			
		||||
 | 
			
		||||
            for (byte i = 0; i < properties.Length; i++)
 | 
			
		||||
                props[i] = new PropertyValue(properties[i], Instance.GetAge(i), Instance.GetModificationDate(i));
 | 
			
		||||
 | 
			
		||||
            return props;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal bool _Attach(PropertyValue[] properties)
 | 
			
		||||
        {
 | 
			
		||||
            if (attached)
 | 
			
		||||
                return false;
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                suspended = false;
 | 
			
		||||
 | 
			
		||||
                this.properties = new object[properties.Length];
 | 
			
		||||
 | 
			
		||||
                this.events = new DistributedResourceEvent[Instance.Template.Events.Length];
 | 
			
		||||
 | 
			
		||||
                for (byte i = 0; i < properties.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    Instance.SetAge(i, properties[i].Age);
 | 
			
		||||
                    Instance.SetModificationDate(i, properties[i].Date);
 | 
			
		||||
                    this.properties[i] = properties[i].Value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // trigger holded events/property updates.
 | 
			
		||||
                //foreach (var r in afterAttachmentTriggers)
 | 
			
		||||
                //    r.Key.Trigger(r.Value);
 | 
			
		||||
 | 
			
		||||
                //afterAttachmentTriggers.Clear();
 | 
			
		||||
 | 
			
		||||
                attached = true;
 | 
			
		||||
 | 
			
		||||
                Instance.SetAge(i, properties[i].Age);
 | 
			
		||||
                Instance.SetModificationDate(i, properties[i].Date);
 | 
			
		||||
                this.properties[i] = properties[i].Value;
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
            // trigger holded events/property updates.
 | 
			
		||||
            //foreach (var r in afterAttachmentTriggers)
 | 
			
		||||
            //    r.Key.Trigger(r.Value);
 | 
			
		||||
 | 
			
		||||
            //afterAttachmentTriggers.Clear();
 | 
			
		||||
 | 
			
		||||
            attached = true;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        protected internal virtual void _EmitEventByIndex(byte index, object args)
 | 
			
		||||
    protected internal virtual void _EmitEventByIndex(byte index, object args)
 | 
			
		||||
    {
 | 
			
		||||
        var et = Instance.Template.GetEventTemplateByIndex(index);
 | 
			
		||||
        events[index]?.Invoke(this, args);
 | 
			
		||||
        Instance.EmitResourceEvent(et.Name, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<object> _InvokeByNamedArguments(byte index, Structure namedArgs)
 | 
			
		||||
    {
 | 
			
		||||
        if (destroyed)
 | 
			
		||||
            throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
        if (suspended)
 | 
			
		||||
            throw new Exception("Trying to access suspended object");
 | 
			
		||||
 | 
			
		||||
        if (index >= Instance.Template.Functions.Length)
 | 
			
		||||
            throw new Exception("Function index is incorrect");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<object> _InvokeByArrayArguments(byte index, object[] args)
 | 
			
		||||
    {
 | 
			
		||||
        if (destroyed)
 | 
			
		||||
            throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
        if (suspended)
 | 
			
		||||
            throw new Exception("Trying to access suspended object");
 | 
			
		||||
 | 
			
		||||
        if (index >= Instance.Template.Functions.Length)
 | 
			
		||||
            throw new Exception("Function index is incorrect");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return connection.SendInvokeByArrayArguments(instanceId, index, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Listen(EventTemplate et)
 | 
			
		||||
    {
 | 
			
		||||
        if (et == null)
 | 
			
		||||
            return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
 | 
			
		||||
 | 
			
		||||
        if (!et.Listenable)
 | 
			
		||||
            return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, ""));
 | 
			
		||||
 | 
			
		||||
        return connection.SendListenRequest(instanceId, et.Index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Listen(string eventName)
 | 
			
		||||
    {
 | 
			
		||||
        var et = Instance.Template.GetEventTemplateByName(eventName);
 | 
			
		||||
 | 
			
		||||
        return Listen(et);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Unlisten(EventTemplate et)
 | 
			
		||||
    {
 | 
			
		||||
        if (et == null)
 | 
			
		||||
            return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
 | 
			
		||||
 | 
			
		||||
        if (!et.Listenable)
 | 
			
		||||
            return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, ""));
 | 
			
		||||
 | 
			
		||||
        return connection.SendUnlistenRequest(instanceId, et.Index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply Unlisten(string eventName)
 | 
			
		||||
    {
 | 
			
		||||
        var et = Instance.Template.GetEventTemplateByName(eventName);
 | 
			
		||||
 | 
			
		||||
        return Unlisten(et);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
 | 
			
		||||
    {
 | 
			
		||||
        var ft = Instance.Template.GetFunctionTemplateByName(binder.Name);
 | 
			
		||||
 | 
			
		||||
        var reply = new AsyncReply<object>();
 | 
			
		||||
 | 
			
		||||
        if (attached && ft != null)
 | 
			
		||||
        {
 | 
			
		||||
            var et = Instance.Template.GetEventTemplateByIndex(index);
 | 
			
		||||
            events[index]?.Invoke(this, args);
 | 
			
		||||
            Instance.EmitResourceEvent(et.Name, args);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<object> _InvokeByNamedArguments(byte index, Structure namedArgs)
 | 
			
		||||
        {
 | 
			
		||||
            if (destroyed)
 | 
			
		||||
                throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
            if (suspended)
 | 
			
		||||
                throw new Exception("Trying to access suspended object");
 | 
			
		||||
 | 
			
		||||
            if (index >= Instance.Template.Functions.Length)
 | 
			
		||||
                throw new Exception("Function index is incorrect");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<object> _InvokeByArrayArguments(byte index, object[] args)
 | 
			
		||||
        {
 | 
			
		||||
            if (destroyed)
 | 
			
		||||
                throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
            if (suspended)
 | 
			
		||||
                throw new Exception("Trying to access suspended object");
 | 
			
		||||
 | 
			
		||||
            if (index >= Instance.Template.Functions.Length)
 | 
			
		||||
                throw new Exception("Function index is incorrect");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return connection.SendInvokeByArrayArguments(instanceId, index, args);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Listen(EventTemplate et)
 | 
			
		||||
        {
 | 
			
		||||
            if (et == null)
 | 
			
		||||
                return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
 | 
			
		||||
 | 
			
		||||
            if (!et.Listenable)
 | 
			
		||||
                return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, ""));
 | 
			
		||||
 | 
			
		||||
            return connection.SendListenRequest(instanceId, et.Index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Listen(string eventName)
 | 
			
		||||
        {
 | 
			
		||||
            var et = Instance.Template.GetEventTemplateByName(eventName);
 | 
			
		||||
 | 
			
		||||
            return Listen(et);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Unlisten(EventTemplate et)
 | 
			
		||||
        {
 | 
			
		||||
            if (et == null)
 | 
			
		||||
                return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, ""));
 | 
			
		||||
 | 
			
		||||
            if (!et.Listenable)
 | 
			
		||||
                return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, ""));
 | 
			
		||||
 | 
			
		||||
            return connection.SendUnlistenRequest(instanceId, et.Index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply Unlisten(string eventName)
 | 
			
		||||
        {
 | 
			
		||||
            var et = Instance.Template.GetEventTemplateByName(eventName);
 | 
			
		||||
 | 
			
		||||
            return Unlisten(et);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
 | 
			
		||||
        {
 | 
			
		||||
            var ft = Instance.Template.GetFunctionTemplateByName(binder.Name);
 | 
			
		||||
 | 
			
		||||
            var reply = new AsyncReply<object>();
 | 
			
		||||
 | 
			
		||||
            if (attached && ft != null)
 | 
			
		||||
            if (args.Length == 1)
 | 
			
		||||
            {
 | 
			
		||||
                if (args.Length == 1)
 | 
			
		||||
                // Detect anonymous types
 | 
			
		||||
                var type = args[0].GetType();
 | 
			
		||||
                if (Codec.IsAnonymous(type))
 | 
			
		||||
                {
 | 
			
		||||
                    // Detect anonymous types
 | 
			
		||||
                    var type = args[0].GetType();
 | 
			
		||||
                    if (Codec.IsAnonymous(type))
 | 
			
		||||
                    {
 | 
			
		||||
                        var namedArgs = new Structure();
 | 
			
		||||
 | 
			
		||||
                        var pi = type.GetTypeInfo().GetProperties();
 | 
			
		||||
                        foreach (var p in pi)
 | 
			
		||||
                            namedArgs[p.Name] = p.GetValue(args[0]);
 | 
			
		||||
                        result = _InvokeByNamedArguments(ft.Index, namedArgs);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        result = _InvokeByArrayArguments(ft.Index, args);
 | 
			
		||||
                    }
 | 
			
		||||
                    var namedArgs = new Structure();
 | 
			
		||||
 | 
			
		||||
                    var pi = type.GetTypeInfo().GetProperties();
 | 
			
		||||
                    foreach (var p in pi)
 | 
			
		||||
                        namedArgs[p.Name] = p.GetValue(args[0]);
 | 
			
		||||
                    result = _InvokeByNamedArguments(ft.Index, namedArgs);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    result = _InvokeByArrayArguments(ft.Index, args);
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                result = null;
 | 
			
		||||
                return false;
 | 
			
		||||
                result = _InvokeByArrayArguments(ft.Index, args);
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Get a property value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="index">Zero-based property index.</param>
 | 
			
		||||
        /// <returns>Value</returns>
 | 
			
		||||
        protected internal object _Get(byte index)
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (index >= properties.Length)
 | 
			
		||||
                return null;
 | 
			
		||||
            return properties[index];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool TryGetMember(GetMemberBinder binder, out object result)
 | 
			
		||||
        {
 | 
			
		||||
            if (destroyed)
 | 
			
		||||
                throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            result = null;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            if (!attached)
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get a property value.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="index">Zero-based property index.</param>
 | 
			
		||||
    /// <returns>Value</returns>
 | 
			
		||||
    protected internal object _Get(byte index)
 | 
			
		||||
    {
 | 
			
		||||
        if (index >= properties.Length)
 | 
			
		||||
            return null;
 | 
			
		||||
        return properties[index];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool TryGetMember(GetMemberBinder binder, out object result)
 | 
			
		||||
    {
 | 
			
		||||
        if (destroyed)
 | 
			
		||||
            throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        result = null;
 | 
			
		||||
 | 
			
		||||
        if (!attached)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        var pt = Instance.Template.GetPropertyTemplateByName(binder.Name);
 | 
			
		||||
 | 
			
		||||
        if (pt != null)
 | 
			
		||||
        {
 | 
			
		||||
            result = properties[pt.Index];
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var et = Instance.Template.GetEventTemplateByName(binder.Name);
 | 
			
		||||
            if (et == null)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            var pt = Instance.Template.GetPropertyTemplateByName(binder.Name);
 | 
			
		||||
            result = events[et.Index];
 | 
			
		||||
 | 
			
		||||
            if (pt != null)
 | 
			
		||||
            {
 | 
			
		||||
                result = properties[pt.Index];
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var et = Instance.Template.GetEventTemplateByName(binder.Name);
 | 
			
		||||
                if (et == null)
 | 
			
		||||
                    return false;
 | 
			
		||||
 | 
			
		||||
                result = events[et.Index];
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        internal void _UpdatePropertyByIndex(byte index, object value)
 | 
			
		||||
        {
 | 
			
		||||
            var pt = Instance.Template.GetPropertyTemplateByIndex(index);
 | 
			
		||||
            properties[index] = value;
 | 
			
		||||
            Instance.EmitModification(pt, value);
 | 
			
		||||
        }
 | 
			
		||||
    internal void _UpdatePropertyByIndex(byte index, object value)
 | 
			
		||||
    {
 | 
			
		||||
        var pt = Instance.Template.GetPropertyTemplateByIndex(index);
 | 
			
		||||
        properties[index] = value;
 | 
			
		||||
        Instance.EmitModification(pt, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Set property value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="index">Zero-based property index.</param>
 | 
			
		||||
        /// <param name="value">Value</param>
 | 
			
		||||
        /// <returns>Indicator when the property is set.</returns>
 | 
			
		||||
        protected internal AsyncReply<object> _Set(byte index, object value)
 | 
			
		||||
        {
 | 
			
		||||
            if (index >= properties.Length)
 | 
			
		||||
                return null;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Set property value.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="index">Zero-based property index.</param>
 | 
			
		||||
    /// <param name="value">Value</param>
 | 
			
		||||
    /// <returns>Indicator when the property is set.</returns>
 | 
			
		||||
    protected internal AsyncReply<object> _Set(byte index, object value)
 | 
			
		||||
    {
 | 
			
		||||
        if (index >= properties.Length)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
            var reply = new AsyncReply<object>();
 | 
			
		||||
        var reply = new AsyncReply<object>();
 | 
			
		||||
 | 
			
		||||
            var parameters = Codec.Compose(value, connection);
 | 
			
		||||
            connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty)
 | 
			
		||||
                        .AddUInt32(instanceId)
 | 
			
		||||
                        .AddUInt8(index)
 | 
			
		||||
                        .AddUInt8Array(parameters)
 | 
			
		||||
                        .Done()
 | 
			
		||||
                        .Then((res) =>
 | 
			
		||||
                        {
 | 
			
		||||
        var parameters = Codec.Compose(value, connection);
 | 
			
		||||
        connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty)
 | 
			
		||||
                    .AddUInt32(instanceId)
 | 
			
		||||
                    .AddUInt8(index)
 | 
			
		||||
                    .AddUInt8Array(parameters)
 | 
			
		||||
                    .Done()
 | 
			
		||||
                    .Then((res) =>
 | 
			
		||||
                    {
 | 
			
		||||
                            // not really needed, server will always send property modified, 
 | 
			
		||||
                            // this only happens if the programmer forgot to emit in property setter
 | 
			
		||||
                            properties[index] = value;
 | 
			
		||||
                            reply.Trigger(null);
 | 
			
		||||
                        });
 | 
			
		||||
                        reply.Trigger(null);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
            return reply;
 | 
			
		||||
        }
 | 
			
		||||
        return reply;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public override bool TrySetMember(SetMemberBinder binder, object value)
 | 
			
		||||
    public override bool TrySetMember(SetMemberBinder binder, object value)
 | 
			
		||||
    {
 | 
			
		||||
        if (destroyed)
 | 
			
		||||
            throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
        if (suspended)
 | 
			
		||||
            throw new Exception("Trying to access suspended object");
 | 
			
		||||
 | 
			
		||||
        if (!attached)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        var pt = Instance.Template.GetPropertyTemplateByName(binder.Name);
 | 
			
		||||
 | 
			
		||||
        if (pt != null)
 | 
			
		||||
        {
 | 
			
		||||
            if (destroyed)
 | 
			
		||||
                throw new Exception("Trying to access destroyed object");
 | 
			
		||||
 | 
			
		||||
            if (suspended)
 | 
			
		||||
                throw new Exception("Trying to access suspended object");
 | 
			
		||||
 | 
			
		||||
            if (!attached)
 | 
			
		||||
            _Set(pt.Index, value);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var et = Instance.Template.GetEventTemplateByName(binder.Name);
 | 
			
		||||
            if (et == null)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            var pt = Instance.Template.GetPropertyTemplateByName(binder.Name);
 | 
			
		||||
 | 
			
		||||
            if (pt != null)
 | 
			
		||||
            {
 | 
			
		||||
                _Set(pt.Index, value);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var et = Instance.Template.GetEventTemplateByName(binder.Name);
 | 
			
		||||
                if (et == null)
 | 
			
		||||
                    return false;
 | 
			
		||||
 | 
			
		||||
                events[et.Index] = (DistributedResourceEvent)value;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            events[et.Index] = (DistributedResourceEvent)value;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
              public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender)
 | 
			
		||||
              {
 | 
			
		||||
                  // get function parameters
 | 
			
		||||
                  Type t = this.GetType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                  MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly |
 | 
			
		||||
                                                          BindingFlags.Public |
 | 
			
		||||
                                                          BindingFlags.Instance | BindingFlags.InvokeMethod);
 | 
			
		||||
                  if (mi != null)
 | 
			
		||||
    /*
 | 
			
		||||
          public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender)
 | 
			
		||||
          {
 | 
			
		||||
              // get function parameters
 | 
			
		||||
              Type t = this.GetType();
 | 
			
		||||
 | 
			
		||||
              MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly |
 | 
			
		||||
                                                      BindingFlags.Public |
 | 
			
		||||
                                                      BindingFlags.Instance | BindingFlags.InvokeMethod);
 | 
			
		||||
              if (mi != null)
 | 
			
		||||
              {
 | 
			
		||||
                  try
 | 
			
		||||
                  {
 | 
			
		||||
                      try
 | 
			
		||||
                      {
 | 
			
		||||
                          var res = await invokeMethod(mi, arguments, sender);
 | 
			
		||||
                          object rt = Codec.Compose(res);
 | 
			
		||||
                          sender.SendParams((byte)0x80, instanceId, index, rt);
 | 
			
		||||
                      }
 | 
			
		||||
                      catch(Exception ex)
 | 
			
		||||
                      {
 | 
			
		||||
                          var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message;
 | 
			
		||||
                          sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg));
 | 
			
		||||
                      }
 | 
			
		||||
                      var res = await invokeMethod(mi, arguments, sender);
 | 
			
		||||
                      object rt = Codec.Compose(res);
 | 
			
		||||
                      sender.SendParams((byte)0x80, instanceId, index, rt);
 | 
			
		||||
                  }
 | 
			
		||||
                  catch(Exception ex)
 | 
			
		||||
                  {
 | 
			
		||||
                      var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message;
 | 
			
		||||
                      sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg));
 | 
			
		||||
                  }
 | 
			
		||||
              }
 | 
			
		||||
              */
 | 
			
		||||
          }
 | 
			
		||||
          */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Resource interface.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Resource interface.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Create a new instance of distributed resource.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DistributedResource()
 | 
			
		||||
        {
 | 
			
		||||
            //stack = new DistributedResourceStack(this);
 | 
			
		||||
            //this.Instance.ResourceModified += this.OnModified;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new instance of distributed resource.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DistributedResource()
 | 
			
		||||
    {
 | 
			
		||||
        //stack = new DistributedResourceStack(this);
 | 
			
		||||
        //this.Instance.ResourceModified += this.OnModified;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Resource interface.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="trigger"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Resource interface.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="trigger"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
                this.Instance.ResourceModified += this.OnModified;
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            this.Instance.ResourceModified += this.OnModified;
 | 
			
		||||
 | 
			
		||||
            // do nothing.
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
        // do nothing.
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -28,7 +28,6 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
{
 | 
			
		||||
    public delegate void DistributedResourceEvent(DistributedResource sender, object argument);
 | 
			
		||||
}
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
 | 
			
		||||
public delegate void DistributedResourceEvent(DistributedResource sender, object argument);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,46 +28,44 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
public class DistributedResourceQueueItem
 | 
			
		||||
{
 | 
			
		||||
    public class DistributedResourceQueueItem
 | 
			
		||||
    public enum DistributedResourceQueueItemType
 | 
			
		||||
    {
 | 
			
		||||
        public enum DistributedResourceQueueItemType
 | 
			
		||||
        {
 | 
			
		||||
            Propery,
 | 
			
		||||
            Event
 | 
			
		||||
        }
 | 
			
		||||
        Propery,
 | 
			
		||||
        Event
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        DistributedResourceQueueItemType type;
 | 
			
		||||
        byte index;
 | 
			
		||||
        object value;
 | 
			
		||||
        DistributedResource resource;
 | 
			
		||||
    DistributedResourceQueueItemType type;
 | 
			
		||||
    byte index;
 | 
			
		||||
    object value;
 | 
			
		||||
    DistributedResource resource;
 | 
			
		||||
 | 
			
		||||
        public DistributedResourceQueueItem(DistributedResource resource, DistributedResourceQueueItemType type, object value, byte index)
 | 
			
		||||
        {
 | 
			
		||||
            this.resource = resource;
 | 
			
		||||
            this.index = index;
 | 
			
		||||
            this.type = type;
 | 
			
		||||
            this.value = value;
 | 
			
		||||
        }
 | 
			
		||||
    public DistributedResourceQueueItem(DistributedResource resource, DistributedResourceQueueItemType type, object value, byte index)
 | 
			
		||||
    {
 | 
			
		||||
        this.resource = resource;
 | 
			
		||||
        this.index = index;
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        this.value = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public DistributedResource Resource
 | 
			
		||||
        {
 | 
			
		||||
            get { return resource; }
 | 
			
		||||
        }
 | 
			
		||||
        public DistributedResourceQueueItemType Type
 | 
			
		||||
        {
 | 
			
		||||
            get { return type; }
 | 
			
		||||
        }
 | 
			
		||||
    public DistributedResource Resource
 | 
			
		||||
    {
 | 
			
		||||
        get { return resource; }
 | 
			
		||||
    }
 | 
			
		||||
    public DistributedResourceQueueItemType Type
 | 
			
		||||
    {
 | 
			
		||||
        get { return type; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public byte Index
 | 
			
		||||
        {
 | 
			
		||||
            get { return index; }
 | 
			
		||||
        }
 | 
			
		||||
    public byte Index
 | 
			
		||||
    {
 | 
			
		||||
        get { return index; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public object Value
 | 
			
		||||
        {
 | 
			
		||||
            get { return value; }
 | 
			
		||||
        }
 | 
			
		||||
    public object Value
 | 
			
		||||
    {
 | 
			
		||||
        get { return value; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,143 +35,141 @@ using System.Net;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using Esiur.Security.Membership;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
public class DistributedServer : NetworkServer<DistributedConnection>, IResource
 | 
			
		||||
{
 | 
			
		||||
    public class DistributedServer : NetworkServer<DistributedConnection>, IResource
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public string IP
 | 
			
		||||
    {
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public string IP
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public IMembership Membership
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public EntryPoint EntryPoint
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public ushort Port
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        } = 10518;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public ExceptionLevel ExceptionLevel { get; set; }
 | 
			
		||||
            = ExceptionLevel.Code
 | 
			
		||||
            | ExceptionLevel.Source
 | 
			
		||||
            | ExceptionLevel.Message
 | 
			
		||||
            | ExceptionLevel.Trace;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            {
 | 
			
		||||
                TCPSocket listener;
 | 
			
		||||
 | 
			
		||||
                if (IP != null)
 | 
			
		||||
                    listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port));
 | 
			
		||||
                else
 | 
			
		||||
                    listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port));
 | 
			
		||||
 | 
			
		||||
                Start(listener);
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
            {
 | 
			
		||||
                Stop();
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
            {
 | 
			
		||||
                Trigger(ResourceTrigger.Terminate);
 | 
			
		||||
                Trigger(ResourceTrigger.Initialize);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //protected override void DataReceived(DistributedConnection sender, NetworkBuffer data)
 | 
			
		||||
        //{
 | 
			
		||||
        //    //throw new NotImplementedException();
 | 
			
		||||
 
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
        //protected override void ClientConnected(DistributedConnection sender)
 | 
			
		||||
        //{
 | 
			
		||||
        //    //Console.WriteLine("DistributedConnection Client Connected");
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //private void ConnectionReadyEventReceiver(DistributedConnection sender)
 | 
			
		||||
        //{
 | 
			
		||||
        //    sender.OnReady -= ConnectionReadyEventReceiver;
 | 
			
		||||
        //    Warehouse.Put(sender, sender.LocalUsername, null, this);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        //public override void RemoveConnection(DistributedConnection connection)
 | 
			
		||||
        //{
 | 
			
		||||
        //    connection.OnReady -= Sender_OnReady;
 | 
			
		||||
        //    //connection.Server = null;
 | 
			
		||||
        //    base.RemoveConnection(connection);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public override void AddConnection(DistributedConnection connection)
 | 
			
		||||
        //{
 | 
			
		||||
        //    connection.OnReady += Sender_OnReady;
 | 
			
		||||
        //    connection.Server = this;
 | 
			
		||||
        //    base.AddConnection(connection);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        protected override void ClientConnected(DistributedConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            //connection.OnReady += ConnectionReadyEventReceiver;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Add(DistributedConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Server = this;
 | 
			
		||||
            connection.ExceptionLevel = ExceptionLevel;
 | 
			
		||||
            base.Add(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Remove(DistributedConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Server = null;
 | 
			
		||||
            base.Remove(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ClientDisconnected(DistributedConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            //connection.OnReady -= ConnectionReadyEventReceiver;
 | 
			
		||||
            //Warehouse.Remove(connection);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public IMembership Membership
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public EntryPoint EntryPoint
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public ushort Port
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    } = 10518;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public ExceptionLevel ExceptionLevel { get; set; }
 | 
			
		||||
        = ExceptionLevel.Code
 | 
			
		||||
        | ExceptionLevel.Source
 | 
			
		||||
        | ExceptionLevel.Message
 | 
			
		||||
        | ExceptionLevel.Trace;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
        {
 | 
			
		||||
            TCPSocket listener;
 | 
			
		||||
 | 
			
		||||
            if (IP != null)
 | 
			
		||||
                listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port));
 | 
			
		||||
            else
 | 
			
		||||
                listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port));
 | 
			
		||||
 | 
			
		||||
            Start(listener);
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
        {
 | 
			
		||||
            Stop();
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
        {
 | 
			
		||||
            Trigger(ResourceTrigger.Terminate);
 | 
			
		||||
            Trigger(ResourceTrigger.Initialize);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //protected override void DataReceived(DistributedConnection sender, NetworkBuffer data)
 | 
			
		||||
    //{
 | 
			
		||||
    //    //throw new NotImplementedException();
 | 
			
		||||
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //protected override void ClientConnected(DistributedConnection sender)
 | 
			
		||||
    //{
 | 
			
		||||
    //    //Console.WriteLine("DistributedConnection Client Connected");
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //private void ConnectionReadyEventReceiver(DistributedConnection sender)
 | 
			
		||||
    //{
 | 
			
		||||
    //    sender.OnReady -= ConnectionReadyEventReceiver;
 | 
			
		||||
    //    Warehouse.Put(sender, sender.LocalUsername, null, this);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //public override void RemoveConnection(DistributedConnection connection)
 | 
			
		||||
    //{
 | 
			
		||||
    //    connection.OnReady -= Sender_OnReady;
 | 
			
		||||
    //    //connection.Server = null;
 | 
			
		||||
    //    base.RemoveConnection(connection);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public override void AddConnection(DistributedConnection connection)
 | 
			
		||||
    //{
 | 
			
		||||
    //    connection.OnReady += Sender_OnReady;
 | 
			
		||||
    //    connection.Server = this;
 | 
			
		||||
    //    base.AddConnection(connection);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected override void ClientConnected(DistributedConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        //connection.OnReady += ConnectionReadyEventReceiver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Add(DistributedConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        connection.Server = this;
 | 
			
		||||
        connection.ExceptionLevel = ExceptionLevel;
 | 
			
		||||
        base.Add(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Remove(DistributedConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        connection.Server = null;
 | 
			
		||||
        base.Remove(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void ClientDisconnected(DistributedConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        //connection.OnReady -= ConnectionReadyEventReceiver;
 | 
			
		||||
        //Warehouse.Remove(connection);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,11 +28,9 @@ using System.Text;
 | 
			
		||||
using Esiur.Net.Sockets;
 | 
			
		||||
using Esiur.Security.Authority;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
public class DistributedSession : NetworkSession
 | 
			
		||||
{
 | 
			
		||||
    public class DistributedSession : NetworkSession
 | 
			
		||||
    {
 | 
			
		||||
        public Source Source { get; set; }
 | 
			
		||||
        public Authentication Authentication { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
    public Source Source { get; set; }
 | 
			
		||||
    public Authentication Authentication { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,12 +29,11 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using Esiur.Resource.Template;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.IIP
 | 
			
		||||
{
 | 
			
		||||
    public abstract class EntryPoint : Esiur.Resource.Resource
 | 
			
		||||
    {
 | 
			
		||||
namespace Esiur.Net.IIP;
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<IResource[]> Query(string path, DistributedConnection sender);
 | 
			
		||||
        protected abstract override bool Create();
 | 
			
		||||
    }
 | 
			
		||||
public abstract class EntryPoint : Esiur.Resource.Resource
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public abstract AsyncReply<IResource[]> Query(string path, DistributedConnection sender);
 | 
			
		||||
    protected abstract override bool Create();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,13 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net
 | 
			
		||||
{
 | 
			
		||||
    public interface INetworkReceiver<T>
 | 
			
		||||
    {
 | 
			
		||||
        void NetworkClose(T sender);
 | 
			
		||||
        void NetworkReceive(T sender, NetworkBuffer buffer);
 | 
			
		||||
        //void NetworkError(T sender);
 | 
			
		||||
namespace Esiur.Net;
 | 
			
		||||
 | 
			
		||||
        void NetworkConnect(T sender);
 | 
			
		||||
    }
 | 
			
		||||
public interface INetworkReceiver<T>
 | 
			
		||||
{
 | 
			
		||||
    void NetworkClose(T sender);
 | 
			
		||||
    void NetworkReceive(T sender, NetworkBuffer buffer);
 | 
			
		||||
    //void NetworkError(T sender);
 | 
			
		||||
 | 
			
		||||
    void NetworkConnect(T sender);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,180 +29,178 @@ using System.Text;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
using Esiur.Misc;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net
 | 
			
		||||
namespace Esiur.Net;
 | 
			
		||||
public class NetworkBuffer
 | 
			
		||||
{
 | 
			
		||||
    public class NetworkBuffer
 | 
			
		||||
    byte[] data;
 | 
			
		||||
 | 
			
		||||
    uint neededDataLength = 0;
 | 
			
		||||
    //bool trim;
 | 
			
		||||
 | 
			
		||||
    object syncLock = new object();
 | 
			
		||||
 | 
			
		||||
    //public object SyncLock
 | 
			
		||||
    //{
 | 
			
		||||
    //  get { return syncLock; }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    public NetworkBuffer()
 | 
			
		||||
    {
 | 
			
		||||
        byte[] data;
 | 
			
		||||
        data = new byte[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        uint neededDataLength = 0;
 | 
			
		||||
        //bool trim;
 | 
			
		||||
 | 
			
		||||
        object syncLock = new object();
 | 
			
		||||
 | 
			
		||||
        //public object SyncLock
 | 
			
		||||
        //{
 | 
			
		||||
          //  get { return syncLock; }
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        public NetworkBuffer()
 | 
			
		||||
    public bool Protected
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            data = new byte[0];
 | 
			
		||||
            return neededDataLength > data.Length;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public bool Protected
 | 
			
		||||
    public uint Available
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            return (uint)data.Length;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //public void HoldForAtLeast(byte[] src, uint offset, uint size, uint needed)
 | 
			
		||||
    //{
 | 
			
		||||
    //    HoldFor(src, offset, size, needed);
 | 
			
		||||
    //    //trim = false;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public void HoldForAtLeast(byte[] src, uint needed)
 | 
			
		||||
    //{
 | 
			
		||||
    //    HoldForAtLeast(src, 0, (uint)src.Length, needed);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    public void HoldForNextWrite(byte[] src)
 | 
			
		||||
    {
 | 
			
		||||
        //HoldForAtLeast(src, (uint)src.Length + 1);
 | 
			
		||||
        HoldFor(src, (uint)src.Length + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void HoldForNextWrite(byte[] src, uint offset, uint size)
 | 
			
		||||
    {
 | 
			
		||||
        //HoldForAtLeast(src, offset, size, size + 1);
 | 
			
		||||
        HoldFor(src, offset, size, size + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void HoldFor(byte[] src, uint offset, uint size, uint needed)
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (size >= needed)
 | 
			
		||||
                throw new Exception("Size >= Needed !");
 | 
			
		||||
 | 
			
		||||
            //trim = true;
 | 
			
		||||
            data = DC.Combine(src, offset, size, data, 0, (uint)data.Length);
 | 
			
		||||
            neededDataLength = needed;
 | 
			
		||||
 | 
			
		||||
            // Console.WriteLine("Hold StackTrace: '{0}'", Environment.StackTrace);
 | 
			
		||||
 | 
			
		||||
            //Console.WriteLine("Holded {0} {1} {2} {3} - {4}", offset, size, needed, data.Length, GetHashCode());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void HoldFor(byte[] src, uint needed)
 | 
			
		||||
    {
 | 
			
		||||
        HoldFor(src, 0, (uint)src.Length, needed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Protect(byte[] data, uint offset, uint needed)//, bool exact = false)
 | 
			
		||||
    {
 | 
			
		||||
        uint dataLength = (uint)data.Length - offset;
 | 
			
		||||
 | 
			
		||||
        // protection
 | 
			
		||||
        if (dataLength < needed)
 | 
			
		||||
        {
 | 
			
		||||
            //if (exact)
 | 
			
		||||
            //    HoldFor(data, offset, dataLength, needed);
 | 
			
		||||
            //else
 | 
			
		||||
            //HoldForAtLeast(data, offset, dataLength, needed);
 | 
			
		||||
            HoldFor(data, offset, dataLength, needed);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Write(byte[] src)
 | 
			
		||||
    {
 | 
			
		||||
        Write(src, 0, (uint)src.Length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Write(byte[] src, uint offset, uint length)
 | 
			
		||||
    {
 | 
			
		||||
        //if (data.Length > 0)
 | 
			
		||||
        //  Console.WriteLine();
 | 
			
		||||
 | 
			
		||||
        lock (syncLock)
 | 
			
		||||
            DC.Append(ref data, src, offset, length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool CanRead
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            if (data.Length == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
            if (data.Length < neededDataLength)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] Read()
 | 
			
		||||
    {
 | 
			
		||||
        lock (syncLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (data.Length == 0)
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
            byte[] rt = null;
 | 
			
		||||
 | 
			
		||||
            if (neededDataLength == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return neededDataLength > data.Length;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public uint Available
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return (uint)data.Length;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //public void HoldForAtLeast(byte[] src, uint offset, uint size, uint needed)
 | 
			
		||||
        //{
 | 
			
		||||
        //    HoldFor(src, offset, size, needed);
 | 
			
		||||
        //    //trim = false;
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public void HoldForAtLeast(byte[] src, uint needed)
 | 
			
		||||
        //{
 | 
			
		||||
        //    HoldForAtLeast(src, 0, (uint)src.Length, needed);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        public void HoldForNextWrite(byte[] src)
 | 
			
		||||
        {
 | 
			
		||||
            //HoldForAtLeast(src, (uint)src.Length + 1);
 | 
			
		||||
            HoldFor(src, (uint)src.Length + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void HoldForNextWrite(byte[] src, uint offset, uint size)
 | 
			
		||||
        {
 | 
			
		||||
            //HoldForAtLeast(src, offset, size, size + 1);
 | 
			
		||||
            HoldFor(src, offset, size, size + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void HoldFor(byte[] src, uint offset, uint size, uint needed)
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (size >= needed)
 | 
			
		||||
                    throw new Exception("Size >= Needed !");
 | 
			
		||||
 | 
			
		||||
                //trim = true;
 | 
			
		||||
                data = DC.Combine(src, offset, size, data, 0, (uint)data.Length);
 | 
			
		||||
                neededDataLength = needed;
 | 
			
		||||
 | 
			
		||||
                // Console.WriteLine("Hold StackTrace: '{0}'", Environment.StackTrace);
 | 
			
		||||
 | 
			
		||||
                //Console.WriteLine("Holded {0} {1} {2} {3} - {4}", offset, size, needed, data.Length, GetHashCode());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void HoldFor(byte[] src, uint needed)
 | 
			
		||||
        {
 | 
			
		||||
            HoldFor(src, 0, (uint)src.Length, needed);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Protect(byte[] data, uint offset, uint needed)//, bool exact = false)
 | 
			
		||||
        {
 | 
			
		||||
            uint dataLength = (uint)data.Length - offset;
 | 
			
		||||
 | 
			
		||||
            // protection
 | 
			
		||||
            if (dataLength < needed)
 | 
			
		||||
            {
 | 
			
		||||
                //if (exact)
 | 
			
		||||
                //    HoldFor(data, offset, dataLength, needed);
 | 
			
		||||
                //else
 | 
			
		||||
                //HoldForAtLeast(data, offset, dataLength, needed);
 | 
			
		||||
                HoldFor(data, offset, dataLength, needed);
 | 
			
		||||
                return true;
 | 
			
		||||
                rt = data;
 | 
			
		||||
                data = new byte[0];
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(byte[] src)
 | 
			
		||||
        {
 | 
			
		||||
            Write(src, 0, (uint)src.Length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Write(byte[] src, uint offset, uint length)
 | 
			
		||||
        {
 | 
			
		||||
            //if (data.Length > 0)
 | 
			
		||||
              //  Console.WriteLine();
 | 
			
		||||
 | 
			
		||||
            lock(syncLock)
 | 
			
		||||
                DC.Append(ref data, src, offset, length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool CanRead
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (data.Length == 0)
 | 
			
		||||
                    return false;
 | 
			
		||||
                if (data.Length < neededDataLength)
 | 
			
		||||
                    return false;
 | 
			
		||||
                //Console.WriteLine("P STATE:" + data.Length + " " + neededDataLength);
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] Read()
 | 
			
		||||
        {
 | 
			
		||||
            lock (syncLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (data.Length == 0)
 | 
			
		||||
                    return null;
 | 
			
		||||
 | 
			
		||||
                byte[] rt = null;
 | 
			
		||||
 | 
			
		||||
                if (neededDataLength == 0)
 | 
			
		||||
                if (data.Length >= neededDataLength)
 | 
			
		||||
                {
 | 
			
		||||
                    //Console.WriteLine("data.Length >= neededDataLength " + data.Length + " >= " + neededDataLength + " " + trim);
 | 
			
		||||
 | 
			
		||||
                    //if (trim)
 | 
			
		||||
                    //{
 | 
			
		||||
                    //  rt = DC.Clip(data, 0, neededDataLength);
 | 
			
		||||
                    //  data = DC.Clip(data, neededDataLength, (uint)data.Length - neededDataLength);
 | 
			
		||||
                    //}
 | 
			
		||||
                    //else
 | 
			
		||||
                    //{
 | 
			
		||||
                    // return all data
 | 
			
		||||
                    rt = data;
 | 
			
		||||
                    data = new byte[0];
 | 
			
		||||
                    //}
 | 
			
		||||
 | 
			
		||||
                    neededDataLength = 0;
 | 
			
		||||
                    return rt;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //Console.WriteLine("P STATE:" + data.Length + " " + neededDataLength);
 | 
			
		||||
 | 
			
		||||
                    if (data.Length >= neededDataLength)
 | 
			
		||||
                    {
 | 
			
		||||
                        //Console.WriteLine("data.Length >= neededDataLength " + data.Length + " >= " + neededDataLength + " " + trim);
 | 
			
		||||
 | 
			
		||||
                        //if (trim)
 | 
			
		||||
                        //{
 | 
			
		||||
                        //  rt = DC.Clip(data, 0, neededDataLength);
 | 
			
		||||
                        //  data = DC.Clip(data, neededDataLength, (uint)data.Length - neededDataLength);
 | 
			
		||||
                        //}
 | 
			
		||||
                        //else
 | 
			
		||||
                        //{
 | 
			
		||||
                        // return all data
 | 
			
		||||
                        rt = data;
 | 
			
		||||
                        data = new byte[0];
 | 
			
		||||
                        //}
 | 
			
		||||
 | 
			
		||||
                        neededDataLength = 0;
 | 
			
		||||
                        return rt;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        return null;
 | 
			
		||||
                    }
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                return rt;
 | 
			
		||||
           }
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,332 +36,330 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Net.Sockets;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net
 | 
			
		||||
namespace Esiur.Net;
 | 
			
		||||
public abstract class NetworkConnection : IDestructible, INetworkReceiver<ISocket>// <TS>: IResource where TS : NetworkSession
 | 
			
		||||
{
 | 
			
		||||
    public abstract class NetworkConnection: IDestructible, INetworkReceiver<ISocket>// <TS>: IResource where TS : NetworkSession
 | 
			
		||||
    private Sockets.ISocket sock;
 | 
			
		||||
    //        private bool connected;
 | 
			
		||||
 | 
			
		||||
    private DateTime lastAction;
 | 
			
		||||
 | 
			
		||||
    //public delegate void DataReceivedEvent(NetworkConnection sender, NetworkBuffer data);
 | 
			
		||||
    //public delegate void ConnectionClosedEvent(NetworkConnection sender);
 | 
			
		||||
    public delegate void NetworkConnectionEvent(NetworkConnection connection);
 | 
			
		||||
 | 
			
		||||
    public event NetworkConnectionEvent OnConnect;
 | 
			
		||||
    //public event DataReceivedEvent OnDataReceived;
 | 
			
		||||
    public event NetworkConnectionEvent OnClose;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
    //object receivingLock = new object();
 | 
			
		||||
 | 
			
		||||
    //object sendLock = new object();
 | 
			
		||||
 | 
			
		||||
    bool processing = false;
 | 
			
		||||
 | 
			
		||||
    // public INetworkReceiver<NetworkConnection> Receiver { get; set; }
 | 
			
		||||
 | 
			
		||||
    public virtual void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        private Sockets.ISocket sock;
 | 
			
		||||
        //        private bool connected;
 | 
			
		||||
        // remove references
 | 
			
		||||
        //sock.OnClose -= Socket_OnClose;
 | 
			
		||||
        //sock.OnConnect -= Socket_OnConnect;
 | 
			
		||||
        //sock.OnReceive -= Socket_OnReceive;
 | 
			
		||||
        sock?.Destroy();
 | 
			
		||||
        //Receiver = null;
 | 
			
		||||
        Close();
 | 
			
		||||
        sock = null;
 | 
			
		||||
 | 
			
		||||
        private DateTime lastAction;
 | 
			
		||||
        OnClose = null;
 | 
			
		||||
        OnConnect = null;
 | 
			
		||||
        //OnDataReceived = null;
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
        OnDestroy = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        //public delegate void DataReceivedEvent(NetworkConnection sender, NetworkBuffer data);
 | 
			
		||||
        //public delegate void ConnectionClosedEvent(NetworkConnection sender);
 | 
			
		||||
        public delegate void NetworkConnectionEvent(NetworkConnection connection);
 | 
			
		||||
 | 
			
		||||
        public event NetworkConnectionEvent OnConnect;
 | 
			
		||||
        //public event DataReceivedEvent OnDataReceived;
 | 
			
		||||
        public event NetworkConnectionEvent OnClose;
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        //object receivingLock = new object();
 | 
			
		||||
 | 
			
		||||
        //object sendLock = new object();
 | 
			
		||||
 | 
			
		||||
        bool processing = false;
 | 
			
		||||
 | 
			
		||||
       // public INetworkReceiver<NetworkConnection> Receiver { get; set; }
 | 
			
		||||
 | 
			
		||||
        public virtual void Destroy()
 | 
			
		||||
    public ISocket Socket
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            // remove references
 | 
			
		||||
            return sock;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void Assign(ISocket socket)
 | 
			
		||||
    {
 | 
			
		||||
        lastAction = DateTime.Now;
 | 
			
		||||
        sock = socket;
 | 
			
		||||
        sock.Receiver = this;
 | 
			
		||||
 | 
			
		||||
        //socket.OnReceive += Socket_OnReceive;
 | 
			
		||||
        //socket.OnClose += Socket_OnClose;
 | 
			
		||||
        //socket.OnConnect += Socket_OnConnect;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //private void Socket_OnConnect()
 | 
			
		||||
    //{
 | 
			
		||||
    //    OnConnect?.Invoke(this);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //private void Socket_OnClose()
 | 
			
		||||
    //{
 | 
			
		||||
    //    ConnectionClosed();
 | 
			
		||||
    //    OnClose?.Invoke(this);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //protected virtual void ConnectionClosed()
 | 
			
		||||
    //{
 | 
			
		||||
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //private void Socket_OnReceive(NetworkBuffer buffer)
 | 
			
		||||
    //{
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    public ISocket Unassign()
 | 
			
		||||
    {
 | 
			
		||||
        if (sock != null)
 | 
			
		||||
        {
 | 
			
		||||
            // connected = false;
 | 
			
		||||
            //sock.OnClose -= Socket_OnClose;
 | 
			
		||||
            //sock.OnConnect -= Socket_OnConnect;
 | 
			
		||||
            //sock.OnReceive -= Socket_OnReceive;
 | 
			
		||||
            sock?.Destroy();
 | 
			
		||||
            //Receiver = null;
 | 
			
		||||
            Close();
 | 
			
		||||
            sock.Receiver = null;
 | 
			
		||||
 | 
			
		||||
            var rt = sock;
 | 
			
		||||
            sock = null;
 | 
			
		||||
 | 
			
		||||
            OnClose = null;
 | 
			
		||||
            OnConnect = null;
 | 
			
		||||
            //OnDataReceived = null;
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
            OnDestroy = null;
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public ISocket Socket
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return sock;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    //protected virtual void DataReceived(NetworkBuffer data)
 | 
			
		||||
    //{
 | 
			
		||||
    //    if (OnDataReceived != null)
 | 
			
		||||
    //    {
 | 
			
		||||
    //        try
 | 
			
		||||
    //        {
 | 
			
		||||
    //            OnDataReceived?.Invoke(this, data);
 | 
			
		||||
    //        }
 | 
			
		||||
    //        catch (Exception ex)
 | 
			
		||||
    //        {
 | 
			
		||||
    //            Global.Log("NetworkConenction:DataReceived", LogType.Error, ex.ToString());
 | 
			
		||||
    //        }
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
        public virtual void Assign(ISocket socket)
 | 
			
		||||
        {
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
            sock = socket;
 | 
			
		||||
            sock.Receiver = this;
 | 
			
		||||
    public void Close()
 | 
			
		||||
    {
 | 
			
		||||
        //if (!connected)
 | 
			
		||||
        //  return;
 | 
			
		||||
 | 
			
		||||
            //socket.OnReceive += Socket_OnReceive;
 | 
			
		||||
            //socket.OnClose += Socket_OnClose;
 | 
			
		||||
            //socket.OnConnect += Socket_OnConnect;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //private void Socket_OnConnect()
 | 
			
		||||
        //{
 | 
			
		||||
        //    OnConnect?.Invoke(this);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //private void Socket_OnClose()
 | 
			
		||||
        //{
 | 
			
		||||
        //    ConnectionClosed();
 | 
			
		||||
        //    OnClose?.Invoke(this);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //protected virtual void ConnectionClosed()
 | 
			
		||||
        //{
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //private void Socket_OnReceive(NetworkBuffer buffer)
 | 
			
		||||
        //{
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        public ISocket Unassign()
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (sock != null)
 | 
			
		||||
            {
 | 
			
		||||
                // connected = false;
 | 
			
		||||
                //sock.OnClose -= Socket_OnClose;
 | 
			
		||||
                //sock.OnConnect -= Socket_OnConnect;
 | 
			
		||||
                //sock.OnReceive -= Socket_OnReceive;
 | 
			
		||||
                sock.Receiver = null;
 | 
			
		||||
                sock.Close();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Global.Log("NetworkConenction:Close", LogType.Error, ex.ToString());
 | 
			
		||||
 | 
			
		||||
                var rt = sock;
 | 
			
		||||
                sock = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                return rt;
 | 
			
		||||
            }
 | 
			
		||||
        //finally
 | 
			
		||||
        //{
 | 
			
		||||
        //connected = false;
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DateTime LastAction
 | 
			
		||||
    {
 | 
			
		||||
        get { return lastAction; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IPEndPoint RemoteEndPoint
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            if (sock != null)
 | 
			
		||||
                return (IPEndPoint)sock.RemoteEndPoint;
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        //protected virtual void DataReceived(NetworkBuffer data)
 | 
			
		||||
        //{
 | 
			
		||||
        //    if (OnDataReceived != null)
 | 
			
		||||
        //    {
 | 
			
		||||
        //        try
 | 
			
		||||
        //        {
 | 
			
		||||
        //            OnDataReceived?.Invoke(this, data);
 | 
			
		||||
        //        }
 | 
			
		||||
        //        catch (Exception ex)
 | 
			
		||||
        //        {
 | 
			
		||||
        //            Global.Log("NetworkConenction:DataReceived", LogType.Error, ex.ToString());
 | 
			
		||||
        //        }
 | 
			
		||||
        //    }
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        public void Close()
 | 
			
		||||
    public IPEndPoint LocalEndPoint
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            //if (!connected)
 | 
			
		||||
            //  return;
 | 
			
		||||
            if (sock != null)
 | 
			
		||||
                return (IPEndPoint)sock.LocalEndPoint;
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
    public bool IsConnected
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return sock.State == SocketState.Established;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public void CloseAndWait()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (!connected)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
                if (sock != null)
 | 
			
		||||
                    sock.Close();
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Global.Log("NetworkConenction:Close", LogType.Error, ex.ToString());
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //finally
 | 
			
		||||
            //{
 | 
			
		||||
            //connected = false;
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DateTime LastAction
 | 
			
		||||
        {
 | 
			
		||||
            get { return lastAction; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IPEndPoint RemoteEndPoint
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (sock != null)
 | 
			
		||||
                    return (IPEndPoint)sock.RemoteEndPoint;
 | 
			
		||||
                else
 | 
			
		||||
                    return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IPEndPoint LocalEndPoint
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (sock != null)
 | 
			
		||||
                    return (IPEndPoint)sock.LocalEndPoint;
 | 
			
		||||
                else
 | 
			
		||||
                    return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool IsConnected
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return sock.State == SocketState.Established;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public void CloseAndWait()
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (!connected)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                    if (sock != null)
 | 
			
		||||
                        sock.Close();
 | 
			
		||||
 | 
			
		||||
                    while (connected)
 | 
			
		||||
                    {
 | 
			
		||||
                        Thread.Sleep(100);
 | 
			
		||||
                    }
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        public virtual AsyncReply<bool> SendAsync(byte[] message, int offset, int length)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                lastAction = DateTime.Now;
 | 
			
		||||
                return sock.SendAsync(message, offset, length);
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return new AsyncReply<bool>(false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void Send(byte[] msg)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {                
 | 
			
		||||
                sock?.Send(msg);       
 | 
			
		||||
                lastAction = DateTime.Now;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void Send(byte[] msg, int offset, int length)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                sock.Send(msg, offset, length);
 | 
			
		||||
                lastAction = DateTime.Now;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void Send(string data)
 | 
			
		||||
        {
 | 
			
		||||
            Send(Encoding.UTF8.GetBytes(data));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public  void NetworkClose(ISocket socket)
 | 
			
		||||
        {
 | 
			
		||||
            Disconencted();
 | 
			
		||||
            OnClose?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void NetworkConnect(ISocket socket)
 | 
			
		||||
        {
 | 
			
		||||
            Connected();
 | 
			
		||||
            OnConnect?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //{
 | 
			
		||||
        //ConnectionClosed();
 | 
			
		||||
        //OnClose?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        //Receiver?.NetworkClose(this);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public void NetworkConenct(ISocket sender)
 | 
			
		||||
        //{
 | 
			
		||||
        //    OnConnect?.Invoke(this);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        protected abstract void DataReceived(NetworkBuffer buffer);
 | 
			
		||||
        protected abstract void Connected();
 | 
			
		||||
        protected abstract void Disconencted();
 | 
			
		||||
 | 
			
		||||
        public void NetworkReceive(ISocket sender, NetworkBuffer buffer)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Unassigned ?
 | 
			
		||||
                if (sock == null)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                // Closed ?
 | 
			
		||||
                if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated) // || !connected)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                lastAction = DateTime.Now;
 | 
			
		||||
 | 
			
		||||
                if (!processing)
 | 
			
		||||
                while (connected)
 | 
			
		||||
                {
 | 
			
		||||
                    processing = true;
 | 
			
		||||
                    Thread.Sleep(100);
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public virtual AsyncReply<bool> SendAsync(byte[] message, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
            return sock.SendAsync(message, offset, length);
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return new AsyncReply<bool>(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void Send(byte[] msg)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            sock?.Send(msg);
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void Send(byte[] msg, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            sock.Send(msg, offset, length);
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void Send(string data)
 | 
			
		||||
    {
 | 
			
		||||
        Send(Encoding.UTF8.GetBytes(data));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void NetworkClose(ISocket socket)
 | 
			
		||||
    {
 | 
			
		||||
        Disconencted();
 | 
			
		||||
        OnClose?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void NetworkConnect(ISocket socket)
 | 
			
		||||
    {
 | 
			
		||||
        Connected();
 | 
			
		||||
        OnConnect?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //{
 | 
			
		||||
    //ConnectionClosed();
 | 
			
		||||
    //OnClose?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
    //Receiver?.NetworkClose(this);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public void NetworkConenct(ISocket sender)
 | 
			
		||||
    //{
 | 
			
		||||
    //    OnConnect?.Invoke(this);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    protected abstract void DataReceived(NetworkBuffer buffer);
 | 
			
		||||
    protected abstract void Connected();
 | 
			
		||||
    protected abstract void Disconencted();
 | 
			
		||||
 | 
			
		||||
    public void NetworkReceive(ISocket sender, NetworkBuffer buffer)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            // Unassigned ?
 | 
			
		||||
            if (sock == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            // Closed ?
 | 
			
		||||
            if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated) // || !connected)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
 | 
			
		||||
            if (!processing)
 | 
			
		||||
            {
 | 
			
		||||
                processing = true;
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    //lock(buffer.SyncLock)
 | 
			
		||||
                    while (buffer.Available > 0 && !buffer.Protected)
 | 
			
		||||
                    {
 | 
			
		||||
                        //lock(buffer.SyncLock)
 | 
			
		||||
                        while (buffer.Available > 0 && !buffer.Protected)
 | 
			
		||||
                        {
 | 
			
		||||
                            //Receiver?.NetworkReceive(this, buffer);
 | 
			
		||||
                            DataReceived(buffer);
 | 
			
		||||
                        }
 | 
			
		||||
                        //Receiver?.NetworkReceive(this, buffer);
 | 
			
		||||
                        DataReceived(buffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    processing = false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                processing = false;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Global.Log("NetworkConnection", LogType.Warning, ex.ToString());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Global.Log("NetworkConnection", LogType.Warning, ex.ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //{
 | 
			
		||||
          //  Receiver?.NetworkError(this);
 | 
			
		||||
            //throw new NotImplementedException();
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public void NetworkConnect(ISocket sender)
 | 
			
		||||
        //{
 | 
			
		||||
          //  Receiver?.NetworkConnect(this);
 | 
			
		||||
            //throw new NotImplementedException();
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //{
 | 
			
		||||
    //  Receiver?.NetworkError(this);
 | 
			
		||||
    //throw new NotImplementedException();
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public void NetworkConnect(ISocket sender)
 | 
			
		||||
    //{
 | 
			
		||||
    //  Receiver?.NetworkConnect(this);
 | 
			
		||||
    //throw new NotImplementedException();
 | 
			
		||||
    //}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,279 +33,277 @@ using Esiur.Resource;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net
 | 
			
		||||
namespace Esiur.Net;
 | 
			
		||||
 | 
			
		||||
public abstract class NetworkServer<TConnection> : IDestructible where TConnection : NetworkConnection, new()
 | 
			
		||||
{
 | 
			
		||||
    //private bool isRunning;
 | 
			
		||||
    private Sockets.ISocket listener;
 | 
			
		||||
    public AutoList<TConnection, NetworkServer<TConnection>> Connections { get; internal set; }
 | 
			
		||||
 | 
			
		||||
    public abstract class NetworkServer<TConnection> : IDestructible where TConnection : NetworkConnection, new()
 | 
			
		||||
    private Thread thread;
 | 
			
		||||
 | 
			
		||||
    //protected abstract void DataReceived(TConnection sender, NetworkBuffer data);
 | 
			
		||||
    //protected abstract void ClientConnected(TConnection sender);
 | 
			
		||||
    //protected abstract void ClientDisconnected(TConnection sender);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private Timer timer;
 | 
			
		||||
    //public KeyList<string, TSession> Sessions = new KeyList<string, TSession>();
 | 
			
		||||
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    //public AutoList<TConnection, NetworkServer<TConnection>> Connections => connections;
 | 
			
		||||
 | 
			
		||||
    private void MinuteThread(object state)
 | 
			
		||||
    {
 | 
			
		||||
        //private bool isRunning;
 | 
			
		||||
        private Sockets.ISocket listener;
 | 
			
		||||
        public AutoList<TConnection, NetworkServer<TConnection>> Connections { get; private set; }
 | 
			
		||||
 | 
			
		||||
        private Thread thread;
 | 
			
		||||
 | 
			
		||||
        //protected abstract void DataReceived(TConnection sender, NetworkBuffer data);
 | 
			
		||||
        //protected abstract void ClientConnected(TConnection sender);
 | 
			
		||||
        //protected abstract void ClientDisconnected(TConnection sender);
 | 
			
		||||
        List<TConnection> ToBeClosed = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private Timer timer;
 | 
			
		||||
        //public KeyList<string, TSession> Sessions = new KeyList<string, TSession>();
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        //public AutoList<TConnection, NetworkServer<TConnection>> Connections => connections;
 | 
			
		||||
 | 
			
		||||
        private void MinuteThread(object state)
 | 
			
		||||
        lock (Connections.SyncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            List<TConnection> ToBeClosed = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            lock (Connections.SyncRoot)
 | 
			
		||||
            foreach (TConnection c in Connections)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (TConnection c in Connections)
 | 
			
		||||
                if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= Timeout)
 | 
			
		||||
                {
 | 
			
		||||
                    if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= Timeout)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (ToBeClosed == null)
 | 
			
		||||
                            ToBeClosed = new List<TConnection>();
 | 
			
		||||
                        ToBeClosed.Add(c);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (ToBeClosed == null)
 | 
			
		||||
                        ToBeClosed = new List<TConnection>();
 | 
			
		||||
                    ToBeClosed.Add(c);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ToBeClosed != null)
 | 
			
		||||
            {
 | 
			
		||||
                //Console.WriteLine("Term: " + ToBeClosed.Count + " " + this.listener.LocalEndPoint.ToString());
 | 
			
		||||
                foreach (TConnection c in ToBeClosed)
 | 
			
		||||
                    c.Close();// CloseAndWait();
 | 
			
		||||
 | 
			
		||||
                ToBeClosed.Clear();
 | 
			
		||||
                ToBeClosed = null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Start(Sockets.ISocket socket)//, uint timeout, uint clock)
 | 
			
		||||
        if (ToBeClosed != null)
 | 
			
		||||
        {
 | 
			
		||||
            if (listener != null)
 | 
			
		||||
                return;
 | 
			
		||||
            //Console.WriteLine("Term: " + ToBeClosed.Count + " " + this.listener.LocalEndPoint.ToString());
 | 
			
		||||
            foreach (TConnection c in ToBeClosed)
 | 
			
		||||
                c.Close();// CloseAndWait();
 | 
			
		||||
 | 
			
		||||
            ToBeClosed.Clear();
 | 
			
		||||
            ToBeClosed = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Start(Sockets.ISocket socket)//, uint timeout, uint clock)
 | 
			
		||||
    {
 | 
			
		||||
        if (listener != null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Connections = new AutoList<TConnection, NetworkServer<TConnection>>(this);
 | 
			
		||||
        Connections = new AutoList<TConnection, NetworkServer<TConnection>>(this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (Timeout > 0 & Clock > 0)
 | 
			
		||||
        if (Timeout > 0 & Clock > 0)
 | 
			
		||||
        {
 | 
			
		||||
            timer = new Timer(MinuteThread, null, TimeSpan.FromMinutes(0), TimeSpan.FromSeconds(Clock));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        listener = socket;
 | 
			
		||||
 | 
			
		||||
        // Start accepting
 | 
			
		||||
        //var r = listener.Accept();
 | 
			
		||||
        //r.Then(NewConnection);
 | 
			
		||||
        //r.timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
        //var rt = listener.Accept().Then()
 | 
			
		||||
        thread = new Thread(new ThreadStart(() =>
 | 
			
		||||
        {
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                timer = new Timer(MinuteThread, null, TimeSpan.FromMinutes(0), TimeSpan.FromSeconds(Clock));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            listener = socket;
 | 
			
		||||
 | 
			
		||||
            // Start accepting
 | 
			
		||||
            //var r = listener.Accept();
 | 
			
		||||
            //r.Then(NewConnection);
 | 
			
		||||
            //r.timeout?.Dispose();
 | 
			
		||||
 | 
			
		||||
            //var rt = listener.Accept().Then()
 | 
			
		||||
            thread = new Thread(new ThreadStart(() =>
 | 
			
		||||
            {
 | 
			
		||||
                while (true)
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    var s = listener.Accept();
 | 
			
		||||
 | 
			
		||||
                    if (s == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var s = listener.Accept();
 | 
			
		||||
                        Console.Write("sock == null");
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                        if (s == null)
 | 
			
		||||
                        {
 | 
			
		||||
                            Console.Write("sock == null");
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        var c = new TConnection();
 | 
			
		||||
                    var c = new TConnection();
 | 
			
		||||
                        //c.OnClose += ClientDisconnectedEventReceiver;
 | 
			
		||||
                        c.Assign(s);
 | 
			
		||||
                        Add(c);
 | 
			
		||||
                    Add(c);
 | 
			
		||||
                        //Connections.Add(c);
 | 
			
		||||
                        
 | 
			
		||||
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                    {
 | 
			
		||||
                            //ClientConnected(c);
 | 
			
		||||
                            ClientConnected(c);
 | 
			
		||||
                            //NetworkConnect(c);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch
 | 
			
		||||
                        {
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                            // something wrong with the child.
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        s.Begin();
 | 
			
		||||
                    s.Begin();
 | 
			
		||||
 | 
			
		||||
                        // Accept more
 | 
			
		||||
                        //listener.Accept().Then(NewConnection);
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        Console.WriteLine(ex);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            thread.Start();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public uint Timeout
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public uint Clock
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Stop()
 | 
			
		||||
        {
 | 
			
		||||
            var port = 0;
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (listener != null)
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    port = listener.LocalEndPoint.Port;
 | 
			
		||||
                    listener.Close();
 | 
			
		||||
                    Console.WriteLine(ex);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // wait until the listener stops
 | 
			
		||||
                //while (isRunning)
 | 
			
		||||
                //{
 | 
			
		||||
                //  Thread.Sleep(100);
 | 
			
		||||
                //}
 | 
			
		||||
 | 
			
		||||
                //Console.WriteLine("Listener stopped");
 | 
			
		||||
 | 
			
		||||
                var cons = Connections.ToArray();
 | 
			
		||||
 | 
			
		||||
                //lock (connections.SyncRoot)
 | 
			
		||||
                //{
 | 
			
		||||
                foreach (TConnection con in cons)
 | 
			
		||||
                    con.Close();
 | 
			
		||||
                //}
 | 
			
		||||
 | 
			
		||||
                //Console.WriteLine("Sockets Closed");
 | 
			
		||||
 | 
			
		||||
                //while (connections.Count > 0)
 | 
			
		||||
                //{
 | 
			
		||||
                //    Console.WriteLine("Waiting... " + connections.Count);  
 | 
			
		||||
                //    Thread.Sleep(1000);
 | 
			
		||||
                //}
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        thread.Start();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public uint Timeout
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public uint Clock
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Stop()
 | 
			
		||||
    {
 | 
			
		||||
        var port = 0;
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (listener != null)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine("Server@{0} is down", port);
 | 
			
		||||
                port = listener.LocalEndPoint.Port;
 | 
			
		||||
                listener.Close();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // wait until the listener stops
 | 
			
		||||
            //while (isRunning)
 | 
			
		||||
            //{
 | 
			
		||||
            //  Thread.Sleep(100);
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
            //Console.WriteLine("Listener stopped");
 | 
			
		||||
 | 
			
		||||
            var cons = Connections.ToArray();
 | 
			
		||||
 | 
			
		||||
            //lock (connections.SyncRoot)
 | 
			
		||||
            //{
 | 
			
		||||
            foreach (TConnection con in cons)
 | 
			
		||||
                con.Close();
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
            //Console.WriteLine("Sockets Closed");
 | 
			
		||||
 | 
			
		||||
            //while (connections.Count > 0)
 | 
			
		||||
            //{
 | 
			
		||||
            //    Console.WriteLine("Waiting... " + connections.Count);  
 | 
			
		||||
            //    Thread.Sleep(1000);
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public virtual void Remove(TConnection connection)
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            //connection.OnDataReceived -= OnDataReceived;
 | 
			
		||||
            //connection.OnConnect -= OnClientConnect;
 | 
			
		||||
            connection.OnClose -= ClientDisconnectedEventReceiver;
 | 
			
		||||
            Connections.Remove(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void Add(TConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            //connection.OnDataReceived += OnDataReceived;
 | 
			
		||||
            //connection.OnConnect += OnClientConnect;
 | 
			
		||||
            connection.OnClose += ClientDisconnectedEventReceiver;// OnClientClose;
 | 
			
		||||
            Connections.Add(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool IsRunning
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return listener.State == SocketState.Listening;
 | 
			
		||||
                //isRunning; 
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //public void OnDataReceived(ISocket sender, NetworkBuffer data)
 | 
			
		||||
        //{
 | 
			
		||||
        //    DataReceived((TConnection)sender, data);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public void OnClientConnect(ISocket sender)
 | 
			
		||||
        //{
 | 
			
		||||
        //    if (sender == null)
 | 
			
		||||
        //        return;
 | 
			
		||||
 | 
			
		||||
        //    if (sender.RemoteEndPoint == null || sender.LocalEndPoint == null)
 | 
			
		||||
        //    { }
 | 
			
		||||
        //    //Console.WriteLine("NULL");
 | 
			
		||||
        //    else
 | 
			
		||||
        //        Global.Log("Connections", LogType.Debug, sender.RemoteEndPoint.Address.ToString()
 | 
			
		||||
        //            + "->" + sender.LocalEndPoint.Port + " at " + DateTime.UtcNow.ToString("d")
 | 
			
		||||
        //            + " " + DateTime.UtcNow.ToString("d"), false);
 | 
			
		||||
 | 
			
		||||
        //    // Console.WriteLine("Connected " + sender.RemoteEndPoint.ToString());
 | 
			
		||||
        //    ClientConnected((TConnection)sender);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public void OnClientClose(ISocket sender)
 | 
			
		||||
        //{
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            Stop();
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ClientDisconnectedEventReceiver(NetworkConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var con = connection as TConnection;
 | 
			
		||||
                con.Destroy();
 | 
			
		||||
                //                con.OnClose -= ClientDisconnectedEventReceiver;
 | 
			
		||||
 | 
			
		||||
                Remove(con);
 | 
			
		||||
 | 
			
		||||
                //Connections.Remove(con);
 | 
			
		||||
                ClientDisconnected(con);
 | 
			
		||||
                //RemoveConnection((TConnection)sender);
 | 
			
		||||
                //connections.Remove(sender)
 | 
			
		||||
                //ClientDisconnected((TConnection)sender);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Global.Log("NetworkServer:OnClientDisconnect", LogType.Error, ex.ToString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected abstract void ClientDisconnected(TConnection connection);
 | 
			
		||||
        protected abstract void ClientConnected(TConnection connection);
 | 
			
		||||
 | 
			
		||||
        ~NetworkServer()
 | 
			
		||||
        {
 | 
			
		||||
            Stop();
 | 
			
		||||
            listener = null;
 | 
			
		||||
            Console.WriteLine("Server@{0} is down", port);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public virtual void Remove(TConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        //connection.OnDataReceived -= OnDataReceived;
 | 
			
		||||
        //connection.OnConnect -= OnClientConnect;
 | 
			
		||||
        connection.OnClose -= ClientDisconnectedEventReceiver;
 | 
			
		||||
        Connections.Remove(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public virtual void Add(TConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        //connection.OnDataReceived += OnDataReceived;
 | 
			
		||||
        //connection.OnConnect += OnClientConnect;
 | 
			
		||||
        connection.OnClose += ClientDisconnectedEventReceiver;// OnClientClose;
 | 
			
		||||
        Connections.Add(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool IsRunning
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return listener.State == SocketState.Listening;
 | 
			
		||||
            //isRunning; 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //public void OnDataReceived(ISocket sender, NetworkBuffer data)
 | 
			
		||||
    //{
 | 
			
		||||
    //    DataReceived((TConnection)sender, data);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public void OnClientConnect(ISocket sender)
 | 
			
		||||
    //{
 | 
			
		||||
    //    if (sender == null)
 | 
			
		||||
    //        return;
 | 
			
		||||
 | 
			
		||||
    //    if (sender.RemoteEndPoint == null || sender.LocalEndPoint == null)
 | 
			
		||||
    //    { }
 | 
			
		||||
    //    //Console.WriteLine("NULL");
 | 
			
		||||
    //    else
 | 
			
		||||
    //        Global.Log("Connections", LogType.Debug, sender.RemoteEndPoint.Address.ToString()
 | 
			
		||||
    //            + "->" + sender.LocalEndPoint.Port + " at " + DateTime.UtcNow.ToString("d")
 | 
			
		||||
    //            + " " + DateTime.UtcNow.ToString("d"), false);
 | 
			
		||||
 | 
			
		||||
    //    // Console.WriteLine("Connected " + sender.RemoteEndPoint.ToString());
 | 
			
		||||
    //    ClientConnected((TConnection)sender);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public void OnClientClose(ISocket sender)
 | 
			
		||||
    //{
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        Stop();
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ClientDisconnectedEventReceiver(NetworkConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var con = connection as TConnection;
 | 
			
		||||
            con.Destroy();
 | 
			
		||||
            //                con.OnClose -= ClientDisconnectedEventReceiver;
 | 
			
		||||
 | 
			
		||||
            Remove(con);
 | 
			
		||||
 | 
			
		||||
            //Connections.Remove(con);
 | 
			
		||||
            ClientDisconnected(con);
 | 
			
		||||
            //RemoveConnection((TConnection)sender);
 | 
			
		||||
            //connections.Remove(sender)
 | 
			
		||||
            //ClientDisconnected((TConnection)sender);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Global.Log("NetworkServer:OnClientDisconnect", LogType.Error, ex.ToString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract void ClientDisconnected(TConnection connection);
 | 
			
		||||
    protected abstract void ClientConnected(TConnection connection);
 | 
			
		||||
 | 
			
		||||
    ~NetworkServer()
 | 
			
		||||
    {
 | 
			
		||||
        Stop();
 | 
			
		||||
        listener = null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,96 +35,94 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Misc;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net
 | 
			
		||||
namespace Esiur.Net;
 | 
			
		||||
public class NetworkSession : IDestructible //<T> where T : TClient
 | 
			
		||||
{
 | 
			
		||||
    public class NetworkSession:IDestructible //<T> where T : TClient
 | 
			
		||||
    public delegate void SessionModifiedEvent(NetworkSession session, string key, object oldValue, object newValue);
 | 
			
		||||
    public delegate void SessionEndedEvent(NetworkSession session);
 | 
			
		||||
 | 
			
		||||
    private string id;
 | 
			
		||||
    private Timer timer;
 | 
			
		||||
    private int timeout;
 | 
			
		||||
    DateTime creation;
 | 
			
		||||
    DateTime lastAction;
 | 
			
		||||
 | 
			
		||||
    private KeyList<string, object> variables;
 | 
			
		||||
 | 
			
		||||
    public event SessionEndedEvent OnEnd;
 | 
			
		||||
    public event SessionModifiedEvent OnModify;
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    public KeyList<string, object> Variables
 | 
			
		||||
    {
 | 
			
		||||
        public delegate void SessionModifiedEvent(NetworkSession session, string key, object oldValue, object newValue);
 | 
			
		||||
        public delegate void SessionEndedEvent(NetworkSession session);
 | 
			
		||||
        get { return variables; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        private string id;
 | 
			
		||||
        private Timer timer;
 | 
			
		||||
        private int timeout;
 | 
			
		||||
        DateTime creation;
 | 
			
		||||
        DateTime lastAction;
 | 
			
		||||
    public NetworkSession()
 | 
			
		||||
    {
 | 
			
		||||
        variables = new KeyList<string, object>();
 | 
			
		||||
        variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
 | 
			
		||||
        creation = DateTime.Now;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        private KeyList<string, object> variables;
 | 
			
		||||
    internal void Set(string id, int timeout)
 | 
			
		||||
    {
 | 
			
		||||
        //modified = sessionModifiedEvent;
 | 
			
		||||
        //ended = sessionEndEvent;
 | 
			
		||||
        this.id = id;
 | 
			
		||||
 | 
			
		||||
        public event SessionEndedEvent OnEnd;
 | 
			
		||||
        public event SessionModifiedEvent OnModify;
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public KeyList<string, object> Variables
 | 
			
		||||
        if (this.timeout != 0)
 | 
			
		||||
        {
 | 
			
		||||
            get { return variables; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public NetworkSession()
 | 
			
		||||
        {
 | 
			
		||||
            variables = new KeyList<string, object>();
 | 
			
		||||
            variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
 | 
			
		||||
            this.timeout = timeout;
 | 
			
		||||
            timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
            creation = DateTime.Now;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        internal void Set(string id, int timeout )
 | 
			
		||||
        {
 | 
			
		||||
            //modified = sessionModifiedEvent;
 | 
			
		||||
            //ended = sessionEndEvent;
 | 
			
		||||
            this.id = id;
 | 
			
		||||
 | 
			
		||||
            if (this.timeout != 0)
 | 
			
		||||
            {
 | 
			
		||||
                this.timeout = timeout;
 | 
			
		||||
                timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
                creation = DateTime.Now;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnSessionEndTimerCallback(object o)
 | 
			
		||||
        {
 | 
			
		||||
            OnEnd?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender)
 | 
			
		||||
        {
 | 
			
		||||
            OnModify?.Invoke(this, key, oldValue, newValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
            timer.Dispose();
 | 
			
		||||
            timer = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal void Refresh()
 | 
			
		||||
        {
 | 
			
		||||
            lastAction = DateTime.Now;
 | 
			
		||||
            timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Timeout // Seconds
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return timeout;
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                timeout = value;
 | 
			
		||||
                Refresh();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Id
 | 
			
		||||
        {
 | 
			
		||||
            get { return id; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DateTime LastAction
 | 
			
		||||
        {
 | 
			
		||||
            get { return lastAction; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
    private void OnSessionEndTimerCallback(object o)
 | 
			
		||||
    {
 | 
			
		||||
        OnEnd?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender)
 | 
			
		||||
    {
 | 
			
		||||
        OnModify?.Invoke(this, key, oldValue, newValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
        timer.Dispose();
 | 
			
		||||
        timer = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal void Refresh()
 | 
			
		||||
    {
 | 
			
		||||
        lastAction = DateTime.Now;
 | 
			
		||||
        timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Timeout // Seconds
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return timeout;
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            timeout = value;
 | 
			
		||||
            Refresh();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string Id
 | 
			
		||||
    {
 | 
			
		||||
        get { return id; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DateTime LastAction
 | 
			
		||||
    {
 | 
			
		||||
        get { return lastAction; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,287 +32,284 @@ using Esiur.Data;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Packets
 | 
			
		||||
namespace Esiur.Net.Packets;
 | 
			
		||||
public class HTTPRequestPacket : Packet
 | 
			
		||||
{
 | 
			
		||||
    public class HTTPRequestPacket : Packet
 | 
			
		||||
 | 
			
		||||
    public enum HTTPMethod : byte
 | 
			
		||||
    {
 | 
			
		||||
        GET,
 | 
			
		||||
        POST,
 | 
			
		||||
        HEAD,
 | 
			
		||||
        PUT,
 | 
			
		||||
        DELETE,
 | 
			
		||||
        OPTIONS,
 | 
			
		||||
        TRACE,
 | 
			
		||||
        CONNECT,
 | 
			
		||||
        UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public enum HTTPMethod:byte
 | 
			
		||||
    public StringKeyList Query;
 | 
			
		||||
    public HTTPMethod Method;
 | 
			
		||||
    public StringKeyList Headers;
 | 
			
		||||
 | 
			
		||||
    public bool WSMode;
 | 
			
		||||
 | 
			
		||||
    public string Version;
 | 
			
		||||
    public StringKeyList Cookies; // String
 | 
			
		||||
    public string URL; /// With query
 | 
			
		||||
    public string Filename; /// Without query
 | 
			
		||||
    //public byte[] PostContents;
 | 
			
		||||
    public KeyList<string, object> PostForms;
 | 
			
		||||
    public byte[] Message;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private HTTPMethod getMethod(string method)
 | 
			
		||||
    {
 | 
			
		||||
        switch (method.ToLower())
 | 
			
		||||
        {
 | 
			
		||||
            GET,
 | 
			
		||||
            POST,
 | 
			
		||||
            HEAD,
 | 
			
		||||
            PUT,
 | 
			
		||||
            DELETE,
 | 
			
		||||
            OPTIONS,
 | 
			
		||||
            TRACE,
 | 
			
		||||
            CONNECT,
 | 
			
		||||
            UNKNOWN
 | 
			
		||||
            case "get":
 | 
			
		||||
                return HTTPMethod.GET;
 | 
			
		||||
            case "post":
 | 
			
		||||
                return HTTPMethod.POST;
 | 
			
		||||
            case "head":
 | 
			
		||||
                return HTTPMethod.HEAD;
 | 
			
		||||
            case "put":
 | 
			
		||||
                return HTTPMethod.PUT;
 | 
			
		||||
            case "delete":
 | 
			
		||||
                return HTTPMethod.DELETE;
 | 
			
		||||
            case "options":
 | 
			
		||||
                return HTTPMethod.OPTIONS;
 | 
			
		||||
            case "trace":
 | 
			
		||||
                return HTTPMethod.TRACE;
 | 
			
		||||
            case "connect":
 | 
			
		||||
                return HTTPMethod.CONNECT;
 | 
			
		||||
            default:
 | 
			
		||||
                return HTTPMethod.UNKNOWN;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public StringKeyList Query;
 | 
			
		||||
        public HTTPMethod Method;
 | 
			
		||||
        public StringKeyList Headers;
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return "HTTPRequestPacket"
 | 
			
		||||
            + "\n\tVersion: " + Version
 | 
			
		||||
            + "\n\tMethod: " + Method
 | 
			
		||||
            + "\n\tURL: " + URL
 | 
			
		||||
            + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public bool WSMode;
 | 
			
		||||
    public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
    {
 | 
			
		||||
        string[] sMethod = null;
 | 
			
		||||
        string[] sLines = null;
 | 
			
		||||
 | 
			
		||||
        public string Version;
 | 
			
		||||
        public StringKeyList Cookies; // String
 | 
			
		||||
        public string URL; /// With query
 | 
			
		||||
        public string Filename; /// Without query
 | 
			
		||||
        //public byte[] PostContents;
 | 
			
		||||
        public KeyList<string, object> PostForms;
 | 
			
		||||
        public byte[] Message;
 | 
			
		||||
        uint headerSize = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private HTTPMethod getMethod(string method)
 | 
			
		||||
        for (uint i = offset; i < ends - 3; i++)
 | 
			
		||||
        {
 | 
			
		||||
            switch (method.ToLower())
 | 
			
		||||
            if (data[i] == '\r' && data[i + 1] == '\n'
 | 
			
		||||
                && data[i + 2] == '\r' && data[i + 3] == '\n')
 | 
			
		||||
            {
 | 
			
		||||
                case "get":
 | 
			
		||||
                    return HTTPMethod.GET;
 | 
			
		||||
                case "post":
 | 
			
		||||
                    return HTTPMethod.POST;
 | 
			
		||||
                case "head":
 | 
			
		||||
                    return HTTPMethod.HEAD;
 | 
			
		||||
                case "put":
 | 
			
		||||
                    return HTTPMethod.PUT;
 | 
			
		||||
                case "delete":
 | 
			
		||||
                    return HTTPMethod.DELETE;
 | 
			
		||||
                case "options":
 | 
			
		||||
                    return HTTPMethod.OPTIONS;
 | 
			
		||||
                case "trace":
 | 
			
		||||
                    return HTTPMethod.TRACE;
 | 
			
		||||
                case "connect":
 | 
			
		||||
                    return HTTPMethod.CONNECT;
 | 
			
		||||
                default:
 | 
			
		||||
                    return HTTPMethod.UNKNOWN;
 | 
			
		||||
                sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" },
 | 
			
		||||
                    StringSplitOptions.None);
 | 
			
		||||
 | 
			
		||||
                headerSize = i + 4;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            return "HTTPRequestPacket"
 | 
			
		||||
                + "\n\tVersion: " + Version
 | 
			
		||||
                + "\n\tMethod: " + Method
 | 
			
		||||
                + "\n\tURL: " + URL
 | 
			
		||||
                + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
 | 
			
		||||
        }
 | 
			
		||||
        if (headerSize == 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
        {
 | 
			
		||||
            string[] sMethod = null;
 | 
			
		||||
            string[] sLines = null;
 | 
			
		||||
            
 | 
			
		||||
            uint headerSize = 0;
 | 
			
		||||
        Cookies = new StringKeyList();
 | 
			
		||||
        PostForms = new KeyList<string, object>();
 | 
			
		||||
        Query = new StringKeyList();
 | 
			
		||||
        Headers = new StringKeyList();
 | 
			
		||||
 | 
			
		||||
            for (uint i = offset; i < ends - 3; i++)
 | 
			
		||||
        sMethod = sLines[0].Split(' ');
 | 
			
		||||
        Method = getMethod(sMethod[0].Trim());
 | 
			
		||||
 | 
			
		||||
        if (sMethod.Length == 3)
 | 
			
		||||
        {
 | 
			
		||||
            sMethod[1] = WebUtility.UrlDecode(sMethod[1]);
 | 
			
		||||
            if (sMethod[1].Length >= 7)
 | 
			
		||||
            {
 | 
			
		||||
                if (data[i] == '\r' && data[i + 1] == '\n'
 | 
			
		||||
                    && data[i + 2] == '\r' && data[i + 3] == '\n')
 | 
			
		||||
                if (sMethod[1].StartsWith("http://"))
 | 
			
		||||
                {
 | 
			
		||||
                    sLines = Encoding.ASCII.GetString(data, (int)offset,(int)( i - offset)).Split(new string[] { "\r\n" },
 | 
			
		||||
                        StringSplitOptions.None);
 | 
			
		||||
 | 
			
		||||
                    headerSize = i + 4;
 | 
			
		||||
                    break;
 | 
			
		||||
                    sMethod[1] = sMethod[1].Substring(sMethod[1].IndexOf("/", 7));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (headerSize == 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
            URL = sMethod[1].Trim();
 | 
			
		||||
 | 
			
		||||
            Cookies = new StringKeyList();
 | 
			
		||||
            PostForms = new KeyList<string, object>();
 | 
			
		||||
            Query = new StringKeyList();
 | 
			
		||||
            Headers = new StringKeyList();
 | 
			
		||||
 | 
			
		||||
            sMethod = sLines[0].Split(' ');
 | 
			
		||||
            Method = getMethod(sMethod[0].Trim());
 | 
			
		||||
 | 
			
		||||
            if (sMethod.Length == 3)
 | 
			
		||||
            if (URL.IndexOf("?", 0) != -1)
 | 
			
		||||
            {
 | 
			
		||||
                sMethod[1] = WebUtility.UrlDecode(sMethod[1]);
 | 
			
		||||
                if (sMethod[1].Length >= 7)
 | 
			
		||||
                Filename = URL.Split(new char[] { '?' }, 2)[0];
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                Filename = URL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (Filename.IndexOf("%", 0) != -1)
 | 
			
		||||
            {
 | 
			
		||||
                Filename = WebUtility.UrlDecode(Filename);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Version = sMethod[2].Trim();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Read all headers
 | 
			
		||||
 | 
			
		||||
        for (int i = 1; i < sLines.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (sLines[i] == String.Empty)
 | 
			
		||||
            {
 | 
			
		||||
                // Invalid header
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (sLines[i].IndexOf(':') == -1)
 | 
			
		||||
            {
 | 
			
		||||
                // Invalid header
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string[] header = sLines[i].Split(new char[] { ':' }, 2);
 | 
			
		||||
 | 
			
		||||
            header[0] = header[0].ToLower();
 | 
			
		||||
            Headers[header[0]] = header[1].Trim();
 | 
			
		||||
 | 
			
		||||
            if (header[0] == "cookie")
 | 
			
		||||
            {
 | 
			
		||||
                string[] cookies = header[1].Split(';');
 | 
			
		||||
 | 
			
		||||
                foreach (string cookie in cookies)
 | 
			
		||||
                {
 | 
			
		||||
                    if (sMethod[1].StartsWith("http://"))
 | 
			
		||||
                    if (cookie.IndexOf('=') != -1)
 | 
			
		||||
                    {
 | 
			
		||||
                        sMethod[1] = sMethod[1].Substring(sMethod[1].IndexOf("/", 7));
 | 
			
		||||
                        string[] splitCookie = cookie.Split('=');
 | 
			
		||||
                        splitCookie[0] = splitCookie[0].Trim();
 | 
			
		||||
                        splitCookie[1] = splitCookie[1].Trim();
 | 
			
		||||
                        if (!(Cookies.ContainsKey(splitCookie[0].Trim())))
 | 
			
		||||
                            Cookies.Add(splitCookie[0], splitCookie[1]);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!(Cookies.ContainsKey(cookie.Trim())))
 | 
			
		||||
                        {
 | 
			
		||||
                            Cookies.Add(cookie.Trim(), String.Empty);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                URL = sMethod[1].Trim();
 | 
			
		||||
 | 
			
		||||
                if (URL.IndexOf("?", 0) != -1)
 | 
			
		||||
        // Query String
 | 
			
		||||
        if (URL.IndexOf("?", 0) != -1)
 | 
			
		||||
        {
 | 
			
		||||
            string[] SQ = URL.Split(new char[] { '?' }, 2)[1].Split('&');
 | 
			
		||||
            foreach (string S in SQ)
 | 
			
		||||
            {
 | 
			
		||||
                if (S.IndexOf("=", 0) != -1)
 | 
			
		||||
                {
 | 
			
		||||
                    Filename = URL.Split(new char[] { '?' }, 2)[0];
 | 
			
		||||
                    string[] qp = S.Split(new char[] { '=' }, 2);
 | 
			
		||||
 | 
			
		||||
                    if (!Query.ContainsKey(WebUtility.UrlDecode(qp[0])))
 | 
			
		||||
                    {
 | 
			
		||||
                        Query.Add(WebUtility.UrlDecode(qp[0]), WebUtility.UrlDecode(qp[1]));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    Filename = URL;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (Filename.IndexOf("%", 0) != -1)
 | 
			
		||||
                {
 | 
			
		||||
                    Filename = WebUtility.UrlDecode(Filename);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Version = sMethod[2].Trim();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Read all headers
 | 
			
		||||
 | 
			
		||||
            for (int i = 1; i < sLines.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
                if (sLines[i] == String.Empty)
 | 
			
		||||
                {
 | 
			
		||||
                    // Invalid header
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (sLines[i].IndexOf(':') == -1)
 | 
			
		||||
                {
 | 
			
		||||
                    // Invalid header
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                string[] header = sLines[i].Split(new char[] { ':' }, 2);
 | 
			
		||||
 | 
			
		||||
                header[0] = header[0].ToLower();
 | 
			
		||||
                Headers[header[0]] = header[1].Trim();
 | 
			
		||||
 | 
			
		||||
                if (header[0] == "cookie")
 | 
			
		||||
                {
 | 
			
		||||
                    string[] cookies = header[1].Split(';');
 | 
			
		||||
 | 
			
		||||
                    foreach (string cookie in cookies)
 | 
			
		||||
                    if (!(Query.ContainsKey(WebUtility.UrlDecode(S))))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (cookie.IndexOf('=') != -1)
 | 
			
		||||
                        Query.Add(WebUtility.UrlDecode(S), null);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Post Content-Length
 | 
			
		||||
        if (Method == HTTPMethod.POST)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                uint postSize = uint.Parse((string)Headers["content-length"]);
 | 
			
		||||
 | 
			
		||||
                // check limit
 | 
			
		||||
                if (postSize > data.Length - headerSize)
 | 
			
		||||
                    return -(postSize - (data.Length - headerSize));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (
 | 
			
		||||
                    Headers["content-type"] == null
 | 
			
		||||
                    || Headers["content-type"] == ""
 | 
			
		||||
                    || Headers["content-type"].StartsWith("application/x-www-form-urlencoded"))
 | 
			
		||||
                {
 | 
			
		||||
                    string[] PostVars = null;
 | 
			
		||||
                    PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&');
 | 
			
		||||
                    for (int J = 0; J < PostVars.Length; J++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (PostVars[J].IndexOf("=") != -1)
 | 
			
		||||
                        {
 | 
			
		||||
                            string[] splitCookie = cookie.Split('=');
 | 
			
		||||
                            splitCookie[0] = splitCookie[0].Trim();
 | 
			
		||||
                            splitCookie[1] = splitCookie[1].Trim();
 | 
			
		||||
                            if (!(Cookies.ContainsKey(splitCookie[0].Trim())))
 | 
			
		||||
                                Cookies.Add(splitCookie[0], splitCookie[1]);
 | 
			
		||||
                            string key = WebUtility.HtmlDecode(
 | 
			
		||||
                                WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[0]));
 | 
			
		||||
                            if (PostForms.Contains(key))
 | 
			
		||||
                                PostForms[key] = WebUtility.HtmlDecode(
 | 
			
		||||
                                    WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1]));
 | 
			
		||||
                            else
 | 
			
		||||
                                PostForms.Add(key, WebUtility.HtmlDecode(
 | 
			
		||||
                                    WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1])));
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            if (!(Cookies.ContainsKey(cookie.Trim())))
 | 
			
		||||
                            {
 | 
			
		||||
                                Cookies.Add(cookie.Trim(), String.Empty);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                            if (PostForms.Contains("unknown"))
 | 
			
		||||
                            PostForms["unknown"] = PostForms["unknown"]
 | 
			
		||||
                                + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]));
 | 
			
		||||
                        else
 | 
			
		||||
                            PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
                else if (Headers["content-type"].StartsWith("multipart/form-data"))
 | 
			
		||||
                {
 | 
			
		||||
                    int st = 1;
 | 
			
		||||
                    int ed = 0;
 | 
			
		||||
                    string strBoundry = "--" + Headers["content-type"].Substring(
 | 
			
		||||
                        Headers["content-type"].IndexOf("boundary=", 0) + 9);
 | 
			
		||||
 | 
			
		||||
            // Query String
 | 
			
		||||
            if (URL.IndexOf("?", 0) != -1)
 | 
			
		||||
                    string[] sc = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split(
 | 
			
		||||
                                                new string[] { strBoundry }, StringSplitOptions.None);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    for (int j = 1; j < sc.Length - 1; j++)
 | 
			
		||||
                    {
 | 
			
		||||
                        string[] ps = sc[j].Split(new string[] { "\r\n\r\n" }, 2, StringSplitOptions.None);
 | 
			
		||||
                        ps[1] = ps[1].Substring(0, ps[1].Length - 2); // remove the empty line
 | 
			
		||||
                        st = ps[0].IndexOf("name=", 0) + 6;
 | 
			
		||||
                        ed = ps[0].IndexOf("\"", st);
 | 
			
		||||
                        PostForms.Add(ps[0].Substring(st, ed - st), ps[1]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                //else if (Headers["content-type"] == "application/json")
 | 
			
		||||
                //{
 | 
			
		||||
                //    var json = DC.Clip(data, headerSize, postSize);
 | 
			
		||||
                //}
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //PostForms.Add(Headers["content-type"], Encoding.Default.GetString( ));
 | 
			
		||||
                    Message = DC.Clip(data, headerSize, postSize);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return headerSize + postSize;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                string[] SQ = URL.Split(new char[] { '?' }, 2)[1].Split('&');
 | 
			
		||||
                foreach (string S in SQ)
 | 
			
		||||
                {
 | 
			
		||||
                    if (S.IndexOf("=", 0) != -1)
 | 
			
		||||
                    {
 | 
			
		||||
                        string[] qp = S.Split(new char[] { '=' }, 2);
 | 
			
		||||
 | 
			
		||||
                        if (!Query.ContainsKey(WebUtility.UrlDecode(qp[0])))
 | 
			
		||||
                        {
 | 
			
		||||
                            Query.Add(WebUtility.UrlDecode(qp[0]), WebUtility.UrlDecode(qp[1]));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!(Query.ContainsKey(WebUtility.UrlDecode(S))))
 | 
			
		||||
                        {
 | 
			
		||||
                            Query.Add(WebUtility.UrlDecode(S), null);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Post Content-Length
 | 
			
		||||
            if (Method == HTTPMethod.POST)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    uint postSize = uint.Parse((string)Headers["content-length"]);
 | 
			
		||||
 | 
			
		||||
                    // check limit
 | 
			
		||||
                    if (postSize > data.Length - headerSize)
 | 
			
		||||
                        return -(postSize - (data.Length - headerSize));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    if (
 | 
			
		||||
                        Headers["content-type"] == null
 | 
			
		||||
                        || Headers["content-type"] == ""
 | 
			
		||||
                        || Headers["content-type"].StartsWith("application/x-www-form-urlencoded"))
 | 
			
		||||
                    {
 | 
			
		||||
                        string[] PostVars = null;
 | 
			
		||||
                        PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&');
 | 
			
		||||
                        for (int J = 0; J < PostVars.Length; J++)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (PostVars[J].IndexOf("=") != -1)
 | 
			
		||||
                            {
 | 
			
		||||
                                string key = WebUtility.HtmlDecode(
 | 
			
		||||
                                    WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[0]));
 | 
			
		||||
                                if (PostForms.Contains(key))
 | 
			
		||||
                                    PostForms[key] = WebUtility.HtmlDecode(
 | 
			
		||||
                                        WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1]));
 | 
			
		||||
                                else
 | 
			
		||||
                                    PostForms.Add(key, WebUtility.HtmlDecode(
 | 
			
		||||
                                        WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1])));
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                                if (PostForms.Contains("unknown"))
 | 
			
		||||
                                PostForms["unknown"] = PostForms["unknown"]
 | 
			
		||||
                                    + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]));
 | 
			
		||||
                            else
 | 
			
		||||
                                PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (Headers["content-type"].StartsWith("multipart/form-data"))
 | 
			
		||||
                    {
 | 
			
		||||
                        int st = 1;
 | 
			
		||||
                        int ed = 0;
 | 
			
		||||
                        string strBoundry = "--" + Headers["content-type"].Substring(
 | 
			
		||||
                            Headers["content-type"].IndexOf("boundary=", 0) + 9);
 | 
			
		||||
 | 
			
		||||
                        string[] sc = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split(
 | 
			
		||||
                                                    new string[] { strBoundry }, StringSplitOptions.None);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        for (int j = 1; j < sc.Length - 1; j++)
 | 
			
		||||
                        {
 | 
			
		||||
                            string[] ps = sc[j].Split(new string[] { "\r\n\r\n" }, 2, StringSplitOptions.None);
 | 
			
		||||
                            ps[1] = ps[1].Substring(0, ps[1].Length - 2); // remove the empty line
 | 
			
		||||
                            st = ps[0].IndexOf("name=", 0) + 6;
 | 
			
		||||
                            ed = ps[0].IndexOf("\"", st);
 | 
			
		||||
                            PostForms.Add(ps[0].Substring(st, ed - st), ps[1]);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    //else if (Headers["content-type"] == "application/json")
 | 
			
		||||
                    //{
 | 
			
		||||
                    //    var json = DC.Clip(data, headerSize, postSize);
 | 
			
		||||
                    //}
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //PostForms.Add(Headers["content-type"], Encoding.Default.GetString( ));
 | 
			
		||||
                        Message = DC.Clip(data, headerSize, postSize);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return headerSize + postSize;
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return headerSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return headerSize;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,278 +27,276 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using Esiur.Misc;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
 
 | 
			
		||||
namespace Esiur.Net.Packets
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Packets;
 | 
			
		||||
public class HTTPResponsePacket : Packet
 | 
			
		||||
{
 | 
			
		||||
    public class HTTPResponsePacket : Packet
 | 
			
		||||
 | 
			
		||||
    public enum ComposeOptions : int
 | 
			
		||||
    {
 | 
			
		||||
        AllCalculateLength,
 | 
			
		||||
        AllDontCalculateLength,
 | 
			
		||||
        SpecifiedHeadersOnly,
 | 
			
		||||
        DataOnly
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public enum ComposeOptions : int
 | 
			
		||||
    public enum ResponseCode : int
 | 
			
		||||
    {
 | 
			
		||||
        Switching = 101,
 | 
			
		||||
        OK = 200,
 | 
			
		||||
        Created = 201,
 | 
			
		||||
        Accepted = 202,
 | 
			
		||||
        NoContent = 204,
 | 
			
		||||
        MovedPermanently = 301,
 | 
			
		||||
        Found = 302,
 | 
			
		||||
        SeeOther = 303,
 | 
			
		||||
        NotModified = 304,
 | 
			
		||||
        TemporaryRedirect = 307,
 | 
			
		||||
        BadRequest = 400,
 | 
			
		||||
        Unauthorized = 401,
 | 
			
		||||
        Forbidden = 403,
 | 
			
		||||
        NotFound = 404,
 | 
			
		||||
        MethodNotAllowed = 405,
 | 
			
		||||
        NotAcceptable = 406,
 | 
			
		||||
        PreconditionFailed = 412,
 | 
			
		||||
        UnsupportedMediaType = 415,
 | 
			
		||||
        InternalServerError = 500,
 | 
			
		||||
        NotImplemented = 501,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public struct HTTPCookie
 | 
			
		||||
    {
 | 
			
		||||
        public string Name;
 | 
			
		||||
        public string Value;
 | 
			
		||||
        public DateTime Expires;
 | 
			
		||||
        public string Path;
 | 
			
		||||
        public bool HttpOnly;
 | 
			
		||||
        public string Domain;
 | 
			
		||||
 | 
			
		||||
        public HTTPCookie(string name, string value)
 | 
			
		||||
        {
 | 
			
		||||
            AllCalculateLength,
 | 
			
		||||
            AllDontCalculateLength,
 | 
			
		||||
            SpecifiedHeadersOnly,
 | 
			
		||||
            DataOnly
 | 
			
		||||
            this.Name = name;
 | 
			
		||||
            this.Value = value;
 | 
			
		||||
            this.Path = null;
 | 
			
		||||
            this.Expires = DateTime.MinValue;
 | 
			
		||||
            this.HttpOnly = false;
 | 
			
		||||
            this.Domain = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public enum ResponseCode : int
 | 
			
		||||
        public HTTPCookie(string name, string value, DateTime expires)
 | 
			
		||||
        {
 | 
			
		||||
            Switching= 101,
 | 
			
		||||
            OK = 200,
 | 
			
		||||
            Created = 201,
 | 
			
		||||
            Accepted = 202,
 | 
			
		||||
            NoContent = 204,
 | 
			
		||||
            MovedPermanently = 301,
 | 
			
		||||
            Found = 302,
 | 
			
		||||
            SeeOther = 303,
 | 
			
		||||
            NotModified = 304,
 | 
			
		||||
            TemporaryRedirect = 307,
 | 
			
		||||
            BadRequest = 400,
 | 
			
		||||
            Unauthorized = 401,
 | 
			
		||||
            Forbidden = 403,
 | 
			
		||||
            NotFound = 404,
 | 
			
		||||
            MethodNotAllowed = 405,
 | 
			
		||||
            NotAcceptable = 406,
 | 
			
		||||
            PreconditionFailed = 412,
 | 
			
		||||
            UnsupportedMediaType = 415,
 | 
			
		||||
            InternalServerError = 500,
 | 
			
		||||
            NotImplemented = 501,
 | 
			
		||||
            this.Name = name;
 | 
			
		||||
            this.Value = value;
 | 
			
		||||
            this.Expires = expires;
 | 
			
		||||
            this.HttpOnly = false;
 | 
			
		||||
            this.Domain = null;
 | 
			
		||||
            this.Path = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public struct HTTPCookie
 | 
			
		||||
        {
 | 
			
		||||
            public string Name;
 | 
			
		||||
            public string Value;
 | 
			
		||||
            public DateTime Expires;
 | 
			
		||||
            public string Path;
 | 
			
		||||
            public bool HttpOnly;
 | 
			
		||||
            public string Domain;
 | 
			
		||||
            
 | 
			
		||||
            public HTTPCookie(string name, string value)
 | 
			
		||||
            {
 | 
			
		||||
                this.Name = name;
 | 
			
		||||
                this.Value = value;
 | 
			
		||||
                this.Path = null;
 | 
			
		||||
                this.Expires = DateTime.MinValue;
 | 
			
		||||
                this.HttpOnly = false;
 | 
			
		||||
                this.Domain = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public HTTPCookie(string name, string value, DateTime expires)
 | 
			
		||||
            {
 | 
			
		||||
                this.Name = name;
 | 
			
		||||
                this.Value = value;
 | 
			
		||||
                this.Expires = expires;
 | 
			
		||||
                this.HttpOnly = false;
 | 
			
		||||
                this.Domain = null;
 | 
			
		||||
                this.Path = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public override string ToString()
 | 
			
		||||
            {
 | 
			
		||||
                //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/
 | 
			
		||||
                //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/
 | 
			
		||||
                var cookie = Name + "=" + Value;
 | 
			
		||||
 | 
			
		||||
                if (Expires.Ticks != 0)
 | 
			
		||||
                    cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT";
 | 
			
		||||
 | 
			
		||||
                if (Domain != null)
 | 
			
		||||
                    cookie += "; domain=" + Domain;
 | 
			
		||||
 | 
			
		||||
                if (Path != null)
 | 
			
		||||
                    cookie += "; path=" + Path;
 | 
			
		||||
 | 
			
		||||
                if (HttpOnly)
 | 
			
		||||
                    cookie += "; HttpOnly";
 | 
			
		||||
 | 
			
		||||
                return cookie;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public StringKeyList Headers = new StringKeyList(true);
 | 
			
		||||
        public string Version = "HTTP/1.1";
 | 
			
		||||
 | 
			
		||||
        public byte[] Message;
 | 
			
		||||
        public ResponseCode Number;
 | 
			
		||||
        public string Text;
 | 
			
		||||
 | 
			
		||||
        public List<HTTPCookie> Cookies = new List<HTTPCookie>();
 | 
			
		||||
        public bool Handled;
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            return "HTTPResponsePacket"
 | 
			
		||||
                + "\n\tVersion: " + Version
 | 
			
		||||
                //+ "\n\tMethod: " + Method
 | 
			
		||||
                //+ "\n\tURL: " + URL
 | 
			
		||||
                + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
 | 
			
		||||
            //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/
 | 
			
		||||
            //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/
 | 
			
		||||
            var cookie = Name + "=" + Value;
 | 
			
		||||
 | 
			
		||||
            if (Expires.Ticks != 0)
 | 
			
		||||
                cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT";
 | 
			
		||||
 | 
			
		||||
            if (Domain != null)
 | 
			
		||||
                cookie += "; domain=" + Domain;
 | 
			
		||||
 | 
			
		||||
            if (Path != null)
 | 
			
		||||
                cookie += "; path=" + Path;
 | 
			
		||||
 | 
			
		||||
            if (HttpOnly)
 | 
			
		||||
                cookie += "; HttpOnly";
 | 
			
		||||
 | 
			
		||||
            return cookie;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StringKeyList Headers = new StringKeyList(true);
 | 
			
		||||
    public string Version = "HTTP/1.1";
 | 
			
		||||
 | 
			
		||||
    public byte[] Message;
 | 
			
		||||
    public ResponseCode Number;
 | 
			
		||||
    public string Text;
 | 
			
		||||
 | 
			
		||||
    public List<HTTPCookie> Cookies = new List<HTTPCookie>();
 | 
			
		||||
    public bool Handled;
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return "HTTPResponsePacket"
 | 
			
		||||
            + "\n\tVersion: " + Version
 | 
			
		||||
            //+ "\n\tMethod: " + Method
 | 
			
		||||
            //+ "\n\tURL: " + URL
 | 
			
		||||
            + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private string MakeHeader(ComposeOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        string header = $"{Version} {(int)Number} {Text}\r\nServer: Esiur {Global.Version}\r\nDate: {DateTime.Now.ToUniversalTime().ToString("r")}\r\n";
 | 
			
		||||
 | 
			
		||||
        if (options == ComposeOptions.AllCalculateLength)
 | 
			
		||||
            Headers["Content-Length"] = Message?.Length.ToString() ?? "0";
 | 
			
		||||
 | 
			
		||||
        foreach (var kv in Headers)
 | 
			
		||||
            header += kv.Key + ": " + kv.Value + "\r\n";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2007 21:00:00 GMT; path=/
 | 
			
		||||
        // Set-Cookie: ASPSESSIONIDQABBDSQA=IPDPMMMALDGFLMICEJIOCIPM; path=/
 | 
			
		||||
 | 
			
		||||
        foreach (var Cookie in Cookies)
 | 
			
		||||
            header += "Set-Cookie: " + Cookie.ToString() + "\r\n";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        header += "\r\n";
 | 
			
		||||
 | 
			
		||||
        return header;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool Compose(ComposeOptions options)
 | 
			
		||||
    {
 | 
			
		||||
        List<byte> msg = new List<byte>();
 | 
			
		||||
 | 
			
		||||
        if (options != ComposeOptions.DataOnly)
 | 
			
		||||
        {
 | 
			
		||||
            msg.AddRange(Encoding.UTF8.GetBytes(MakeHeader(options)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string MakeHeader(ComposeOptions options)
 | 
			
		||||
        if (options != ComposeOptions.SpecifiedHeadersOnly)
 | 
			
		||||
        {
 | 
			
		||||
            string header = $"{Version} {(int)Number} {Text}\r\nServer: Esiur {Global.Version}\r\nDate: {DateTime.Now.ToUniversalTime().ToString("r")}\r\n";
 | 
			
		||||
 | 
			
		||||
            if (options == ComposeOptions.AllCalculateLength) 
 | 
			
		||||
                Headers["Content-Length"] = Message?.Length.ToString() ?? "0";
 | 
			
		||||
 | 
			
		||||
            foreach (var kv in Headers)
 | 
			
		||||
                header += kv.Key + ": " + kv.Value + "\r\n";
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            // Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2007 21:00:00 GMT; path=/
 | 
			
		||||
            // Set-Cookie: ASPSESSIONIDQABBDSQA=IPDPMMMALDGFLMICEJIOCIPM; path=/
 | 
			
		||||
            
 | 
			
		||||
            foreach (var Cookie in Cookies)
 | 
			
		||||
                header += "Set-Cookie: " + Cookie.ToString() + "\r\n";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            header += "\r\n";
 | 
			
		||||
 | 
			
		||||
            return header;
 | 
			
		||||
            if (Message != null)
 | 
			
		||||
                msg.AddRange(Message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Data = msg.ToArray();
 | 
			
		||||
 | 
			
		||||
        public bool Compose(ComposeOptions options)
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool Compose()
 | 
			
		||||
    {
 | 
			
		||||
        return Compose(ComposeOptions.AllDontCalculateLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
    {
 | 
			
		||||
        string[] sMethod = null;
 | 
			
		||||
        string[] sLines = null;
 | 
			
		||||
 | 
			
		||||
        uint headerSize = 0;
 | 
			
		||||
 | 
			
		||||
        for (uint i = offset; i < ends - 3; i++)
 | 
			
		||||
        {
 | 
			
		||||
            List<byte> msg = new List<byte>();
 | 
			
		||||
 | 
			
		||||
            if (options != ComposeOptions.DataOnly)
 | 
			
		||||
            if (data[i] == '\r' && data[i + 1] == '\n'
 | 
			
		||||
                && data[i + 2] == '\r' && data[i + 3] == '\n')
 | 
			
		||||
            {
 | 
			
		||||
                msg.AddRange(Encoding.UTF8.GetBytes(MakeHeader(options)));
 | 
			
		||||
            }
 | 
			
		||||
                sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" },
 | 
			
		||||
                    StringSplitOptions.None);
 | 
			
		||||
 | 
			
		||||
            if (options != ComposeOptions.SpecifiedHeadersOnly)
 | 
			
		||||
            {
 | 
			
		||||
                if (Message != null)
 | 
			
		||||
                   msg.AddRange(Message);
 | 
			
		||||
                headerSize = i + 4;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            Data = msg.ToArray();
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Compose()
 | 
			
		||||
        if (headerSize == 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        //Cookies = new DStringDictionary();
 | 
			
		||||
        //Headers = new DStringDictionary(true);
 | 
			
		||||
 | 
			
		||||
        sMethod = sLines[0].Split(' ');
 | 
			
		||||
 | 
			
		||||
        if (sMethod.Length == 3)
 | 
			
		||||
        {
 | 
			
		||||
            return Compose(ComposeOptions.AllDontCalculateLength);
 | 
			
		||||
            Version = sMethod[0].Trim();
 | 
			
		||||
            Number = (ResponseCode)(Convert.ToInt32(sMethod[1].Trim()));
 | 
			
		||||
            Text = sMethod[2];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
        // Read all headers
 | 
			
		||||
 | 
			
		||||
        for (int i = 1; i < sLines.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            string[] sMethod = null;
 | 
			
		||||
            string[] sLines = null;
 | 
			
		||||
 | 
			
		||||
            uint headerSize = 0;
 | 
			
		||||
 | 
			
		||||
            for (uint i = offset; i < ends - 3; i++)
 | 
			
		||||
            if (sLines[i] == String.Empty)
 | 
			
		||||
            {
 | 
			
		||||
                if (data[i] == '\r' && data[i + 1] == '\n'
 | 
			
		||||
                    && data[i + 2] == '\r' && data[i + 3] == '\n')
 | 
			
		||||
                {
 | 
			
		||||
                    sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" },
 | 
			
		||||
                        StringSplitOptions.None);
 | 
			
		||||
 | 
			
		||||
                    headerSize = i + 4;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // Invalid header
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (headerSize == 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
 | 
			
		||||
            //Cookies = new DStringDictionary();
 | 
			
		||||
            //Headers = new DStringDictionary(true);
 | 
			
		||||
 | 
			
		||||
            sMethod = sLines[0].Split(' ');
 | 
			
		||||
 | 
			
		||||
            if (sMethod.Length == 3)
 | 
			
		||||
            if (sLines[i].IndexOf(':') == -1)
 | 
			
		||||
            {
 | 
			
		||||
                Version = sMethod[0].Trim();
 | 
			
		||||
                Number = (ResponseCode)(Convert.ToInt32(sMethod[1].Trim()));
 | 
			
		||||
                Text = sMethod[2];
 | 
			
		||||
                // Invalid header
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Read all headers
 | 
			
		||||
            string[] header = sLines[i].Split(new char[] { ':' }, 2);
 | 
			
		||||
 | 
			
		||||
            for (int i = 1; i < sLines.Length; i++)
 | 
			
		||||
            header[0] = header[0].ToLower();
 | 
			
		||||
            Headers[header[0]] = header[1].Trim();
 | 
			
		||||
 | 
			
		||||
            //Set-Cookie: NAME=VALUE; expires=DATE;
 | 
			
		||||
 | 
			
		||||
            if (header[0] == "set-cookie")
 | 
			
		||||
            {
 | 
			
		||||
                if (sLines[i] == String.Empty)
 | 
			
		||||
                string[] cookie = header[1].Split(';');
 | 
			
		||||
 | 
			
		||||
                if (cookie.Length >= 1)
 | 
			
		||||
                {
 | 
			
		||||
                    // Invalid header
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
                    string[] splitCookie = cookie[0].Split('=');
 | 
			
		||||
                    HTTPCookie c = new HTTPCookie(splitCookie[0], splitCookie[1]);
 | 
			
		||||
 | 
			
		||||
                if (sLines[i].IndexOf(':') == -1)
 | 
			
		||||
                {
 | 
			
		||||
                    // Invalid header
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                string[] header = sLines[i].Split(new char[] { ':' }, 2);
 | 
			
		||||
 | 
			
		||||
                header[0] = header[0].ToLower();
 | 
			
		||||
                Headers[header[0]] = header[1].Trim();
 | 
			
		||||
 | 
			
		||||
                //Set-Cookie: NAME=VALUE; expires=DATE;
 | 
			
		||||
 | 
			
		||||
                if (header[0] == "set-cookie")
 | 
			
		||||
                {
 | 
			
		||||
                    string[] cookie = header[1].Split(';');
 | 
			
		||||
 | 
			
		||||
                    if (cookie.Length >= 1)
 | 
			
		||||
                    for (int j = 1; j < cookie.Length; j++)
 | 
			
		||||
                    {
 | 
			
		||||
                        string[] splitCookie = cookie[0].Split('=');
 | 
			
		||||
                        HTTPCookie c = new HTTPCookie(splitCookie[0], splitCookie[1]);
 | 
			
		||||
 | 
			
		||||
                        for (int j = 1; j < cookie.Length; j++)
 | 
			
		||||
                        splitCookie = cookie[j].Split('=');
 | 
			
		||||
                        switch (splitCookie[0].ToLower())
 | 
			
		||||
                        {
 | 
			
		||||
                            splitCookie = cookie[j].Split('=');
 | 
			
		||||
                            switch (splitCookie[0].ToLower())
 | 
			
		||||
                            {
 | 
			
		||||
                                case "domain":
 | 
			
		||||
                                    c.Domain = splitCookie[1];
 | 
			
		||||
                                    break;
 | 
			
		||||
                                case "path":
 | 
			
		||||
                                    c.Path = splitCookie[1];
 | 
			
		||||
                                    break;
 | 
			
		||||
                                case "httponly":
 | 
			
		||||
                                    c.HttpOnly = true;
 | 
			
		||||
                                    break;
 | 
			
		||||
                                case "expires":
 | 
			
		||||
                                    // Wed, 13-Jan-2021 22:23:01 GMT
 | 
			
		||||
                                    c.Expires = DateTime.Parse(splitCookie[1]);
 | 
			
		||||
                                    break;
 | 
			
		||||
                            }
 | 
			
		||||
                            case "domain":
 | 
			
		||||
                                c.Domain = splitCookie[1];
 | 
			
		||||
                                break;
 | 
			
		||||
                            case "path":
 | 
			
		||||
                                c.Path = splitCookie[1];
 | 
			
		||||
                                break;
 | 
			
		||||
                            case "httponly":
 | 
			
		||||
                                c.HttpOnly = true;
 | 
			
		||||
                                break;
 | 
			
		||||
                            case "expires":
 | 
			
		||||
                                // Wed, 13-Jan-2021 22:23:01 GMT
 | 
			
		||||
                                c.Expires = DateTime.Parse(splitCookie[1]);
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Content-Length
 | 
			
		||||
        // Content-Length
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            uint contentLength = uint.Parse((string)Headers["content-length"]);
 | 
			
		||||
 | 
			
		||||
            // check limit
 | 
			
		||||
            if (contentLength > data.Length - headerSize)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                uint contentLength = uint.Parse((string)Headers["content-length"]);
 | 
			
		||||
 | 
			
		||||
                // check limit
 | 
			
		||||
                if (contentLength > data.Length - headerSize)
 | 
			
		||||
                {
 | 
			
		||||
                    return contentLength - (data.Length - headerSize);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Message = DC.Clip(data, offset, contentLength);
 | 
			
		||||
 | 
			
		||||
                return headerSize + contentLength;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return 0;
 | 
			
		||||
                return contentLength - (data.Length - headerSize);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Message = DC.Clip(data, offset, contentLength);
 | 
			
		||||
 | 
			
		||||
            return headerSize + contentLength;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,385 +31,384 @@ using System.Security.Cryptography;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Packets
 | 
			
		||||
namespace Esiur.Net.Packets;
 | 
			
		||||
class IIPAuthPacket : Packet
 | 
			
		||||
{
 | 
			
		||||
    class IIPAuthPacket : Packet
 | 
			
		||||
    public enum IIPAuthPacketCommand : byte
 | 
			
		||||
    {
 | 
			
		||||
        public enum IIPAuthPacketCommand : byte
 | 
			
		||||
        Action = 0,
 | 
			
		||||
        Declare,
 | 
			
		||||
        Acknowledge,
 | 
			
		||||
        Error,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum IIPAuthPacketAction : byte
 | 
			
		||||
    {
 | 
			
		||||
        // Authenticate
 | 
			
		||||
        AuthenticateHash,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //Challenge,
 | 
			
		||||
        //CertificateRequest,
 | 
			
		||||
        //CertificateReply,
 | 
			
		||||
        //EstablishRequest,
 | 
			
		||||
        //EstablishReply
 | 
			
		||||
 | 
			
		||||
        NewConnection = 0x20,
 | 
			
		||||
        ResumeConnection,
 | 
			
		||||
 | 
			
		||||
        ConnectionEstablished = 0x28
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public IIPAuthPacketCommand Command
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
    public IIPAuthPacketAction Action
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte ErrorCode { get; set; }
 | 
			
		||||
    public string ErrorMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
    public AuthenticationMethod LocalMethod
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] SourceInfo
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] Hash
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] SessionId
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AuthenticationMethod RemoteMethod
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string Domain
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long CertificateId
 | 
			
		||||
    {
 | 
			
		||||
        get; set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string LocalUsername
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string RemoteUsername
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] LocalPassword
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
    public byte[] RemotePassword
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] LocalToken
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] RemoteToken
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] AsymetricEncryptionKey
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] LocalNonce
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] RemoteNonce
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ulong RemoteTokenIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
    private uint dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
    bool NotEnough(uint offset, uint ends, uint needed)
 | 
			
		||||
    {
 | 
			
		||||
        if (offset + needed > ends)
 | 
			
		||||
        {
 | 
			
		||||
            Action = 0,
 | 
			
		||||
            Declare,
 | 
			
		||||
            Acknowledge,
 | 
			
		||||
            Error,
 | 
			
		||||
            dataLengthNeeded = needed - (ends - offset);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public enum IIPAuthPacketAction : byte
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return Command.ToString() + " " + Action.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
    {
 | 
			
		||||
        var oOffset = offset;
 | 
			
		||||
 | 
			
		||||
        if (NotEnough(offset, ends, 1))
 | 
			
		||||
            return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
        Command = (IIPAuthPacketCommand)(data[offset] >> 6);
 | 
			
		||||
 | 
			
		||||
        if (Command == IIPAuthPacketCommand.Action)
 | 
			
		||||
        {
 | 
			
		||||
            // Authenticate
 | 
			
		||||
            AuthenticateHash,
 | 
			
		||||
            Action = (IIPAuthPacketAction)(data[offset++] & 0x3f);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //Challenge,
 | 
			
		||||
            //CertificateRequest,
 | 
			
		||||
            //CertificateReply,
 | 
			
		||||
            //EstablishRequest,
 | 
			
		||||
            //EstablishReply
 | 
			
		||||
 | 
			
		||||
            NewConnection = 0x20,
 | 
			
		||||
            ResumeConnection,
 | 
			
		||||
 | 
			
		||||
            ConnectionEstablished = 0x28
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public IIPAuthPacketCommand Command
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
        public IIPAuthPacketAction Action
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte ErrorCode { get; set; }
 | 
			
		||||
        public string ErrorMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
        public AuthenticationMethod LocalMethod
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] SourceInfo
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] Hash
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] SessionId
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AuthenticationMethod RemoteMethod
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Domain
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public long CertificateId
 | 
			
		||||
        {
 | 
			
		||||
            get; set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string LocalUsername
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string RemoteUsername
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] LocalPassword
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
        public byte[] RemotePassword
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] LocalToken
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] RemoteToken
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] AsymetricEncryptionKey
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] LocalNonce
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public byte[] RemoteNonce
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ulong RemoteTokenIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        private uint dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
        bool NotEnough(uint offset, uint ends, uint needed)
 | 
			
		||||
        {
 | 
			
		||||
            if (offset + needed > ends)
 | 
			
		||||
            if (Action == IIPAuthPacketAction.AuthenticateHash)
 | 
			
		||||
            {
 | 
			
		||||
                dataLengthNeeded = needed - (ends - offset);
 | 
			
		||||
                return true;
 | 
			
		||||
                if (NotEnough(offset, ends, 32))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                Hash = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                //var hash = new byte[32];
 | 
			
		||||
                //Buffer.BlockCopy(data, (int)offset, hash, 0, 32);
 | 
			
		||||
                //Hash = hash;
 | 
			
		||||
 | 
			
		||||
                offset += 32;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
            else if (Action == IIPAuthPacketAction.NewConnection)
 | 
			
		||||
            {
 | 
			
		||||
                if (NotEnough(offset, ends, 2))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            return Command.ToString() + " " + Action.ToString();
 | 
			
		||||
        }
 | 
			
		||||
                var length = data.GetUInt16(offset);
 | 
			
		||||
 | 
			
		||||
        public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
                offset += 2;
 | 
			
		||||
 | 
			
		||||
                if (NotEnough(offset, ends, length))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                SourceInfo = data.Clip(offset, length);
 | 
			
		||||
 | 
			
		||||
                //var sourceInfo = new byte[length];
 | 
			
		||||
                //Buffer.BlockCopy(data, (int)offset, sourceInfo, 0, length);
 | 
			
		||||
                //SourceInfo = sourceInfo;
 | 
			
		||||
 | 
			
		||||
                offset += 32;
 | 
			
		||||
            }
 | 
			
		||||
            else if (Action == IIPAuthPacketAction.ResumeConnection
 | 
			
		||||
                 || Action == IIPAuthPacketAction.ConnectionEstablished)
 | 
			
		||||
            {
 | 
			
		||||
                //var sessionId = new byte[32];
 | 
			
		||||
 | 
			
		||||
                if (NotEnough(offset, ends, 32))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                SessionId = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                //Buffer.BlockCopy(data, (int)offset, sessionId, 0, 32);
 | 
			
		||||
                //SessionId = sessionId;
 | 
			
		||||
 | 
			
		||||
                offset += 32;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (Command == IIPAuthPacketCommand.Declare)
 | 
			
		||||
        {
 | 
			
		||||
            var oOffset = offset;
 | 
			
		||||
            RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3);
 | 
			
		||||
            LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3);
 | 
			
		||||
            var encrypt = ((data[offset++] & 0x2) == 0x2);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (NotEnough(offset, ends, 1))
 | 
			
		||||
                return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
            Command = (IIPAuthPacketCommand)(data[offset] >> 6);
 | 
			
		||||
            var domainLength = data[offset++];
 | 
			
		||||
            if (NotEnough(offset, ends, domainLength))
 | 
			
		||||
                return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
            if (Command == IIPAuthPacketCommand.Action)
 | 
			
		||||
            var domain = data.GetString(offset, domainLength);
 | 
			
		||||
 | 
			
		||||
            Domain = domain;
 | 
			
		||||
 | 
			
		||||
            offset += domainLength;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (RemoteMethod == AuthenticationMethod.Credentials)
 | 
			
		||||
            {
 | 
			
		||||
                Action = (IIPAuthPacketAction)(data[offset++] & 0x3f);
 | 
			
		||||
 | 
			
		||||
                if (Action == IIPAuthPacketAction.AuthenticateHash)
 | 
			
		||||
                if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                {
 | 
			
		||||
                    if (NotEnough(offset, ends, 32))
 | 
			
		||||
                    if (NotEnough(offset, ends, 33))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    Hash = data.Clip(offset, 32);
 | 
			
		||||
                    //var remoteNonce = new byte[32];
 | 
			
		||||
                    //Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32);
 | 
			
		||||
                    //RemoteNonce = remoteNonce;
 | 
			
		||||
 | 
			
		||||
                    //var hash = new byte[32];
 | 
			
		||||
                    //Buffer.BlockCopy(data, (int)offset, hash, 0, 32);
 | 
			
		||||
                    //Hash = hash;
 | 
			
		||||
                    RemoteNonce = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                    offset += 32;
 | 
			
		||||
                }
 | 
			
		||||
                else if (Action == IIPAuthPacketAction.NewConnection)
 | 
			
		||||
                {
 | 
			
		||||
                    if (NotEnough(offset, ends, 2))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    var length = data.GetUInt16(offset);
 | 
			
		||||
 | 
			
		||||
                    offset += 2;
 | 
			
		||||
                    var length = data[offset++];
 | 
			
		||||
 | 
			
		||||
                    if (NotEnough(offset, ends, length))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    SourceInfo = data.Clip(offset, length);
 | 
			
		||||
                    RemoteUsername = data.GetString(offset, length);
 | 
			
		||||
 | 
			
		||||
                    //var sourceInfo = new byte[length];
 | 
			
		||||
                    //Buffer.BlockCopy(data, (int)offset, sourceInfo, 0, length);
 | 
			
		||||
                    //SourceInfo = sourceInfo;
 | 
			
		||||
 | 
			
		||||
                    offset += length;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (RemoteMethod == AuthenticationMethod.Token)
 | 
			
		||||
            {
 | 
			
		||||
                if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                {
 | 
			
		||||
                    if (NotEnough(offset, ends, 37))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    RemoteNonce = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                    offset += 32;
 | 
			
		||||
                }
 | 
			
		||||
                else if (Action == IIPAuthPacketAction.ResumeConnection
 | 
			
		||||
                     || Action == IIPAuthPacketAction.ConnectionEstablished)
 | 
			
		||||
                {
 | 
			
		||||
                    //var sessionId = new byte[32];
 | 
			
		||||
 | 
			
		||||
                    RemoteTokenIndex = data.GetUInt64(offset);
 | 
			
		||||
                    offset += 8;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (encrypt)
 | 
			
		||||
            {
 | 
			
		||||
                if (NotEnough(offset, ends, 2))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                var keyLength = data.GetUInt16(offset);
 | 
			
		||||
 | 
			
		||||
                offset += 2;
 | 
			
		||||
 | 
			
		||||
                if (NotEnough(offset, ends, keyLength))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                //var key = new byte[keyLength];
 | 
			
		||||
                //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength);
 | 
			
		||||
                //AsymetricEncryptionKey = key;
 | 
			
		||||
 | 
			
		||||
                AsymetricEncryptionKey = data.Clip(offset, keyLength);
 | 
			
		||||
 | 
			
		||||
                offset += keyLength;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (Command == IIPAuthPacketCommand.Acknowledge)
 | 
			
		||||
        {
 | 
			
		||||
            RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3);
 | 
			
		||||
            LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3);
 | 
			
		||||
            var encrypt = ((data[offset++] & 0x2) == 0x2);
 | 
			
		||||
 | 
			
		||||
            if (RemoteMethod == AuthenticationMethod.None)
 | 
			
		||||
            {
 | 
			
		||||
                if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                {
 | 
			
		||||
                    // do nothing
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (RemoteMethod == AuthenticationMethod.Credentials
 | 
			
		||||
                   || RemoteMethod == AuthenticationMethod.Token)
 | 
			
		||||
            {
 | 
			
		||||
                if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                {
 | 
			
		||||
                    if (NotEnough(offset, ends, 32))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    SessionId = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                    //Buffer.BlockCopy(data, (int)offset, sessionId, 0, 32);
 | 
			
		||||
                    //SessionId = sessionId;
 | 
			
		||||
 | 
			
		||||
                    RemoteNonce = data.Clip(offset, 32);
 | 
			
		||||
                    offset += 32;
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (Command == IIPAuthPacketCommand.Declare)
 | 
			
		||||
 | 
			
		||||
            if (encrypt)
 | 
			
		||||
            {
 | 
			
		||||
                RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3);
 | 
			
		||||
                LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3);
 | 
			
		||||
                var encrypt = ((data[offset++] & 0x2) == 0x2);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (NotEnough(offset, ends, 1))
 | 
			
		||||
                if (NotEnough(offset, ends, 2))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                var domainLength = data[offset++];
 | 
			
		||||
                if (NotEnough(offset, ends, domainLength))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
                var keyLength = data.GetUInt16(offset);
 | 
			
		||||
 | 
			
		||||
                var domain = data.GetString(offset, domainLength);
 | 
			
		||||
 | 
			
		||||
                Domain = domain;
 | 
			
		||||
 | 
			
		||||
                offset += domainLength;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (RemoteMethod == AuthenticationMethod.Credentials)
 | 
			
		||||
                {
 | 
			
		||||
                    if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (NotEnough(offset, ends, 33))
 | 
			
		||||
                            return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                        //var remoteNonce = new byte[32];
 | 
			
		||||
                        //Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32);
 | 
			
		||||
                        //RemoteNonce = remoteNonce;
 | 
			
		||||
 | 
			
		||||
                        RemoteNonce = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                        offset += 32;
 | 
			
		||||
 | 
			
		||||
                        var length = data[offset++];
 | 
			
		||||
 | 
			
		||||
                        if (NotEnough(offset, ends, length))
 | 
			
		||||
                            return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                        RemoteUsername = data.GetString(offset, length);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        offset += length;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (RemoteMethod == AuthenticationMethod.Token)
 | 
			
		||||
                {
 | 
			
		||||
                    if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (NotEnough(offset, ends, 37))
 | 
			
		||||
                            return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                        RemoteNonce = data.Clip(offset, 32);
 | 
			
		||||
 | 
			
		||||
                        offset += 32;
 | 
			
		||||
 | 
			
		||||
                        RemoteTokenIndex = data.GetUInt64(offset);
 | 
			
		||||
                        offset += 8;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (encrypt)
 | 
			
		||||
                {
 | 
			
		||||
                    if (NotEnough(offset, ends, 2))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    var keyLength = data.GetUInt16(offset);
 | 
			
		||||
 | 
			
		||||
                    offset += 2;
 | 
			
		||||
 | 
			
		||||
                    if (NotEnough(offset, ends, keyLength))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    //var key = new byte[keyLength];
 | 
			
		||||
                    //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength);
 | 
			
		||||
                    //AsymetricEncryptionKey = key;
 | 
			
		||||
 | 
			
		||||
                    AsymetricEncryptionKey = data.Clip(offset, keyLength);
 | 
			
		||||
 | 
			
		||||
                    offset += keyLength;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (Command == IIPAuthPacketCommand.Acknowledge)
 | 
			
		||||
            {
 | 
			
		||||
                RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3);
 | 
			
		||||
                LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3);
 | 
			
		||||
                var encrypt = ((data[offset++] & 0x2) == 0x2);
 | 
			
		||||
 | 
			
		||||
                if (RemoteMethod == AuthenticationMethod.None)
 | 
			
		||||
                {
 | 
			
		||||
                    if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                    {
 | 
			
		||||
                        // do nothing
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (RemoteMethod == AuthenticationMethod.Credentials
 | 
			
		||||
                       || RemoteMethod == AuthenticationMethod.Token)
 | 
			
		||||
                {
 | 
			
		||||
                    if (LocalMethod == AuthenticationMethod.None)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (NotEnough(offset, ends, 32))
 | 
			
		||||
                            return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                        RemoteNonce = data.Clip(offset, 32);
 | 
			
		||||
                        offset += 32;
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (encrypt)
 | 
			
		||||
                {
 | 
			
		||||
                    if (NotEnough(offset, ends, 2))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    var keyLength = data.GetUInt16(offset);
 | 
			
		||||
 | 
			
		||||
                    offset += 2;
 | 
			
		||||
 | 
			
		||||
                    if (NotEnough(offset, ends, keyLength))
 | 
			
		||||
                        return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                    //var key = new byte[keyLength];
 | 
			
		||||
                    //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength);
 | 
			
		||||
                    //AsymetricEncryptionKey = key;
 | 
			
		||||
 | 
			
		||||
                    AsymetricEncryptionKey = data.Clip(offset, keyLength);
 | 
			
		||||
 | 
			
		||||
                    offset += keyLength;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (Command == IIPAuthPacketCommand.Error)
 | 
			
		||||
            {
 | 
			
		||||
                if (NotEnough(offset, ends, 4))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                offset++;
 | 
			
		||||
                ErrorCode = data[offset++];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                var cl = data.GetUInt16(offset);
 | 
			
		||||
                offset += 2;
 | 
			
		||||
 | 
			
		||||
                if (NotEnough(offset, ends, cl))
 | 
			
		||||
                if (NotEnough(offset, ends, keyLength))
 | 
			
		||||
                    return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
                ErrorMessage = data.GetString(offset, cl);
 | 
			
		||||
                offset += cl;
 | 
			
		||||
                //var key = new byte[keyLength];
 | 
			
		||||
                //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength);
 | 
			
		||||
                //AsymetricEncryptionKey = key;
 | 
			
		||||
 | 
			
		||||
                AsymetricEncryptionKey = data.Clip(offset, keyLength);
 | 
			
		||||
 | 
			
		||||
                offset += keyLength;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (Command == IIPAuthPacketCommand.Error)
 | 
			
		||||
        {
 | 
			
		||||
            if (NotEnough(offset, ends, 4))
 | 
			
		||||
                return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
            offset++;
 | 
			
		||||
            ErrorCode = data[offset++];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return offset - oOffset;
 | 
			
		||||
            var cl = data.GetUInt16(offset);
 | 
			
		||||
            offset += 2;
 | 
			
		||||
 | 
			
		||||
            if (NotEnough(offset, ends, cl))
 | 
			
		||||
                return -dataLengthNeeded;
 | 
			
		||||
 | 
			
		||||
            ErrorMessage = data.GetString(offset, cl);
 | 
			
		||||
            offset += cl;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return offset - oOffset;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -2,21 +2,20 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Packets
 | 
			
		||||
{
 | 
			
		||||
    struct IIPPacketAttachInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Link;
 | 
			
		||||
        public ulong Age;
 | 
			
		||||
        public byte[] Content;
 | 
			
		||||
        public Guid ClassId;
 | 
			
		||||
namespace Esiur.Net.Packets;
 | 
			
		||||
 | 
			
		||||
        public IIPPacketAttachInfo(Guid classId, ulong age, string link, byte[] content)
 | 
			
		||||
        {
 | 
			
		||||
            ClassId = classId;
 | 
			
		||||
            Age = age;
 | 
			
		||||
            Content = content;
 | 
			
		||||
            Link = link;
 | 
			
		||||
        }
 | 
			
		||||
struct IIPPacketAttachInfo
 | 
			
		||||
{
 | 
			
		||||
    public string Link;
 | 
			
		||||
    public ulong Age;
 | 
			
		||||
    public byte[] Content;
 | 
			
		||||
    public Guid ClassId;
 | 
			
		||||
 | 
			
		||||
    public IIPPacketAttachInfo(Guid classId, ulong age, string link, byte[] content)
 | 
			
		||||
    {
 | 
			
		||||
        ClassId = classId;
 | 
			
		||||
        Age = age;
 | 
			
		||||
        Content = content;
 | 
			
		||||
        Link = link;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,238 +38,219 @@ using Esiur.Net.DataLink;
 | 
			
		||||
using System.Net.NetworkInformation;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Packets
 | 
			
		||||
namespace Esiur.Net.Packets;
 | 
			
		||||
internal static class Functions
 | 
			
		||||
{
 | 
			
		||||
    internal static class Functions
 | 
			
		||||
    public static void AddData(ref byte[] dest, byte[] src)
 | 
			
		||||
    {
 | 
			
		||||
        public static void AddData(ref byte[] dest, byte[] src)
 | 
			
		||||
        int I = 0;
 | 
			
		||||
        if (src == null)
 | 
			
		||||
        {
 | 
			
		||||
            int I = 0;
 | 
			
		||||
            if (src == null)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (dest != null)
 | 
			
		||||
            {
 | 
			
		||||
                I = dest.Length;
 | 
			
		||||
                Array.Resize(ref dest, dest.Length + src.Length);
 | 
			
		||||
                //dest = (byte[])Resize(dest, dest.Length + src.Length);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                dest = new byte[src.Length];
 | 
			
		||||
            }
 | 
			
		||||
            Array.Copy(src, 0, dest, I, src.Length);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static Array Resize(Array array, int newSize)
 | 
			
		||||
        if (dest != null)
 | 
			
		||||
        {
 | 
			
		||||
            Type myType = Type.GetType(array.GetType().FullName.TrimEnd('[', ']'));
 | 
			
		||||
            Array nA = Array.CreateInstance(myType, newSize);
 | 
			
		||||
            Array.Copy(array, nA, (newSize > array.Length ? array.Length : newSize));
 | 
			
		||||
            return nA;
 | 
			
		||||
        } */
 | 
			
		||||
 | 
			
		||||
        //Computes the checksum used in IP, ARP..., ie the
 | 
			
		||||
        // "The 16 bit one's complement of the one 's complement sum
 | 
			
		||||
        //of all 16 bit words" as seen in RFCs
 | 
			
		||||
        // Returns a 4 characters hex string
 | 
			
		||||
        // data's lenght must be multiple of 4, else zero padding
 | 
			
		||||
        public static ushort IP_CRC16(byte[] data)
 | 
			
		||||
        {
 | 
			
		||||
            ulong Sum = 0;
 | 
			
		||||
            bool Padding = false;
 | 
			
		||||
            /// * Padding if needed
 | 
			
		||||
            if (data.Length % 2 != 0)
 | 
			
		||||
            {
 | 
			
		||||
                Array.Resize(ref data, data.Length + 1);
 | 
			
		||||
                //data = (byte[])Resize(data, data.Length + 1);
 | 
			
		||||
                Padding = true;
 | 
			
		||||
            }
 | 
			
		||||
            int count = data.Length;
 | 
			
		||||
            ///* add 16-bit words */
 | 
			
		||||
            while (count > 0) //1)
 | 
			
		||||
            {
 | 
			
		||||
                ///*  this is the inner loop  */ 
 | 
			
		||||
                Sum += GetInteger(data[count - 2], data[count - 1]);
 | 
			
		||||
                ///*  Fold 32-bit sum to 16-bit  */ 
 | 
			
		||||
                while (Sum >> 16 != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    Sum = (Sum & 0XFFFF) + (Sum >> 16);
 | 
			
		||||
                }
 | 
			
		||||
                count -= 2;
 | 
			
		||||
            }
 | 
			
		||||
            /// * reverse padding 
 | 
			
		||||
            if (Padding)
 | 
			
		||||
            {
 | 
			
		||||
                Array.Resize(ref data, data.Length - 1);
 | 
			
		||||
                //data = (byte[])Resize(data, data.Length - 1);
 | 
			
		||||
            }
 | 
			
		||||
            ///* Return one's compliment of final sum. 
 | 
			
		||||
            //return (ushort)(ushort.MaxValue - (ushort)Sum);
 | 
			
		||||
            return (ushort)(~Sum);
 | 
			
		||||
            I = dest.Length;
 | 
			
		||||
            Array.Resize(ref dest, dest.Length + src.Length);
 | 
			
		||||
            //dest = (byte[])Resize(dest, dest.Length + src.Length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static ushort GetInteger(byte B1, byte B2)
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return BitConverter.ToUInt16(new byte[] { B2, B1 }, 0);
 | 
			
		||||
            //return System.Convert.ToUInt16("&h" + GetHex(B1) + GetHex(B2));
 | 
			
		||||
            dest = new byte[src.Length];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static uint GetLong(byte B1, byte B2, byte B3, byte B4)
 | 
			
		||||
        {
 | 
			
		||||
            return BitConverter.ToUInt32(new byte[] { B4, B3, B2, B1 }, 0);
 | 
			
		||||
            //return System.Convert.ToUInt32("&h" + GetHex(B1) + GetHex(B2) + GetHex(B3) + GetHex(B4));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetHex(byte B)
 | 
			
		||||
        {
 | 
			
		||||
            return (((B < 15) ? 0 + System.Convert.ToString(B, 16).ToUpper() : System.Convert.ToString(B, 16).ToUpper()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static bool GetBit(uint B, byte Pos)
 | 
			
		||||
        {
 | 
			
		||||
            //return BitConverter.ToBoolean(BitConverter.GetBytes(B), Pos + 1);
 | 
			
		||||
            return (B & (uint)(Math.Pow(2, (Pos - 1)))) == (Math.Pow(2, (Pos - 1)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static ushort RemoveBit(ushort I, byte Pos)
 | 
			
		||||
        {
 | 
			
		||||
            return (ushort)RemoveBit((uint)I, Pos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static uint RemoveBit(uint I, byte Pos)
 | 
			
		||||
        {
 | 
			
		||||
            if (GetBit(I, Pos))
 | 
			
		||||
            {
 | 
			
		||||
                return I - (uint)(Math.Pow(2, (Pos - 1)));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return I;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void SplitInteger(ushort I, ref byte BLeft, ref byte BRight)
 | 
			
		||||
        {
 | 
			
		||||
            byte[] b = BitConverter.GetBytes(I);
 | 
			
		||||
            BLeft = b[1];
 | 
			
		||||
            BRight = b[0];
 | 
			
		||||
            //BLeft = I >> 8;
 | 
			
		||||
            //BRight = (I << 8) >> 8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void SplitLong(uint I, ref byte BLeft, ref byte BLeftMiddle, ref byte BRightMiddle, ref byte BRight)
 | 
			
		||||
        {
 | 
			
		||||
            byte[] b = BitConverter.GetBytes(I);
 | 
			
		||||
            BLeft = b[3];
 | 
			
		||||
            BLeftMiddle = b[2];
 | 
			
		||||
            BRightMiddle = b[1];
 | 
			
		||||
            BRight = b[0];
 | 
			
		||||
            //BLeft = I >> 24;
 | 
			
		||||
            //BLeftMiddle = (I << 8) >> 24;
 | 
			
		||||
            //BRightMiddle = (I << 16) >> 24;
 | 
			
		||||
            //BRight = (I << 24) >> 24;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Array.Copy(src, 0, dest, I, src.Length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class PosixTime
 | 
			
		||||
    /*
 | 
			
		||||
    public static Array Resize(Array array, int newSize)
 | 
			
		||||
    {
 | 
			
		||||
        ulong seconds;
 | 
			
		||||
        ulong microseconds;
 | 
			
		||||
        Type myType = Type.GetType(array.GetType().FullName.TrimEnd('[', ']'));
 | 
			
		||||
        Array nA = Array.CreateInstance(myType, newSize);
 | 
			
		||||
        Array.Copy(array, nA, (newSize > array.Length ? array.Length : newSize));
 | 
			
		||||
        return nA;
 | 
			
		||||
    } */
 | 
			
		||||
 | 
			
		||||
        PosixTime(ulong Seconds, ulong Microseconds)
 | 
			
		||||
    //Computes the checksum used in IP, ARP..., ie the
 | 
			
		||||
    // "The 16 bit one's complement of the one 's complement sum
 | 
			
		||||
    //of all 16 bit words" as seen in RFCs
 | 
			
		||||
    // Returns a 4 characters hex string
 | 
			
		||||
    // data's lenght must be multiple of 4, else zero padding
 | 
			
		||||
    public static ushort IP_CRC16(byte[] data)
 | 
			
		||||
    {
 | 
			
		||||
        ulong Sum = 0;
 | 
			
		||||
        bool Padding = false;
 | 
			
		||||
        /// * Padding if needed
 | 
			
		||||
        if (data.Length % 2 != 0)
 | 
			
		||||
        {
 | 
			
		||||
            seconds = Seconds;
 | 
			
		||||
            microseconds = Microseconds;
 | 
			
		||||
            Array.Resize(ref data, data.Length + 1);
 | 
			
		||||
            //data = (byte[])Resize(data, data.Length + 1);
 | 
			
		||||
            Padding = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        int count = data.Length;
 | 
			
		||||
        ///* add 16-bit words */
 | 
			
		||||
        while (count > 0) //1)
 | 
			
		||||
        {
 | 
			
		||||
            return seconds + "." + microseconds;
 | 
			
		||||
            ///*  this is the inner loop  */ 
 | 
			
		||||
            Sum += GetInteger(data[count - 2], data[count - 1]);
 | 
			
		||||
            ///*  Fold 32-bit sum to 16-bit  */ 
 | 
			
		||||
            while (Sum >> 16 != 0)
 | 
			
		||||
            {
 | 
			
		||||
                Sum = (Sum & 0XFFFF) + (Sum >> 16);
 | 
			
		||||
            }
 | 
			
		||||
            count -= 2;
 | 
			
		||||
        }
 | 
			
		||||
        /// * reverse padding 
 | 
			
		||||
        if (Padding)
 | 
			
		||||
        {
 | 
			
		||||
            Array.Resize(ref data, data.Length - 1);
 | 
			
		||||
            //data = (byte[])Resize(data, data.Length - 1);
 | 
			
		||||
        }
 | 
			
		||||
        ///* Return one's compliment of final sum. 
 | 
			
		||||
        //return (ushort)(ushort.MaxValue - (ushort)Sum);
 | 
			
		||||
        return (ushort)(~Sum);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ushort GetInteger(byte B1, byte B2)
 | 
			
		||||
    {
 | 
			
		||||
        return BitConverter.ToUInt16(new byte[] { B2, B1 }, 0);
 | 
			
		||||
        //return System.Convert.ToUInt16("&h" + GetHex(B1) + GetHex(B2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static uint GetLong(byte B1, byte B2, byte B3, byte B4)
 | 
			
		||||
    {
 | 
			
		||||
        return BitConverter.ToUInt32(new byte[] { B4, B3, B2, B1 }, 0);
 | 
			
		||||
        //return System.Convert.ToUInt32("&h" + GetHex(B1) + GetHex(B2) + GetHex(B3) + GetHex(B4));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string GetHex(byte B)
 | 
			
		||||
    {
 | 
			
		||||
        return (((B < 15) ? 0 + System.Convert.ToString(B, 16).ToUpper() : System.Convert.ToString(B, 16).ToUpper()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static bool GetBit(uint B, byte Pos)
 | 
			
		||||
    {
 | 
			
		||||
        //return BitConverter.ToBoolean(BitConverter.GetBytes(B), Pos + 1);
 | 
			
		||||
        return (B & (uint)(Math.Pow(2, (Pos - 1)))) == (Math.Pow(2, (Pos - 1)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ushort RemoveBit(ushort I, byte Pos)
 | 
			
		||||
    {
 | 
			
		||||
        return (ushort)RemoveBit((uint)I, Pos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static uint RemoveBit(uint I, byte Pos)
 | 
			
		||||
    {
 | 
			
		||||
        if (GetBit(I, Pos))
 | 
			
		||||
        {
 | 
			
		||||
            return I - (uint)(Math.Pow(2, (Pos - 1)));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return I;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class Packet
 | 
			
		||||
    public static void SplitInteger(ushort I, ref byte BLeft, ref byte BRight)
 | 
			
		||||
    {
 | 
			
		||||
        //public EtherServer2.EthernetSource Source;
 | 
			
		||||
        byte[] b = BitConverter.GetBytes(I);
 | 
			
		||||
        BLeft = b[1];
 | 
			
		||||
        BRight = b[0];
 | 
			
		||||
        //BLeft = I >> 8;
 | 
			
		||||
        //BRight = (I << 8) >> 8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public PacketSource Source;
 | 
			
		||||
    public static void SplitLong(uint I, ref byte BLeft, ref byte BLeftMiddle, ref byte BRightMiddle, ref byte BRight)
 | 
			
		||||
    {
 | 
			
		||||
        byte[] b = BitConverter.GetBytes(I);
 | 
			
		||||
        BLeft = b[3];
 | 
			
		||||
        BLeftMiddle = b[2];
 | 
			
		||||
        BRightMiddle = b[1];
 | 
			
		||||
        BRight = b[0];
 | 
			
		||||
        //BLeft = I >> 24;
 | 
			
		||||
        //BLeftMiddle = (I << 8) >> 24;
 | 
			
		||||
        //BRightMiddle = (I << 16) >> 24;
 | 
			
		||||
        //BRight = (I << 24) >> 24;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public DateTime Timestamp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        public enum PPPType : ushort
 | 
			
		||||
        {
 | 
			
		||||
            IP = 0x0021,                         // Internet Protocol version 4                   [RFC1332]
 | 
			
		||||
            SDTP = 0x0049,                       // Serial Data Transport Protocol (PPP-SDTP)     [RFC1963]
 | 
			
		||||
            IPv6HeaderCompression = 0x004f,      // IPv6 Header Compression
 | 
			
		||||
            IPv6 = 0x0057,                       // Internet Protocol version 6                   [RFC5072]
 | 
			
		||||
            W8021dHelloPacket = 0x0201,          // 802.1d Hello Packets                          [RFC3518]
 | 
			
		||||
            IPv6ControlProtocol = 0x8057,        // IPv6 Control Protocol                         [RFC5072]
 | 
			
		||||
        }
 | 
			
		||||
public class PosixTime
 | 
			
		||||
{
 | 
			
		||||
    ulong seconds;
 | 
			
		||||
    ulong microseconds;
 | 
			
		||||
 | 
			
		||||
        public enum ProtocolType : ushort
 | 
			
		||||
        {
 | 
			
		||||
            IP = 0x800,                          // IPv4
 | 
			
		||||
            ARP = 0x806,                         // Address Resolution Protocol
 | 
			
		||||
            IPv6 = 0x86DD,                       // IPv6
 | 
			
		||||
            FrameRelayARP = 0x0808,              // Frame Relay ARP          [RFC1701]
 | 
			
		||||
            VINESLoopback = 0x0BAE,              // VINES Loopback           [RFC1701]
 | 
			
		||||
            VINESEcho = 0x0BAF,                  // VINES ECHO               [RFC1701]
 | 
			
		||||
            TransEtherBridging = 0x6558,         // TransEther Bridging      [RFC1701]
 | 
			
		||||
            RawFrameRelay = 0x6559,              // Raw Frame Relay          [RFC1701]
 | 
			
		||||
            IEE8021QVLAN = 0x8100,               // IEEE 802.1Q VLAN-tagged frames (initially Wellfleet)
 | 
			
		||||
            SNMP = 0x814C,                       // SNMP                     [JKR1]
 | 
			
		||||
            TCPIP_Compression = 0x876B,          // TCP/IP Compression       [RFC1144]
 | 
			
		||||
            IPAutonomousSystems = 0x876C,        // IP Autonomous Systems    [RFC1701]
 | 
			
		||||
            SecureData = 0x876D,                 // Secure Data              [RFC1701]
 | 
			
		||||
            PPP = 0x880B,                        // PPP                      [IANA] 
 | 
			
		||||
            MPLS = 0x8847,                       // MPLS                     [RFC5332]
 | 
			
		||||
            MPLS_UpstreamAssignedLabel = 0x8848, // MPLS with upstream-assigned label   [RFC5332]
 | 
			
		||||
            PPPoEDiscoveryStage = 0x8863,        // PPPoE Discovery Stage    [RFC2516]
 | 
			
		||||
            PPPoESessionStage = 0x8864,          // PPPoE Session Stage      [RFC2516]
 | 
			
		||||
        }
 | 
			
		||||
    PosixTime(ulong Seconds, ulong Microseconds)
 | 
			
		||||
    {
 | 
			
		||||
        seconds = Seconds;
 | 
			
		||||
        microseconds = Microseconds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return seconds + "." + microseconds;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class Packet
 | 
			
		||||
{
 | 
			
		||||
    //public EtherServer2.EthernetSource Source;
 | 
			
		||||
 | 
			
		||||
    public PacketSource Source;
 | 
			
		||||
 | 
			
		||||
    public DateTime Timestamp;
 | 
			
		||||
 | 
			
		||||
    public enum PPPType : ushort
 | 
			
		||||
    {
 | 
			
		||||
        IP = 0x0021,                         // Internet Protocol version 4                   [RFC1332]
 | 
			
		||||
        SDTP = 0x0049,                       // Serial Data Transport Protocol (PPP-SDTP)     [RFC1963]
 | 
			
		||||
        IPv6HeaderCompression = 0x004f,      // IPv6 Header Compression
 | 
			
		||||
        IPv6 = 0x0057,                       // Internet Protocol version 6                   [RFC5072]
 | 
			
		||||
        W8021dHelloPacket = 0x0201,          // 802.1d Hello Packets                          [RFC3518]
 | 
			
		||||
        IPv6ControlProtocol = 0x8057,        // IPv6 Control Protocol                         [RFC5072]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum ProtocolType : ushort
 | 
			
		||||
    {
 | 
			
		||||
        IP = 0x800,                          // IPv4
 | 
			
		||||
        ARP = 0x806,                         // Address Resolution Protocol
 | 
			
		||||
        IPv6 = 0x86DD,                       // IPv6
 | 
			
		||||
        FrameRelayARP = 0x0808,              // Frame Relay ARP          [RFC1701]
 | 
			
		||||
        VINESLoopback = 0x0BAE,              // VINES Loopback           [RFC1701]
 | 
			
		||||
        VINESEcho = 0x0BAF,                  // VINES ECHO               [RFC1701]
 | 
			
		||||
        TransEtherBridging = 0x6558,         // TransEther Bridging      [RFC1701]
 | 
			
		||||
        RawFrameRelay = 0x6559,              // Raw Frame Relay          [RFC1701]
 | 
			
		||||
        IEE8021QVLAN = 0x8100,               // IEEE 802.1Q VLAN-tagged frames (initially Wellfleet)
 | 
			
		||||
        SNMP = 0x814C,                       // SNMP                     [JKR1]
 | 
			
		||||
        TCPIP_Compression = 0x876B,          // TCP/IP Compression       [RFC1144]
 | 
			
		||||
        IPAutonomousSystems = 0x876C,        // IP Autonomous Systems    [RFC1701]
 | 
			
		||||
        SecureData = 0x876D,                 // Secure Data              [RFC1701]
 | 
			
		||||
        PPP = 0x880B,                        // PPP                      [IANA] 
 | 
			
		||||
        MPLS = 0x8847,                       // MPLS                     [RFC5332]
 | 
			
		||||
        MPLS_UpstreamAssignedLabel = 0x8848, // MPLS with upstream-assigned label   [RFC5332]
 | 
			
		||||
        PPPoEDiscoveryStage = 0x8863,        // PPPoE Discovery Stage    [RFC2516]
 | 
			
		||||
        PPPoESessionStage = 0x8864,          // PPPoE Session Stage      [RFC2516]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static void GetPacketMACAddresses(Packet packet, out byte[] srcMAC, out byte[] dstMAC)
 | 
			
		||||
    /*
 | 
			
		||||
    public static void GetPacketMACAddresses(Packet packet, out byte[] srcMAC, out byte[] dstMAC)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        // get the node address
 | 
			
		||||
        Packet root = packet.RootPacket;
 | 
			
		||||
        if (root is TZSPPacket)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            // get the node address
 | 
			
		||||
            Packet root = packet.RootPacket;
 | 
			
		||||
            if (root is TZSPPacket)
 | 
			
		||||
            TZSPPacket tp = (TZSPPacket)root;
 | 
			
		||||
            if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                TZSPPacket tp = (TZSPPacket)root;
 | 
			
		||||
                if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet)
 | 
			
		||||
                {
 | 
			
		||||
                    EthernetPacket ep = (EthernetPacket)tp.SubPacket;
 | 
			
		||||
                    srcMAC = ep.SourceMAC;
 | 
			
		||||
                    dstMAC = ep.DestinationMAC;
 | 
			
		||||
                }
 | 
			
		||||
                else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11)
 | 
			
		||||
                {
 | 
			
		||||
                    W802_11Packet wp = (W802_11Packet)tp.SubPacket;
 | 
			
		||||
                    srcMAC = wp.SA;
 | 
			
		||||
                    dstMAC = wp.DA;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    srcMAC = null;
 | 
			
		||||
                    dstMAC = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (root is EthernetPacket)
 | 
			
		||||
            {
 | 
			
		||||
                EthernetPacket ep = (EthernetPacket)root;
 | 
			
		||||
                EthernetPacket ep = (EthernetPacket)tp.SubPacket;
 | 
			
		||||
                srcMAC = ep.SourceMAC;
 | 
			
		||||
                dstMAC = ep.DestinationMAC;
 | 
			
		||||
            }
 | 
			
		||||
            else if (root is W802_11Packet)
 | 
			
		||||
            else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11)
 | 
			
		||||
            {
 | 
			
		||||
                W802_11Packet wp = (W802_11Packet)root;
 | 
			
		||||
                W802_11Packet wp = (W802_11Packet)tp.SubPacket;
 | 
			
		||||
                srcMAC = wp.SA;
 | 
			
		||||
                dstMAC = wp.DA;
 | 
			
		||||
            }
 | 
			
		||||
@@ -278,92 +259,109 @@ namespace Esiur.Net.Packets
 | 
			
		||||
                srcMAC = null;
 | 
			
		||||
                dstMAC = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else if (root is EthernetPacket)
 | 
			
		||||
        {
 | 
			
		||||
            EthernetPacket ep = (EthernetPacket)root;
 | 
			
		||||
            srcMAC = ep.SourceMAC;
 | 
			
		||||
            dstMAC = ep.DestinationMAC;
 | 
			
		||||
        }
 | 
			
		||||
        else if (root is W802_11Packet)
 | 
			
		||||
        {
 | 
			
		||||
            W802_11Packet wp = (W802_11Packet)root;
 | 
			
		||||
            srcMAC = wp.SA;
 | 
			
		||||
            dstMAC = wp.DA;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            srcMAC = null;
 | 
			
		||||
            dstMAC = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        public static void GetPacketAddresses(Packet packet, ref string srcMAC, ref string dstMAC, ref string srcIP, ref string dstIP)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static void GetPacketAddresses(Packet packet, ref string srcMAC, ref string dstMAC, ref string srcIP, ref string dstIP)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (packet is TCPv4Packet)
 | 
			
		||||
        {
 | 
			
		||||
            if (packet.ParentPacket is IPv4Packet)
 | 
			
		||||
            {
 | 
			
		||||
                IPv4Packet ip = (IPv4Packet)packet.ParentPacket;
 | 
			
		||||
                srcIP = ip.SourceIP.ToString();
 | 
			
		||||
                dstIP = ip.DestinationIP.ToString();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // get the node address
 | 
			
		||||
        Packet root = packet.RootPacket;
 | 
			
		||||
        if (root is TZSPPacket)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (packet is TCPv4Packet)
 | 
			
		||||
            TZSPPacket tp = (TZSPPacket)root;
 | 
			
		||||
            if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet)
 | 
			
		||||
            {
 | 
			
		||||
                if (packet.ParentPacket is IPv4Packet)
 | 
			
		||||
                {
 | 
			
		||||
                    IPv4Packet ip = (IPv4Packet)packet.ParentPacket;
 | 
			
		||||
                    srcIP = ip.SourceIP.ToString();
 | 
			
		||||
                    dstIP = ip.DestinationIP.ToString();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // get the node address
 | 
			
		||||
            Packet root = packet.RootPacket;
 | 
			
		||||
            if (root is TZSPPacket)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                TZSPPacket tp = (TZSPPacket)root;
 | 
			
		||||
                if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet)
 | 
			
		||||
                {
 | 
			
		||||
                    EthernetPacket ep = (EthernetPacket)tp.SubPacket;
 | 
			
		||||
                    srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString();
 | 
			
		||||
                    dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString();
 | 
			
		||||
                }
 | 
			
		||||
                else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11)
 | 
			
		||||
                {
 | 
			
		||||
                    W802_11Packet wp = (W802_11Packet)tp.SubPacket;
 | 
			
		||||
                    srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString();
 | 
			
		||||
                    dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (root is EthernetPacket)
 | 
			
		||||
            {
 | 
			
		||||
                EthernetPacket ep = (EthernetPacket)root;
 | 
			
		||||
                EthernetPacket ep = (EthernetPacket)tp.SubPacket;
 | 
			
		||||
                srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString();
 | 
			
		||||
                dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString();
 | 
			
		||||
            }
 | 
			
		||||
            else if (root is W802_11Packet)
 | 
			
		||||
            else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11)
 | 
			
		||||
            {
 | 
			
		||||
                W802_11Packet wp = (W802_11Packet)root;
 | 
			
		||||
                W802_11Packet wp = (W802_11Packet)tp.SubPacket;
 | 
			
		||||
                srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString();
 | 
			
		||||
                dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        //PosixTime Timeval;
 | 
			
		||||
        public byte[] Header;
 | 
			
		||||
        public byte[] Preamble;
 | 
			
		||||
        //public byte[] Payload;
 | 
			
		||||
        public byte[] Data;
 | 
			
		||||
 | 
			
		||||
        public Packet SubPacket;
 | 
			
		||||
        public Packet ParentPacket;
 | 
			
		||||
        public virtual long Parse(byte[] data, uint offset, uint ends) { return 0; }
 | 
			
		||||
        public virtual bool Compose() { return false; }
 | 
			
		||||
 | 
			
		||||
        public Packet RootPacket
 | 
			
		||||
        else if (root is EthernetPacket)
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                Packet root = this;
 | 
			
		||||
                while (root.ParentPacket != null)
 | 
			
		||||
                    root = root.ParentPacket;
 | 
			
		||||
                return root;
 | 
			
		||||
            }
 | 
			
		||||
            EthernetPacket ep = (EthernetPacket)root;
 | 
			
		||||
            srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString();
 | 
			
		||||
            dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Packet LeafPacket
 | 
			
		||||
        else if (root is W802_11Packet)
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                Packet leaf = this;
 | 
			
		||||
                while (leaf.SubPacket != null)
 | 
			
		||||
                    leaf = leaf.SubPacket;
 | 
			
		||||
                return leaf;
 | 
			
		||||
            }
 | 
			
		||||
            W802_11Packet wp = (W802_11Packet)root;
 | 
			
		||||
            srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString();
 | 
			
		||||
            dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    //PosixTime Timeval;
 | 
			
		||||
    public byte[] Header;
 | 
			
		||||
    public byte[] Preamble;
 | 
			
		||||
    //public byte[] Payload;
 | 
			
		||||
    public byte[] Data;
 | 
			
		||||
 | 
			
		||||
    public Packet SubPacket;
 | 
			
		||||
    public Packet ParentPacket;
 | 
			
		||||
    public virtual long Parse(byte[] data, uint offset, uint ends) { return 0; }
 | 
			
		||||
    public virtual bool Compose() { return false; }
 | 
			
		||||
 | 
			
		||||
    public Packet RootPacket
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            Packet root = this;
 | 
			
		||||
            while (root.ParentPacket != null)
 | 
			
		||||
                root = root.ParentPacket;
 | 
			
		||||
            return root;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Packet LeafPacket
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            Packet leaf = this;
 | 
			
		||||
            while (leaf.SubPacket != null)
 | 
			
		||||
                leaf = leaf.SubPacket;
 | 
			
		||||
            return leaf;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/************************************ EOF *************************************/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,189 +29,187 @@ using System.Text;
 | 
			
		||||
using Esiur.Misc;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Packets
 | 
			
		||||
namespace Esiur.Net.Packets;
 | 
			
		||||
public class WebsocketPacket : Packet
 | 
			
		||||
{
 | 
			
		||||
    public class WebsocketPacket : Packet
 | 
			
		||||
    public enum WSOpcode : byte
 | 
			
		||||
    {
 | 
			
		||||
        public enum WSOpcode : byte
 | 
			
		||||
        ContinuationFrame = 0x0, //  %x0 denotes a continuation frame
 | 
			
		||||
 | 
			
		||||
        TextFrame = 0x1, // %x1 denotes a text frame
 | 
			
		||||
 | 
			
		||||
        BinaryFrame = 0x2,            // %x2 denotes a binary frame
 | 
			
		||||
 | 
			
		||||
        // %x3-7 are reserved for further non-control frames
 | 
			
		||||
 | 
			
		||||
        ConnectionClose = 0x8,    // %x8 denotes a connection close
 | 
			
		||||
 | 
			
		||||
        Ping = 0x9, // %x9 denotes a ping
 | 
			
		||||
 | 
			
		||||
        Pong = 0xA,            // %xA denotes a pong
 | 
			
		||||
 | 
			
		||||
        //*  %xB-F are reserved for further control frames
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool FIN;
 | 
			
		||||
    public bool RSV1;
 | 
			
		||||
    public bool RSV2;
 | 
			
		||||
    public bool RSV3;
 | 
			
		||||
    public WSOpcode Opcode;
 | 
			
		||||
    public bool Mask;
 | 
			
		||||
    public long PayloadLength;
 | 
			
		||||
    //        public UInt32 MaskKey;
 | 
			
		||||
    public byte[] MaskKey;
 | 
			
		||||
 | 
			
		||||
    public byte[] Message;
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return "WebsocketPacket"
 | 
			
		||||
            + "\n\tFIN: " + FIN
 | 
			
		||||
            + "\n\tOpcode: " + Opcode
 | 
			
		||||
            + "\n\tPayload: " + PayloadLength
 | 
			
		||||
            + "\n\tMaskKey: " + MaskKey
 | 
			
		||||
            + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool Compose()
 | 
			
		||||
    {
 | 
			
		||||
        var pkt = new List<byte>();
 | 
			
		||||
        pkt.Add((byte)((FIN ? 0x80 : 0x0) |
 | 
			
		||||
            (RSV1 ? 0x40 : 0x0) |
 | 
			
		||||
            (RSV2 ? 0x20 : 0x0) |
 | 
			
		||||
            (RSV3 ? 0x10 : 0x0) |
 | 
			
		||||
            (byte)Opcode));
 | 
			
		||||
 | 
			
		||||
        // calculate length
 | 
			
		||||
        if (Message.Length > UInt16.MaxValue)
 | 
			
		||||
        // 4 bytes
 | 
			
		||||
        {
 | 
			
		||||
            ContinuationFrame = 0x0, //  %x0 denotes a continuation frame
 | 
			
		||||
 | 
			
		||||
            TextFrame = 0x1, // %x1 denotes a text frame
 | 
			
		||||
 | 
			
		||||
            BinaryFrame = 0x2,            // %x2 denotes a binary frame
 | 
			
		||||
 | 
			
		||||
            // %x3-7 are reserved for further non-control frames
 | 
			
		||||
 | 
			
		||||
            ConnectionClose = 0x8,    // %x8 denotes a connection close
 | 
			
		||||
 | 
			
		||||
            Ping = 0x9, // %x9 denotes a ping
 | 
			
		||||
 | 
			
		||||
            Pong = 0xA,            // %xA denotes a pong
 | 
			
		||||
 | 
			
		||||
            //*  %xB-F are reserved for further control frames
 | 
			
		||||
            pkt.Add((byte)((Mask ? 0x80 : 0x0) | 127));
 | 
			
		||||
            pkt.AddRange(DC.ToBytes((UInt64)Message.LongCount()));
 | 
			
		||||
        }
 | 
			
		||||
        else if (Message.Length > 125)
 | 
			
		||||
        // 2 bytes
 | 
			
		||||
        {
 | 
			
		||||
            pkt.Add((byte)((Mask ? 0x80 : 0x0) | 126));
 | 
			
		||||
            pkt.AddRange(DC.ToBytes((UInt16)Message.Length));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            pkt.Add((byte)((Mask ? 0x80 : 0x0) | Message.Length));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool FIN;
 | 
			
		||||
        public bool RSV1;
 | 
			
		||||
        public bool RSV2;
 | 
			
		||||
        public bool RSV3;
 | 
			
		||||
        public WSOpcode Opcode;
 | 
			
		||||
        public bool Mask;
 | 
			
		||||
        public long PayloadLength;
 | 
			
		||||
        //        public UInt32 MaskKey;
 | 
			
		||||
        public byte[] MaskKey;
 | 
			
		||||
 | 
			
		||||
        public byte[] Message;
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        if (Mask)
 | 
			
		||||
        {
 | 
			
		||||
            return "WebsocketPacket"
 | 
			
		||||
                + "\n\tFIN: " + FIN
 | 
			
		||||
                + "\n\tOpcode: " + Opcode
 | 
			
		||||
                + "\n\tPayload: " + PayloadLength
 | 
			
		||||
                + "\n\tMaskKey: " + MaskKey
 | 
			
		||||
                + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
 | 
			
		||||
            pkt.AddRange(MaskKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Compose()
 | 
			
		||||
        {
 | 
			
		||||
            var pkt = new List<byte>();
 | 
			
		||||
            pkt.Add((byte)((FIN ? 0x80 : 0x0) |
 | 
			
		||||
                (RSV1 ? 0x40 : 0x0) |
 | 
			
		||||
                (RSV2 ? 0x20 : 0x0) |
 | 
			
		||||
                (RSV3 ? 0x10 : 0x0) |
 | 
			
		||||
                (byte)Opcode));
 | 
			
		||||
        pkt.AddRange(Message);
 | 
			
		||||
 | 
			
		||||
            // calculate length
 | 
			
		||||
            if (Message.Length > UInt16.MaxValue)
 | 
			
		||||
            // 4 bytes
 | 
			
		||||
        Data = pkt.ToArray();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            long needed = 2;
 | 
			
		||||
            var length = (ends - offset);
 | 
			
		||||
            if (length < needed)
 | 
			
		||||
            {
 | 
			
		||||
                pkt.Add((byte)((Mask ? 0x80 : 0x0) | 127));
 | 
			
		||||
                pkt.AddRange(DC.ToBytes((UInt64)Message.LongCount()));
 | 
			
		||||
            }
 | 
			
		||||
            else if (Message.Length > 125)
 | 
			
		||||
            // 2 bytes
 | 
			
		||||
            {
 | 
			
		||||
                pkt.Add((byte)((Mask ? 0x80 : 0x0) | 126));
 | 
			
		||||
                pkt.AddRange(DC.ToBytes((UInt16)Message.Length));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                pkt.Add((byte)((Mask ? 0x80 : 0x0) | Message.Length));
 | 
			
		||||
                //Console.WriteLine("stage 1 " + needed);
 | 
			
		||||
                return length - needed;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint oOffset = offset;
 | 
			
		||||
            FIN = ((data[offset] & 0x80) == 0x80);
 | 
			
		||||
            RSV1 = ((data[offset] & 0x40) == 0x40);
 | 
			
		||||
            RSV2 = ((data[offset] & 0x20) == 0x20);
 | 
			
		||||
            RSV3 = ((data[offset] & 0x10) == 0x10);
 | 
			
		||||
            Opcode = (WSOpcode)(data[offset++] & 0xF);
 | 
			
		||||
            Mask = ((data[offset] & 0x80) == 0x80);
 | 
			
		||||
            PayloadLength = (long)(data[offset++] & 0x7F);
 | 
			
		||||
 | 
			
		||||
            if (Mask)
 | 
			
		||||
                needed += 4;
 | 
			
		||||
 | 
			
		||||
            if (PayloadLength == 126)
 | 
			
		||||
            {
 | 
			
		||||
                pkt.AddRange(MaskKey);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pkt.AddRange(Message);
 | 
			
		||||
 | 
			
		||||
            Data = pkt.ToArray();
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override long Parse(byte[] data, uint offset, uint ends)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                long needed = 2;
 | 
			
		||||
                var length = (ends - offset);
 | 
			
		||||
                needed += 2;
 | 
			
		||||
                if (length < needed)
 | 
			
		||||
                {
 | 
			
		||||
                    //Console.WriteLine("stage 1 " + needed);
 | 
			
		||||
                    //Console.WriteLine("stage 2 " + needed);
 | 
			
		||||
                    return length - needed;
 | 
			
		||||
                }
 | 
			
		||||
                PayloadLength = data.GetUInt16(offset);
 | 
			
		||||
                offset += 2;
 | 
			
		||||
            }
 | 
			
		||||
            else if (PayloadLength == 127)
 | 
			
		||||
            {
 | 
			
		||||
                needed += 8;
 | 
			
		||||
                if (length < needed)
 | 
			
		||||
                {
 | 
			
		||||
                    //Console.WriteLine("stage 3 " + needed);
 | 
			
		||||
                    return length - needed;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                uint oOffset = offset;
 | 
			
		||||
                FIN = ((data[offset] & 0x80) == 0x80);
 | 
			
		||||
                RSV1 = ((data[offset] & 0x40) == 0x40);
 | 
			
		||||
                RSV2 = ((data[offset] & 0x20) == 0x20);
 | 
			
		||||
                RSV3 = ((data[offset] & 0x10) == 0x10);
 | 
			
		||||
                Opcode = (WSOpcode)(data[offset++] & 0xF);
 | 
			
		||||
                Mask = ((data[offset] & 0x80) == 0x80);
 | 
			
		||||
                PayloadLength = (long)(data[offset++] & 0x7F);
 | 
			
		||||
                PayloadLength = data.GetInt64(offset);
 | 
			
		||||
                offset += 8;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            if (Mask)
 | 
			
		||||
            {
 | 
			
		||||
                                MaskKey = new byte[4];
 | 
			
		||||
                MaskKey[0] = data[offset++];
 | 
			
		||||
                MaskKey[1] = data[offset++];
 | 
			
		||||
                MaskKey[2] = data[offset++];
 | 
			
		||||
                MaskKey[3] = data[offset++];
 | 
			
		||||
 | 
			
		||||
                //MaskKey = DC.GetUInt32(data, offset);
 | 
			
		||||
                //offset += 4;
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
 | 
			
		||||
            needed += PayloadLength;
 | 
			
		||||
            if (length < needed)
 | 
			
		||||
            {
 | 
			
		||||
                //Console.WriteLine("stage 4");
 | 
			
		||||
                return length - needed;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (Mask)
 | 
			
		||||
                    needed += 4;
 | 
			
		||||
 | 
			
		||||
                if (PayloadLength == 126)
 | 
			
		||||
                {
 | 
			
		||||
                    needed += 2;
 | 
			
		||||
                    if (length < needed)
 | 
			
		||||
                    {
 | 
			
		||||
                        //Console.WriteLine("stage 2 " + needed);
 | 
			
		||||
                        return length - needed;
 | 
			
		||||
                    }
 | 
			
		||||
                    PayloadLength = data.GetUInt16( offset);
 | 
			
		||||
                    offset += 2;
 | 
			
		||||
                }
 | 
			
		||||
                else if (PayloadLength == 127)
 | 
			
		||||
                {
 | 
			
		||||
                    needed += 8;
 | 
			
		||||
                    if (length < needed)
 | 
			
		||||
                    {
 | 
			
		||||
                        //Console.WriteLine("stage 3 " + needed);
 | 
			
		||||
                        return length - needed;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    PayloadLength = data.GetInt64(offset);
 | 
			
		||||
                    offset += 8;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                /*
 | 
			
		||||
                if (Mask)
 | 
			
		||||
                {
 | 
			
		||||
                                    MaskKey = new byte[4];
 | 
			
		||||
                    MaskKey = new byte[4];
 | 
			
		||||
                    MaskKey[0] = data[offset++];
 | 
			
		||||
                    MaskKey[1] = data[offset++];
 | 
			
		||||
                    MaskKey[2] = data[offset++];
 | 
			
		||||
                    MaskKey[3] = data[offset++];
 | 
			
		||||
 | 
			
		||||
                    //MaskKey = DC.GetUInt32(data, offset);
 | 
			
		||||
                    //offset += 4;
 | 
			
		||||
                }
 | 
			
		||||
                */
 | 
			
		||||
                    Message = DC.Clip(data, offset, (uint)PayloadLength);
 | 
			
		||||
 | 
			
		||||
                needed += PayloadLength;
 | 
			
		||||
                if (length < needed)
 | 
			
		||||
                {
 | 
			
		||||
                    //Console.WriteLine("stage 4");
 | 
			
		||||
                    return length - needed;
 | 
			
		||||
                    //var aMask = BitConverter.GetBytes(MaskKey);
 | 
			
		||||
                    for (int i = 0; i < Message.Length; i++)
 | 
			
		||||
                        Message[i] = (byte)(Message[i] ^ MaskKey[i % 4]);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    if (Mask)
 | 
			
		||||
                    {
 | 
			
		||||
                        MaskKey = new byte[4];
 | 
			
		||||
                        MaskKey[0] = data[offset++];
 | 
			
		||||
                        MaskKey[1] = data[offset++];
 | 
			
		||||
                        MaskKey[2] = data[offset++];
 | 
			
		||||
                        MaskKey[3] = data[offset++];
 | 
			
		||||
 | 
			
		||||
                        Message = DC.Clip(data, offset, (uint)PayloadLength);
 | 
			
		||||
 | 
			
		||||
                        //var aMask = BitConverter.GetBytes(MaskKey);
 | 
			
		||||
                        for (int i = 0; i < Message.Length; i++)
 | 
			
		||||
                            Message[i] = (byte)(Message[i] ^ MaskKey[i % 4]);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                        Message = DC.Clip(data, offset, (uint)PayloadLength);
 | 
			
		||||
                    Message = DC.Clip(data, offset, (uint)PayloadLength);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    return (offset - oOffset) + (int)PayloadLength;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine(ex.ToString());
 | 
			
		||||
                Console.WriteLine(offset + "::" + DC.ToHex(data));
 | 
			
		||||
                throw ex;
 | 
			
		||||
                return (offset - oOffset) + (int)PayloadLength;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Console.WriteLine(ex.ToString());
 | 
			
		||||
            Console.WriteLine(offset + "::" + DC.ToHex(data));
 | 
			
		||||
            throw ex;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,23 +4,22 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net
 | 
			
		||||
namespace Esiur.Net;
 | 
			
		||||
 | 
			
		||||
public class SendList : BinaryList
 | 
			
		||||
{
 | 
			
		||||
    public class SendList : BinaryList
 | 
			
		||||
    NetworkConnection connection;
 | 
			
		||||
    AsyncReply<object[]> reply;
 | 
			
		||||
 | 
			
		||||
    public SendList(NetworkConnection connection, AsyncReply<object[]> reply)
 | 
			
		||||
    {
 | 
			
		||||
        NetworkConnection connection;
 | 
			
		||||
        AsyncReply<object[]> reply;
 | 
			
		||||
        this.reply = reply;
 | 
			
		||||
        this.connection = connection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public SendList(NetworkConnection connection, AsyncReply<object[]> reply)
 | 
			
		||||
        {
 | 
			
		||||
            this.reply = reply;
 | 
			
		||||
            this.connection = connection;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override AsyncReply<object[]> Done()
 | 
			
		||||
        {
 | 
			
		||||
            connection.Send(this.ToArray());
 | 
			
		||||
            return reply;
 | 
			
		||||
        }
 | 
			
		||||
    public override AsyncReply<object[]> Done()
 | 
			
		||||
    {
 | 
			
		||||
        connection.Send(this.ToArray());
 | 
			
		||||
        return reply;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,39 +36,37 @@ using System.Collections.Concurrent;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Sockets
 | 
			
		||||
namespace Esiur.Net.Sockets;
 | 
			
		||||
//public delegate void ISocketReceiveEvent(NetworkBuffer buffer);
 | 
			
		||||
//public delegate void ISocketConnectEvent();
 | 
			
		||||
//public delegate void ISocketCloseEvent();
 | 
			
		||||
 | 
			
		||||
public interface ISocket : IDestructible
 | 
			
		||||
{
 | 
			
		||||
    //public delegate void ISocketReceiveEvent(NetworkBuffer buffer);
 | 
			
		||||
    //public delegate void ISocketConnectEvent();
 | 
			
		||||
    //public delegate void ISocketCloseEvent();
 | 
			
		||||
    SocketState State { get; }
 | 
			
		||||
 | 
			
		||||
    public interface ISocket : IDestructible
 | 
			
		||||
    {
 | 
			
		||||
        SocketState State { get; }
 | 
			
		||||
    //event ISocketReceiveEvent OnReceive;
 | 
			
		||||
    //event ISocketConnectEvent OnConnect;
 | 
			
		||||
    //event ISocketCloseEvent OnClose;
 | 
			
		||||
 | 
			
		||||
        //event ISocketReceiveEvent OnReceive;
 | 
			
		||||
        //event ISocketConnectEvent OnConnect;
 | 
			
		||||
        //event ISocketCloseEvent OnClose;
 | 
			
		||||
    INetworkReceiver<ISocket> Receiver { get; set; }
 | 
			
		||||
 | 
			
		||||
        INetworkReceiver<ISocket> Receiver { get; set; }
 | 
			
		||||
    AsyncReply<bool> SendAsync(byte[] message, int offset, int length);
 | 
			
		||||
 | 
			
		||||
        AsyncReply<bool> SendAsync(byte[] message, int offset, int length);
 | 
			
		||||
    void Send(byte[] message);
 | 
			
		||||
    void Send(byte[] message, int offset, int length);
 | 
			
		||||
    void Close();
 | 
			
		||||
    AsyncReply<bool> Connect(string hostname, ushort port);
 | 
			
		||||
    bool Begin();
 | 
			
		||||
    AsyncReply<bool> BeginAsync();
 | 
			
		||||
    //ISocket Accept();
 | 
			
		||||
    AsyncReply<ISocket> AcceptAsync();
 | 
			
		||||
    ISocket Accept();
 | 
			
		||||
 | 
			
		||||
        void Send(byte[] message);
 | 
			
		||||
        void Send(byte[] message, int offset, int length);
 | 
			
		||||
        void Close();
 | 
			
		||||
        AsyncReply<bool> Connect(string hostname, ushort port);
 | 
			
		||||
        bool Begin();
 | 
			
		||||
        AsyncReply<bool> BeginAsync();
 | 
			
		||||
        //ISocket Accept();
 | 
			
		||||
        AsyncReply<ISocket> AcceptAsync();
 | 
			
		||||
        ISocket Accept();
 | 
			
		||||
    IPEndPoint RemoteEndPoint { get; }
 | 
			
		||||
    IPEndPoint LocalEndPoint { get; }
 | 
			
		||||
 | 
			
		||||
        IPEndPoint RemoteEndPoint { get; }
 | 
			
		||||
        IPEndPoint LocalEndPoint { get; }
 | 
			
		||||
    void Hold();
 | 
			
		||||
 | 
			
		||||
        void Hold();
 | 
			
		||||
 | 
			
		||||
        void Unhold();
 | 
			
		||||
    }
 | 
			
		||||
    void Unhold();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,519 +37,518 @@ using Esiur.Resource;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Sockets
 | 
			
		||||
namespace Esiur.Net.Sockets;
 | 
			
		||||
public class SSLSocket : ISocket
 | 
			
		||||
{
 | 
			
		||||
    public class SSLSocket : ISocket
 | 
			
		||||
 | 
			
		||||
    public INetworkReceiver<ISocket> Receiver { get; set; }
 | 
			
		||||
 | 
			
		||||
    Socket sock;
 | 
			
		||||
    byte[] receiveBuffer;
 | 
			
		||||
 | 
			
		||||
    bool held;
 | 
			
		||||
 | 
			
		||||
    //ArraySegment<byte> receiveBufferSegment;
 | 
			
		||||
 | 
			
		||||
    NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
 | 
			
		||||
 | 
			
		||||
    readonly object sendLock = new object();
 | 
			
		||||
 | 
			
		||||
    Queue<KeyValuePair<AsyncReply<bool>, byte[]>> sendBufferQueue = new Queue<KeyValuePair<AsyncReply<bool>, byte[]>>();// Queue<byte[]>();
 | 
			
		||||
 | 
			
		||||
    bool asyncSending;
 | 
			
		||||
    bool began = false;
 | 
			
		||||
 | 
			
		||||
    SocketState state = SocketState.Initial;
 | 
			
		||||
 | 
			
		||||
    //public event ISocketReceiveEvent OnReceive;
 | 
			
		||||
    //public event ISocketConnectEvent OnConnect;
 | 
			
		||||
    //public event ISocketCloseEvent OnClose;
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    SslStream ssl;
 | 
			
		||||
    X509Certificate2 cert;
 | 
			
		||||
    bool server;
 | 
			
		||||
    string hostname;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public async AsyncReply<bool> Connect(string hostname, ushort port)
 | 
			
		||||
    {
 | 
			
		||||
        var rt = new AsyncReply<bool>();
 | 
			
		||||
 | 
			
		||||
        public INetworkReceiver<ISocket> Receiver { get; set; }
 | 
			
		||||
        this.hostname = hostname;
 | 
			
		||||
        this.server = false;
 | 
			
		||||
 | 
			
		||||
        Socket sock;
 | 
			
		||||
        byte[] receiveBuffer;
 | 
			
		||||
 | 
			
		||||
        bool held;
 | 
			
		||||
 | 
			
		||||
        //ArraySegment<byte> receiveBufferSegment;
 | 
			
		||||
 | 
			
		||||
        NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
 | 
			
		||||
 | 
			
		||||
        readonly object sendLock = new object();
 | 
			
		||||
 | 
			
		||||
        Queue<KeyValuePair<AsyncReply<bool>, byte[]>> sendBufferQueue = new Queue<KeyValuePair<AsyncReply<bool>, byte[]>>();// Queue<byte[]>();
 | 
			
		||||
 | 
			
		||||
        bool asyncSending;
 | 
			
		||||
        bool began = false;
 | 
			
		||||
 | 
			
		||||
        SocketState state = SocketState.Initial;
 | 
			
		||||
 | 
			
		||||
        //public event ISocketReceiveEvent OnReceive;
 | 
			
		||||
        //public event ISocketConnectEvent OnConnect;
 | 
			
		||||
        //public event ISocketCloseEvent OnClose;
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
        state = SocketState.Connecting;
 | 
			
		||||
        await sock.ConnectAsync(hostname, port);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        SslStream ssl;
 | 
			
		||||
        X509Certificate2 cert;
 | 
			
		||||
        bool server;
 | 
			
		||||
        string hostname;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public async AsyncReply<bool> Connect(string hostname, ushort port)
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var rt = new AsyncReply<bool>();
 | 
			
		||||
 | 
			
		||||
            this.hostname = hostname;
 | 
			
		||||
            this.server = false;
 | 
			
		||||
 | 
			
		||||
            state = SocketState.Connecting;
 | 
			
		||||
            await sock.ConnectAsync(hostname, port);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await BeginAsync();
 | 
			
		||||
                state = SocketState.Established;
 | 
			
		||||
                //OnConnect?.Invoke();
 | 
			
		||||
                Receiver?.NetworkConnect(this);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                state = SocketState.Closed;// .Terminated;
 | 
			
		||||
                Close();
 | 
			
		||||
                Global.Log(ex);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
            await BeginAsync();
 | 
			
		||||
            state = SocketState.Established;
 | 
			
		||||
            //OnConnect?.Invoke();
 | 
			
		||||
            Receiver?.NetworkConnect(this);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            state = SocketState.Closed;// .Terminated;
 | 
			
		||||
            Close();
 | 
			
		||||
            Global.Log(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //private void DataSent(Task task)
 | 
			
		||||
        //{
 | 
			
		||||
        //    try
 | 
			
		||||
        //    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        //        if (sendBufferQueue.Count > 0)
 | 
			
		||||
        //        {
 | 
			
		||||
        //            byte[] data = sendBufferQueue.Dequeue();
 | 
			
		||||
        //            lock (sendLock)
 | 
			
		||||
        //                ssl.WriteAsync(data, 0, data.Length).ContinueWith(DataSent);
 | 
			
		||||
        //        }
 | 
			
		||||
        //        else
 | 
			
		||||
        //        {
 | 
			
		||||
        //            asyncSending = false;
 | 
			
		||||
        //        }
 | 
			
		||||
        //    }
 | 
			
		||||
        //    catch (Exception ex)
 | 
			
		||||
        //    {
 | 
			
		||||
        //        if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
        //        {
 | 
			
		||||
        //            state = SocketState.Terminated;
 | 
			
		||||
        //            Close();
 | 
			
		||||
        //        }
 | 
			
		||||
    //private void DataSent(Task task)
 | 
			
		||||
    //{
 | 
			
		||||
    //    try
 | 
			
		||||
    //    {
 | 
			
		||||
 | 
			
		||||
        //        asyncSending = false;
 | 
			
		||||
    //        if (sendBufferQueue.Count > 0)
 | 
			
		||||
    //        {
 | 
			
		||||
    //            byte[] data = sendBufferQueue.Dequeue();
 | 
			
		||||
    //            lock (sendLock)
 | 
			
		||||
    //                ssl.WriteAsync(data, 0, data.Length).ContinueWith(DataSent);
 | 
			
		||||
    //        }
 | 
			
		||||
    //        else
 | 
			
		||||
    //        {
 | 
			
		||||
    //            asyncSending = false;
 | 
			
		||||
    //        }
 | 
			
		||||
    //    }
 | 
			
		||||
    //    catch (Exception ex)
 | 
			
		||||
    //    {
 | 
			
		||||
    //        if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
    //        {
 | 
			
		||||
    //            state = SocketState.Terminated;
 | 
			
		||||
    //            Close();
 | 
			
		||||
    //        }
 | 
			
		||||
 | 
			
		||||
        //        Global.Log("SSLSocket", LogType.Error, ex.ToString());
 | 
			
		||||
        //    }
 | 
			
		||||
        //}
 | 
			
		||||
    //        asyncSending = false;
 | 
			
		||||
 | 
			
		||||
    //        Global.Log("SSLSocket", LogType.Error, ex.ToString());
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private void SendCallback(IAsyncResult ar)
 | 
			
		||||
        {
 | 
			
		||||
            if (ar != null)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ssl.EndWrite(ar);
 | 
			
		||||
 | 
			
		||||
                    if (ar.AsyncState != null)
 | 
			
		||||
                        ((AsyncReply<bool>)ar.AsyncState).Trigger(true);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
                    {
 | 
			
		||||
                        //state = SocketState.Closed;//.Terminated;
 | 
			
		||||
                        Close();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (sendBufferQueue.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    var kv = sendBufferQueue.Dequeue();
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch //(Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        asyncSending = false;
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            if (kv.Key != null)
 | 
			
		||||
                                kv.Key.Trigger(false);
 | 
			
		||||
 | 
			
		||||
                            if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
                            {
 | 
			
		||||
                                //state = SocketState.Terminated;
 | 
			
		||||
                                Close();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        catch //(Exception ex2)
 | 
			
		||||
                        {
 | 
			
		||||
                            //state = SocketState.Closed;// .Terminated;
 | 
			
		||||
                            Close();
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        //Global.Log("TCPSocket", LogType.Error, ex.ToString());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    asyncSending = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IPEndPoint LocalEndPoint
 | 
			
		||||
        {
 | 
			
		||||
            get { return (IPEndPoint)sock.LocalEndPoint; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SSLSocket()
 | 
			
		||||
        {
 | 
			
		||||
            sock = new Socket(AddressFamily.InterNetwork,
 | 
			
		||||
                                 SocketType.Stream,
 | 
			
		||||
                                 ProtocolType.Tcp);
 | 
			
		||||
            receiveBuffer = new byte[sock.ReceiveBufferSize];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SSLSocket(IPEndPoint localEndPoint, X509Certificate2 certificate)
 | 
			
		||||
        {
 | 
			
		||||
            // create the socket
 | 
			
		||||
            sock = new Socket(AddressFamily.InterNetwork,
 | 
			
		||||
                                             SocketType.Stream,
 | 
			
		||||
                                             ProtocolType.Tcp);
 | 
			
		||||
 | 
			
		||||
            state = SocketState.Listening;
 | 
			
		||||
 | 
			
		||||
            // bind
 | 
			
		||||
            sock.Bind(localEndPoint);
 | 
			
		||||
 | 
			
		||||
            // start listening
 | 
			
		||||
            sock.Listen(UInt16.MaxValue);
 | 
			
		||||
 | 
			
		||||
            cert = certificate;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IPEndPoint RemoteEndPoint
 | 
			
		||||
        {
 | 
			
		||||
            get { return (IPEndPoint)sock.RemoteEndPoint; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SocketState State
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return state;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public SSLSocket(Socket socket, X509Certificate2 certificate, bool authenticateAsServer)
 | 
			
		||||
        {
 | 
			
		||||
            cert = certificate;
 | 
			
		||||
            sock = socket;
 | 
			
		||||
            receiveBuffer = new byte[sock.ReceiveBufferSize];
 | 
			
		||||
 | 
			
		||||
            ssl = new SslStream(new NetworkStream(sock));
 | 
			
		||||
 | 
			
		||||
            server = authenticateAsServer;
 | 
			
		||||
 | 
			
		||||
            if (socket.Connected)
 | 
			
		||||
                state = SocketState.Established;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Close()
 | 
			
		||||
        {
 | 
			
		||||
            if (state != SocketState.Closed)// && state != SocketState.Terminated)
 | 
			
		||||
            {
 | 
			
		||||
                state = SocketState.Closed;
 | 
			
		||||
 | 
			
		||||
                if (sock.Connected)
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        sock.Shutdown(SocketShutdown.Both);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                        //state = SocketState.Terminated;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Receiver?.NetworkClose(this);
 | 
			
		||||
                //OnClose?.Invoke();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Send(byte[] message)
 | 
			
		||||
        {
 | 
			
		||||
            Send(message, 0, message.Length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Send(byte[] message, int offset, int size)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var msg = message.Clip((uint)offset, (uint)size);
 | 
			
		||||
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (!sock.Connected)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                if (asyncSending || held)
 | 
			
		||||
                {
 | 
			
		||||
                    sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(null, msg));// message.Clip((uint)offset, (uint)size));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    asyncSending = true;
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        ssl.BeginWrite(msg, 0, msg.Length, SendCallback, null);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
                        asyncSending = false;
 | 
			
		||||
                        //state = SocketState.Terminated;
 | 
			
		||||
                        Close();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //public void Send(byte[] message)
 | 
			
		||||
        //{
 | 
			
		||||
        //    Send(message, 0, message.Length);
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //public void Send(byte[] message, int offset, int size)
 | 
			
		||||
        //{
 | 
			
		||||
        //    lock (sendLock)
 | 
			
		||||
        //    {
 | 
			
		||||
        //        if (asyncSending)
 | 
			
		||||
        //        {
 | 
			
		||||
        //            sendBufferQueue.Enqueue(message.Clip((uint)offset, (uint)size));
 | 
			
		||||
        //        }
 | 
			
		||||
        //        else
 | 
			
		||||
        //        {
 | 
			
		||||
        //            asyncSending = true;
 | 
			
		||||
        //            ssl.WriteAsync(message, offset, size).ContinueWith(DataSent);
 | 
			
		||||
        //        }
 | 
			
		||||
        //    }
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //private void DataReceived(Task<int> task)
 | 
			
		||||
        //{
 | 
			
		||||
        //    try
 | 
			
		||||
        //    {
 | 
			
		||||
        //        if (state == SocketState.Closed || state == SocketState.Terminated)
 | 
			
		||||
        //            return;
 | 
			
		||||
 | 
			
		||||
        //        if (task.Result <= 0)
 | 
			
		||||
        //        {
 | 
			
		||||
        //            Close();
 | 
			
		||||
        //            return;
 | 
			
		||||
        //        }
 | 
			
		||||
 | 
			
		||||
        //        receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)task.Result);
 | 
			
		||||
        //        OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
        //        if (state == SocketState.Established)
 | 
			
		||||
        //            ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived);
 | 
			
		||||
        //    }
 | 
			
		||||
        //    catch (Exception ex)
 | 
			
		||||
        //    {
 | 
			
		||||
        //        if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
        //        {
 | 
			
		||||
        //            state = SocketState.Terminated;
 | 
			
		||||
        //            Close();
 | 
			
		||||
        //        }
 | 
			
		||||
 | 
			
		||||
        //        Global.Log("SSLSocket", LogType.Error, ex.ToString());
 | 
			
		||||
        //    }
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool Begin()
 | 
			
		||||
        {
 | 
			
		||||
            if (began)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            began = true;
 | 
			
		||||
 | 
			
		||||
            if (server)
 | 
			
		||||
                ssl.AuthenticateAsServer(cert);
 | 
			
		||||
            else
 | 
			
		||||
                ssl.AuthenticateAsClient(hostname);
 | 
			
		||||
 | 
			
		||||
            if (state == SocketState.Established)
 | 
			
		||||
            {
 | 
			
		||||
                ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async AsyncReply<bool> BeginAsync()
 | 
			
		||||
        {
 | 
			
		||||
            if (began)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            began = true;
 | 
			
		||||
 | 
			
		||||
            if (server)
 | 
			
		||||
                await ssl.AuthenticateAsServerAsync(cert);
 | 
			
		||||
            else
 | 
			
		||||
                await ssl.AuthenticateAsClientAsync(hostname);
 | 
			
		||||
 | 
			
		||||
            if (state == SocketState.Established)
 | 
			
		||||
            {
 | 
			
		||||
                ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ReceiveCallback(IAsyncResult results)
 | 
			
		||||
    private void SendCallback(IAsyncResult ar)
 | 
			
		||||
    {
 | 
			
		||||
        if (ar != null)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (state != SocketState.Established)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                var bytesReceived = ssl.EndRead(results);
 | 
			
		||||
 | 
			
		||||
                if (bytesReceived <= 0)
 | 
			
		||||
                {
 | 
			
		||||
                    Close();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)bytesReceived);
 | 
			
		||||
 | 
			
		||||
                //OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
 | 
			
		||||
                Receiver?.NetworkReceive(this, receiveNetworkBuffer);
 | 
			
		||||
 | 
			
		||||
                ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
 | 
			
		||||
                ssl.EndWrite(ar);
 | 
			
		||||
 | 
			
		||||
                if (ar.AsyncState != null)
 | 
			
		||||
                    ((AsyncReply<bool>)ar.AsyncState).Trigger(true);
 | 
			
		||||
            }
 | 
			
		||||
            catch //(Exception ex)
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
                {
 | 
			
		||||
                    //state = SocketState.Terminated;
 | 
			
		||||
                    //state = SocketState.Closed;//.Terminated;
 | 
			
		||||
                    Close();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                //Global.Log("SSLSocket", LogType.Error, ex.ToString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            Close();
 | 
			
		||||
            Receiver = null;
 | 
			
		||||
            receiveNetworkBuffer = null;
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
            OnDestroy = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async AsyncReply<ISocket> AcceptAsync()
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var s = await sock.AcceptAsync();
 | 
			
		||||
                return new SSLSocket(s, cert, true);
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                state = SocketState.Closed;// Terminated;
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Hold()
 | 
			
		||||
        {
 | 
			
		||||
            held = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Unhold()
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                SendCallback(null);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Global.Log(ex);
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                held = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> SendAsync(byte[] message, int offset, int length)
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            var msg = message.Clip((uint)offset, (uint)length);
 | 
			
		||||
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
            if (sendBufferQueue.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (!sock.Connected)
 | 
			
		||||
                    return new AsyncReply<bool>(false);
 | 
			
		||||
                var kv = sendBufferQueue.Dequeue();
 | 
			
		||||
 | 
			
		||||
                var rt = new AsyncReply<bool>();
 | 
			
		||||
 | 
			
		||||
                if (asyncSending || held)
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(rt, msg));
 | 
			
		||||
                    ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                catch //(Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    asyncSending = true;
 | 
			
		||||
                    asyncSending = false;
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        ssl.BeginWrite(msg, 0, msg.Length, SendCallback, rt);// null);
 | 
			
		||||
                        if (kv.Key != null)
 | 
			
		||||
                            kv.Key.Trigger(false);
 | 
			
		||||
 | 
			
		||||
                        if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
                        {
 | 
			
		||||
                            //state = SocketState.Terminated;
 | 
			
		||||
                            Close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    catch //(Exception ex2)
 | 
			
		||||
                    {
 | 
			
		||||
                        rt.TriggerError(ex);
 | 
			
		||||
                        asyncSending = false;
 | 
			
		||||
                        //state = SocketState.Terminated;
 | 
			
		||||
                        //state = SocketState.Closed;// .Terminated;
 | 
			
		||||
                        Close();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    //Global.Log("TCPSocket", LogType.Error, ex.ToString());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return rt;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public ISocket Accept()
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new SSLSocket(sock.Accept(), cert, true);
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                state = SocketState.Closed;// .Terminated;
 | 
			
		||||
                return null;
 | 
			
		||||
                asyncSending = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public IPEndPoint LocalEndPoint
 | 
			
		||||
    {
 | 
			
		||||
        get { return (IPEndPoint)sock.LocalEndPoint; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SSLSocket()
 | 
			
		||||
    {
 | 
			
		||||
        sock = new Socket(AddressFamily.InterNetwork,
 | 
			
		||||
                             SocketType.Stream,
 | 
			
		||||
                             ProtocolType.Tcp);
 | 
			
		||||
        receiveBuffer = new byte[sock.ReceiveBufferSize];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SSLSocket(IPEndPoint localEndPoint, X509Certificate2 certificate)
 | 
			
		||||
    {
 | 
			
		||||
        // create the socket
 | 
			
		||||
        sock = new Socket(AddressFamily.InterNetwork,
 | 
			
		||||
                                         SocketType.Stream,
 | 
			
		||||
                                         ProtocolType.Tcp);
 | 
			
		||||
 | 
			
		||||
        state = SocketState.Listening;
 | 
			
		||||
 | 
			
		||||
        // bind
 | 
			
		||||
        sock.Bind(localEndPoint);
 | 
			
		||||
 | 
			
		||||
        // start listening
 | 
			
		||||
        sock.Listen(UInt16.MaxValue);
 | 
			
		||||
 | 
			
		||||
        cert = certificate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IPEndPoint RemoteEndPoint
 | 
			
		||||
    {
 | 
			
		||||
        get { return (IPEndPoint)sock.RemoteEndPoint; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SocketState State
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return state;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public SSLSocket(Socket socket, X509Certificate2 certificate, bool authenticateAsServer)
 | 
			
		||||
    {
 | 
			
		||||
        cert = certificate;
 | 
			
		||||
        sock = socket;
 | 
			
		||||
        receiveBuffer = new byte[sock.ReceiveBufferSize];
 | 
			
		||||
 | 
			
		||||
        ssl = new SslStream(new NetworkStream(sock));
 | 
			
		||||
 | 
			
		||||
        server = authenticateAsServer;
 | 
			
		||||
 | 
			
		||||
        if (socket.Connected)
 | 
			
		||||
            state = SocketState.Established;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Close()
 | 
			
		||||
    {
 | 
			
		||||
        if (state != SocketState.Closed)// && state != SocketState.Terminated)
 | 
			
		||||
        {
 | 
			
		||||
            state = SocketState.Closed;
 | 
			
		||||
 | 
			
		||||
            if (sock.Connected)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    sock.Shutdown(SocketShutdown.Both);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    //state = SocketState.Terminated;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Receiver?.NetworkClose(this);
 | 
			
		||||
            //OnClose?.Invoke();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Send(byte[] message)
 | 
			
		||||
    {
 | 
			
		||||
        Send(message, 0, message.Length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Send(byte[] message, int offset, int size)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var msg = message.Clip((uint)offset, (uint)size);
 | 
			
		||||
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (!sock.Connected)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (asyncSending || held)
 | 
			
		||||
            {
 | 
			
		||||
                sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(null, msg));// message.Clip((uint)offset, (uint)size));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                asyncSending = true;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ssl.BeginWrite(msg, 0, msg.Length, SendCallback, null);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    asyncSending = false;
 | 
			
		||||
                    //state = SocketState.Terminated;
 | 
			
		||||
                    Close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //public void Send(byte[] message)
 | 
			
		||||
    //{
 | 
			
		||||
    //    Send(message, 0, message.Length);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //public void Send(byte[] message, int offset, int size)
 | 
			
		||||
    //{
 | 
			
		||||
    //    lock (sendLock)
 | 
			
		||||
    //    {
 | 
			
		||||
    //        if (asyncSending)
 | 
			
		||||
    //        {
 | 
			
		||||
    //            sendBufferQueue.Enqueue(message.Clip((uint)offset, (uint)size));
 | 
			
		||||
    //        }
 | 
			
		||||
    //        else
 | 
			
		||||
    //        {
 | 
			
		||||
    //            asyncSending = true;
 | 
			
		||||
    //            ssl.WriteAsync(message, offset, size).ContinueWith(DataSent);
 | 
			
		||||
    //        }
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //private void DataReceived(Task<int> task)
 | 
			
		||||
    //{
 | 
			
		||||
    //    try
 | 
			
		||||
    //    {
 | 
			
		||||
    //        if (state == SocketState.Closed || state == SocketState.Terminated)
 | 
			
		||||
    //            return;
 | 
			
		||||
 | 
			
		||||
    //        if (task.Result <= 0)
 | 
			
		||||
    //        {
 | 
			
		||||
    //            Close();
 | 
			
		||||
    //            return;
 | 
			
		||||
    //        }
 | 
			
		||||
 | 
			
		||||
    //        receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)task.Result);
 | 
			
		||||
    //        OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
    //        if (state == SocketState.Established)
 | 
			
		||||
    //            ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived);
 | 
			
		||||
    //    }
 | 
			
		||||
    //    catch (Exception ex)
 | 
			
		||||
    //    {
 | 
			
		||||
    //        if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
    //        {
 | 
			
		||||
    //            state = SocketState.Terminated;
 | 
			
		||||
    //            Close();
 | 
			
		||||
    //        }
 | 
			
		||||
 | 
			
		||||
    //        Global.Log("SSLSocket", LogType.Error, ex.ToString());
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool Begin()
 | 
			
		||||
    {
 | 
			
		||||
        if (began)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        began = true;
 | 
			
		||||
 | 
			
		||||
        if (server)
 | 
			
		||||
            ssl.AuthenticateAsServer(cert);
 | 
			
		||||
        else
 | 
			
		||||
            ssl.AuthenticateAsClient(hostname);
 | 
			
		||||
 | 
			
		||||
        if (state == SocketState.Established)
 | 
			
		||||
        {
 | 
			
		||||
            ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async AsyncReply<bool> BeginAsync()
 | 
			
		||||
    {
 | 
			
		||||
        if (began)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        began = true;
 | 
			
		||||
 | 
			
		||||
        if (server)
 | 
			
		||||
            await ssl.AuthenticateAsServerAsync(cert);
 | 
			
		||||
        else
 | 
			
		||||
            await ssl.AuthenticateAsClientAsync(hostname);
 | 
			
		||||
 | 
			
		||||
        if (state == SocketState.Established)
 | 
			
		||||
        {
 | 
			
		||||
            ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ReceiveCallback(IAsyncResult results)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (state != SocketState.Established)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var bytesReceived = ssl.EndRead(results);
 | 
			
		||||
 | 
			
		||||
            if (bytesReceived <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)bytesReceived);
 | 
			
		||||
 | 
			
		||||
            //OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
 | 
			
		||||
            Receiver?.NetworkReceive(this, receiveNetworkBuffer);
 | 
			
		||||
 | 
			
		||||
            ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch //(Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            if (state != SocketState.Closed && !sock.Connected)
 | 
			
		||||
            {
 | 
			
		||||
                //state = SocketState.Terminated;
 | 
			
		||||
                Close();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Global.Log("SSLSocket", LogType.Error, ex.ToString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        Close();
 | 
			
		||||
        Receiver = null;
 | 
			
		||||
        receiveNetworkBuffer = null;
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
        OnDestroy = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async AsyncReply<ISocket> AcceptAsync()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var s = await sock.AcceptAsync();
 | 
			
		||||
            return new SSLSocket(s, cert, true);
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            state = SocketState.Closed;// Terminated;
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Hold()
 | 
			
		||||
    {
 | 
			
		||||
        held = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Unhold()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            SendCallback(null);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Global.Log(ex);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            held = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> SendAsync(byte[] message, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        var msg = message.Clip((uint)offset, (uint)length);
 | 
			
		||||
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (!sock.Connected)
 | 
			
		||||
                return new AsyncReply<bool>(false);
 | 
			
		||||
 | 
			
		||||
            var rt = new AsyncReply<bool>();
 | 
			
		||||
 | 
			
		||||
            if (asyncSending || held)
 | 
			
		||||
            {
 | 
			
		||||
                sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(rt, msg));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                asyncSending = true;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    ssl.BeginWrite(msg, 0, msg.Length, SendCallback, rt);// null);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    rt.TriggerError(ex);
 | 
			
		||||
                    asyncSending = false;
 | 
			
		||||
                    //state = SocketState.Terminated;
 | 
			
		||||
                    Close();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return rt;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public ISocket Accept()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            return new SSLSocket(sock.Accept(), cert, true);
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            state = SocketState.Closed;// .Terminated;
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,15 +28,13 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Sockets
 | 
			
		||||
namespace Esiur.Net.Sockets;
 | 
			
		||||
public enum SocketState
 | 
			
		||||
{
 | 
			
		||||
    public enum SocketState
 | 
			
		||||
    {
 | 
			
		||||
        Initial,
 | 
			
		||||
        Listening,
 | 
			
		||||
        Connecting,
 | 
			
		||||
        Established,
 | 
			
		||||
        Closed,
 | 
			
		||||
        //Terminated
 | 
			
		||||
    }
 | 
			
		||||
    Initial,
 | 
			
		||||
    Listening,
 | 
			
		||||
    Connecting,
 | 
			
		||||
    Established,
 | 
			
		||||
    Closed,
 | 
			
		||||
    //Terminated
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -36,326 +36,324 @@ using Esiur.Resource;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.Sockets
 | 
			
		||||
namespace Esiur.Net.Sockets;
 | 
			
		||||
public class WSocket : ISocket, INetworkReceiver<ISocket>
 | 
			
		||||
{
 | 
			
		||||
    public class WSocket : ISocket, INetworkReceiver<ISocket>
 | 
			
		||||
    WebsocketPacket pkt_receive = new WebsocketPacket();
 | 
			
		||||
    WebsocketPacket pkt_send = new WebsocketPacket();
 | 
			
		||||
 | 
			
		||||
    ISocket sock;
 | 
			
		||||
    NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
 | 
			
		||||
    NetworkBuffer sendNetworkBuffer = new NetworkBuffer();
 | 
			
		||||
 | 
			
		||||
    object sendLock = new object();
 | 
			
		||||
    bool held;
 | 
			
		||||
 | 
			
		||||
    //public event ISocketReceiveEvent OnReceive;
 | 
			
		||||
    //public event ISocketConnectEvent OnConnect;
 | 
			
		||||
    //public event ISocketCloseEvent OnClose;
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    long totalSent, totalReceived;
 | 
			
		||||
 | 
			
		||||
    bool processing = false;
 | 
			
		||||
 | 
			
		||||
    public IPEndPoint LocalEndPoint
 | 
			
		||||
    {
 | 
			
		||||
        WebsocketPacket pkt_receive = new WebsocketPacket();
 | 
			
		||||
        WebsocketPacket pkt_send = new WebsocketPacket();
 | 
			
		||||
        get { return (IPEndPoint)sock.LocalEndPoint; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        ISocket sock;
 | 
			
		||||
        NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
 | 
			
		||||
        NetworkBuffer sendNetworkBuffer = new NetworkBuffer();
 | 
			
		||||
    public IPEndPoint RemoteEndPoint
 | 
			
		||||
    {
 | 
			
		||||
        get { return sock.RemoteEndPoint; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        object sendLock = new object();
 | 
			
		||||
        bool held;
 | 
			
		||||
 | 
			
		||||
        //public event ISocketReceiveEvent OnReceive;
 | 
			
		||||
        //public event ISocketConnectEvent OnConnect;
 | 
			
		||||
        //public event ISocketCloseEvent OnClose;
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        long totalSent, totalReceived;
 | 
			
		||||
 | 
			
		||||
        bool processing = false;
 | 
			
		||||
 | 
			
		||||
        public IPEndPoint LocalEndPoint
 | 
			
		||||
    public SocketState State
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get { return (IPEndPoint)sock.LocalEndPoint; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IPEndPoint RemoteEndPoint
 | 
			
		||||
        {
 | 
			
		||||
            get { return sock.RemoteEndPoint; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public SocketState State
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return sock.State;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public INetworkReceiver<ISocket> Receiver { get; set; }
 | 
			
		||||
 | 
			
		||||
        public WSocket(ISocket socket)
 | 
			
		||||
        {
 | 
			
		||||
            pkt_send.FIN = true;
 | 
			
		||||
            pkt_send.Mask = false;
 | 
			
		||||
            pkt_send.Opcode = WebsocketPacket.WSOpcode.BinaryFrame;
 | 
			
		||||
            sock = socket;
 | 
			
		||||
 | 
			
		||||
            sock.Receiver = this;
 | 
			
		||||
 | 
			
		||||
            //sock.OnClose += Sock_OnClose;
 | 
			
		||||
            //sock.OnConnect += Sock_OnConnect;
 | 
			
		||||
            //sock.OnReceive += Sock_OnReceive;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //private void Sock_OnReceive(NetworkBuffer buffer)
 | 
			
		||||
        //{
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //private void Sock_OnConnect()
 | 
			
		||||
        //{
 | 
			
		||||
        //    OnConnect?.Invoke();
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        //private void Sock_OnClose()
 | 
			
		||||
        //{
 | 
			
		||||
        //    OnClose?.Invoke();
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        public void Send(WebsocketPacket packet)
 | 
			
		||||
        {
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
                if (packet.Compose())
 | 
			
		||||
                    sock.Send(packet.Data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Send(byte[] message)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (held)
 | 
			
		||||
                {
 | 
			
		||||
                    sendNetworkBuffer.Write(message);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    totalSent += message.Length;
 | 
			
		||||
                    //Console.WriteLine("TX " + message.Length +"/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size));
 | 
			
		||||
 | 
			
		||||
                    pkt_send.Message = message;
 | 
			
		||||
 | 
			
		||||
                    if (pkt_send.Compose())
 | 
			
		||||
                        sock?.Send(pkt_send.Data);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Send(byte[] message, int offset, int size)
 | 
			
		||||
        {
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
            {
 | 
			
		||||
                if (held)
 | 
			
		||||
                {
 | 
			
		||||
                    sendNetworkBuffer.Write(message, (uint)offset, (uint)size);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    totalSent += size;
 | 
			
		||||
                    //Console.WriteLine("TX " + size + "/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size));
 | 
			
		||||
 | 
			
		||||
                    pkt_send.Message = new byte[size];
 | 
			
		||||
                    Buffer.BlockCopy(message, offset, pkt_send.Message, 0, size);
 | 
			
		||||
                    if (pkt_send.Compose())
 | 
			
		||||
                        sock.Send(pkt_send.Data);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public void Close()
 | 
			
		||||
        {
 | 
			
		||||
            sock?.Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Connect(string hostname, ushort port)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public bool Begin()
 | 
			
		||||
        {
 | 
			
		||||
            return sock.Begin();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            Close();
 | 
			
		||||
            //OnClose = null;
 | 
			
		||||
            //OnConnect = null;
 | 
			
		||||
            //OnReceive = null;
 | 
			
		||||
            receiveNetworkBuffer = null;
 | 
			
		||||
            //sock.OnReceive -= Sock_OnReceive;
 | 
			
		||||
            //sock.OnClose -= Sock_OnClose;
 | 
			
		||||
            //sock.OnConnect -= Sock_OnConnect;
 | 
			
		||||
            sock.Receiver = null;
 | 
			
		||||
            sock = null;
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
            OnDestroy = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<ISocket> AcceptAsync()
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Hold()
 | 
			
		||||
        {
 | 
			
		||||
            //Console.WriteLine("WS Hold  ");
 | 
			
		||||
            held = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Unhold()
 | 
			
		||||
        {
 | 
			
		||||
            lock (sendLock)
 | 
			
		||||
            {
 | 
			
		||||
                held = false;
 | 
			
		||||
 | 
			
		||||
                var message = sendNetworkBuffer.Read();
 | 
			
		||||
 | 
			
		||||
                //Console.WriteLine("WS Unhold {0}", message == null ? 0 : message.Length);
 | 
			
		||||
 | 
			
		||||
                if (message == null)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                totalSent += message.Length;
 | 
			
		||||
 | 
			
		||||
                pkt_send.Message = message;
 | 
			
		||||
                if (pkt_send.Compose())
 | 
			
		||||
                    sock.Send(pkt_send.Data);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> SendAsync(byte[] message, int offset, int length)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ISocket Accept()
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> BeginAsync()
 | 
			
		||||
        {
 | 
			
		||||
            return sock.BeginAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void NetworkClose(ISocket sender)
 | 
			
		||||
        {
 | 
			
		||||
            Receiver?.NetworkClose(sender);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void NetworkReceive(ISocket sender, NetworkBuffer buffer)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (buffer.Protected)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (processing)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var msg = buffer.Read();
 | 
			
		||||
 | 
			
		||||
            if (msg == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var wsPacketLength = pkt_receive.Parse(msg, 0, (uint)msg.Length);
 | 
			
		||||
            //Console.WriteLine("WSP: " + wsPacketLength);
 | 
			
		||||
 | 
			
		||||
            if (wsPacketLength < 0)
 | 
			
		||||
            {
 | 
			
		||||
                buffer.Protect(msg, 0, (uint)msg.Length + (uint)-wsPacketLength);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint offset = 0;
 | 
			
		||||
 | 
			
		||||
            while (wsPacketLength > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.ConnectionClose)
 | 
			
		||||
                {
 | 
			
		||||
                    Close();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Ping)
 | 
			
		||||
                {
 | 
			
		||||
                    var pkt_pong = new WebsocketPacket();
 | 
			
		||||
 | 
			
		||||
                    pkt_pong.FIN = true;
 | 
			
		||||
                    pkt_pong.Mask = false;
 | 
			
		||||
                    pkt_pong.Opcode = WebsocketPacket.WSOpcode.Pong;
 | 
			
		||||
                    pkt_pong.Message = pkt_receive.Message;
 | 
			
		||||
                    offset += (uint)wsPacketLength;
 | 
			
		||||
 | 
			
		||||
                    Send(pkt_pong);
 | 
			
		||||
                }
 | 
			
		||||
                else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Pong)
 | 
			
		||||
                {
 | 
			
		||||
                    offset += (uint)wsPacketLength;
 | 
			
		||||
                }
 | 
			
		||||
                else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.BinaryFrame
 | 
			
		||||
                        || pkt_receive.Opcode == WebsocketPacket.WSOpcode.TextFrame
 | 
			
		||||
                        || pkt_receive.Opcode == WebsocketPacket.WSOpcode.ContinuationFrame)
 | 
			
		||||
                {
 | 
			
		||||
                    totalReceived += pkt_receive.Message.Length;
 | 
			
		||||
                    //Console.WriteLine("RX " + pkt_receive.Message.Length + "/" + totalReceived);// + " " + DC.ToHex(message, 0, (uint)size));
 | 
			
		||||
 | 
			
		||||
                    receiveNetworkBuffer.Write(pkt_receive.Message);
 | 
			
		||||
                    offset += (uint)wsPacketLength;
 | 
			
		||||
 | 
			
		||||
                    //Console.WriteLine("WS IN: " + pkt_receive.Opcode.ToString() + " " + pkt_receive.Message.Length + " | " + offset + " " + string.Join(" ", pkt_receive.Message));//  DC.ToHex(pkt_receive.Message));
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    Console.WriteLine("Unknown WS opcode:" + pkt_receive.Opcode);
 | 
			
		||||
 | 
			
		||||
                if (offset == msg.Length)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    //OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
                    Receiver?.NetworkReceive(this, receiveNetworkBuffer);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                wsPacketLength = pkt_receive.Parse(msg, offset, (uint)msg.Length);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (wsPacketLength < 0)//(offset < msg.Length) && (offset > 0))
 | 
			
		||||
            {
 | 
			
		||||
                //receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)msg.Length + (uint)-wsPacketLength);
 | 
			
		||||
                // save the incomplete packet to the heldBuffer queue
 | 
			
		||||
 | 
			
		||||
                buffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)(msg.Length - offset) + (uint)-wsPacketLength);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Console.WriteLine("WS IN: " + receiveNetworkBuffer.Available);
 | 
			
		||||
 | 
			
		||||
            //OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
            Receiver?.NetworkReceive(this, receiveNetworkBuffer);
 | 
			
		||||
 | 
			
		||||
            processing = false;
 | 
			
		||||
 | 
			
		||||
            if (buffer.Available > 0 && !buffer.Protected)
 | 
			
		||||
                Receiver?.NetworkReceive(this, buffer);
 | 
			
		||||
                //Sock_OnReceive(buffer);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
        public void NetworkConnect(ISocket sender)
 | 
			
		||||
        {
 | 
			
		||||
            Receiver?.NetworkConnect(this);
 | 
			
		||||
            return sock.State;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    public INetworkReceiver<ISocket> Receiver { get; set; }
 | 
			
		||||
 | 
			
		||||
    public WSocket(ISocket socket)
 | 
			
		||||
    {
 | 
			
		||||
        pkt_send.FIN = true;
 | 
			
		||||
        pkt_send.Mask = false;
 | 
			
		||||
        pkt_send.Opcode = WebsocketPacket.WSOpcode.BinaryFrame;
 | 
			
		||||
        sock = socket;
 | 
			
		||||
 | 
			
		||||
        sock.Receiver = this;
 | 
			
		||||
 | 
			
		||||
        //sock.OnClose += Sock_OnClose;
 | 
			
		||||
        //sock.OnConnect += Sock_OnConnect;
 | 
			
		||||
        //sock.OnReceive += Sock_OnReceive;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //private void Sock_OnReceive(NetworkBuffer buffer)
 | 
			
		||||
    //{
 | 
			
		||||
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //private void Sock_OnConnect()
 | 
			
		||||
    //{
 | 
			
		||||
    //    OnConnect?.Invoke();
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    //private void Sock_OnClose()
 | 
			
		||||
    //{
 | 
			
		||||
    //    OnClose?.Invoke();
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    public void Send(WebsocketPacket packet)
 | 
			
		||||
    {
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
            if (packet.Compose())
 | 
			
		||||
                sock.Send(packet.Data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Send(byte[] message)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (held)
 | 
			
		||||
            {
 | 
			
		||||
                sendNetworkBuffer.Write(message);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                totalSent += message.Length;
 | 
			
		||||
                //Console.WriteLine("TX " + message.Length +"/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size));
 | 
			
		||||
 | 
			
		||||
                pkt_send.Message = message;
 | 
			
		||||
 | 
			
		||||
                if (pkt_send.Compose())
 | 
			
		||||
                    sock?.Send(pkt_send.Data);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Send(byte[] message, int offset, int size)
 | 
			
		||||
    {
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
        {
 | 
			
		||||
            if (held)
 | 
			
		||||
            {
 | 
			
		||||
                sendNetworkBuffer.Write(message, (uint)offset, (uint)size);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                totalSent += size;
 | 
			
		||||
                //Console.WriteLine("TX " + size + "/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size));
 | 
			
		||||
 | 
			
		||||
                pkt_send.Message = new byte[size];
 | 
			
		||||
                Buffer.BlockCopy(message, offset, pkt_send.Message, 0, size);
 | 
			
		||||
                if (pkt_send.Compose())
 | 
			
		||||
                    sock.Send(pkt_send.Data);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Close()
 | 
			
		||||
    {
 | 
			
		||||
        sock?.Close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Connect(string hostname, ushort port)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool Begin()
 | 
			
		||||
    {
 | 
			
		||||
        return sock.Begin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        Close();
 | 
			
		||||
        //OnClose = null;
 | 
			
		||||
        //OnConnect = null;
 | 
			
		||||
        //OnReceive = null;
 | 
			
		||||
        receiveNetworkBuffer = null;
 | 
			
		||||
        //sock.OnReceive -= Sock_OnReceive;
 | 
			
		||||
        //sock.OnClose -= Sock_OnClose;
 | 
			
		||||
        //sock.OnConnect -= Sock_OnConnect;
 | 
			
		||||
        sock.Receiver = null;
 | 
			
		||||
        sock = null;
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
        OnDestroy = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<ISocket> AcceptAsync()
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Hold()
 | 
			
		||||
    {
 | 
			
		||||
        //Console.WriteLine("WS Hold  ");
 | 
			
		||||
        held = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Unhold()
 | 
			
		||||
    {
 | 
			
		||||
        lock (sendLock)
 | 
			
		||||
        {
 | 
			
		||||
            held = false;
 | 
			
		||||
 | 
			
		||||
            var message = sendNetworkBuffer.Read();
 | 
			
		||||
 | 
			
		||||
            //Console.WriteLine("WS Unhold {0}", message == null ? 0 : message.Length);
 | 
			
		||||
 | 
			
		||||
            if (message == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            totalSent += message.Length;
 | 
			
		||||
 | 
			
		||||
            pkt_send.Message = message;
 | 
			
		||||
            if (pkt_send.Compose())
 | 
			
		||||
                sock.Send(pkt_send.Data);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> SendAsync(byte[] message, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ISocket Accept()
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> BeginAsync()
 | 
			
		||||
    {
 | 
			
		||||
        return sock.BeginAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void NetworkClose(ISocket sender)
 | 
			
		||||
    {
 | 
			
		||||
        Receiver?.NetworkClose(sender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void NetworkReceive(ISocket sender, NetworkBuffer buffer)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (buffer.Protected)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (processing)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var msg = buffer.Read();
 | 
			
		||||
 | 
			
		||||
        if (msg == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        var wsPacketLength = pkt_receive.Parse(msg, 0, (uint)msg.Length);
 | 
			
		||||
        //Console.WriteLine("WSP: " + wsPacketLength);
 | 
			
		||||
 | 
			
		||||
        if (wsPacketLength < 0)
 | 
			
		||||
        {
 | 
			
		||||
            buffer.Protect(msg, 0, (uint)msg.Length + (uint)-wsPacketLength);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint offset = 0;
 | 
			
		||||
 | 
			
		||||
        while (wsPacketLength > 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.ConnectionClose)
 | 
			
		||||
            {
 | 
			
		||||
                Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Ping)
 | 
			
		||||
            {
 | 
			
		||||
                var pkt_pong = new WebsocketPacket();
 | 
			
		||||
 | 
			
		||||
                pkt_pong.FIN = true;
 | 
			
		||||
                pkt_pong.Mask = false;
 | 
			
		||||
                pkt_pong.Opcode = WebsocketPacket.WSOpcode.Pong;
 | 
			
		||||
                pkt_pong.Message = pkt_receive.Message;
 | 
			
		||||
                offset += (uint)wsPacketLength;
 | 
			
		||||
 | 
			
		||||
                Send(pkt_pong);
 | 
			
		||||
            }
 | 
			
		||||
            else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Pong)
 | 
			
		||||
            {
 | 
			
		||||
                offset += (uint)wsPacketLength;
 | 
			
		||||
            }
 | 
			
		||||
            else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.BinaryFrame
 | 
			
		||||
                    || pkt_receive.Opcode == WebsocketPacket.WSOpcode.TextFrame
 | 
			
		||||
                    || pkt_receive.Opcode == WebsocketPacket.WSOpcode.ContinuationFrame)
 | 
			
		||||
            {
 | 
			
		||||
                totalReceived += pkt_receive.Message.Length;
 | 
			
		||||
                //Console.WriteLine("RX " + pkt_receive.Message.Length + "/" + totalReceived);// + " " + DC.ToHex(message, 0, (uint)size));
 | 
			
		||||
 | 
			
		||||
                receiveNetworkBuffer.Write(pkt_receive.Message);
 | 
			
		||||
                offset += (uint)wsPacketLength;
 | 
			
		||||
 | 
			
		||||
                //Console.WriteLine("WS IN: " + pkt_receive.Opcode.ToString() + " " + pkt_receive.Message.Length + " | " + offset + " " + string.Join(" ", pkt_receive.Message));//  DC.ToHex(pkt_receive.Message));
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                Console.WriteLine("Unknown WS opcode:" + pkt_receive.Opcode);
 | 
			
		||||
 | 
			
		||||
            if (offset == msg.Length)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                //OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
                Receiver?.NetworkReceive(this, receiveNetworkBuffer);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            wsPacketLength = pkt_receive.Parse(msg, offset, (uint)msg.Length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (wsPacketLength < 0)//(offset < msg.Length) && (offset > 0))
 | 
			
		||||
        {
 | 
			
		||||
            //receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)msg.Length + (uint)-wsPacketLength);
 | 
			
		||||
            // save the incomplete packet to the heldBuffer queue
 | 
			
		||||
 | 
			
		||||
            buffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)(msg.Length - offset) + (uint)-wsPacketLength);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Console.WriteLine("WS IN: " + receiveNetworkBuffer.Available);
 | 
			
		||||
 | 
			
		||||
        //OnReceive?.Invoke(receiveNetworkBuffer);
 | 
			
		||||
        Receiver?.NetworkReceive(this, receiveNetworkBuffer);
 | 
			
		||||
 | 
			
		||||
        processing = false;
 | 
			
		||||
 | 
			
		||||
        if (buffer.Available > 0 && !buffer.Protected)
 | 
			
		||||
            Receiver?.NetworkReceive(this, buffer);
 | 
			
		||||
        //Sock_OnReceive(buffer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void NetworkConnect(ISocket sender)
 | 
			
		||||
    {
 | 
			
		||||
        Receiver?.NetworkConnect(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,35 +32,34 @@ using System.Collections;
 | 
			
		||||
using Esiur.Misc;
 | 
			
		||||
using Esiur.Data;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.TCP
 | 
			
		||||
namespace Esiur.Net.TCP;
 | 
			
		||||
public class TCPConnection : NetworkConnection
 | 
			
		||||
{
 | 
			
		||||
    public class TCPConnection:NetworkConnection    {
 | 
			
		||||
 | 
			
		||||
        private KeyList<string, object> variables = new KeyList<string, object>();
 | 
			
		||||
    private KeyList<string, object> variables = new KeyList<string, object>();
 | 
			
		||||
 | 
			
		||||
        public TCPServer Server { get; internal set; }
 | 
			
		||||
    public TCPServer Server { get; internal set; }
 | 
			
		||||
 | 
			
		||||
        public KeyList<string, object> Variables
 | 
			
		||||
    public KeyList<string, object> Variables
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return variables;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Connected()
 | 
			
		||||
        {
 | 
			
		||||
            // do nothing
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void DataReceived(NetworkBuffer buffer)
 | 
			
		||||
        {
 | 
			
		||||
            Server?.Execute(this, buffer);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Disconencted()
 | 
			
		||||
        {
 | 
			
		||||
            // do nothing
 | 
			
		||||
            return variables;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void Connected()
 | 
			
		||||
    {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void DataReceived(NetworkBuffer buffer)
 | 
			
		||||
    {
 | 
			
		||||
        Server?.Execute(this, buffer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void Disconencted()
 | 
			
		||||
    {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,35 +32,34 @@ using Esiur.Net.Sockets;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.TCP
 | 
			
		||||
namespace Esiur.Net.TCP;
 | 
			
		||||
 | 
			
		||||
public abstract class TCPFilter : IResource
 | 
			
		||||
{
 | 
			
		||||
    public abstract class TCPFilter: IResource
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
    public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
        public virtual bool Connected(TCPConnection sender)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    public virtual bool Connected(TCPConnection sender)
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public virtual bool Disconnected(TCPConnection sender)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    public virtual bool Disconnected(TCPConnection sender)
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public abstract bool Execute(byte[] msg, NetworkBuffer data, TCPConnection sender);
 | 
			
		||||
    public abstract bool Execute(byte[] msg, NetworkBuffer data, TCPConnection sender);
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,123 +34,121 @@ using Esiur.Core;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.TCP
 | 
			
		||||
namespace Esiur.Net.TCP;
 | 
			
		||||
public class TCPServer : NetworkServer<TCPConnection>, IResource
 | 
			
		||||
{
 | 
			
		||||
    public class TCPServer : NetworkServer<TCPConnection>, IResource
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public string IP
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    public ushort Port
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
    //[Storable]
 | 
			
		||||
    //public uint Timeout
 | 
			
		||||
    //{
 | 
			
		||||
    //    get;
 | 
			
		||||
    //    set;
 | 
			
		||||
    //}
 | 
			
		||||
    //[Attribute]
 | 
			
		||||
    //public uint Clock
 | 
			
		||||
    //{
 | 
			
		||||
    //    get;
 | 
			
		||||
    //    set;
 | 
			
		||||
    //}
 | 
			
		||||
    public Instance Instance { get; set; }
 | 
			
		||||
 | 
			
		||||
    TCPFilter[] filters = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
        {
 | 
			
		||||
            TCPSocket listener;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (IP != null)
 | 
			
		||||
                listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port));
 | 
			
		||||
            else
 | 
			
		||||
                listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port));
 | 
			
		||||
 | 
			
		||||
            Start(listener);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
        {
 | 
			
		||||
            Stop();
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
        {
 | 
			
		||||
            Trigger(ResourceTrigger.Terminate);
 | 
			
		||||
            Trigger(ResourceTrigger.Initialize);
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemInitialized)
 | 
			
		||||
        {
 | 
			
		||||
            Instance.Children<TCPFilter>().Then(x => filters = x);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new AsyncReply<bool>(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    internal bool Execute(TCPConnection sender, NetworkBuffer data)
 | 
			
		||||
    {
 | 
			
		||||
        var msg = data.Read();
 | 
			
		||||
 | 
			
		||||
        foreach (var filter in filters)
 | 
			
		||||
        {
 | 
			
		||||
            if (filter.Execute(msg, data, sender))
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void SessionModified(TCPConnection session, string key, object newValue)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public string IP
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void ClientDisconnected(TCPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        foreach (var filter in filters)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            filter.Disconnected(connection);
 | 
			
		||||
        }
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        public ushort Port
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Add(TCPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        connection.Server = this;
 | 
			
		||||
        base.Add(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Remove(TCPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        connection.Server = null;
 | 
			
		||||
        base.Remove(connection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void ClientConnected(TCPConnection connection)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var filter in filters)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
            filter.Connected(connection);
 | 
			
		||||
        }
 | 
			
		||||
        //[Storable]
 | 
			
		||||
        //public uint Timeout
 | 
			
		||||
        //{
 | 
			
		||||
        //    get;
 | 
			
		||||
        //    set;
 | 
			
		||||
        //}
 | 
			
		||||
        //[Attribute]
 | 
			
		||||
        //public uint Clock
 | 
			
		||||
        //{
 | 
			
		||||
        //    get;
 | 
			
		||||
        //    set;
 | 
			
		||||
        //}
 | 
			
		||||
        public Instance Instance { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        TCPFilter[] filters = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public AsyncReply<bool> Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            {
 | 
			
		||||
                TCPSocket listener;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                if (IP != null)
 | 
			
		||||
                    listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port));
 | 
			
		||||
                else
 | 
			
		||||
                    listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port));
 | 
			
		||||
 | 
			
		||||
                Start(listener);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
            {
 | 
			
		||||
                Stop();
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemReload)
 | 
			
		||||
            {
 | 
			
		||||
                Trigger(ResourceTrigger.Terminate);
 | 
			
		||||
                Trigger(ResourceTrigger.Initialize);
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemInitialized)
 | 
			
		||||
            {
 | 
			
		||||
                Instance.Children<TCPFilter>().Then(x => filters = x);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new AsyncReply<bool>(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        internal bool Execute(TCPConnection sender, NetworkBuffer data)
 | 
			
		||||
        {
 | 
			
		||||
            var msg = data.Read();
 | 
			
		||||
 | 
			
		||||
            foreach (var filter in filters)
 | 
			
		||||
            {
 | 
			
		||||
                if (filter.Execute(msg, data, sender))
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SessionModified(TCPConnection session, string key, object newValue)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ClientDisconnected(TCPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            
 | 
			
		||||
            foreach (var filter in filters)
 | 
			
		||||
            {
 | 
			
		||||
                filter.Disconnected(connection);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Add(TCPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Server = this;
 | 
			
		||||
            base.Add(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Remove(TCPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            connection.Server = null;
 | 
			
		||||
            base.Remove(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void ClientConnected(TCPConnection connection)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var filter in filters)
 | 
			
		||||
            {
 | 
			
		||||
                filter.Connected(connection);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,10 +28,8 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.TCP
 | 
			
		||||
namespace Esiur.Net.TCP;
 | 
			
		||||
public class TCPSession : NetworkSession
 | 
			
		||||
{
 | 
			
		||||
    public class TCPSession : NetworkSession
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,26 +32,24 @@ using Esiur.Data;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.UDP
 | 
			
		||||
namespace Esiur.Net.UDP;
 | 
			
		||||
public abstract class UDPFilter : IResource
 | 
			
		||||
{
 | 
			
		||||
    public abstract class UDPFilter : IResource
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
        public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
        public abstract bool Execute(byte[] data, IPEndPoint sender);
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
 | 
			
		||||
 | 
			
		||||
    public abstract bool Execute(byte[] data, IPEndPoint sender);
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,172 +33,170 @@ using Esiur.Misc;
 | 
			
		||||
using Esiur.Resource;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Net.UDP
 | 
			
		||||
namespace Esiur.Net.UDP;
 | 
			
		||||
 | 
			
		||||
/* public class IIPConnection
 | 
			
		||||
{
 | 
			
		||||
    public EndPoint SenderPoint;
 | 
			
		||||
    public 
 | 
			
		||||
}*/
 | 
			
		||||
public class UDPServer : IResource
 | 
			
		||||
{
 | 
			
		||||
    Thread receiver;
 | 
			
		||||
    UdpClient udp;
 | 
			
		||||
    UDPFilter[] filters = new UDPFilter[0];
 | 
			
		||||
 | 
			
		||||
    /* public class IIPConnection
 | 
			
		||||
    public event DestroyedEvent OnDestroy;
 | 
			
		||||
 | 
			
		||||
    public Instance Instance
 | 
			
		||||
    {
 | 
			
		||||
        public EndPoint SenderPoint;
 | 
			
		||||
        public 
 | 
			
		||||
    }*/
 | 
			
		||||
    public class UDPServer : IResource
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    string IP
 | 
			
		||||
    {
 | 
			
		||||
        Thread receiver;
 | 
			
		||||
        UdpClient udp;
 | 
			
		||||
        UDPFilter[] filters = new UDPFilter[0];
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public event DestroyedEvent OnDestroy;
 | 
			
		||||
    [Attribute]
 | 
			
		||||
    ushort Port
 | 
			
		||||
    {
 | 
			
		||||
        get;
 | 
			
		||||
        set;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public Instance Instance
 | 
			
		||||
    private void Receiving()
 | 
			
		||||
    {
 | 
			
		||||
        IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
            byte[] b = udp.Receive(ref ep);
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        string IP
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Attribute]
 | 
			
		||||
        ushort Port
 | 
			
		||||
        {
 | 
			
		||||
            get;
 | 
			
		||||
            set;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
         private void Receiving()
 | 
			
		||||
        {
 | 
			
		||||
            IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            while (true)
 | 
			
		||||
            foreach (var child in filters)
 | 
			
		||||
            {
 | 
			
		||||
                byte[] b = udp.Receive(ref ep);
 | 
			
		||||
                var f = child as UDPFilter;
 | 
			
		||||
 | 
			
		||||
                foreach (var child in filters)
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var f = child as UDPFilter;
 | 
			
		||||
 | 
			
		||||
                    try
 | 
			
		||||
                    if (f.Execute(b, ep))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (f.Execute(b, ep))
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        Global.Log("UDPServer", LogType.Error, ex.ToString());
 | 
			
		||||
                        //Console.WriteLine(ex.ToString());
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Global.Log("UDPServer", LogType.Error, ex.ToString());
 | 
			
		||||
                    //Console.WriteLine(ex.ToString());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public bool Send(byte[] Data, int Count, IPEndPoint EP)
 | 
			
		||||
    public bool Send(byte[] Data, int Count, IPEndPoint EP)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                udp.Send(Data, Count, EP);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Send(byte[] Data, IPEndPoint EP)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                udp.Send(Data, Data.Length, EP);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Send(byte[] Data, int Count, string Host, int Port)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                udp.Send(Data, Count, Host, Port);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Send(byte[] Data, string Host, int Port)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                udp.Send(Data, Data.Length, Host, Port);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Send(string Data, IPEndPoint EP)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                udp.Send(Encoding.Default.GetBytes(Data), Data.Length, EP);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Send(string Data, string Host, int Port)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                udp.Send(Encoding.Default.GetBytes(Data), Data.Length, Host, Port);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
        {
 | 
			
		||||
            udp.Close();
 | 
			
		||||
            OnDestroy?.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async AsyncReply<bool> IResource.Trigger(ResourceTrigger trigger)
 | 
			
		||||
        {
 | 
			
		||||
            if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
            {
 | 
			
		||||
                var address = IP == null ? IPAddress.Any : IPAddress.Parse(IP);
 | 
			
		||||
 | 
			
		||||
                udp = new UdpClient(new IPEndPoint(address, Port));
 | 
			
		||||
 | 
			
		||||
                receiver = new Thread(Receiving);
 | 
			
		||||
                receiver.Start();
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
            {
 | 
			
		||||
                if (receiver != null)
 | 
			
		||||
                    receiver.Abort();
 | 
			
		||||
            }
 | 
			
		||||
            else if (trigger == ResourceTrigger.SystemInitialized)
 | 
			
		||||
            {
 | 
			
		||||
                filters = await Instance.Children<UDPFilter>();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            udp.Send(Data, Count, EP);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    public bool Send(byte[] Data, IPEndPoint EP)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            udp.Send(Data, Data.Length, EP);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public bool Send(byte[] Data, int Count, string Host, int Port)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            udp.Send(Data, Count, Host, Port);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public bool Send(byte[] Data, string Host, int Port)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            udp.Send(Data, Data.Length, Host, Port);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public bool Send(string Data, IPEndPoint EP)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            udp.Send(Encoding.Default.GetBytes(Data), Data.Length, EP);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public bool Send(string Data, string Host, int Port)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            udp.Send(Encoding.Default.GetBytes(Data), Data.Length, Host, Port);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Destroy()
 | 
			
		||||
    {
 | 
			
		||||
        udp.Close();
 | 
			
		||||
        OnDestroy?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async AsyncReply<bool> IResource.Trigger(ResourceTrigger trigger)
 | 
			
		||||
    {
 | 
			
		||||
        if (trigger == ResourceTrigger.Initialize)
 | 
			
		||||
        {
 | 
			
		||||
            var address = IP == null ? IPAddress.Any : IPAddress.Parse(IP);
 | 
			
		||||
 | 
			
		||||
            udp = new UdpClient(new IPEndPoint(address, Port));
 | 
			
		||||
 | 
			
		||||
            receiver = new Thread(Receiving);
 | 
			
		||||
            receiver.Start();
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.Terminate)
 | 
			
		||||
        {
 | 
			
		||||
            if (receiver != null)
 | 
			
		||||
                receiver.Abort();
 | 
			
		||||
        }
 | 
			
		||||
        else if (trigger == ResourceTrigger.SystemInitialized)
 | 
			
		||||
        {
 | 
			
		||||
            filters = await Instance.Children<UDPFilter>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,177 +14,176 @@ using Esiur.Data;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using Esiur.Core;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Proxy
 | 
			
		||||
namespace Esiur.Proxy;
 | 
			
		||||
 | 
			
		||||
[Generator]
 | 
			
		||||
public class ResourceGenerator : ISourceGenerator
 | 
			
		||||
{
 | 
			
		||||
    [Generator]
 | 
			
		||||
    public class ResourceGenerator : ISourceGenerator
 | 
			
		||||
 | 
			
		||||
    private KeyList<string, TypeTemplate[]> cache = new();
 | 
			
		||||
    // private List<string> inProgress = new();
 | 
			
		||||
 | 
			
		||||
    public void Initialize(GeneratorInitializationContext context)
 | 
			
		||||
    {
 | 
			
		||||
        // Register receiver
 | 
			
		||||
 | 
			
		||||
        context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void ReportError(GeneratorExecutionContext context, string title, string msg, string category)
 | 
			
		||||
    {
 | 
			
		||||
        context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("MySG001", title, msg, category, DiagnosticSeverity.Error, true), Location.None));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void GenerateModel(GeneratorExecutionContext context, TypeTemplate[] templates)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var tmp in templates)
 | 
			
		||||
        {
 | 
			
		||||
            if (tmp.Type == TemplateType.Resource)
 | 
			
		||||
            {
 | 
			
		||||
                var source = TemplateGenerator.GenerateClass(tmp, templates);
 | 
			
		||||
                // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
 | 
			
		||||
                context.AddSource(tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
            }
 | 
			
		||||
            else if (tmp.Type == TemplateType.Record)
 | 
			
		||||
            {
 | 
			
		||||
                var source = TemplateGenerator.GenerateRecord(tmp, templates);
 | 
			
		||||
                // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
 | 
			
		||||
                context.AddSource(tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // generate info class
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " +
 | 
			
		||||
                            string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                        + " }; \r\n public static Type[] Records { get; } = new Type[] { " +
 | 
			
		||||
                            string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                        + " }; " +
 | 
			
		||||
 | 
			
		||||
                        "\r\n } \r\n}";
 | 
			
		||||
 | 
			
		||||
        //File.WriteAllText($@"C:\gen\Esiur.Generated.cs", gen);
 | 
			
		||||
 | 
			
		||||
        context.AddSource("Esiur.Generated.cs", typesFile);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void Execute(GeneratorExecutionContext context)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        private KeyList<string, TypeTemplate[]> cache = new();
 | 
			
		||||
        // private List<string> inProgress = new();
 | 
			
		||||
        if (!(context.SyntaxContextReceiver is ResourceGeneratorReceiver receiver))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        public void Initialize(GeneratorInitializationContext context)
 | 
			
		||||
        //if (receiver.Imports.Count > 0 && !Debugger.IsAttached)
 | 
			
		||||
        //{
 | 
			
		||||
        //    Debugger.Launch();
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        foreach (var path in receiver.Imports)
 | 
			
		||||
        {
 | 
			
		||||
            // Register receiver
 | 
			
		||||
            if (!TemplateGenerator.urlRegex.IsMatch(path))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      
 | 
			
		||||
        void ReportError(GeneratorExecutionContext context, string title, string msg, string category)
 | 
			
		||||
        {
 | 
			
		||||
            context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("MySG001", title, msg, category, DiagnosticSeverity.Error, true), Location.None));
 | 
			
		||||
        }
 | 
			
		||||
            //File.WriteAllLines("C:\\gen\\ref.log", context.Compilation.ReferencedAssemblyNames.Select(x => x.ToString()));
 | 
			
		||||
 | 
			
		||||
     
 | 
			
		||||
        void GenerateModel(GeneratorExecutionContext context, TypeTemplate[] templates)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var tmp in templates)
 | 
			
		||||
            if (cache.Contains(path))
 | 
			
		||||
            {
 | 
			
		||||
                if (tmp.Type == TemplateType.Resource)
 | 
			
		||||
                {
 | 
			
		||||
                    var source = TemplateGenerator.GenerateClass(tmp, templates);
 | 
			
		||||
                    // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
 | 
			
		||||
                    context.AddSource(tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
                }
 | 
			
		||||
                else if (tmp.Type == TemplateType.Record)
 | 
			
		||||
                {
 | 
			
		||||
                    var source = TemplateGenerator.GenerateRecord(tmp, templates);
 | 
			
		||||
                    // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
 | 
			
		||||
                    context.AddSource(tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
                }
 | 
			
		||||
                GenerateModel(context, cache[path]);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // generate info class
 | 
			
		||||
            // Syncronization
 | 
			
		||||
            //if (inProgress.Contains(path))
 | 
			
		||||
            //  continue;
 | 
			
		||||
 | 
			
		||||
            //inProgress.Add(path);
 | 
			
		||||
 | 
			
		||||
            var url = TemplateGenerator.urlRegex.Split(path);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " +
 | 
			
		||||
                                string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                            + " }; \r\n public static Type[] Records { get; } = new Type[] { " +
 | 
			
		||||
                                string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                            + " }; " +
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var con = Warehouse.Get<DistributedConnection>(url[1] + "://" + url[2]).Wait(20000);
 | 
			
		||||
                var templates = con.GetLinkTemplates(url[3]).Wait(60000);
 | 
			
		||||
 | 
			
		||||
                            "\r\n } \r\n}";
 | 
			
		||||
                cache[path] = templates;
 | 
			
		||||
 | 
			
		||||
            //File.WriteAllText($@"C:\gen\Esiur.Generated.cs", gen);
 | 
			
		||||
                // make sources
 | 
			
		||||
                GenerateModel(context, templates);
 | 
			
		||||
 | 
			
		||||
            context.AddSource("Esiur.Generated.cs", typesFile);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                ReportError(context, ex.Source, ex.Message, "Esiur");
 | 
			
		||||
                //System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //inProgress.Remove(path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
        public void Execute(GeneratorExecutionContext context)
 | 
			
		||||
        //#if DEBUG
 | 
			
		||||
 | 
			
		||||
        //#endif
 | 
			
		||||
 | 
			
		||||
        //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0);
 | 
			
		||||
 | 
			
		||||
        foreach (var ci in receiver.Classes.Values)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (!(context.SyntaxContextReceiver is ResourceGeneratorReceiver receiver))
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            //if (receiver.Imports.Count > 0 && !Debugger.IsAttached)
 | 
			
		||||
            //{
 | 
			
		||||
            //    Debugger.Launch();
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
            foreach (var path in receiver.Imports)
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (!TemplateGenerator.urlRegex.IsMatch(path))
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                //File.WriteAllLines("C:\\gen\\ref.log", context.Compilation.ReferencedAssemblyNames.Select(x => x.ToString()));
 | 
			
		||||
 | 
			
		||||
                if (cache.Contains(path))
 | 
			
		||||
                {
 | 
			
		||||
                    GenerateModel(context, cache[path]);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Syncronization
 | 
			
		||||
                //if (inProgress.Contains(path))
 | 
			
		||||
                //  continue;
 | 
			
		||||
 | 
			
		||||
                //inProgress.Add(path);
 | 
			
		||||
 | 
			
		||||
                var url = TemplateGenerator.urlRegex.Split(path);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var con = Warehouse.Get<DistributedConnection>(url[1] + "://" + url[2]).Wait(20000);
 | 
			
		||||
                    var templates = con.GetLinkTemplates(url[3]).Wait(60000);
 | 
			
		||||
 | 
			
		||||
                    cache[path] = templates;
 | 
			
		||||
 | 
			
		||||
                    // make sources
 | 
			
		||||
                    GenerateModel(context, templates);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    ReportError(context, ex.Source, ex.Message, "Esiur");
 | 
			
		||||
                    //System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                //inProgress.Remove(path);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //#if DEBUG
 | 
			
		||||
 | 
			
		||||
            //#endif
 | 
			
		||||
 | 
			
		||||
            //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0);
 | 
			
		||||
 | 
			
		||||
            foreach (var ci in receiver.Classes.Values)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    var code = @$"using Esiur.Resource; 
 | 
			
		||||
                var code = @$"using Esiur.Resource; 
 | 
			
		||||
using Esiur.Core; 
 | 
			
		||||
namespace { ci.ClassSymbol.ContainingNamespace.ToDisplayString() } {{
 | 
			
		||||
";
 | 
			
		||||
 | 
			
		||||
                    if (ci.HasInterface)
 | 
			
		||||
                        code += $"public partial class {ci.Name} {{";
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        code += @$"public partial class {ci.Name} : IResource {{
 | 
			
		||||
                if (ci.HasInterface)
 | 
			
		||||
                    code += $"public partial class {ci.Name} {{";
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    code += @$"public partial class {ci.Name} : IResource {{
 | 
			
		||||
public Instance Instance {{ get; set; }}
 | 
			
		||||
public event DestroyedEvent OnDestroy;
 | 
			
		||||
public virtual void Destroy() {{ OnDestroy?.Invoke(this); }}
 | 
			
		||||
";
 | 
			
		||||
 | 
			
		||||
                        if (!ci.HasTrigger)
 | 
			
		||||
                            code += "public AsyncReply<bool> Trigger(ResourceTrigger trigger) => new AsyncReply<bool>(true);\r\n";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    //Debugger.Launch();
 | 
			
		||||
 | 
			
		||||
                    foreach (var f in ci.Fields)
 | 
			
		||||
                    {
 | 
			
		||||
                        var givenName = f.GetAttributes().Where(x=>x.AttributeClass.Name == "PublicAttribute").FirstOrDefault()?.ConstructorArguments.FirstOrDefault().Value;
 | 
			
		||||
 | 
			
		||||
                        var fn = f.Name;
 | 
			
		||||
                        var pn = givenName ?? fn.Substring(0, 1).ToUpper() + fn.Substring(1);
 | 
			
		||||
 | 
			
		||||
                        //System.IO.File.AppendAllText("c:\\gen\\fields.txt", fn + " -> " + pn + "\r\n");
 | 
			
		||||
 | 
			
		||||
                        // copy attributes 
 | 
			
		||||
                        var attrs = string.Join(" ", f.GetAttributes().Select(x => $"[{x.ToString()}]"));
 | 
			
		||||
                        code += $"{attrs} public {f.Type} {pn} {{ get => {fn}; set {{ {fn} = value; Instance?.Modified(); }} }}\r\n";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    code += "}}\r\n";
 | 
			
		||||
 | 
			
		||||
                    //System.IO.File.WriteAllText("c:\\gen\\" + ci.Name + "_esiur.cs", code);
 | 
			
		||||
                    context.AddSource(ci.Name + ".Generated.cs", code);
 | 
			
		||||
 | 
			
		||||
                    if (!ci.HasTrigger)
 | 
			
		||||
                        code += "public AsyncReply<bool> Trigger(ResourceTrigger trigger) => new AsyncReply<bool>(true);\r\n";
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
 | 
			
		||||
                //Debugger.Launch();
 | 
			
		||||
 | 
			
		||||
                foreach (var f in ci.Fields)
 | 
			
		||||
                {
 | 
			
		||||
                    //System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n");
 | 
			
		||||
                    var givenName = f.GetAttributes().Where(x => x.AttributeClass.Name == "PublicAttribute").FirstOrDefault()?.ConstructorArguments.FirstOrDefault().Value;
 | 
			
		||||
 | 
			
		||||
                    var fn = f.Name;
 | 
			
		||||
                    var pn = givenName ?? fn.Substring(0, 1).ToUpper() + fn.Substring(1);
 | 
			
		||||
 | 
			
		||||
                    //System.IO.File.AppendAllText("c:\\gen\\fields.txt", fn + " -> " + pn + "\r\n");
 | 
			
		||||
 | 
			
		||||
                    // copy attributes 
 | 
			
		||||
                    var attrs = string.Join(" ", f.GetAttributes().Select(x => $"[{x.ToString()}]"));
 | 
			
		||||
                    code += $"{attrs} public {f.Type} {pn} {{ get => {fn}; set {{ {fn} = value; Instance?.Modified(); }} }}\r\n";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                code += "}}\r\n";
 | 
			
		||||
 | 
			
		||||
                //System.IO.File.WriteAllText("c:\\gen\\" + ci.Name + "_esiur.cs", code);
 | 
			
		||||
                context.AddSource(ci.Name + ".Generated.cs", code);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch //(Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                //System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,18 +4,16 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Proxy
 | 
			
		||||
namespace Esiur.Proxy;
 | 
			
		||||
public struct ResourceGeneratorClassInfo
 | 
			
		||||
{
 | 
			
		||||
    public struct ResourceGeneratorClassInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
        public bool HasInterface { get; set; }
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
    public bool HasInterface { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool HasTrigger { get; set; }
 | 
			
		||||
        public List<IFieldSymbol> Fields { get; set; }
 | 
			
		||||
        public ITypeSymbol ClassSymbol { get; set; }
 | 
			
		||||
    public bool HasTrigger { get; set; }
 | 
			
		||||
    public List<IFieldSymbol> Fields { get; set; }
 | 
			
		||||
    public ITypeSymbol ClassSymbol { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ClassDeclarationSyntax ClassDeclaration { get; set; }
 | 
			
		||||
    public ClassDeclarationSyntax ClassDeclaration { get; set; }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,9 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Proxy
 | 
			
		||||
namespace Esiur.Proxy;
 | 
			
		||||
public struct ResourceGeneratorFieldInfo
 | 
			
		||||
{
 | 
			
		||||
    public struct ResourceGeneratorFieldInfo
 | 
			
		||||
    {
 | 
			
		||||
        public IFieldSymbol FieldSymbol { get; set; }
 | 
			
		||||
        public string[] Attributes { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
    public IFieldSymbol FieldSymbol { get; set; }
 | 
			
		||||
    public string[] Attributes { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,92 +6,91 @@ using System.Diagnostics;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Proxy
 | 
			
		||||
namespace Esiur.Proxy;
 | 
			
		||||
public class ResourceGeneratorReceiver : ISyntaxContextReceiver
 | 
			
		||||
{
 | 
			
		||||
    public class ResourceGeneratorReceiver : ISyntaxContextReceiver
 | 
			
		||||
 | 
			
		||||
    public Dictionary<string, ResourceGeneratorClassInfo> Classes { get; } = new();
 | 
			
		||||
 | 
			
		||||
    public List<string> Imports { get; } = new();
 | 
			
		||||
 | 
			
		||||
    public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        public Dictionary<string, ResourceGeneratorClassInfo> Classes { get; } = new();
 | 
			
		||||
 | 
			
		||||
        public List<string> Imports { get; } = new ();
 | 
			
		||||
 | 
			
		||||
        public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
 | 
			
		||||
        if (context.Node is ClassDeclarationSyntax)
 | 
			
		||||
        {
 | 
			
		||||
            var cds = context.Node as ClassDeclarationSyntax;
 | 
			
		||||
            var cls = context.SemanticModel.GetDeclaredSymbol(cds) as ITypeSymbol;
 | 
			
		||||
            var attrs = cls.GetAttributes();
 | 
			
		||||
 | 
			
		||||
            if (context.Node is ClassDeclarationSyntax)
 | 
			
		||||
            var imports = attrs.Where(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ImportAttribute");
 | 
			
		||||
 | 
			
		||||
            foreach (var import in imports)
 | 
			
		||||
            {
 | 
			
		||||
                var cds = context.Node as ClassDeclarationSyntax;
 | 
			
		||||
                var cls = context.SemanticModel.GetDeclaredSymbol(cds) as ITypeSymbol;
 | 
			
		||||
                var attrs = cls.GetAttributes();
 | 
			
		||||
                // Debugger.Launch();
 | 
			
		||||
 | 
			
		||||
                var imports = attrs.Where(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ImportAttribute");
 | 
			
		||||
                var url = import.ConstructorArguments.First().Value.ToString();
 | 
			
		||||
 | 
			
		||||
                foreach (var import in imports)
 | 
			
		||||
                if (!Imports.Contains(url))
 | 
			
		||||
                    Imports.Add(url);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (attrs.Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute"))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                var hasTrigger = cds.Members
 | 
			
		||||
                    .Where(x => x is MethodDeclarationSyntax)
 | 
			
		||||
                    .Select(x => context.SemanticModel.GetDeclaredSymbol(x) as IMethodSymbol)
 | 
			
		||||
                    .Any(x => x.Name == "Trigger"
 | 
			
		||||
                            && x.Parameters.Length == 1
 | 
			
		||||
                            && x.Parameters[0].Type.ToDisplayString() == "Esiur.Resource.ResourceTrigger");
 | 
			
		||||
 | 
			
		||||
                var fields = cds.Members.Where(x => x is FieldDeclarationSyntax)
 | 
			
		||||
                                        .Select(x => context.SemanticModel.GetDeclaredSymbol((x as FieldDeclarationSyntax).Declaration.Variables.First()) as IFieldSymbol)
 | 
			
		||||
                                        .Where(x => x.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.PublicAttribute"))
 | 
			
		||||
                                        .ToArray();
 | 
			
		||||
 | 
			
		||||
                //if (!Debugger.IsAttached)
 | 
			
		||||
                //{
 | 
			
		||||
                //    if (cls.Name == "User")
 | 
			
		||||
                //        Debugger.Launch();
 | 
			
		||||
                //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                // get fields
 | 
			
		||||
 | 
			
		||||
                var fullName = cls.ContainingAssembly + "." + cls.Name;
 | 
			
		||||
 | 
			
		||||
                // Partial class check
 | 
			
		||||
                if (Classes.ContainsKey(fullName))
 | 
			
		||||
                {
 | 
			
		||||
                   // Debugger.Launch();
 | 
			
		||||
 | 
			
		||||
                    var url = import.ConstructorArguments.First().Value.ToString();
 | 
			
		||||
 | 
			
		||||
                    if (!Imports.Contains(url))
 | 
			
		||||
                        Imports.Add(url);
 | 
			
		||||
                    // append fields
 | 
			
		||||
                    var c = Classes[fullName];
 | 
			
		||||
                    c.Fields.AddRange(fields);
 | 
			
		||||
                    if (!c.HasInterface)
 | 
			
		||||
                        c.HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource");
 | 
			
		||||
                    if (!c.HasTrigger)
 | 
			
		||||
                        c.HasTrigger = hasTrigger;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    Classes.Add(fullName, new ResourceGeneratorClassInfo()
 | 
			
		||||
                    {
 | 
			
		||||
                        Name = cls.Name,
 | 
			
		||||
                        ClassDeclaration = cds,
 | 
			
		||||
                        ClassSymbol = cls,
 | 
			
		||||
                        Fields = fields.ToList(),
 | 
			
		||||
                        HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"),
 | 
			
		||||
                        HasTrigger = hasTrigger
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (attrs.Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute"))
 | 
			
		||||
                {
 | 
			
		||||
                    
 | 
			
		||||
                    
 | 
			
		||||
                    var hasTrigger = cds.Members
 | 
			
		||||
                        .Where(x => x is MethodDeclarationSyntax)
 | 
			
		||||
                        .Select(x => context.SemanticModel.GetDeclaredSymbol(x) as IMethodSymbol)
 | 
			
		||||
                        .Any(x => x.Name == "Trigger"
 | 
			
		||||
                                && x.Parameters.Length == 1
 | 
			
		||||
                                && x.Parameters[0].Type.ToDisplayString() == "Esiur.Resource.ResourceTrigger");
 | 
			
		||||
 | 
			
		||||
                    var fields = cds.Members.Where(x => x is FieldDeclarationSyntax)
 | 
			
		||||
                                            .Select(x => context.SemanticModel.GetDeclaredSymbol((x as FieldDeclarationSyntax).Declaration.Variables.First()) as IFieldSymbol)
 | 
			
		||||
                                            .Where(x => x.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.PublicAttribute"))
 | 
			
		||||
                                            .ToArray();
 | 
			
		||||
 | 
			
		||||
                    //if (!Debugger.IsAttached)
 | 
			
		||||
                    //{
 | 
			
		||||
                    //    if (cls.Name == "User")
 | 
			
		||||
                    //        Debugger.Launch();
 | 
			
		||||
                    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    // get fields
 | 
			
		||||
 | 
			
		||||
                    var fullName = cls.ContainingAssembly + "." + cls.Name;
 | 
			
		||||
 | 
			
		||||
                    // Partial class check
 | 
			
		||||
                    if (Classes.ContainsKey(fullName))
 | 
			
		||||
                    {
 | 
			
		||||
                        // append fields
 | 
			
		||||
                        var c = Classes[fullName];
 | 
			
		||||
                        c.Fields.AddRange(fields);
 | 
			
		||||
                        if (!c.HasInterface)
 | 
			
		||||
                            c.HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource");
 | 
			
		||||
                        if (!c.HasTrigger)
 | 
			
		||||
                            c.HasTrigger = hasTrigger;
 | 
			
		||||
                    } else
 | 
			
		||||
                    {
 | 
			
		||||
                        Classes.Add(fullName, new ResourceGeneratorClassInfo()
 | 
			
		||||
                        {
 | 
			
		||||
                            Name = cls.Name,
 | 
			
		||||
                            ClassDeclaration = cds,
 | 
			
		||||
                            ClassSymbol = cls,
 | 
			
		||||
                            Fields = fields.ToList(),
 | 
			
		||||
                            HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"),
 | 
			
		||||
                            HasTrigger = hasTrigger
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,72 +7,71 @@ using System.Reflection;
 | 
			
		||||
using System.Reflection.Emit;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Proxy
 | 
			
		||||
namespace Esiur.Proxy;
 | 
			
		||||
public static class ResourceProxy
 | 
			
		||||
{
 | 
			
		||||
    public static class ResourceProxy
 | 
			
		||||
    {
 | 
			
		||||
        static Dictionary<Type, Type> cache = new Dictionary<Type, Type>();
 | 
			
		||||
        
 | 
			
		||||
    static Dictionary<Type, Type> cache = new Dictionary<Type, Type>();
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
        static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified");
 | 
			
		||||
        static MethodInfo instanceGet = typeof(IResource).GetTypeInfo().GetProperty("Instance").GetGetMethod();
 | 
			
		||||
    static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified");
 | 
			
		||||
    static MethodInfo instanceGet = typeof(IResource).GetTypeInfo().GetProperty("Instance").GetGetMethod();
 | 
			
		||||
#else
 | 
			
		||||
        static MethodInfo modifyMethod = typeof(Instance).GetMethod("Modified");
 | 
			
		||||
        static MethodInfo instanceGet = typeof(IResource).GetProperty("Instance").GetGetMethod();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public static Type GetBaseType(object resource)
 | 
			
		||||
    public static Type GetBaseType(object resource)
 | 
			
		||||
    {
 | 
			
		||||
        return GetBaseType(resource.GetType());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Type GetBaseType(Type type)
 | 
			
		||||
    {
 | 
			
		||||
        if (type.Assembly.IsDynamic)
 | 
			
		||||
            return type.GetTypeInfo().BaseType;
 | 
			
		||||
        else
 | 
			
		||||
            return type;
 | 
			
		||||
 | 
			
		||||
        //            if (type.FullName.Contains("Esiur.Proxy.T"))
 | 
			
		||||
        //#if NETSTANDARD
 | 
			
		||||
        //                return type.GetTypeInfo().BaseType;
 | 
			
		||||
        //#else
 | 
			
		||||
        //            return type.BaseType;
 | 
			
		||||
        //#endif
 | 
			
		||||
        //            else
 | 
			
		||||
        //                return type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Type GetProxy(Type type)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (cache.ContainsKey(type))
 | 
			
		||||
            return cache[type];
 | 
			
		||||
 | 
			
		||||
        // check if the type was made with code generation
 | 
			
		||||
        if (type.GetCustomAttribute<ResourceAttribute>(false) != null)
 | 
			
		||||
        {
 | 
			
		||||
            return GetBaseType(resource.GetType());
 | 
			
		||||
            cache.Add(type, type);
 | 
			
		||||
            return type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Type GetBaseType(Type type)
 | 
			
		||||
        if (!Codec.ImplementsInterface(type, typeof(IResource)))
 | 
			
		||||
        {
 | 
			
		||||
            if (type.Assembly.IsDynamic)
 | 
			
		||||
                return type.GetTypeInfo().BaseType;
 | 
			
		||||
            else
 | 
			
		||||
                return type;
 | 
			
		||||
 | 
			
		||||
//            if (type.FullName.Contains("Esiur.Proxy.T"))
 | 
			
		||||
//#if NETSTANDARD
 | 
			
		||||
//                return type.GetTypeInfo().BaseType;
 | 
			
		||||
//#else
 | 
			
		||||
//            return type.BaseType;
 | 
			
		||||
//#endif
 | 
			
		||||
//            else
 | 
			
		||||
//                return type;
 | 
			
		||||
            cache.Add(type, type);
 | 
			
		||||
            return type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Type GetProxy(Type type)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (cache.ContainsKey(type))
 | 
			
		||||
                return cache[type];
 | 
			
		||||
 | 
			
		||||
            // check if the type was made with code generation
 | 
			
		||||
            if (type.GetCustomAttribute<ResourceAttribute>(false) != null)
 | 
			
		||||
            {
 | 
			
		||||
                cache.Add(type, type);
 | 
			
		||||
                return type;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!Codec.ImplementsInterface(type, typeof(IResource)))
 | 
			
		||||
            {
 | 
			
		||||
                cache.Add(type, type);
 | 
			
		||||
                return type;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
            var typeInfo = type.GetTypeInfo();
 | 
			
		||||
        var typeInfo = type.GetTypeInfo();
 | 
			
		||||
 | 
			
		||||
            if (typeInfo.IsSealed || typeInfo.IsAbstract)
 | 
			
		||||
                throw new Exception("Sealed/Abastract classes can't be proxied.");
 | 
			
		||||
        if (typeInfo.IsSealed || typeInfo.IsAbstract)
 | 
			
		||||
            throw new Exception("Sealed/Abastract classes can't be proxied.");
 | 
			
		||||
 | 
			
		||||
            var props = from p in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public)
 | 
			
		||||
                        where p.CanWrite && p.SetMethod.IsVirtual && !p.SetMethod.IsFinal && 
 | 
			
		||||
                        p.GetCustomAttribute<PublicAttribute>(false) != null
 | 
			
		||||
                        select p;
 | 
			
		||||
        var props = from p in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public)
 | 
			
		||||
                    where p.CanWrite && p.SetMethod.IsVirtual && !p.SetMethod.IsFinal &&
 | 
			
		||||
                    p.GetCustomAttribute<PublicAttribute>(false) != null
 | 
			
		||||
                    select p;
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
            if (type.IsSealed)
 | 
			
		||||
@@ -84,172 +83,171 @@ namespace Esiur.Proxy
 | 
			
		||||
                select p;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
            var assemblyName = new AssemblyName("Esiur.Proxy.T." + type.Assembly.GetName().Name);// type.Namespace);
 | 
			
		||||
            assemblyName.Version = type.Assembly.GetName().Version;
 | 
			
		||||
            assemblyName.CultureInfo = type.Assembly.GetName().CultureInfo;
 | 
			
		||||
            //assemblyName.SetPublicKeyToken(null);
 | 
			
		||||
        var assemblyName = new AssemblyName("Esiur.Proxy.T." + type.Assembly.GetName().Name);// type.Namespace);
 | 
			
		||||
        assemblyName.Version = type.Assembly.GetName().Version;
 | 
			
		||||
        assemblyName.CultureInfo = type.Assembly.GetName().CultureInfo;
 | 
			
		||||
        //assemblyName.SetPublicKeyToken(null);
 | 
			
		||||
 | 
			
		||||
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
 | 
			
		||||
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
 | 
			
		||||
            var typeName = "Esiur.Proxy.T." + type.FullName;// Assembly.CreateQualifiedName(assemblyName.FullName, "Esiur.Proxy.T." + type.FullName);
 | 
			
		||||
        var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
 | 
			
		||||
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
 | 
			
		||||
        var typeName = "Esiur.Proxy.T." + type.FullName;// Assembly.CreateQualifiedName(assemblyName.FullName, "Esiur.Proxy.T." + type.FullName);
 | 
			
		||||
 | 
			
		||||
            var typeBuilder = moduleBuilder.DefineType(typeName,
 | 
			
		||||
                TypeAttributes.Public | TypeAttributes.Class, type);
 | 
			
		||||
        var typeBuilder = moduleBuilder.DefineType(typeName,
 | 
			
		||||
            TypeAttributes.Public | TypeAttributes.Class, type);
 | 
			
		||||
 | 
			
		||||
            foreach (PropertyInfo propertyInfo in props)
 | 
			
		||||
                CreateProperty(propertyInfo, typeBuilder, type);
 | 
			
		||||
        foreach (PropertyInfo propertyInfo in props)
 | 
			
		||||
            CreateProperty(propertyInfo, typeBuilder, type);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if NETSTANDARD
 | 
			
		||||
            var t = typeBuilder.CreateTypeInfo().AsType();
 | 
			
		||||
            cache.Add(type, t);
 | 
			
		||||
            return t;
 | 
			
		||||
        var t = typeBuilder.CreateTypeInfo().AsType();
 | 
			
		||||
        cache.Add(type, t);
 | 
			
		||||
        return t;
 | 
			
		||||
#else
 | 
			
		||||
            
 | 
			
		||||
            var t = typeBuilder.CreateType();
 | 
			
		||||
            cache.Add(type, t);
 | 
			
		||||
            return t;
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Type GetProxy<T>()
 | 
			
		||||
            where T : IResource
 | 
			
		||||
        {
 | 
			
		||||
            return GetProxy(typeof(T));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //private static void C
 | 
			
		||||
        private static void CreateProperty(PropertyInfo pi, TypeBuilder typeBuilder, Type resourceType)
 | 
			
		||||
        {
 | 
			
		||||
            var propertyBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, null);
 | 
			
		||||
 | 
			
		||||
            // Create set method
 | 
			
		||||
            MethodBuilder builder = typeBuilder.DefineMethod("set_" + pi.Name,
 | 
			
		||||
                MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { pi.PropertyType });
 | 
			
		||||
            builder.DefineParameter(1, ParameterAttributes.None, "value");
 | 
			
		||||
            ILGenerator g = builder.GetILGenerator();
 | 
			
		||||
 | 
			
		||||
            var getInstance = resourceType.GetTypeInfo().GetProperty("Instance").GetGetMethod();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            //g.Emit(OpCodes.Ldarg_1);
 | 
			
		||||
            //g.Emit(OpCodes.Call, pi.GetSetMethod());
 | 
			
		||||
            //g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
            //g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            //g.Emit(OpCodes.Call, getInstance);
 | 
			
		||||
            //g.Emit(OpCodes.Ldstr, pi.Name);
 | 
			
		||||
            //g.Emit(OpCodes.Call, modifyMethod);
 | 
			
		||||
            //g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
            //g.Emit(OpCodes.Ret);
 | 
			
		||||
 | 
			
		||||
            Label exitMethod = g.DefineLabel();
 | 
			
		||||
            Label callModified = g.DefineLabel();
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_1);
 | 
			
		||||
            g.Emit(OpCodes.Call, pi.GetSetMethod());
 | 
			
		||||
            g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            g.Emit(OpCodes.Call, getInstance);
 | 
			
		||||
            g.Emit(OpCodes.Dup);
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Brtrue_S, callModified);
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Pop);
 | 
			
		||||
            g.Emit(OpCodes.Br_S, exitMethod);
 | 
			
		||||
 | 
			
		||||
            g.MarkLabel(callModified);
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Ldstr, pi.Name);
 | 
			
		||||
            g.Emit(OpCodes.Call, modifyMethod);
 | 
			
		||||
            g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
            g.MarkLabel(exitMethod);
 | 
			
		||||
            g.Emit(OpCodes.Ret);
 | 
			
		||||
            propertyBuilder.SetSetMethod(builder);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            builder = typeBuilder.DefineMethod("get_" + pi.Name, MethodAttributes.Public | MethodAttributes.Virtual, pi.PropertyType, null);
 | 
			
		||||
            g = builder.GetILGenerator();
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            g.Emit(OpCodes.Call, pi.GetGetMethod());
 | 
			
		||||
            g.Emit(OpCodes.Ret);
 | 
			
		||||
 | 
			
		||||
            propertyBuilder.SetGetMethod(builder);
 | 
			
		||||
 | 
			
		||||
            // g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            // g.Emit(OpCodes.Call, pi.GetGetMethod());
 | 
			
		||||
            // g.Emit(OpCodes.Ret);
 | 
			
		||||
 | 
			
		||||
            // propertyBuilder.SetGetMethod(builder);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Label callModified = g.DefineLabel();
 | 
			
		||||
            Label exitMethod = g.DefineLabel();
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
             //   IL_0000: ldarg.0
 | 
			
		||||
	            //IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance()
 | 
			
		||||
	            //// (no C# code)
 | 
			
		||||
	            //IL_0006: dup
 | 
			
		||||
	            //IL_0007: brtrue.s IL_000c
 | 
			
		||||
	            //IL_0009: pop
 | 
			
		||||
	            //// }
 | 
			
		||||
	            //IL_000a: br.s IL_0017
 | 
			
		||||
	            //// (no C# code)
 | 
			
		||||
	            //IL_000c: ldstr "Level3"
 | 
			
		||||
	            //IL_0011: call instance void [Esiur]Esiur.Resource.Instance::Modified(string)
 | 
			
		||||
	            //IL_0016: nop
 | 
			
		||||
	            //IL_0017: ret
 | 
			
		||||
             
 | 
			
		||||
 | 
			
		||||
            // Add IL code for set method
 | 
			
		||||
            g.Emit(OpCodes.Nop);
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_1);
 | 
			
		||||
            g.Emit(OpCodes.Call, pi.GetSetMethod());
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
             //   IL_0000: ldarg.0
 | 
			
		||||
 	           // IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance()
 | 
			
		||||
 	           // IL_0006: ldstr "Level3"
 | 
			
		||||
	            //IL_000b: callvirt instance void [Esiur]Esiur.Resource.Instance::Modified(string)
 | 
			
		||||
	            //IL_0010: ret
 | 
			
		||||
             
 | 
			
		||||
 | 
			
		||||
            // Call property changed for object
 | 
			
		||||
            g.Emit(OpCodes.Nop);
 | 
			
		||||
            g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
            g.Emit(OpCodes.Call, instanceGet);
 | 
			
		||||
 | 
			
		||||
            g.Emit(OpCodes.Dup);
 | 
			
		||||
            g.Emit(OpCodes.Brtrue_S, callModified);
 | 
			
		||||
            g.Emit(OpCodes.Pop);
 | 
			
		||||
            g.Emit(OpCodes.Br_S, exitMethod);
 | 
			
		||||
 | 
			
		||||
            g.MarkLabel(callModified);
 | 
			
		||||
            g.Emit(OpCodes.Ldstr, pi.Name);
 | 
			
		||||
            g.Emit(OpCodes.Callvirt, modifyMethod);
 | 
			
		||||
            g.Emit(OpCodes.Nop);
 | 
			
		||||
            g.MarkLabel(exitMethod);
 | 
			
		||||
            g.Emit(OpCodes.Ret);
 | 
			
		||||
            propertyBuilder.SetSetMethod(builder);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // create get method
 | 
			
		||||
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Type GetProxy<T>()
 | 
			
		||||
        where T : IResource
 | 
			
		||||
    {
 | 
			
		||||
        return GetProxy(typeof(T));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //private static void C
 | 
			
		||||
    private static void CreateProperty(PropertyInfo pi, TypeBuilder typeBuilder, Type resourceType)
 | 
			
		||||
    {
 | 
			
		||||
        var propertyBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, null);
 | 
			
		||||
 | 
			
		||||
        // Create set method
 | 
			
		||||
        MethodBuilder builder = typeBuilder.DefineMethod("set_" + pi.Name,
 | 
			
		||||
            MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { pi.PropertyType });
 | 
			
		||||
        builder.DefineParameter(1, ParameterAttributes.None, "value");
 | 
			
		||||
        ILGenerator g = builder.GetILGenerator();
 | 
			
		||||
 | 
			
		||||
        var getInstance = resourceType.GetTypeInfo().GetProperty("Instance").GetGetMethod();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        //g.Emit(OpCodes.Ldarg_1);
 | 
			
		||||
        //g.Emit(OpCodes.Call, pi.GetSetMethod());
 | 
			
		||||
        //g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
        //g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        //g.Emit(OpCodes.Call, getInstance);
 | 
			
		||||
        //g.Emit(OpCodes.Ldstr, pi.Name);
 | 
			
		||||
        //g.Emit(OpCodes.Call, modifyMethod);
 | 
			
		||||
        //g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
        //g.Emit(OpCodes.Ret);
 | 
			
		||||
 | 
			
		||||
        Label exitMethod = g.DefineLabel();
 | 
			
		||||
        Label callModified = g.DefineLabel();
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_1);
 | 
			
		||||
        g.Emit(OpCodes.Call, pi.GetSetMethod());
 | 
			
		||||
        g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        g.Emit(OpCodes.Call, getInstance);
 | 
			
		||||
        g.Emit(OpCodes.Dup);
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Brtrue_S, callModified);
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Pop);
 | 
			
		||||
        g.Emit(OpCodes.Br_S, exitMethod);
 | 
			
		||||
 | 
			
		||||
        g.MarkLabel(callModified);
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Ldstr, pi.Name);
 | 
			
		||||
        g.Emit(OpCodes.Call, modifyMethod);
 | 
			
		||||
        g.Emit(OpCodes.Nop);
 | 
			
		||||
 | 
			
		||||
        g.MarkLabel(exitMethod);
 | 
			
		||||
        g.Emit(OpCodes.Ret);
 | 
			
		||||
        propertyBuilder.SetSetMethod(builder);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        builder = typeBuilder.DefineMethod("get_" + pi.Name, MethodAttributes.Public | MethodAttributes.Virtual, pi.PropertyType, null);
 | 
			
		||||
        g = builder.GetILGenerator();
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        g.Emit(OpCodes.Call, pi.GetGetMethod());
 | 
			
		||||
        g.Emit(OpCodes.Ret);
 | 
			
		||||
 | 
			
		||||
        propertyBuilder.SetGetMethod(builder);
 | 
			
		||||
 | 
			
		||||
        // g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        // g.Emit(OpCodes.Call, pi.GetGetMethod());
 | 
			
		||||
        // g.Emit(OpCodes.Ret);
 | 
			
		||||
 | 
			
		||||
        // propertyBuilder.SetGetMethod(builder);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        Label callModified = g.DefineLabel();
 | 
			
		||||
        Label exitMethod = g.DefineLabel();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
         //   IL_0000: ldarg.0
 | 
			
		||||
            //IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance()
 | 
			
		||||
            //// (no C# code)
 | 
			
		||||
            //IL_0006: dup
 | 
			
		||||
            //IL_0007: brtrue.s IL_000c
 | 
			
		||||
            //IL_0009: pop
 | 
			
		||||
            //// }
 | 
			
		||||
            //IL_000a: br.s IL_0017
 | 
			
		||||
            //// (no C# code)
 | 
			
		||||
            //IL_000c: ldstr "Level3"
 | 
			
		||||
            //IL_0011: call instance void [Esiur]Esiur.Resource.Instance::Modified(string)
 | 
			
		||||
            //IL_0016: nop
 | 
			
		||||
            //IL_0017: ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Add IL code for set method
 | 
			
		||||
        g.Emit(OpCodes.Nop);
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_1);
 | 
			
		||||
        g.Emit(OpCodes.Call, pi.GetSetMethod());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
         //   IL_0000: ldarg.0
 | 
			
		||||
           // IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance()
 | 
			
		||||
           // IL_0006: ldstr "Level3"
 | 
			
		||||
            //IL_000b: callvirt instance void [Esiur]Esiur.Resource.Instance::Modified(string)
 | 
			
		||||
            //IL_0010: ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Call property changed for object
 | 
			
		||||
        g.Emit(OpCodes.Nop);
 | 
			
		||||
        g.Emit(OpCodes.Ldarg_0);
 | 
			
		||||
        g.Emit(OpCodes.Call, instanceGet);
 | 
			
		||||
 | 
			
		||||
        g.Emit(OpCodes.Dup);
 | 
			
		||||
        g.Emit(OpCodes.Brtrue_S, callModified);
 | 
			
		||||
        g.Emit(OpCodes.Pop);
 | 
			
		||||
        g.Emit(OpCodes.Br_S, exitMethod);
 | 
			
		||||
 | 
			
		||||
        g.MarkLabel(callModified);
 | 
			
		||||
        g.Emit(OpCodes.Ldstr, pi.Name);
 | 
			
		||||
        g.Emit(OpCodes.Callvirt, modifyMethod);
 | 
			
		||||
        g.Emit(OpCodes.Nop);
 | 
			
		||||
        g.MarkLabel(exitMethod);
 | 
			
		||||
        g.Emit(OpCodes.Ret);
 | 
			
		||||
        propertyBuilder.SetSetMethod(builder);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // create get method
 | 
			
		||||
 | 
			
		||||
        */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,229 +10,227 @@ using Esiur.Resource;
 | 
			
		||||
using Esiur.Net.IIP;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Esiur.Proxy
 | 
			
		||||
namespace Esiur.Proxy;
 | 
			
		||||
public static class TemplateGenerator
 | 
			
		||||
{
 | 
			
		||||
    public static class TemplateGenerator
 | 
			
		||||
    internal static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
 | 
			
		||||
 | 
			
		||||
    internal static string GenerateRecord(TypeTemplate template, TypeTemplate[] templates)
 | 
			
		||||
    {
 | 
			
		||||
        internal static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
 | 
			
		||||
        var cls = template.ClassName.Split('.');
 | 
			
		||||
 | 
			
		||||
        internal static string GenerateRecord(TypeTemplate template, TypeTemplate[] templates)
 | 
			
		||||
        var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
 | 
			
		||||
        var className = cls.Last();
 | 
			
		||||
 | 
			
		||||
        var rt = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
        rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
 | 
			
		||||
        rt.AppendLine($"namespace { nameSpace} {{");
 | 
			
		||||
        rt.AppendLine($"public class {className} : IRecord {{");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        foreach (var p in template.Properties)
 | 
			
		||||
        {
 | 
			
		||||
            var cls = template.ClassName.Split('.');
 | 
			
		||||
 | 
			
		||||
            var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
 | 
			
		||||
            var className = cls.Last();
 | 
			
		||||
 | 
			
		||||
            var rt = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
 | 
			
		||||
            rt.AppendLine($"namespace { nameSpace} {{");
 | 
			
		||||
            rt.AppendLine($"public class {className} : IRecord {{");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            foreach (var p in template.Properties)
 | 
			
		||||
            {
 | 
			
		||||
                var ptTypeName = GetTypeName(p.ValueType, templates);
 | 
			
		||||
                rt.AppendLine($"public {ptTypeName} {p.Name} {{ get; set; }}");
 | 
			
		||||
                rt.AppendLine();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine("\r\n}\r\n}");
 | 
			
		||||
 | 
			
		||||
            return rt.ToString();
 | 
			
		||||
            var ptTypeName = GetTypeName(p.ValueType, templates);
 | 
			
		||||
            rt.AppendLine($"public {ptTypeName} {p.Name} {{ get; set; }}");
 | 
			
		||||
            rt.AppendLine();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static string GetTypeName(TemplateDataType templateDataType, TypeTemplate[] templates)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (templateDataType.Type == DataType.Resource)
 | 
			
		||||
                return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper )).ClassName;
 | 
			
		||||
            else if (templateDataType.Type == DataType.ResourceArray)
 | 
			
		||||
                return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper )).ClassName + "[]";
 | 
			
		||||
            else if (templateDataType.Type == DataType.Record)
 | 
			
		||||
                return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName;
 | 
			
		||||
            else if (templateDataType.Type == DataType.RecordArray)
 | 
			
		||||
                return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName + "[]";
 | 
			
		||||
 | 
			
		||||
            var name = templateDataType.Type switch
 | 
			
		||||
            {
 | 
			
		||||
                DataType.Bool => "bool",
 | 
			
		||||
                DataType.BoolArray => "bool[]",
 | 
			
		||||
                DataType.Char => "char",
 | 
			
		||||
                DataType.CharArray => "char[]",
 | 
			
		||||
                DataType.DateTime => "DateTime",
 | 
			
		||||
                DataType.DateTimeArray => "DateTime[]",
 | 
			
		||||
                DataType.Decimal => "decimal",
 | 
			
		||||
                DataType.DecimalArray => "decimal[]",
 | 
			
		||||
                DataType.Float32 => "float",
 | 
			
		||||
                DataType.Float32Array => "float[]",
 | 
			
		||||
                DataType.Float64 => "double",
 | 
			
		||||
                DataType.Float64Array => "double[]",
 | 
			
		||||
                DataType.Int16 => "short",
 | 
			
		||||
                DataType.Int16Array => "short[]",
 | 
			
		||||
                DataType.Int32 => "int",
 | 
			
		||||
                DataType.Int32Array => "int[]",
 | 
			
		||||
                DataType.Int64 => "long",
 | 
			
		||||
                DataType.Int64Array => "long[]",
 | 
			
		||||
                DataType.Int8 => "sbyte",
 | 
			
		||||
                DataType.Int8Array => "sbyte[]",
 | 
			
		||||
                DataType.String => "string",
 | 
			
		||||
                DataType.StringArray => "string[]",
 | 
			
		||||
                DataType.Structure => "Structure",
 | 
			
		||||
                DataType.StructureArray => "Structure[]",
 | 
			
		||||
                DataType.UInt16 => "ushort",
 | 
			
		||||
                DataType.UInt16Array => "ushort[]",
 | 
			
		||||
                DataType.UInt32 => "uint",
 | 
			
		||||
                DataType.UInt32Array => "uint[]",
 | 
			
		||||
                DataType.UInt64 => "ulong",
 | 
			
		||||
                DataType.UInt64Array => "ulong[]",
 | 
			
		||||
                DataType.UInt8 => "byte",
 | 
			
		||||
                DataType.UInt8Array => "byte[]",
 | 
			
		||||
                DataType.VarArray => "object[]",
 | 
			
		||||
                DataType.Void => "object",
 | 
			
		||||
                _ => "object"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetTemplate(string url, string dir = null, string username= null, string password = null)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (!urlRegex.IsMatch(url))
 | 
			
		||||
                    throw new Exception("Invalid IIP URL");
 | 
			
		||||
 | 
			
		||||
                var path = urlRegex.Split(url);
 | 
			
		||||
                var con = Warehouse.Get<DistributedConnection>(path[1] + "://" + path[2],
 | 
			
		||||
                        !string.IsNullOrEmpty( username) && !string.IsNullOrEmpty( password) ? new { Username = username, Password = password } : null
 | 
			
		||||
                    ).Wait(20000);
 | 
			
		||||
 | 
			
		||||
                if (con == null)
 | 
			
		||||
                    throw new Exception("Can't connect to server");
 | 
			
		||||
 | 
			
		||||
                if (string.IsNullOrEmpty(dir))
 | 
			
		||||
                    dir = path[2].Replace(":", "_");
 | 
			
		||||
 | 
			
		||||
                var templates = con.GetLinkTemplates(path[3]).Wait(60000);
 | 
			
		||||
                // no longer needed
 | 
			
		||||
                Warehouse.Remove(con);
 | 
			
		||||
 | 
			
		||||
                var tempDir = new DirectoryInfo(Path.GetTempPath() + Path.DirectorySeparatorChar
 | 
			
		||||
                                + Misc.Global.GenerateCode(20) + Path.DirectorySeparatorChar + dir);
 | 
			
		||||
 | 
			
		||||
                if (!tempDir.Exists)
 | 
			
		||||
                    tempDir.Create();
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (FileInfo file in tempDir.GetFiles())
 | 
			
		||||
                        file.Delete();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // make sources
 | 
			
		||||
                foreach (var tmp in templates)
 | 
			
		||||
                {
 | 
			
		||||
                    if (tmp.Type == TemplateType.Resource)
 | 
			
		||||
                    {
 | 
			
		||||
                        var source = GenerateClass(tmp, templates);
 | 
			
		||||
                        File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (tmp.Type == TemplateType.Record)
 | 
			
		||||
                    {
 | 
			
		||||
                        var source = GenerateRecord(tmp, templates);
 | 
			
		||||
                        File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // generate info class
 | 
			
		||||
 | 
			
		||||
                var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " +
 | 
			
		||||
                        string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                    + " }; \r\n public static Type[] Records { get; } = new Type[] { " +
 | 
			
		||||
                        string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                    + " }; " +
 | 
			
		||||
 | 
			
		||||
                    "\r\n } \r\n}";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + "Esiur.Generated.cs", typesFile);
 | 
			
		||||
 | 
			
		||||
                
 | 
			
		||||
                return tempDir.FullName;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch(Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                //File.WriteAllText("C:\\gen\\gettemplate.err", ex.ToString());
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal static string GenerateClass(TypeTemplate template, TypeTemplate[] templates)
 | 
			
		||||
        {
 | 
			
		||||
            var cls = template.ClassName.Split('.');
 | 
			
		||||
 | 
			
		||||
            var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
 | 
			
		||||
            var className = cls.Last();
 | 
			
		||||
 | 
			
		||||
            var rt = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
 | 
			
		||||
            rt.AppendLine($"namespace { nameSpace} {{");
 | 
			
		||||
            rt.AppendLine($"public class {className} : DistributedResource {{");
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}");
 | 
			
		||||
            rt.AppendLine($"public {className}() {{}}");
 | 
			
		||||
 | 
			
		||||
            foreach (var f in template.Functions)
 | 
			
		||||
            {
 | 
			
		||||
                var rtTypeName = GetTypeName(f.ReturnType, templates);
 | 
			
		||||
                rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}(");
 | 
			
		||||
                rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name)));
 | 
			
		||||
 | 
			
		||||
                rt.AppendLine(") {");
 | 
			
		||||
                rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();");
 | 
			
		||||
                rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})");
 | 
			
		||||
                rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))");
 | 
			
		||||
                rt.AppendLine($".Error(x => rt.TriggerError(x))");
 | 
			
		||||
                rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));");
 | 
			
		||||
                rt.AppendLine("return rt; }");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (var p in template.Properties)
 | 
			
		||||
            {
 | 
			
		||||
                var ptTypeName = GetTypeName(p.ValueType, templates);
 | 
			
		||||
                rt.AppendLine($"public {ptTypeName} {p.Name} {{");
 | 
			
		||||
                rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];");
 | 
			
		||||
                rt.AppendLine($"set =>  _Set({p.Index}, value);");
 | 
			
		||||
                rt.AppendLine("}");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (template.Events.Length > 0)
 | 
			
		||||
            {
 | 
			
		||||
                rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {");
 | 
			
		||||
                rt.AppendLine("switch (index) {");
 | 
			
		||||
 | 
			
		||||
                var eventsList = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
                foreach (var e in template.Events)
 | 
			
		||||
                {
 | 
			
		||||
                    var etTypeName = GetTypeName(e.ArgumentType, templates);
 | 
			
		||||
                    rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;");
 | 
			
		||||
                    eventsList.AppendLine($"public event ResourceEventHandler<{etTypeName}> {e.Name};");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                rt.AppendLine("}}");
 | 
			
		||||
 | 
			
		||||
                rt.AppendLine(eventsList.ToString());
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine("\r\n}\r\n}");
 | 
			
		||||
 | 
			
		||||
            return rt.ToString();
 | 
			
		||||
        }
 | 
			
		||||
        rt.AppendLine("\r\n}\r\n}");
 | 
			
		||||
 | 
			
		||||
        return rt.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static string GetTypeName(TemplateDataType templateDataType, TypeTemplate[] templates)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (templateDataType.Type == DataType.Resource)
 | 
			
		||||
            return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper)).ClassName;
 | 
			
		||||
        else if (templateDataType.Type == DataType.ResourceArray)
 | 
			
		||||
            return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper)).ClassName + "[]";
 | 
			
		||||
        else if (templateDataType.Type == DataType.Record)
 | 
			
		||||
            return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName;
 | 
			
		||||
        else if (templateDataType.Type == DataType.RecordArray)
 | 
			
		||||
            return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName + "[]";
 | 
			
		||||
 | 
			
		||||
        var name = templateDataType.Type switch
 | 
			
		||||
        {
 | 
			
		||||
            DataType.Bool => "bool",
 | 
			
		||||
            DataType.BoolArray => "bool[]",
 | 
			
		||||
            DataType.Char => "char",
 | 
			
		||||
            DataType.CharArray => "char[]",
 | 
			
		||||
            DataType.DateTime => "DateTime",
 | 
			
		||||
            DataType.DateTimeArray => "DateTime[]",
 | 
			
		||||
            DataType.Decimal => "decimal",
 | 
			
		||||
            DataType.DecimalArray => "decimal[]",
 | 
			
		||||
            DataType.Float32 => "float",
 | 
			
		||||
            DataType.Float32Array => "float[]",
 | 
			
		||||
            DataType.Float64 => "double",
 | 
			
		||||
            DataType.Float64Array => "double[]",
 | 
			
		||||
            DataType.Int16 => "short",
 | 
			
		||||
            DataType.Int16Array => "short[]",
 | 
			
		||||
            DataType.Int32 => "int",
 | 
			
		||||
            DataType.Int32Array => "int[]",
 | 
			
		||||
            DataType.Int64 => "long",
 | 
			
		||||
            DataType.Int64Array => "long[]",
 | 
			
		||||
            DataType.Int8 => "sbyte",
 | 
			
		||||
            DataType.Int8Array => "sbyte[]",
 | 
			
		||||
            DataType.String => "string",
 | 
			
		||||
            DataType.StringArray => "string[]",
 | 
			
		||||
            DataType.Structure => "Structure",
 | 
			
		||||
            DataType.StructureArray => "Structure[]",
 | 
			
		||||
            DataType.UInt16 => "ushort",
 | 
			
		||||
            DataType.UInt16Array => "ushort[]",
 | 
			
		||||
            DataType.UInt32 => "uint",
 | 
			
		||||
            DataType.UInt32Array => "uint[]",
 | 
			
		||||
            DataType.UInt64 => "ulong",
 | 
			
		||||
            DataType.UInt64Array => "ulong[]",
 | 
			
		||||
            DataType.UInt8 => "byte",
 | 
			
		||||
            DataType.UInt8Array => "byte[]",
 | 
			
		||||
            DataType.VarArray => "object[]",
 | 
			
		||||
            DataType.Void => "object",
 | 
			
		||||
            _ => "object"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string GetTemplate(string url, string dir = null, string username = null, string password = null)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (!urlRegex.IsMatch(url))
 | 
			
		||||
                throw new Exception("Invalid IIP URL");
 | 
			
		||||
 | 
			
		||||
            var path = urlRegex.Split(url);
 | 
			
		||||
            var con = Warehouse.Get<DistributedConnection>(path[1] + "://" + path[2],
 | 
			
		||||
                    !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) ? new { Username = username, Password = password } : null
 | 
			
		||||
                ).Wait(20000);
 | 
			
		||||
 | 
			
		||||
            if (con == null)
 | 
			
		||||
                throw new Exception("Can't connect to server");
 | 
			
		||||
 | 
			
		||||
            if (string.IsNullOrEmpty(dir))
 | 
			
		||||
                dir = path[2].Replace(":", "_");
 | 
			
		||||
 | 
			
		||||
            var templates = con.GetLinkTemplates(path[3]).Wait(60000);
 | 
			
		||||
            // no longer needed
 | 
			
		||||
            Warehouse.Remove(con);
 | 
			
		||||
 | 
			
		||||
            var tempDir = new DirectoryInfo(Path.GetTempPath() + Path.DirectorySeparatorChar
 | 
			
		||||
                            + Misc.Global.GenerateCode(20) + Path.DirectorySeparatorChar + dir);
 | 
			
		||||
 | 
			
		||||
            if (!tempDir.Exists)
 | 
			
		||||
                tempDir.Create();
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                foreach (FileInfo file in tempDir.GetFiles())
 | 
			
		||||
                    file.Delete();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // make sources
 | 
			
		||||
            foreach (var tmp in templates)
 | 
			
		||||
            {
 | 
			
		||||
                if (tmp.Type == TemplateType.Resource)
 | 
			
		||||
                {
 | 
			
		||||
                    var source = GenerateClass(tmp, templates);
 | 
			
		||||
                    File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
                }
 | 
			
		||||
                else if (tmp.Type == TemplateType.Record)
 | 
			
		||||
                {
 | 
			
		||||
                    var source = GenerateRecord(tmp, templates);
 | 
			
		||||
                    File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // generate info class
 | 
			
		||||
 | 
			
		||||
            var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " +
 | 
			
		||||
                    string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                + " }; \r\n public static Type[] Records { get; } = new Type[] { " +
 | 
			
		||||
                    string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})"))
 | 
			
		||||
                + " }; " +
 | 
			
		||||
 | 
			
		||||
                "\r\n } \r\n}";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + "Esiur.Generated.cs", typesFile);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return tempDir.FullName;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            //File.WriteAllText("C:\\gen\\gettemplate.err", ex.ToString());
 | 
			
		||||
            throw ex;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static string GenerateClass(TypeTemplate template, TypeTemplate[] templates)
 | 
			
		||||
    {
 | 
			
		||||
        var cls = template.ClassName.Split('.');
 | 
			
		||||
 | 
			
		||||
        var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
 | 
			
		||||
        var className = cls.Last();
 | 
			
		||||
 | 
			
		||||
        var rt = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
        rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
 | 
			
		||||
        rt.AppendLine($"namespace { nameSpace} {{");
 | 
			
		||||
        rt.AppendLine($"public class {className} : DistributedResource {{");
 | 
			
		||||
 | 
			
		||||
        rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}");
 | 
			
		||||
        rt.AppendLine($"public {className}() {{}}");
 | 
			
		||||
 | 
			
		||||
        foreach (var f in template.Functions)
 | 
			
		||||
        {
 | 
			
		||||
            var rtTypeName = GetTypeName(f.ReturnType, templates);
 | 
			
		||||
            rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}(");
 | 
			
		||||
            rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name)));
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine(") {");
 | 
			
		||||
            rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();");
 | 
			
		||||
            rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})");
 | 
			
		||||
            rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))");
 | 
			
		||||
            rt.AppendLine($".Error(x => rt.TriggerError(x))");
 | 
			
		||||
            rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));");
 | 
			
		||||
            rt.AppendLine("return rt; }");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach (var p in template.Properties)
 | 
			
		||||
        {
 | 
			
		||||
            var ptTypeName = GetTypeName(p.ValueType, templates);
 | 
			
		||||
            rt.AppendLine($"public {ptTypeName} {p.Name} {{");
 | 
			
		||||
            rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];");
 | 
			
		||||
            rt.AppendLine($"set =>  _Set({p.Index}, value);");
 | 
			
		||||
            rt.AppendLine("}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (template.Events.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {");
 | 
			
		||||
            rt.AppendLine("switch (index) {");
 | 
			
		||||
 | 
			
		||||
            var eventsList = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
            foreach (var e in template.Events)
 | 
			
		||||
            {
 | 
			
		||||
                var etTypeName = GetTypeName(e.ArgumentType, templates);
 | 
			
		||||
                rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;");
 | 
			
		||||
                eventsList.AppendLine($"public event ResourceEventHandler<{etTypeName}> {e.Name};");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine("}}");
 | 
			
		||||
 | 
			
		||||
            rt.AppendLine(eventsList.ToString());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rt.AppendLine("\r\n}\r\n}");
 | 
			
		||||
 | 
			
		||||
        return rt.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user