mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-06-27 05:23:13 +00:00
Add project files.
This commit is contained in:
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user