mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-05-06 03:32:57 +00:00
.Net 6 Upgrade
This commit is contained in:
parent
1166e93ba9
commit
530df018ec
@ -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
Loading…
x
Reference in New Issue
Block a user