mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-05-06 11:32:59 +00:00
Add project files.
This commit is contained in:
parent
4c95cb1cc6
commit
7ae722ab51
16
Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj
Normal file
16
Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj
Normal file
@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.5</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Bson" Version="2.4.4" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Esiur\Esiur.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
440
Esiur.Stores.MongoDB/MongoDBStore.cs
Normal file
440
Esiur.Stores.MongoDB/MongoDBStore.cs
Normal file
@ -0,0 +1,440 @@
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using Esiur.Engine;
|
||||
using MongoDB.Driver.Core;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Bson;
|
||||
using Esiur.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Stores.MongoDB
|
||||
{
|
||||
public class MongoDBStore : IStore
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
MongoClient client;
|
||||
IMongoDatabase database;
|
||||
|
||||
Dictionary<string, IResource> resources = new Dictionary<string, IResource>();
|
||||
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return resources.Count; }
|
||||
}
|
||||
public void Destroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MongoDBStore()
|
||||
{
|
||||
client = new MongoClient();
|
||||
this.database = client.GetDatabase("esiur");
|
||||
}
|
||||
|
||||
public MongoDBStore(string connectionString, string database)
|
||||
{
|
||||
client = new MongoClient(connectionString);
|
||||
this.database = client.GetDatabase(database);
|
||||
}
|
||||
|
||||
|
||||
AsyncReply<IResource> Fetch(string id)
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Eq("_id", new BsonObjectId(new ObjectId(id)));
|
||||
var list = this.database.GetCollection<BsonDocument>("resources").Find(filter).ToList();
|
||||
if (list.Count == 0)
|
||||
return new AsyncReply<IResource>(null);
|
||||
var document = list[0];
|
||||
|
||||
|
||||
IResource resource = (IResource)Activator.CreateInstance(Type.GetType(document["classname"].AsString));
|
||||
resources.Add(document["_id"].AsObjectId.ToString(), resource);
|
||||
|
||||
Warehouse.Put(resource, document["name"].AsString, this);
|
||||
|
||||
|
||||
var parents = document["parents"].AsBsonArray;
|
||||
var children = document["children"].AsBsonArray;
|
||||
|
||||
var bag = new AsyncBag<object>();
|
||||
|
||||
foreach (var p in parents)
|
||||
{
|
||||
var ap = Warehouse.Get(p.AsString);
|
||||
bag.Add(ap);
|
||||
ap.Then((x) =>
|
||||
{
|
||||
resource.Instance.Parents.Add(x);
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var c in children)
|
||||
{
|
||||
|
||||
var ac = Warehouse.Get(c.AsString);
|
||||
bag.Add(ac);
|
||||
ac.Then((x) =>
|
||||
{
|
||||
resource.Instance.Children.Add(x);
|
||||
});
|
||||
}
|
||||
|
||||
// Load values
|
||||
var values = document["values"].AsBsonDocument;
|
||||
|
||||
|
||||
foreach (var v in values)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = resource.GetType().GetTypeInfo().GetProperty(v.Name);
|
||||
#else
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
|
||||
var av = Parse(v.Value);
|
||||
bag.Add(av);
|
||||
av.Then((x) =>
|
||||
{
|
||||
if (pi.CanWrite)
|
||||
pi.SetValue(resource, DC.CastConvert(x, pi.PropertyType));
|
||||
});
|
||||
}
|
||||
|
||||
bag.Seal();
|
||||
|
||||
var rt = new AsyncReply<IResource>();
|
||||
|
||||
bag.Then((x) =>
|
||||
{
|
||||
rt.Trigger(resource);
|
||||
});
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
AsyncReply Parse(BsonValue value)
|
||||
{
|
||||
if (value.BsonType == BsonType.Document)
|
||||
{
|
||||
var doc = value.AsBsonDocument;
|
||||
if (doc["type"] == 0)
|
||||
{
|
||||
return Warehouse.Get(doc["link"].AsString);
|
||||
} // structure
|
||||
else if (doc["type"] == 1)
|
||||
{
|
||||
var bag = new AsyncBag<object>();
|
||||
var rt = new AsyncReply<Structure>();
|
||||
|
||||
var bs = (BsonDocument)doc["values"].AsBsonDocument;
|
||||
var s = new Structure();
|
||||
|
||||
foreach (var v in bs)
|
||||
bag.Add(Parse(v.Value));
|
||||
|
||||
bag.Seal();
|
||||
bag.Then((x) =>
|
||||
{
|
||||
for (var i = 0; i < x.Length; i++)
|
||||
s[bs.GetElement(i).Name] = x[i];
|
||||
|
||||
rt.Trigger(s);
|
||||
});
|
||||
|
||||
return rt;
|
||||
}
|
||||
else
|
||||
return new AsyncReply(null);
|
||||
}
|
||||
else if (value.BsonType == BsonType.Array)
|
||||
{
|
||||
var array = value.AsBsonArray;
|
||||
var bag = new AsyncBag<object>();
|
||||
|
||||
foreach (var v in array)
|
||||
bag.Add(Parse(v));
|
||||
|
||||
bag.Seal();
|
||||
|
||||
return bag;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AsyncReply(value.RawValue);
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncReply<IResource> Get(string path)
|
||||
{
|
||||
var p = path.Split('/');
|
||||
if (p.Length == 2)
|
||||
if (p[0] == "id")
|
||||
{
|
||||
// load from Id
|
||||
|
||||
if (resources.ContainsKey(p[1]))
|
||||
return new AsyncReply<IResource>(resources[p[1]]);
|
||||
else
|
||||
return Fetch(p[1]);
|
||||
}
|
||||
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
|
||||
public string Link(IResource resource)
|
||||
{
|
||||
return this.Instance.Name + "/id/" + (string)resource.Instance.Attributes["objectId"];
|
||||
}
|
||||
|
||||
public bool Put(IResource resource)
|
||||
{
|
||||
|
||||
foreach (var kv in resources)
|
||||
if (kv.Value == resource)
|
||||
{
|
||||
resource.Instance.Attributes.Add("objectId", kv.Key);
|
||||
return true;
|
||||
}
|
||||
|
||||
var parents = new BsonArray();
|
||||
var children = new BsonArray();
|
||||
var template = resource.Instance.Template;
|
||||
|
||||
foreach (IResource c in resource.Instance.Children)
|
||||
children.Add(c.Instance.Link);
|
||||
|
||||
foreach (IResource p in resource.Instance.Parents)
|
||||
parents.Add(p.Instance.Link);
|
||||
|
||||
var document = new BsonDocument
|
||||
{
|
||||
{ "parents", parents },
|
||||
{ "children", children },
|
||||
{ "classname", resource.GetType().AssemblyQualifiedName },
|
||||
{ "name", resource.Instance.Name }
|
||||
};
|
||||
|
||||
|
||||
var col = this.database.GetCollection<BsonDocument>("resources");
|
||||
|
||||
col.InsertOne(document);
|
||||
|
||||
resource.Instance.Attributes["objectId"] = document["_id"].ToString();
|
||||
|
||||
var values = new BsonDocument();
|
||||
|
||||
foreach (var pt in template.Properties)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
var rt = pi.GetValue(resource, null);
|
||||
|
||||
values.Add(pt.Name, Compose(rt));
|
||||
}
|
||||
|
||||
var filter = Builders<BsonDocument>.Filter.Eq("_id", document["_id"]);
|
||||
var update = Builders<BsonDocument>.Update
|
||||
.Set("values", values);
|
||||
|
||||
col.UpdateOne(filter, update);
|
||||
|
||||
//document.Add("values", values);
|
||||
|
||||
//col.ReplaceOne(document, document);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public BsonDocument ComposeStructure(Structure value)
|
||||
{
|
||||
var rt = new BsonDocument { { "type", 1 } };
|
||||
|
||||
var values = new BsonDocument();
|
||||
foreach (var i in value)
|
||||
values.Add(i.Key, Compose(i.Value));
|
||||
|
||||
rt.Add("values", values);
|
||||
return rt;
|
||||
}
|
||||
|
||||
public BsonArray ComposeVarArray(object[] array)
|
||||
{
|
||||
var rt = new BsonArray();
|
||||
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
rt.Add(Compose(array[i]));
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
BsonArray ComposeStructureArray(Structure[] structures)
|
||||
{
|
||||
var rt = new BsonArray();
|
||||
|
||||
if (structures == null || structures?.Length == 0)
|
||||
return rt;
|
||||
|
||||
foreach (var s in structures)
|
||||
rt.Add(ComposeStructure(s));
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
BsonArray ComposeResourceArray(IResource[] array)
|
||||
{
|
||||
var rt = new BsonArray();
|
||||
foreach (var r in array)
|
||||
{
|
||||
rt.Add(new BsonDocument { { "type", 0 }, { "link", r.Instance.Link } });
|
||||
|
||||
//if (r.Instance.Attributes.ContainsKey("objectId"))
|
||||
|
||||
//rt.Add(new BsonObjectId(new ObjectId((string)r.Instance.Attributes["objectId"])));
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
private BsonValue Compose(object value)
|
||||
{
|
||||
var type = Codec.GetDataType(value, null);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DataType.Void:
|
||||
// nothing to do;
|
||||
return BsonNull.Value;
|
||||
|
||||
case DataType.String:
|
||||
return new BsonString((string)value);
|
||||
|
||||
case DataType.Resource:
|
||||
case DataType.DistributedResource:
|
||||
return new BsonDocument { { "type", 0 }, { "link", (value as IResource).Instance.Link } };
|
||||
|
||||
//return new BsonObjectId(new ObjectId((string)(value as IResource).Instance.Attributes["objectId"]));
|
||||
|
||||
case DataType.Structure:
|
||||
return ComposeStructure((Structure)value);
|
||||
|
||||
case DataType.VarArray:
|
||||
return ComposeVarArray((object[])value);
|
||||
|
||||
case DataType.ResourceArray:
|
||||
if (value is IResource[])
|
||||
return ComposeResourceArray((IResource[])value);
|
||||
else
|
||||
return ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[])));
|
||||
|
||||
|
||||
case DataType.StructureArray:
|
||||
return ComposeStructureArray((Structure[])value);
|
||||
|
||||
|
||||
default:
|
||||
return BsonValue.Create(value);
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncReply<IResource> Retrieve(uint iid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
|
||||
if (trigger == ResourceTrigger.Initialize)
|
||||
{
|
||||
var filter = new BsonDocument();
|
||||
|
||||
var list = this.database.GetCollection<BsonDocument>("resources").Find(filter).ToList();
|
||||
|
||||
|
||||
// if (list.Count == 0)
|
||||
// return new AsyncBag<IResource>(new IResource[0]);
|
||||
|
||||
var bag = new AsyncBag<IResource>();
|
||||
|
||||
foreach (var r in list)
|
||||
{
|
||||
bag.Add(Get("id/" + r["_id"].AsObjectId.ToString()));
|
||||
}
|
||||
|
||||
bag.Seal();
|
||||
|
||||
var rt = new AsyncReply<bool>();
|
||||
|
||||
bag.Then((x) => { rt.Trigger(true); });
|
||||
|
||||
return rt;
|
||||
}
|
||||
else if (trigger == ResourceTrigger.Terminate)
|
||||
{
|
||||
// save all resources
|
||||
foreach (var resource in resources.Values)
|
||||
SaveResource(resource);
|
||||
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
else
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
|
||||
|
||||
public void SaveResource(IResource resource)
|
||||
{
|
||||
var parents = new BsonArray();
|
||||
var children = new BsonArray();
|
||||
var template = resource.Instance.Template;
|
||||
|
||||
foreach (IResource c in resource.Instance.Children)
|
||||
children.Add(c.Instance.Link);
|
||||
|
||||
foreach (IResource p in resource.Instance.Parents)
|
||||
parents.Add(p.Instance.Link);
|
||||
|
||||
var values = new BsonDocument();
|
||||
|
||||
foreach (var pt in template.Properties)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
var rt = pi.GetValue(resource, null);
|
||||
|
||||
values.Add(pt.Name, Compose(rt));
|
||||
}
|
||||
|
||||
var document = new BsonDocument
|
||||
{
|
||||
{ "parents", parents },
|
||||
{ "children", children },
|
||||
{ "classname", resource.GetType().AssemblyQualifiedName },
|
||||
{ "name", resource.Instance.Name },
|
||||
{ "_id", new BsonObjectId(new ObjectId(resource.Instance.Attributes["objectId"].ToString())) },
|
||||
{"values", values }
|
||||
};
|
||||
|
||||
|
||||
var col = this.database.GetCollection<BsonDocument>("resources");
|
||||
|
||||
var filter = Builders<BsonDocument>.Filter.Eq("_id", document["_id"]);
|
||||
var update = Builders<BsonDocument>.Update
|
||||
.Set("values", values);
|
||||
|
||||
col.UpdateOne(filter, update);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
34
Esiur.sln
Normal file
34
Esiur.sln
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.9
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esiur", "Esiur\Esiur.csproj", "{DEBF78DB-E3B3-47F4-994C-5C970E98C686}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{08AC2E1C-24F0-4309-B35E-73D36A427D2D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esiur.Stores.MongoDB", "Esiur.Stores.MongoDB\Esiur.Stores.MongoDB.csproj", "{F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
253
Esiur/Data/AutoList.cs
Normal file
253
Esiur/Data/AutoList.cs
Normal file
@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using Esiur.Engine;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class AutoList<T, ST> : IEnumerable
|
||||
{
|
||||
|
||||
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;
|
||||
|
||||
ST state;
|
||||
bool removableList;
|
||||
|
||||
/*
|
||||
IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
|
||||
{
|
||||
return list.OrderBy<T,TK>(keySelector);
|
||||
}
|
||||
*/
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
list.Sort();
|
||||
}
|
||||
|
||||
public void Sort(IComparer<T> comparer)
|
||||
{
|
||||
list.Sort(comparer);
|
||||
}
|
||||
|
||||
|
||||
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 AutoList(ST state)
|
||||
{
|
||||
this.state = state;
|
||||
#if NETSTANDARD1_5
|
||||
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(T[] values)
|
||||
{
|
||||
|
||||
#if NETSTANDARD1_5
|
||||
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
|
||||
#else
|
||||
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
|
||||
#endif
|
||||
|
||||
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];
|
||||
|
||||
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 (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);
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <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 void Remove(T value)
|
||||
{
|
||||
if (!list.Contains(value))
|
||||
return;
|
||||
|
||||
if (removableList)
|
||||
if (value != null)
|
||||
((IDestructible)value).OnDestroy -= ItemDestroyed;
|
||||
|
||||
lock (syncRoot)
|
||||
list.Remove(value);
|
||||
|
||||
OnRemoved?.Invoke(state, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of items in the list
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return list.Count; }
|
||||
}
|
||||
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
196
Esiur/Data/BinaryList.cs
Normal file
196
Esiur/Data/BinaryList.cs
Normal file
@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// BinaryList holds a list of items to be converted to binary for storage and transmission
|
||||
/// </summary>
|
||||
public class BinaryList
|
||||
{
|
||||
private List<byte> held = new List<byte>();
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of BinaryList
|
||||
/// </summary>
|
||||
public BinaryList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts parameters to binary in same order
|
||||
/// </summary>
|
||||
/// <param name="values">Variables to convert</param>
|
||||
public static byte[] ToBytes(params object[] values)
|
||||
{
|
||||
var held = new List<byte>();
|
||||
|
||||
foreach (var i in values)
|
||||
{
|
||||
if (i is byte)
|
||||
held.Add((byte)i);
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
|
||||
#else
|
||||
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
|
||||
#endif
|
||||
if (mi != null)
|
||||
{
|
||||
var b = (byte[])mi.Invoke(null, new object[] { i });
|
||||
held.AddRange(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return held.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of BinaryList
|
||||
/// </summary>
|
||||
/// <param name="values">Populate the list items</param>
|
||||
public BinaryList(params object[] values)
|
||||
{
|
||||
AddRange(values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an array of items at the end of the list
|
||||
/// </summary>
|
||||
/// <param name="values">Array of items</param>
|
||||
public void AddRange(object[] values)
|
||||
{
|
||||
foreach (var i in values)
|
||||
{
|
||||
if (i is byte)
|
||||
held.Add((byte)i);
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
|
||||
#else
|
||||
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
|
||||
#endif
|
||||
if (mi != null)
|
||||
{
|
||||
var b = (byte[])mi.Invoke(null, new object[] {i});
|
||||
held.AddRange(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add multiple items at the end of the list
|
||||
/// </summary>
|
||||
/// <param name="values">Parameters of items</param>
|
||||
public void Append(params object[] values)
|
||||
{
|
||||
AddRange(values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert new items to the list at a specified index
|
||||
/// </summary>
|
||||
/// <param name="offset">Position in the list</param>
|
||||
/// <param name="values">Items to insert</param>
|
||||
public void Insert(int offset, params object[] values)
|
||||
{
|
||||
foreach (var i in values)
|
||||
{
|
||||
if (i is byte)
|
||||
{
|
||||
held.Insert(offset++, (byte)i);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
|
||||
#else
|
||||
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
|
||||
#endif
|
||||
if (mi != null)
|
||||
{
|
||||
var b = (byte[])mi.Invoke(null, new object[] { i });
|
||||
held.InsertRange(offset, b);
|
||||
offset += b.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of the items in the list
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return held.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void Append(byte data)
|
||||
{
|
||||
held.Add(data);
|
||||
}
|
||||
|
||||
public void Append(byte[] data)
|
||||
{
|
||||
held.AddRange(data);
|
||||
}
|
||||
|
||||
public void Append(int data)
|
||||
{
|
||||
held.AddRange(DC.ToBytes(data));
|
||||
}
|
||||
|
||||
public void Append(uint data)
|
||||
{
|
||||
held.AddRange(DC.ToBytes(data));
|
||||
}
|
||||
|
||||
public void Append(float data)
|
||||
{
|
||||
held.AddRange(DC.ToBytes(data));
|
||||
}
|
||||
|
||||
public void Append(short data)
|
||||
{
|
||||
held.AddRange(DC.ToBytes(data));
|
||||
}
|
||||
|
||||
public void Append(ushort data)
|
||||
{
|
||||
held.AddRange(DC.ToBytes(data));
|
||||
}
|
||||
|
||||
public void Append(double data)
|
||||
{
|
||||
held.AddRange(DC.ToBytes(data));
|
||||
}
|
||||
|
||||
public void Append(sbyte data)
|
||||
{
|
||||
held.Add((byte)data);
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Convert the list to an array of bytes
|
||||
/// </summary>
|
||||
/// <returns>Bytes array</returns>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
return held.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
991
Esiur/Data/Codec.cs
Normal file
991
Esiur/Data/Codec.cs
Normal file
@ -0,0 +1,991 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using System.ComponentModel;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net.IIP;
|
||||
using Esiur.Resource;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public static class Codec
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if a DataType is an array
|
||||
/// </summary>
|
||||
/// <param name="type">DataType to check</param>
|
||||
/// <returns>True if DataType is an array, otherwise false</returns>
|
||||
public static bool IsArray(this DataType type)
|
||||
{
|
||||
return (((byte)type & 0x80) == 0x80) && (type != DataType.NotModified);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the element DataType
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Passing UInt8Array will return UInt8
|
||||
/// </example>
|
||||
/// <param name="type">DataType to get its element DataType</param>
|
||||
public static DataType GetElementType(this DataType type)
|
||||
{
|
||||
return (DataType)((byte)type & 0x7F);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get DataType array of a given Structure
|
||||
/// </summary>
|
||||
/// <param name="structure">Structure to get its DataTypes</param>
|
||||
/// <param name="connection">Distributed connection is required in case a type is at the other end</param>
|
||||
private static DataType[] GetStructureDateTypes(Structure structure, DistributedConnection connection)
|
||||
{
|
||||
var keys = structure.GetKeys();
|
||||
var types = new DataType[keys.Length];
|
||||
|
||||
for (var i = 0; i < keys.Length; i++)
|
||||
types[i] = Codec.GetDataType(structure[keys[i]], connection);
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two structures
|
||||
/// </summary>
|
||||
/// <param name="initial">Initial structure to compare with</param>
|
||||
/// <param name="next">Next structure to compare with the initial</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure holds items at the other end</param>
|
||||
public static StructureComparisonResult Compare(Structure initial, Structure next, DistributedConnection connection)
|
||||
{
|
||||
if (next == null)
|
||||
return StructureComparisonResult.Null;
|
||||
|
||||
if (initial == null)
|
||||
return StructureComparisonResult.Structure;
|
||||
|
||||
if (next == initial)
|
||||
return StructureComparisonResult.Same;
|
||||
|
||||
if (initial.Length != next.Length)
|
||||
return StructureComparisonResult.Structure;
|
||||
|
||||
var previousKeys = initial.GetKeys();
|
||||
var nextKeys = next.GetKeys();
|
||||
|
||||
for (var i = 0; i < previousKeys.Length; i++)
|
||||
if (previousKeys[i] != nextKeys[i])
|
||||
return StructureComparisonResult.Structure;
|
||||
|
||||
var previousTypes = GetStructureDateTypes(initial, connection);
|
||||
var nextTypes = GetStructureDateTypes(next, connection);
|
||||
|
||||
for (var i = 0; i < previousTypes.Length; i++)
|
||||
if (previousTypes[i] != nextTypes[i])
|
||||
return StructureComparisonResult.StructureSameKeys;
|
||||
|
||||
return StructureComparisonResult.StructureSameTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose an array of structures into an array of bytes
|
||||
/// </summary>
|
||||
/// <param name="structures">Array of Structure to compose</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
|
||||
/// <param name="prependLength">If true, prepend the length as UInt32 at the beginning of the returned bytes array</param>
|
||||
/// <returns>Array of bytes in the network byte order</returns>
|
||||
public static byte[] ComposeStructureArray(Structure[] structures, DistributedConnection connection, bool prependLength = false)
|
||||
{
|
||||
if (structures == null || structures?.Length == 0)
|
||||
return new byte[0];
|
||||
|
||||
var rt = new BinaryList();
|
||||
var comparsion = StructureComparisonResult.Structure;
|
||||
|
||||
rt.Append((byte)comparsion);
|
||||
rt.Append(ComposeStructure(structures[0], connection));
|
||||
|
||||
for (var i = 1; i < structures.Length; i++)
|
||||
{
|
||||
comparsion = Compare(structures[i - 1], structures[i], connection);
|
||||
rt.Append((byte)comparsion);
|
||||
|
||||
if (comparsion == StructureComparisonResult.Structure)
|
||||
rt.Append(ComposeStructure(structures[i], connection));
|
||||
else if (comparsion == StructureComparisonResult.StructureSameKeys)
|
||||
rt.Append(ComposeStructure(structures[i], connection, false));
|
||||
else if (comparsion == StructureComparisonResult.StructureSameTypes)
|
||||
rt.Append(ComposeStructure(structures[i], connection, false, false));
|
||||
}
|
||||
|
||||
if (prependLength)
|
||||
rt.Insert(0, rt.Length);
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an array of structures
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset</param>
|
||||
/// <param name="length">Number of bytes to parse</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
|
||||
/// <returns>Array of structures</returns>
|
||||
public static AsyncBag<Structure> ParseStructureArray(byte[] data, uint offset, uint length, DistributedConnection connection)
|
||||
{
|
||||
var reply = new AsyncBag<Structure>();
|
||||
if (length == 0)
|
||||
{
|
||||
reply.Seal();
|
||||
return reply;
|
||||
}
|
||||
|
||||
var end = offset + length;
|
||||
|
||||
var result = (StructureComparisonResult)data[offset++];
|
||||
|
||||
AsyncReply previous = null;
|
||||
string[] previousKeys = null;
|
||||
DataType[] previousTypes = null;
|
||||
|
||||
|
||||
|
||||
if (result == StructureComparisonResult.Null)
|
||||
previous = new AsyncReply<Structure>(null);
|
||||
else if (result == StructureComparisonResult.Structure)
|
||||
{
|
||||
uint cs = data.GetUInt32(offset);
|
||||
cs += 4;
|
||||
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
reply.Add(previous);
|
||||
|
||||
|
||||
while (offset < end)
|
||||
{
|
||||
result = (StructureComparisonResult)data[offset++];
|
||||
|
||||
if (result == StructureComparisonResult.Null)
|
||||
previous = new AsyncReply<Structure>(null);
|
||||
else if (result == StructureComparisonResult.Structure)
|
||||
{
|
||||
uint cs = data.GetUInt32(offset);
|
||||
cs += 4;
|
||||
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes);
|
||||
offset += cs;
|
||||
}
|
||||
else if (result == StructureComparisonResult.StructureSameKeys)
|
||||
{
|
||||
uint cs = data.GetUInt32(offset);
|
||||
cs += 4;
|
||||
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes, previousKeys);
|
||||
offset += cs;
|
||||
}
|
||||
else if (result == StructureComparisonResult.StructureSameTypes)
|
||||
{
|
||||
uint cs = data.GetUInt32(offset);
|
||||
cs += 4;
|
||||
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes, previousKeys, previousTypes);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
reply.Add(previous);
|
||||
}
|
||||
|
||||
reply.Seal();
|
||||
return reply;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose a structure into an array of bytes
|
||||
/// </summary>
|
||||
/// <param name="value">Structure to compose</param>
|
||||
/// <param name="connection">DistributedConnection is required in case an item in the structure is at the other end</param>
|
||||
/// <param name="includeKeys">Whether to include the structure keys</param>
|
||||
/// <param name="includeTypes">Whether to include each item DataType</param>
|
||||
/// <param name="prependLength">If true, prepend the length as UInt32 at the beginning of the returned bytes array</param>
|
||||
/// <returns>Array of bytes in the network byte order</returns>
|
||||
public static byte[] ComposeStructure(Structure value, DistributedConnection connection, bool includeKeys = true, bool includeTypes = true, bool prependLength = false)
|
||||
{
|
||||
var rt = new BinaryList();
|
||||
|
||||
if (includeKeys)
|
||||
{
|
||||
foreach (var i in value)
|
||||
{
|
||||
var key = DC.ToBytes(i.Key);
|
||||
rt.Append((byte)key.Length, key, Compose(i.Value, connection));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var i in value)
|
||||
rt.Append(Compose(i.Value, connection, includeTypes));
|
||||
}
|
||||
|
||||
if (prependLength)
|
||||
rt.Insert(0, rt.Length);
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a structure
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="length">Number of bytes to parse.</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
|
||||
/// <returns>Value</returns>
|
||||
public static AsyncReply<Structure> ParseStructure(byte[] data, uint offset, uint contentLength, DistributedConnection connection)
|
||||
{
|
||||
string[] pk;
|
||||
DataType[] pt;
|
||||
return ParseStructure(data, offset, contentLength, connection, out pk, out pt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a structure
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="length">Number of bytes to parse.</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
|
||||
/// <param name="parsedKeys">Array to store keys in.</param>
|
||||
/// <param name="parsedTypes">Array to store DataTypes in.</param>
|
||||
/// <param name="keys">Array of keys, in case the data doesn't include keys</param>
|
||||
/// <param name="types">Array of DataTypes, in case the data doesn't include DataTypes</param>
|
||||
/// <returns>Structure</returns>
|
||||
public static AsyncReply<Structure> ParseStructure(byte[] data, uint offset, uint length, DistributedConnection connection, out string[] parsedKeys, out DataType[] parsedTypes, string[] keys = null, DataType[] types = null)
|
||||
{
|
||||
var reply = new AsyncReply<Structure>();
|
||||
var bag = new AsyncBag<object>();
|
||||
var keylist = new List<string>();
|
||||
var typelist = new List<DataType>();
|
||||
|
||||
if (keys == null)
|
||||
{
|
||||
while (length > 0)
|
||||
{
|
||||
var len = data[offset++];
|
||||
keylist.Add(data.GetString(offset, len));
|
||||
offset += len;
|
||||
|
||||
typelist.Add((DataType)data[offset]);
|
||||
|
||||
uint rt;
|
||||
bag.Add(Codec.Parse(data, offset, out rt, connection));
|
||||
length -= rt + len + 1;
|
||||
offset += rt;
|
||||
}
|
||||
}
|
||||
else if (types == null)
|
||||
{
|
||||
keylist.AddRange(keys);
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
typelist.Add((DataType)data[offset]);
|
||||
|
||||
uint rt;
|
||||
bag.Add(Codec.Parse(data, offset, out rt, connection));
|
||||
length -= rt + 1;
|
||||
offset += rt + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keylist.AddRange(keys);
|
||||
typelist.AddRange(types);
|
||||
|
||||
var i = 0;
|
||||
while (length > 0)
|
||||
{
|
||||
uint rt;
|
||||
bag.Add(Codec.Parse(data, offset, out rt, connection, types[i]));
|
||||
length -= rt;
|
||||
offset += rt;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
bag.Seal();
|
||||
|
||||
bag.Then((res) =>
|
||||
{
|
||||
// compose the list
|
||||
var s = new Structure();
|
||||
for (var i = 0; i < keylist.Count; i++)
|
||||
s[keylist[i]] = res[i];
|
||||
reply.Trigger(s);
|
||||
});
|
||||
|
||||
parsedKeys = keylist.ToArray();
|
||||
parsedTypes = typelist.ToArray();
|
||||
return reply;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a value
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
|
||||
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
|
||||
/// <returns>Structure</returns>
|
||||
public static AsyncReply Parse(byte[] data, uint offset, DistributedConnection connection, DataType dataType = DataType.Unspecified)
|
||||
{
|
||||
uint size;
|
||||
return Parse(data, offset, out size, connection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a value
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="size">Output the number of bytes parsed</param>
|
||||
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
|
||||
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
|
||||
/// <returns>Value</returns>
|
||||
public static AsyncReply Parse(byte[] data, uint offset, out uint size, DistributedConnection connection, DataType dataType = DataType.Unspecified)
|
||||
{
|
||||
var reply = new AsyncReply();
|
||||
|
||||
bool isArray;
|
||||
DataType t;
|
||||
|
||||
if (dataType == DataType.Unspecified)
|
||||
{
|
||||
size = 1;
|
||||
dataType = (DataType)data[offset++];
|
||||
}
|
||||
else
|
||||
size = 0;
|
||||
|
||||
t = (DataType)((byte)dataType & 0x7F);
|
||||
|
||||
isArray = ((byte)dataType & 0x80) == 0x80;
|
||||
|
||||
var payloadSize = dataType.Size();// SizeOf();
|
||||
|
||||
|
||||
uint contentLength = 0;
|
||||
|
||||
// check if we have the enough data
|
||||
if (payloadSize == -1)
|
||||
{
|
||||
contentLength = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
size += 4 + contentLength;
|
||||
}
|
||||
else
|
||||
size += (uint)payloadSize;
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
// VarArray ?
|
||||
case DataType.Void:
|
||||
return ParseVarArray(data, offset, contentLength, connection);
|
||||
|
||||
case DataType.Bool:
|
||||
return new AsyncReply<bool[]>(data.GetBooleanArray(offset, contentLength));
|
||||
|
||||
case DataType.UInt8:
|
||||
return new AsyncReply<byte[]>(data.GetUInt8Array(offset, contentLength));
|
||||
|
||||
case DataType.Int8:
|
||||
return new AsyncReply<sbyte[]>(data.GetInt8Array(offset, contentLength));
|
||||
|
||||
case DataType.Char:
|
||||
return new AsyncReply<char[]>(data.GetCharArray(offset, contentLength));
|
||||
|
||||
case DataType.Int16:
|
||||
return new AsyncReply<short[]>(data.GetInt16Array( offset, contentLength));
|
||||
|
||||
case DataType.UInt16:
|
||||
return new AsyncReply<ushort[]>(data.GetUInt16Array(offset, contentLength));
|
||||
|
||||
case DataType.Int32:
|
||||
return new AsyncReply<int[]>(data.GetInt32Array(offset, contentLength));
|
||||
|
||||
case DataType.UInt32:
|
||||
return new AsyncReply<uint[]>(data.GetUInt32Array(offset, contentLength));
|
||||
|
||||
case DataType.Int64:
|
||||
return new AsyncReply<long[]>(data.GetInt64Array(offset, contentLength));
|
||||
|
||||
case DataType.UInt64:
|
||||
return new AsyncReply<ulong[]>(data.GetUInt64Array(offset, contentLength));
|
||||
|
||||
case DataType.Float32:
|
||||
return new AsyncReply<float[]>(data.GetFloat32Array(offset, contentLength));
|
||||
|
||||
case DataType.Float64:
|
||||
return new AsyncReply<double[]>(data.GetFloat64Array(offset, contentLength));
|
||||
|
||||
case DataType.String:
|
||||
return new AsyncReply<string[]>(data.GetStringArray(offset, contentLength));
|
||||
|
||||
case DataType.Resource:
|
||||
case DataType.DistributedResource:
|
||||
return ParseResourceArray(data, offset, contentLength, connection);
|
||||
|
||||
case DataType.DateTime:
|
||||
return new AsyncReply<DateTime[]>(data.GetDateTimeArray(offset, contentLength));
|
||||
|
||||
case DataType.Structure:
|
||||
return ParseStructureArray(data, offset, contentLength, connection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case DataType.NotModified:
|
||||
return new AsyncReply<object>(new NotModified());
|
||||
|
||||
case DataType.Void:
|
||||
return new AsyncReply<object>(null);
|
||||
|
||||
case DataType.Bool:
|
||||
return new AsyncReply<bool>(data.GetBoolean(offset));
|
||||
|
||||
case DataType.UInt8:
|
||||
return new AsyncReply<byte>(data[offset]);
|
||||
|
||||
case DataType.Int8:
|
||||
return new AsyncReply<sbyte>((sbyte)data[offset]);
|
||||
|
||||
case DataType.Char:
|
||||
return new AsyncReply<char>(data.GetChar(offset));
|
||||
|
||||
case DataType.Int16:
|
||||
return new AsyncReply<short>(data.GetInt16(offset));
|
||||
|
||||
case DataType.UInt16:
|
||||
return new AsyncReply<ushort>(data.GetUInt16(offset));
|
||||
|
||||
case DataType.Int32:
|
||||
return new AsyncReply<int>(data.GetInt32(offset));
|
||||
|
||||
case DataType.UInt32:
|
||||
return new AsyncReply<uint>(data.GetUInt32(offset));
|
||||
|
||||
case DataType.Int64:
|
||||
return new AsyncReply<long>(data.GetInt64(offset));
|
||||
|
||||
case DataType.UInt64:
|
||||
return new AsyncReply<ulong>(data.GetUInt64(offset));
|
||||
|
||||
case DataType.Float32:
|
||||
return new AsyncReply<float>(data.GetFloat32(offset));
|
||||
|
||||
case DataType.Float64:
|
||||
return new AsyncReply<double>(data.GetFloat64(offset));
|
||||
|
||||
case DataType.String:
|
||||
return new AsyncReply<string>(data.GetString(offset, contentLength));
|
||||
|
||||
case DataType.Resource:
|
||||
return ParseResource(data, offset);
|
||||
|
||||
case DataType.DistributedResource:
|
||||
return ParseDistributedResource(data, offset, connection);
|
||||
|
||||
case DataType.DateTime:
|
||||
return new AsyncReply<DateTime>(data.GetDateTime(offset));
|
||||
|
||||
case DataType.Structure:
|
||||
return ParseStructure(data, offset, contentLength, connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a resource
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <returns>Resource</returns>
|
||||
public static AsyncReply<IResource> ParseResource(byte[] data, uint offset)
|
||||
{
|
||||
return Warehouse.Get(data.GetUInt32(offset));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a DistributedResource
|
||||
/// </summary>
|
||||
/// <param name="data">Bytes array</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="connection">DistributedConnection is required.</param>
|
||||
/// <returns>DistributedResource</returns>
|
||||
public static AsyncReply<DistributedResource> ParseDistributedResource(byte[] data, uint offset, DistributedConnection connection)
|
||||
{
|
||||
//var g = data.GetGuid(offset);
|
||||
//offset += 16;
|
||||
|
||||
// find the object
|
||||
var iid = data.GetUInt32(offset);
|
||||
|
||||
return connection.Fetch(iid);// Warehouse.Get(iid);
|
||||
}
|
||||
|
||||
public enum ResourceComparisonResult
|
||||
{
|
||||
Null,
|
||||
Distributed,
|
||||
DistributedSameClass,
|
||||
Local,
|
||||
Same
|
||||
}
|
||||
|
||||
public enum StructureComparisonResult : byte
|
||||
{
|
||||
Null,
|
||||
Structure,
|
||||
StructureSameKeys,
|
||||
StructureSameTypes,
|
||||
Same
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a resource is local to a given connection.
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource to check.</param>
|
||||
/// <param name="connection">DistributedConnection to check if the resource is local to it.</param>
|
||||
/// <returns>True, if the resource owner is the given connection, otherwise False.</returns>
|
||||
static bool IsLocalResource(IResource resource, DistributedConnection connection)
|
||||
{
|
||||
if (resource is DistributedResource)
|
||||
if ((resource as DistributedResource).Connection == connection)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two resources
|
||||
/// </summary>
|
||||
/// <param name="initial">Initial resource to make comparison with.</param>
|
||||
/// <param name="next">Next resource to compare with the initial.</param>
|
||||
/// <param name="connection">DistributedConnection is required to check locality.</param>
|
||||
/// <returns>Null, same, local, distributed or same class distributed.</returns>
|
||||
|
||||
public static ResourceComparisonResult Compare(IResource initial, IResource next, DistributedConnection connection)
|
||||
{
|
||||
if (next == null)
|
||||
return ResourceComparisonResult.Null;
|
||||
|
||||
if (next == initial)
|
||||
return ResourceComparisonResult.Same;
|
||||
|
||||
if (IsLocalResource(next, connection))
|
||||
return ResourceComparisonResult.Local;
|
||||
|
||||
if (initial == null)
|
||||
return ResourceComparisonResult.Distributed;
|
||||
|
||||
if (initial.Instance.Template.ClassId == next.Instance.Template.ClassId)
|
||||
return ResourceComparisonResult.DistributedSameClass;
|
||||
|
||||
return ResourceComparisonResult.Distributed;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose a resource
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource to compose.</param>
|
||||
/// <param name="connection">DistributedConnection is required to check locality.</param>
|
||||
/// <returns>Array of bytes in the network byte order.</returns>
|
||||
public static byte[] ComposeResource(IResource resource, DistributedConnection connection)
|
||||
{
|
||||
if (IsLocalResource(resource, connection))
|
||||
return DC.ToBytes((resource as DistributedResource).Id);
|
||||
else
|
||||
{
|
||||
return BinaryList.ToBytes(resource.Instance.Template.ClassId, resource.Instance.Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose an array of resources
|
||||
/// </summary>
|
||||
/// <param name="resources">Array of resources.</param>
|
||||
/// <param name="connection">DistributedConnection is required to check locality.</param>
|
||||
/// <param name="prependLength">If True, prepend the length of the output at the beginning.</param>
|
||||
/// <returns>Array of bytes in the network byte order.</returns>
|
||||
|
||||
public static byte[] ComposeResourceArray(IResource[] resources, DistributedConnection connection, bool prependLength = false)
|
||||
{
|
||||
if (resources == null || resources?.Length == 0)
|
||||
return new byte[0];
|
||||
|
||||
var rt = new BinaryList();
|
||||
var comparsion = Compare(null, resources[0], connection);
|
||||
|
||||
rt.Append((byte)comparsion);
|
||||
|
||||
if (comparsion == ResourceComparisonResult.Local)
|
||||
rt.Append((resources[0] as DistributedResource).Id);
|
||||
else if (comparsion == ResourceComparisonResult.Distributed)
|
||||
{
|
||||
rt.Append(resources[0].Instance.Template.ClassId);
|
||||
rt.Append(resources[0].Instance.Id);
|
||||
}
|
||||
|
||||
for (var i = 1; i < resources.Length; i++)
|
||||
{
|
||||
comparsion = Compare(resources[i - 1], resources[i], connection);
|
||||
rt.Append((byte)comparsion);
|
||||
if (comparsion == ResourceComparisonResult.Local)
|
||||
rt.Append((resources[0] as DistributedResource).Id);
|
||||
else if (comparsion == ResourceComparisonResult.Distributed)
|
||||
{
|
||||
rt.Append(resources[0].Instance.Template.ClassId);
|
||||
rt.Append(resources[0].Instance.Id);
|
||||
}
|
||||
else if (comparsion == ResourceComparisonResult.DistributedSameClass)
|
||||
{
|
||||
rt.Append(resources[0].Instance.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (prependLength)
|
||||
rt.Insert(0, rt.Length);
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an array of bytes into array of resources
|
||||
/// </summary>
|
||||
/// <param name="data">Array of bytes.</param>
|
||||
/// <param name="length">Number of bytes to parse.</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
|
||||
/// <returns>Array of resources.</returns>
|
||||
public static AsyncBag<IResource> ParseResourceArray(byte[] data, uint offset, uint length, DistributedConnection connection)
|
||||
{
|
||||
var reply = new AsyncBag<IResource>();
|
||||
if (length == 0)
|
||||
{
|
||||
reply.Seal();
|
||||
return reply;
|
||||
}
|
||||
|
||||
var end = offset + length;
|
||||
|
||||
//
|
||||
var result = (ResourceComparisonResult)data[offset++];
|
||||
|
||||
AsyncReply previous = null;
|
||||
Guid previousGuid = Guid.Empty;
|
||||
|
||||
if (result == ResourceComparisonResult.Null)
|
||||
previous = new AsyncReply<IResource>(null);
|
||||
else if (result == ResourceComparisonResult.Local)
|
||||
{
|
||||
previous = Warehouse.Get(data.GetUInt32(offset));
|
||||
offset += 4;
|
||||
}
|
||||
else if (result == ResourceComparisonResult.Distributed)
|
||||
{
|
||||
previousGuid = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
reply.Add(previous);
|
||||
|
||||
|
||||
while (offset < end)
|
||||
{
|
||||
result = (ResourceComparisonResult)data[offset++];
|
||||
|
||||
if (result == ResourceComparisonResult.Null)
|
||||
previous = new AsyncReply<IResource>(null);
|
||||
//else if (result == ResourceComparisonResult.Same)
|
||||
// reply.Add(previous);
|
||||
else if (result == ResourceComparisonResult.Local)
|
||||
{
|
||||
// overwrite previous
|
||||
previous = Warehouse.Get(data.GetUInt32(offset));
|
||||
offset += 4;
|
||||
}
|
||||
else if (result == ResourceComparisonResult.Distributed)
|
||||
{
|
||||
// overwrite previous
|
||||
previousGuid = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
|
||||
offset += 4;
|
||||
}
|
||||
else if (result == ResourceComparisonResult.DistributedSameClass)
|
||||
{
|
||||
// overwrite previous
|
||||
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
reply.Add(previous);
|
||||
}
|
||||
|
||||
reply.Seal();
|
||||
return reply;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose an array of variables
|
||||
/// </summary>
|
||||
/// <param name="array">Variables.</param>
|
||||
/// <param name="connection">DistributedConnection is required to check locality.</param>
|
||||
/// <param name="prependLength">If True, prepend the length as UInt32 at the beginning of the output.</param>
|
||||
/// <returns>Array of bytes in the network byte order.</returns>
|
||||
public static byte[] ComposeVarArray(object[] array, DistributedConnection connection, bool prependLength = false)
|
||||
{
|
||||
var rt = new List<byte>();
|
||||
|
||||
for (var i = 0; i < array.Length; i++)
|
||||
rt.AddRange(Compose(array[i], connection));
|
||||
if (prependLength)
|
||||
rt.InsertRange(0, DC.ToBytes(rt.Count));
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
public static AsyncBag<object> ParseVarArray(byte[] data, DistributedConnection connection)
|
||||
{
|
||||
return ParseVarArray(data, 0, (uint)data.Length, connection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an array of bytes into an array of varialbes.
|
||||
/// </summary>
|
||||
/// <param name="data">Array of bytes.</param>
|
||||
/// <param name="offset">Zero-indexed offset.</param>
|
||||
/// <param name="length">Number of bytes to parse.</param>
|
||||
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
|
||||
/// <returns>Array of variables.</returns>
|
||||
public static AsyncBag<object> ParseVarArray(byte[] data, uint offset, uint length, DistributedConnection connection)
|
||||
{
|
||||
var rt = new AsyncBag<object>();
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
uint cs;
|
||||
|
||||
rt.Add(Parse(data, offset, out cs, connection));
|
||||
|
||||
if (cs > 0)
|
||||
{
|
||||
offset += (uint)cs;
|
||||
length -= (uint)cs;
|
||||
}
|
||||
else
|
||||
throw new Exception("Error while parsing structured data");
|
||||
|
||||
}
|
||||
|
||||
rt.Seal();
|
||||
return rt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose a variable
|
||||
/// </summary>
|
||||
/// <param name="value">Value to compose.</param>
|
||||
/// <param name="connection">DistributedConnection is required to check locality.</param>
|
||||
/// <param name="prependType">If True, prepend the DataType at the beginning of the output.</param>
|
||||
/// <returns>Array of bytes in the network byte order.</returns>
|
||||
public static byte[] Compose(object value, DistributedConnection connection, bool prependType = true)
|
||||
{
|
||||
|
||||
var type = GetDataType(value, connection);
|
||||
var rt = new BinaryList();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DataType.Void:
|
||||
// nothing to do;
|
||||
break;
|
||||
|
||||
case DataType.String:
|
||||
var st = DC.ToBytes((string)value);
|
||||
rt.Append(st.Length, st);
|
||||
break;
|
||||
|
||||
case DataType.Resource:
|
||||
rt.Append((value as DistributedResource).Id);
|
||||
break;
|
||||
|
||||
case DataType.DistributedResource:
|
||||
//rt.Append((value as IResource).Instance.Template.ClassId, (value as IResource).Instance.Id);
|
||||
rt.Append((value as IResource).Instance.Id);
|
||||
|
||||
break;
|
||||
|
||||
case DataType.Structure:
|
||||
rt.Append(ComposeStructure((Structure)value, connection, true, true, true));
|
||||
break;
|
||||
|
||||
case DataType.VarArray:
|
||||
rt.Append(ComposeVarArray((object[])value, connection, true));
|
||||
break;
|
||||
|
||||
case DataType.ResourceArray:
|
||||
if (value is IResource[])
|
||||
rt.Append(ComposeResourceArray((IResource[])value, connection, true));
|
||||
else
|
||||
rt.Append(ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[])), connection, true));
|
||||
break;
|
||||
|
||||
case DataType.StructureArray:
|
||||
rt.Append(ComposeStructureArray((Structure[])value, connection, true));
|
||||
break;
|
||||
|
||||
default:
|
||||
rt.Append(value);
|
||||
if (type.IsArray())
|
||||
rt.Insert(0, rt.Length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (prependType)
|
||||
rt.Insert(0, (byte)type);
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a type implements an interface
|
||||
/// </summary>
|
||||
/// <param name="type">Sub-class type.</param>
|
||||
/// <param name="iface">Super-interface type.</param>
|
||||
/// <returns>True, if <paramref name="type"/> implements <paramref name="iface"/>.</returns>
|
||||
private static bool ImplementsInterface(Type type, Type iface)
|
||||
{
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
if (type.GetTypeInfo().GetInterfaces().Contains(iface))
|
||||
return true;
|
||||
type = type.GetTypeInfo().BaseType;
|
||||
#else
|
||||
if (type.GetInterfaces().Contains(iface))
|
||||
return true;
|
||||
type = type.BaseType;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a type inherits another type.
|
||||
/// </summary>
|
||||
/// <param name="childType">Child type.</param>
|
||||
/// <param name="parentType">Parent type.</param>
|
||||
/// <returns>True, if <paramref name="childType"/> inherits <paramref name="parentType"/>.</returns>
|
||||
private static bool HasParentType(Type childType, Type parentType)
|
||||
{
|
||||
while (childType != null)
|
||||
{
|
||||
if (childType == parentType)
|
||||
return true;
|
||||
#if NETSTANDARD1_5
|
||||
childType = childType.GetTypeInfo().BaseType;
|
||||
#else
|
||||
childType = childType.BaseType;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DataType of a given value.
|
||||
/// This function is needed to compose a value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to find its DataType.</param>
|
||||
/// <param name="connection">DistributedConnection is required to check locality of resources.</param>
|
||||
/// <returns>DataType.</returns>
|
||||
public static DataType GetDataType(object value, DistributedConnection connection)
|
||||
{
|
||||
if (value == null)
|
||||
return DataType.Void;
|
||||
|
||||
var t = value.GetType();
|
||||
|
||||
var isArray = t.IsArray;
|
||||
if (isArray)
|
||||
t = t.GetElementType();
|
||||
|
||||
DataType type;
|
||||
|
||||
if (t == typeof(bool))
|
||||
type = DataType.Bool;
|
||||
else if (t == typeof(char))
|
||||
type = DataType.Char;
|
||||
else if (t == typeof(byte))
|
||||
type = DataType.UInt8;
|
||||
else if (t == typeof(sbyte))
|
||||
type = DataType.Int8;
|
||||
else if (t == typeof(short))
|
||||
type = DataType.Int16;
|
||||
else if (t == typeof(ushort))
|
||||
type = DataType.UInt16;
|
||||
else if (t == typeof(int))
|
||||
type = DataType.Int32;
|
||||
else if (t == typeof(uint))
|
||||
type = DataType.UInt32;
|
||||
else if (t == typeof(long))
|
||||
type = DataType.Int64;
|
||||
else if (t == typeof(ulong))
|
||||
type = DataType.UInt64;
|
||||
else if (t == typeof(float))
|
||||
type = DataType.Float32;
|
||||
else if (t == typeof(double))
|
||||
type = DataType.Float64;
|
||||
else if (t == typeof(decimal))
|
||||
type = DataType.Decimal;
|
||||
else if (t == typeof(string))
|
||||
type = DataType.String;
|
||||
else if (t == typeof(DateTime))
|
||||
type = DataType.DateTime;
|
||||
else if (t == typeof(Structure))
|
||||
type = DataType.Structure;
|
||||
//else if (t == typeof(DistributedResource))
|
||||
// type = DataType.DistributedResource;
|
||||
else if (ImplementsInterface(t, typeof(IResource)))
|
||||
{
|
||||
if (isArray)
|
||||
return DataType.ResourceArray;
|
||||
else
|
||||
{
|
||||
return IsLocalResource((IResource)value, connection) ? DataType.Resource : DataType.DistributedResource;
|
||||
}
|
||||
}
|
||||
else
|
||||
return DataType.Void;
|
||||
|
||||
|
||||
if (isArray)
|
||||
return (DataType)((byte)type | 0x80);
|
||||
else
|
||||
return type;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
1069
Esiur/Data/DataConverter.cs
Normal file
1069
Esiur/Data/DataConverter.cs
Normal file
File diff suppressed because it is too large
Load Diff
95
Esiur/Data/DataType.cs
Normal file
95
Esiur/Data/DataType.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
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,
|
||||
//Stream,
|
||||
//Array = 0x80,
|
||||
VarArray = 0x80,
|
||||
BoolArray,
|
||||
UInt8Array,
|
||||
Int8Array,
|
||||
CharArray,
|
||||
Int16Array,
|
||||
UInt16Array,
|
||||
Int32Array,
|
||||
UInt32Array,
|
||||
Int64Array,
|
||||
UInt64Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
DecimalArray,
|
||||
DateTimeArray,
|
||||
ResourceArray,
|
||||
DistributedResourceArray,
|
||||
ResourceLinkArray,
|
||||
StringArray,
|
||||
StructureArray,
|
||||
NotModified = 0x7f,
|
||||
Unspecified = 0xff,
|
||||
}
|
||||
|
||||
public static class DataTypeExpansions
|
||||
{
|
||||
public static int Size(this DataType 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;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
204
Esiur/Data/KeyList.cs
Normal file
204
Esiur/Data/KeyList.cs
Normal file
@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
|
||||
public class KeyList<KT, T> : IEnumerable
|
||||
{
|
||||
private readonly object syncRoot = new object();
|
||||
private Dictionary<KT, T> dic;
|
||||
|
||||
public delegate void Modified(KT key, T oldValue, T newValue);
|
||||
public delegate void Added(T value);
|
||||
public delegate void Removed(KT key, T value);
|
||||
public delegate void Cleared();
|
||||
|
||||
public event Modified OnModified;
|
||||
public event Removed OnRemoved;
|
||||
public event Cleared OnCleared;
|
||||
public event Added OnAdd;
|
||||
|
||||
bool removableList;
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
return syncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
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 oldValue = dic[key];
|
||||
if (removableList)
|
||||
if (oldValue != null)
|
||||
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
|
||||
dic[key] = value;
|
||||
if (OnModified != null)
|
||||
OnModified(key, oldValue, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
dic.Add(key, value);
|
||||
|
||||
if (OnAdd != null)
|
||||
OnAdd(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
set
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator 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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
|
||||
#else
|
||||
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
|
||||
#endif
|
||||
|
||||
if (typeof(KT) == typeof(string))
|
||||
dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
|
||||
else
|
||||
dic = new Dictionary<KT, T>();
|
||||
}
|
||||
}
|
||||
}
|
13
Esiur/Data/NotModified.cs
Normal file
13
Esiur/Data/NotModified.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class NotModified
|
||||
{
|
||||
|
||||
}
|
||||
}
|
194
Esiur/Data/StringKeyList.cs
Normal file
194
Esiur/Data/StringKeyList.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
|
||||
if (OnModified != null)
|
||||
OnModified(Key, value);
|
||||
|
||||
|
||||
foreach (var kv in m_Variables)
|
||||
if (kv.Key.ToLower() == key)
|
||||
m_Variables.Remove(kv);
|
||||
|
||||
m_Variables.Add(new KeyValuePair<string, string>(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;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
83
Esiur/Data/Structure.cs
Normal file
83
Esiur/Data/Structure.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class Structure : IEnumerable<KeyValuePair<string, object>>
|
||||
{
|
||||
|
||||
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 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()
|
||||
{
|
||||
return dic.Keys.ToArray();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
64
Esiur/Engine/AsyncBag.cs
Normal file
64
Esiur/Engine/AsyncBag.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Engine
|
||||
{
|
||||
public class AsyncBag<T>:AsyncReply
|
||||
{
|
||||
//List<AsyncReply> replies = new List<AsyncReply>();
|
||||
//List<T> results = new List<T>();
|
||||
Dictionary<AsyncReply, T> results = new Dictionary<AsyncReply, T>();
|
||||
int count = 0;
|
||||
bool sealedBag = false;
|
||||
|
||||
public void Then(Action<T[]> callback)
|
||||
{
|
||||
base.Then(new Action<object>(o => callback((T[])o)));
|
||||
}
|
||||
|
||||
/*
|
||||
public void Trigger(T[] result)
|
||||
{
|
||||
Trigger((object)result);
|
||||
}
|
||||
*/
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
sealedBag = true;
|
||||
|
||||
if (results.Count == 0)
|
||||
Trigger(new T[0]);
|
||||
|
||||
for(var i = 0; i < results.Count; i++)
|
||||
//foreach(var reply in results.Keys)
|
||||
results.Keys.ElementAt(i).Then((r) => {
|
||||
results[results.Keys.ElementAt(i)] = (T)r;
|
||||
count++;
|
||||
if (count == results.Count)
|
||||
Trigger(results.Values.ToArray());
|
||||
});
|
||||
}
|
||||
|
||||
public void Add(AsyncReply reply)
|
||||
{
|
||||
if (!sealedBag)
|
||||
results.Add(reply, default(T));
|
||||
}
|
||||
|
||||
public AsyncBag()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
public AsyncBag(T[] result)
|
||||
{
|
||||
this.result = result;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
57
Esiur/Engine/AsyncQueue.cs
Normal file
57
Esiur/Engine/AsyncQueue.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Engine
|
||||
{
|
||||
public class AsyncQueue<T> : AsyncReply
|
||||
{
|
||||
List<AsyncReply<T>> list = new List<AsyncReply<T>>();
|
||||
//Action<T> callback;
|
||||
object queueLock = new object();
|
||||
|
||||
public void Then(Action<T> callback)
|
||||
{
|
||||
base.Then(new Action<object>(o => callback((T)o)));
|
||||
}
|
||||
|
||||
public void Add(AsyncReply<T> reply)
|
||||
{
|
||||
lock (queueLock)
|
||||
list.Add(reply);
|
||||
|
||||
resultReady = false;
|
||||
reply.Then(processQueue);
|
||||
}
|
||||
|
||||
public void Remove(AsyncReply<T> reply)
|
||||
{
|
||||
lock (queueLock)
|
||||
list.Remove(reply);
|
||||
processQueue(default(T));
|
||||
}
|
||||
|
||||
void processQueue(T o)
|
||||
{
|
||||
lock (queueLock)
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
if (list[i].Ready)
|
||||
{
|
||||
Trigger(list[i].Result);
|
||||
list.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
resultReady = (list.Count == 0);
|
||||
}
|
||||
|
||||
public AsyncQueue()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
75
Esiur/Engine/AsyncReply.cs
Normal file
75
Esiur/Engine/AsyncReply.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Engine
|
||||
{
|
||||
public class AsyncReply
|
||||
{
|
||||
protected List<Action<object>> callbacks = new List<Action<object>>();
|
||||
protected object result;
|
||||
object callbacksLock = new object();
|
||||
|
||||
//bool fired = false;
|
||||
protected bool resultReady = false;
|
||||
|
||||
public bool Ready
|
||||
{
|
||||
get { return resultReady; }
|
||||
}
|
||||
|
||||
public object Result
|
||||
{
|
||||
get { return result; }
|
||||
}
|
||||
|
||||
public void Then(Action<object> callback)
|
||||
{
|
||||
callbacks.Add(callback);
|
||||
|
||||
if (resultReady)
|
||||
callback(result);
|
||||
// Trigger(this.result);
|
||||
}
|
||||
|
||||
public void Trigger(object result)
|
||||
{
|
||||
//if (!fired)
|
||||
//{
|
||||
this.result = result;
|
||||
resultReady = true;
|
||||
|
||||
lock (callbacksLock)
|
||||
{
|
||||
foreach (var cb in callbacks)
|
||||
cb(result);
|
||||
//callbacks.Clear();
|
||||
}
|
||||
/*
|
||||
if (callback == null)
|
||||
{
|
||||
fireAtChance = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(result);
|
||||
fired = true;
|
||||
}
|
||||
*/
|
||||
//}
|
||||
}
|
||||
|
||||
public AsyncReply()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AsyncReply(object result)
|
||||
{
|
||||
resultReady = true;
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
}
|
36
Esiur/Engine/AsyncReplyGeneric.cs
Normal file
36
Esiur/Engine/AsyncReplyGeneric.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Engine
|
||||
{
|
||||
public class AsyncReply<T>: AsyncReply
|
||||
{
|
||||
public void Then(Action<T> callback)
|
||||
{
|
||||
base.Then(new Action<object>(o => callback((T)o)));
|
||||
}
|
||||
|
||||
public void Trigger(T result)
|
||||
{
|
||||
Trigger((object)result);
|
||||
}
|
||||
|
||||
public AsyncReply()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AsyncReply(T result)
|
||||
: base(result)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
16
Esiur/Engine/IDestructible.cs
Normal file
16
Esiur/Engine/IDestructible.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Engine
|
||||
{
|
||||
public delegate void DestroyedEvent(object sender);
|
||||
|
||||
public interface IDestructible
|
||||
{
|
||||
event DestroyedEvent OnDestroy;
|
||||
void Destroy();
|
||||
}
|
||||
}
|
15
Esiur/Engine/LogType.cs
Normal file
15
Esiur/Engine/LogType.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Engine
|
||||
{
|
||||
public enum LogType
|
||||
{
|
||||
Debug,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
}
|
32
Esiur/Esiur.csproj
Normal file
32
Esiur/Esiur.csproj
Normal file
@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.5</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<DefineConstants>TRACE;DEBUG;NETSTANDARD1_5</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Net\UDP\UDPServer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Net\DataLink\Sources\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Net\UDP\UDPServer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Diagnostics.StackTrace" Version="4.3.0" />
|
||||
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.Security" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
435
Esiur/Misc/Global.cs
Normal file
435
Esiur/Misc/Global.cs
Normal file
@ -0,0 +1,435 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Xml;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using Esiur.Data;
|
||||
using System.Collections.Generic;
|
||||
//using Esiur.Net.Packets;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Linq;
|
||||
using Esiur.Engine;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Esiur.Misc
|
||||
{
|
||||
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 LogEvent SystemLog;
|
||||
|
||||
static Random random = new Random();
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
if (SystemLog != null)
|
||||
SystemLog(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[random.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(
|
||||
//}
|
||||
}
|
||||
}
|
32
Esiur/Net/DataLink/PacketFilter.cs
Normal file
32
Esiur/Net/DataLink/PacketFilter.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Data;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.DataLink
|
||||
{
|
||||
public abstract class PacketFilter : IResource
|
||||
{
|
||||
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
|
||||
|
||||
public abstract bool Execute(Packet packet);
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
99
Esiur/Net/DataLink/PacketServer.cs
Normal file
99
Esiur/Net/DataLink/PacketServer.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Data;
|
||||
using System.Runtime.InteropServices;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.DataLink
|
||||
{
|
||||
public class PacketServer:IResource
|
||||
{
|
||||
List<PacketSource> sources = new List<PacketSource>();
|
||||
List<PacketFilter> filters = new List<PacketFilter>();
|
||||
|
||||
|
||||
[Storable]
|
||||
public string Mode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public List<PacketSource> Sources
|
||||
{
|
||||
get
|
||||
{
|
||||
return sources;
|
||||
}
|
||||
}
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
if (trigger == ResourceTrigger.Initialize)
|
||||
{
|
||||
|
||||
foreach (Instance instance in Instance.Children)
|
||||
{
|
||||
|
||||
if (instance.Resource is PacketFilter)
|
||||
{
|
||||
filters.Add(instance.Resource as PacketFilter);
|
||||
}
|
||||
else if (instance.Resource is PacketSource)
|
||||
{
|
||||
sources.Add(instance.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
Esiur/Net/DataLink/PacketSource.cs
Normal file
71
Esiur/Net/DataLink/PacketSource.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using Esiur.Net.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.DataLink
|
||||
{
|
||||
public abstract class PacketSource: IResource
|
||||
{
|
||||
public delegate void NewPacket(Packet Packet);
|
||||
public abstract event NewPacket OnNewPacket;
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public Instance Instance
|
||||
{
|
||||
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()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/*
|
||||
public virtual string TypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Raw";
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public abstract byte[] Address
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string DeviceId
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
346
Esiur/Net/HTTP/HTTPConnection.cs
Normal file
346
Esiur/Net/HTTP/HTTPConnection.cs
Normal file
@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Data;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Misc;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Esiur.Net.HTTP
|
||||
{
|
||||
//[Serializable]
|
||||
public class HTTPConnection : NetworkConnection
|
||||
{
|
||||
/*
|
||||
public enum SendOptions : int
|
||||
{
|
||||
AllCalculateLength,
|
||||
AllDontCalculateLength,
|
||||
SpecifiedHeadersOnly,
|
||||
DataOnly
|
||||
}
|
||||
*/
|
||||
|
||||
public HTTPConnection()
|
||||
{
|
||||
Response = new HTTPResponsePacket();
|
||||
variables = new KeyList<string, object>();
|
||||
}
|
||||
|
||||
public void SetParent(HTTPServer Parent)
|
||||
{
|
||||
Server = Parent;
|
||||
}
|
||||
|
||||
//public bool HeadersSent;
|
||||
|
||||
|
||||
private KeyList<string, object> variables;
|
||||
private bool Busy = false;
|
||||
private DateTime RequestTime = DateTime.MinValue;
|
||||
|
||||
public bool WSMode;
|
||||
private HTTPServer Server;
|
||||
public WebsocketPacket WSRequest;
|
||||
public HTTPRequestPacket Request;
|
||||
public HTTPResponsePacket Response;
|
||||
|
||||
HTTPSession session;
|
||||
|
||||
public KeyList<string, object> Variables
|
||||
{
|
||||
get
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool IsBusy()
|
||||
{
|
||||
return Busy;
|
||||
}
|
||||
|
||||
|
||||
internal long Parse(byte[] data)
|
||||
{
|
||||
if (WSMode)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HTTPRequestPacket rp = new HTTPRequestPacket();
|
||||
var pSize = rp.Parse(data, 0, (uint)data.Length);
|
||||
if (pSize > 0)
|
||||
{
|
||||
Request = rp;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return pSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public override void Send(string Response)
|
||||
{
|
||||
Send(Response, SendOptions.AllCalculateLength);
|
||||
}
|
||||
|
||||
public void Send(string Message, SendOptions Options)
|
||||
{
|
||||
|
||||
if (Response.Handled)
|
||||
return;
|
||||
|
||||
if (Response != null)
|
||||
Send(Encoding.Default.GetBytes(Response), Options);
|
||||
else
|
||||
Send((byte[])null, Options);
|
||||
}
|
||||
|
||||
public void Send(MemoryStream ms)
|
||||
{
|
||||
Send(ms.ToArray(), SendOptions.AllCalculateLength);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
// close the connection
|
||||
if (Request.Headers["connection"].ToLower() != "keep-alive" & Connected)
|
||||
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.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"];
|
||||
//Response.Headers["Origin"] = Request.Headers["Origin"];
|
||||
|
||||
Response.Number = HTTPResponsePacket.ResponseCode.HTTP_SWITCHING;
|
||||
Response.Text = "Switching Protocols";
|
||||
WSMode = true;
|
||||
|
||||
//Send((byte[])null, SendOptions.AllDontCalculateLength);
|
||||
Send();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public HTTPServer Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return Server;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Send(string data)
|
||||
{
|
||||
Response.Message = Encoding.UTF8.GetBytes(data);
|
||||
Send();
|
||||
}
|
||||
|
||||
public override void Send(byte[] message)
|
||||
{
|
||||
Response.Message = message;
|
||||
Send();
|
||||
}
|
||||
|
||||
public void Send(HTTPResponsePacket.ComposeOptions Options = HTTPResponsePacket.ComposeOptions.AllCalculateLength)
|
||||
{
|
||||
if (Response.Handled)
|
||||
return;
|
||||
|
||||
Busy = true;
|
||||
|
||||
try
|
||||
{
|
||||
Response.Compose(Options);
|
||||
base.Send(Response.Data);
|
||||
|
||||
// Refresh the current session
|
||||
if (session != null)
|
||||
session.Refresh();
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
Close();// Server.CloseClient(Connection);
|
||||
}
|
||||
finally { }
|
||||
}
|
||||
finally
|
||||
{
|
||||
Busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendFile(string filename)
|
||||
{
|
||||
if (Response.Handled == true)
|
||||
return;
|
||||
|
||||
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.HTTP_NOTFOUND;
|
||||
Send("File Not Found");//, SendOptions.AllCalculateLength);
|
||||
return;
|
||||
}
|
||||
|
||||
Busy = true;
|
||||
|
||||
System.DateTime FWD = File.GetLastWriteTime(filename);
|
||||
if (Request.Headers.ContainsKey("if-modified-since"))// != DateTime.Parse("12:00:00 AM"))
|
||||
{
|
||||
try
|
||||
{
|
||||
DateTime IMS = DateTime.Parse(Request.Headers["if-modified-since"]);
|
||||
if (FWD <= IMS)
|
||||
{
|
||||
Response.Number = HTTPResponsePacket.ResponseCode.HTTP_NOTMODIFIED;
|
||||
Response.Text = "Not Modified";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Response.Number == HTTPResponsePacket.ResponseCode.HTTP_NOTMODIFIED)
|
||||
{
|
||||
Send((byte[])null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fri, 30 Oct 2007 14:19:41 GMT
|
||||
Response.Headers["Last-Modified"] = FWD.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss");
|
||||
FileInfo fi = new FileInfo(filename);
|
||||
Response.Headers["Content-Length"] = fi.Length.ToString();
|
||||
Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly);
|
||||
using (var fs = new FileStream(filename, FileMode.Open))
|
||||
{
|
||||
var buffer = new byte[5000];
|
||||
var offset = 0;
|
||||
while (offset < fs.Length)
|
||||
{
|
||||
var n = fs.Read(buffer, offset, buffer.Length);
|
||||
offset += n;
|
||||
base.Send(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Busy = false;
|
||||
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Busy = false;
|
||||
|
||||
try
|
||||
{
|
||||
Close();
|
||||
}
|
||||
finally { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
Esiur/Net/HTTP/HTTPFilter.cs
Normal file
58
Esiur/Net/HTTP/HTTPFilter.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.HTTP
|
||||
{
|
||||
|
||||
public abstract class HTTPFilter : IResource
|
||||
{
|
||||
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 bool Execute(HTTPConnection sender);
|
||||
|
||||
public virtual void ClientConnected(HTTPConnection HTTP)
|
||||
{
|
||||
//return false;
|
||||
}
|
||||
|
||||
public virtual void ClientDisconnected(HTTPConnection HTTP)
|
||||
{
|
||||
//return false;
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
416
Esiur/Net/HTTP/HTTPServer.cs
Normal file
416
Esiur/Net/HTTP/HTTPServer.cs
Normal file
@ -0,0 +1,416 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net.Packets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.HTTP
|
||||
{
|
||||
public class HTTPServer : NetworkServer<HTTPConnection>, IResource
|
||||
{
|
||||
Dictionary<string, HTTPSession> sessions= new Dictionary<string, HTTPSession>();
|
||||
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
string ip
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Storable]
|
||||
ushort port
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
uint timeout
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
uint clock
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
uint maxPost
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
bool ssl
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
string certificate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//public override void ClientConnected(TClient Sender)
|
||||
//{
|
||||
//}
|
||||
/*
|
||||
public DStringDictionary Configurations
|
||||
{
|
||||
get { return config; }
|
||||
}
|
||||
*/
|
||||
|
||||
public enum ResponseCodes : int
|
||||
{
|
||||
HTTP_OK = 200,
|
||||
HTTP_NOTFOUND = 404,
|
||||
HTTP_SERVERERROR = 500,
|
||||
HTTP_MOVED = 301,
|
||||
HTTP_NOTMODIFIED = 304,
|
||||
HTTP_REDIRECT = 307
|
||||
}
|
||||
|
||||
|
||||
public HTTPSession CreateSession(string id, int timeout)
|
||||
{
|
||||
var s = new HTTPSession();
|
||||
|
||||
s.Set(id, timeout);
|
||||
|
||||
|
||||
sessions.Add(id, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
protected override void SessionModified(NetworkSession session, string key, object oldValue, object newValue)
|
||||
{
|
||||
foreach (var instance in Instance.Children)
|
||||
{
|
||||
var f = (HTTPFilter)instance;
|
||||
f.SessionModified(session as HTTPSession, key, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//public override object InitializeLifetimeService()
|
||||
//{
|
||||
// return null;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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 sender)
|
||||
{
|
||||
Console.WriteLine("OUT: " + this.Connections.Count);
|
||||
|
||||
foreach (IResource resource in Instance.Children)
|
||||
{
|
||||
if (resource is HTTPFilter)
|
||||
{
|
||||
(resource as HTTPFilter).ClientDisconnected(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void DataReceived(HTTPConnection sender, NetworkBuffer data)
|
||||
{
|
||||
//Console.WriteLine(Data);
|
||||
// Initialize a new session
|
||||
//HTTPConnection HTTP = (HTTPConnection)sender.ExtraObject;
|
||||
|
||||
//string Data = System.Text.Encoding.Default.GetString(ReceivedData);
|
||||
|
||||
byte[] msg = data.Read();
|
||||
|
||||
|
||||
|
||||
|
||||
var BL = sender.Parse(msg);
|
||||
|
||||
if (BL == 0)
|
||||
{
|
||||
if (sender.Request.Method == HTTPRequestPacket.HTTPMethod.UNKNOWN)
|
||||
{
|
||||
sender.Close();
|
||||
return;
|
||||
}
|
||||
if (sender.Request.URL == "")
|
||||
{
|
||||
sender.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 > maxPost)
|
||||
{
|
||||
sender.Send(
|
||||
"<html><body>POST method content is larger than "
|
||||
+ maxPost
|
||||
+ " bytes.</body></html>");
|
||||
|
||||
sender.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
data.HoldFor(msg, (uint)(msg.Length + BL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (BL < 0) // for security
|
||||
{
|
||||
sender.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (sender.IsWebsocketRequest() & !sender.WSMode)
|
||||
{
|
||||
sender.Upgrade();
|
||||
//return;
|
||||
}
|
||||
|
||||
|
||||
//return;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (IResource resource in Instance.Children)
|
||||
{
|
||||
if (resource is HTTPFilter)
|
||||
{
|
||||
if ((resource as HTTPFilter).Execute(sender))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sender.Send("Bad Request");
|
||||
sender.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);
|
||||
sender.Send(Return500(ex.Message));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private string Return500(string sMessage)
|
||||
{
|
||||
string sTMP = null;
|
||||
sTMP = "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD><br>\r\n";
|
||||
sTMP = sTMP + "<BODY BGCOLOR=" + (char)(34) + "#FFFFFF" + (char)(34) + " Text=" + (char)(34) + "#000000" + (char)(34) + " LINK=" + (char)(34) + "#0000FF" + (char)(34) + " VLINK=" + (char)(34) + "#000080" + (char)(34) + " ALINK=" + (char)(34) + "#008000" + (char)(34) + "><br>\r\n";
|
||||
sTMP = sTMP + "<b>500</b> Sorry - Internal Server Error<br>" + sMessage + "\r\n";
|
||||
sTMP = sTMP + "</BODY><br>\r\n";
|
||||
sTMP = sTMP + "</HTML><br>\r\n";
|
||||
return sTMP;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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 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;
|
||||
|
||||
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,
|
||||
timeout,
|
||||
clock);
|
||||
}
|
||||
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 ClientConnected(HTTPConnection sender)
|
||||
{
|
||||
//sender.SessionModified += SessionModified;
|
||||
//sender.SessionEnded += SessionExpired;
|
||||
sender.SetParent(this);
|
||||
|
||||
Console.WriteLine("IN: " + this.Connections.Count);
|
||||
|
||||
foreach (IResource resource in Instance.Children)
|
||||
{
|
||||
if (resource is HTTPFilter)
|
||||
{
|
||||
(resource as HTTPFilter).ClientConnected(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/*
|
||||
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();
|
||||
//}
|
||||
}
|
||||
}
|
106
Esiur/Net/HTTP/HTTPSession.cs
Normal file
106
Esiur/Net/HTTP/HTTPSession.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Net.HTTP
|
||||
{
|
||||
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
|
||||
{
|
||||
get { return variables; }
|
||||
}
|
||||
|
||||
public HTTPSession()
|
||||
{
|
||||
variables = new KeyList<string, object>();
|
||||
variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
|
||||
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)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
Esiur/Net/HTTP/IIPoWS.cs
Normal file
51
Esiur/Net/HTTP/IIPoWS.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Net.IIP;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Net.HTTP
|
||||
{
|
||||
public class IIPoWS: HTTPFilter
|
||||
{
|
||||
public override bool Execute(HTTPConnection sender)
|
||||
{
|
||||
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();
|
||||
var wsSocket = new WSSocket(tcpSocket);
|
||||
httpServer.Connections.Remove(sender);
|
||||
var iipConnection = new DistributedConnection();
|
||||
iipConnection.Server = iipServer;
|
||||
iipConnection.Assign(wsSocket);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
514
Esiur/Net/IIP/DistributedConnection.cs
Normal file
514
Esiur/Net/IIP/DistributedConnection.cs
Normal file
@ -0,0 +1,514 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Security.Authority;
|
||||
using Esiur.Resource.Template;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
public partial class DistributedConnection : NetworkConnection, IStore
|
||||
{
|
||||
public delegate void ReadyEvent(DistributedConnection sender);
|
||||
public delegate void ErrorEvent(DistributedConnection sender, byte errorCode, string errorMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Ready event is raised when the connection is fully established.
|
||||
/// </summary>
|
||||
public event ReadyEvent OnReady;
|
||||
|
||||
/// <summary>
|
||||
/// Error event
|
||||
/// </summary>
|
||||
public event ErrorEvent OnError;
|
||||
|
||||
IIPPacket packet = new IIPPacket();
|
||||
IIPAuthPacket authPacket = new IIPAuthPacket();
|
||||
|
||||
byte[] sessionId;
|
||||
AuthenticationType hostType;
|
||||
string domain;
|
||||
string localUsername, remoteUsername;
|
||||
byte[] localPassword;
|
||||
|
||||
byte[] localNonce, remoteNonce;
|
||||
|
||||
bool ready, readyToEstablish;
|
||||
|
||||
DateTime loginDate;
|
||||
KeyList<string, object> variables = new KeyList<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Local username to authenticate ourselves.
|
||||
/// </summary>
|
||||
public string LocalUsername { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Peer's username.
|
||||
/// </summary>
|
||||
public string RemoteUsername { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Working domain.
|
||||
/// </summary>
|
||||
public string Domain { get { return domain; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Distributed server responsible for this connection, usually for incoming connections.
|
||||
/// </summary>
|
||||
public DistributedServer Server
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send data to the other end as parameters
|
||||
/// </summary>
|
||||
/// <param name="values">Values will be converted to bytes then sent.</param>
|
||||
internal void SendParams(params object[] values)
|
||||
{
|
||||
var ar = BinaryList.ToBytes(values);
|
||||
Send(ar);
|
||||
|
||||
//StackTrace stackTrace = new StackTrace(;
|
||||
|
||||
// Get calling method name
|
||||
|
||||
//Console.WriteLine("TX " + hostType + " " + ar.Length + " " + stackTrace.GetFrame(1).GetMethod().ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send raw data through the connection.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to send.</param>
|
||||
public override void Send(byte[] data)
|
||||
{
|
||||
//Console.WriteLine("Client: {0}", Data.Length);
|
||||
|
||||
Global.Counters["IIP Sent Packets"]++;
|
||||
base.Send(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KeyList to store user variables related to this connection.
|
||||
/// </summary>
|
||||
public KeyList<string, object> Variables
|
||||
{
|
||||
get
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IResource interface.
|
||||
/// </summary>
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign a socket to the connection.
|
||||
/// </summary>
|
||||
/// <param name="socket">Any socket that implements ISocket.</param>
|
||||
public override void Assign(ISocket socket)
|
||||
{
|
||||
base.Assign(socket);
|
||||
|
||||
|
||||
if (hostType == AuthenticationType.Client)
|
||||
{
|
||||
// declare (Credentials -> No Auth, No Enctypt)
|
||||
var un = DC.ToBytes(localUsername);
|
||||
var dmn = DC.ToBytes(domain);
|
||||
|
||||
if (socket.State == SocketState.Established)
|
||||
{
|
||||
SendParams((byte)0x60, (byte)dmn.Length, dmn, localNonce, (byte)un.Length, un);
|
||||
}
|
||||
else
|
||||
{
|
||||
socket.OnConnect += () =>
|
||||
{ // declare (Credentials -> No Auth, No Enctypt)
|
||||
SendParams((byte)0x60, (byte)dmn.Length, dmn, localNonce, (byte)un.Length, un);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a new distributed connection.
|
||||
/// </summary>
|
||||
/// <param name="socket">Socket to transfer data through.</param>
|
||||
/// <param name="domain">Working domain.</param>
|
||||
/// <param name="username">Username.</param>
|
||||
/// <param name="password">Password.</param>
|
||||
public DistributedConnection(ISocket socket, string domain, string username, string password)
|
||||
{
|
||||
//Instance.Name = Global.GenerateCode(12);
|
||||
this.hostType = AuthenticationType.Client;
|
||||
this.domain = domain;
|
||||
this.localUsername = username;
|
||||
this.localPassword = DC.ToBytes(password);
|
||||
|
||||
init();
|
||||
|
||||
Assign(socket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of a distributed connection
|
||||
/// </summary>
|
||||
public DistributedConnection()
|
||||
{
|
||||
//myId = Global.GenerateCode(12);
|
||||
// localParams.Host = DistributedParameters.HostType.Host;
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string Link(IResource resource)
|
||||
{
|
||||
if (resource is DistributedConnection)
|
||||
{
|
||||
var r = resource as DistributedResource;
|
||||
if (r.Instance.Store == this)
|
||||
return this.Instance.Name + "/" + r.Id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
void init()
|
||||
{
|
||||
queue.Then((x) =>
|
||||
{
|
||||
if (x.Type == DistributedResourceQueueItem.DistributedResourceQueueItemType.Event)
|
||||
x.Resource._EmitEventByIndex(x.Index, (object[])x.Value);
|
||||
else
|
||||
x.Resource.UpdatePropertyByIndex(x.Index, x.Value);
|
||||
});
|
||||
|
||||
var r = new Random();
|
||||
localNonce = new byte[32];
|
||||
r.NextBytes(localNonce);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void DataReceived(NetworkBuffer data)
|
||||
{
|
||||
// Console.WriteLine("DR " + hostType + " " + data.Available + " " + RemoteEndPoint.ToString());
|
||||
var msg = data.Read();
|
||||
uint offset = 0;
|
||||
uint ends = (uint)msg.Length;
|
||||
while (offset < ends)
|
||||
{
|
||||
|
||||
if (ready)
|
||||
{
|
||||
var rt = packet.Parse(msg, offset, ends);
|
||||
if (rt <= 0)
|
||||
{
|
||||
data.HoldFor(msg, offset, ends - offset, (uint)(-rt));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += (uint)rt;
|
||||
|
||||
if (packet.Command == IIPPacket.IIPPacketCommand.Event)
|
||||
{
|
||||
switch (packet.Event)
|
||||
{
|
||||
case IIPPacket.IIPPacketEvent.ResourceReassigned:
|
||||
IIPEventResourceReassigned(packet.ResourceId, packet.NewResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketEvent.ResourceDestroyed:
|
||||
IIPEventResourceDestroyed(packet.ResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketEvent.PropertyUpdated:
|
||||
IIPEventPropertyUpdated(packet.ResourceId, packet.MethodIndex, packet.Content);
|
||||
break;
|
||||
case IIPPacket.IIPPacketEvent.EventOccured:
|
||||
IIPEventEventOccured(packet.ResourceId, packet.MethodIndex, packet.Content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (packet.Command == IIPPacket.IIPPacketCommand.Request)
|
||||
{
|
||||
switch (packet.Action)
|
||||
{
|
||||
case IIPPacket.IIPPacketAction.AttachResource:
|
||||
IIPRequestAttachResource(packet.CallbackId, packet.ResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.ReattachResource:
|
||||
IIPRequestReattachResource(packet.CallbackId, packet.ResourceId, packet.ResourceAge);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.DetachResource:
|
||||
IIPRequestDetachResource(packet.CallbackId, packet.ResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.CreateResource:
|
||||
IIPRequestCreateResource(packet.CallbackId, packet.ClassName);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.DeleteResource:
|
||||
IIPRequestDeleteResource(packet.CallbackId, packet.ResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromClassName:
|
||||
IIPRequestTemplateFromClassName(packet.CallbackId, packet.ClassName);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromClassId:
|
||||
IIPRequestTemplateFromClassId(packet.CallbackId, packet.ClassId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromResourceLink:
|
||||
IIPRequestTemplateFromResourceLink(packet.CallbackId, packet.ResourceLink);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromResourceId:
|
||||
IIPRequestTemplateFromResourceId(packet.CallbackId, packet.ResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.ResourceIdFromResourceLink:
|
||||
IIPRequestResourceIdFromResourceLink(packet.CallbackId, packet.ResourceLink);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.InvokeFunction:
|
||||
IIPRequestInvokeFunction(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.GetProperty:
|
||||
IIPRequestGetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.GetPropertyIfModified:
|
||||
IIPRequestGetPropertyIfModifiedSince(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.ResourceAge);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.SetProperty:
|
||||
IIPRequestSetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (packet.Command == IIPPacket.IIPPacketCommand.Reply)
|
||||
{
|
||||
switch (packet.Action)
|
||||
{
|
||||
case IIPPacket.IIPPacketAction.AttachResource:
|
||||
IIPReply(packet.CallbackId, packet.ClassId, packet.ResourceAge, packet.ResourceLink, packet.Content);
|
||||
|
||||
//IIPReplyAttachResource(packet.CallbackId, packet.ResourceAge, Codec.ParseValues(packet.Content));
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.ReattachResource:
|
||||
//IIPReplyReattachResource(packet.CallbackId, packet.ResourceAge, Codec.ParseValues(packet.Content));
|
||||
IIPReply(packet.CallbackId, packet.ResourceAge, packet.Content);
|
||||
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.DetachResource:
|
||||
//IIPReplyDetachResource(packet.CallbackId);
|
||||
IIPReply(packet.CallbackId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.CreateResource:
|
||||
//IIPReplyCreateResource(packet.CallbackId, packet.ClassId, packet.ResourceId);
|
||||
IIPReply(packet.CallbackId, packet.ClassId, packet.ResourceId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.DeleteResource:
|
||||
//IIPReplyDeleteResource(packet.CallbackId);
|
||||
IIPReply(packet.CallbackId);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromClassName:
|
||||
//IIPReplyTemplateFromClassName(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromClassId:
|
||||
//IIPReplyTemplateFromClassId(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromResourceLink:
|
||||
//IIPReplyTemplateFromResourceLink(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.TemplateFromResourceId:
|
||||
//IIPReplyTemplateFromResourceId(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.ResourceIdFromResourceLink:
|
||||
//IIPReplyResourceIdFromResourceLink(packet.CallbackId, packet.ClassId, packet.ResourceId, packet.ResourceAge);
|
||||
IIPReply(packet.CallbackId, packet.ClassId, packet.ResourceId, packet.ResourceAge);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.InvokeFunction:
|
||||
//IIPReplyInvokeFunction(packet.CallbackId, Codec.Parse(packet.Content, 0));
|
||||
IIPReply(packet.CallbackId, packet.Content);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.GetProperty:
|
||||
//IIPReplyGetProperty(packet.CallbackId, Codec.Parse(packet.Content, 0));
|
||||
IIPReply(packet.CallbackId, packet.Content);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.GetPropertyIfModified:
|
||||
//IIPReplyGetPropertyIfModifiedSince(packet.CallbackId, Codec.Parse(packet.Content, 0));
|
||||
IIPReply(packet.CallbackId, packet.Content);
|
||||
break;
|
||||
case IIPPacket.IIPPacketAction.SetProperty:
|
||||
//IIPReplySetProperty(packet.CallbackId);
|
||||
IIPReply(packet.CallbackId);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var rt = authPacket.Parse(msg, offset, ends);
|
||||
|
||||
Console.WriteLine(hostType.ToString() + " " + offset + " " + ends + " " + rt + " " + authPacket.ToString());
|
||||
|
||||
if (rt <= 0)
|
||||
{
|
||||
data.HoldFor(msg, ends + (uint)(-rt));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += (uint)rt;
|
||||
|
||||
if (hostType == AuthenticationType.Host)
|
||||
{
|
||||
if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Declare)
|
||||
{
|
||||
if (authPacket.RemoteMethod == IIPAuthPacket.IIPAuthPacketMethod.Credentials && authPacket.LocalMethod == IIPAuthPacket.IIPAuthPacketMethod.None)
|
||||
{
|
||||
remoteUsername = authPacket.RemoteUsername;
|
||||
remoteNonce = authPacket.RemoteNonce;
|
||||
domain = authPacket.Domain;
|
||||
SendParams((byte)0xa0, localNonce);
|
||||
}
|
||||
}
|
||||
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Action)
|
||||
{
|
||||
if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.AuthenticateHash)
|
||||
{
|
||||
var remoteHash = authPacket.Hash;
|
||||
|
||||
Server.Membership.GetPassword(remoteUsername, domain).Then((pw) =>
|
||||
{
|
||||
|
||||
|
||||
if (pw != null)
|
||||
{
|
||||
var hashFunc = SHA256.Create();
|
||||
var hash = hashFunc.ComputeHash(BinaryList.ToBytes(pw, remoteNonce, localNonce));
|
||||
if (hash.SequenceEqual(remoteHash))
|
||||
{
|
||||
// send our hash
|
||||
var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localNonce, remoteNonce, pw));
|
||||
|
||||
SendParams((byte)0, localHash);
|
||||
|
||||
readyToEstablish = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Incorrect password");
|
||||
SendParams((byte)0xc0, (byte)1, (ushort)5, DC.ToBytes("Error"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.NewConnection)
|
||||
{
|
||||
if (readyToEstablish)
|
||||
{
|
||||
var r = new Random();
|
||||
sessionId = new byte[32];
|
||||
r.NextBytes(sessionId);
|
||||
SendParams((byte)0x28, sessionId);
|
||||
ready = true;
|
||||
OnReady?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hostType == AuthenticationType.Client)
|
||||
{
|
||||
if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Acknowledge)
|
||||
{
|
||||
remoteNonce = authPacket.RemoteNonce;
|
||||
|
||||
// send our hash
|
||||
var hashFunc = SHA256.Create();
|
||||
var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localPassword, localNonce, remoteNonce));
|
||||
|
||||
SendParams((byte)0, localHash);
|
||||
}
|
||||
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Action)
|
||||
{
|
||||
if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.AuthenticateHash)
|
||||
{
|
||||
// check if the server knows my password
|
||||
var hashFunc = SHA256.Create();
|
||||
var remoteHash = hashFunc.ComputeHash(BinaryList.ToBytes(remoteNonce, localNonce, localPassword));
|
||||
|
||||
if (remoteHash.SequenceEqual(authPacket.Hash))
|
||||
{
|
||||
// send establish request
|
||||
SendParams((byte)0x20, (ushort)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendParams((byte)0xc0, 1, (ushort)5, DC.ToBytes("Error"));
|
||||
}
|
||||
}
|
||||
else if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.ConnectionEstablished)
|
||||
{
|
||||
sessionId = authPacket.SessionId;
|
||||
ready = true;
|
||||
OnReady?.Invoke(this);
|
||||
}
|
||||
}
|
||||
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Error)
|
||||
{
|
||||
OnError?.Invoke(this, authPacket.ErrorCode, authPacket.ErrorMessage);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource interface
|
||||
/// </summary>
|
||||
/// <param name="trigger">Resource trigger.</param>
|
||||
/// <returns></returns>
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return new AsyncReply<bool>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store interface.
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource.</param>
|
||||
/// <returns></returns>
|
||||
public bool Put(IResource resource)
|
||||
{
|
||||
resources.Add(Convert.ToUInt32(resource.Instance.Name), (DistributedResource)resource);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
802
Esiur/Net/IIP/DistributedConnectionProtocol.cs
Normal file
802
Esiur/Net/IIP/DistributedConnectionProtocol.cs
Normal file
@ -0,0 +1,802 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Resource.Template;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
partial class DistributedConnection
|
||||
{
|
||||
KeyList<uint, DistributedResource> resources = new KeyList<uint, DistributedResource>();
|
||||
KeyList<uint, AsyncReply<DistributedResource>> resourceRequests = new KeyList<uint, AsyncReply<DistributedResource>>();
|
||||
KeyList<Guid, AsyncReply<ResourceTemplate>> templateRequests = new KeyList<Guid, AsyncReply<ResourceTemplate>>();
|
||||
|
||||
|
||||
KeyList<string, AsyncReply<IResource>> pathRequests = new KeyList<string, AsyncReply<IResource>>();
|
||||
|
||||
Dictionary<Guid, ResourceTemplate> templates = new Dictionary<Guid, ResourceTemplate>();
|
||||
|
||||
KeyList<uint, AsyncReply<object[]>> requests = new KeyList<uint, AsyncReply<object[]>>();
|
||||
|
||||
uint callbackCounter = 0;
|
||||
|
||||
AsyncQueue<DistributedResourceQueueItem> queue = new AsyncQueue<DistributedResourceQueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Send IIP request.
|
||||
/// </summary>
|
||||
/// <param name="action">Packet action.</param>
|
||||
/// <param name="args">Arguments to send.</param>
|
||||
/// <returns></returns>
|
||||
internal AsyncReply<object[]> SendRequest(IIPPacket.IIPPacketAction action, params object[] args)
|
||||
{
|
||||
var reply = new AsyncReply<object[]>();
|
||||
callbackCounter++;
|
||||
var bl = new BinaryList((byte)(0x40 | (byte)action), callbackCounter);
|
||||
bl.AddRange(args);
|
||||
Send(bl.ToArray());
|
||||
requests.Add(callbackCounter, reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
void IIPReply(uint callbackId, params object[] results)
|
||||
{
|
||||
var req = requests.Take(callbackId);
|
||||
req?.Trigger(results);
|
||||
}
|
||||
|
||||
void IIPEventResourceReassigned(uint resourceId, uint newResourceId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IIPEventResourceDestroyed(uint resourceId)
|
||||
{
|
||||
if (resources.Contains(resourceId))
|
||||
{
|
||||
var r = resources[resourceId];
|
||||
resources.Remove(resourceId);
|
||||
r.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void IIPEventPropertyUpdated(uint resourceId, byte index, byte[] content)
|
||||
{
|
||||
if (resources.Contains(resourceId))
|
||||
{
|
||||
// push to the queue to gaurantee serialization
|
||||
var reply = new AsyncReply<DistributedResourceQueueItem>();
|
||||
queue.Add(reply);
|
||||
|
||||
var r = resources[resourceId];
|
||||
Codec.Parse(content, 0, this).Then((arguments) =>
|
||||
{
|
||||
var pt = r.Template.GetPropertyTemplate(index);
|
||||
if (pt != null)
|
||||
{
|
||||
reply.Trigger(new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery, arguments, index));
|
||||
}
|
||||
else
|
||||
{ // ft found, fi not found, this should never happen
|
||||
queue.Remove(reply);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IIPEventEventOccured(uint resourceId, byte index, byte[] content)
|
||||
{
|
||||
if (resources.Contains(resourceId))
|
||||
{
|
||||
// push to the queue to gaurantee serialization
|
||||
var reply = new AsyncReply<DistributedResourceQueueItem>();
|
||||
var r = resources[resourceId];
|
||||
|
||||
queue.Add(reply);
|
||||
|
||||
Codec.ParseVarArray(content, this).Then((arguments) =>
|
||||
{
|
||||
var et = r.Template.GetEventTemplate(index);
|
||||
if (et != null)
|
||||
{
|
||||
reply.Trigger(new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index));
|
||||
}
|
||||
else
|
||||
{ // ft found, fi not found, this should never happen
|
||||
queue.Remove(reply);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void IIPRequestAttachResource(uint callback, uint resourceId)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((res) =>
|
||||
{
|
||||
if (res != null)
|
||||
{
|
||||
var r = res as IResource;
|
||||
r.Instance.ResourceEventOccured += Instance_EventOccured;
|
||||
r.Instance.ResourceModified += Instance_PropertyModified;
|
||||
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
|
||||
|
||||
var link = DC.ToBytes(r.Instance.Link);
|
||||
|
||||
// reply ok
|
||||
SendParams((byte)0x80, callback, r.Instance.Template.ClassId, r.Instance.Age, (ushort)link.Length, link, Codec.ComposeVarArray(r.Instance.Serialize(), this, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
//SendParams(0x80, r.Instance.Id, r.Instance.Age, r.Instance.Serialize(false, this));
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestReattachResource(uint callback, uint resourceId, uint resourceAge)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((res) =>
|
||||
{
|
||||
if (res != null)
|
||||
{
|
||||
var r = res as IResource;
|
||||
r.Instance.ResourceEventOccured += Instance_EventOccured;
|
||||
r.Instance.ResourceModified += Instance_PropertyModified;
|
||||
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
|
||||
// reply ok
|
||||
SendParams((byte)0x81, callback, r.Instance.Age, Codec.ComposeVarArray(r.Instance.Serialize(), this, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestDetachResource(uint callback, uint resourceId)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((res) =>
|
||||
{
|
||||
if (res != null)
|
||||
{
|
||||
var r = res as IResource;
|
||||
r.Instance.ResourceEventOccured -= Instance_EventOccured;
|
||||
r.Instance.ResourceModified -= Instance_PropertyModified;
|
||||
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
|
||||
// reply ok
|
||||
SendParams((byte)0x82, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestCreateResource(uint callback, string className)
|
||||
{
|
||||
// not implemented
|
||||
}
|
||||
|
||||
void IIPRequestDeleteResource(uint callback, uint resourceId)
|
||||
{
|
||||
// not implemented
|
||||
|
||||
}
|
||||
|
||||
void IIPRequestTemplateFromClassName(uint callback, string className)
|
||||
{
|
||||
Warehouse.GetTemplate(className).Then((t) =>
|
||||
{
|
||||
if (t != null)
|
||||
SendParams((byte)0x88, callback, t.Content);
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestTemplateFromClassId(uint callback, Guid classId)
|
||||
{
|
||||
Warehouse.GetTemplate(classId).Then((t) =>
|
||||
{
|
||||
if (t != null)
|
||||
SendParams((byte)0x89, callback, (uint)t.Content.Length, t.Content);
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestTemplateFromResourceLink(uint callback, string resourceLink)
|
||||
{
|
||||
Warehouse.GetTemplate(resourceLink).Then((t) =>
|
||||
{
|
||||
if (t != null)
|
||||
SendParams((byte)0x8A, callback, t.Content);
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestTemplateFromResourceId(uint callback, uint resourceId)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((r) =>
|
||||
{
|
||||
if (r != null)
|
||||
SendParams((byte)0x8B, callback, r.Instance.Template.Content);
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestResourceIdFromResourceLink(uint callback, string resourceLink)
|
||||
{
|
||||
Warehouse.Get(resourceLink).Then((r) =>
|
||||
{
|
||||
if (r != null)
|
||||
SendParams((byte)0x8C, callback, r.Instance.Template.ClassId, r.Instance.Id, r.Instance.Age);
|
||||
else
|
||||
{
|
||||
// reply failed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestInvokeFunction(uint callback, uint resourceId, byte index, byte[] content)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((r) =>
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
Codec.ParseVarArray(content, this).Then(async (arguments) =>
|
||||
{
|
||||
var ft = r.Instance.Template.GetFunctionTemplate(index);
|
||||
if (ft != null)
|
||||
{
|
||||
if (r is DistributedResource)
|
||||
{
|
||||
var rt = (r as DistributedResource)._Invoke(index, arguments);
|
||||
if (rt != null)
|
||||
{
|
||||
rt.Then(res =>
|
||||
{
|
||||
SendParams((byte)0x90, callback, Codec.Compose(res, this));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// function not found on a distributed object
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var fi = r.GetType().GetTypeInfo().GetMethod(ft.Name);
|
||||
#else
|
||||
var fi = r.GetType().GetMethod(ft.Name);
|
||||
#endif
|
||||
|
||||
if (fi != null)
|
||||
{
|
||||
// cast shit
|
||||
ParameterInfo[] pi = fi.GetParameters();
|
||||
object[] args = null;
|
||||
|
||||
if (pi.Length > 0)
|
||||
{
|
||||
int argsCount = pi.Length;
|
||||
args = new object[pi.Length];
|
||||
|
||||
if (pi[pi.Length - 1].ParameterType == typeof(DistributedConnection))
|
||||
{
|
||||
args[--argsCount] = this;
|
||||
}
|
||||
|
||||
if (arguments != null)
|
||||
{
|
||||
for (int i = 0; i < argsCount && i < arguments.Length; i++)
|
||||
{
|
||||
args[i] = DC.CastConvert(arguments[i], pi[i].ParameterType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rt = fi.Invoke(r, args);
|
||||
|
||||
if (rt is Task)
|
||||
{
|
||||
var t = (Task)rt;
|
||||
//Console.WriteLine(t.IsCompleted);
|
||||
await t;
|
||||
#if NETSTANDARD1_5
|
||||
var res = t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
|
||||
#else
|
||||
var res = t.GetType().GetProperty("Result").GetValue(t);
|
||||
#endif
|
||||
SendParams((byte)0x90, callback, Codec.Compose(res, this));
|
||||
}
|
||||
else if (rt is AsyncReply) //(rt.GetType().IsGenericType && (rt.GetType().GetGenericTypeDefinition() == typeof(AsyncReply<>)))
|
||||
{
|
||||
(rt as AsyncReply).Then(res =>
|
||||
{
|
||||
SendParams((byte)0x90, callback, Codec.Compose(res, this));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
SendParams((byte)0x90, callback, Codec.Compose(rt, this));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ft found, fi not found, this should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no function at this index
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// no resource with this id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestGetProperty(uint callback, uint resourceId, byte index)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((r) =>
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
var pt = r.Instance.Template.GetFunctionTemplate(index);
|
||||
if (pt != null)
|
||||
{
|
||||
if (r is DistributedResource)
|
||||
{
|
||||
SendParams((byte)0x91, callback, Codec.Compose((r as DistributedResource)._Get(pt.Index), this));
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = r.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
|
||||
if (pi != null)
|
||||
{
|
||||
SendParams((byte)0x91, callback, Codec.Compose(pi.GetValue(r), this));
|
||||
}
|
||||
else
|
||||
{
|
||||
// pt found, pi not found, this should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pt not found
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// resource not found
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, uint age)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((r) =>
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
var pt = r.Instance.Template.GetFunctionTemplate(index);
|
||||
if (pt != null)
|
||||
{
|
||||
if (r.Instance.GetAge(index) > age)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = r.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
if (pi != null)
|
||||
{
|
||||
SendParams((byte)0x92, callback, Codec.Compose(pi.GetValue(r), this));
|
||||
}
|
||||
else
|
||||
{
|
||||
// pt found, pi not found, this should never happen
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SendParams((byte)0x92, callback, (byte)DataType.NotModified);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pt not found
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// resource not found
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content)
|
||||
{
|
||||
Warehouse.Get(resourceId).Then((r) =>
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
|
||||
|
||||
var pt = r.Instance.Template.GetPropertyTemplate(index);
|
||||
if (pt != null)
|
||||
{
|
||||
Codec.Parse(content, 0, this).Then((value) =>
|
||||
{
|
||||
if (r is DistributedResource)
|
||||
{
|
||||
// propagation
|
||||
(r as DistributedResource)._Set(index, value).Then((x) =>
|
||||
{
|
||||
SendParams((byte)0x93, callback);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = r.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
if (pi != null)
|
||||
{
|
||||
// cast new value type to property type
|
||||
|
||||
var v = DC.CastConvert(value, pi.PropertyType);
|
||||
pi.SetValue(r, v);
|
||||
|
||||
SendParams((byte)0x93, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
// pt found, pi not found, this should never happen
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// property not found
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// resource not found
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
void IIPReplyAttachResource(uint callback, uint resourceAge, object[] properties)
|
||||
{
|
||||
if (requests.ContainsKey(callback))
|
||||
{
|
||||
var req = requests[callback];
|
||||
var r = resources[(uint)req.Arguments[0]];
|
||||
|
||||
if (r == null)
|
||||
{
|
||||
r.Instance.Deserialize(properties);
|
||||
r.Instance.Age = resourceAge;
|
||||
r.Attached();
|
||||
|
||||
// process stack
|
||||
foreach (var rr in resources.Values)
|
||||
rr.Stack.ProcessStack();
|
||||
}
|
||||
else
|
||||
{
|
||||
// resource not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IIPReplyReattachResource(uint callback, uint resourceAge, object[] properties)
|
||||
{
|
||||
var req = requests.Take(callback);
|
||||
|
||||
if (req != null)
|
||||
{
|
||||
var r = resources[(uint)req.Arguments[0]];
|
||||
|
||||
if (r == null)
|
||||
{
|
||||
r.Instance.Deserialize(properties);
|
||||
r.Instance.Age = resourceAge;
|
||||
r.Attached();
|
||||
|
||||
// process stack
|
||||
foreach (var rr in resources.Values)
|
||||
rr.Stack.ProcessStack();
|
||||
}
|
||||
else
|
||||
{
|
||||
// resource not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IIPReplyDetachResource(uint callback)
|
||||
{
|
||||
var req = requests.Take(callback);
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void IIPReplyCreateResource(uint callback, Guid classId, uint resourceId)
|
||||
{
|
||||
var req = requests.Take(callback);
|
||||
// nothing to do
|
||||
|
||||
}
|
||||
void IIPReplyDeleteResource(uint callback)
|
||||
{
|
||||
var req = requests.Take(callback);
|
||||
// nothing to do
|
||||
|
||||
}
|
||||
|
||||
void IIPReplyTemplateFromClassName(uint callback, ResourceTemplate template)
|
||||
{
|
||||
// cache
|
||||
if (!templates.ContainsKey(template.ClassId))
|
||||
templates.Add(template.ClassId, template);
|
||||
|
||||
var req = requests.Take(callback);
|
||||
req?.Trigger(template);
|
||||
}
|
||||
|
||||
void IIPReplyTemplateFromClassId(uint callback, ResourceTemplate template)
|
||||
{
|
||||
// cache
|
||||
if (!templates.ContainsKey(template.ClassId))
|
||||
templates.Add(template.ClassId, template);
|
||||
|
||||
var req = requests.Take(callback);
|
||||
req?.Trigger(template);
|
||||
|
||||
}
|
||||
|
||||
void IIPReplyTemplateFromResourceLink(uint callback, ResourceTemplate template)
|
||||
{
|
||||
// cache
|
||||
if (!templates.ContainsKey(template.ClassId))
|
||||
templates.Add(template.ClassId, template);
|
||||
|
||||
var req = requests.Take(callback);
|
||||
req?.Trigger(template);
|
||||
}
|
||||
|
||||
void IIPReplyTemplateFromResourceId(uint callback, ResourceTemplate template)
|
||||
{
|
||||
// cache
|
||||
if (!templates.ContainsKey(template.ClassId))
|
||||
templates.Add(template.ClassId, template);
|
||||
|
||||
var req = requests.Take(callback);
|
||||
req?.Trigger(template);
|
||||
}
|
||||
|
||||
void IIPReplyResourceIdFromResourceLink(uint callback, Guid classId, uint resourceId, uint resourceAge)
|
||||
{
|
||||
var req = requests.Take(callback);
|
||||
req?.Trigger(template);
|
||||
}
|
||||
|
||||
void IIPReplyInvokeFunction(uint callback, object returnValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IIPReplyGetProperty(uint callback, object value)
|
||||
{
|
||||
|
||||
}
|
||||
void IIPReplyGetPropertyIfModifiedSince(uint callback, object value)
|
||||
{
|
||||
|
||||
}
|
||||
void IIPReplySetProperty(uint callback)
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Get the ResourceTemplate for a given class Id.
|
||||
/// </summary>
|
||||
/// <param name="classId">Class GUID.</param>
|
||||
/// <returns>ResourceTemplate.</returns>
|
||||
public AsyncReply<ResourceTemplate> GetTemplate(Guid classId)
|
||||
{
|
||||
if (templates.ContainsKey(classId))
|
||||
return new AsyncReply<ResourceTemplate>(templates[classId]);
|
||||
else if (templateRequests.ContainsKey(classId))
|
||||
return templateRequests[classId];
|
||||
|
||||
var reply = new AsyncReply<ResourceTemplate>();
|
||||
templateRequests.Add(classId, reply);
|
||||
|
||||
SendRequest(IIPPacket.IIPPacketAction.TemplateFromClassId, classId).Then((rt) =>
|
||||
{
|
||||
templateRequests.Remove(classId);
|
||||
templates.Add(((ResourceTemplate)rt[0]).ClassId, (ResourceTemplate)rt[0]);
|
||||
reply.Trigger(rt[0]);
|
||||
});
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
// IStore interface
|
||||
/// <summary>
|
||||
/// Get a resource by its path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the resource.</param>
|
||||
/// <returns>Resource</returns>
|
||||
public AsyncReply<IResource> Get(string path)
|
||||
{
|
||||
if (pathRequests.ContainsKey(path))
|
||||
return pathRequests[path];
|
||||
|
||||
var reply = new AsyncReply<IResource>();
|
||||
pathRequests.Add(path, reply);
|
||||
|
||||
var bl = new BinaryList(path);
|
||||
bl.Insert(0, (ushort)bl.Length);
|
||||
|
||||
SendRequest(IIPPacket.IIPPacketAction.ResourceIdFromResourceLink, bl.ToArray()).Then((rt) =>
|
||||
{
|
||||
pathRequests.Remove(path);
|
||||
//(Guid)rt[0],
|
||||
Fetch( (uint)rt[1]).Then((r) =>
|
||||
{
|
||||
reply.Trigger(r);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrive a resource by its instance Id.
|
||||
/// </summary>
|
||||
/// <param name="iid">Instance Id</param>
|
||||
/// <returns>Resource</returns>
|
||||
public AsyncReply<IResource> Retrieve(uint iid)
|
||||
{
|
||||
foreach (var r in resources.Values)
|
||||
if (r.Instance.Id == iid)
|
||||
return new AsyncReply<IResource>(r);
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetch a resource from the other end
|
||||
/// </summary>
|
||||
/// <param name="classId">Class GUID</param>
|
||||
/// <param name="id">Resource Id</param>Guid classId
|
||||
/// <returns>DistributedResource</returns>
|
||||
public AsyncReply<DistributedResource> Fetch( uint id)
|
||||
{
|
||||
if (resourceRequests.ContainsKey(id) && resources.ContainsKey(id))
|
||||
{
|
||||
// dig for dead locks
|
||||
return resourceRequests[id];
|
||||
}
|
||||
else if (resourceRequests.ContainsKey(id))
|
||||
return resourceRequests[id];
|
||||
else if (resources.ContainsKey(id))
|
||||
return new AsyncReply<DistributedResource>(resources[id]);
|
||||
|
||||
var reply = new AsyncReply<DistributedResource>();
|
||||
|
||||
SendRequest(IIPPacket.IIPPacketAction.AttachResource, id).Then((rt) =>
|
||||
{
|
||||
GetTemplate((Guid)rt[0]).Then((tmp) =>
|
||||
{
|
||||
|
||||
// ClassId, ResourceAge, ResourceLink, Content
|
||||
|
||||
//var dr = Warehouse.New<DistributedResource>(id.ToString(), this);
|
||||
//var dr = nInitialize(this, tmp, id, (uint)rt[0]);
|
||||
var dr = new DistributedResource(this, tmp, id, (uint)rt[1], (string)rt[2]);
|
||||
Warehouse.Put(dr, id.ToString(), this);
|
||||
|
||||
Codec.ParseVarArray((byte[])rt[3], this).Then((ar) =>
|
||||
{
|
||||
dr._Attached(ar);
|
||||
resourceRequests.Remove(id);
|
||||
reply.Trigger(dr);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private void Instance_ResourceDestroyed(IResource resource)
|
||||
{
|
||||
// compose the packet
|
||||
SendParams((byte)0x1, resource.Instance.Id);
|
||||
}
|
||||
|
||||
private void Instance_PropertyModified(IResource resource, string name, object newValue, object oldValue)
|
||||
{
|
||||
var pt = resource.Instance.Template.GetPropertyTemplate(name);
|
||||
|
||||
if (pt == null)
|
||||
return;
|
||||
|
||||
// compose the packet
|
||||
if (newValue is Func<DistributedConnection, object>)
|
||||
SendParams((byte)0x10, resource.Instance.Id, pt.Index, Codec.Compose((newValue as Func<DistributedConnection, object>)(this), this));
|
||||
else
|
||||
SendParams((byte)0x10, resource.Instance.Id, pt.Index, Codec.Compose(newValue, this));
|
||||
|
||||
}
|
||||
|
||||
private void Instance_EventOccured(IResource resource, string name, string[] receivers, object[] args)
|
||||
{
|
||||
var et = resource.Instance.Template.GetEventTemplate(name);
|
||||
|
||||
if (et == null)
|
||||
return;
|
||||
|
||||
if (receivers != null)
|
||||
if (!receivers.Contains(RemoteUsername))
|
||||
return;
|
||||
|
||||
var clientArgs = new object[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
if (args[i] is Func<DistributedConnection, object>)
|
||||
clientArgs[i] = (args[i] as Func<DistributedConnection, object>)(this);
|
||||
else
|
||||
clientArgs[i] = args[i];
|
||||
|
||||
|
||||
// compose the packet
|
||||
SendParams((byte)0x11, resource.Instance.Id, (byte)et.Index, Codec.ComposeVarArray(args, this, true));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
374
Esiur/Net/IIP/DistributedResource.cs
Normal file
374
Esiur/Net/IIP/DistributedResource.cs
Normal file
@ -0,0 +1,374 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
using System.Dynamic;
|
||||
using System.Security.Cryptography;
|
||||
using Esiur.Engine;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Reflection.Emit;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Resource.Template;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
|
||||
//[System.Runtime.InteropServices.ComVisible(true)]
|
||||
public class DistributedResource : DynamicObject, IResource
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the distributed resource is destroyed.
|
||||
/// </summary>
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
uint instanceId;
|
||||
DistributedConnection connection;
|
||||
|
||||
|
||||
bool isAttached = false;
|
||||
bool isReady = false;
|
||||
|
||||
|
||||
//Structure properties = new Structure();
|
||||
|
||||
string link;
|
||||
uint age;
|
||||
uint[] ages;
|
||||
object[] properties;
|
||||
DistributedResourceEvent[] events;
|
||||
|
||||
ResourceTemplate template;
|
||||
|
||||
|
||||
//DistributedResourceStack stack;
|
||||
|
||||
|
||||
bool destroyed;
|
||||
|
||||
/// <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;
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource is ready when all its properties are attached.
|
||||
/// </summary>
|
||||
internal bool IsReady
|
||||
{
|
||||
get
|
||||
{
|
||||
return isReady;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource is attached when all its properties are received.
|
||||
/// </summary>
|
||||
internal bool IsAttached
|
||||
{
|
||||
get
|
||||
{
|
||||
return isAttached;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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, ResourceTemplate template, uint instanceId, uint age, string link)
|
||||
{
|
||||
this.link = link;
|
||||
this.connection = connection;
|
||||
this.instanceId = instanceId;
|
||||
this.template = template;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
internal void _Ready()
|
||||
{
|
||||
isReady = true;
|
||||
}
|
||||
|
||||
internal bool _Attached(object[] properties)
|
||||
{
|
||||
|
||||
if (isAttached)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
this.properties = properties;
|
||||
ages = new uint[properties.Length];
|
||||
this.events = new DistributedResourceEvent[template.Events.Length];
|
||||
isAttached = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void _EmitEventByIndex(byte index, object[] args)
|
||||
{
|
||||
var et = template.GetEventTemplate(index);
|
||||
events[index]?.Invoke(this, args);
|
||||
Instance.EmitResourceEvent(et.Name, null, args);
|
||||
}
|
||||
|
||||
public AsyncReply _Invoke(byte index, object[] args)
|
||||
{
|
||||
if (destroyed)
|
||||
throw new Exception("Trying to access destroyed object");
|
||||
|
||||
if (index >= template.Functions.Length)
|
||||
throw new Exception("Function index is incorrect");
|
||||
|
||||
var reply = new AsyncReply();
|
||||
|
||||
var parameters = Codec.ComposeVarArray(args, connection, true);
|
||||
connection.SendRequest(Packets.IIPPacket.IIPPacketAction.InvokeFunction, instanceId, index, parameters).Then((res) =>
|
||||
{
|
||||
Codec.Parse((byte[])res[0], 0, connection).Then((rt) =>
|
||||
{
|
||||
reply.Trigger(rt);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return reply;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
|
||||
{
|
||||
var ft = template.GetFunctionTemplate(binder.Name);
|
||||
|
||||
var reply = new AsyncReply();
|
||||
|
||||
if (isAttached && ft!=null)
|
||||
{
|
||||
result = _Invoke(ft.Index, args);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a property value.
|
||||
/// </summary>
|
||||
/// <param name="index">Zero-based property index.</param>
|
||||
/// <returns>Value</returns>
|
||||
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 (!isAttached)
|
||||
return false;
|
||||
|
||||
var pt = template.GetPropertyTemplate(binder.Name);
|
||||
|
||||
if (pt != null)
|
||||
{
|
||||
result = properties[pt.Index];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var et = template.GetEventTemplate(binder.Name);
|
||||
if (et == null)
|
||||
return false;
|
||||
|
||||
result = events[et.Index];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void UpdatePropertyByIndex(byte index, object value)
|
||||
{
|
||||
var pt = template.GetPropertyTemplate(index);
|
||||
properties[index] = value;
|
||||
Instance.Modified(pt.Name, 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>
|
||||
internal AsyncReply _Set(byte index, object value)
|
||||
{
|
||||
if (index >= properties.Length)
|
||||
return null;
|
||||
|
||||
var reply = new AsyncReply();
|
||||
|
||||
var parameters = Codec.Compose(value, connection);
|
||||
connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty, instanceId, index, parameters).Then((res) =>
|
||||
{
|
||||
// not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter
|
||||
//Update(index, value);
|
||||
reply.Trigger(null);
|
||||
// nothing to do here
|
||||
});
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
public override bool TrySetMember(SetMemberBinder binder, object value)
|
||||
{
|
||||
if (destroyed)
|
||||
throw new Exception("Trying to access destroyed object");
|
||||
|
||||
if (!isAttached)
|
||||
return false;
|
||||
|
||||
var pt = template.GetPropertyTemplate(binder.Name);
|
||||
|
||||
|
||||
if (pt != null)
|
||||
{
|
||||
_Set(pt.Index, value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var et = template.GetEventTemplate(binder.Name);
|
||||
if (et == null)
|
||||
return false;
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resource interface.
|
||||
/// </summary>
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of distributed resource.
|
||||
/// </summary>
|
||||
public DistributedResource()
|
||||
{
|
||||
//stack = new DistributedResourceStack(this);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource interface.
|
||||
/// </summary>
|
||||
/// <param name="trigger"></param>
|
||||
/// <returns></returns>
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
// do nothing.
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
}
|
||||
}
|
10
Esiur/Net/IIP/DistributedResourceEvent.cs
Normal file
10
Esiur/Net/IIP/DistributedResourceEvent.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
public delegate void DistributedResourceEvent(DistributedResource sender, params object[] arguments);
|
||||
}
|
49
Esiur/Net/IIP/DistributedResourceQueueItem.cs
Normal file
49
Esiur/Net/IIP/DistributedResourceQueueItem.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
public class DistributedResourceQueueItem
|
||||
{
|
||||
public enum DistributedResourceQueueItemType
|
||||
{
|
||||
Propery,
|
||||
Event
|
||||
}
|
||||
|
||||
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 DistributedResource Resource
|
||||
{
|
||||
get { return resource; }
|
||||
}
|
||||
public DistributedResourceQueueItemType Type
|
||||
{
|
||||
get { return type; }
|
||||
}
|
||||
|
||||
public byte Index
|
||||
{
|
||||
get { return index; }
|
||||
}
|
||||
|
||||
public object Value
|
||||
{
|
||||
get { return value; }
|
||||
}
|
||||
}
|
||||
}
|
117
Esiur/Net/IIP/DistributedServer.cs
Normal file
117
Esiur/Net/IIP/DistributedServer.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Misc;
|
||||
using System.Threading;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using System.Net;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Security.Membership;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
public class DistributedServer : NetworkServer<DistributedConnection>, IResource
|
||||
{
|
||||
|
||||
[Storable]
|
||||
[ResourceProperty]
|
||||
public string ip
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
[ResourceProperty]
|
||||
public IMembership Membership
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Storable]
|
||||
[ResourceProperty]
|
||||
public ushort port
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
[ResourceProperty]
|
||||
public uint timeout
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
[ResourceProperty]
|
||||
public uint clock
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
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, timeout, clock);
|
||||
}
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
private void SessionModified(DistributedConnection Session, string Key, object NewValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void ClientConnected(DistributedConnection sender)
|
||||
{
|
||||
Console.WriteLine("DistributedConnection Client Connected");
|
||||
sender.Server = this;
|
||||
}
|
||||
|
||||
protected override void ClientDisconnected(DistributedConnection sender)
|
||||
{
|
||||
Console.WriteLine("DistributedConnection Client Disconnected");
|
||||
}
|
||||
}
|
||||
}
|
14
Esiur/Net/IIP/DistributedSession.cs
Normal file
14
Esiur/Net/IIP/DistributedSession.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Security.Authority;
|
||||
|
||||
namespace Esiur.Net.IIP
|
||||
{
|
||||
public class DistributedSession : NetworkSession
|
||||
{
|
||||
Source Source { get; }
|
||||
Authentication Authentication;
|
||||
}
|
||||
}
|
167
Esiur/Net/NetworkBuffer.cs
Normal file
167
Esiur/Net/NetworkBuffer.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
|
||||
namespace Esiur.Net
|
||||
{
|
||||
public class NetworkBuffer
|
||||
{
|
||||
byte[] data;
|
||||
|
||||
uint neededDataLength = 0;
|
||||
//bool trim;
|
||||
|
||||
public NetworkBuffer()
|
||||
{
|
||||
data = new byte[0];
|
||||
}
|
||||
|
||||
public bool Protected
|
||||
{
|
||||
get
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (data.Length == 0)
|
||||
return null;
|
||||
|
||||
byte[] rt = null;
|
||||
|
||||
if (neededDataLength == 0)
|
||||
{
|
||||
rt = data;
|
||||
data = new byte[0];
|
||||
}
|
||||
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 rt;
|
||||
}
|
||||
}
|
||||
}
|
248
Esiur/Net/NetworkConnection.cs
Normal file
248
Esiur/Net/NetworkConnection.cs
Normal file
@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Data;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net
|
||||
{
|
||||
public class NetworkConnection: IDestructible// <TS>: IResource where TS : NetworkSession
|
||||
{
|
||||
private 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 ConnectionEstablishedEvent(NetworkConnection sender);
|
||||
|
||||
public event ConnectionEstablishedEvent OnConnect;
|
||||
public event DataReceivedEvent OnDataReceived;
|
||||
public event ConnectionClosedEvent OnClose;
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
// if (connected)
|
||||
Close();
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
public NetworkConnection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ISocket Socket
|
||||
{
|
||||
get
|
||||
{
|
||||
return sock;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Assign(ISocket socket)
|
||||
{
|
||||
lastAction = DateTime.Now;
|
||||
sock = socket;
|
||||
//connected = true;
|
||||
socket.OnReceive += Socket_OnReceive;
|
||||
socket.OnClose += Socket_OnClose;
|
||||
socket.OnConnect += Socket_OnConnect;
|
||||
if (socket.State == SocketState.Established)
|
||||
socket.Begin();
|
||||
}
|
||||
|
||||
private void Socket_OnConnect()
|
||||
{
|
||||
OnConnect?.Invoke(this);
|
||||
}
|
||||
|
||||
private void Socket_OnClose()
|
||||
{
|
||||
OnClose?.Invoke(this);
|
||||
}
|
||||
|
||||
private void Socket_OnReceive(NetworkBuffer buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Unassigned ?
|
||||
if (sock == null)
|
||||
return;
|
||||
|
||||
// Closed ?
|
||||
if (sock.State == SocketState.Closed || sock.State == SocketState.Terminated) // || !connected)
|
||||
return;
|
||||
|
||||
lastAction = DateTime.Now;
|
||||
|
||||
while (buffer.Available > 0 && !buffer.Protected)
|
||||
DataReceived(buffer);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log("NetworkConnection", LogType.Warning, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public ISocket Unassign()
|
||||
{
|
||||
if (sock != null)
|
||||
{
|
||||
// connected = false;
|
||||
sock.OnClose -= Socket_OnClose;
|
||||
sock.OnConnect -= Socket_OnConnect;
|
||||
sock.OnReceive -= Socket_OnReceive;
|
||||
|
||||
var rt = sock;
|
||||
sock = null;
|
||||
|
||||
return rt;
|
||||
}
|
||||
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()
|
||||
{
|
||||
//if (!connected)
|
||||
// return;
|
||||
|
||||
try
|
||||
{
|
||||
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 Connected
|
||||
{
|
||||
get
|
||||
{
|
||||
return sock.State == SocketState.Established;// connected;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public void CloseAndWait()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!connected)
|
||||
return;
|
||||
|
||||
if (sock != null)
|
||||
sock.Close();
|
||||
|
||||
while (connected)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public virtual void Send(byte[] msg)
|
||||
{
|
||||
//Console.WriteLine("TXX " + msg.Length);
|
||||
|
||||
try
|
||||
{
|
||||
//if (!connected)
|
||||
//{
|
||||
//Console.WriteLine("not connected");
|
||||
// return;
|
||||
//}
|
||||
|
||||
if (sock != null)
|
||||
{
|
||||
lastAction = DateTime.Now;
|
||||
sock.Send(msg);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Send(string data)
|
||||
{
|
||||
Send(Encoding.UTF8.GetBytes(data));
|
||||
}
|
||||
}
|
||||
}
|
346
Esiur/Net/NetworkServer.cs
Normal file
346
Esiur/Net/NetworkServer.cs
Normal file
@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Resource;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net
|
||||
{
|
||||
//public abstract class NetworkServer<TConnection, TSession> : IResource where TSession : NetworkSession, new() where TConnection : NetworkConnection<TSession>, new()
|
||||
|
||||
public abstract class NetworkServer<TConnection>: IDestructible where TConnection : NetworkConnection, new()
|
||||
{
|
||||
//private bool isRunning;
|
||||
uint clock;
|
||||
private ISocket listener;
|
||||
private AutoList<TConnection, NetworkServer<TConnection>> connections;
|
||||
|
||||
//private Thread thread;
|
||||
|
||||
protected abstract void DataReceived(TConnection sender, NetworkBuffer data);
|
||||
protected abstract void ClientConnected(TConnection sender);
|
||||
protected abstract void ClientDisconnected(TConnection sender);
|
||||
|
||||
// private int port;
|
||||
// private IPAddress ip = null;
|
||||
|
||||
private uint timeout;
|
||||
private Timer timer;
|
||||
//public KeyList<string, TSession> Sessions = new KeyList<string, TSession>();
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public AutoList<TConnection, NetworkServer<TConnection>> Connections
|
||||
{
|
||||
get
|
||||
{
|
||||
return connections;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void RemoveSession(string ID)
|
||||
{
|
||||
Sessions.Remove(ID);
|
||||
}
|
||||
|
||||
public void RemoveSession(TSession Session)
|
||||
{
|
||||
if (Session != null)
|
||||
Sessions.Remove(Session.Id);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public TSession CreateSession(string ID, int Timeout)
|
||||
{
|
||||
TSession s = new TSession();
|
||||
|
||||
s.SetSession(ID, Timeout, new NetworkSession.SessionModifiedEvent(SessionModified)
|
||||
, new NetworkSession.SessionEndedEvent(SessionEnded));
|
||||
|
||||
|
||||
Sessions.Add(ID, s);
|
||||
return s;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
private void pSessionModified(TSession session, string key, object oldValue, object newValue)
|
||||
{
|
||||
SessionModified((TSession)session, key, oldValue, newValue);
|
||||
}
|
||||
|
||||
private void pSessionEnded(NetworkSession session)
|
||||
{
|
||||
SessionEnded((TSession)session);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
protected virtual void SessionModified(NetworkSession session, string key, object oldValue, object newValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void SessionEnded(NetworkSession session)
|
||||
{
|
||||
Sessions.Remove(session.Id);
|
||||
session.Destroy();
|
||||
}
|
||||
*/
|
||||
|
||||
private void MinuteThread(object state)
|
||||
{
|
||||
List<TConnection> ToBeClosed = null;
|
||||
|
||||
|
||||
lock (connections.SyncRoot)
|
||||
{
|
||||
foreach (TConnection c in connections)
|
||||
{
|
||||
if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= timeout)
|
||||
{
|
||||
if (ToBeClosed == null)
|
||||
ToBeClosed = new List<TConnection>();
|
||||
ToBeClosed.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Console.WriteLine("UnLock MinuteThread");
|
||||
|
||||
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(ISocket socket, uint timeout, uint clock)
|
||||
{
|
||||
if (listener != null)
|
||||
return;
|
||||
|
||||
//if (socket.State == SocketState.Listening)
|
||||
// return;
|
||||
|
||||
//if (isRunning)
|
||||
// return;
|
||||
|
||||
connections = new AutoList<TConnection, NetworkServer<TConnection>>(this);
|
||||
|
||||
|
||||
if (timeout > 0 & clock > 0)
|
||||
{
|
||||
timer = new Timer(MinuteThread, null, TimeSpan.FromMinutes(0), TimeSpan.FromSeconds(clock));
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
//this.ip = ip;
|
||||
//this.port = port;
|
||||
this.clock = clock;
|
||||
|
||||
|
||||
// start a new thread for the server to live on
|
||||
//isRunning = true;
|
||||
|
||||
|
||||
|
||||
listener = socket;
|
||||
|
||||
// Start accepting
|
||||
listener.Accept().Then(NewConnection);
|
||||
|
||||
//var rt = listener.Accept().Then()
|
||||
//thread = new Thread(new System.Threading.ThreadStart(ListenForConnections));
|
||||
|
||||
//thread.Start();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
public int LocalPort
|
||||
{
|
||||
get
|
||||
{
|
||||
return port;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public uint Clock
|
||||
{
|
||||
get { return clock; }
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
var port = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.WriteLine("Server@{0} is down", port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void NewConnection(ISocket sock)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
/*
|
||||
if (listener.State == SocketState.Closed || listener.State == SocketState.Terminated)
|
||||
{
|
||||
Console.WriteLine("Listen socket break ");
|
||||
Console.WriteLine(listener.LocalEndPoint.Port);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
if (sock == null)
|
||||
{
|
||||
Console.Write("sock == null");
|
||||
return;
|
||||
}
|
||||
|
||||
//sock.ReceiveBufferSize = 102400;
|
||||
//sock.SendBufferSize = 102400;
|
||||
|
||||
TConnection c = new TConnection();
|
||||
c.OnDataReceived += OnDataReceived;
|
||||
c.OnConnect += OnClientConnect;
|
||||
c.OnClose += OnClientClose;
|
||||
|
||||
|
||||
connections.Add(c);
|
||||
c.Assign(sock);
|
||||
|
||||
ClientConnected(c);
|
||||
|
||||
// Accept more
|
||||
listener.Accept().Then(NewConnection);
|
||||
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("TSERVER " + ex.ToString());
|
||||
Global.Log("TServer", LogType.Error, ex.ToString());
|
||||
}
|
||||
|
||||
//isRunning = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
return listener.State == SocketState.Listening;
|
||||
//isRunning;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDataReceived(NetworkConnection sender, NetworkBuffer data)
|
||||
{
|
||||
DataReceived((TConnection)sender, data);
|
||||
}
|
||||
|
||||
public void OnClientConnect(NetworkConnection 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(NetworkConnection sender)
|
||||
{
|
||||
try
|
||||
{
|
||||
sender.Destroy();
|
||||
ClientDisconnected((TConnection)sender);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log("NetworkServer:OnClientDisconnect", LogType.Error, ex.ToString());
|
||||
}
|
||||
|
||||
sender = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
Stop();
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
~NetworkServer()
|
||||
{
|
||||
Stop();
|
||||
//Connections = null;
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
106
Esiur/Net/NetworkSession.cs
Normal file
106
Esiur/Net/NetworkSession.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Net
|
||||
{
|
||||
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
|
||||
{
|
||||
get { return variables; }
|
||||
}
|
||||
|
||||
public NetworkSession()
|
||||
{
|
||||
variables = new KeyList<string, object>();
|
||||
variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
|
||||
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)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
287
Esiur/Net/Packets/HTTPRequestPacket.cs
Normal file
287
Esiur/Net/Packets/HTTPRequestPacket.cs
Normal file
@ -0,0 +1,287 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
using System.Net;
|
||||
|
||||
namespace Esiur.Net.Packets
|
||||
{
|
||||
public class HTTPRequestPacket : Packet
|
||||
{
|
||||
|
||||
public enum HTTPMethod:byte
|
||||
{
|
||||
GET,
|
||||
POST,
|
||||
HEAD,
|
||||
PUT,
|
||||
DELETE,
|
||||
OPTIONS,
|
||||
TRACE,
|
||||
CONNECT,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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 override string ToString()
|
||||
{
|
||||
return "HTTPRequestPacket"
|
||||
+ "\n\tVersion: " + Version
|
||||
+ "\n\tMethod: " + Method
|
||||
+ "\n\tURL: " + URL
|
||||
+ "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (headerSize == 0)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
{
|
||||
sMethod[1] = WebUtility.UrlDecode(sMethod[1]);
|
||||
if (sMethod[1].Length >= 7)
|
||||
{
|
||||
if (sMethod[1].Substring(0, 7) == "http://")
|
||||
{
|
||||
sMethod[1] = sMethod[1].Substring(sMethod[1].IndexOf("/", 7));
|
||||
}
|
||||
}
|
||||
|
||||
URL = sMethod[1].Trim();
|
||||
|
||||
if (URL.IndexOf("?", 0) != -1)
|
||||
{
|
||||
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 (cookie.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]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(Cookies.ContainsKey(cookie.Trim())))
|
||||
{
|
||||
Cookies.Add(cookie.Trim(), String.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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"] == "application/x-www-form-urlencoded"
|
||||
|| Headers["content-type"] == ""
|
||||
|| Headers["content-type"] == null)
|
||||
{
|
||||
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
|
||||
{
|
||||
//PostForms.Add(Headers["content-type"], Encoding.Default.GetString( ));
|
||||
Message = DC.Clip(data, headerSize, postSize);
|
||||
}
|
||||
|
||||
return headerSize + postSize;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return headerSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
284
Esiur/Net/Packets/HTTPResponsePacket.cs
Normal file
284
Esiur/Net/Packets/HTTPResponsePacket.cs
Normal file
@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace Esiur.Net.Packets
|
||||
{
|
||||
public class HTTPResponsePacket : Packet
|
||||
{
|
||||
|
||||
public enum ComposeOptions : int
|
||||
{
|
||||
AllCalculateLength,
|
||||
AllDontCalculateLength,
|
||||
SpecifiedHeadersOnly,
|
||||
DataOnly
|
||||
}
|
||||
|
||||
public enum ResponseCode : int
|
||||
{
|
||||
HTTP_SWITCHING = 101,
|
||||
HTTP_OK = 200,
|
||||
HTTP_NOTFOUND = 404,
|
||||
HTTP_SERVERERROR = 500,
|
||||
HTTP_MOVED = 301,
|
||||
HTTP_NOTMODIFIED = 304,
|
||||
HTTP_REDIRECT = 307
|
||||
}
|
||||
|
||||
public class 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;
|
||||
}
|
||||
|
||||
public HTTPCookie(string Name, string Value, DateTime Expires)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Value = Value;
|
||||
this.Expires = Expires;
|
||||
}
|
||||
|
||||
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=/
|
||||
string 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 DStringDictionary Cookies;
|
||||
public List<HTTPCookie> Cookies = new List<HTTPCookie>();
|
||||
public bool Handled;
|
||||
//public bool ResponseHandled; //flag this as true if you are handling it yourself
|
||||
|
||||
//private bool createSession;
|
||||
|
||||
//private HTTPServer Server;
|
||||
//public HTTPSession Session;
|
||||
|
||||
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\n"
|
||||
+ "Server: Delta Web Server\r\n"
|
||||
//Fri, 30 Oct 2007 14:19:41 GMT"
|
||||
+ "Date: " + DateTime.Now.ToUniversalTime().ToString("r") + "\r\n";
|
||||
|
||||
|
||||
if (Options == ComposeOptions.AllCalculateLength && Message != null)
|
||||
{
|
||||
Headers["Content-Length"] = Message.Length.ToString();
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
if (options != ComposeOptions.SpecifiedHeadersOnly)
|
||||
{
|
||||
if (Message != null)
|
||||
msg.AddRange(Message);
|
||||
}
|
||||
|
||||
Data = msg.ToArray();
|
||||
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (headerSize == 0)
|
||||
return -1;
|
||||
|
||||
//Cookies = new DStringDictionary();
|
||||
//Headers = new DStringDictionary(true);
|
||||
|
||||
sMethod = sLines[0].Split(' ');
|
||||
|
||||
if (sMethod.Length == 3)
|
||||
{
|
||||
Version = sMethod[0].Trim();
|
||||
Number = (ResponseCode)(Convert.ToInt32(sMethod[1].Trim()));
|
||||
Text = sMethod[2];
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
//Set-Cookie: NAME=VALUE; expires=DATE;
|
||||
|
||||
if (header[0] == "set-cookie")
|
||||
{
|
||||
string[] cookie = header[1].Split(';');
|
||||
|
||||
if (cookie.Length >= 1)
|
||||
{
|
||||
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())
|
||||
{
|
||||
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
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
382
Esiur/Net/Packets/IIPAuthPacket.cs
Normal file
382
Esiur/Net/Packets/IIPAuthPacket.cs
Normal file
@ -0,0 +1,382 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.Packets
|
||||
{
|
||||
class IIPAuthPacket : Packet
|
||||
{
|
||||
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 enum IIPAuthPacketMethod: byte
|
||||
{
|
||||
None,
|
||||
Certificate,
|
||||
Credentials,
|
||||
Token
|
||||
}
|
||||
|
||||
|
||||
public IIPAuthPacketCommand Command
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public IIPAuthPacketAction Action
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public byte ErrorCode { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public IIPAuthPacketMethod LocalMethod
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public byte[] SourceInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public byte[] Hash
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public byte[] SessionId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IIPAuthPacketMethod 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;
|
||||
}
|
||||
|
||||
private uint dataLengthNeeded;
|
||||
|
||||
bool NotEnough(uint offset, uint ends, uint needed)
|
||||
{
|
||||
if (offset + needed > ends)
|
||||
{
|
||||
dataLengthNeeded = needed - (ends - offset);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Action = (IIPAuthPacketAction)(data[offset++] & 0x3f);
|
||||
|
||||
if (Action == IIPAuthPacketAction.AuthenticateHash)
|
||||
{
|
||||
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 if (Action == IIPAuthPacketAction.NewConnection)
|
||||
{
|
||||
if (NotEnough(offset, ends, 2))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var length = data.GetUInt16(offset);
|
||||
|
||||
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)
|
||||
{
|
||||
RemoteMethod = (IIPAuthPacketMethod)((data[offset] >> 4) & 0x3);
|
||||
LocalMethod = (IIPAuthPacketMethod)((data[offset] >> 2) & 0x3);
|
||||
var encrypt = ((data[offset++] & 0x2) == 0x2);
|
||||
|
||||
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var domainLength = data[offset++];
|
||||
if (NotEnough(offset, ends, domainLength))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var domain = data.GetString(offset, domainLength);
|
||||
|
||||
Domain = domain;
|
||||
|
||||
offset += domainLength;
|
||||
|
||||
|
||||
if (RemoteMethod == IIPAuthPacketMethod.Credentials)
|
||||
{
|
||||
if (LocalMethod == IIPAuthPacketMethod.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;
|
||||
}
|
||||
}
|
||||
|
||||
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 = (IIPAuthPacketMethod)((data[offset] >> 4) & 0x3);
|
||||
LocalMethod = (IIPAuthPacketMethod)((data[offset] >> 2) & 0x3);
|
||||
var encrypt = ((data[offset++] & 0x2) == 0x2);
|
||||
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
|
||||
if (RemoteMethod == IIPAuthPacketMethod.Credentials)
|
||||
{
|
||||
if (LocalMethod == IIPAuthPacketMethod.None)
|
||||
{
|
||||
if (NotEnough(offset, ends, 32))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
/*
|
||||
var remoteNonce = new byte[32];
|
||||
Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32);
|
||||
RemoteNonce = remoteNonce;
|
||||
*/
|
||||
|
||||
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))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ErrorMessage = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return offset - oOffset;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
550
Esiur/Net/Packets/IIPPacket.cs
Normal file
550
Esiur/Net/Packets/IIPPacket.cs
Normal file
@ -0,0 +1,550 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Net.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.Packets
|
||||
{
|
||||
class IIPPacket : Packet
|
||||
{
|
||||
public enum IIPPacketCommand : byte
|
||||
{
|
||||
Event = 0,
|
||||
Request,
|
||||
Reply,
|
||||
Error,
|
||||
}
|
||||
|
||||
public enum IIPPacketEvent: byte
|
||||
{
|
||||
// Event Manage
|
||||
ResourceReassigned = 0,
|
||||
ResourceDestroyed,
|
||||
|
||||
// Event Invoke
|
||||
PropertyUpdated = 0x10,
|
||||
EventOccured,
|
||||
}
|
||||
|
||||
public enum IIPPacketAction : byte
|
||||
{
|
||||
// Request Manage
|
||||
AttachResource = 0,
|
||||
ReattachResource,
|
||||
DetachResource,
|
||||
CreateResource,
|
||||
DeleteResource,
|
||||
|
||||
// Request Inquire
|
||||
TemplateFromClassName = 0x8,
|
||||
TemplateFromClassId,
|
||||
TemplateFromResourceLink,
|
||||
TemplateFromResourceId,
|
||||
ResourceIdFromResourceLink,
|
||||
|
||||
// Request Invoke
|
||||
InvokeFunction = 0x10,
|
||||
GetProperty,
|
||||
GetPropertyIfModified,
|
||||
SetProperty,
|
||||
}
|
||||
|
||||
|
||||
public IIPPacketCommand Command
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public IIPPacketAction Action
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IIPPacketEvent Event
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public uint ResourceId { get; set; }
|
||||
public uint NewResourceId { get; set; }
|
||||
|
||||
public uint ResourceAge { get; set; }
|
||||
public byte[] Content { get; set; }
|
||||
public byte ErrorCode { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public string ResourceLink { get; set; }
|
||||
public Guid ClassId { get; set; }
|
||||
public byte MethodIndex { get; set; }
|
||||
public string MethodName { get; set; }
|
||||
public uint CallbackId { get; set; }
|
||||
|
||||
private uint dataLengthNeeded;
|
||||
|
||||
public override bool Compose()
|
||||
{
|
||||
return base.Compose();
|
||||
}
|
||||
|
||||
bool NotEnough(uint offset, uint ends, uint needed)
|
||||
{
|
||||
if (offset + needed > ends)
|
||||
{
|
||||
dataLengthNeeded = needed - (ends - offset);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public override long Parse(byte[] data, uint offset, uint ends)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Command = (IIPPacketCommand)(data[offset] >> 6);
|
||||
|
||||
if (Command == IIPPacketCommand.Event)
|
||||
{
|
||||
Event = (IIPPacketEvent)(data[offset++] & 0x3f);
|
||||
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
Action = (IIPPacketAction)(data[offset++] & 0x3f);
|
||||
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
CallbackId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (Command == IIPPacketCommand.Event)
|
||||
{
|
||||
if (Event == IIPPacketEvent.ResourceReassigned)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
NewResourceId = data.GetUInt32( offset);
|
||||
offset += 4;
|
||||
|
||||
}
|
||||
else if (Event == IIPPacketEvent.ResourceDestroyed)
|
||||
{
|
||||
// nothing to parse
|
||||
}
|
||||
else if (Event == IIPPacketEvent.PropertyUpdated)
|
||||
{
|
||||
if (NotEnough(offset, ends, 2))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
MethodIndex = data[offset++];
|
||||
|
||||
var dt = (DataType)data[offset++];
|
||||
var size = dt.Size();// Codec.SizeOf(dt);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt32( offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip( offset - 5, cl + 5);
|
||||
offset += cl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NotEnough(offset, ends, (uint)size))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset - 1, (uint)size + 1);
|
||||
offset += (uint)size;
|
||||
}
|
||||
}
|
||||
else if (Event == IIPPacketEvent.EventOccured)
|
||||
{
|
||||
if (NotEnough(offset, ends, 5))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
MethodIndex = data[offset++];
|
||||
|
||||
var cl = data.GetUInt32( offset);
|
||||
offset += 4;
|
||||
|
||||
Content = data.Clip( offset, cl);
|
||||
offset += cl;
|
||||
|
||||
}
|
||||
}
|
||||
else if (Command == IIPPacketCommand.Request)
|
||||
{
|
||||
if (Action == IIPPacketAction.AttachResource)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
}
|
||||
else if (Action == IIPPacketAction.ReattachResource)
|
||||
{
|
||||
if (NotEnough(offset, ends, 8))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
ResourceAge = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.DetachResource)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.CreateResource)
|
||||
{
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data[offset++];
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ClassName = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
}
|
||||
else if (Action == IIPPacketAction.DeleteResource)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.TemplateFromClassName)
|
||||
{
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data[offset++];
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ClassName = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.TemplateFromClassId)
|
||||
{
|
||||
if (NotEnough(offset, ends, 16))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ClassId = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.TemplateFromResourceLink)
|
||||
{
|
||||
if (NotEnough(offset, ends, 2))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt16(offset);
|
||||
offset += 2;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceLink = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
}
|
||||
else if (Action == IIPPacketAction.TemplateFromResourceId)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
}
|
||||
else if (Action == IIPPacketAction.ResourceIdFromResourceLink)
|
||||
{
|
||||
if (NotEnough(offset, ends, 2))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt16(offset);
|
||||
offset += 2;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceLink = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
}
|
||||
else if (Action == IIPPacketAction.InvokeFunction)
|
||||
{
|
||||
if (NotEnough(offset, ends, 9))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
MethodIndex = data[offset++];
|
||||
|
||||
var cl = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset, cl);
|
||||
offset += cl;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.GetProperty)
|
||||
{
|
||||
if (NotEnough(offset, ends, 5))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
MethodIndex = data[offset++];
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.GetPropertyIfModified)
|
||||
{
|
||||
if (NotEnough(offset, ends, 9))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
MethodIndex = data[offset++];
|
||||
|
||||
ResourceAge = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.SetProperty)
|
||||
{
|
||||
if (NotEnough(offset, ends, 6))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
MethodIndex = data[offset++];
|
||||
|
||||
|
||||
var dt = (DataType)data[offset++];
|
||||
var size = dt.Size();// Codec.SizeOf(dt);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset-5, cl + 5);
|
||||
offset += cl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NotEnough(offset, ends, (uint)size))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset-1, (uint)size + 1);
|
||||
offset += (uint)size;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Command == IIPPacketCommand.Reply)
|
||||
{
|
||||
if (Action == IIPPacketAction.AttachResource
|
||||
|| Action == IIPPacketAction.ReattachResource)
|
||||
{
|
||||
|
||||
if (NotEnough(offset, ends, 26))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ClassId = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
|
||||
ResourceAge = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
uint cl = data.GetUInt16(offset);
|
||||
offset += 2;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ResourceLink = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
cl = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset, cl);
|
||||
offset += cl;
|
||||
}
|
||||
else if (Action == IIPPacketAction.DetachResource)
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
else if (Action == IIPPacketAction.CreateResource)
|
||||
{
|
||||
if (NotEnough(offset, ends, 20))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ClassId = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
}
|
||||
else if (Action == IIPPacketAction.DetachResource)
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
else if (Action == IIPPacketAction.TemplateFromClassName
|
||||
|| Action == IIPPacketAction.TemplateFromClassId
|
||||
|| Action == IIPPacketAction.TemplateFromResourceLink
|
||||
|| Action == IIPPacketAction.TemplateFromResourceId)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset, cl);
|
||||
offset += cl;
|
||||
}
|
||||
else if (Action == IIPPacketAction.ResourceIdFromResourceLink)
|
||||
{
|
||||
if (NotEnough(offset, ends, 24))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ClassId = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
|
||||
ResourceId = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
ResourceAge = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
}
|
||||
else if (Action == IIPPacketAction.InvokeFunction
|
||||
|| Action == IIPPacketAction.GetProperty
|
||||
|| Action == IIPPacketAction.GetPropertyIfModified)
|
||||
{
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var dt = (DataType)data[offset++];
|
||||
var size = dt.Size();// Codec.SizeOf(dt);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset - 5, cl + 5);
|
||||
offset += cl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NotEnough(offset, ends, (uint)size))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
Content = data.Clip(offset - 1, (uint)size + 1);
|
||||
offset += (uint)size;
|
||||
}
|
||||
}
|
||||
else if (Action == IIPPacketAction.SetProperty)
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
else if (Command == IIPPacketCommand.Error)
|
||||
{
|
||||
// Error
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
CallbackId = data.GetUInt32(offset);
|
||||
|
||||
if (NotEnough(offset, ends, 1))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ErrorCode = data[offset++];
|
||||
|
||||
if (NotEnough(offset, ends, 4))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
var cl = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
if (NotEnough(offset, ends, cl))
|
||||
return -dataLengthNeeded;
|
||||
|
||||
ErrorMessage = data.GetString(offset, cl);
|
||||
offset += cl;
|
||||
}
|
||||
|
||||
return offset - oOffset;
|
||||
}
|
||||
}
|
||||
}
|
367
Esiur/Net/Packets/Packet.cs
Normal file
367
Esiur/Net/Packets/Packet.cs
Normal file
@ -0,0 +1,367 @@
|
||||
|
||||
/******************************************************************************\
|
||||
* Uruky Sniffer Project *
|
||||
* *
|
||||
* Copyright (C) 2006 Ahmed Khalaf - ahmed@uruky.com *
|
||||
* ahmed_baghdad@yahoo.com *
|
||||
* http://www.uruky.com *
|
||||
* http://www.dijlh.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2, or (at your option) *
|
||||
* any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software *
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
|
||||
* *
|
||||
* File: Packet.cs *
|
||||
* Description: Ethernet/ARP/IPv4/TCP/UDP Packet Decoding & Encoding Class *
|
||||
* Compatibility: .Net Framework 2.0 / Mono 1.1.8 *
|
||||
* *
|
||||
\******************************************************************************/
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Net.DataLink;
|
||||
using System.Net.NetworkInformation;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace Esiur.Net.Packets
|
||||
{
|
||||
internal static class Functions
|
||||
{
|
||||
public static void AddData(ref byte[] dest, byte[] src)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
public static Array Resize(Array array, int newSize)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class PosixTime
|
||||
{
|
||||
ulong seconds;
|
||||
ulong microseconds;
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
// 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 = 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;
|
||||
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)
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
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;
|
||||
srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString();
|
||||
dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString();
|
||||
}
|
||||
else if (root is W802_11Packet)
|
||||
{
|
||||
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 *************************************/
|
||||
|
||||
|
188
Esiur/Net/Packets/WebsocketPacket.cs
Normal file
188
Esiur/Net/Packets/WebsocketPacket.cs
Normal file
@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace Esiur.Net.Packets
|
||||
{
|
||||
public class WebsocketPacket : Packet
|
||||
{
|
||||
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
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
if (Mask)
|
||||
{
|
||||
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);
|
||||
if (length < needed)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
needed += 2;
|
||||
if (length < needed)
|
||||
{
|
||||
//Console.WriteLine("stage 2 " + needed);
|
||||
return length - needed;
|
||||
}
|
||||
PayloadLength = DC.GetUInt16(data, offset);
|
||||
offset += 2;
|
||||
}
|
||||
else if (PayloadLength == 127)
|
||||
{
|
||||
needed += 8;
|
||||
if (length < needed)
|
||||
{
|
||||
//Console.WriteLine("stage 3 " + needed);
|
||||
return length - needed;
|
||||
}
|
||||
|
||||
PayloadLength = DC.GetInt64(data, 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;
|
||||
}
|
||||
|
||||
// if ((int)PayloadLength > (ends - offset))
|
||||
// {
|
||||
// return -((int)PayloadLength - (ends - offset));
|
||||
// }
|
||||
else
|
||||
{
|
||||
Message = DC.Clip(data, offset, (uint)PayloadLength);
|
||||
|
||||
if (Mask)
|
||||
{
|
||||
//var aMask = BitConverter.GetBytes(MaskKey);
|
||||
for (int i = 0; i < Message.Length; i++)
|
||||
{
|
||||
Message[i] = (byte)(Message[i] ^ MaskKey[i % 4]);
|
||||
}
|
||||
}
|
||||
|
||||
return (offset - oOffset) + (int)PayloadLength;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
Console.WriteLine(offset + "::" + DC.ToHex(data));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
Esiur/Net/Sockets/ISocket.cs
Normal file
39
Esiur/Net/Sockets/ISocket.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using System.Collections.Concurrent;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Net.Sockets
|
||||
{
|
||||
public delegate void ISocketReceiveEvent(NetworkBuffer buffer);
|
||||
public delegate void ISocketConnectEvent();
|
||||
public delegate void ISocketCloseEvent();
|
||||
|
||||
public interface ISocket: IDestructible
|
||||
{
|
||||
SocketState State { get; }
|
||||
|
||||
event ISocketReceiveEvent OnReceive;
|
||||
event ISocketConnectEvent OnConnect;
|
||||
event ISocketCloseEvent OnClose;
|
||||
|
||||
void Send(byte[] message);
|
||||
void Send(byte[] message, int offset, int size);
|
||||
void Close();
|
||||
bool Connect(string hostname, ushort port);
|
||||
bool Begin();
|
||||
//ISocket Accept();
|
||||
AsyncReply<ISocket> Accept();
|
||||
IPEndPoint RemoteEndPoint { get; }
|
||||
IPEndPoint LocalEndPoint { get; }
|
||||
}
|
||||
}
|
310
Esiur/Net/Sockets/SSLSocket.cs
Normal file
310
Esiur/Net/Sockets/SSLSocket.cs
Normal file
@ -0,0 +1,310 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using System.Threading;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Esiur.Resource;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace Esiur.Net.Sockets
|
||||
{
|
||||
public class SSLSocket : ISocket
|
||||
{
|
||||
Socket sock;
|
||||
byte[] receiveBuffer;
|
||||
|
||||
NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
|
||||
|
||||
object sendLock = new object();
|
||||
|
||||
Queue<byte[]> sendBufferQueue = new Queue<byte[]>();
|
||||
|
||||
bool asyncSending;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
private void Connected(Task t)
|
||||
{
|
||||
if (server)
|
||||
{
|
||||
ssl.AuthenticateAsServerAsync(cert).ContinueWith(Authenticated);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssl.AuthenticateAsClientAsync(hostname).ContinueWith(Authenticated);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Connect(string hostname, ushort port)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.hostname = hostname;
|
||||
server = false;
|
||||
state = SocketState.Connecting;
|
||||
sock.ConnectAsync(hostname, port).ContinueWith(Connected);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DataSent(Task task)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
asyncSending = false;
|
||||
|
||||
Global.Log("SSLSocket", LogType.Error, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sock.Shutdown(SocketShutdown.Both);
|
||||
|
||||
OnClose?.Invoke();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Authenticated(Task task)
|
||||
{
|
||||
try
|
||||
{
|
||||
state = SocketState.Established;
|
||||
OnConnect?.Invoke();
|
||||
|
||||
if (!server)
|
||||
Begin();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state = SocketState.Terminated;
|
||||
Close();
|
||||
Global.Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataReceived(Task<int> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
// SocketError err;
|
||||
|
||||
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 (state == SocketState.Established)
|
||||
{
|
||||
ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
Close();
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
public AsyncReply<ISocket> Accept()
|
||||
{
|
||||
var reply = new AsyncReply<ISocket>();
|
||||
|
||||
try
|
||||
{
|
||||
sock.AcceptAsync().ContinueWith((x) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
reply.Trigger(new SSLSocket(x.Result, cert, true));
|
||||
}
|
||||
catch
|
||||
{
|
||||
reply.Trigger(null);
|
||||
}
|
||||
|
||||
}, null);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
state = SocketState.Terminated;
|
||||
return null;
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
18
Esiur/Net/Sockets/SocketState.cs
Normal file
18
Esiur/Net/Sockets/SocketState.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.Sockets
|
||||
{
|
||||
public enum SocketState
|
||||
{
|
||||
Initial,
|
||||
Listening,
|
||||
Connecting,
|
||||
Established,
|
||||
Closed,
|
||||
Terminated
|
||||
}
|
||||
}
|
291
Esiur/Net/Sockets/TCPSocket.cs
Normal file
291
Esiur/Net/Sockets/TCPSocket.cs
Normal file
@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using System.Threading;
|
||||
using Esiur.Resource;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace Esiur.Net.Sockets
|
||||
{
|
||||
public class TCPSocket : ISocket
|
||||
{
|
||||
Socket sock;
|
||||
byte[] receiveBuffer;
|
||||
|
||||
ArraySegment<byte> receiveBufferSegment;
|
||||
|
||||
NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
|
||||
|
||||
object sendLock = new object();
|
||||
|
||||
Queue<byte[]> sendBufferQueue = new Queue<byte[]>();
|
||||
|
||||
bool asyncSending;
|
||||
|
||||
|
||||
SocketState state = SocketState.Initial;
|
||||
|
||||
public event ISocketReceiveEvent OnReceive;
|
||||
public event ISocketConnectEvent OnConnect;
|
||||
public event ISocketCloseEvent OnClose;
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
|
||||
private void Connected(Task t)
|
||||
{
|
||||
state = SocketState.Established;
|
||||
OnConnect?.Invoke();
|
||||
Begin();
|
||||
}
|
||||
|
||||
public bool Begin()
|
||||
{
|
||||
sock.ReceiveAsync(receiveBufferSegment, SocketFlags.None).ContinueWith(DataReceived);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Connect(string hostname, ushort port)
|
||||
{
|
||||
try
|
||||
{
|
||||
state = SocketState.Connecting;
|
||||
sock.ConnectAsync(hostname, port).ContinueWith(Connected);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DataReceived(Task<int> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
// SocketError err;
|
||||
|
||||
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)
|
||||
sock.ReceiveAsync(receiveBufferSegment, SocketFlags.None).ContinueWith(DataReceived);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (state != SocketState.Closed && !sock.Connected)
|
||||
{
|
||||
state = SocketState.Terminated;
|
||||
Close();
|
||||
}
|
||||
|
||||
Global.Log("TCPSocket", LogType.Error, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IPEndPoint LocalEndPoint
|
||||
{
|
||||
get { return (IPEndPoint)sock.LocalEndPoint; }
|
||||
}
|
||||
|
||||
public TCPSocket()
|
||||
{
|
||||
sock = new Socket(AddressFamily.InterNetwork,
|
||||
SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
receiveBuffer = new byte[sock.ReceiveBufferSize];
|
||||
receiveBufferSegment = new ArraySegment<byte>(receiveBuffer);
|
||||
|
||||
}
|
||||
|
||||
public TCPSocket(string hostname, ushort port)
|
||||
{
|
||||
// create the socket
|
||||
sock = new Socket(AddressFamily.InterNetwork,
|
||||
SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
|
||||
receiveBuffer = new byte[sock.ReceiveBufferSize];
|
||||
receiveBufferSegment = new ArraySegment<byte>(receiveBuffer);
|
||||
|
||||
Connect(hostname, port);
|
||||
|
||||
}
|
||||
|
||||
private void DataSent(Task<int> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (sendBufferQueue.Count > 0)
|
||||
{
|
||||
byte[] data = sendBufferQueue.Dequeue();
|
||||
lock (sendLock)
|
||||
sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ContinueWith(DataSent);
|
||||
}
|
||||
else
|
||||
{
|
||||
asyncSending = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (state != SocketState.Closed && !sock.Connected)
|
||||
{
|
||||
state = SocketState.Terminated;
|
||||
Close();
|
||||
}
|
||||
|
||||
asyncSending = false;
|
||||
|
||||
Global.Log("TCPSocket", LogType.Error, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public TCPSocket(IPEndPoint localEndPoint)
|
||||
{
|
||||
// create the socket
|
||||
sock = new Socket(AddressFamily.InterNetwork,
|
||||
SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
|
||||
receiveBuffer = new byte[sock.ReceiveBufferSize];
|
||||
|
||||
state = SocketState.Listening;
|
||||
|
||||
|
||||
// bind
|
||||
sock.Bind(localEndPoint);
|
||||
|
||||
// start listening
|
||||
sock.Listen(UInt16.MaxValue);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public IPEndPoint RemoteEndPoint
|
||||
{
|
||||
get { return (IPEndPoint)sock.RemoteEndPoint; }
|
||||
}
|
||||
|
||||
public SocketState State
|
||||
{
|
||||
get
|
||||
{
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public TCPSocket(Socket socket)
|
||||
{
|
||||
sock = socket;
|
||||
receiveBuffer = new byte[sock.ReceiveBufferSize];
|
||||
receiveBufferSegment = new ArraySegment<byte>(receiveBuffer);
|
||||
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;
|
||||
}
|
||||
|
||||
sock.Shutdown(SocketShutdown.Both);// Close();
|
||||
OnClose?.Invoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
sock.SendAsync(new ArraySegment<byte>(message, offset, size), SocketFlags.None).ContinueWith(DataSent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
Close();
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
public AsyncReply<ISocket> Accept()
|
||||
{
|
||||
var reply = new AsyncReply<ISocket>();
|
||||
|
||||
try
|
||||
{
|
||||
sock.AcceptAsync().ContinueWith((x) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
reply.Trigger(new TCPSocket(x.Result));
|
||||
}
|
||||
catch
|
||||
{
|
||||
reply.Trigger(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
state = SocketState.Terminated;
|
||||
return null;
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
220
Esiur/Net/Sockets/WSSocket.cs
Normal file
220
Esiur/Net/Sockets/WSSocket.cs
Normal file
@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using Esiur.Net.Packets;
|
||||
using Esiur.Misc;
|
||||
using System.IO;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.Sockets
|
||||
{
|
||||
public class WSSocket : ISocket
|
||||
{
|
||||
WebsocketPacket pkt_receive = new WebsocketPacket();
|
||||
WebsocketPacket pkt_send = new WebsocketPacket();
|
||||
|
||||
ISocket sock;
|
||||
NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
|
||||
object sendLock = new object();
|
||||
|
||||
public event ISocketReceiveEvent OnReceive;
|
||||
public event ISocketConnectEvent OnConnect;
|
||||
public event ISocketCloseEvent OnClose;
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
long totalSent, totalReceived;
|
||||
|
||||
public IPEndPoint LocalEndPoint
|
||||
{
|
||||
get { return (IPEndPoint)sock.LocalEndPoint; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public IPEndPoint RemoteEndPoint
|
||||
{
|
||||
get { return sock.RemoteEndPoint; }
|
||||
}
|
||||
|
||||
|
||||
public SocketState State
|
||||
{
|
||||
get
|
||||
{
|
||||
return sock.State;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public WSSocket(ISocket socket)
|
||||
{
|
||||
pkt_send.FIN = true;
|
||||
pkt_send.Mask = false;
|
||||
pkt_send.Opcode = WebsocketPacket.WSOpcode.BinaryFrame;
|
||||
sock = socket;
|
||||
sock.OnClose += Sock_OnClose;
|
||||
sock.OnConnect += Sock_OnConnect;
|
||||
sock.OnReceive += Sock_OnReceive;
|
||||
}
|
||||
|
||||
private void Sock_OnReceive(NetworkBuffer buffer)
|
||||
{
|
||||
|
||||
if (sock.State == SocketState.Closed || sock.State == SocketState.Terminated)
|
||||
return;
|
||||
|
||||
if (buffer.Protected)
|
||||
return;
|
||||
|
||||
var msg = buffer.Read();
|
||||
|
||||
var wsPacketLength = pkt_receive.Parse(msg, 0, (uint)msg.Length);
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
Console.WriteLine("Unknown WS opcode:" + pkt_receive.Opcode);
|
||||
|
||||
if (offset == msg.Length)
|
||||
{
|
||||
OnReceive?.Invoke(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
|
||||
|
||||
receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)(msg.Length - offset) + (uint)-wsPacketLength);
|
||||
|
||||
}
|
||||
|
||||
OnReceive?.Invoke(receiveNetworkBuffer);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 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();
|
||||
OnDestroy?.Invoke(this);
|
||||
}
|
||||
|
||||
public AsyncReply<ISocket> Accept()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
27
Esiur/Net/TCP/TCPConnection.cs
Normal file
27
Esiur/Net/TCP/TCPConnection.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
|
||||
namespace Esiur.Net.TCP
|
||||
{
|
||||
public class TCPConnection: NetworkConnection
|
||||
{
|
||||
|
||||
private KeyList<string, object> variables = new KeyList<string, object>();
|
||||
|
||||
|
||||
public KeyList<string, object> Variables
|
||||
{
|
||||
get
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
42
Esiur/Net/TCP/TCPFilter.cs
Normal file
42
Esiur/Net/TCP/TCPFilter.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using Esiur.Data;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.TCP
|
||||
{
|
||||
public abstract class TCPFilter: IResource
|
||||
{
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
|
||||
|
||||
public virtual bool Connected(TCPConnection sender)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool Disconnected(TCPConnection sender)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract bool Execute(byte[] msg, NetworkBuffer data, TCPConnection sender);
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
195
Esiur/Net/TCP/TCPServer.cs
Normal file
195
Esiur/Net/TCP/TCPServer.cs
Normal file
@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Misc;
|
||||
using System.Threading;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using System.Net;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.TCP
|
||||
{
|
||||
public class TCPServer : NetworkServer<TCPConnection>, IResource
|
||||
{
|
||||
|
||||
[Storable]
|
||||
string ip
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Storable]
|
||||
ushort port
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Storable]
|
||||
uint timeout
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Storable]
|
||||
uint clock
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
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, timeout, clock);
|
||||
}
|
||||
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(TCPConnection sender, NetworkBuffer data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
var msg = data.Read();
|
||||
|
||||
foreach (Instance instance in Instance.Children)
|
||||
{
|
||||
var f = instance.Resource as TCPFilter;
|
||||
if (f.Execute(msg, data, sender))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SessionModified(TCPConnection Session, string Key, object NewValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
public TCPServer(string IP, int Port, int Timeout, int Clock)
|
||||
: base(IP, Port, Timeout, Clock)
|
||||
{
|
||||
if (Timeout > 0 && Clock > 0)
|
||||
{
|
||||
mTimer = new Timer(OnlineThread, null, 0, Clock * 1000);// TimeSpan.FromSeconds(Clock));
|
||||
mTimeout = Timeout;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
private void OnlineThread(object state)
|
||||
{
|
||||
List<TCPConnection> ToBeClosed = null;
|
||||
//Console.WriteLine("Minute Thread");
|
||||
|
||||
if (Connections.Count > 0)
|
||||
{
|
||||
Global.Log("TCPServer:OnlineThread", LogType.Debug,
|
||||
//"Tick:" + DateTime.Now.Subtract(Connections[0].LastAction).TotalSeconds + ":" + mTimeout + ":" +
|
||||
"Tick | Connections: " + Connections.Count + " Threads:" + System.Diagnostics.Process.GetCurrentProcess().Threads.Count);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
foreach (TCPConnection c in Connections)//.Values)
|
||||
{
|
||||
if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= mTimeout)
|
||||
{
|
||||
if (ToBeClosed == null)
|
||||
ToBeClosed = new List<TCPConnection>();
|
||||
ToBeClosed.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (ToBeClosed != null)
|
||||
{
|
||||
|
||||
Global.Log("TCPServer:OnlineThread", LogType.Debug, "Inactive Closed:" + ToBeClosed.Count);
|
||||
|
||||
foreach (TCPConnection c in ToBeClosed)
|
||||
c.Close();
|
||||
|
||||
ToBeClosed.Clear();
|
||||
ToBeClosed = null;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log("TCPServer:OnlineThread", LogType.Debug, ex.ToString());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//~TCPServer()
|
||||
//{
|
||||
// StopServer();
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
protected override void ClientConnected(TCPConnection sender)
|
||||
{
|
||||
//Console.WriteLine("TCP Client Connected");
|
||||
|
||||
// Global.Log("TCPServer",
|
||||
// LogType.Debug,
|
||||
// "Connected:" + Connections.Count
|
||||
// + ":" + sender.RemoteEndPoint.ToString());
|
||||
|
||||
foreach (Instance instance in Instance.Children)
|
||||
{
|
||||
var f = instance.Resource as TCPFilter;
|
||||
f.Connected(sender);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ClientDisconnected(TCPConnection sender)
|
||||
{
|
||||
//Console.WriteLine("TCP Client Disconnected");
|
||||
|
||||
// Global.Log("TCPServer", LogType.Debug, "Disconnected:" + Connections.Count);// + ":" + sender.RemoteEndPoint.ToString());
|
||||
|
||||
foreach (Instance instance in Instance.Children)
|
||||
{
|
||||
try
|
||||
{
|
||||
var f = instance.Resource as TCPFilter;
|
||||
f.Disconnected(sender);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Global.Log(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
13
Esiur/Net/TCP/TCPSession.cs
Normal file
13
Esiur/Net/TCP/TCPSession.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Net.TCP
|
||||
{
|
||||
public class TCPSession : NetworkSession
|
||||
{
|
||||
|
||||
}
|
||||
}
|
33
Esiur/Net/UDP/UDPFilter.cs
Normal file
33
Esiur/Net/UDP/UDPFilter.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Net;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.UDP
|
||||
{
|
||||
public abstract class UDPFilter : IResource
|
||||
{
|
||||
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()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
173
Esiur/Net/UDP/UDPServer.cs
Normal file
173
Esiur/Net/UDP/UDPServer.cs
Normal file
@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Net.UDP
|
||||
{
|
||||
|
||||
/* public class IIPConnection
|
||||
{
|
||||
public EndPoint SenderPoint;
|
||||
public
|
||||
}*/
|
||||
public class UDPServer : IResource
|
||||
{
|
||||
Thread Receiver;
|
||||
UdpClient Udp;
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
public Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
string ip
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Storable]
|
||||
ushort port
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
if (trigger == ResourceTrigger.Initialize)
|
||||
{
|
||||
var address = ip == null ? IPAddress.Any : IPAddress.Parse(ip);
|
||||
|
||||
Udp = new UdpClient(new IPEndPoint(address, (int)port));
|
||||
Receiver = new Thread(Receiving);
|
||||
Receiver.Start();
|
||||
}
|
||||
else if (trigger == ResourceTrigger.Terminate)
|
||||
{
|
||||
if (Receiver != null)
|
||||
Receiver.Abort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Receiving()
|
||||
{
|
||||
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte[] b = Udp.Receive(ref ep);
|
||||
|
||||
foreach(var child in Instance.Children)
|
||||
{
|
||||
var f = child as UDPFilter;
|
||||
|
||||
try
|
||||
{
|
||||
if (f.Execute(b, ep))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log("UDPServer", LogType.Error, ex.ToString());
|
||||
//Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Send(byte[] Data, int Count, IPEndPoint EP)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
23
Esiur/Resource/IResource.cs
Normal file
23
Esiur/Resource/IResource.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public delegate bool QueryFilter<T>(T value);
|
||||
|
||||
public interface IResource : IDestructible
|
||||
{
|
||||
|
||||
AsyncReply<bool> Trigger(ResourceTrigger trigger);
|
||||
|
||||
Instance Instance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
17
Esiur/Resource/IStore.cs
Normal file
17
Esiur/Resource/IStore.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Esiur.Engine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public interface IStore:IResource
|
||||
{
|
||||
AsyncReply<IResource> Get(string path);
|
||||
AsyncReply<IResource> Retrieve(uint iid);
|
||||
bool Put(IResource resource);
|
||||
string Link(IResource resource);
|
||||
}
|
||||
}
|
427
Esiur/Resource/Instance.cs
Normal file
427
Esiur/Resource/Instance.cs
Normal file
@ -0,0 +1,427 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Reflection;
|
||||
using Esiur.Net.IIP;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Security.Permissions;
|
||||
using Esiur.Resource.Template;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public class Instance
|
||||
{
|
||||
string name;
|
||||
|
||||
AutoList<IResource, Instance> children;// = new AutoList<IResource, Instance>();
|
||||
IResource resource;
|
||||
IStore store;
|
||||
AutoList<IResource, Instance> parents;// = new AutoList<IResource>();
|
||||
bool inherit;
|
||||
ResourceTemplate template;
|
||||
|
||||
AutoList<IPermissionManager, Instance> managers;// = new AutoList<IPermissionManager, Instance>();
|
||||
|
||||
public delegate void ResourceModifiedEvent(IResource resource, string propertyName, object newValue, object oldValue);
|
||||
public delegate void ResourceEventOccurredEvent(IResource resource, string eventName, string[] receivers, object[] args);
|
||||
public delegate void ResourceDestroyedEvent(IResource resource);
|
||||
|
||||
public event ResourceModifiedEvent ResourceModified;
|
||||
public event ResourceEventOccurredEvent ResourceEventOccured;
|
||||
public event ResourceDestroyedEvent ResourceDestroyed;
|
||||
|
||||
KeyList<string, object> attributes = new KeyList<string, object>();
|
||||
|
||||
List<uint> ages = new List<uint>();
|
||||
private uint age;
|
||||
|
||||
uint id;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Instance attributes are custom properties associated with the instance, a place to store information by IStore.
|
||||
/// </summary>
|
||||
public KeyList<string, object> Attributes
|
||||
{
|
||||
get
|
||||
{
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the age of a given property index.
|
||||
/// </summary>
|
||||
/// <param name="index">Zero-based property index.</param>
|
||||
/// <returns>Age.</returns>
|
||||
public uint GetAge(byte index)
|
||||
{
|
||||
if (index < ages.Count)
|
||||
return ages[index];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Age of the instance, increments by 1 in every modification.
|
||||
/// </summary>
|
||||
public uint Age
|
||||
{
|
||||
get { return age; }
|
||||
internal set { age = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instance Id.
|
||||
/// </summary>
|
||||
public uint Id
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import properties from bytes array.
|
||||
/// </summary>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
public bool Deserialize(object[] properties)
|
||||
{
|
||||
foreach (var pt in template.Properties)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
if (!(properties[pt.Index] is NotModified))
|
||||
pi.SetValue(resource, properties[pt.Index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export all properties with ResourceProperty attributed as bytes array.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object[] Serialize()
|
||||
{
|
||||
List<object> props = new List<object>();
|
||||
|
||||
foreach (var pt in template.Properties)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
|
||||
#else
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
#endif
|
||||
var rt = pi.GetValue(resource, null);
|
||||
props.Add(rt);
|
||||
}
|
||||
|
||||
return props.ToArray();
|
||||
}
|
||||
/*
|
||||
public bool Deserialize(byte[] data, uint offset, uint length)
|
||||
{
|
||||
|
||||
var props = Codec.ParseValues(data, offset, length);
|
||||
Deserialize(props);
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
public byte[] Serialize(bool includeLength = false, DistributedConnection sender = null)
|
||||
{
|
||||
|
||||
//var bl = new BinaryList();
|
||||
List<object> props = new List<object>();
|
||||
|
||||
foreach (var pt in template.Properties)
|
||||
{
|
||||
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
|
||||
var rt = pi.GetValue(resource, null);
|
||||
|
||||
// this is a cool hack to let the property know the sender
|
||||
if (rt is Func<DistributedConnection, object>)
|
||||
rt = (rt as Func<DistributedConnection, object>)(sender);
|
||||
|
||||
props.Add(rt);
|
||||
|
||||
}
|
||||
|
||||
if (includeLength)
|
||||
{
|
||||
return Codec.Compose(props.ToArray(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var rt = Codec.Compose(props.ToArray(), false);
|
||||
return DC.Clip(rt, 4, (uint)(rt.Length - 4));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] StorageSerialize()
|
||||
{
|
||||
|
||||
var props = new List<object>();
|
||||
|
||||
foreach(var pt in template.Properties)
|
||||
{
|
||||
if (!pt.Storable)
|
||||
continue;
|
||||
|
||||
var pi = resource.GetType().GetProperty(pt.Name);
|
||||
|
||||
if (!pi.CanWrite)
|
||||
continue;
|
||||
|
||||
var rt = pi.GetValue(resource, null);
|
||||
|
||||
props.Add(rt);
|
||||
|
||||
}
|
||||
|
||||
return Codec.Compose(props.ToArray(), false);
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// If True, the instance can be stored to disk.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsStorable()
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
var attrs = resource.GetType().GetTypeInfo().GetCustomAttributes(typeof(Storable), true).ToArray();
|
||||
#else
|
||||
var attrs = resource.GetType().GetCustomAttributes(typeof(Storable), true);
|
||||
#endif
|
||||
return attrs.Length > 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Notify listeners that a property was modified.
|
||||
/// </summary>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <param name="newValue"></param>
|
||||
/// <param name="oldValue"></param>
|
||||
public void Modified([CallerMemberName] string propertyName = "", object newValue = null, object oldValue = null)
|
||||
{
|
||||
if (newValue == null)
|
||||
{
|
||||
object val;
|
||||
if (GetPropertyValue(propertyName, out val))
|
||||
ResourceModified?.Invoke(resource, propertyName, val, oldValue);
|
||||
}
|
||||
else
|
||||
ResourceModified?.Invoke(resource, propertyName, newValue, oldValue);
|
||||
}
|
||||
|
||||
internal void EmitResourceEvent(string name, string[] receivers, object[] args)
|
||||
{
|
||||
ResourceEventOccured?.Invoke(resource, name, receivers, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a given property by name.
|
||||
/// </summary>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="value">Output value</param>
|
||||
/// <returns>True, if the resource has the property.</returns>
|
||||
public bool GetPropertyValue(string name, out object value)
|
||||
{
|
||||
#if NETSTANDARD1_5
|
||||
PropertyInfo pi = resource.GetType().GetTypeInfo().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
|
||||
#else
|
||||
PropertyInfo pi = resource.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
#endif
|
||||
|
||||
if (pi != null)
|
||||
{
|
||||
|
||||
#if NETSTANDARD1_5
|
||||
object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false).ToArray();
|
||||
|
||||
#else
|
||||
object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false);
|
||||
#endif
|
||||
|
||||
if (ca.Length > 0)
|
||||
{
|
||||
value = pi.GetValue(resource, null);
|
||||
//if (value is Func<IManager, object>)
|
||||
// value = (value as Func<IManager, object>)(sender);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public bool Inherit
|
||||
{
|
||||
get { return inherit; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of parents.
|
||||
/// </summary>
|
||||
public AutoList<IResource, Instance> Parents
|
||||
{
|
||||
get { return parents; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store responsible for creating and keeping the resource.
|
||||
/// </summary>
|
||||
public IStore Store
|
||||
{
|
||||
get { return store; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of children.
|
||||
/// </summary>
|
||||
public AutoList<IResource, Instance> Children
|
||||
{
|
||||
get { return children; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unique and permanent link to the resource.
|
||||
/// </summary>
|
||||
public string Link
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.store != null)
|
||||
return this.store.Link(this.resource);
|
||||
else
|
||||
{
|
||||
var l = new List<string>();
|
||||
//l.Add(name);
|
||||
|
||||
var p = this.resource; // parents.First();
|
||||
|
||||
while (true)
|
||||
{
|
||||
l.Insert(0, p.Instance.name);
|
||||
|
||||
if (p.Instance.parents.Count == 0)
|
||||
break;
|
||||
|
||||
p = p.Instance.parents.First();
|
||||
}
|
||||
|
||||
return String.Join("/", l.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instance name.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resource managed by this instance.
|
||||
/// </summary>
|
||||
public IResource Resource
|
||||
{
|
||||
get { return resource; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource template describes the properties, functions and events of the resource.
|
||||
/// </summary>
|
||||
public ResourceTemplate Template
|
||||
{
|
||||
get { return template; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create new instance.
|
||||
/// </summary>
|
||||
/// <param name="id">Instance Id.</param>
|
||||
/// <param name="name">Name of the instance.</param>
|
||||
/// <param name="resource">Resource to manage.</param>
|
||||
/// <param name="store">Store responsible for the resource.</param>
|
||||
public Instance(uint id, string name, IResource resource, IStore store)
|
||||
{
|
||||
this.store = store;
|
||||
this.resource = resource;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
|
||||
children = new AutoList<IResource, Instance>(this);
|
||||
parents = new AutoList<IResource, Instance>(this);
|
||||
managers = new AutoList<IPermissionManager, Instance>(this);
|
||||
children.OnAdd += Children_OnAdd;
|
||||
children.OnRemoved += Children_OnRemoved;
|
||||
|
||||
resource.OnDestroy += Resource_OnDestroy;
|
||||
|
||||
template = Warehouse.GetTemplate(resource.GetType());
|
||||
|
||||
|
||||
// set ages
|
||||
for (byte i = 0; i < template.Properties.Length; i++)
|
||||
ages.Add(0);
|
||||
|
||||
|
||||
// connect events
|
||||
Type t = resource.GetType();
|
||||
|
||||
#if NETSTANDARD1_5
|
||||
var events = t.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
|
||||
#else
|
||||
var events = t.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
#endif
|
||||
|
||||
foreach (var evt in events)
|
||||
{
|
||||
if (evt.EventHandlerType != typeof(ResourceEventHanlder))
|
||||
continue;
|
||||
|
||||
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
|
||||
|
||||
if (ca.Length == 0)
|
||||
continue;
|
||||
|
||||
ResourceEventHanlder proxyDelegate = (receivers, args) => EmitResourceEvent(evt.Name, receivers, args);
|
||||
evt.AddEventHandler(resource, proxyDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
private void Children_OnRemoved(Instance parent, IResource value)
|
||||
{
|
||||
value.Instance.parents.Remove(resource);
|
||||
}
|
||||
|
||||
private void Children_OnAdd(Instance parent, IResource value)
|
||||
{
|
||||
value.Instance.parents.Add(resource);
|
||||
}
|
||||
|
||||
private void Resource_OnDestroy(object sender)
|
||||
{
|
||||
ResourceDestroyed?.Invoke((IResource)sender);
|
||||
}
|
||||
}
|
||||
}
|
30
Esiur/Resource/ResourceEvent.cs
Normal file
30
Esiur/Resource/ResourceEvent.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
|
||||
[AttributeUsage(AttributeTargets.Event)]
|
||||
public class ResourceEvent : System.Attribute
|
||||
{
|
||||
|
||||
string expansion;
|
||||
|
||||
public string Expansion
|
||||
{
|
||||
get
|
||||
{
|
||||
return expansion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ResourceEvent(string expansion = null)
|
||||
{
|
||||
this.expansion = expansion;
|
||||
}
|
||||
}
|
||||
}
|
13
Esiur/Resource/ResourceEventHandler.cs
Normal file
13
Esiur/Resource/ResourceEventHandler.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public delegate void ResourceEventHanlder(string[] receivers, params object[] args);
|
||||
|
||||
}
|
28
Esiur/Resource/ResourceFunction.cs
Normal file
28
Esiur/Resource/ResourceFunction.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class ResourceFunction : System.Attribute
|
||||
{
|
||||
private string expansion = null;
|
||||
|
||||
public string Expansion
|
||||
{
|
||||
get
|
||||
{
|
||||
return expansion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ResourceFunction(string expansion = null)
|
||||
{
|
||||
this.expansion = expansion;
|
||||
}
|
||||
}
|
||||
}
|
39
Esiur/Resource/ResourceProperty.cs
Normal file
39
Esiur/Resource/ResourceProperty.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class ResourceProperty : System.Attribute
|
||||
{
|
||||
string readExpansion;
|
||||
string writeExpansion;
|
||||
|
||||
|
||||
public string ReadExpansion
|
||||
{
|
||||
get
|
||||
{
|
||||
return readExpansion;
|
||||
}
|
||||
}
|
||||
|
||||
public string WriteExpansion
|
||||
{
|
||||
get
|
||||
{
|
||||
return writeExpansion;
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceProperty(string readExpansion = null, string writeExpansion = null)
|
||||
{
|
||||
this.readExpansion = readExpansion;
|
||||
this.writeExpansion = writeExpansion;
|
||||
}
|
||||
}
|
||||
}
|
19
Esiur/Resource/ResourceTrigger.cs
Normal file
19
Esiur/Resource/ResourceTrigger.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
public enum ResourceTrigger : int
|
||||
{
|
||||
Loaded = 0,
|
||||
Initialize,
|
||||
Terminate,
|
||||
Configure,
|
||||
SystemInitialized,
|
||||
SystemTerminated,
|
||||
SystemReload,
|
||||
}
|
||||
}
|
48
Esiur/Resource/Storable.cs
Normal file
48
Esiur/Resource/Storable.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class Storable : global::System.Attribute
|
||||
{
|
||||
public delegate object SerializerFunction(object value);
|
||||
public delegate object DeserializerFunction(object data);
|
||||
|
||||
SerializerFunction serializer;
|
||||
DeserializerFunction deserializer;
|
||||
DataType type;
|
||||
|
||||
public Storable()
|
||||
{
|
||||
type = DataType.Void;
|
||||
}
|
||||
|
||||
public DeserializerFunction Deserializer
|
||||
{
|
||||
get { return deserializer; }
|
||||
}
|
||||
|
||||
public SerializerFunction Serializer
|
||||
{
|
||||
get { return serializer; }
|
||||
}
|
||||
|
||||
public Storable(DataType type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Storable(DataType type, SerializerFunction serializer, DeserializerFunction deserializer)
|
||||
{
|
||||
this.type = type;
|
||||
this.serializer = serializer;
|
||||
this.deserializer = deserializer;
|
||||
}
|
||||
}
|
||||
}
|
33
Esiur/Resource/Template/EventTemplate.cs
Normal file
33
Esiur/Resource/Template/EventTemplate.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource.Template
|
||||
{
|
||||
public class EventTemplate : MemberTemplate
|
||||
{
|
||||
public string Expansion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override byte[] Compose()
|
||||
{
|
||||
var name = base.Compose();
|
||||
|
||||
if (Expansion != null)
|
||||
{
|
||||
var exp = DC.ToBytes(Expansion);
|
||||
return BinaryList.ToBytes((byte)0x50, exp.Length, exp, (byte)name.Length, name);
|
||||
}
|
||||
else
|
||||
return BinaryList.ToBytes((byte)0x40, (byte)name.Length, name);
|
||||
}
|
||||
|
||||
public EventTemplate() { Type = MemberType.Event; }
|
||||
}
|
||||
}
|
42
Esiur/Resource/Template/FunctionTemplate.cs
Normal file
42
Esiur/Resource/Template/FunctionTemplate.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource.Template
|
||||
{
|
||||
public class FunctionTemplate : MemberTemplate
|
||||
{
|
||||
|
||||
public string Expansion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsVoid
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public override byte[] Compose()
|
||||
{
|
||||
var name = base.Compose();
|
||||
|
||||
if (Expansion != null)
|
||||
{
|
||||
var exp = DC.ToBytes(Expansion);
|
||||
return BinaryList.ToBytes((byte)(0x10 | (IsVoid ? 0x8 : 0x0)), exp.Length, exp, (byte)name.Length, name);
|
||||
}
|
||||
else
|
||||
return BinaryList.ToBytes((byte)(IsVoid ? 0x8 : 0x0), (byte)name.Length, name);
|
||||
}
|
||||
|
||||
|
||||
public FunctionTemplate() { Type = MemberType.Function; }
|
||||
}
|
||||
}
|
29
Esiur/Resource/Template/MemberTemplate.cs
Normal file
29
Esiur/Resource/Template/MemberTemplate.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource.Template
|
||||
{
|
||||
public class MemberTemplate
|
||||
{
|
||||
public enum MemberType
|
||||
{
|
||||
Function = 0,
|
||||
Property = 1,
|
||||
Event = 2,
|
||||
}
|
||||
|
||||
public byte Index { get; set; }
|
||||
public string Name { get; set; }
|
||||
public MemberType Type { get; set; }
|
||||
|
||||
public virtual byte[] Compose()
|
||||
{
|
||||
return DC.ToBytes(Name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
71
Esiur/Resource/Template/PropertyTemplate.cs
Normal file
71
Esiur/Resource/Template/PropertyTemplate.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource.Template
|
||||
{
|
||||
public class PropertyTemplate : MemberTemplate
|
||||
{
|
||||
public enum PropertyPermission:byte
|
||||
{
|
||||
Read = 1,
|
||||
Write,
|
||||
ReadWrite
|
||||
}
|
||||
|
||||
//bool ReadOnly;
|
||||
//IIPTypes::DataType ReturnType;
|
||||
public PropertyPermission Permission {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string ReadExpansion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string WriteExpansion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool Storable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public override byte[] Compose()
|
||||
{
|
||||
var name = base.Compose();
|
||||
|
||||
if (WriteExpansion != null && ReadExpansion != null)
|
||||
{
|
||||
var rexp = DC.ToBytes(ReadExpansion);
|
||||
var wexp = DC.ToBytes(WriteExpansion);
|
||||
return BinaryList.ToBytes((byte)(0x38 | (byte)Permission), wexp.Length, wexp, rexp.Length, rexp, (byte)name.Length, name);
|
||||
}
|
||||
else if (WriteExpansion != null)
|
||||
{
|
||||
var wexp = DC.ToBytes(WriteExpansion);
|
||||
return BinaryList.ToBytes((byte)(0x30 | (byte)Permission), wexp.Length, wexp, (byte)name.Length, name);
|
||||
}
|
||||
else if (ReadExpansion != null)
|
||||
{
|
||||
var rexp = DC.ToBytes(ReadExpansion);
|
||||
return BinaryList.ToBytes((byte)(0x28 | (byte)Permission), rexp.Length, rexp, (byte)name.Length, name);
|
||||
}
|
||||
else
|
||||
return BinaryList.ToBytes((byte)(0x20 | (byte)Permission), (byte)name.Length, name);
|
||||
}
|
||||
|
||||
public PropertyTemplate() { Type = MemberType.Property; }
|
||||
}
|
||||
}
|
359
Esiur/Resource/Template/ResourceTemplate.cs
Normal file
359
Esiur/Resource/Template/ResourceTemplate.cs
Normal file
@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Esiur.Resource.Template
|
||||
{
|
||||
public class ResourceTemplate
|
||||
{
|
||||
Guid classId;
|
||||
string className;
|
||||
List<MemberTemplate> members = new List<MemberTemplate>();
|
||||
List<FunctionTemplate> functions = new List<FunctionTemplate>();
|
||||
List<EventTemplate> events = new List<EventTemplate>();
|
||||
List<PropertyTemplate> properties = new List<PropertyTemplate>();
|
||||
int version;
|
||||
//bool isReady;
|
||||
|
||||
byte[] content;
|
||||
|
||||
public byte[] Content
|
||||
{
|
||||
get { return content; }
|
||||
}
|
||||
|
||||
public MemberTemplate GetMemberTemplate(MemberInfo member)
|
||||
{
|
||||
if (member is MethodInfo)
|
||||
return GetFunctionTemplate(member.Name);
|
||||
else if (member is EventInfo)
|
||||
return GetEventTemplate(member.Name);
|
||||
else if (member is PropertyInfo)
|
||||
return GetPropertyTemplate(member.Name);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public EventTemplate GetEventTemplate(string eventName)
|
||||
{
|
||||
foreach (var i in events)
|
||||
if (i.Name == eventName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public EventTemplate GetEventTemplate(byte index)
|
||||
{
|
||||
foreach (var i in events)
|
||||
if (i.Index == index)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public FunctionTemplate GetFunctionTemplate(string functionName)
|
||||
{
|
||||
foreach (var i in functions)
|
||||
if (i.Name == functionName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
public FunctionTemplate GetFunctionTemplate(byte index)
|
||||
{
|
||||
foreach (var i in functions)
|
||||
if (i.Index == index)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public PropertyTemplate GetPropertyTemplate(byte index)
|
||||
{
|
||||
foreach (var i in properties)
|
||||
if (i.Index == index)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public PropertyTemplate GetPropertyTemplate(string propertyName)
|
||||
{
|
||||
foreach (var i in properties)
|
||||
if (i.Name == propertyName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Guid ClassId
|
||||
{
|
||||
get { return classId; }
|
||||
}
|
||||
public string ClassName
|
||||
{
|
||||
get { return className; }
|
||||
}
|
||||
|
||||
public MemberTemplate[] Methods
|
||||
{
|
||||
get{return members.ToArray();}
|
||||
}
|
||||
|
||||
public FunctionTemplate[] Functions
|
||||
{
|
||||
get { return functions.ToArray(); }
|
||||
}
|
||||
|
||||
public EventTemplate[] Events
|
||||
{
|
||||
get { return events.ToArray(); }
|
||||
}
|
||||
|
||||
public PropertyTemplate[] Properties
|
||||
{
|
||||
get { return properties.ToArray(); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ResourceTemplate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ResourceTemplate(Type type)
|
||||
{
|
||||
// set guid
|
||||
|
||||
var typeName = Encoding.UTF8.GetBytes(type.FullName);
|
||||
var hash = SHA256.Create().ComputeHash(typeName).Clip(0, 16);
|
||||
|
||||
classId = new Guid(hash);
|
||||
className = type.FullName;
|
||||
|
||||
|
||||
#if NETSTANDARD1_5
|
||||
PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
EventInfo[] eventsInfo = type.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
MethodInfo[] methodsInfo = type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
|
||||
#else
|
||||
PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
#endif
|
||||
|
||||
//byte currentIndex = 0;
|
||||
|
||||
byte i = 0;
|
||||
|
||||
foreach (var pi in propsInfo)
|
||||
{
|
||||
var ps = (ResourceProperty[])pi.GetCustomAttributes(typeof(ResourceProperty), true);
|
||||
if (ps.Length > 0)
|
||||
{
|
||||
var pt = new PropertyTemplate();
|
||||
pt.Name = pi.Name;
|
||||
pt.Index = i++;
|
||||
pt.ReadExpansion = ps[0].ReadExpansion;
|
||||
pt.WriteExpansion = ps[0].WriteExpansion;
|
||||
properties.Add(pt);
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
foreach (var ei in eventsInfo)
|
||||
{
|
||||
var es = (ResourceEvent[])ei.GetCustomAttributes(typeof(ResourceEvent), true);
|
||||
if (es.Length > 0)
|
||||
{
|
||||
var et = new EventTemplate();
|
||||
et.Name = ei.Name;
|
||||
et.Index = i++;
|
||||
et.Expansion = es[0].Expansion;
|
||||
events.Add(et);
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
foreach (MethodInfo mi in methodsInfo)
|
||||
{
|
||||
var fs = (ResourceFunction[])mi.GetCustomAttributes(typeof(ResourceFunction), true);
|
||||
if (fs.Length > 0)
|
||||
{
|
||||
var ft = new FunctionTemplate();
|
||||
ft.Name = mi.Name;
|
||||
ft.Index = i++;
|
||||
ft.IsVoid = mi.ReturnType == typeof(void);
|
||||
ft.Expansion = fs[0].Expansion;
|
||||
functions.Add(ft);
|
||||
}
|
||||
}
|
||||
|
||||
// append signals
|
||||
for (i = 0; i < events.Count; i++)
|
||||
members.Add(events[i]);
|
||||
// append slots
|
||||
for (i = 0; i < functions.Count; i++)
|
||||
members.Add(functions[i]);
|
||||
// append properties
|
||||
for (i = 0; i < properties.Count; i++)
|
||||
members.Add(properties[i]);
|
||||
|
||||
// bake it binarily
|
||||
var b = new BinaryList();
|
||||
b.Append(classId);
|
||||
b.Append((byte)className.Length, className);
|
||||
b.Append(version);
|
||||
b.Append((ushort)members.Count);
|
||||
foreach (var ft in functions)
|
||||
b.Append(ft.Compose());
|
||||
foreach (var pt in properties)
|
||||
b.Append(pt.Compose());
|
||||
foreach (var et in events)
|
||||
b.Append(et.Compose());
|
||||
content = b.ToArray();
|
||||
}
|
||||
|
||||
public static ResourceTemplate Parse(byte[] data)
|
||||
{
|
||||
return Parse(data, 0, (uint)data.Length);
|
||||
}
|
||||
|
||||
|
||||
public static ResourceTemplate Parse(byte[] data, uint offset, uint contentLength)
|
||||
{
|
||||
|
||||
uint ends = offset + contentLength;
|
||||
|
||||
uint oOffset = offset;
|
||||
|
||||
// start parsing...
|
||||
|
||||
var od = new ResourceTemplate();
|
||||
od.content = data.Clip(offset, contentLength);
|
||||
|
||||
od.classId = data.GetGuid(offset);
|
||||
offset += 16;
|
||||
od.className = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
od.version = data.GetInt32(offset);
|
||||
offset += 4;
|
||||
|
||||
ushort methodsCount = data.GetUInt16(offset);
|
||||
offset += 2;
|
||||
|
||||
byte functionIndex = 0;
|
||||
byte propertyIndex = 0;
|
||||
byte eventIndex = 0;
|
||||
|
||||
for (int i = 0; i < methodsCount; i++)
|
||||
{
|
||||
var type = data[offset] >> 5;
|
||||
|
||||
if (type == 0) // function
|
||||
{
|
||||
var ft = new FunctionTemplate();
|
||||
ft.Index = functionIndex++;
|
||||
var expansion = ((data[offset] & 0x10) == 0x10);
|
||||
ft.IsVoid = ((data[offset++] & 0x08) == 0x08);
|
||||
ft.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
if (expansion) // expansion ?
|
||||
{
|
||||
var cs = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
ft.Expansion = data.GetString(offset, cs);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
od.functions.Add(ft);
|
||||
}
|
||||
else if (type == 1) // property
|
||||
{
|
||||
|
||||
var pt = new PropertyTemplate();
|
||||
pt.Index = propertyIndex++;
|
||||
var readExpansion = ((data[offset] & 0x8) == 0x8);
|
||||
var writeExpansion = ((data[offset] & 0x10) == 0x10);
|
||||
pt.Permission = (PropertyTemplate.PropertyPermission)((data[offset++] >> 1) & 0x3);
|
||||
pt.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
if (readExpansion) // expansion ?
|
||||
{
|
||||
var cs = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
pt.ReadExpansion = data.GetString(offset, cs);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
if (writeExpansion) // expansion ?
|
||||
{
|
||||
var cs = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
pt.WriteExpansion = data.GetString(offset, cs);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
od.properties.Add(pt);
|
||||
}
|
||||
else if (type == 2) // Event
|
||||
{
|
||||
var et = new EventTemplate();
|
||||
et.Index = eventIndex++;
|
||||
var expansion = ((data[offset++] & 0x10) == 0x10);
|
||||
|
||||
et.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
if (expansion) // expansion ?
|
||||
{
|
||||
var cs = data.GetUInt32(offset);
|
||||
offset += 4;
|
||||
et.Expansion = data.GetString(offset, cs);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
od.events.Add(et);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// append signals
|
||||
for (int i = 0; i < od.events.Count; i++)
|
||||
od.members.Add(od.events[i]);
|
||||
// append slots
|
||||
for (int i = 0; i < od.functions.Count; i++)
|
||||
od.members.Add(od.functions[i]);
|
||||
// append properties
|
||||
for (int i = 0; i < od.properties.Count; i++)
|
||||
od.members.Add(od.properties[i]);
|
||||
|
||||
|
||||
//od.isReady = true;
|
||||
/*
|
||||
var oo = owner.Socket.Engine.GetObjectDescription(od.GUID);
|
||||
if (oo != null)
|
||||
{
|
||||
Console.WriteLine("Already there ! description");
|
||||
return oo;
|
||||
}
|
||||
else
|
||||
{
|
||||
owner.Socket.Engine.AddObjectDescription(od);
|
||||
return od;
|
||||
}
|
||||
*/
|
||||
|
||||
return od;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
258
Esiur/Resource/Warehouse.cs
Normal file
258
Esiur/Resource/Warehouse.cs
Normal file
@ -0,0 +1,258 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource.Template;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Resource
|
||||
{
|
||||
// Centeral Resource Issuer
|
||||
public static class Warehouse
|
||||
{
|
||||
//static byte prefixCounter;
|
||||
|
||||
static List<IStore> stores = new List<IStore>();
|
||||
static Dictionary<uint, IResource> resources = new Dictionary<uint, IResource>();
|
||||
static uint resourceCounter = 0;
|
||||
|
||||
static KeyList<Guid, ResourceTemplate> templates = new KeyList<Guid, ResourceTemplate>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a store by its name.
|
||||
/// </summary>
|
||||
/// <param name="name">Store instance name</param>
|
||||
/// <returns></returns>
|
||||
public static IStore GetStore(string name)
|
||||
{
|
||||
foreach (var s in stores)
|
||||
if (s.Instance.Name == name)
|
||||
return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource by instance Id.
|
||||
/// </summary>
|
||||
/// <param name="id">Instance Id</param>
|
||||
/// <returns></returns>
|
||||
public static AsyncReply<IResource> Get(uint id)
|
||||
{
|
||||
if (resources.ContainsKey(id))
|
||||
return new AsyncReply<IResource>(resources[id]);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the warehouse.
|
||||
/// This function issues the initialize trigger to all stores and resources.
|
||||
/// </summary>
|
||||
/// <returns>True, if no problem occurred.</returns>
|
||||
public static AsyncReply<bool> Open()
|
||||
{
|
||||
var bag = new AsyncBag<bool>();
|
||||
|
||||
foreach (var store in stores)
|
||||
bag.Add(store.Trigger(ResourceTrigger.Initialize));
|
||||
|
||||
foreach (var store in stores)
|
||||
bag.Add(store.Trigger(ResourceTrigger.SystemInitialized));
|
||||
|
||||
bag.Seal();
|
||||
|
||||
var rt = new AsyncReply<bool>();
|
||||
bag.Then((x) =>
|
||||
{
|
||||
foreach(var b in x)
|
||||
if (!b)
|
||||
{
|
||||
rt.Trigger(false);
|
||||
return;
|
||||
}
|
||||
|
||||
rt.Trigger(true);
|
||||
});
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the warehouse.
|
||||
/// This function issues terminate trigger to all resources and stores.
|
||||
/// </summary>
|
||||
/// <returns>True, if no problem occurred.</returns>
|
||||
public static AsyncReply<bool> Close()
|
||||
{
|
||||
|
||||
var bag = new AsyncBag<bool>();
|
||||
|
||||
foreach (var resource in resources.Values)
|
||||
if (!(resource is IStore))
|
||||
bag.Add(resource.Trigger(ResourceTrigger.Terminate));
|
||||
|
||||
foreach (var store in stores)
|
||||
bag.Add(store.Trigger(ResourceTrigger.Terminate));
|
||||
|
||||
foreach (var resource in resources.Values)
|
||||
if (!(resource is IStore))
|
||||
bag.Add(resource.Trigger(ResourceTrigger.SystemTerminated));
|
||||
|
||||
foreach (var store in stores)
|
||||
bag.Add(store.Trigger(ResourceTrigger.SystemTerminated));
|
||||
|
||||
bag.Seal();
|
||||
|
||||
var rt = new AsyncReply<bool>();
|
||||
bag.Then((x) =>
|
||||
{
|
||||
foreach (var b in x)
|
||||
if (!b)
|
||||
{
|
||||
rt.Trigger(false);
|
||||
return;
|
||||
}
|
||||
|
||||
rt.Trigger(true);
|
||||
});
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource by its path.
|
||||
/// Resource path is sperated by '/' character, e.g. "system/http".
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns>Resource instance.</returns>
|
||||
public static AsyncReply<IResource> Get(string path)
|
||||
{
|
||||
|
||||
var p = path.Split('/');
|
||||
IResource res;
|
||||
|
||||
foreach(IStore d in stores)
|
||||
if (p[0] == d.Instance.Name)
|
||||
{
|
||||
var i = 1;
|
||||
res = d;
|
||||
while(p.Length > i)
|
||||
{
|
||||
var si = i;
|
||||
|
||||
foreach (IResource r in res.Instance.Children)
|
||||
if (r.Instance.Name == p[i])
|
||||
{
|
||||
i++;
|
||||
res = r;
|
||||
break;
|
||||
}
|
||||
|
||||
if (si == i)
|
||||
// not found, ask the store
|
||||
return d.Get(path.Substring(p[0].Length + 1));
|
||||
}
|
||||
|
||||
return new AsyncReply<IResource>(res);
|
||||
}
|
||||
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Put a resource in the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource instance.</param>
|
||||
/// <param name="name">Resource name.</param>
|
||||
/// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param>
|
||||
/// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param>
|
||||
public static void Put(IResource resource, string name, IStore store = null, IResource parent = null)
|
||||
{
|
||||
resource.Instance = new Instance(resourceCounter++, name, resource, store);
|
||||
|
||||
if (store == parent)
|
||||
parent = null;
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
if (!(resource is IStore))
|
||||
store.Instance.Children.Add(resource);
|
||||
}
|
||||
else
|
||||
parent.Instance.Children.Add(resource);
|
||||
|
||||
|
||||
if (resource is IStore)
|
||||
stores.Add(resource as IStore);
|
||||
else
|
||||
store.Put(resource);
|
||||
|
||||
resources.Add(resource.Instance.Id, resource);
|
||||
}
|
||||
|
||||
public static T New<T>(string name, IStore store = null, IResource parent = null)
|
||||
{
|
||||
var res = Activator.CreateInstance(typeof(T)) as IResource;
|
||||
Put(res, name, store, parent);
|
||||
return (T)res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Put a resource template in the templates warehouse.
|
||||
/// </summary>
|
||||
/// <param name="template">Resource template.</param>
|
||||
public static void PutTemplate(ResourceTemplate template)
|
||||
{
|
||||
if (templates.ContainsKey(template.ClassId))
|
||||
templates.Add(template.ClassId, template);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a template by type from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="type">.Net type.</param>
|
||||
/// <returns>Resource template.</returns>
|
||||
public static ResourceTemplate GetTemplate(Type type)
|
||||
{
|
||||
// loaded ?
|
||||
foreach (var t in templates.Values)
|
||||
if (t.ClassName == type.FullName)
|
||||
return t;
|
||||
|
||||
var template = new ResourceTemplate(type);
|
||||
templates.Add(template.ClassId, template);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a template by class Id from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="classId">Class Id.</param>
|
||||
/// <returns>Resource template.</returns>
|
||||
public static AsyncReply<ResourceTemplate> GetTemplate(Guid classId)
|
||||
{
|
||||
if (templates.ContainsKey(classId))
|
||||
return new AsyncReply<ResourceTemplate>(templates[classId]);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a template by class name from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
|
||||
/// </summary>
|
||||
/// <param name="className">Class name.</param>
|
||||
/// <returns>Resource template.</returns>
|
||||
public static AsyncReply<ResourceTemplate> GetTemplate(string className)
|
||||
{
|
||||
foreach (var t in templates.Values)
|
||||
if (t.ClassName == className)
|
||||
return new AsyncReply<ResourceTemplate>(t);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
17
Esiur/Security/Authority/AlienAuthentication.cs
Normal file
17
Esiur/Security/Authority/AlienAuthentication.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class AlienAuthentication : Authentication
|
||||
{
|
||||
public AlienAuthentication(Certificate certificate, AuthenticationState state) :
|
||||
base(certificate, state, AuthenticationType.Alien)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
35
Esiur/Security/Authority/Authentication.cs
Normal file
35
Esiur/Security/Authority/Authentication.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class Authentication
|
||||
{
|
||||
Certificate certificate;
|
||||
AuthenticationState state;
|
||||
AuthenticationType type;
|
||||
|
||||
public Certificate Certificate
|
||||
{
|
||||
get { return certificate; }
|
||||
}
|
||||
|
||||
public AuthenticationState State
|
||||
{
|
||||
get { return state; }
|
||||
}
|
||||
|
||||
public AuthenticationType Type
|
||||
{
|
||||
get { return type; }
|
||||
}
|
||||
|
||||
public Authentication(Certificate certificate, AuthenticationState state, AuthenticationType type)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
18
Esiur/Security/Authority/AuthenticationState.cs
Normal file
18
Esiur/Security/Authority/AuthenticationState.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public enum AuthenticationState : int
|
||||
{
|
||||
Denied = 0x1,
|
||||
Succeeded = 0x2,
|
||||
Blocked = 0x4,
|
||||
Rejected = 0x8,
|
||||
NeedsUpdate = 0x10,
|
||||
NotFound = 0x20
|
||||
}
|
||||
}
|
16
Esiur/Security/Authority/AuthenticationType.cs
Normal file
16
Esiur/Security/Authority/AuthenticationType.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public enum AuthenticationType
|
||||
{
|
||||
Host,
|
||||
CoHost,
|
||||
Client,
|
||||
Alien
|
||||
}
|
||||
}
|
163
Esiur/Security/Authority/CACertificate.cs
Normal file
163
Esiur/Security/Authority/CACertificate.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Security.Cryptography;
|
||||
using Esiur.Security.Integrity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class CACertificate : Certificate
|
||||
{
|
||||
|
||||
string name;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public CACertificate(byte[] data, uint offset, uint length, bool privateKeyIncluded = false)
|
||||
:base(0, DateTime.MinValue, DateTime.MinValue, HashFunctionType.MD5)
|
||||
{
|
||||
|
||||
uint oOffset = offset;
|
||||
|
||||
this.id = DC.GetUInt64(data, offset);
|
||||
offset += 8;
|
||||
this.issueDate = DC.GetDateTime(data, offset);
|
||||
offset += 8;
|
||||
this.expireDate = DC.GetDateTime(data, offset);
|
||||
offset += 8;
|
||||
this.hashFunction = (HashFunctionType)(data[offset++] >> 4);
|
||||
|
||||
|
||||
this.name = (Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]));
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
|
||||
var aea = (AsymetricEncryptionAlgorithmType)(data[offset] >> 5);
|
||||
|
||||
if (aea == AsymetricEncryptionAlgorithmType.RSA)
|
||||
{
|
||||
var key = new RSAParameters();
|
||||
uint exponentLength = (uint)data[offset++] & 0x1F;
|
||||
|
||||
key.Exponent = DC.Clip(data, offset, exponentLength);
|
||||
|
||||
offset += exponentLength;
|
||||
|
||||
uint keySize = DC.GetUInt16(data, offset);
|
||||
offset += 2;
|
||||
|
||||
key.Modulus = DC.Clip(data, offset, keySize);
|
||||
|
||||
offset += keySize;
|
||||
|
||||
// copy cert data
|
||||
this.publicRawData = new byte[offset - oOffset];
|
||||
Buffer.BlockCopy(data, (int)oOffset, publicRawData, 0, publicRawData.Length);
|
||||
|
||||
if (privateKeyIncluded)
|
||||
{
|
||||
uint privateKeyLength = (keySize * 3) + (keySize / 2);
|
||||
uint halfKeySize = keySize / 2;
|
||||
|
||||
privateRawData = DC.Clip(data, offset, privateKeyLength);
|
||||
|
||||
key.D = DC.Clip(data, offset, keySize);
|
||||
offset += keySize;
|
||||
|
||||
key.DP = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
|
||||
key.DQ = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
|
||||
|
||||
key.InverseQ = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
|
||||
key.P = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
|
||||
key.Q = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
|
||||
}
|
||||
|
||||
// setup rsa
|
||||
this.rsa = RSA.Create();// new RSACryptoServiceProvider();
|
||||
this.rsa.ImportParameters(key);
|
||||
}
|
||||
}
|
||||
|
||||
public CACertificate(ulong id, string authorityName, DateTime issueDate, DateTime expireDate,
|
||||
HashFunctionType hashFunction = HashFunctionType.SHA1, uint ip = 0, byte[] ip6 = null)
|
||||
: base(id, issueDate, expireDate, hashFunction)
|
||||
{
|
||||
// assign type
|
||||
|
||||
BinaryList cr = new BinaryList();
|
||||
|
||||
// make header
|
||||
|
||||
cr.Append(id, issueDate, expireDate);
|
||||
|
||||
// hash function
|
||||
cr.Append((byte)((byte)hashFunction << 4));
|
||||
this.hashFunction = hashFunction;
|
||||
|
||||
// CA Name
|
||||
this.name = authorityName;
|
||||
cr.Append((byte)(authorityName.Length), Encoding.ASCII.GetBytes(authorityName));
|
||||
|
||||
// public key
|
||||
rsa = RSA.Create();// new RSACryptoServiceProvider(2048);
|
||||
rsa.KeySize = 2048;
|
||||
RSAParameters dRSAKey = rsa.ExportParameters(true);
|
||||
|
||||
|
||||
cr.Append((byte)dRSAKey.Exponent.Length, dRSAKey.Exponent, (ushort)dRSAKey.Modulus.Length, dRSAKey.Modulus);
|
||||
|
||||
|
||||
publicRawData = cr.ToArray();
|
||||
|
||||
privateRawData = DC.Merge(dRSAKey.D, dRSAKey.DP, dRSAKey.DQ, dRSAKey.InverseQ, dRSAKey.P, dRSAKey.Q);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override bool Save(string filename, bool includePrivate = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (includePrivate)
|
||||
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.CAPrivate, publicRawData, privateRawData));
|
||||
else
|
||||
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.CAPublic, publicRawData));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Serialize(bool includePrivate = false)
|
||||
{
|
||||
if (includePrivate)
|
||||
return BinaryList.ToBytes(publicRawData, privateRawData);
|
||||
else
|
||||
return publicRawData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
198
Esiur/Security/Authority/Certificate.cs
Normal file
198
Esiur/Security/Authority/Certificate.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Security.Cryptography;
|
||||
using Esiur.Security.Integrity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public abstract class Certificate
|
||||
{
|
||||
protected DateTime issueDate, expireDate;
|
||||
protected RSA rsa;
|
||||
protected Aes aes;
|
||||
|
||||
protected byte[] publicRawData;
|
||||
protected byte[] privateRawData;
|
||||
protected ulong id;
|
||||
protected HashFunctionType hashFunction;
|
||||
|
||||
public Certificate(ulong id, DateTime issueDate, DateTime expireDate, HashFunctionType hashFunction)
|
||||
{
|
||||
this.id = id;
|
||||
this.issueDate = issueDate;
|
||||
this.expireDate = expireDate;
|
||||
this.hashFunction = hashFunction;
|
||||
}
|
||||
|
||||
public ulong Id
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
public AsymetricEncryptionAlgorithmType AsymetricEncryptionAlgorithm
|
||||
{
|
||||
get { return AsymetricEncryptionAlgorithmType.RSA; }
|
||||
}
|
||||
|
||||
public byte[] AsymetricEncrypt(byte[] message)
|
||||
{
|
||||
return rsa.Encrypt(message, RSAEncryptionPadding.OaepSHA512);
|
||||
}
|
||||
|
||||
|
||||
public byte[] AsymetricEncrypt(byte[] message, uint offset, uint length)
|
||||
{
|
||||
if (message.Length != length)
|
||||
return rsa.Encrypt(DC.Clip(message, offset, length), RSAEncryptionPadding.OaepSHA512);
|
||||
else
|
||||
return rsa.Encrypt(message, RSAEncryptionPadding.OaepSHA512);
|
||||
}
|
||||
|
||||
public byte[] AsymetricDecrypt(byte[] message)
|
||||
{
|
||||
try
|
||||
{
|
||||
return rsa.Decrypt(message, RSAEncryptionPadding.OaepSHA512);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log("Certificate", LogType.Error, ex.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] AsymetricDecrypt(byte[] message, uint offset, uint length)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (message.Length != length)
|
||||
return rsa.Decrypt(DC.Clip(message, offset, length), RSAEncryptionPadding.OaepSHA512);
|
||||
else
|
||||
return rsa.Decrypt(message, RSAEncryptionPadding.OaepSHA512);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Global.Log("Certificate", LogType.Error, ex.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] SymetricEncrypt(byte[] message, uint offset, uint length)
|
||||
{
|
||||
byte[] rt = null;
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
|
||||
cs.Write(message, (int)offset, (int)length);
|
||||
|
||||
rt = ms.ToArray();
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public byte[] SymetricEncrypt(byte[] message)
|
||||
{
|
||||
return SymetricEncrypt(message, 0, (uint)message.Length);
|
||||
}
|
||||
|
||||
public byte[] SymetricDecrypt(byte[] message, uint offset, uint length)
|
||||
{
|
||||
byte[] rt = null;
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
|
||||
cs.Write(message, (int)offset, (int)length);
|
||||
|
||||
rt = ms.ToArray();
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public byte[] SymetricDecrypt(byte[] message)
|
||||
{
|
||||
return SymetricDecrypt(message, 0, (uint)message.Length);
|
||||
}
|
||||
|
||||
public byte[] Sign(byte[] message)
|
||||
{
|
||||
return Sign(message, 0, (uint)message.Length);
|
||||
}
|
||||
|
||||
public byte[] Sign(byte[] message, uint offset, uint length)
|
||||
{
|
||||
if (hashFunction == HashFunctionType.SHA1)
|
||||
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
|
||||
else if (hashFunction == HashFunctionType.MD5)
|
||||
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.MD5, RSASignaturePadding.Pkcs1);
|
||||
else if (hashFunction == HashFunctionType.SHA256)
|
||||
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
else if (hashFunction == HashFunctionType.SHA384)
|
||||
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
|
||||
else if (hashFunction == HashFunctionType.SHA512)
|
||||
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool InitializeSymetricCipher(SymetricEncryptionAlgorithmType algorithm, int keyLength, byte[] key, byte[] iv)
|
||||
{
|
||||
if (algorithm == SymetricEncryptionAlgorithmType.AES)
|
||||
{
|
||||
if (keyLength == 0) // 128 bit
|
||||
{
|
||||
aes = Aes.Create();
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public abstract bool Save(string filename, bool includePrivate = false);
|
||||
public abstract byte[] Serialize(bool includePrivate = false);
|
||||
|
||||
public static Certificate Load(string filename)
|
||||
{
|
||||
byte[] ar = File.ReadAllBytes(filename);
|
||||
var t = (CertificateType)ar[0];
|
||||
|
||||
switch (t)
|
||||
{
|
||||
case CertificateType.CAPublic:
|
||||
return new CACertificate(ar, 1, (uint)ar.Length - 1);
|
||||
case CertificateType.CAPrivate:
|
||||
return new CACertificate(ar, 1, (uint)ar.Length - 1, true);
|
||||
case CertificateType.DomainPublic:
|
||||
return new DomainCertificate(ar, 1, (uint)ar.Length - 1);
|
||||
case CertificateType.DomainPrivate:
|
||||
return new DomainCertificate(ar, 1, (uint)ar.Length - 1, true);
|
||||
case CertificateType.UserPublic:
|
||||
return new UserCertificate(ar, 1, (uint)ar.Length - 1);
|
||||
case CertificateType.UserPrivate:
|
||||
return new UserCertificate(ar, 1, (uint)ar.Length - 1, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
18
Esiur/Security/Authority/CertificateType.cs
Normal file
18
Esiur/Security/Authority/CertificateType.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public enum CertificateType
|
||||
{
|
||||
CAPublic = 0,
|
||||
CAPrivate,
|
||||
DomainPublic,
|
||||
DomainPrivate,
|
||||
UserPublic,
|
||||
UserPrivate
|
||||
}
|
||||
}
|
17
Esiur/Security/Authority/ClientAuthentication.cs
Normal file
17
Esiur/Security/Authority/ClientAuthentication.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class ClientAuthentication : Authentication
|
||||
{
|
||||
public ClientAuthentication(byte[] credentials, UserCertificate certificate, AuthenticationState state)
|
||||
: base(certificate, state, AuthenticationType.Client)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
17
Esiur/Security/Authority/CoHostAuthentication.cs
Normal file
17
Esiur/Security/Authority/CoHostAuthentication.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class CoHostAuthentication : Authentication
|
||||
{
|
||||
public CoHostAuthentication(DomainCertificate certificate, AuthenticationState state)
|
||||
: base(certificate, state, AuthenticationType.CoHost)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
220
Esiur/Security/Authority/DomainCertificate.cs
Normal file
220
Esiur/Security/Authority/DomainCertificate.cs
Normal file
@ -0,0 +1,220 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Security.Cryptography;
|
||||
using Esiur.Security.Integrity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class DomainCertificate : Certificate
|
||||
{
|
||||
uint ip;
|
||||
byte[] ip6;
|
||||
string domain;
|
||||
|
||||
//CACertificate ca;
|
||||
string caName;
|
||||
ulong caId;
|
||||
byte[] signature;
|
||||
|
||||
string authorityName;
|
||||
|
||||
public string AuthorityName
|
||||
{
|
||||
get { return authorityName; }
|
||||
}
|
||||
|
||||
public string Domain
|
||||
{
|
||||
get { return domain; }
|
||||
}
|
||||
|
||||
public byte[] Signature
|
||||
{
|
||||
get { return signature; }
|
||||
}
|
||||
|
||||
public uint IPAddress
|
||||
{
|
||||
get { return ip; }
|
||||
}
|
||||
|
||||
public byte[] IPv6Address
|
||||
{
|
||||
get { return ip6; }
|
||||
}
|
||||
|
||||
public DomainCertificate(byte[] data, uint offset, uint length, bool privateKeyIncluded = false)
|
||||
:base(0, DateTime.MinValue, DateTime.MinValue, HashFunctionType.MD5)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
this.id = DC.GetUInt64(data, offset);
|
||||
offset += 8;
|
||||
|
||||
// load IPs
|
||||
this.ip = DC.GetUInt32(data, offset);
|
||||
offset += 4;
|
||||
this.ip6 = DC.Clip(data, offset, 16);
|
||||
|
||||
offset += 16;
|
||||
|
||||
this.issueDate = DC.GetDateTime(data, offset);
|
||||
offset += 8;
|
||||
this.expireDate = DC.GetDateTime(data, offset);
|
||||
offset += 8;
|
||||
|
||||
this.domain = Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
this.authorityName = (Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]));
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
caId = DC.GetUInt64(data, offset);
|
||||
offset += 8;
|
||||
|
||||
var aea = (AsymetricEncryptionAlgorithmType)(data[offset] >> 5);
|
||||
|
||||
if (aea == AsymetricEncryptionAlgorithmType.RSA)
|
||||
{
|
||||
var key = new RSAParameters();
|
||||
uint exponentLength = (uint)data[offset++] & 0x1F;
|
||||
|
||||
key.Exponent = DC.Clip(data, offset, exponentLength);
|
||||
offset += exponentLength;
|
||||
|
||||
uint keySize = DC.GetUInt16(data, offset);
|
||||
offset += 2;
|
||||
|
||||
key.Modulus = DC.Clip(data, offset, keySize);
|
||||
|
||||
offset += keySize;
|
||||
|
||||
// copy cert data
|
||||
publicRawData = new byte[offset - oOffset];
|
||||
Buffer.BlockCopy(data, (int)oOffset, publicRawData, 0, publicRawData.Length);
|
||||
|
||||
if (privateKeyIncluded)
|
||||
{
|
||||
|
||||
uint privateKeyLength = (keySize * 3) + (keySize / 2);
|
||||
privateRawData = DC.Clip(data, offset, privateKeyLength);
|
||||
|
||||
uint halfKeySize = keySize / 2;
|
||||
|
||||
key.D = DC.Clip(data, offset, keySize);
|
||||
offset += keySize;
|
||||
key.DP = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.DQ = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.InverseQ = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.P = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.Q = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
|
||||
}
|
||||
|
||||
// setup rsa
|
||||
rsa = RSA.Create();// new RSACryptoServiceProvider();
|
||||
rsa.ImportParameters(key);
|
||||
|
||||
this.signature = DC.Clip(data, offset, length - (offset - oOffset));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DomainCertificate(ulong id, string domain, CACertificate authority, DateTime issueDate,
|
||||
DateTime expireDate, HashFunctionType hashFunction = HashFunctionType.SHA1, uint ip = 0, byte[] ip6 = null)
|
||||
: base (id, issueDate, expireDate, hashFunction)
|
||||
{
|
||||
// assign type
|
||||
|
||||
var cr = new BinaryList();
|
||||
|
||||
// id
|
||||
cr.Append(id);
|
||||
|
||||
// ip
|
||||
this.ip = ip;
|
||||
this.ip6 = ip6;
|
||||
|
||||
cr.Append(ip);
|
||||
|
||||
|
||||
if (ip6?.Length == 16)
|
||||
cr.Append(ip6);
|
||||
else
|
||||
cr.Append(new byte[16]);
|
||||
|
||||
|
||||
cr.Append(issueDate, expireDate);
|
||||
|
||||
// domain
|
||||
this.domain = domain;
|
||||
cr.Append((byte)(domain.Length), Encoding.ASCII.GetBytes(domain));
|
||||
|
||||
// CA
|
||||
this.caName = authority.Name;
|
||||
cr.Append((byte)(authority.Name.Length), Encoding.ASCII.GetBytes(authority.Name));
|
||||
|
||||
this.authorityName = authority.Name;
|
||||
|
||||
// CA Index
|
||||
//co.KeyIndex = authority.KeyIndex;
|
||||
this.caId = authority.Id;
|
||||
cr.Append(caId);
|
||||
|
||||
|
||||
// public key
|
||||
rsa = RSA.Create();// new RSACryptoServiceProvider(2048);
|
||||
rsa.KeySize = 2048;
|
||||
RSAParameters dRSAKey = rsa.ExportParameters(true);
|
||||
cr.Append((byte)dRSAKey.Exponent.Length, dRSAKey.Exponent, (ushort)dRSAKey.Modulus.Length, dRSAKey.Modulus, AsymetricEncryptionAlgorithmType.RSA);
|
||||
|
||||
|
||||
publicRawData = cr.ToArray();
|
||||
|
||||
// private key
|
||||
this.privateRawData = DC.Merge(dRSAKey.D, dRSAKey.DP, dRSAKey.DQ, dRSAKey.InverseQ, dRSAKey.P, dRSAKey.Q);
|
||||
|
||||
this.signature = authority.Sign(publicRawData);
|
||||
|
||||
}
|
||||
|
||||
public override bool Save(string filename, bool includePrivate = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (includePrivate)
|
||||
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPrivate, publicRawData, signature, privateRawData));
|
||||
else
|
||||
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPublic, publicRawData, signature));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Serialize(bool includePrivate = false)
|
||||
{
|
||||
if (includePrivate)
|
||||
return BinaryList.ToBytes(publicRawData, signature, privateRawData);
|
||||
else
|
||||
return BinaryList.ToBytes(publicRawData, signature);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
17
Esiur/Security/Authority/HostAuthentication.cs
Normal file
17
Esiur/Security/Authority/HostAuthentication.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class HostAuthentication : Authentication
|
||||
{
|
||||
public HostAuthentication(DomainCertificate certificate, AuthenticationState state)
|
||||
: base(certificate, state, AuthenticationType.Host)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
23
Esiur/Security/Authority/Session.cs
Normal file
23
Esiur/Security/Authority/Session.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class Session
|
||||
{
|
||||
Authentication Authentication { get; }
|
||||
Source Source { get; }
|
||||
string Id { get; }
|
||||
DateTime Creation { get; }
|
||||
DateTime Modification { get; }
|
||||
//KeyList<string, object> Variables { get; }
|
||||
//IStore Store { get; }
|
||||
}
|
||||
}
|
30
Esiur/Security/Authority/Source.cs
Normal file
30
Esiur/Security/Authority/Source.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class Source
|
||||
{
|
||||
|
||||
string id;
|
||||
KeyList<SourceAttributeType, Structure> attributes;
|
||||
|
||||
string Id { get { return id; } }
|
||||
|
||||
KeyList<SourceAttributeType, Structure> Attributes
|
||||
{
|
||||
get { return attributes; }
|
||||
}
|
||||
|
||||
public Source(string id, KeyList<SourceAttributeType, Structure> attributes)
|
||||
{
|
||||
this.id = id;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
53
Esiur/Security/Authority/SourceAttributeType.cs
Normal file
53
Esiur/Security/Authority/SourceAttributeType.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public enum SourceAttributeType
|
||||
{
|
||||
Mobility, // Stationary/Mobile
|
||||
CPU, // Arc, Speed, Cores
|
||||
IP, // IPv4, IPv6 Address
|
||||
Route, // Trace Root
|
||||
Location, // Lon, Lat, Alt, Accuracy
|
||||
OS, // OS name, version, distro, kernel
|
||||
Application, // lib version, app version
|
||||
Network, // Bandwidth, MAC, IP, Route
|
||||
Display, // Screen WxH
|
||||
Media, // AudioIn, AudioOut, VideoIn,
|
||||
Identity, // IMEI, IMSI, Manufacture
|
||||
}
|
||||
/*
|
||||
public class SourceAttribute
|
||||
{
|
||||
SourceAttributeType type;
|
||||
Structure value;
|
||||
|
||||
public SourceAttributeType Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
public Structure Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public SourceAttribute(SourceAttributeType type, Structure value)
|
||||
{
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
230
Esiur/Security/Authority/UserCertificate.cs
Normal file
230
Esiur/Security/Authority/UserCertificate.cs
Normal file
@ -0,0 +1,230 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Security.Cryptography;
|
||||
using Esiur.Security.Integrity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Authority
|
||||
{
|
||||
public class UserCertificate : Certificate
|
||||
{
|
||||
uint ip;
|
||||
byte[] ip6;
|
||||
byte[] signature;
|
||||
string domain;
|
||||
string username;
|
||||
ulong domainId;
|
||||
|
||||
public ulong DomainId
|
||||
{
|
||||
get { return domainId; }
|
||||
}
|
||||
|
||||
public string Username
|
||||
{
|
||||
get { return username; }
|
||||
}
|
||||
|
||||
public string Domain
|
||||
{
|
||||
get { return domain; }
|
||||
}
|
||||
|
||||
public byte[] Signature
|
||||
{
|
||||
get { return signature; }
|
||||
}
|
||||
|
||||
public uint IPAddress
|
||||
{
|
||||
get { return ip; }
|
||||
}
|
||||
|
||||
public byte[] IPv6Address
|
||||
{
|
||||
get { return ip6; }
|
||||
}
|
||||
|
||||
public UserCertificate(byte[] data, uint offset, uint length, bool privateKeyIncluded = false)
|
||||
: base(0, DateTime.MinValue, DateTime.MinValue, HashFunctionType.MD5)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
this.id = DC.GetUInt64(data, offset);
|
||||
offset += 8;
|
||||
|
||||
// load IPs
|
||||
this.ip = DC.GetUInt32(data, offset);
|
||||
offset += 4;
|
||||
ip6 = DC.Clip(data, offset, 16);
|
||||
offset += 16;
|
||||
|
||||
this.issueDate = DC.GetDateTime(data, offset);
|
||||
offset += 8;
|
||||
this.expireDate = DC.GetDateTime(data, offset);
|
||||
offset += 8;
|
||||
|
||||
this.domainId = DC.GetUInt64(data, offset);
|
||||
offset += 8;
|
||||
|
||||
this.domain = Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
this.username = Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
// Hash Function
|
||||
this.hashFunction = (HashFunctionType)(data[offset++] >> 4);
|
||||
|
||||
// Public Key Encryption Algorithm
|
||||
var aea = (AsymetricEncryptionAlgorithmType)(data[offset] >> 5);
|
||||
|
||||
if (aea == AsymetricEncryptionAlgorithmType.RSA)
|
||||
{
|
||||
|
||||
var key = new RSAParameters();
|
||||
|
||||
uint exponentLength = (uint)data[offset++] & 0x1F;
|
||||
|
||||
key.Exponent = DC.Clip(data, offset, exponentLength);
|
||||
offset += exponentLength;
|
||||
|
||||
|
||||
uint keySize = DC.GetUInt16(data, offset);
|
||||
offset += 2;
|
||||
|
||||
key.Modulus = DC.Clip(data, offset, keySize);
|
||||
|
||||
offset += keySize;
|
||||
|
||||
// copy cert data
|
||||
this.publicRawData = new byte[offset - oOffset];
|
||||
Buffer.BlockCopy(data, (int)oOffset, publicRawData, 0, publicRawData.Length);
|
||||
|
||||
|
||||
if (privateKeyIncluded)
|
||||
{
|
||||
uint privateKeyLength = (keySize * 3) + (keySize / 2);
|
||||
uint halfKeySize = keySize / 2;
|
||||
|
||||
this.privateRawData = DC.Clip(data, offset, privateKeyLength);
|
||||
|
||||
key.D = DC.Clip(data, offset, keySize);
|
||||
offset += keySize;
|
||||
key.DP = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.DQ = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.InverseQ = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.P = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
key.Q = DC.Clip(data, offset, halfKeySize);
|
||||
offset += halfKeySize;
|
||||
}
|
||||
|
||||
// setup rsa
|
||||
this.rsa = RSA.Create();// new RSACryptoServiceProvider();
|
||||
this.rsa.ImportParameters(key);
|
||||
|
||||
this.signature = DC.Clip(data, offset, length - (offset - oOffset));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public UserCertificate(ulong id, string username, DomainCertificate domainCertificate, DateTime issueDate,
|
||||
DateTime expireDate, HashFunctionType hashFunction = HashFunctionType.SHA1, uint ip = 0, byte[] ip6 = null)
|
||||
: base(id, issueDate, expireDate, hashFunction)
|
||||
{
|
||||
// assign type
|
||||
var cr = new BinaryList();
|
||||
|
||||
//id
|
||||
cr.Append(id);
|
||||
|
||||
// ip
|
||||
this.ip = ip;
|
||||
this.ip6 = ip6;
|
||||
|
||||
cr.Append(ip);
|
||||
|
||||
|
||||
if (ip6?.Length == 16)
|
||||
cr.Append(ip6);
|
||||
else
|
||||
cr.Append(new byte[16]);
|
||||
|
||||
|
||||
// dates
|
||||
this.issueDate = DateTime.UtcNow;
|
||||
this.expireDate = expireDate;
|
||||
|
||||
cr.Append(issueDate, expireDate);
|
||||
|
||||
|
||||
// domain
|
||||
this.domainId = domainCertificate.Id;
|
||||
cr.Append(domainCertificate.Id);
|
||||
this.domain = domainCertificate.Domain;
|
||||
cr.Append((byte)domainCertificate.Domain.Length, Encoding.ASCII.GetBytes(domainCertificate.Domain));
|
||||
|
||||
|
||||
// username
|
||||
this.username = username;
|
||||
|
||||
cr.Append((byte)(username.Length), Encoding.ASCII.GetBytes(username));
|
||||
|
||||
// hash function (SHA1)
|
||||
cr.Append((byte)((byte)hashFunction << 4));// (byte)0x10);
|
||||
|
||||
// public key
|
||||
|
||||
rsa = RSA.Create();// new RSACryptoServiceProvider(2048);
|
||||
rsa.KeySize = 2048;
|
||||
// write public certificate file
|
||||
|
||||
var key = rsa.ExportParameters(true);
|
||||
publicRawData = BinaryList.ToBytes((byte)key.Exponent.Length, key.Exponent, (ushort)key.Modulus.Length, key.Modulus);
|
||||
|
||||
|
||||
// sign it
|
||||
this.signature = domainCertificate.Sign(publicRawData);
|
||||
|
||||
|
||||
// store private info
|
||||
privateRawData = DC.Merge(key.D, key.DP, key.DQ, key.InverseQ, key.P, key.Q, signature);
|
||||
|
||||
}
|
||||
|
||||
public override bool Save(string filename, bool includePrivate = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (includePrivate)
|
||||
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPrivate, publicRawData, signature, privateRawData));
|
||||
else
|
||||
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPublic, publicRawData, signature));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Serialize(bool includePrivate = false)
|
||||
{
|
||||
if (includePrivate)
|
||||
return BinaryList.ToBytes(publicRawData, signature, privateRawData);
|
||||
else
|
||||
return BinaryList.ToBytes(publicRawData, signature);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Cryptography
|
||||
{
|
||||
// Enums
|
||||
public enum AsymetricEncryptionAlgorithmType
|
||||
{
|
||||
RSA = 0,
|
||||
DSA = 1,
|
||||
ECDSA = 2
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Cryptography
|
||||
{
|
||||
public enum SymetricEncryptionAlgorithmType
|
||||
{
|
||||
AES = 0,
|
||||
Blowfish = 1,
|
||||
DES = 2
|
||||
}
|
||||
}
|
17
Esiur/Security/Integrity/HashFunctionType.cs
Normal file
17
Esiur/Security/Integrity/HashFunctionType.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Integrity
|
||||
{
|
||||
public enum HashFunctionType
|
||||
{
|
||||
MD5 = 0,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512
|
||||
}
|
||||
}
|
16
Esiur/Security/Membership/IDomain.cs
Normal file
16
Esiur/Security/Membership/IDomain.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Esiur.Resource;
|
||||
using Esiur.Security.Authority;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Authority
|
||||
{
|
||||
public interface IDomain : IResource
|
||||
{
|
||||
string Name { get; }
|
||||
DomainCertificate Certificate { get; }
|
||||
}
|
||||
}
|
48
Esiur/Security/Membership/IMembership.cs
Normal file
48
Esiur/Security/Membership/IMembership.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Data;
|
||||
using Esiur.Net.IIP;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Security.Authority;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public interface IMembership:IResource
|
||||
{
|
||||
//IUser[] GetUsers(QueryFilter<string> user);
|
||||
|
||||
//bool AddCertificate(Certificate certificate);
|
||||
|
||||
//CACertificate[] GetCACertificates(string authority);
|
||||
//UserCertificate[] GetUserCertificate(string user, string domain);
|
||||
//DomainCertificate[] GetDomainCertificates(string domain);
|
||||
|
||||
|
||||
bool UserExists(string username);
|
||||
AsyncReply<byte[]> GetPassword(string username, string domain);
|
||||
|
||||
//ClientAuthentication Authenticate(string username, byte[] credentials, int flag);
|
||||
//HostAuthentication Authenticate(DomainCertificate domainCertificate);
|
||||
//CoHostAuthentication Authenticate(DomainCertificate hostCertificate, int hostId);
|
||||
|
||||
/*
|
||||
object GetUserInfo(User user, string field);
|
||||
object[] GetUserInfo(User user, string[] fields);
|
||||
|
||||
bool SetUserInfo(User user, string field, object value);
|
||||
bool SetUserInfo(User user, KeyList<string, object> info);
|
||||
*/
|
||||
|
||||
//bool AddUser(User user, KeyList<string, object> info);
|
||||
//bool RemoveUser(string username);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
17
Esiur/Security/Membership/IUser.cs
Normal file
17
Esiur/Security/Membership/IUser.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Esiur.Engine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Membership
|
||||
{
|
||||
public interface IUser
|
||||
{
|
||||
string Username
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
17
Esiur/Security/Permissions/ActionType.cs
Normal file
17
Esiur/Security/Permissions/ActionType.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Permissions
|
||||
{
|
||||
public enum ActionType
|
||||
{
|
||||
Attach,
|
||||
Delete,
|
||||
Execute,
|
||||
Get,
|
||||
Set,
|
||||
}
|
||||
}
|
18
Esiur/Security/Permissions/IPermissionManager.cs
Normal file
18
Esiur/Security/Permissions/IPermissionManager.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Resource.Template;
|
||||
using Esiur.Security.Authority;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Security.Permissions
|
||||
{
|
||||
public interface IPermissionManager
|
||||
{
|
||||
bool Applicable(IResource resource, Session session, ActionType action, MemberTemplate member);
|
||||
}
|
||||
}
|
56
Esiur/Stores/MemoryStore.cs
Normal file
56
Esiur/Stores/MemoryStore.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Engine;
|
||||
|
||||
namespace Esiur.Stores
|
||||
{
|
||||
public class MemoryStore : IStore
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
Dictionary<uint, IResource> resources = new Dictionary<uint, IResource>();
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string Link(IResource resource)
|
||||
{
|
||||
if (resource.Instance.Store == this)
|
||||
return this.Instance.Name + "/" + resource.Instance.Id;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AsyncReply<IResource> Get(string path)
|
||||
{
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
|
||||
public bool Put(IResource resource)
|
||||
{
|
||||
resources.Add(resource.Instance.Id, resource);
|
||||
return true;
|
||||
}
|
||||
|
||||
public AsyncReply<IResource> Retrieve(uint iid)
|
||||
{
|
||||
if (resources.ContainsKey(iid))
|
||||
return new AsyncReply<IResource>(resources[iid]);
|
||||
else
|
||||
return new AsyncReply<IResource>(null);
|
||||
}
|
||||
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
}
|
||||
}
|
39
Test/MyMembership.cs
Normal file
39
Test/MyMembership.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Security.Membership;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
class MyMembership : IMembership
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
|
||||
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
}
|
||||
|
||||
public AsyncReply<byte[]> GetPassword(string username, string domain)
|
||||
{
|
||||
return new AsyncReply<byte[]>(DC.ToBytes("password"));
|
||||
}
|
||||
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return new AsyncReply<bool>(true);
|
||||
}
|
||||
|
||||
public bool UserExists(string username)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
92
Test/MyObject.cs
Normal file
92
Test/MyObject.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Engine;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
class MyObject : IResource
|
||||
{
|
||||
public Instance Instance { get; set; }
|
||||
|
||||
public event DestroyedEvent OnDestroy;
|
||||
[ResourceEvent]
|
||||
public event ResourceEventHanlder LevelUp;
|
||||
[ResourceEvent]
|
||||
public event ResourceEventHanlder LevelDown;
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
|
||||
}
|
||||
public MyObject()
|
||||
{
|
||||
Info = new Structure();
|
||||
Info["size"] = 200;
|
||||
Info["age"] = 30;
|
||||
Info["name"] = "Zamil";
|
||||
Name = "Ahmed";
|
||||
Level = 5;
|
||||
}
|
||||
|
||||
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
|
||||
{
|
||||
return new AsyncReply<bool>();
|
||||
}
|
||||
|
||||
[ResourceFunction]
|
||||
public int Add(int value)
|
||||
{
|
||||
Level += value;
|
||||
LevelUp?.Invoke(null, "going up", value);
|
||||
return Level;
|
||||
}
|
||||
|
||||
[ResourceFunction]
|
||||
public int Subtract(int value)
|
||||
{
|
||||
Level -= value;
|
||||
LevelDown?.Invoke(null, "going down", value);
|
||||
return Level;
|
||||
}
|
||||
|
||||
[ResourceProperty]
|
||||
public Structure Info
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ResourceProperty]
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ResourceProperty]
|
||||
public MyObject Me
|
||||
{
|
||||
get
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
int level;
|
||||
[ResourceProperty]
|
||||
public int Level
|
||||
{
|
||||
get { return level; }
|
||||
set
|
||||
{
|
||||
level = value;
|
||||
Instance?.Modified();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
152
Test/Program.cs
Normal file
152
Test/Program.cs
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
|
||||
Copyright(c) Ahmed Kh. Zamil
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Engine;
|
||||
using Esiur.Net.HTTP;
|
||||
using Esiur.Net.IIP;
|
||||
using Esiur.Net.Sockets;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Stores;
|
||||
using Esiur.Stores.MongoDB;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static MyObject myObject;
|
||||
static DistributedResource remoteObject;
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
var system = Warehouse.New<MemoryStore>("system");
|
||||
var remote = Warehouse.New<MemoryStore>("remote");
|
||||
var mongo = Warehouse.New<MongoDBStore>("db");
|
||||
|
||||
Warehouse.Open().Then((ok)=> {
|
||||
if (mongo.Count == 0)
|
||||
myObject = Warehouse.New<MyObject>("my", mongo);
|
||||
else
|
||||
Warehouse.Get("db/my").Then((o) => { myObject = (MyObject)o; });
|
||||
|
||||
var iip = Warehouse.New<DistributedServer>("iip", system);
|
||||
iip.Membership = new MyMembership();
|
||||
iip.Start(new TCPSocket(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 5000)), 600000, 60000);
|
||||
|
||||
|
||||
|
||||
var http = Warehouse.New<HTTPServer>("http", system);
|
||||
http.Start(new TCPSocket(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 5001)), 600000, 60000);
|
||||
|
||||
var wsOverHttp = Warehouse.New<IIPoWS>("IIPoWS", system, http);
|
||||
|
||||
|
||||
TestClient();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
var running = true;
|
||||
|
||||
while (running)
|
||||
{
|
||||
var cmd = Console.ReadLine();
|
||||
if (cmd.ToLower() == "exit")
|
||||
Warehouse.Close().Then((x) =>
|
||||
{
|
||||
if (!x)
|
||||
Console.WriteLine("Failed to close the warehouse.");
|
||||
else
|
||||
Console.WriteLine("Successfully closed the warehouse.");
|
||||
|
||||
running = false;
|
||||
});
|
||||
else
|
||||
Console.WriteLine(myObject.Name + " " + myObject.Level);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TestClient()
|
||||
{
|
||||
var client = new DistributedConnection(new TCPSocket("localhost", 5000), "any", "ahmed", "password");
|
||||
|
||||
var remote = Warehouse.GetStore("remote");
|
||||
|
||||
|
||||
Warehouse.Put(client, client.RemoteEndPoint.ToString(), remote);
|
||||
|
||||
|
||||
client.OnReady += (c) =>
|
||||
{
|
||||
client.Get("db/my").Then((dynamic x) =>
|
||||
{
|
||||
remoteObject = x;
|
||||
|
||||
Console.WriteLine("My Name is: " + x.Name);
|
||||
x.Name = "Hamoo";
|
||||
x.LevelUp += new DistributedResourceEvent((sender, parameters) =>
|
||||
{
|
||||
Console.WriteLine("LevelUp " + parameters[0] + " " + parameters[1]);
|
||||
});
|
||||
|
||||
x.LevelDown += new DistributedResourceEvent((sender, parameters) =>
|
||||
{
|
||||
Console.WriteLine("LevelUp " + parameters[0] + " " + parameters[1]);
|
||||
});
|
||||
|
||||
(x.Add(10) as AsyncReply).Then((r) =>
|
||||
{
|
||||
Console.WriteLine("RT: " + r + " " + x.Level);
|
||||
});
|
||||
|
||||
(x.Subtract(10) as AsyncReply).Then((r) =>
|
||||
{
|
||||
Console.WriteLine("RT: " + r + " " + x.Level);
|
||||
});
|
||||
|
||||
|
||||
var t = new Timer(T_Elapsed, null, 5000, 5000);
|
||||
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private static void T_Elapsed(object state)
|
||||
{
|
||||
myObject.Level++;
|
||||
dynamic o = remoteObject;
|
||||
|
||||
|
||||
Console.WriteLine(myObject.Level + " " + o.Level + o.Me.Me.Level);
|
||||
|
||||
Console.WriteLine(o.Info.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
Test/Test.csproj
Normal file
13
Test/Test.csproj
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Esiur.Stores.MongoDB\Esiur.Stores.MongoDB.csproj" />
|
||||
<ProjectReference Include="..\Esiur\Esiur.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user