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