mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-05-07 12:02: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