mirror of
				https://github.com/esiur/esiur-dotnet.git
				synced 2025-10-29 07:10:29 +00:00 
			
		
		
		
	.Net 6 Upgrade
This commit is contained in:
		| @@ -4,50 +4,49 @@ using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncAwaiter : INotifyCompletion | ||||
| { | ||||
|     public class AsyncAwaiter : INotifyCompletion | ||||
|     Action callback = null; | ||||
|  | ||||
|     AsyncException exception = null; | ||||
|  | ||||
|     object result; | ||||
|  | ||||
|  | ||||
|     public AsyncAwaiter(AsyncReply reply) | ||||
|     { | ||||
|         Action callback = null; | ||||
|  | ||||
|         AsyncException exception = null; | ||||
|  | ||||
|         object result; | ||||
|  | ||||
|  | ||||
|         public AsyncAwaiter(AsyncReply reply) | ||||
|         reply.Then(x => | ||||
|         { | ||||
|             reply.Then(x => | ||||
|             { | ||||
|                 this.IsCompleted = true; | ||||
|                 this.result = x; | ||||
|                 this.callback?.Invoke(); | ||||
|             }).Error(x => | ||||
|             { | ||||
|                 exception = x; | ||||
|                 this.IsCompleted = true; | ||||
|                 this.callback?.Invoke(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public object GetResult() | ||||
|             this.IsCompleted = true; | ||||
|             this.result = x; | ||||
|             this.callback?.Invoke(); | ||||
|         }).Error(x => | ||||
|         { | ||||
|             if (exception != null) | ||||
|                 throw exception; | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public bool IsCompleted { get; private set; } | ||||
|  | ||||
|         public void OnCompleted(Action continuation) | ||||
|         { | ||||
|             if (IsCompleted) | ||||
|                 continuation?.Invoke(); | ||||
|             else | ||||
|                 // Continue.... | ||||
|                 callback = continuation; | ||||
|         } | ||||
|  | ||||
|  | ||||
|             exception = x; | ||||
|             this.IsCompleted = true; | ||||
|             this.callback?.Invoke(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public object GetResult() | ||||
|     { | ||||
|         if (exception != null) | ||||
|             throw exception; | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public bool IsCompleted { get; private set; } | ||||
|  | ||||
|     public void OnCompleted(Action continuation) | ||||
|     { | ||||
|         if (IsCompleted) | ||||
|             continuation?.Invoke(); | ||||
|         else | ||||
|             // Continue.... | ||||
|             callback = continuation; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,50 +4,48 @@ using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncAwaiter<T> : INotifyCompletion | ||||
| { | ||||
|     public class AsyncAwaiter<T> : INotifyCompletion | ||||
|     Action callback = null; | ||||
|  | ||||
|     AsyncException exception = null; | ||||
|  | ||||
|     T result; | ||||
|  | ||||
|     public AsyncAwaiter(AsyncReply<T> reply) | ||||
|     { | ||||
|         Action callback = null; | ||||
|  | ||||
|         AsyncException exception = null; | ||||
|  | ||||
|         T result; | ||||
|  | ||||
|         public AsyncAwaiter(AsyncReply<T> reply) | ||||
|         reply.Then(x => | ||||
|         { | ||||
|             reply.Then(x => | ||||
|             { | ||||
|                 this.IsCompleted = true; | ||||
|                 this.result = (T)x; | ||||
|                 this.callback?.Invoke(); | ||||
|             }).Error(x => | ||||
|             { | ||||
|                 exception = x; | ||||
|                 this.IsCompleted = true; | ||||
|                 this.callback?.Invoke(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public T GetResult() | ||||
|             this.IsCompleted = true; | ||||
|             this.result = (T)x; | ||||
|             this.callback?.Invoke(); | ||||
|         }).Error(x => | ||||
|         { | ||||
|             if (exception != null) | ||||
|                throw exception; | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public bool IsCompleted { get; private set; } | ||||
|  | ||||
|         public void OnCompleted(Action continuation) | ||||
|         { | ||||
|             if (IsCompleted) | ||||
|                 continuation?.Invoke(); | ||||
|             else | ||||
|                 // Continue.... | ||||
|                 callback = continuation; | ||||
|         } | ||||
|  | ||||
|  | ||||
|             exception = x; | ||||
|             this.IsCompleted = true; | ||||
|             this.callback?.Invoke(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     public T GetResult() | ||||
|     { | ||||
|         if (exception != null) | ||||
|             throw exception; | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public bool IsCompleted { get; private set; } | ||||
|  | ||||
|     public void OnCompleted(Action continuation) | ||||
|     { | ||||
|         if (IsCompleted) | ||||
|             continuation?.Invoke(); | ||||
|         else | ||||
|             // Continue.... | ||||
|             callback = continuation; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -28,104 +28,103 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncBag : AsyncReply | ||||
| { | ||||
|     public class AsyncBag: AsyncReply | ||||
|  | ||||
|     protected List<AsyncReply> replies = new List<AsyncReply>(); | ||||
|     List<object> results = new List<object>(); | ||||
|  | ||||
|     int count = 0; | ||||
|     bool sealedBag = false; | ||||
|  | ||||
|  | ||||
|     public Type ArrayType { get; set; } | ||||
|  | ||||
|     public AsyncBag Then(Action<object[]> callback) | ||||
|     { | ||||
|         base.Then(new Action<object>(o => callback((object[])o))); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public new AsyncBagAwaiter GetAwaiter() | ||||
|     { | ||||
|         return new AsyncBagAwaiter(this); | ||||
|     } | ||||
|  | ||||
|     public new object[] Wait() | ||||
|     { | ||||
|         return (object[])base.Wait(); | ||||
|     } | ||||
|  | ||||
|     public new object[] Wait(int timeout) | ||||
|     { | ||||
|         return (object[])base.Wait(timeout); | ||||
|     } | ||||
|  | ||||
|     public void Seal() | ||||
|     { | ||||
|         if (sealedBag) | ||||
|             return; | ||||
|  | ||||
|         sealedBag = true; | ||||
|  | ||||
|         if (results.Count == 0) | ||||
|             Trigger(new object[0]); | ||||
|  | ||||
|         for (var i = 0; i < results.Count; i++) | ||||
|         //foreach(var reply in results.Keys) | ||||
|         { | ||||
|             var k = replies[i];// results.Keys.ElementAt(i); | ||||
|             var index = i; | ||||
|  | ||||
|             k.Then((r) => | ||||
|             { | ||||
|                 results[index] = r; | ||||
|                 count++; | ||||
|                 if (count == results.Count) | ||||
|                 { | ||||
|                     if (ArrayType != null) | ||||
|                     { | ||||
|                         var ar = Array.CreateInstance(ArrayType, count); | ||||
|                         for (var i = 0; i < count; i++) | ||||
|                             ar.SetValue(results[i], i); | ||||
|                         Trigger(ar); | ||||
|                     } | ||||
|                     else | ||||
|                         Trigger(results.ToArray()); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void Add(AsyncReply reply) | ||||
|     { | ||||
|         if (!sealedBag) | ||||
|         { | ||||
|             results.Add(null); | ||||
|             replies.Add(reply); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void AddBag(AsyncBag bag) | ||||
|     { | ||||
|         foreach (var r in bag.replies) | ||||
|             Add(r); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public AsyncBag() | ||||
|     { | ||||
|  | ||||
|         protected List<AsyncReply> replies = new List<AsyncReply>(); | ||||
|         List<object> results = new List<object>(); | ||||
|     } | ||||
|  | ||||
|         int count = 0; | ||||
|         bool sealedBag = false; | ||||
|  | ||||
|  | ||||
|         public Type ArrayType { get; set; } | ||||
|  | ||||
|         public AsyncBag Then(Action<object[]> callback) | ||||
|         { | ||||
|             base.Then(new Action<object>(o => callback((object[])o))); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public new AsyncBagAwaiter GetAwaiter() | ||||
|         { | ||||
|             return new AsyncBagAwaiter(this); | ||||
|         } | ||||
|  | ||||
|         public new object[] Wait() | ||||
|         { | ||||
|             return (object[])base.Wait(); | ||||
|         } | ||||
|  | ||||
|         public new object[] Wait(int timeout) | ||||
|         { | ||||
|             return (object[])base.Wait(timeout); | ||||
|         } | ||||
|  | ||||
|         public void Seal() | ||||
|         { | ||||
|             if (sealedBag) | ||||
|                 return; | ||||
|  | ||||
|             sealedBag = true; | ||||
|  | ||||
|             if (results.Count == 0) | ||||
|                 Trigger(new object[0]); | ||||
|  | ||||
|             for (var i = 0; i < results.Count; i++) | ||||
|             //foreach(var reply in results.Keys) | ||||
|             { | ||||
|                 var k = replies[i];// results.Keys.ElementAt(i); | ||||
|                 var index = i; | ||||
|  | ||||
|                 k.Then((r) => | ||||
|                 { | ||||
|                     results[index] = r; | ||||
|                     count++; | ||||
|                     if (count == results.Count) | ||||
|                     { | ||||
|                         if (ArrayType != null) | ||||
|                         { | ||||
|                             var ar = Array.CreateInstance(ArrayType, count); | ||||
|                             for (var i = 0; i < count; i++) | ||||
|                                 ar.SetValue(results[i], i); | ||||
|                             Trigger(ar); | ||||
|                         } | ||||
|                         else | ||||
|                             Trigger(results.ToArray()); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Add(AsyncReply reply) | ||||
|         { | ||||
|             if (!sealedBag) | ||||
|             { | ||||
|                 results.Add(null); | ||||
|                 replies.Add(reply); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void AddBag(AsyncBag bag) | ||||
|         { | ||||
|             foreach (var r in bag.replies) | ||||
|                 Add(r); | ||||
|         } | ||||
|  | ||||
|        | ||||
|  | ||||
|         public AsyncBag() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public AsyncBag(object[] results) | ||||
|             : base(results) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     public AsyncBag(object[] results) | ||||
|         : base(results) | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,50 +4,48 @@ using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncBagAwaiter : INotifyCompletion | ||||
| { | ||||
|     public class AsyncBagAwaiter : INotifyCompletion | ||||
|     Action callback = null; | ||||
|  | ||||
|     AsyncException exception = null; | ||||
|  | ||||
|     object[] result; | ||||
|  | ||||
|     public AsyncBagAwaiter(AsyncBag reply) | ||||
|     { | ||||
|         Action callback = null; | ||||
|  | ||||
|         AsyncException exception = null; | ||||
|  | ||||
|         object[] result; | ||||
|  | ||||
|         public AsyncBagAwaiter(AsyncBag reply) | ||||
|         reply.Then(x => | ||||
|         { | ||||
|             reply.Then(x => | ||||
|             { | ||||
|                 this.IsCompleted = true; | ||||
|                 this.result = x; | ||||
|                 this.callback?.Invoke(); | ||||
|             }).Error(x => | ||||
|             { | ||||
|                 exception = x; | ||||
|                 this.IsCompleted = true; | ||||
|                 this.callback?.Invoke(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public object[] GetResult() | ||||
|             this.IsCompleted = true; | ||||
|             this.result = x; | ||||
|             this.callback?.Invoke(); | ||||
|         }).Error(x => | ||||
|         { | ||||
|             if (exception != null) | ||||
|                 throw exception; | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public bool IsCompleted { get; private set; } | ||||
|  | ||||
|         public void OnCompleted(Action continuation) | ||||
|         { | ||||
|             if (IsCompleted) | ||||
|                 continuation?.Invoke(); | ||||
|             else | ||||
|                 // Continue.... | ||||
|                 callback = continuation; | ||||
|         } | ||||
|  | ||||
|  | ||||
|             exception = x; | ||||
|             this.IsCompleted = true; | ||||
|             this.callback?.Invoke(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     public object[] GetResult() | ||||
|     { | ||||
|         if (exception != null) | ||||
|             throw exception; | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public bool IsCompleted { get; private set; } | ||||
|  | ||||
|     public void OnCompleted(Action continuation) | ||||
|     { | ||||
|         if (IsCompleted) | ||||
|             continuation?.Invoke(); | ||||
|         else | ||||
|             // Continue.... | ||||
|             callback = continuation; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,50 +4,48 @@ using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncBagAwaiter<T> : INotifyCompletion | ||||
| { | ||||
|     public class AsyncBagAwaiter<T> : INotifyCompletion | ||||
|     Action callback = null; | ||||
|  | ||||
|     AsyncException exception = null; | ||||
|  | ||||
|     T[] result; | ||||
|  | ||||
|     public AsyncBagAwaiter(AsyncBag<T> reply) | ||||
|     { | ||||
|         Action callback = null; | ||||
|  | ||||
|         AsyncException exception = null; | ||||
|  | ||||
|         T[] result; | ||||
|  | ||||
|         public AsyncBagAwaiter(AsyncBag<T> reply) | ||||
|         reply.Then(x => | ||||
|         { | ||||
|             reply.Then(x => | ||||
|             { | ||||
|                 this.IsCompleted = true; | ||||
|                 this.result = x; | ||||
|                 this.callback?.Invoke(); | ||||
|             }).Error(x => | ||||
|             { | ||||
|                 exception = x; | ||||
|                 this.IsCompleted = true; | ||||
|                 this.callback?.Invoke(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public T[] GetResult() | ||||
|             this.IsCompleted = true; | ||||
|             this.result = x; | ||||
|             this.callback?.Invoke(); | ||||
|         }).Error(x => | ||||
|         { | ||||
|             if (exception != null) | ||||
|                 throw exception; | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public bool IsCompleted { get; private set; } | ||||
|  | ||||
|         public void OnCompleted(Action continuation) | ||||
|         { | ||||
|             if (IsCompleted) | ||||
|                 continuation?.Invoke(); | ||||
|             else | ||||
|                 // Continue.... | ||||
|                 callback = continuation; | ||||
|         } | ||||
|  | ||||
|  | ||||
|             exception = x; | ||||
|             this.IsCompleted = true; | ||||
|             this.callback?.Invoke(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     public T[] GetResult() | ||||
|     { | ||||
|         if (exception != null) | ||||
|             throw exception; | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public bool IsCompleted { get; private set; } | ||||
|  | ||||
|     public void OnCompleted(Action continuation) | ||||
|     { | ||||
|         if (IsCompleted) | ||||
|             continuation?.Invoke(); | ||||
|         else | ||||
|             // Continue.... | ||||
|             callback = continuation; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -28,48 +28,47 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncBag<T> : AsyncBag | ||||
| { | ||||
|     public class AsyncBag<T>: AsyncBag | ||||
|     {      | ||||
|         public AsyncBag<T> Then(Action<T[]> callback) | ||||
|         { | ||||
|             base.Then(new Action<object>((o) => callback(((object[])o).Select(x=>(T)x).ToArray()))); | ||||
|             return this; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         public void Add(AsyncReply<T> reply) | ||||
|         { | ||||
|             base.Add(reply); | ||||
|         } | ||||
|  | ||||
|         public void AddBag(AsyncBag<T> bag) | ||||
|         { | ||||
|             foreach (var r in bag.replies) | ||||
|                 Add(r); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public new AsyncBagAwaiter<T> GetAwaiter() | ||||
|         { | ||||
|             return new AsyncBagAwaiter<T>(this); | ||||
|         } | ||||
|  | ||||
|         public new T[] Wait() | ||||
|         { | ||||
|             return base.Wait().Select(x => (T)x).ToArray(); | ||||
|         } | ||||
|  | ||||
|         public AsyncBag() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public AsyncBag(T[] results) | ||||
|             : base(results.Select(x=>(object)x).ToArray()) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     public AsyncBag<T> Then(Action<T[]> callback) | ||||
|     { | ||||
|         base.Then(new Action<object>((o) => callback(((object[])o).Select(x => (T)x).ToArray()))); | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|     public void Add(AsyncReply<T> reply) | ||||
|     { | ||||
|         base.Add(reply); | ||||
|     } | ||||
|  | ||||
|     public void AddBag(AsyncBag<T> bag) | ||||
|     { | ||||
|         foreach (var r in bag.replies) | ||||
|             Add(r); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public new AsyncBagAwaiter<T> GetAwaiter() | ||||
|     { | ||||
|         return new AsyncBagAwaiter<T>(this); | ||||
|     } | ||||
|  | ||||
|     public new T[] Wait() | ||||
|     { | ||||
|         return base.Wait().Select(x => (T)x).ToArray(); | ||||
|     } | ||||
|  | ||||
|     public AsyncBag() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public AsyncBag(T[] results) | ||||
|         : base(results.Select(x => (object)x).ToArray()) | ||||
|     { | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -26,32 +26,31 @@ using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncException : Exception | ||||
| { | ||||
|     public class AsyncException : Exception | ||||
|     public readonly ErrorType Type; | ||||
|     public readonly ExceptionCode Code; | ||||
|  | ||||
|     public AsyncException(Exception exception) : base(exception.Message, exception) | ||||
|     { | ||||
|         public readonly ErrorType Type; | ||||
|         public readonly ExceptionCode Code; | ||||
|         Type = ErrorType.Exception; | ||||
|         Code = 0; | ||||
|     } | ||||
|  | ||||
|         public AsyncException(Exception exception) :base(exception.Message, exception) | ||||
|         { | ||||
|             Type = ErrorType.Exception; | ||||
|             Code = 0; | ||||
|         } | ||||
|     public override string StackTrace => InnerException != null && Type == ErrorType.Exception ? InnerException.StackTrace : base.StackTrace; | ||||
|  | ||||
|         public override string StackTrace => InnerException != null && Type == ErrorType.Exception ? InnerException.StackTrace : base.StackTrace; | ||||
|     public AsyncException(ErrorType type, ushort code, string message) | ||||
|         : base(type == ErrorType.Management ? ((ExceptionCode)code).ToString() : message) | ||||
|     { | ||||
|         this.Type = type; | ||||
|         this.Code = (ExceptionCode)code; | ||||
|  | ||||
|         public AsyncException(ErrorType type, ushort code, string message) | ||||
|             : base(type == ErrorType.Management ? ((ExceptionCode)code).ToString() : message) | ||||
|         { | ||||
|             this.Type = type; | ||||
|             this.Code = (ExceptionCode)code; | ||||
|     } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return Code.ToString() + ": " + Message; | ||||
|         } | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return Code.ToString() + ": " + Message; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,57 +28,56 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncQueue<T> : AsyncReply<T> | ||||
| { | ||||
|     public class AsyncQueue<T> : AsyncReply<T> | ||||
|     List<AsyncReply<T>> list = new List<AsyncReply<T>>(); | ||||
|     //Action<T> callback; | ||||
|     object queueLock = new object(); | ||||
|  | ||||
|     //public AsyncQueue<T> Then(Action<T> callback) | ||||
|     //{ | ||||
|     //  base.Then(new Action<object>(o => callback((T)o))); | ||||
|  | ||||
|     //return this; | ||||
|     //} | ||||
|  | ||||
|     public void Add(AsyncReply<T> reply) | ||||
|     { | ||||
|         List<AsyncReply<T>> list = new List<AsyncReply<T>>(); | ||||
|         //Action<T> callback; | ||||
|         object queueLock = new object(); | ||||
|         lock (queueLock) | ||||
|             list.Add(reply); | ||||
|  | ||||
|         //public AsyncQueue<T> Then(Action<T> callback) | ||||
|         //{ | ||||
|           //  base.Then(new Action<object>(o => callback((T)o))); | ||||
|         resultReady = false; | ||||
|         reply.Then(processQueue); | ||||
|     } | ||||
|  | ||||
|             //return this; | ||||
|         //} | ||||
|     public void Remove(AsyncReply<T> reply) | ||||
|     { | ||||
|         lock (queueLock) | ||||
|             list.Remove(reply); | ||||
|         processQueue(default(T)); | ||||
|     } | ||||
|  | ||||
|         public void Add(AsyncReply<T> reply) | ||||
|         { | ||||
|             lock (queueLock) | ||||
|                 list.Add(reply); | ||||
|     void processQueue(T o) | ||||
|     { | ||||
|         lock (queueLock) | ||||
|             for (var i = 0; i < list.Count; i++) | ||||
|                 if (list[i].Ready) | ||||
|                 { | ||||
|                     Trigger(list[i].Result); | ||||
|                     resultReady = false; | ||||
|                     list.RemoveAt(i); | ||||
|                     i--; | ||||
|                 } | ||||
|                 else | ||||
|                     break; | ||||
|  | ||||
|             resultReady = false; | ||||
|             reply.Then(processQueue); | ||||
|         } | ||||
|         resultReady = (list.Count == 0); | ||||
|     } | ||||
|  | ||||
|         public void Remove(AsyncReply<T> reply) | ||||
|         { | ||||
|             lock (queueLock) | ||||
|                 list.Remove(reply); | ||||
|             processQueue(default(T)); | ||||
|         } | ||||
|     public AsyncQueue() | ||||
|     { | ||||
|  | ||||
|         void processQueue(T o) | ||||
|         { | ||||
|             lock (queueLock) | ||||
|                 for (var i = 0; i < list.Count; i++) | ||||
|                     if (list[i].Ready) | ||||
|                     { | ||||
|                         Trigger(list[i].Result); | ||||
|                         resultReady = false; | ||||
|                         list.RemoveAt(i); | ||||
|                         i--; | ||||
|                     } | ||||
|                     else | ||||
|                         break; | ||||
|  | ||||
|             resultReady = (list.Count == 0); | ||||
|         } | ||||
|  | ||||
|         public AsyncQueue() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -33,328 +33,327 @@ using System.Threading; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Diagnostics; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| [AsyncMethodBuilder(typeof(AsyncReplyBuilder))] | ||||
| public class AsyncReply | ||||
| { | ||||
|     [AsyncMethodBuilder(typeof(AsyncReplyBuilder))] | ||||
|     public class AsyncReply  | ||||
|     public bool Debug = false; | ||||
|  | ||||
|     protected List<Action<object>> callbacks = new List<Action<object>>(); | ||||
|     protected object result; | ||||
|  | ||||
|     protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>(); | ||||
|  | ||||
|     protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>(); | ||||
|  | ||||
|     protected List<Action<object>> chunkCallbacks = new List<Action<object>>(); | ||||
|  | ||||
|     //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>(); | ||||
|  | ||||
|     object asyncLock = new object(); | ||||
|  | ||||
|     //public Timer timeout;// = new Timer() | ||||
|     protected bool resultReady = false; | ||||
|     AsyncException exception; | ||||
|     // StackTrace trace; | ||||
|     AutoResetEvent mutex = new AutoResetEvent(false); | ||||
|  | ||||
|     public static int MaxId; | ||||
|  | ||||
|     public int Id; | ||||
|  | ||||
|     public bool Ready | ||||
|     { | ||||
|         public bool Debug = false; | ||||
|  | ||||
|         protected List<Action<object>> callbacks = new List<Action<object>>(); | ||||
|         protected object result; | ||||
|  | ||||
|         protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>(); | ||||
|  | ||||
|         protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>(); | ||||
|  | ||||
|         protected List<Action<object>> chunkCallbacks = new List<Action<object>>(); | ||||
|  | ||||
|         //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>(); | ||||
|  | ||||
|         object asyncLock = new object(); | ||||
|  | ||||
|         //public Timer timeout;// = new Timer() | ||||
|         protected bool resultReady = false; | ||||
|         AsyncException exception; | ||||
|         // StackTrace trace; | ||||
|         AutoResetEvent mutex = new AutoResetEvent(false); | ||||
|  | ||||
|         public static int MaxId; | ||||
|  | ||||
|         public int Id; | ||||
|  | ||||
|         public bool Ready | ||||
|         { | ||||
|             get { return resultReady; } | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public object Wait() | ||||
|         { | ||||
|             if (resultReady) | ||||
|                 return result; | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Wait"); | ||||
|  | ||||
|             //mutex = new AutoResetEvent(false); | ||||
|             mutex.WaitOne(); | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Wait ended"); | ||||
|  | ||||
|             if (exception != null) | ||||
|                 throw exception; | ||||
|  | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public object Wait(int millisecondsTimeout) | ||||
|         { | ||||
|             if (resultReady) | ||||
|                 return result; | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Wait"); | ||||
|  | ||||
|             if (!mutex.WaitOne(millisecondsTimeout)) | ||||
|             { | ||||
|                 var e = new Exception("AsyncReply timeout"); | ||||
|                 TriggerError(e); | ||||
|                 throw e; | ||||
|             } | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Wait ended"); | ||||
|  | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         public object Result | ||||
|         { | ||||
|             get { return result; } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply Then(Action<object> callback) | ||||
|         { | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             lock (asyncLock) | ||||
|             { | ||||
|                 //  trace = new StackTrace(); | ||||
|  | ||||
|                 if (resultReady) | ||||
|                 { | ||||
|                     if (Debug) | ||||
|                         Console.WriteLine($"AsyncReply: {Id} Then ready"); | ||||
|  | ||||
|                     callback(result); | ||||
|                     return this; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 //timeout = new Timer(x => | ||||
|                 //{ | ||||
|                 //    // Get calling method name | ||||
|                 //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name); | ||||
|  | ||||
|                 //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name)); | ||||
|                 //    timeout.Dispose(); | ||||
|  | ||||
|                 //    tr = trace.ToString(); | ||||
|                 //    throw new Exception("Request timeout " + Id); | ||||
|                 //}, null, 15000, 0); | ||||
|  | ||||
|  | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Then pending"); | ||||
|  | ||||
|  | ||||
|  | ||||
|                 callbacks.Add(callback); | ||||
|  | ||||
|                 return this; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public AsyncReply Error(Action<AsyncException> callback) | ||||
|         { | ||||
|             // lock (callbacksLock) | ||||
|             //  { | ||||
|             errorCallbacks.Add(callback); | ||||
|  | ||||
|             if (exception != null) | ||||
|                 callback(exception); | ||||
|  | ||||
|             return this; | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Progress(Action<ProgressType, int, int> callback) | ||||
|         { | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             progressCallbacks.Add(callback); | ||||
|             return this; | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply Chunk(Action<object> callback) | ||||
|         { | ||||
|             // lock (callbacksLock) | ||||
|             // { | ||||
|             chunkCallbacks.Add(callback); | ||||
|             return this; | ||||
|             // } | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Trigger(object result) | ||||
|         { | ||||
|             lock (asyncLock) | ||||
|             { | ||||
|                 //timeout?.Dispose(); | ||||
|  | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Trigger"); | ||||
|  | ||||
|                 if (resultReady) | ||||
|                     return this; | ||||
|  | ||||
|                 this.result = result; | ||||
|  | ||||
|                 resultReady = true; | ||||
|  | ||||
|                 //if (mutex != null) | ||||
|                 mutex.Set(); | ||||
|  | ||||
|                 foreach (var cb in callbacks) | ||||
|                     cb(result); | ||||
|  | ||||
|  | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Trigger ended"); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply TriggerError(Exception exception) | ||||
|         { | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return this; | ||||
|  | ||||
|             if (exception is AsyncException) | ||||
|                 this.exception = exception as AsyncException; | ||||
|             else | ||||
|                 this.exception = new AsyncException(exception); | ||||
|  | ||||
|              | ||||
|             // lock (callbacksLock) | ||||
|             // { | ||||
|             foreach (var cb in errorCallbacks) | ||||
|                 cb(this.exception); | ||||
|             //  } | ||||
|  | ||||
|             mutex?.Set(); | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply TriggerProgress(ProgressType type, int value, int max) | ||||
|         { | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             foreach (var cb in progressCallbacks) | ||||
|                 cb(type, value, max); | ||||
|  | ||||
|             //} | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply TriggerChunk(object value) | ||||
|         { | ||||
|  | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|              | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             foreach (var cb in chunkCallbacks) | ||||
|                 cb(value); | ||||
|  | ||||
|             //} | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AsyncAwaiter GetAwaiter() | ||||
|         { | ||||
|             return new AsyncAwaiter(this); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public AsyncReply() | ||||
|         { | ||||
|             //   this.Debug = true; | ||||
|             Id = MaxId++; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply(object result) | ||||
|         { | ||||
|             //   this.Debug = true; | ||||
|             resultReady = true; | ||||
|             this.result = result; | ||||
|  | ||||
|             Id = MaxId++; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|     public AsyncReply<T> Then(Action<T> callback) | ||||
|         { | ||||
|            base.Then(new Action<object>(o => callback((T)o))); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public void Trigger(T result) | ||||
|         { | ||||
|             Trigger((object)result); | ||||
|         } | ||||
|  | ||||
|         public Task<bool> MoveNext(CancellationToken cancellationToken) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|          } | ||||
|  | ||||
|         public AsyncReply() | ||||
|         { | ||||
|              | ||||
|         } | ||||
|  | ||||
|         public new Task<T> Task | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return base.Task.ContinueWith<T>((t) => | ||||
|                 { | ||||
|  | ||||
| #if NETSTANDARD | ||||
|                     return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); | ||||
| #else | ||||
|                     return (T)t.GetType().GetProperty("Result").GetValue(t); | ||||
| #endif | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public T Current => throw new NotImplementedException(); | ||||
|  | ||||
|         public AsyncReply(T result) | ||||
|             : base(result) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|  */ | ||||
|  | ||||
|  | ||||
|         get { return resultReady; } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public object Wait() | ||||
|     { | ||||
|         if (resultReady) | ||||
|             return result; | ||||
|  | ||||
|         if (Debug) | ||||
|             Console.WriteLine($"AsyncReply: {Id} Wait"); | ||||
|  | ||||
|         //mutex = new AutoResetEvent(false); | ||||
|         mutex.WaitOne(); | ||||
|  | ||||
|         if (Debug) | ||||
|             Console.WriteLine($"AsyncReply: {Id} Wait ended"); | ||||
|  | ||||
|         if (exception != null) | ||||
|             throw exception; | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public object Wait(int millisecondsTimeout) | ||||
|     { | ||||
|         if (resultReady) | ||||
|             return result; | ||||
|  | ||||
|         if (Debug) | ||||
|             Console.WriteLine($"AsyncReply: {Id} Wait"); | ||||
|  | ||||
|         if (!mutex.WaitOne(millisecondsTimeout)) | ||||
|         { | ||||
|             var e = new Exception("AsyncReply timeout"); | ||||
|             TriggerError(e); | ||||
|             throw e; | ||||
|         } | ||||
|  | ||||
|         if (Debug) | ||||
|             Console.WriteLine($"AsyncReply: {Id} Wait ended"); | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public object Result | ||||
|     { | ||||
|         get { return result; } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply Then(Action<object> callback) | ||||
|     { | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         lock (asyncLock) | ||||
|         { | ||||
|             //  trace = new StackTrace(); | ||||
|  | ||||
|             if (resultReady) | ||||
|             { | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Then ready"); | ||||
|  | ||||
|                 callback(result); | ||||
|                 return this; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             //timeout = new Timer(x => | ||||
|             //{ | ||||
|             //    // Get calling method name | ||||
|             //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name); | ||||
|  | ||||
|             //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name)); | ||||
|             //    timeout.Dispose(); | ||||
|  | ||||
|             //    tr = trace.ToString(); | ||||
|             //    throw new Exception("Request timeout " + Id); | ||||
|             //}, null, 15000, 0); | ||||
|  | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Then pending"); | ||||
|  | ||||
|  | ||||
|  | ||||
|             callbacks.Add(callback); | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public AsyncReply Error(Action<AsyncException> callback) | ||||
|     { | ||||
|         // lock (callbacksLock) | ||||
|         //  { | ||||
|         errorCallbacks.Add(callback); | ||||
|  | ||||
|         if (exception != null) | ||||
|             callback(exception); | ||||
|  | ||||
|         return this; | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|     public AsyncReply Progress(Action<ProgressType, int, int> callback) | ||||
|     { | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         progressCallbacks.Add(callback); | ||||
|         return this; | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply Chunk(Action<object> callback) | ||||
|     { | ||||
|         // lock (callbacksLock) | ||||
|         // { | ||||
|         chunkCallbacks.Add(callback); | ||||
|         return this; | ||||
|         // } | ||||
|     } | ||||
|  | ||||
|     public AsyncReply Trigger(object result) | ||||
|     { | ||||
|         lock (asyncLock) | ||||
|         { | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Trigger"); | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return this; | ||||
|  | ||||
|             this.result = result; | ||||
|  | ||||
|             resultReady = true; | ||||
|  | ||||
|             //if (mutex != null) | ||||
|             mutex.Set(); | ||||
|  | ||||
|             foreach (var cb in callbacks) | ||||
|                 cb(result); | ||||
|  | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Trigger ended"); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply TriggerError(Exception exception) | ||||
|     { | ||||
|         //timeout?.Dispose(); | ||||
|  | ||||
|         if (resultReady) | ||||
|             return this; | ||||
|  | ||||
|         if (exception is AsyncException) | ||||
|             this.exception = exception as AsyncException; | ||||
|         else | ||||
|             this.exception = new AsyncException(exception); | ||||
|  | ||||
|  | ||||
|         // lock (callbacksLock) | ||||
|         // { | ||||
|         foreach (var cb in errorCallbacks) | ||||
|             cb(this.exception); | ||||
|         //  } | ||||
|  | ||||
|         mutex?.Set(); | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply TriggerProgress(ProgressType type, int value, int max) | ||||
|     { | ||||
|         //timeout?.Dispose(); | ||||
|  | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         foreach (var cb in progressCallbacks) | ||||
|             cb(type, value, max); | ||||
|  | ||||
|         //} | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply TriggerChunk(object value) | ||||
|     { | ||||
|  | ||||
|         //timeout?.Dispose(); | ||||
|  | ||||
|  | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         foreach (var cb in chunkCallbacks) | ||||
|             cb(value); | ||||
|  | ||||
|         //} | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public AsyncAwaiter GetAwaiter() | ||||
|     { | ||||
|         return new AsyncAwaiter(this); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public AsyncReply() | ||||
|     { | ||||
|         //   this.Debug = true; | ||||
|         Id = MaxId++; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply(object result) | ||||
|     { | ||||
|         //   this.Debug = true; | ||||
|         resultReady = true; | ||||
|         this.result = result; | ||||
|  | ||||
|         Id = MaxId++; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
| public AsyncReply<T> Then(Action<T> callback) | ||||
|     { | ||||
|        base.Then(new Action<object>(o => callback((T)o))); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public void Trigger(T result) | ||||
|     { | ||||
|         Trigger((object)result); | ||||
|     } | ||||
|  | ||||
|     public Task<bool> MoveNext(CancellationToken cancellationToken) | ||||
|     { | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|     public void Dispose() | ||||
|     { | ||||
|      } | ||||
|  | ||||
|     public AsyncReply() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public new Task<T> Task | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return base.Task.ContinueWith<T>((t) => | ||||
|             { | ||||
|  | ||||
| #if NETSTANDARD | ||||
|                 return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); | ||||
| #else | ||||
|                 return (T)t.GetType().GetProperty("Result").GetValue(t); | ||||
| #endif | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public T Current => throw new NotImplementedException(); | ||||
|  | ||||
|     public AsyncReply(T result) | ||||
|         : base(result) | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,66 +3,65 @@ using System.Collections.Generic; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncReplyBuilder | ||||
| { | ||||
|     public class AsyncReplyBuilder | ||||
|     AsyncReply reply; | ||||
|  | ||||
|     AsyncReplyBuilder(AsyncReply reply) | ||||
|     { | ||||
|         AsyncReply reply; | ||||
|  | ||||
|         AsyncReplyBuilder(AsyncReply reply) | ||||
|         { | ||||
|             this.reply = reply; | ||||
|         } | ||||
|  | ||||
|         public static AsyncReplyBuilder Create() | ||||
|         { | ||||
|             return new AsyncReplyBuilder(new AsyncReply()); | ||||
|         } | ||||
|  | ||||
|         public void Start<TStateMachine>(ref TStateMachine stateMachine) | ||||
|             where TStateMachine : IAsyncStateMachine | ||||
|         { | ||||
|             stateMachine.MoveNext(); | ||||
|         } | ||||
|  | ||||
|         public void SetStateMachine(IAsyncStateMachine stateMachine) | ||||
|         { | ||||
|             Console.WriteLine("SetStateMachine"); | ||||
|         } | ||||
|  | ||||
|         public void SetException(Exception exception) | ||||
|         { | ||||
|             reply.TriggerError(exception); | ||||
|         } | ||||
|  | ||||
|         public void SetResult() | ||||
|         { | ||||
|             reply.Trigger(null); | ||||
|         } | ||||
|  | ||||
|         public void AwaitOnCompleted<TAwaiter, TStateMachine>( | ||||
|             ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|             where TAwaiter : INotifyCompletion | ||||
|             where TStateMachine : IAsyncStateMachine | ||||
|         { | ||||
|             awaiter.OnCompleted(stateMachine.MoveNext); | ||||
|         } | ||||
|  | ||||
|         public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( | ||||
|             ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|             where TAwaiter : ICriticalNotifyCompletion | ||||
|             where TStateMachine : IAsyncStateMachine | ||||
|         { | ||||
|             awaiter.UnsafeOnCompleted(stateMachine.MoveNext);             | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Task | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return reply; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.reply = reply; | ||||
|     } | ||||
|  | ||||
|     public static AsyncReplyBuilder Create() | ||||
|     { | ||||
|         return new AsyncReplyBuilder(new AsyncReply()); | ||||
|     } | ||||
|  | ||||
|     public void Start<TStateMachine>(ref TStateMachine stateMachine) | ||||
|         where TStateMachine : IAsyncStateMachine | ||||
|     { | ||||
|         stateMachine.MoveNext(); | ||||
|     } | ||||
|  | ||||
|     public void SetStateMachine(IAsyncStateMachine stateMachine) | ||||
|     { | ||||
|         Console.WriteLine("SetStateMachine"); | ||||
|     } | ||||
|  | ||||
|     public void SetException(Exception exception) | ||||
|     { | ||||
|         reply.TriggerError(exception); | ||||
|     } | ||||
|  | ||||
|     public void SetResult() | ||||
|     { | ||||
|         reply.Trigger(null); | ||||
|     } | ||||
|  | ||||
|     public void AwaitOnCompleted<TAwaiter, TStateMachine>( | ||||
|         ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|         where TAwaiter : INotifyCompletion | ||||
|         where TStateMachine : IAsyncStateMachine | ||||
|     { | ||||
|         awaiter.OnCompleted(stateMachine.MoveNext); | ||||
|     } | ||||
|  | ||||
|     public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( | ||||
|         ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|         where TAwaiter : ICriticalNotifyCompletion | ||||
|         where TStateMachine : IAsyncStateMachine | ||||
|     { | ||||
|         awaiter.UnsafeOnCompleted(stateMachine.MoveNext); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply Task | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return reply; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,65 +3,65 @@ using System.Collections.Generic; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncReplyBuilder<T> | ||||
| { | ||||
|     public class AsyncReplyBuilder<T> | ||||
|     AsyncReply<T> reply; | ||||
|  | ||||
|     AsyncReplyBuilder(AsyncReply<T> reply) | ||||
|     { | ||||
|         AsyncReply<T> reply; | ||||
|  | ||||
|         AsyncReplyBuilder(AsyncReply<T> reply) | ||||
|         { | ||||
|             this.reply = reply; | ||||
|         } | ||||
|  | ||||
|         public static AsyncReplyBuilder<T> Create() | ||||
|         { | ||||
|             return new AsyncReplyBuilder<T>(new AsyncReply<T>()); | ||||
|         } | ||||
|  | ||||
|         public void Start<TStateMachine>(ref TStateMachine stateMachine) | ||||
|             where TStateMachine : IAsyncStateMachine | ||||
|         { | ||||
|             stateMachine.MoveNext(); | ||||
|         } | ||||
|  | ||||
|         public void SetStateMachine(IAsyncStateMachine stateMachine) | ||||
|         { | ||||
|             Console.WriteLine("SetStateMachine"); | ||||
|         } | ||||
|  | ||||
|         public void SetException(Exception exception) | ||||
|         { | ||||
|             reply.TriggerError(exception); | ||||
|         } | ||||
|  | ||||
|         public void SetResult(T result) | ||||
|         { | ||||
|             reply.Trigger(result); | ||||
|         } | ||||
|  | ||||
|         public void AwaitOnCompleted<TAwaiter, TStateMachine>( | ||||
|             ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|             where TAwaiter : INotifyCompletion | ||||
|             where TStateMachine : IAsyncStateMachine | ||||
|         { | ||||
|             awaiter.OnCompleted(stateMachine.MoveNext); | ||||
|         } | ||||
|  | ||||
|         public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( | ||||
|             ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|             where TAwaiter : ICriticalNotifyCompletion | ||||
|             where TStateMachine : IAsyncStateMachine | ||||
|         { | ||||
|             awaiter.UnsafeOnCompleted(stateMachine.MoveNext); | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<T> Task | ||||
|         { | ||||
|             get { | ||||
|                 return reply; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.reply = reply; | ||||
|     } | ||||
|  | ||||
|     public static AsyncReplyBuilder<T> Create() | ||||
|     { | ||||
|         return new AsyncReplyBuilder<T>(new AsyncReply<T>()); | ||||
|     } | ||||
|  | ||||
|     public void Start<TStateMachine>(ref TStateMachine stateMachine) | ||||
|         where TStateMachine : IAsyncStateMachine | ||||
|     { | ||||
|         stateMachine.MoveNext(); | ||||
|     } | ||||
|  | ||||
|     public void SetStateMachine(IAsyncStateMachine stateMachine) | ||||
|     { | ||||
|         Console.WriteLine("SetStateMachine"); | ||||
|     } | ||||
|  | ||||
|     public void SetException(Exception exception) | ||||
|     { | ||||
|         reply.TriggerError(exception); | ||||
|     } | ||||
|  | ||||
|     public void SetResult(T result) | ||||
|     { | ||||
|         reply.Trigger(result); | ||||
|     } | ||||
|  | ||||
|     public void AwaitOnCompleted<TAwaiter, TStateMachine>( | ||||
|         ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|         where TAwaiter : INotifyCompletion | ||||
|         where TStateMachine : IAsyncStateMachine | ||||
|     { | ||||
|         awaiter.OnCompleted(stateMachine.MoveNext); | ||||
|     } | ||||
|  | ||||
|     public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( | ||||
|         ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||||
|         where TAwaiter : ICriticalNotifyCompletion | ||||
|         where TStateMachine : IAsyncStateMachine | ||||
|     { | ||||
|         awaiter.UnsafeOnCompleted(stateMachine.MoveNext); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<T> Task | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return reply; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -33,347 +33,346 @@ using System.Threading; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Diagnostics; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| [AsyncMethodBuilder(typeof(AsyncReplyBuilder<>))] | ||||
| public class AsyncReply<T> : AsyncReply | ||||
| { | ||||
|     [AsyncMethodBuilder(typeof(AsyncReplyBuilder<>))] | ||||
|     public class AsyncReply<T> : AsyncReply | ||||
|  | ||||
|     public AsyncReply<T> Then(Action<T> callback) | ||||
|     { | ||||
|         base.Then((x) => callback((T)x)); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public new AsyncReply<T> Progress(Action<ProgressType, int, int> callback) | ||||
|     { | ||||
|         base.Progress(callback); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply<T> Chunk(Action<T> callback) | ||||
|     { | ||||
|         chunkCallbacks.Add((x) => callback((T)x)); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply(T result) | ||||
|        : base(result) | ||||
|     { | ||||
|  | ||||
|         public AsyncReply<T> Then(Action<T> callback) | ||||
|         { | ||||
|             base.Then((x)=>callback((T)x)); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public new AsyncReply<T> Progress(Action<ProgressType, int, int> callback) | ||||
|         { | ||||
|             base.Progress(callback); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply<T> Chunk(Action<T> callback) | ||||
|         { | ||||
|             chunkCallbacks.Add((x)=>callback((T)x)); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply(T result) | ||||
|            : base(result) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public AsyncReply() | ||||
|             :base() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public new AsyncAwaiter<T> GetAwaiter() | ||||
|         { | ||||
|             return new AsyncAwaiter<T>(this); | ||||
|         } | ||||
|  | ||||
|         public new T Wait() | ||||
|         { | ||||
|             return (T)base.Wait(); | ||||
|         } | ||||
|  | ||||
|         public new T Wait(int millisecondsTimeout) | ||||
|         { | ||||
|             return (T)base.Wait(millisecondsTimeout); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|         protected new List<Action> callbacks = new List<Action>(); | ||||
|         protected new object result; | ||||
|  | ||||
|         protected new List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>(); | ||||
|  | ||||
|         protected new List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>(); | ||||
|  | ||||
|         protected new List<Action> chunkCallbacks = new List<Action>(); | ||||
|  | ||||
|         //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>(); | ||||
|  | ||||
|         object asyncLock = new object(); | ||||
|  | ||||
|         //public Timer timeout;// = new Timer() | ||||
|  | ||||
|         AsyncException exception; | ||||
|         // StackTrace trace; | ||||
|         AutoResetEvent mutex = new AutoResetEvent(false); | ||||
|  | ||||
|         public static int MaxId; | ||||
|  | ||||
|         public int Id; | ||||
|  | ||||
|         public bool Ready | ||||
|         { | ||||
|             get { return resultReady; } | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public T Wait() | ||||
|         { | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return result; | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Wait"); | ||||
|  | ||||
|             //mutex = new AutoResetEvent(false); | ||||
|             mutex.WaitOne(); | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Wait ended"); | ||||
|  | ||||
|  | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public object Result | ||||
|         { | ||||
|             get { return result; } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public IAsyncReply<T> Then(Action<T> callback) | ||||
|         { | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             lock (asyncLock) | ||||
|             { | ||||
|                 //  trace = new StackTrace(); | ||||
|  | ||||
|                 if (resultReady) | ||||
|                 { | ||||
|                     if (Debug) | ||||
|                         Console.WriteLine($"AsyncReply: {Id} Then ready"); | ||||
|  | ||||
|                     callback(result); | ||||
|                     return this; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 //timeout = new Timer(x => | ||||
|                 //{ | ||||
|                 //    // Get calling method name | ||||
|                 //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name); | ||||
|  | ||||
|                 //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name)); | ||||
|                 //    timeout.Dispose(); | ||||
|  | ||||
|                 //    tr = trace.ToString(); | ||||
|                 //    throw new Exception("Request timeout " + Id); | ||||
|                 //}, null, 15000, 0); | ||||
|  | ||||
|  | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Then pending"); | ||||
|  | ||||
|  | ||||
|  | ||||
|                 callbacks.Add(callback); | ||||
|  | ||||
|                 return this; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public IAsyncReply<T> Error(Action<AsyncException> callback) | ||||
|         { | ||||
|             // lock (callbacksLock) | ||||
|             //  { | ||||
|             errorCallbacks.Add(callback); | ||||
|  | ||||
|             if (exception != null) | ||||
|                 callback(exception); | ||||
|  | ||||
|             return this; | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|         public IAsyncReply<T> Progress(Action<ProgressType, int, int> callback) | ||||
|         { | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             progressCallbacks.Add(callback); | ||||
|             return this; | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public IAsyncReply<T> Chunk(Action<T> callback) | ||||
|         { | ||||
|             // lock (callbacksLock) | ||||
|             // { | ||||
|             chunkCallbacks.Add(callback); | ||||
|             return this; | ||||
|             // } | ||||
|         } | ||||
|  | ||||
|         public void Trigger(object result) | ||||
|         { | ||||
|             lock (asyncLock) | ||||
|             { | ||||
|                 //timeout?.Dispose(); | ||||
|  | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Trigger"); | ||||
|  | ||||
|                 if (resultReady) | ||||
|                     return; | ||||
|  | ||||
|                 this.result = (T)result; | ||||
|  | ||||
|                 resultReady = true; | ||||
|  | ||||
|                 //if (mutex != null) | ||||
|                 mutex.Set(); | ||||
|  | ||||
|                 foreach (var cb in callbacks) | ||||
|                     cb((T)result); | ||||
|  | ||||
|  | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Trigger ended"); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void TriggerError(Exception exception) | ||||
|         { | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             if (exception is AsyncException) | ||||
|                 this.exception = exception as AsyncException; | ||||
|             else | ||||
|                 this.exception = new AsyncException(ErrorType.Management, 0, exception.Message); | ||||
|  | ||||
|  | ||||
|             // lock (callbacksLock) | ||||
|             // { | ||||
|             foreach (var cb in errorCallbacks) | ||||
|                 cb(this.exception); | ||||
|             //  } | ||||
|  | ||||
|             mutex?.Set(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public void TriggerProgress(ProgressType type, int value, int max) | ||||
|         { | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             foreach (var cb in progressCallbacks) | ||||
|                 cb(type, value, max); | ||||
|  | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void TriggerChunk(object value) | ||||
|         { | ||||
|  | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             //lock (callbacksLock) | ||||
|             //{ | ||||
|             foreach (var cb in chunkCallbacks) | ||||
|                 cb((T)value); | ||||
|  | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|         public AsyncAwaiter<T> GetAwaiter() | ||||
|         { | ||||
|             return new AsyncAwaiter<T>(this); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public AsyncReply() | ||||
|         { | ||||
|             //   this.Debug = true; | ||||
|             Id = MaxId++; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply(T result) | ||||
|         { | ||||
|             //   this.Debug = true; | ||||
|             resultReady = true; | ||||
|             this.result = result; | ||||
|  | ||||
|             Id = MaxId++; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|     public AsyncReply<T> Then(Action<T> callback) | ||||
|         { | ||||
|            base.Then(new Action<object>(o => callback((T)o))); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public void Trigger(T result) | ||||
|         { | ||||
|             Trigger((object)result); | ||||
|         } | ||||
|  | ||||
|         public Task<bool> MoveNext(CancellationToken cancellationToken) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|          } | ||||
|  | ||||
|         public AsyncReply() | ||||
|         { | ||||
|              | ||||
|         } | ||||
|  | ||||
|         public new Task<T> Task | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return base.Task.ContinueWith<T>((t) => | ||||
|                 { | ||||
|  | ||||
| #if NETSTANDARD | ||||
|                     return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); | ||||
| #else | ||||
|                     return (T)t.GetType().GetProperty("Result").GetValue(t); | ||||
| #endif | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public T Current => throw new NotImplementedException(); | ||||
|  | ||||
|         | ||||
|  | ||||
|  */ | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public AsyncReply() | ||||
|         : base() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public new AsyncAwaiter<T> GetAwaiter() | ||||
|     { | ||||
|         return new AsyncAwaiter<T>(this); | ||||
|     } | ||||
|  | ||||
|     public new T Wait() | ||||
|     { | ||||
|         return (T)base.Wait(); | ||||
|     } | ||||
|  | ||||
|     public new T Wait(int millisecondsTimeout) | ||||
|     { | ||||
|         return (T)base.Wait(millisecondsTimeout); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     protected new List<Action> callbacks = new List<Action>(); | ||||
|     protected new object result; | ||||
|  | ||||
|     protected new List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>(); | ||||
|  | ||||
|     protected new List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>(); | ||||
|  | ||||
|     protected new List<Action> chunkCallbacks = new List<Action>(); | ||||
|  | ||||
|     //List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>(); | ||||
|  | ||||
|     object asyncLock = new object(); | ||||
|  | ||||
|     //public Timer timeout;// = new Timer() | ||||
|  | ||||
|     AsyncException exception; | ||||
|     // StackTrace trace; | ||||
|     AutoResetEvent mutex = new AutoResetEvent(false); | ||||
|  | ||||
|     public static int MaxId; | ||||
|  | ||||
|     public int Id; | ||||
|  | ||||
|     public bool Ready | ||||
|     { | ||||
|         get { return resultReady; } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public T Wait() | ||||
|     { | ||||
|  | ||||
|         if (resultReady) | ||||
|             return result; | ||||
|  | ||||
|         if (Debug) | ||||
|             Console.WriteLine($"AsyncReply: {Id} Wait"); | ||||
|  | ||||
|         //mutex = new AutoResetEvent(false); | ||||
|         mutex.WaitOne(); | ||||
|  | ||||
|         if (Debug) | ||||
|             Console.WriteLine($"AsyncReply: {Id} Wait ended"); | ||||
|  | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public object Result | ||||
|     { | ||||
|         get { return result; } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public IAsyncReply<T> Then(Action<T> callback) | ||||
|     { | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         lock (asyncLock) | ||||
|         { | ||||
|             //  trace = new StackTrace(); | ||||
|  | ||||
|             if (resultReady) | ||||
|             { | ||||
|                 if (Debug) | ||||
|                     Console.WriteLine($"AsyncReply: {Id} Then ready"); | ||||
|  | ||||
|                 callback(result); | ||||
|                 return this; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             //timeout = new Timer(x => | ||||
|             //{ | ||||
|             //    // Get calling method name | ||||
|             //    Console.WriteLine(trace.GetFrame(1).GetMethod().Name); | ||||
|  | ||||
|             //    var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name)); | ||||
|             //    timeout.Dispose(); | ||||
|  | ||||
|             //    tr = trace.ToString(); | ||||
|             //    throw new Exception("Request timeout " + Id); | ||||
|             //}, null, 15000, 0); | ||||
|  | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Then pending"); | ||||
|  | ||||
|  | ||||
|  | ||||
|             callbacks.Add(callback); | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public IAsyncReply<T> Error(Action<AsyncException> callback) | ||||
|     { | ||||
|         // lock (callbacksLock) | ||||
|         //  { | ||||
|         errorCallbacks.Add(callback); | ||||
|  | ||||
|         if (exception != null) | ||||
|             callback(exception); | ||||
|  | ||||
|         return this; | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|     public IAsyncReply<T> Progress(Action<ProgressType, int, int> callback) | ||||
|     { | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         progressCallbacks.Add(callback); | ||||
|         return this; | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public IAsyncReply<T> Chunk(Action<T> callback) | ||||
|     { | ||||
|         // lock (callbacksLock) | ||||
|         // { | ||||
|         chunkCallbacks.Add(callback); | ||||
|         return this; | ||||
|         // } | ||||
|     } | ||||
|  | ||||
|     public void Trigger(object result) | ||||
|     { | ||||
|         lock (asyncLock) | ||||
|         { | ||||
|             //timeout?.Dispose(); | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Trigger"); | ||||
|  | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             this.result = (T)result; | ||||
|  | ||||
|             resultReady = true; | ||||
|  | ||||
|             //if (mutex != null) | ||||
|             mutex.Set(); | ||||
|  | ||||
|             foreach (var cb in callbacks) | ||||
|                 cb((T)result); | ||||
|  | ||||
|  | ||||
|             if (Debug) | ||||
|                 Console.WriteLine($"AsyncReply: {Id} Trigger ended"); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void TriggerError(Exception exception) | ||||
|     { | ||||
|         //timeout?.Dispose(); | ||||
|  | ||||
|         if (resultReady) | ||||
|             return; | ||||
|  | ||||
|         if (exception is AsyncException) | ||||
|             this.exception = exception as AsyncException; | ||||
|         else | ||||
|             this.exception = new AsyncException(ErrorType.Management, 0, exception.Message); | ||||
|  | ||||
|  | ||||
|         // lock (callbacksLock) | ||||
|         // { | ||||
|         foreach (var cb in errorCallbacks) | ||||
|             cb(this.exception); | ||||
|         //  } | ||||
|  | ||||
|         mutex?.Set(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void TriggerProgress(ProgressType type, int value, int max) | ||||
|     { | ||||
|         //timeout?.Dispose(); | ||||
|  | ||||
|         if (resultReady) | ||||
|             return; | ||||
|  | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         foreach (var cb in progressCallbacks) | ||||
|             cb(type, value, max); | ||||
|  | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void TriggerChunk(object value) | ||||
|     { | ||||
|  | ||||
|         //timeout?.Dispose(); | ||||
|  | ||||
|         if (resultReady) | ||||
|             return; | ||||
|  | ||||
|         //lock (callbacksLock) | ||||
|         //{ | ||||
|         foreach (var cb in chunkCallbacks) | ||||
|             cb((T)value); | ||||
|  | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|     public AsyncAwaiter<T> GetAwaiter() | ||||
|     { | ||||
|         return new AsyncAwaiter<T>(this); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public AsyncReply() | ||||
|     { | ||||
|         //   this.Debug = true; | ||||
|         Id = MaxId++; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply(T result) | ||||
|     { | ||||
|         //   this.Debug = true; | ||||
|         resultReady = true; | ||||
|         this.result = result; | ||||
|  | ||||
|         Id = MaxId++; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
| public AsyncReply<T> Then(Action<T> callback) | ||||
|     { | ||||
|        base.Then(new Action<object>(o => callback((T)o))); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public void Trigger(T result) | ||||
|     { | ||||
|         Trigger((object)result); | ||||
|     } | ||||
|  | ||||
|     public Task<bool> MoveNext(CancellationToken cancellationToken) | ||||
|     { | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|     public void Dispose() | ||||
|     { | ||||
|      } | ||||
|  | ||||
|     public AsyncReply() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public new Task<T> Task | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return base.Task.ContinueWith<T>((t) => | ||||
|             { | ||||
|  | ||||
| #if NETSTANDARD | ||||
|                 return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); | ||||
| #else | ||||
|                 return (T)t.GetType().GetProperty("Result").GetValue(t); | ||||
| #endif | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public T Current => throw new NotImplementedException(); | ||||
|  | ||||
|  | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -28,158 +28,157 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public class AsyncReply | ||||
| { | ||||
|     public class AsyncReply | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     protected List<Action<object>> callbacks = new List<Action<object>>(); | ||||
|     protected object result; | ||||
|  | ||||
|     protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>(); | ||||
|  | ||||
|     protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>(); | ||||
|  | ||||
|     protected List<Action<object>> chunkCallbacks = new List<Action<object>>(); | ||||
|  | ||||
|     object callbacksLock = new object(); | ||||
|  | ||||
|     protected bool resultReady = false; | ||||
|     AsyncException exception; | ||||
|  | ||||
|     TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); | ||||
|  | ||||
|  | ||||
|     public bool Ready | ||||
|     { | ||||
|     | ||||
|         get { return resultReady; } | ||||
|     } | ||||
|  | ||||
|     public object Result | ||||
|     { | ||||
|         get { return result; } | ||||
|     } | ||||
|  | ||||
|     public AsyncReply Then(Action<object> callback) | ||||
|     { | ||||
|         callbacks.Add(callback); | ||||
|  | ||||
|         protected List<Action<object>> callbacks = new List<Action<object>>(); | ||||
|         protected object result; | ||||
|         if (resultReady) | ||||
|             callback(result); | ||||
|  | ||||
|         protected List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>(); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|         protected List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>(); | ||||
|     public AsyncReply Error(Action<AsyncException> callback) | ||||
|     { | ||||
|         errorCallbacks.Add(callback); | ||||
|  | ||||
|         protected List<Action<object>> chunkCallbacks = new List<Action<object>>(); | ||||
|  | ||||
|         object callbacksLock = new object(); | ||||
|  | ||||
|         protected bool resultReady = false; | ||||
|         AsyncException exception; | ||||
|  | ||||
|         TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); | ||||
|  | ||||
|  | ||||
|         public bool Ready | ||||
|         if (exception != null) | ||||
|         { | ||||
|             get { return resultReady; } | ||||
|             callback(exception); | ||||
|             tcs.SetException(exception); | ||||
|         } | ||||
|  | ||||
|         public object Result | ||||
|         { | ||||
|             get { return result; } | ||||
|         } | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|         public AsyncReply Then(Action<object> callback) | ||||
|         { | ||||
|             callbacks.Add(callback); | ||||
|     public AsyncReply Progress(Action<ProgressType, int, int> callback) | ||||
|     { | ||||
|         progressCallbacks.Add(callback); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|             if (resultReady) | ||||
|                 callback(result); | ||||
|     public AsyncReply Chunk(Action<object> callback) | ||||
|     { | ||||
|         chunkCallbacks.Add(callback); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|     public void Trigger(object result) | ||||
|     { | ||||
|  | ||||
|         public AsyncReply Error(Action<AsyncException> callback) | ||||
|         { | ||||
|             errorCallbacks.Add(callback); | ||||
|  | ||||
|             if (exception != null) | ||||
|             { | ||||
|                 callback(exception); | ||||
|                 tcs.SetException(exception); | ||||
|             } | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Progress(Action<ProgressType, int, int> callback) | ||||
|         { | ||||
|             progressCallbacks.Add(callback); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Chunk(Action<object> callback) | ||||
|         { | ||||
|             chunkCallbacks.Add(callback); | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public void Trigger(object result) | ||||
|         { | ||||
|  | ||||
|             lock (callbacksLock) | ||||
|             { | ||||
|                 if (resultReady) | ||||
|                     return; | ||||
|  | ||||
|                 this.result = result; | ||||
|                 resultReady = true; | ||||
|  | ||||
|                 foreach (var cb in callbacks) | ||||
|                     cb(result); | ||||
|  | ||||
|                 tcs.TrySetResult(result); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public void TriggerError(AsyncException exception) | ||||
|         lock (callbacksLock) | ||||
|         { | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             this.exception = exception; | ||||
|  | ||||
|  | ||||
|             lock (callbacksLock) | ||||
|             { | ||||
|                 foreach (var cb in errorCallbacks) | ||||
|                     cb(exception); | ||||
|             } | ||||
|  | ||||
|             tcs.TrySetException(exception); | ||||
|         } | ||||
|  | ||||
|         public void TriggerProgress(ProgressType type, int value, int max) | ||||
|         { | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             lock (callbacksLock) | ||||
|             { | ||||
|                 foreach (var cb in progressCallbacks) | ||||
|                     cb(type, value, max); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void TriggerChunk(object value) | ||||
|         { | ||||
|             if (resultReady) | ||||
|                 return; | ||||
|  | ||||
|             lock (callbacksLock) | ||||
|             { | ||||
|                 foreach (var cb in chunkCallbacks) | ||||
|                     cb(value); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public Task Task | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return tcs.Task; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public AsyncReply() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public AsyncReply(object result) | ||||
|         { | ||||
|             resultReady = true; | ||||
|             tcs.SetResult(result); | ||||
|             this.result = result; | ||||
|             resultReady = true; | ||||
|  | ||||
|             foreach (var cb in callbacks) | ||||
|                 cb(result); | ||||
|  | ||||
|             tcs.TrySetResult(result); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void TriggerError(AsyncException exception) | ||||
|     { | ||||
|         if (resultReady) | ||||
|             return; | ||||
|  | ||||
|         this.exception = exception; | ||||
|  | ||||
|  | ||||
|         lock (callbacksLock) | ||||
|         { | ||||
|             foreach (var cb in errorCallbacks) | ||||
|                 cb(exception); | ||||
|         } | ||||
|  | ||||
|         tcs.TrySetException(exception); | ||||
|     } | ||||
|  | ||||
|     public void TriggerProgress(ProgressType type, int value, int max) | ||||
|     { | ||||
|         if (resultReady) | ||||
|             return; | ||||
|  | ||||
|         lock (callbacksLock) | ||||
|         { | ||||
|             foreach (var cb in progressCallbacks) | ||||
|                 cb(type, value, max); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void TriggerChunk(object value) | ||||
|     { | ||||
|         if (resultReady) | ||||
|             return; | ||||
|  | ||||
|         lock (callbacksLock) | ||||
|         { | ||||
|             foreach (var cb in chunkCallbacks) | ||||
|                 cb(value); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public Task Task | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return tcs.Task; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public AsyncReply() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public AsyncReply(object result) | ||||
|     { | ||||
|         resultReady = true; | ||||
|         tcs.SetResult(result); | ||||
|         this.result = result; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,11 +2,10 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public enum ErrorType | ||||
| { | ||||
|     public enum ErrorType | ||||
|     { | ||||
|         Management, | ||||
|         Exception | ||||
|     } | ||||
|     Management, | ||||
|     Exception | ||||
| } | ||||
|   | ||||
| @@ -2,44 +2,43 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public enum ExceptionCode : ushort | ||||
| { | ||||
|     public enum ExceptionCode : ushort | ||||
|     { | ||||
|         HostNotReachable, | ||||
|         AccessDenied, | ||||
|         UserOrTokenNotFound, | ||||
|         ChallengeFailed, | ||||
|         ResourceNotFound, | ||||
|         AttachDenied, | ||||
|         InvalidMethod, | ||||
|         InvokeDenied, | ||||
|         CreateDenied, | ||||
|         AddParentDenied, | ||||
|         AddChildDenied, | ||||
|         ViewAttributeDenied, | ||||
|         UpdateAttributeDenied, | ||||
|         StoreNotFound, | ||||
|         ParentNotFound, | ||||
|         ChildNotFound, | ||||
|         ResourceIsNotStore, | ||||
|         DeleteDenied, | ||||
|         DeleteFailed, | ||||
|         UpdateAttributeFailed, | ||||
|         GetAttributesFailed, | ||||
|         ClearAttributesFailed, | ||||
|         TemplateNotFound, | ||||
|         RenameDenied, | ||||
|         ClassNotFound, | ||||
|         MethodNotFound, | ||||
|         PropertyNotFound, | ||||
|         SetPropertyDenied, | ||||
|         ReadOnlyProperty, | ||||
|         GeneralFailure, | ||||
|         AddToStoreFailed, | ||||
|         NotAttached, | ||||
|         AlreadyListened, | ||||
|         AlreadyUnlistened, | ||||
|         NotListenable | ||||
|     } | ||||
|     HostNotReachable, | ||||
|     AccessDenied, | ||||
|     UserOrTokenNotFound, | ||||
|     ChallengeFailed, | ||||
|     ResourceNotFound, | ||||
|     AttachDenied, | ||||
|     InvalidMethod, | ||||
|     InvokeDenied, | ||||
|     CreateDenied, | ||||
|     AddParentDenied, | ||||
|     AddChildDenied, | ||||
|     ViewAttributeDenied, | ||||
|     UpdateAttributeDenied, | ||||
|     StoreNotFound, | ||||
|     ParentNotFound, | ||||
|     ChildNotFound, | ||||
|     ResourceIsNotStore, | ||||
|     DeleteDenied, | ||||
|     DeleteFailed, | ||||
|     UpdateAttributeFailed, | ||||
|     GetAttributesFailed, | ||||
|     ClearAttributesFailed, | ||||
|     TemplateNotFound, | ||||
|     RenameDenied, | ||||
|     ClassNotFound, | ||||
|     MethodNotFound, | ||||
|     PropertyNotFound, | ||||
|     SetPropertyDenied, | ||||
|     ReadOnlyProperty, | ||||
|     GeneralFailure, | ||||
|     AddToStoreFailed, | ||||
|     NotAttached, | ||||
|     AlreadyListened, | ||||
|     AlreadyUnlistened, | ||||
|     NotListenable | ||||
| } | ||||
|   | ||||
| @@ -2,13 +2,12 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public enum ExceptionLevel | ||||
| { | ||||
|     public enum ExceptionLevel | ||||
|     { | ||||
|         Code = 0x1, | ||||
|         Message = 0x2, | ||||
|         Source = 0x4, | ||||
|         Trace = 0x8 | ||||
|     } | ||||
|     Code = 0x1, | ||||
|     Message = 0x2, | ||||
|     Source = 0x4, | ||||
|     Trace = 0x8 | ||||
| } | ||||
|   | ||||
| @@ -3,19 +3,18 @@ using System.Collections.Generic; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| { | ||||
|     public interface IAsyncReply<out T>//IAsyncEnumerator<T>  | ||||
|     {    | ||||
|         IAsyncReply<T> Then(Action<T> callback); | ||||
|         IAsyncReply<T> Error(Action<AsyncException> callback); | ||||
|         IAsyncReply<T> Progress(Action<ProgressType, int, int> callback); | ||||
|         IAsyncReply<T> Chunk(Action<T> callback); | ||||
|         void Trigger(object result); | ||||
|         void TriggerError(Exception exception); | ||||
|         void TriggerProgress(ProgressType type, int value, int max); | ||||
|         void TriggerChunk(object value); | ||||
| namespace Esiur.Core; | ||||
|  | ||||
|         T Wait(); | ||||
|     } | ||||
| public interface IAsyncReply<out T>//IAsyncEnumerator<T>  | ||||
| { | ||||
|     IAsyncReply<T> Then(Action<T> callback); | ||||
|     IAsyncReply<T> Error(Action<AsyncException> callback); | ||||
|     IAsyncReply<T> Progress(Action<ProgressType, int, int> callback); | ||||
|     IAsyncReply<T> Chunk(Action<T> callback); | ||||
|     void Trigger(object result); | ||||
|     void TriggerError(Exception exception); | ||||
|     void TriggerProgress(ProgressType type, int value, int max); | ||||
|     void TriggerChunk(object value); | ||||
|  | ||||
|     T Wait(); | ||||
| } | ||||
|   | ||||
| @@ -27,13 +27,12 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| { | ||||
|     public delegate void DestroyedEvent(object sender); | ||||
| namespace Esiur.Core; | ||||
|  | ||||
|     public interface IDestructible | ||||
|     { | ||||
|         event DestroyedEvent OnDestroy; | ||||
|         void Destroy(); | ||||
|     } | ||||
| public delegate void DestroyedEvent(object sender); | ||||
|  | ||||
| public interface IDestructible | ||||
| { | ||||
|     event DestroyedEvent OnDestroy; | ||||
|     void Destroy(); | ||||
| } | ||||
|   | ||||
| @@ -29,12 +29,11 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public enum LogType | ||||
| { | ||||
|     public enum LogType | ||||
|     { | ||||
|         Debug, | ||||
|         Warning, | ||||
|         Error, | ||||
|     } | ||||
|     Debug, | ||||
|     Warning, | ||||
|     Error, | ||||
| } | ||||
|   | ||||
| @@ -2,11 +2,10 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Core | ||||
| namespace Esiur.Core; | ||||
|  | ||||
| public enum ProgressType | ||||
| { | ||||
|     public enum ProgressType | ||||
|     { | ||||
|         Execution, | ||||
|         Network, | ||||
|     } | ||||
|     Execution, | ||||
|     Network, | ||||
| } | ||||
|   | ||||
| @@ -30,285 +30,284 @@ using System.Collections; | ||||
| using Esiur.Core; | ||||
| using System.Reflection; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T> | ||||
| { | ||||
|     public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T> | ||||
|  | ||||
|     private readonly object syncRoot = new object(); | ||||
|     private List<T> list = new List<T>(); | ||||
|  | ||||
|     public delegate void Modified(ST sender, int index, T oldValue, T newValue); | ||||
|     public delegate void Added(ST sender, T value); | ||||
|     public delegate void Removed(ST sender, T value); | ||||
|     public delegate void Cleared(ST sender); | ||||
|  | ||||
|  | ||||
|     public event Modified OnModified; | ||||
|     public event Removed OnRemoved; | ||||
|     public event Cleared OnCleared; | ||||
|     public event Added OnAdd; | ||||
|  | ||||
|     bool removableList; | ||||
|  | ||||
|  | ||||
|     public ST State { get; set; } | ||||
|     /* | ||||
|     IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector) | ||||
|     { | ||||
|         return list.OrderBy<T,TK>(keySelector); | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|         private readonly object syncRoot = new object(); | ||||
|         private List<T> list = new List<T>(); | ||||
|  | ||||
|         public delegate void Modified(ST sender, int index, T oldValue, T newValue); | ||||
|         public delegate void Added(ST sender, T value); | ||||
|         public delegate void Removed(ST sender, T value); | ||||
|         public delegate void Cleared(ST sender); | ||||
|  | ||||
|  | ||||
|         public event Modified OnModified; | ||||
|         public event Removed OnRemoved; | ||||
|         public event Cleared OnCleared; | ||||
|         public event Added OnAdd; | ||||
|  | ||||
|         bool removableList; | ||||
|  | ||||
|  | ||||
|         public ST State { get; set; } | ||||
|         /* | ||||
|         IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector) | ||||
|         { | ||||
|             return list.OrderBy<T,TK>(keySelector); | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public void Sort() | ||||
|         { | ||||
|             lock(syncRoot) | ||||
|     public void Sort() | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|             list.Sort(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public void Sort(IComparer<T> comparer) | ||||
|         { | ||||
|             lock (syncRoot) | ||||
|                 list.Sort(comparer); | ||||
|         } | ||||
|     public void Sort(IComparer<T> comparer) | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|             list.Sort(comparer); | ||||
|     } | ||||
|  | ||||
|         public void Sort(Comparison<T> comparison) | ||||
|         { | ||||
|             lock (syncRoot) | ||||
|                 list.Sort(comparison); | ||||
|         } | ||||
|     public void Sort(Comparison<T> comparison) | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|             list.Sort(comparison); | ||||
|     } | ||||
|  | ||||
|         public IEnumerable<T> Where(Func<T, bool> predicate) | ||||
|         { | ||||
|             return list.Where(predicate); | ||||
|         } | ||||
|     public IEnumerable<T> Where(Func<T, bool> predicate) | ||||
|     { | ||||
|         return list.Where(predicate); | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Convert AutoList to array | ||||
|         /// </summary> | ||||
|         /// <returns>Array</returns> | ||||
|         public T[] ToArray() | ||||
|         { | ||||
|             //    list.OrderBy() | ||||
|             return list.ToArray(); | ||||
|         } | ||||
|     /// <summary> | ||||
|     /// Convert AutoList to array | ||||
|     /// </summary> | ||||
|     /// <returns>Array</returns> | ||||
|     public T[] ToArray() | ||||
|     { | ||||
|         //    list.OrderBy() | ||||
|         return list.ToArray(); | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create a new instance of AutoList | ||||
|         /// </summary> | ||||
|         /// <param name="state">State object to be included when an event is raised.</param> | ||||
|         public AutoList(ST state) | ||||
|         { | ||||
|             State = state; | ||||
|     /// <summary> | ||||
|     /// Create a new instance of AutoList | ||||
|     /// </summary> | ||||
|     /// <param name="state">State object to be included when an event is raised.</param> | ||||
|     public AutoList(ST state) | ||||
|     { | ||||
|         State = state; | ||||
| #if NETSTANDARD | ||||
|             removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); | ||||
|         removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); | ||||
| #else | ||||
|                         removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T))); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create a new instance of AutoList | ||||
|         /// </summary> | ||||
|         /// <param name="values">Populate the list with items</param> | ||||
|         /// <returns></returns> | ||||
|         public AutoList(ST state, T[] values) | ||||
|         { | ||||
|             State = state; | ||||
|     /// <summary> | ||||
|     /// Create a new instance of AutoList | ||||
|     /// </summary> | ||||
|     /// <param name="values">Populate the list with items</param> | ||||
|     /// <returns></returns> | ||||
|     public AutoList(ST state, T[] values) | ||||
|     { | ||||
|         State = state; | ||||
| #if NETSTANDARD | ||||
|             removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); | ||||
|         removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); | ||||
| #else | ||||
|                                         removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T))); | ||||
| #endif | ||||
|  | ||||
|             AddRange(values); | ||||
|         } | ||||
|         AddRange(values); | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Synchronization lock of the list | ||||
|         /// </summary> | ||||
|         public object SyncRoot | ||||
|     /// <summary> | ||||
|     /// Synchronization lock of the list | ||||
|     /// </summary> | ||||
|     public object SyncRoot | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return syncRoot; | ||||
|             } | ||||
|             return syncRoot; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// First item in the list | ||||
|         /// </summary> | ||||
|         public T First() | ||||
|     /// <summary> | ||||
|     /// First item in the list | ||||
|     /// </summary> | ||||
|     public T First() | ||||
|     { | ||||
|         return list.First(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Get an item at a specified index | ||||
|     /// </summary> | ||||
|     public T this[int index] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return list.First(); | ||||
|             return list[index]; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Get an item at a specified index | ||||
|         /// </summary> | ||||
|         public T this[int index] | ||||
|         set | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return list[index]; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 var oldValue = list[index]; | ||||
|             var oldValue = list[index]; | ||||
|  | ||||
|                 if (removableList) | ||||
|                 { | ||||
|                     if (oldValue != null) | ||||
|                         ((IDestructible)oldValue).OnDestroy -= ItemDestroyed; | ||||
|                     if (value != null) | ||||
|                         ((IDestructible)value).OnDestroy += ItemDestroyed; | ||||
|                 } | ||||
|  | ||||
|                 lock (syncRoot) | ||||
|                     list[index] = value; | ||||
|  | ||||
|                 OnModified?.Invoke(State, index, oldValue, value); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Add item to the list | ||||
|         /// </summary> | ||||
|         public void Add(T value) | ||||
|         { | ||||
|             if (removableList) | ||||
|             { | ||||
|                 if (oldValue != null) | ||||
|                     ((IDestructible)oldValue).OnDestroy -= ItemDestroyed; | ||||
|                 if (value != null) | ||||
|                     ((IDestructible)value).OnDestroy += ItemDestroyed; | ||||
|             } | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 list.Add(value); | ||||
|                 list[index] = value; | ||||
|  | ||||
|             OnAdd?.Invoke(State, value); | ||||
|             OnModified?.Invoke(State, index, oldValue, value); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Add an array of items to the list | ||||
|         /// </summary> | ||||
|         public void AddRange(T[] values) | ||||
|         { | ||||
|             foreach (var v in values) | ||||
|                 Add(v); | ||||
|         } | ||||
|  | ||||
|         private void ItemDestroyed(object sender) | ||||
|         { | ||||
|             Remove((T)sender); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Clear the list | ||||
|         /// </summary> | ||||
|         public void Clear() | ||||
|         { | ||||
|             if (removableList) | ||||
|                 foreach (IDestructible v in list) | ||||
|                     if (v != null) | ||||
|                         v.OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 list.Clear(); | ||||
|  | ||||
|             OnCleared?.Invoke(State); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Remove an item from the list | ||||
|         /// <param name="value">Item to remove</param> | ||||
|         /// </summary> | ||||
|         public bool Remove(T value) | ||||
|         { | ||||
|             if (!list.Contains(value)) | ||||
|                 return false; | ||||
|  | ||||
|             if (removableList) | ||||
|                 if (value != null) | ||||
|                     ((IDestructible)value).OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 list.Remove(value); | ||||
|  | ||||
|             OnRemoved?.Invoke(State, value); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Number of items in the list | ||||
|         /// </summary> | ||||
|         public int Count | ||||
|         { | ||||
|             get { return list.Count; } | ||||
|         } | ||||
|  | ||||
|         public bool IsSynchronized => (list as ICollection).IsSynchronized; | ||||
|  | ||||
|         public bool IsReadOnly => false; | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Check if an item exists in the list | ||||
|         /// </summary> | ||||
|         /// <param name="value">Item to check if exists</param> | ||||
|         public bool Contains(T value) | ||||
|         { | ||||
|             return list.Contains(value); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Check if any item of the given array is in the list | ||||
|         /// </summary> | ||||
|         /// <param name="values">Array of items</param> | ||||
|         public bool ContainsAny(T[] values) | ||||
|         { | ||||
|             foreach (var v in values) | ||||
|                 if (list.Contains(v)) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Check if any item of the given list is in the list | ||||
|         /// </summary> | ||||
|         /// <param name="values">List of items</param> | ||||
|         public bool ContainsAny(AutoList<T, ST> values) | ||||
|         { | ||||
|             foreach (var v in values) | ||||
|                 if (list.Contains((T)v)) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public IEnumerator<T> GetEnumerator() | ||||
|         { | ||||
|             return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         IEnumerator IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         public void CopyTo(Array array, int index) | ||||
|         { | ||||
|             lock (syncRoot) | ||||
|                 (list as ICollection).CopyTo(array, index); | ||||
|         } | ||||
|  | ||||
|         public void CopyTo(T[] array, int arrayIndex) | ||||
|         { | ||||
|             lock (syncRoot) | ||||
|                 list.CopyTo(array, arrayIndex); | ||||
|         } | ||||
|  | ||||
|         //bool ICollection<T>.Remove(T item) | ||||
|         //{ | ||||
|         //    lock(syncRoot) | ||||
|         //        return Remove(item); | ||||
|         //} | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Add item to the list | ||||
|     /// </summary> | ||||
|     public void Add(T value) | ||||
|     { | ||||
|         if (removableList) | ||||
|             if (value != null) | ||||
|                 ((IDestructible)value).OnDestroy += ItemDestroyed; | ||||
|  | ||||
|         lock (syncRoot) | ||||
|             list.Add(value); | ||||
|  | ||||
|         OnAdd?.Invoke(State, value); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Add an array of items to the list | ||||
|     /// </summary> | ||||
|     public void AddRange(T[] values) | ||||
|     { | ||||
|         foreach (var v in values) | ||||
|             Add(v); | ||||
|     } | ||||
|  | ||||
|     private void ItemDestroyed(object sender) | ||||
|     { | ||||
|         Remove((T)sender); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Clear the list | ||||
|     /// </summary> | ||||
|     public void Clear() | ||||
|     { | ||||
|         if (removableList) | ||||
|             foreach (IDestructible v in list) | ||||
|                 if (v != null) | ||||
|                     v.OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|         lock (syncRoot) | ||||
|             list.Clear(); | ||||
|  | ||||
|         OnCleared?.Invoke(State); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Remove an item from the list | ||||
|     /// <param name="value">Item to remove</param> | ||||
|     /// </summary> | ||||
|     public bool Remove(T value) | ||||
|     { | ||||
|         if (!list.Contains(value)) | ||||
|             return false; | ||||
|  | ||||
|         if (removableList) | ||||
|             if (value != null) | ||||
|                 ((IDestructible)value).OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|         lock (syncRoot) | ||||
|             list.Remove(value); | ||||
|  | ||||
|         OnRemoved?.Invoke(State, value); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Number of items in the list | ||||
|     /// </summary> | ||||
|     public int Count | ||||
|     { | ||||
|         get { return list.Count; } | ||||
|     } | ||||
|  | ||||
|     public bool IsSynchronized => (list as ICollection).IsSynchronized; | ||||
|  | ||||
|     public bool IsReadOnly => false; | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Check if an item exists in the list | ||||
|     /// </summary> | ||||
|     /// <param name="value">Item to check if exists</param> | ||||
|     public bool Contains(T value) | ||||
|     { | ||||
|         return list.Contains(value); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Check if any item of the given array is in the list | ||||
|     /// </summary> | ||||
|     /// <param name="values">Array of items</param> | ||||
|     public bool ContainsAny(T[] values) | ||||
|     { | ||||
|         foreach (var v in values) | ||||
|             if (list.Contains(v)) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Check if any item of the given list is in the list | ||||
|     /// </summary> | ||||
|     /// <param name="values">List of items</param> | ||||
|     public bool ContainsAny(AutoList<T, ST> values) | ||||
|     { | ||||
|         foreach (var v in values) | ||||
|             if (list.Contains((T)v)) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public IEnumerator<T> GetEnumerator() | ||||
|     { | ||||
|         return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     IEnumerator IEnumerable.GetEnumerator() | ||||
|     { | ||||
|         return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     public void CopyTo(Array array, int index) | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|             (list as ICollection).CopyTo(array, index); | ||||
|     } | ||||
|  | ||||
|     public void CopyTo(T[] array, int arrayIndex) | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|             list.CopyTo(array, arrayIndex); | ||||
|     } | ||||
|  | ||||
|     //bool ICollection<T>.Remove(T item) | ||||
|     //{ | ||||
|     //    lock(syncRoot) | ||||
|     //        return Remove(item); | ||||
|     //} | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2874
									
								
								Esiur/Data/Codec.cs
									
									
									
									
									
								
							
							
						
						
									
										2874
									
								
								Esiur/Data/Codec.cs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,92 +28,90 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
| public enum DataType : byte | ||||
| { | ||||
|     public enum DataType : byte | ||||
|     { | ||||
|         Void = 0x0, | ||||
|         //Variant, | ||||
|         Bool, | ||||
|         Int8, | ||||
|         UInt8, | ||||
|         Char, | ||||
|         Int16, | ||||
|         UInt16, | ||||
|         Int32, | ||||
|         UInt32, | ||||
|         Int64, | ||||
|         UInt64, | ||||
|         Float32, | ||||
|         Float64, | ||||
|         Decimal, | ||||
|         DateTime, | ||||
|         Resource, | ||||
|         DistributedResource, | ||||
|         ResourceLink, | ||||
|         String, | ||||
|         Structure, | ||||
|         Record, | ||||
|         //Stream, | ||||
|         //Array = 0x80, | ||||
|         VarArray = 0x80, | ||||
|         BoolArray, | ||||
|         Int8Array, | ||||
|         UInt8Array, | ||||
|         CharArray, | ||||
|         Int16Array, | ||||
|         UInt16Array, | ||||
|         Int32Array, | ||||
|         UInt32Array, | ||||
|         Int64Array, | ||||
|         UInt64Array, | ||||
|         Float32Array, | ||||
|         Float64Array, | ||||
|         DecimalArray, | ||||
|         DateTimeArray, | ||||
|         ResourceArray, | ||||
|         DistributedResourceArray, | ||||
|         ResourceLinkArray, | ||||
|         StringArray, | ||||
|         StructureArray, | ||||
|         RecordArray, | ||||
|         NotModified = 0x7f, | ||||
|         Unspecified = 0xff, | ||||
|     } | ||||
|     Void = 0x0, | ||||
|     //Variant, | ||||
|     Bool, | ||||
|     Int8, | ||||
|     UInt8, | ||||
|     Char, | ||||
|     Int16, | ||||
|     UInt16, | ||||
|     Int32, | ||||
|     UInt32, | ||||
|     Int64, | ||||
|     UInt64, | ||||
|     Float32, | ||||
|     Float64, | ||||
|     Decimal, | ||||
|     DateTime, | ||||
|     Resource, | ||||
|     DistributedResource, | ||||
|     ResourceLink, | ||||
|     String, | ||||
|     Structure, | ||||
|     Record, | ||||
|     //Stream, | ||||
|     //Array = 0x80, | ||||
|     VarArray = 0x80, | ||||
|     BoolArray, | ||||
|     Int8Array, | ||||
|     UInt8Array, | ||||
|     CharArray, | ||||
|     Int16Array, | ||||
|     UInt16Array, | ||||
|     Int32Array, | ||||
|     UInt32Array, | ||||
|     Int64Array, | ||||
|     UInt64Array, | ||||
|     Float32Array, | ||||
|     Float64Array, | ||||
|     DecimalArray, | ||||
|     DateTimeArray, | ||||
|     ResourceArray, | ||||
|     DistributedResourceArray, | ||||
|     ResourceLinkArray, | ||||
|     StringArray, | ||||
|     StructureArray, | ||||
|     RecordArray, | ||||
|     NotModified = 0x7f, | ||||
|     Unspecified = 0xff, | ||||
| } | ||||
|  | ||||
|     public static class DataTypeExpansions | ||||
| public static class DataTypeExpansions | ||||
| { | ||||
|     public static int Size(this DataType t) | ||||
|     { | ||||
|         public static int Size(this DataType t) | ||||
|         switch (t) | ||||
|         { | ||||
|             switch (t) | ||||
|             { | ||||
|                 case DataType.Void: | ||||
|                 case DataType.NotModified: | ||||
|                     return 0; | ||||
|                 case DataType.Bool: | ||||
|                 case DataType.UInt8: | ||||
|                 case DataType.Int8: | ||||
|                     return 1; | ||||
|                 case DataType.Char: | ||||
|                 case DataType.UInt16: | ||||
|                 case DataType.Int16: | ||||
|                     return 2; | ||||
|                 case DataType.Int32: | ||||
|                 case DataType.UInt32: | ||||
|                 case DataType.Float32: | ||||
|                 case DataType.Resource: | ||||
|                     return 4; | ||||
|                 case DataType.Int64: | ||||
|                 case DataType.UInt64: | ||||
|                 case DataType.Float64: | ||||
|                 case DataType.DateTime: | ||||
|                     return 8; | ||||
|                 case DataType.DistributedResource: | ||||
|                     return 4; | ||||
|             case DataType.Void: | ||||
|             case DataType.NotModified: | ||||
|                 return 0; | ||||
|             case DataType.Bool: | ||||
|             case DataType.UInt8: | ||||
|             case DataType.Int8: | ||||
|                 return 1; | ||||
|             case DataType.Char: | ||||
|             case DataType.UInt16: | ||||
|             case DataType.Int16: | ||||
|                 return 2; | ||||
|             case DataType.Int32: | ||||
|             case DataType.UInt32: | ||||
|             case DataType.Float32: | ||||
|             case DataType.Resource: | ||||
|                 return 4; | ||||
|             case DataType.Int64: | ||||
|             case DataType.UInt64: | ||||
|             case DataType.Float64: | ||||
|             case DataType.DateTime: | ||||
|                 return 8; | ||||
|             case DataType.DistributedResource: | ||||
|                 return 4; | ||||
|  | ||||
|                 default: | ||||
|                     return -1; | ||||
|             } | ||||
|             default: | ||||
|                 return -1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,10 +2,9 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| { | ||||
|     public interface IRecord | ||||
|     { | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public interface IRecord | ||||
| { | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,11 +2,10 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
| public interface IUserType | ||||
| { | ||||
|     public interface IUserType | ||||
|     { | ||||
|         object Get(); | ||||
|         void Set(object value); | ||||
|     } | ||||
|     object Get(); | ||||
|     void Set(object value); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -33,212 +33,210 @@ using System.Linq; | ||||
| using System.Linq.Expressions; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>> | ||||
| { | ||||
|     private readonly object syncRoot = new object(); | ||||
|     private Dictionary<KT, T> dic; | ||||
|  | ||||
|     public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>> | ||||
|     public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender); | ||||
|     public delegate void Added(T value, KeyList<KT, T> sender); | ||||
|     public delegate void Removed(KT key, T value, KeyList<KT, T> sender); | ||||
|     public delegate void Cleared(KeyList<KT, T> sender); | ||||
|  | ||||
|     public event Modified OnModified; | ||||
|     public event Removed OnRemoved; | ||||
|     public event Cleared OnCleared; | ||||
|     public event Added OnAdd; | ||||
|  | ||||
|     bool removableList; | ||||
|  | ||||
|     public object SyncRoot | ||||
|     { | ||||
|         private readonly object syncRoot = new object(); | ||||
|         private Dictionary<KT, T> dic; | ||||
|  | ||||
|         public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender); | ||||
|         public delegate void Added(T value, KeyList<KT, T> sender); | ||||
|         public delegate void Removed(KT key, T value, KeyList<KT, T> sender); | ||||
|         public delegate void Cleared(KeyList<KT, T> sender); | ||||
|  | ||||
|         public event Modified OnModified; | ||||
|         public event Removed OnRemoved; | ||||
|         public event Cleared OnCleared; | ||||
|         public event Added OnAdd; | ||||
|  | ||||
|         bool removableList; | ||||
|  | ||||
|         public object SyncRoot | ||||
|         get | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return syncRoot; | ||||
|             } | ||||
|             return syncRoot; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public T Take(KT key) | ||||
|     public T Take(KT key) | ||||
|     { | ||||
|         if (dic.ContainsKey(key)) | ||||
|         { | ||||
|             var v = dic[key]; | ||||
|             Remove(key); | ||||
|             return v; | ||||
|         } | ||||
|         else | ||||
|             return default(T); | ||||
|     } | ||||
|  | ||||
|     public void Sort(Func<KeyValuePair<KT, T>, object> keySelector) | ||||
|     { | ||||
|         dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value); | ||||
|     } | ||||
|  | ||||
|     public T[] ToArray() | ||||
|     { | ||||
|         var a = new T[Count]; | ||||
|         dic.Values.CopyTo(a, 0); | ||||
|         return a; | ||||
|     } | ||||
|  | ||||
|     public void Add(KT key, T value) | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|         { | ||||
|             if (removableList) | ||||
|                 if (value != null) | ||||
|                     ((IDestructible)value).OnDestroy += ItemDestroyed; | ||||
|  | ||||
|             if (dic.ContainsKey(key)) | ||||
|             { | ||||
|                 var v = dic[key]; | ||||
|                 Remove(key); | ||||
|                 return v; | ||||
|                 var oldValue = dic[key]; | ||||
|                 if (removableList) | ||||
|                     if (oldValue != null) | ||||
|                         ((IDestructible)oldValue).OnDestroy -= ItemDestroyed; | ||||
|                 dic[key] = value; | ||||
|                 if (OnModified != null) | ||||
|                     OnModified(key, oldValue, value, this); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 dic.Add(key, value); | ||||
|  | ||||
|                 if (OnAdd != null) | ||||
|                     OnAdd(value, this); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void ItemDestroyed(object sender) | ||||
|     { | ||||
|         RemoveValue((T)sender); | ||||
|     } | ||||
|  | ||||
|     public void RemoveValue(T value) | ||||
|     { | ||||
|         var toRemove = new List<KT>(); | ||||
|         foreach (var kv in dic) | ||||
|             if (kv.Value.Equals(value)) | ||||
|                 toRemove.Add(kv.Key); | ||||
|  | ||||
|         foreach (var k in toRemove) | ||||
|             Remove(k); | ||||
|     } | ||||
|  | ||||
|     public T this[KT key] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             if (dic.ContainsKey(key)) | ||||
|                 return dic[key]; | ||||
|             else | ||||
|                 return default(T); | ||||
|         } | ||||
|  | ||||
|         public void Sort(Func<KeyValuePair<KT, T>, object> keySelector) | ||||
|         set | ||||
|         { | ||||
|             dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value); | ||||
|             Add(key, value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public T[] ToArray() | ||||
|  | ||||
|     public IEnumerator<KeyValuePair<KT, T>> GetEnumerator() | ||||
|     { | ||||
|         return dic.GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     IEnumerator IEnumerable.GetEnumerator() | ||||
|     { | ||||
|         return dic.GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Clear() | ||||
|     { | ||||
|         if (removableList) | ||||
|             foreach (IDestructible v in dic.Values) | ||||
|                 if (v != null) | ||||
|                     v.OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|         lock (syncRoot) | ||||
|             dic.Clear(); | ||||
|  | ||||
|         if (OnCleared != null) | ||||
|             OnCleared(this); | ||||
|     } | ||||
|  | ||||
|     public Dictionary<KT, T>.KeyCollection Keys | ||||
|     { | ||||
|         get { return dic.Keys; } | ||||
|     } | ||||
|  | ||||
|     public Dictionary<KT, T>.ValueCollection Values | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             var a = new T[Count]; | ||||
|             dic.Values.CopyTo(a, 0); | ||||
|             return a; | ||||
|             return dic.Values; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public void Add(KT key, T value) | ||||
|         { | ||||
|             lock (syncRoot) | ||||
|             { | ||||
|                 if (removableList) | ||||
|                     if (value != null) | ||||
|                         ((IDestructible)value).OnDestroy += ItemDestroyed; | ||||
|     public void Remove(KT key) | ||||
|     { | ||||
|         if (!dic.ContainsKey(key)) | ||||
|             return; | ||||
|  | ||||
|                 if (dic.ContainsKey(key)) | ||||
|                 { | ||||
|                     var oldValue = dic[key]; | ||||
|                     if (removableList) | ||||
|                         if (oldValue != null) | ||||
|                             ((IDestructible)oldValue).OnDestroy -= ItemDestroyed; | ||||
|                     dic[key] = value; | ||||
|                     if (OnModified != null) | ||||
|                         OnModified(key, oldValue, value, this); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     dic.Add(key, value); | ||||
|         var value = dic[key]; | ||||
|  | ||||
|                     if (OnAdd != null) | ||||
|                         OnAdd(value, this); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (removableList) | ||||
|             if (value != null) | ||||
|                 ((IDestructible)value).OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|         private void ItemDestroyed(object sender) | ||||
|         { | ||||
|             RemoveValue((T)sender); | ||||
|         } | ||||
|         lock (syncRoot) | ||||
|             dic.Remove(key); | ||||
|  | ||||
|         public void RemoveValue(T value) | ||||
|         { | ||||
|             var toRemove = new List<KT>(); | ||||
|             foreach (var kv in dic) | ||||
|                 if (kv.Value.Equals(value)) | ||||
|                     toRemove.Add(kv.Key); | ||||
|         if (OnRemoved != null) | ||||
|             OnRemoved(key, value, this); | ||||
|     } | ||||
|  | ||||
|             foreach (var k in toRemove) | ||||
|                 Remove(k); | ||||
|         } | ||||
|     public object Owner | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public T this[KT key] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (dic.ContainsKey(key)) | ||||
|                     return dic[key]; | ||||
|                 else | ||||
|                     return default(T); | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                  Add(key, value); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|       | ||||
|         public IEnumerator<KeyValuePair<KT, T>> GetEnumerator() | ||||
|         { | ||||
|             return dic.GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         IEnumerator IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return dic.GetEnumerator(); | ||||
|         } | ||||
|     public int Count | ||||
|     { | ||||
|         get { return dic.Count; } | ||||
|     } | ||||
|     public bool Contains(KT Key) | ||||
|     { | ||||
|         return dic.ContainsKey(Key); | ||||
|     } | ||||
|     public bool ContainsKey(KT Key) | ||||
|     { | ||||
|         return dic.ContainsKey(Key); | ||||
|     } | ||||
|     public bool ContainsValue(T Value) | ||||
|     { | ||||
|         return dic.ContainsValue(Value); | ||||
|     } | ||||
|  | ||||
|  | ||||
|         public void Clear() | ||||
|         { | ||||
|             if (removableList) | ||||
|                 foreach (IDestructible v in dic.Values) | ||||
|                     if (v != null) | ||||
|                         v.OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 dic.Clear(); | ||||
|  | ||||
|             if (OnCleared != null) | ||||
|                 OnCleared(this); | ||||
|         } | ||||
|  | ||||
|         public Dictionary<KT, T>.KeyCollection Keys | ||||
|         { | ||||
|             get { return dic.Keys; } | ||||
|         } | ||||
|  | ||||
|         public Dictionary<KT, T>.ValueCollection Values | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return dic.Values; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Remove(KT key) | ||||
|         { | ||||
|             if (!dic.ContainsKey(key)) | ||||
|                 return; | ||||
|  | ||||
|             var value = dic[key]; | ||||
|  | ||||
|             if (removableList) | ||||
|                 if (value != null) | ||||
|                     ((IDestructible)value).OnDestroy -= ItemDestroyed; | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 dic.Remove(key); | ||||
|  | ||||
|             if (OnRemoved != null) | ||||
|                 OnRemoved(key, value, this); | ||||
|         } | ||||
|  | ||||
|         public object Owner | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public int Count | ||||
|         { | ||||
|             get { return dic.Count; } | ||||
|         } | ||||
|         public bool Contains(KT Key) | ||||
|         { | ||||
|             return dic.ContainsKey(Key); | ||||
|         } | ||||
|         public bool ContainsKey(KT Key) | ||||
|         { | ||||
|             return dic.ContainsKey(Key); | ||||
|         } | ||||
|         public bool ContainsValue(T Value) | ||||
|         { | ||||
|             return dic.ContainsValue(Value); | ||||
|         } | ||||
|  | ||||
|          | ||||
|         public KeyList(object owner = null) | ||||
|         { | ||||
|             #if NETSTANDARD | ||||
|                         removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); | ||||
|     public KeyList(object owner = null) | ||||
|     { | ||||
| #if NETSTANDARD | ||||
|         removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); | ||||
| #else | ||||
|                         removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T))); | ||||
| #endif | ||||
|  | ||||
|             this.Owner = owner; | ||||
|         this.Owner = owner; | ||||
|  | ||||
|             if (typeof(KT) == typeof(string)) | ||||
|                 dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); | ||||
|             else | ||||
|                 dic = new Dictionary<KT, T>(); | ||||
|         } | ||||
|         if (typeof(KT) == typeof(string)) | ||||
|             dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); | ||||
|         else | ||||
|             dic = new Dictionary<KT, T>(); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -28,10 +28,8 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
| public class NotModified | ||||
| { | ||||
|     public class NotModified | ||||
|     { | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,34 +2,33 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| { | ||||
|     public class PropertyValue | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Get or set the value. | ||||
|         /// </summary> | ||||
|         public object Value { get; set; } | ||||
|         /// <summary> | ||||
|         /// Get or set date of modification or occurrence. | ||||
|         /// </summary> | ||||
|         public DateTime Date { get; set; } | ||||
|         /// <summary> | ||||
|         /// Get or set property age. | ||||
|         /// </summary> | ||||
|         public ulong Age { get; set; } | ||||
| namespace Esiur.Data; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create an instance of PropertyValue. | ||||
|         /// </summary> | ||||
|         /// <param name="value">Value.</param> | ||||
|         /// <param name="age">Age.</param> | ||||
|         /// <param name="date">Date.</param> | ||||
|         public PropertyValue(object value, ulong age, DateTime date) | ||||
|         { | ||||
|             Value = value; | ||||
|             Age = age; | ||||
|             Date = date; | ||||
|         } | ||||
| public class PropertyValue | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Get or set the value. | ||||
|     /// </summary> | ||||
|     public object Value { get; set; } | ||||
|     /// <summary> | ||||
|     /// Get or set date of modification or occurrence. | ||||
|     /// </summary> | ||||
|     public DateTime Date { get; set; } | ||||
|     /// <summary> | ||||
|     /// Get or set property age. | ||||
|     /// </summary> | ||||
|     public ulong Age { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Create an instance of PropertyValue. | ||||
|     /// </summary> | ||||
|     /// <param name="value">Value.</param> | ||||
|     /// <param name="age">Age.</param> | ||||
|     /// <param name="date">Date.</param> | ||||
|     public PropertyValue(object value, ulong age, DateTime date) | ||||
|     { | ||||
|         Value = value; | ||||
|         Age = age; | ||||
|         Date = date; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,10 +2,8 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
| public class Record : KeyList<string, object>, IRecord | ||||
| { | ||||
|     public class Record: KeyList<string, object>, IRecord | ||||
|     { | ||||
|          | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,13 +2,12 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public enum RecordComparisonResult : byte | ||||
| { | ||||
|     public enum RecordComparisonResult : byte | ||||
|     { | ||||
|         Null, | ||||
|         Record, | ||||
|         RecordSameType, | ||||
|         Same | ||||
|     } | ||||
|     Null, | ||||
|     Record, | ||||
|     RecordSameType, | ||||
|     Same | ||||
| } | ||||
|   | ||||
| @@ -2,12 +2,11 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public enum ResourceArrayType | ||||
| { | ||||
|     public enum ResourceArrayType | ||||
|     { | ||||
|         Dynamic = 0x0, | ||||
|         Static = 0x10, | ||||
|         Wrapper = 0x20, | ||||
|     } | ||||
|     Dynamic = 0x0, | ||||
|     Static = 0x10, | ||||
|     Wrapper = 0x20, | ||||
| } | ||||
|   | ||||
| @@ -2,13 +2,12 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public enum ResourceComparisonResult | ||||
| { | ||||
|     public enum ResourceComparisonResult | ||||
|     { | ||||
|         Null, // null | ||||
|         Distributed, // resource is distributed | ||||
|         Local, // resource is local | ||||
|         Same, // Same as previous | ||||
|     } | ||||
|     Null, // null | ||||
|     Distributed, // resource is distributed | ||||
|     Local, // resource is local | ||||
|     Same, // Same as previous | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| using Esiur.Net.IIP; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| /* | ||||
|   | ||||
| Copyright (c) 2017-2021 Ahmed Kh. Zamil | ||||
| @@ -24,76 +23,77 @@ SOFTWARE. | ||||
|  | ||||
| */ | ||||
|  | ||||
| using Esiur.Net.IIP; | ||||
| using Esiur.Resource; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| class ResourceJsonConverter : JsonConverter<IResource> | ||||
| { | ||||
|     class ResourceJsonConverter : JsonConverter<IResource> | ||||
|     public override IResource Read( | ||||
|         ref Utf8JsonReader reader, | ||||
|         Type typeToConvert, | ||||
|         JsonSerializerOptions options) | ||||
|     { | ||||
|         public override IResource Read( | ||||
|             ref Utf8JsonReader reader, | ||||
|             Type typeToConvert, | ||||
|             JsonSerializerOptions options) | ||||
|         { | ||||
|             return (IResource)JsonSerializer.Deserialize(ref reader,typeof(IResource), options); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public override void Write( | ||||
|             Utf8JsonWriter writer, | ||||
|             IResource resource, | ||||
|             JsonSerializerOptions options) | ||||
|         { | ||||
|  | ||||
|             writer.WriteStartObject(); | ||||
|  | ||||
|             foreach (var pt in resource.Instance.Template.Properties) | ||||
|             { | ||||
|                 var rt = pt.PropertyInfo.GetValue(resource, null); | ||||
|                 if (rt is DistributedPropertyContext) | ||||
|                     continue; | ||||
|                  | ||||
|                 writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name); | ||||
|  | ||||
|                 if (rt is IResource) | ||||
|                     JsonSerializer.Serialize(writer, (IResource) rt, options); | ||||
|                 else | ||||
|                     JsonSerializer.Serialize(writer, rt, options); | ||||
|             } | ||||
|  | ||||
|             writer.WriteEndObject(); | ||||
|  | ||||
|         } | ||||
|         return (IResource)JsonSerializer.Deserialize(ref reader, typeof(IResource), options); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public class DoubleJsonConverter : JsonConverter<double> | ||||
|     public override void Write( | ||||
|         Utf8JsonWriter writer, | ||||
|         IResource resource, | ||||
|         JsonSerializerOptions options) | ||||
|     { | ||||
|         public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||||
|         { | ||||
|             if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN") | ||||
|             { | ||||
|                 return double.NaN; | ||||
|             } | ||||
|  | ||||
|             return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number | ||||
|         } | ||||
|         writer.WriteStartObject(); | ||||
|  | ||||
|         public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) | ||||
|         foreach (var pt in resource.Instance.Template.Properties) | ||||
|         { | ||||
|             if (double.IsNaN(value)) | ||||
|             { | ||||
|                 writer.WriteStringValue("NaN"); | ||||
|             } | ||||
|             var rt = pt.PropertyInfo.GetValue(resource, null); | ||||
|             if (rt is DistributedPropertyContext) | ||||
|                 continue; | ||||
|  | ||||
|             writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name); | ||||
|  | ||||
|             if (rt is IResource) | ||||
|                 JsonSerializer.Serialize(writer, (IResource)rt, options); | ||||
|             else | ||||
|             { | ||||
|                 writer.WriteNumberValue(value); | ||||
|             } | ||||
|                 JsonSerializer.Serialize(writer, rt, options); | ||||
|         } | ||||
|  | ||||
|         writer.WriteEndObject(); | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| public class DoubleJsonConverter : JsonConverter<double> | ||||
| { | ||||
|     public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||||
|     { | ||||
|         if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN") | ||||
|         { | ||||
|             return double.NaN; | ||||
|         } | ||||
|  | ||||
|         return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number | ||||
|     } | ||||
|  | ||||
|     public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) | ||||
|     { | ||||
|         if (double.IsNaN(value)) | ||||
|         { | ||||
|             writer.WriteStringValue("NaN"); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             writer.WriteNumberValue(value); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -30,245 +30,244 @@ using System.Collections; | ||||
| using Esiur.Core; | ||||
| using System.Reflection; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T> | ||||
| { | ||||
|     public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T> | ||||
|  | ||||
|     private readonly object syncRoot = new object(); | ||||
|     private List<T> list = new List<T>(); | ||||
|  | ||||
|     public delegate void Modified(ST sender, int index, T oldValue, T newValue); | ||||
|     public delegate void Added(ST sender, T value); | ||||
|     public delegate void Removed(ST sender, int index, T value); | ||||
|     public delegate void Cleared(ST sender); | ||||
|  | ||||
|  | ||||
|     public event Modified OnModified; | ||||
|     public event Removed OnRemoved; | ||||
|     public event Cleared OnCleared; | ||||
|     public event Added OnAdd; | ||||
|  | ||||
|     ST state; | ||||
|  | ||||
|     public void Sort() | ||||
|     { | ||||
|         list.Sort(); | ||||
|     } | ||||
|  | ||||
|         private readonly object syncRoot = new object(); | ||||
|         private List<T> list = new List<T>(); | ||||
|     public void Sort(IComparer<T> comparer) | ||||
|     { | ||||
|         list.Sort(comparer); | ||||
|     } | ||||
|  | ||||
|         public delegate void Modified(ST sender, int index, T oldValue, T newValue); | ||||
|         public delegate void Added(ST sender, T value); | ||||
|         public delegate void Removed(ST sender, int index, T value); | ||||
|         public delegate void Cleared(ST sender); | ||||
|     public void Sort(Comparison<T> comparison) | ||||
|     { | ||||
|         list.Sort(comparison); | ||||
|     } | ||||
|  | ||||
|     public IEnumerable<T> Where(Func<T, bool> predicate) | ||||
|     { | ||||
|         return list.Where(predicate); | ||||
|     } | ||||
|  | ||||
|         public event Modified OnModified; | ||||
|         public event Removed OnRemoved; | ||||
|         public event Cleared OnCleared; | ||||
|         public event Added OnAdd; | ||||
|     /// <summary> | ||||
|     /// Convert AutoList to array | ||||
|     /// </summary> | ||||
|     /// <returns>Array</returns> | ||||
|     public T[] ToArray() | ||||
|     { | ||||
|         //    list.OrderBy() | ||||
|         return list.ToArray(); | ||||
|     } | ||||
|  | ||||
|         ST state; | ||||
|     /// <summary> | ||||
|     /// Create a new instance of AutoList | ||||
|     /// </summary> | ||||
|     /// <param name="state">State object to be included when an event is raised.</param> | ||||
|     public ResourceList(ST state) | ||||
|     { | ||||
|         this.state = state; | ||||
|     } | ||||
|  | ||||
|         public void Sort() | ||||
|     /// <summary> | ||||
|     /// Create a new instance of AutoList | ||||
|     /// </summary> | ||||
|     /// <param name="values">Populate the list with items</param> | ||||
|     /// <returns></returns> | ||||
|     public ResourceList(ST state, T[] values) | ||||
|     { | ||||
|         this.state = state; | ||||
|         AddRange(values); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Synchronization lock of the list | ||||
|     /// </summary> | ||||
|     public object SyncRoot | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             list.Sort(); | ||||
|         } | ||||
|  | ||||
|         public void Sort(IComparer<T> comparer) | ||||
|         { | ||||
|             list.Sort(comparer); | ||||
|         } | ||||
|  | ||||
|         public void Sort(Comparison<T> comparison) | ||||
|         { | ||||
|             list.Sort(comparison); | ||||
|         } | ||||
|  | ||||
|         public IEnumerable<T> Where(Func<T, bool> predicate) | ||||
|         { | ||||
|             return list.Where(predicate); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Convert AutoList to array | ||||
|         /// </summary> | ||||
|         /// <returns>Array</returns> | ||||
|         public T[] ToArray() | ||||
|         { | ||||
|             //    list.OrderBy() | ||||
|             return list.ToArray(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create a new instance of AutoList | ||||
|         /// </summary> | ||||
|         /// <param name="state">State object to be included when an event is raised.</param> | ||||
|         public ResourceList(ST state) | ||||
|         { | ||||
|             this.state = state; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create a new instance of AutoList | ||||
|         /// </summary> | ||||
|         /// <param name="values">Populate the list with items</param> | ||||
|         /// <returns></returns> | ||||
|         public ResourceList(ST state, T[] values) | ||||
|         { | ||||
|             this.state = state; | ||||
|             AddRange(values); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Synchronization lock of the list | ||||
|         /// </summary> | ||||
|         public object SyncRoot | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return syncRoot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// First item in the list | ||||
|         /// </summary> | ||||
|         public T First() | ||||
|         { | ||||
|             return list.First(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Get an item at a specified index | ||||
|         /// </summary> | ||||
|         public T this[int index] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return list[index]; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 var oldValue = list[index]; | ||||
|  | ||||
|                 lock (syncRoot) | ||||
|                     list[index] = value; | ||||
|  | ||||
|                 OnModified?.Invoke(state, index, oldValue, value); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Add item to the list | ||||
|         /// </summary> | ||||
|         public void Add(T value) | ||||
|         { | ||||
|             lock (syncRoot) | ||||
|                 list.Add(value); | ||||
|  | ||||
|             OnAdd?.Invoke(state, value); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Add an array of items to the list | ||||
|         /// </summary> | ||||
|         public void AddRange(T[] values) | ||||
|         { | ||||
|             foreach (var v in values) | ||||
|                 Add(v); | ||||
|         } | ||||
|  | ||||
|         private void ItemDestroyed(object sender) | ||||
|         { | ||||
|             Remove((T)sender); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Clear the list | ||||
|         /// </summary> | ||||
|         public void Clear() | ||||
|         { | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 list.Clear(); | ||||
|  | ||||
|             OnCleared?.Invoke(state); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Remove an item from the list | ||||
|         /// <param name="value">Item to remove</param> | ||||
|         /// </summary> | ||||
|         public void Remove(T value) | ||||
|         { | ||||
|             var index = 0; | ||||
|  | ||||
|             lock (syncRoot) | ||||
|             { | ||||
|                 index = list.IndexOf(value); | ||||
|  | ||||
|                 if (index == -1) | ||||
|                     return; | ||||
|  | ||||
|                 list.RemoveAt(index); | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|             OnRemoved?.Invoke(state, index, value); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Number of items in the list | ||||
|         /// </summary> | ||||
|         public int Count | ||||
|         { | ||||
|             get { return list.Count; } | ||||
|         } | ||||
|  | ||||
|         public bool IsSynchronized => (list as ICollection).IsSynchronized; | ||||
|  | ||||
|         public bool IsReadOnly => throw new NotImplementedException(); | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Check if an item exists in the list | ||||
|         /// </summary> | ||||
|         /// <param name="value">Item to check if exists</param> | ||||
|         public bool Contains(T value) | ||||
|         { | ||||
|             return list.Contains(value); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Check if any item of the given array is in the list | ||||
|         /// </summary> | ||||
|         /// <param name="values">Array of items</param> | ||||
|         public bool ContainsAny(T[] values) | ||||
|         { | ||||
|             foreach (var v in values) | ||||
|                 if (list.Contains(v)) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Check if any item of the given list is in the list | ||||
|         /// </summary> | ||||
|         /// <param name="values">List of items</param> | ||||
|         public bool ContainsAny(AutoList<T, ST> values) | ||||
|         { | ||||
|             foreach (var v in values) | ||||
|                 if (list.Contains((T)v)) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public IEnumerator<T> GetEnumerator() | ||||
|         { | ||||
|             return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         IEnumerator IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         public void CopyTo(Array array, int index) | ||||
|         { | ||||
|             (list as ICollection).CopyTo(array, index); | ||||
|         } | ||||
|  | ||||
|         public void CopyTo(T[] array, int arrayIndex) | ||||
|         { | ||||
|             list.CopyTo(array, arrayIndex); | ||||
|         } | ||||
|  | ||||
|         bool ICollection<T>.Remove(T item) | ||||
|         { | ||||
|             return list.Remove(item); | ||||
|             return syncRoot; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// First item in the list | ||||
|     /// </summary> | ||||
|     public T First() | ||||
|     { | ||||
|         return list.First(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Get an item at a specified index | ||||
|     /// </summary> | ||||
|     public T this[int index] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return list[index]; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             var oldValue = list[index]; | ||||
|  | ||||
|             lock (syncRoot) | ||||
|                 list[index] = value; | ||||
|  | ||||
|             OnModified?.Invoke(state, index, oldValue, value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Add item to the list | ||||
|     /// </summary> | ||||
|     public void Add(T value) | ||||
|     { | ||||
|         lock (syncRoot) | ||||
|             list.Add(value); | ||||
|  | ||||
|         OnAdd?.Invoke(state, value); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Add an array of items to the list | ||||
|     /// </summary> | ||||
|     public void AddRange(T[] values) | ||||
|     { | ||||
|         foreach (var v in values) | ||||
|             Add(v); | ||||
|     } | ||||
|  | ||||
|     private void ItemDestroyed(object sender) | ||||
|     { | ||||
|         Remove((T)sender); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Clear the list | ||||
|     /// </summary> | ||||
|     public void Clear() | ||||
|     { | ||||
|  | ||||
|         lock (syncRoot) | ||||
|             list.Clear(); | ||||
|  | ||||
|         OnCleared?.Invoke(state); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Remove an item from the list | ||||
|     /// <param name="value">Item to remove</param> | ||||
|     /// </summary> | ||||
|     public void Remove(T value) | ||||
|     { | ||||
|         var index = 0; | ||||
|  | ||||
|         lock (syncRoot) | ||||
|         { | ||||
|             index = list.IndexOf(value); | ||||
|  | ||||
|             if (index == -1) | ||||
|                 return; | ||||
|  | ||||
|             list.RemoveAt(index); | ||||
|  | ||||
|  | ||||
|         } | ||||
|  | ||||
|         OnRemoved?.Invoke(state, index, value); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Number of items in the list | ||||
|     /// </summary> | ||||
|     public int Count | ||||
|     { | ||||
|         get { return list.Count; } | ||||
|     } | ||||
|  | ||||
|     public bool IsSynchronized => (list as ICollection).IsSynchronized; | ||||
|  | ||||
|     public bool IsReadOnly => throw new NotImplementedException(); | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Check if an item exists in the list | ||||
|     /// </summary> | ||||
|     /// <param name="value">Item to check if exists</param> | ||||
|     public bool Contains(T value) | ||||
|     { | ||||
|         return list.Contains(value); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Check if any item of the given array is in the list | ||||
|     /// </summary> | ||||
|     /// <param name="values">Array of items</param> | ||||
|     public bool ContainsAny(T[] values) | ||||
|     { | ||||
|         foreach (var v in values) | ||||
|             if (list.Contains(v)) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Check if any item of the given list is in the list | ||||
|     /// </summary> | ||||
|     /// <param name="values">List of items</param> | ||||
|     public bool ContainsAny(AutoList<T, ST> values) | ||||
|     { | ||||
|         foreach (var v in values) | ||||
|             if (list.Contains((T)v)) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public IEnumerator<T> GetEnumerator() | ||||
|     { | ||||
|         return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     IEnumerator IEnumerable.GetEnumerator() | ||||
|     { | ||||
|         return ((IEnumerable<T>)list).GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     public void CopyTo(Array array, int index) | ||||
|     { | ||||
|         (list as ICollection).CopyTo(array, index); | ||||
|     } | ||||
|  | ||||
|     public void CopyTo(T[] array, int arrayIndex) | ||||
|     { | ||||
|         list.CopyTo(array, arrayIndex); | ||||
|     } | ||||
|  | ||||
|     bool ICollection<T>.Remove(T item) | ||||
|     { | ||||
|         return list.Remove(item); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,190 +31,189 @@ using System.Text; | ||||
| using System.Reflection; | ||||
| using System.Linq; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
|  | ||||
| public class StringKeyList : IEnumerable<KeyValuePair<string, string>> | ||||
| { | ||||
|  | ||||
|     public class StringKeyList : IEnumerable<KeyValuePair<string, string>> | ||||
|     //private List<string> m_keys = new List<string>(); | ||||
|     //private List<string> m_values = new List<string>(); | ||||
|  | ||||
|     private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>(); | ||||
|  | ||||
|     private bool allowMultiple; | ||||
|  | ||||
|     public delegate void Modified(string Key, string NewValue); | ||||
|     public event Modified OnModified; | ||||
|  | ||||
|     public StringKeyList(bool AllowMultipleValues = false) | ||||
|     { | ||||
|          | ||||
|         //private List<string> m_keys = new List<string>(); | ||||
|         //private List<string> m_values = new List<string>(); | ||||
|         allowMultiple = AllowMultipleValues; | ||||
|     } | ||||
|  | ||||
|         private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>(); | ||||
|     public void Add(string Key, string Value) | ||||
|     { | ||||
|         if (OnModified != null) | ||||
|             OnModified(Key, Value); | ||||
|  | ||||
|         private bool allowMultiple; | ||||
|         var key = Key.ToLower(); | ||||
|  | ||||
|         public delegate void Modified(string Key, string NewValue); | ||||
|         public event Modified OnModified; | ||||
|  | ||||
|         public StringKeyList(bool AllowMultipleValues = false) | ||||
|         if (!allowMultiple) | ||||
|         { | ||||
|             allowMultiple = AllowMultipleValues; | ||||
|         } | ||||
|  | ||||
|         public void Add(string Key, string Value) | ||||
|         { | ||||
|             if (OnModified != null) | ||||
|                 OnModified(Key, Value); | ||||
|  | ||||
|             var key = Key.ToLower(); | ||||
|  | ||||
|             if (!allowMultiple) | ||||
|             { | ||||
|                 foreach(var kv in m_Variables) | ||||
|                 { | ||||
|                     if (kv.Key.ToLower() == key) | ||||
|                     { | ||||
|                         m_Variables.Remove(kv); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             m_Variables.Add(new KeyValuePair<string, string>(Key, Value)); | ||||
|         } | ||||
|  | ||||
|         public string this[string Key] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 var key = Key.ToLower(); | ||||
|                 foreach (var kv in m_Variables) | ||||
|                     if (kv.Key.ToLower() == key) | ||||
|                         return kv.Value; | ||||
|  | ||||
|                 return null; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 var key = Key.ToLower(); | ||||
|  | ||||
|                 var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray(); | ||||
|  | ||||
|                 foreach (var item in toRemove) | ||||
|                     m_Variables.Remove(item); | ||||
|  | ||||
|  | ||||
|                 m_Variables.Add(new KeyValuePair<string, string>(Key, value)); | ||||
|  | ||||
|                 OnModified?.Invoke(Key, value); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator() | ||||
|         { | ||||
|             //return m_keys.GetEnumerator(); | ||||
|             return m_Variables.GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return m_Variables.GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         public void Clear() | ||||
|         { | ||||
|             if (OnModified != null) | ||||
|                 OnModified(null, null); | ||||
|  | ||||
|             m_Variables.Clear(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|         public string[] Keys | ||||
|         { | ||||
|             get  | ||||
|             {  | ||||
|                 return m_keys.ToArray(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //public Dictionary<string, string>.ValueCollection Values | ||||
|         public string[] Values | ||||
|         { | ||||
|             get  | ||||
|             {  | ||||
|                 //return m_Variables.Values;  | ||||
|                 return m_values.ToArray(); | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public List<string> GetValues(string Key) | ||||
|         { | ||||
|             var key = Key.ToLower(); | ||||
|  | ||||
|             List<string> values = new List<string>(); | ||||
|  | ||||
|             foreach (var kv in m_Variables) | ||||
|                 if (kv.Key.ToLower() == key) | ||||
|                     values.Add(kv.Value); | ||||
|              | ||||
|             return values; | ||||
|         } | ||||
|  | ||||
|         public void RemoveAll(string Key) | ||||
|         { | ||||
|             while (Remove(Key)){} | ||||
|         } | ||||
|  | ||||
|         public bool Remove(string Key) | ||||
|         { | ||||
|             var key = Key.ToLower(); | ||||
|  | ||||
|             foreach(var kv in m_Variables) | ||||
|             { | ||||
|                 if (kv.Key.ToLower() == key) | ||||
|                 { | ||||
|                     if (OnModified != null) | ||||
|                         OnModified(Key, null); | ||||
|                     m_Variables.Remove(kv); | ||||
|                     return true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public int Count | ||||
|         { | ||||
|             get { return m_Variables.Count; } | ||||
|         } | ||||
|          | ||||
|         public bool ContainsKey(string Key) | ||||
|         m_Variables.Add(new KeyValuePair<string, string>(Key, Value)); | ||||
|     } | ||||
|  | ||||
|     public string this[string Key] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             var key = Key.ToLower(); | ||||
|             foreach (var kv in m_Variables) | ||||
|                 if (kv.Key.ToLower() == key) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|                     return kv.Value; | ||||
|  | ||||
|         /* | ||||
|         public bool ContainsKey(string Key) | ||||
|             return null; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             //return m_Variables.ContainsKey(Key); | ||||
|             return m_keys.Contains(Key.ToLower()); | ||||
|         } | ||||
|          */ | ||||
|             var key = Key.ToLower(); | ||||
|  | ||||
|         public bool ContainsValue(string Value) | ||||
|         { | ||||
|             var value = Value.ToLower(); | ||||
|             foreach (var kv in m_Variables) | ||||
|                 if (kv.Value.ToLower() == value) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|             var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray(); | ||||
|  | ||||
|         //internal KeyList() | ||||
|         //{ | ||||
|         //    m_Session = Session; | ||||
|         //    m_Server = Server; | ||||
|         //} | ||||
|             foreach (var item in toRemove) | ||||
|                 m_Variables.Remove(item); | ||||
|  | ||||
|  | ||||
|             m_Variables.Add(new KeyValuePair<string, string>(Key, value)); | ||||
|  | ||||
|             OnModified?.Invoke(Key, value); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator() | ||||
|     { | ||||
|         //return m_keys.GetEnumerator(); | ||||
|         return m_Variables.GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | ||||
|     { | ||||
|         return m_Variables.GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     public void Clear() | ||||
|     { | ||||
|         if (OnModified != null) | ||||
|             OnModified(null, null); | ||||
|  | ||||
|         m_Variables.Clear(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     public string[] Keys | ||||
|     { | ||||
|         get  | ||||
|         {  | ||||
|             return m_keys.ToArray(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     //public Dictionary<string, string>.ValueCollection Values | ||||
|     public string[] Values | ||||
|     { | ||||
|         get  | ||||
|         {  | ||||
|             //return m_Variables.Values;  | ||||
|             return m_values.ToArray(); | ||||
|         } | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public List<string> GetValues(string Key) | ||||
|     { | ||||
|         var key = Key.ToLower(); | ||||
|  | ||||
|         List<string> values = new List<string>(); | ||||
|  | ||||
|         foreach (var kv in m_Variables) | ||||
|             if (kv.Key.ToLower() == key) | ||||
|                 values.Add(kv.Value); | ||||
|  | ||||
|         return values; | ||||
|     } | ||||
|  | ||||
|     public void RemoveAll(string Key) | ||||
|     { | ||||
|         while (Remove(Key)) { } | ||||
|     } | ||||
|  | ||||
|     public bool Remove(string Key) | ||||
|     { | ||||
|         var key = Key.ToLower(); | ||||
|  | ||||
|         foreach (var kv in m_Variables) | ||||
|         { | ||||
|             if (kv.Key.ToLower() == key) | ||||
|             { | ||||
|                 if (OnModified != null) | ||||
|                     OnModified(Key, null); | ||||
|                 m_Variables.Remove(kv); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public int Count | ||||
|     { | ||||
|         get { return m_Variables.Count; } | ||||
|     } | ||||
|  | ||||
|     public bool ContainsKey(string Key) | ||||
|     { | ||||
|         var key = Key.ToLower(); | ||||
|         foreach (var kv in m_Variables) | ||||
|             if (kv.Key.ToLower() == key) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     public bool ContainsKey(string Key) | ||||
|     { | ||||
|         //return m_Variables.ContainsKey(Key); | ||||
|         return m_keys.Contains(Key.ToLower()); | ||||
|     } | ||||
|      */ | ||||
|  | ||||
|     public bool ContainsValue(string Value) | ||||
|     { | ||||
|         var value = Value.ToLower(); | ||||
|         foreach (var kv in m_Variables) | ||||
|             if (kv.Value.ToLower() == value) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     //internal KeyList() | ||||
|     //{ | ||||
|     //    m_Session = Session; | ||||
|     //    m_Server = Server; | ||||
|     //} | ||||
|  | ||||
| } | ||||
| @@ -34,148 +34,147 @@ using Esiur.Core; | ||||
| using System.Reflection; | ||||
| using System.Dynamic; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public class Structure : IEnumerable<KeyValuePair<string, object>> | ||||
| { | ||||
|     public class Structure : IEnumerable<KeyValuePair<string, object>> | ||||
|  | ||||
|     public struct StructureMetadata | ||||
|     { | ||||
|         public string[] Keys; | ||||
|         public DataType[] Types; | ||||
|     } | ||||
|  | ||||
|     private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); | ||||
|     private object syncRoot = new object(); | ||||
|  | ||||
|  | ||||
|     public bool ContainsKey(string key) | ||||
|     { | ||||
|         return dic.ContainsKey(key); | ||||
|     } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         var rt = ""; | ||||
|         foreach (var kv in dic) | ||||
|             rt += kv.Key + ": " + kv.Value.ToString() + " \r\n"; | ||||
|  | ||||
|         return rt.TrimEnd('\r', '\n'); | ||||
|     } | ||||
|  | ||||
|     public Structure(Structure source) | ||||
|     { | ||||
|         dic = source.dic; | ||||
|     } | ||||
|     public Structure() | ||||
|     { | ||||
|  | ||||
|         public struct StructureMetadata | ||||
|         { | ||||
|             public string[] Keys; | ||||
|             public DataType[] Types; | ||||
|         } | ||||
|  | ||||
|         private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); | ||||
|         private object syncRoot = new object(); | ||||
|  | ||||
|  | ||||
|         public bool ContainsKey(string key) | ||||
|         { | ||||
|             return dic.ContainsKey(key); | ||||
|         } | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             var rt = ""; | ||||
|             foreach (var kv in dic) | ||||
|                 rt += kv.Key + ": " + kv.Value.ToString() + " \r\n"; | ||||
|  | ||||
|             return rt.TrimEnd('\r', '\n'); | ||||
|         } | ||||
|  | ||||
|         public Structure(Structure source) | ||||
|         { | ||||
|             dic = source.dic; | ||||
|         } | ||||
|         public Structure() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public static Structure FromStructure(Structure source, Type destinationType) | ||||
|         { | ||||
|             var rt = Activator.CreateInstance(destinationType) as Structure; | ||||
|             rt.dic = source.dic; | ||||
|             return rt; | ||||
|         } | ||||
|  | ||||
|         public static T FromStructure<T>(Structure source) where T : Structure | ||||
|         { | ||||
|             var rt = Activator.CreateInstance<T>(); | ||||
|             rt.dic = source.dic; | ||||
|             return rt; | ||||
|         } | ||||
|          | ||||
|         public static explicit operator Structure(ExpandoObject obj) => FromDynamic(obj); | ||||
|  | ||||
|         public static Structure FromDynamic(ExpandoObject obj) | ||||
|         { | ||||
|             var rt = new Structure(); | ||||
|             foreach (var kv in obj) | ||||
|                 rt[kv.Key] = kv.Value; | ||||
|             return rt; | ||||
|         } | ||||
|  | ||||
|         public static Structure FromObject(object obj) | ||||
|         { | ||||
|             var type = obj.GetType(); | ||||
|  | ||||
|             if (obj is Structure) | ||||
|                 return obj as Structure; | ||||
|             else //if (Codec.IsAnonymous(type)) | ||||
|             { | ||||
|                 var st = new Structure(); | ||||
|  | ||||
|                 var pi = type.GetTypeInfo().GetProperties().Where(x=>x.CanRead); | ||||
|                 foreach (var p in pi) | ||||
|                     st[p.Name] = p.GetValue(obj); | ||||
|  | ||||
|                 var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic); | ||||
|                 foreach (var f in fi) | ||||
|                     st[f.Name] = f.GetValue(obj); | ||||
|  | ||||
|                 return st; | ||||
|             } | ||||
|             //else | ||||
|               //  return null; | ||||
|         } | ||||
|         public IEnumerator<KeyValuePair<string, object>> GetEnumerator() | ||||
|         { | ||||
|             return dic.GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         IEnumerator IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return dic.GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         public int Length | ||||
|         { | ||||
|             get { return dic.Count; } | ||||
|         } | ||||
|  | ||||
|         public KeyValuePair<string, object> At(int index) | ||||
|         { | ||||
|             return dic.ElementAt(index); | ||||
|         } | ||||
|  | ||||
|         public object SyncRoot | ||||
|         { | ||||
|             get { return syncRoot; } | ||||
|         } | ||||
|  | ||||
|         public string[] GetKeys() => dic.Keys.ToArray();//GetKeys() | ||||
|         //{ | ||||
|           //  return dic.Keys.ToArray(); | ||||
|         //} | ||||
|          | ||||
|         public Structure Add(string key, object value) | ||||
|         { | ||||
|             if (dic.ContainsKey(key)) | ||||
|                 dic[key] = value; | ||||
|             else | ||||
|                 dic.Add(key, value); | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public object this[string index] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (dic.ContainsKey(index)) | ||||
|                     return dic[index]; | ||||
|                 else | ||||
|                     return null; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 if (dic.ContainsKey(index)) | ||||
|                     dic[index] = value; | ||||
|                 else | ||||
|                     dic.Add(index, value); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public static Structure FromStructure(Structure source, Type destinationType) | ||||
|     { | ||||
|         var rt = Activator.CreateInstance(destinationType) as Structure; | ||||
|         rt.dic = source.dic; | ||||
|         return rt; | ||||
|     } | ||||
|  | ||||
|     public static T FromStructure<T>(Structure source) where T : Structure | ||||
|     { | ||||
|         var rt = Activator.CreateInstance<T>(); | ||||
|         rt.dic = source.dic; | ||||
|         return rt; | ||||
|     } | ||||
|  | ||||
|     public static explicit operator Structure(ExpandoObject obj) => FromDynamic(obj); | ||||
|  | ||||
|     public static Structure FromDynamic(ExpandoObject obj) | ||||
|     { | ||||
|         var rt = new Structure(); | ||||
|         foreach (var kv in obj) | ||||
|             rt[kv.Key] = kv.Value; | ||||
|         return rt; | ||||
|     } | ||||
|  | ||||
|     public static Structure FromObject(object obj) | ||||
|     { | ||||
|         var type = obj.GetType(); | ||||
|  | ||||
|         if (obj is Structure) | ||||
|             return obj as Structure; | ||||
|         else //if (Codec.IsAnonymous(type)) | ||||
|         { | ||||
|             var st = new Structure(); | ||||
|  | ||||
|             var pi = type.GetTypeInfo().GetProperties().Where(x => x.CanRead); | ||||
|             foreach (var p in pi) | ||||
|                 st[p.Name] = p.GetValue(obj); | ||||
|  | ||||
|             var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic); | ||||
|             foreach (var f in fi) | ||||
|                 st[f.Name] = f.GetValue(obj); | ||||
|  | ||||
|             return st; | ||||
|         } | ||||
|         //else | ||||
|         //  return null; | ||||
|     } | ||||
|     public IEnumerator<KeyValuePair<string, object>> GetEnumerator() | ||||
|     { | ||||
|         return dic.GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     IEnumerator IEnumerable.GetEnumerator() | ||||
|     { | ||||
|         return dic.GetEnumerator(); | ||||
|     } | ||||
|  | ||||
|     public int Length | ||||
|     { | ||||
|         get { return dic.Count; } | ||||
|     } | ||||
|  | ||||
|     public KeyValuePair<string, object> At(int index) | ||||
|     { | ||||
|         return dic.ElementAt(index); | ||||
|     } | ||||
|  | ||||
|     public object SyncRoot | ||||
|     { | ||||
|         get { return syncRoot; } | ||||
|     } | ||||
|  | ||||
|     public string[] GetKeys() => dic.Keys.ToArray();//GetKeys() | ||||
|                                                     //{ | ||||
|                                                     //  return dic.Keys.ToArray(); | ||||
|                                                     //} | ||||
|  | ||||
|     public Structure Add(string key, object value) | ||||
|     { | ||||
|         if (dic.ContainsKey(key)) | ||||
|             dic[key] = value; | ||||
|         else | ||||
|             dic.Add(key, value); | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public object this[string index] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             if (dic.ContainsKey(index)) | ||||
|                 return dic[index]; | ||||
|             else | ||||
|                 return null; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             if (dic.ContainsKey(index)) | ||||
|                 dic[index] = value; | ||||
|             else | ||||
|                 dic.Add(index, value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,14 +2,13 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Data | ||||
| namespace Esiur.Data; | ||||
|  | ||||
| public enum StructureComparisonResult : byte | ||||
| { | ||||
|     public enum StructureComparisonResult : byte | ||||
|     { | ||||
|         Null, | ||||
|         Structure, | ||||
|         StructureSameKeys, | ||||
|         StructureSameTypes, | ||||
|         Same | ||||
|     } | ||||
|     Null, | ||||
|     Structure, | ||||
|     StructureSameKeys, | ||||
|     StructureSameTypes, | ||||
|     Same | ||||
| } | ||||
|   | ||||
| @@ -4,20 +4,20 @@ | ||||
|     <TargetFramework>netstandard2.0</TargetFramework> | ||||
|     <Description>Distributed Resources Platform</Description> | ||||
|     <Copyright>Ahmed Kh. Zamil</Copyright> | ||||
|     <PackageLicenseUrl>https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE</PackageLicenseUrl> | ||||
|     <PackageProjectUrl>http://www.esiur.com</PackageProjectUrl> | ||||
|      <PackageProjectUrl>http://www.esiur.com</PackageProjectUrl> | ||||
|     <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
|     <Version>1.8.8</Version> | ||||
|     <Version>2.0.0-alpha</Version> | ||||
|     <RepositoryUrl>https://github.com/esiur/esiur-dotnet</RepositoryUrl> | ||||
|     <Authors>Ahmed Kh. Zamil</Authors> | ||||
|     <AssemblyVersion>1.8.8.0</AssemblyVersion> | ||||
|     <AssemblyVersion>2.0.0.0</AssemblyVersion> | ||||
|     <Company>Esiur Foundation</Company> | ||||
|     <FileVersion>1.8.8.0</FileVersion> | ||||
|     <FileVersion>2.0.0.0</FileVersion> | ||||
|     <AssemblyName>Esiur</AssemblyName> | ||||
|     <RootNamespace>Esiur</RootNamespace> | ||||
|     <PackageId>Esiur</PackageId> | ||||
|     <Product>Esiur</Product> | ||||
|     <LangVersion>9.0</LangVersion> | ||||
|     <LangVersion>latest</LangVersion> | ||||
|     <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||||
| @@ -79,10 +79,13 @@ | ||||
|     <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||||
|     <!-- Package the Newtonsoft.Json dependency alongside the generator assembly --> | ||||
|     <None Include="$(PkgSystem_Text_Json)\lib\netstandard2.0\*.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||||
|  | ||||
|  | ||||
|     <None Include="Tools/*" Pack="true" PackagePath="tools/" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <None Include="LICENSE" Pack="true" PackagePath=""> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project>  | ||||
							
								
								
									
										21
									
								
								Esiur/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Esiur/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| MIT License | ||||
|  | ||||
| Copyright (c) 2017-2021 Esiur Foundation, Ahmed Kh. Zamil. | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
| @@ -42,449 +42,448 @@ using System.Text.Json; | ||||
| using Esiur.Resource; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace Esiur.Misc | ||||
| namespace Esiur.Misc; | ||||
| public static class Global | ||||
| { | ||||
|     public static class Global | ||||
|     private static KeyList<string, object> variables = new KeyList<string, object>(); | ||||
|     // private static Hashtable m_Cached = new Hashtable(); | ||||
|     //internal static bool SystemIsWorking = false; | ||||
|  | ||||
|     private static Random rand = new Random(System.Environment.TickCount); | ||||
|  | ||||
|     //public static Encoding DefaultEncoding =  Encoding.GetEncoding(1252);// .GetEncoding("windows-1252"); | ||||
|  | ||||
|     public static KeyList<string, long> Counters = new KeyList<string, long>(); | ||||
|  | ||||
|     public delegate void LogEvent(string service, LogType type, string message); | ||||
|  | ||||
|     public static event LogEvent SystemLog; | ||||
|  | ||||
|  | ||||
|     public static string ToJson(this IResource resource) | ||||
|     { | ||||
|         private static KeyList<string, object> variables = new KeyList<string, object>(); | ||||
|         // private static Hashtable m_Cached = new Hashtable(); | ||||
|         //internal static bool SystemIsWorking = false; | ||||
|  | ||||
|         private static Random rand = new Random(System.Environment.TickCount); | ||||
|  | ||||
|         //public static Encoding DefaultEncoding =  Encoding.GetEncoding(1252);// .GetEncoding("windows-1252"); | ||||
|  | ||||
|         public static KeyList<string, long> Counters = new KeyList<string, long>(); | ||||
|  | ||||
|         public delegate void LogEvent(string service, LogType type, string message); | ||||
|  | ||||
|         public static event LogEvent SystemLog; | ||||
|  | ||||
|  | ||||
|         public static string ToJson(this IResource resource) | ||||
|         try | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 return JsonSerializer.Serialize(resource, Global.SerializeOptions); | ||||
|             }catch (Exception ex) | ||||
|             { | ||||
|                 Console.WriteLine(ex.ToString()); | ||||
|                 return "{}"; | ||||
|             } | ||||
|             return JsonSerializer.Serialize(resource, Global.SerializeOptions); | ||||
|         } | ||||
|  | ||||
|         public static JsonSerializerOptions SerializeOptions = new JsonSerializerOptions | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             ReferenceHandler = ReferenceHandler.Preserve, | ||||
|             WriteIndented = true, | ||||
|             Converters = | ||||
|             Console.WriteLine(ex.ToString()); | ||||
|             return "{}"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static JsonSerializerOptions SerializeOptions = new JsonSerializerOptions | ||||
|     { | ||||
|         ReferenceHandler = ReferenceHandler.Preserve, | ||||
|         WriteIndented = true, | ||||
|         Converters = | ||||
|             { | ||||
|                 new ResourceJsonConverter(), | ||||
|                 new DoubleJsonConverter() | ||||
|             } | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|  | ||||
|         public static string Version { get; }= FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; | ||||
|     public static string Version { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; | ||||
|  | ||||
|         //FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location); | ||||
|         //      string version = fvi.FileVersion; | ||||
|     //FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location); | ||||
|     //      string version = fvi.FileVersion; | ||||
|  | ||||
|  | ||||
|         /* | ||||
|         public static char GetDirectorySeparator() | ||||
|         { | ||||
|             return System.IO.Path.DirectorySeparatorChar; | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public static void Log(Exception ex, params object[] arguments) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|  | ||||
|                   | ||||
|                 var stack = new StackTrace(ex, true); | ||||
|                 var frame = stack.GetFrames().First(); | ||||
|                 var method = frame.GetMethod(); | ||||
|                 var parameters = method.GetParameters(); | ||||
|                 var service = method.DeclaringType.Name; | ||||
|                 var message = ""; | ||||
|  | ||||
|                 if (arguments.Length > 0 && parameters.Length > 0) | ||||
|                 { | ||||
|                     message = "Arguments ( "; | ||||
|  | ||||
|                     for (int i = 0; i < parameters.Length && i < arguments.Length; i++) | ||||
|                     { | ||||
|                         message += parameters[i].Name + ": " + arguments[i].ToString() + " "; | ||||
|                     } | ||||
|  | ||||
|                     message += ")" + Environment.NewLine + "------------------------------------------------"; | ||||
|                 } | ||||
|  | ||||
|                                 message += ex.ToString(); | ||||
|  | ||||
|                 Log(service, LogType.Error, message); | ||||
|  | ||||
|                | ||||
|  | ||||
|                 Log(service, LogType.Error, ex.ToString()); | ||||
|  | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static void Log(string service, LogType type, string message, bool appendHeader = true) | ||||
|         { | ||||
|             //if (type != LogType.Debug) | ||||
|             Console.WriteLine(service + " " + message); | ||||
|  | ||||
|             SystemLog?.Invoke(service, type, message); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|         public static string GetTempPath() | ||||
|         { | ||||
|             return System.IO.Path.GetTempPath(); | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public static string RemoveControlCharacters(string inString) | ||||
|         { | ||||
|             if (inString == null) return null; | ||||
|  | ||||
|             StringBuilder newString = new StringBuilder(); | ||||
|             char ch; | ||||
|  | ||||
|             for (int i = 0; i < inString.Length; i++) | ||||
|             { | ||||
|  | ||||
|                 ch = inString[i]; | ||||
|  | ||||
|                 if (!char.IsControl(ch)) | ||||
|                 { | ||||
|                     newString.Append(ch); | ||||
|                 } | ||||
|             } | ||||
|             return newString.ToString(); | ||||
|         } | ||||
|  | ||||
|         public static void PrintCounters() | ||||
|         { | ||||
|             string[] keys = new string[Counters.Keys.Count]; | ||||
|             Counters.Keys.CopyTo(keys, 0); | ||||
|  | ||||
|             foreach (string k in keys) | ||||
|             { | ||||
|                 Console.WriteLine(k + ":" + Counters[k]); | ||||
|             } | ||||
|         } | ||||
| //        Encoding ANSI = Encoding.GetEncoding(1252); | ||||
|  | ||||
|             /* | ||||
|         public static Hashtable Cached | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return m_Cached; | ||||
|             } | ||||
|         }*/ | ||||
|  | ||||
|         /* | ||||
|         public static string ByteArrayToMAC(byte[] array) | ||||
|         { | ||||
|             string rt=""; | ||||
|  | ||||
|             if (array == null) | ||||
|                 return "00:00:00:00:00:00"; | ||||
|             else | ||||
|             { | ||||
|                 //for (int i = 0; i < array.Length - 1; i++) | ||||
|                 //    rt += Convert.ToString(array[i], 16) + ":"; | ||||
|                  | ||||
|                 //rt += Convert.ToString(array[array.Length - 1], 16); | ||||
|  | ||||
|                 rt = BitConverter.ToString(array); | ||||
|                 rt = rt.Replace('-', ':'); | ||||
|                 return rt; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|          | ||||
|  | ||||
|         /* | ||||
|         public static string IPAddressFromInt32(UInt32 IP) | ||||
|         { | ||||
|             //var dIP = DC.ToBytes(IP); | ||||
|  | ||||
|             return (IP >> 24) + "." + ((IP >> 16) & 0xFF) + "." + ((IP >> 8) & 0xFF) + "." + (IP & 0xFF); | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public static KeyList<string, object> Variables | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return variables; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public static uint CurrentUnixTime() | ||||
|         { | ||||
|             return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; | ||||
|         } | ||||
|  | ||||
|         public static void SetConsoleColors(ConsoleColor ForegroundColor, ConsoleColor BackgroundColor) | ||||
|         { | ||||
|             if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||||
|             { | ||||
|                 switch (ForegroundColor) | ||||
|                 { | ||||
|                     case ConsoleColor.Black: | ||||
|                         Console.Write("\u001B[30m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Blue: | ||||
|                         Console.Write("\u001B[1;34m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Cyan: | ||||
|                         Console.Write("\u001B[1;36m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Gray: | ||||
|                     case ConsoleColor.DarkGray: | ||||
|                         Console.Write("\u001B[1;30m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Green: | ||||
|                         Console.Write("\u001B[1;32m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Magenta: | ||||
|                         Console.Write("\u001B[1;35m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Red: | ||||
|                         Console.Write("\u001B[1;31m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.White: | ||||
|                         Console.Write("\u001B[1;37m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Yellow: | ||||
|                         Console.Write("\u001B[1;33m"); | ||||
|                         break; | ||||
|  | ||||
|                     case ConsoleColor.DarkBlue: | ||||
|                         Console.Write("\u001B[34m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkCyan: | ||||
|                         Console.Write("\u001B[36m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkGreen: | ||||
|                         Console.Write("\u001B[32m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkMagenta: | ||||
|                         Console.Write("\u001B[35m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkRed: | ||||
|                         Console.Write("\u001B[31m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkYellow: | ||||
|                         Console.Write("\u001B[33m"); | ||||
|                         break; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 switch (BackgroundColor) | ||||
|                 { | ||||
|                     case ConsoleColor.Black: | ||||
|                         Console.Write("\u001B[40m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Blue: | ||||
|                         Console.Write("\u001B[1;44m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Cyan: | ||||
|                         Console.Write("\u001B[1;46m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Gray: | ||||
|                     case ConsoleColor.DarkGray: | ||||
|                         Console.Write("\u001B[1;40m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Green: | ||||
|                         Console.Write("\u001B[1;42m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Magenta: | ||||
|                         Console.Write("\u001B[1;45m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Red: | ||||
|                         Console.Write("\u001B[1;41m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.White: | ||||
|                         Console.Write("\u001B[1;47m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.Yellow: | ||||
|                         Console.Write("\u001B[1;43m"); | ||||
|                         break; | ||||
|  | ||||
|                     case ConsoleColor.DarkBlue: | ||||
|                         Console.Write("\u001B[44m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkCyan: | ||||
|                         Console.Write("\u001B[46m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkGreen: | ||||
|                         Console.Write("\u001B[42m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkMagenta: | ||||
|                         Console.Write("\u001B[45m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkRed: | ||||
|                         Console.Write("\u001B[41m"); | ||||
|                         break; | ||||
|                     case ConsoleColor.DarkYellow: | ||||
|                         Console.Write("\u001B[43m"); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 Console.ForegroundColor = ForegroundColor; | ||||
|                 Console.BackgroundColor = BackgroundColor; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static string GetUserPart(string strAddress) | ||||
|         { | ||||
|             return strAddress.Substring(0, strAddress.IndexOf("@", 0)); | ||||
|         } | ||||
|  | ||||
|         public static byte[][] GetBytesFromChunk(byte[] Data, int ChunkSize) | ||||
|         { | ||||
|             if (ChunkSize == 1) | ||||
|             { | ||||
|                 byte[][] ar = new byte[0][]; | ||||
|                 int ptr = 0; | ||||
|                 while (ptr < Data.Length) | ||||
|                 { | ||||
|                     Array.Resize<byte[]>(ref ar, ar.Length + 1); | ||||
|                     ar[ar.Length - 1] = new byte[Data[ptr]]; | ||||
|                     Buffer.BlockCopy(Data, ++ptr, ar[ar.Length - 1], 0, Data[ptr]); | ||||
|                     ptr += Data[ptr] + 1; | ||||
|                 } | ||||
|                 return ar; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|  | ||||
|   | ||||
|          | ||||
|   | ||||
|         public static string GetFileTitle(string Filename) | ||||
|         { | ||||
|             string[] s = Filename.Split(Path.DirectorySeparatorChar); | ||||
|             return s[s.Length - 1]; | ||||
|         } | ||||
|  | ||||
|         public static string GetNewFileName(string FileDir) | ||||
|         { | ||||
|             string tempGetNewFileName = null; | ||||
|             short i = 0; | ||||
|             string NewFile = null; | ||||
|             NewFile = FileDir; | ||||
|         Begin: | ||||
|             FileInfo FF = new FileInfo(NewFile); | ||||
|             if (FF.Exists) | ||||
|             { | ||||
|                 //If FSO.FileExists(NewFile) Then | ||||
|                 i++; //= i + 1; | ||||
|                 NewFile = FileDir.Substring(0, FileDir.Length - 4) + "_" + i + "." + FileDir.Substring(FileDir.Length - 3); | ||||
|                 goto Begin; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 tempGetNewFileName = NewFile; | ||||
|             } | ||||
|             return tempGetNewFileName; | ||||
|         } | ||||
|  | ||||
|         ///////////////////////////////////// | ||||
|         public static string TrimEx(string strIn) | ||||
|         { | ||||
|             return strIn.Replace("\r", "").Replace("\n", ""); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|         public static bool IsUnix() | ||||
|         { | ||||
|             // Linux OSs under Mono 1.2 uses unknown integer numbers so this should identify all non windows as unix | ||||
|             return (Environment.OSVersion.Platform != PlatformID.Win32NT | ||||
|                 && Environment.OSVersion.Platform != PlatformID.Win32Windows); // || Environment.OSVersion.Platform == PlatformID.Linux;  | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public static string GenerateCode() | ||||
|         { | ||||
|             return GenerateCode(16); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public static byte[] GenerateBytes(int length) | ||||
|         { | ||||
|             var b  = new byte[length]; | ||||
|             rand.NextBytes(b); | ||||
|             return b; | ||||
|         } | ||||
|  | ||||
|         public static string GenerateCode(int length) | ||||
|         { | ||||
|             return GenerateCode(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");// ~!@#$%^&*()_-+=\\?/"); | ||||
|         } | ||||
|  | ||||
|         public static string GenerateCode(int length, string chars) | ||||
|         //public static string GenerateCode(int Length) | ||||
|         { | ||||
|             //var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_-+=\\?/"; | ||||
|             var result = new string( | ||||
|                 Enumerable.Repeat(chars, length) | ||||
|                           .Select(s => s[rand.Next(s.Length)]) | ||||
|                           .ToArray()); | ||||
|             //if (result.Length < length) | ||||
|               //  Console.WriteLine(); | ||||
|             return result; | ||||
|             /* | ||||
|             int len = 0; | ||||
|             string code = ""; | ||||
|  | ||||
|             while(len < Length) | ||||
|             { | ||||
|                 var c = Convert.ToChar((byte)(rand.NextDouble() * 255)); | ||||
|                 if (Char.IsLetterOrDigit(c)) | ||||
|                 { | ||||
|                     code += c; | ||||
|                     len++; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return code; | ||||
|             */ | ||||
|         } | ||||
|  | ||||
|         public static string ReplaceOnce(string Expression, string Find, string Replacement) | ||||
|         { | ||||
|             int pos = Expression.IndexOf(Find); | ||||
|             if (pos != -1) | ||||
|                 return Expression.Substring(0, pos) + Replacement + Expression.Substring(pos + Find.Length); | ||||
|             else | ||||
|                 return Expression; | ||||
|         } | ||||
|         //public void Replace(string Expression, string Find, string Replacement, int Start, int Count) | ||||
|         //{ | ||||
|         //    Expression.IndexOf( | ||||
|         //} | ||||
|     /* | ||||
|     public static char GetDirectorySeparator() | ||||
|     { | ||||
|         return System.IO.Path.DirectorySeparatorChar; | ||||
|     } | ||||
| } | ||||
|     */ | ||||
|  | ||||
|     public static void Log(Exception ex, params object[] arguments) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|  | ||||
|  | ||||
|             var stack = new StackTrace(ex, true); | ||||
|             var frame = stack.GetFrames().First(); | ||||
|             var method = frame.GetMethod(); | ||||
|             var parameters = method.GetParameters(); | ||||
|             var service = method.DeclaringType.Name; | ||||
|             var message = ""; | ||||
|  | ||||
|             if (arguments.Length > 0 && parameters.Length > 0) | ||||
|             { | ||||
|                 message = "Arguments ( "; | ||||
|  | ||||
|                 for (int i = 0; i < parameters.Length && i < arguments.Length; i++) | ||||
|                 { | ||||
|                     message += parameters[i].Name + ": " + arguments[i].ToString() + " "; | ||||
|                 } | ||||
|  | ||||
|                 message += ")" + Environment.NewLine + "------------------------------------------------"; | ||||
|             } | ||||
|  | ||||
|             message += ex.ToString(); | ||||
|  | ||||
|             Log(service, LogType.Error, message); | ||||
|  | ||||
|  | ||||
|  | ||||
|             Log(service, LogType.Error, ex.ToString()); | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void Log(string service, LogType type, string message, bool appendHeader = true) | ||||
|     { | ||||
|         //if (type != LogType.Debug) | ||||
|         Console.WriteLine(service + " " + message); | ||||
|  | ||||
|         SystemLog?.Invoke(service, type, message); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     public static string GetTempPath() | ||||
|     { | ||||
|         return System.IO.Path.GetTempPath(); | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public static string RemoveControlCharacters(string inString) | ||||
|     { | ||||
|         if (inString == null) return null; | ||||
|  | ||||
|         StringBuilder newString = new StringBuilder(); | ||||
|         char ch; | ||||
|  | ||||
|         for (int i = 0; i < inString.Length; i++) | ||||
|         { | ||||
|  | ||||
|             ch = inString[i]; | ||||
|  | ||||
|             if (!char.IsControl(ch)) | ||||
|             { | ||||
|                 newString.Append(ch); | ||||
|             } | ||||
|         } | ||||
|         return newString.ToString(); | ||||
|     } | ||||
|  | ||||
|     public static void PrintCounters() | ||||
|     { | ||||
|         string[] keys = new string[Counters.Keys.Count]; | ||||
|         Counters.Keys.CopyTo(keys, 0); | ||||
|  | ||||
|         foreach (string k in keys) | ||||
|         { | ||||
|             Console.WriteLine(k + ":" + Counters[k]); | ||||
|         } | ||||
|     } | ||||
|     //        Encoding ANSI = Encoding.GetEncoding(1252); | ||||
|  | ||||
|     /* | ||||
| public static Hashtable Cached | ||||
| { | ||||
|     get | ||||
|     { | ||||
|         return m_Cached; | ||||
|     } | ||||
| }*/ | ||||
|  | ||||
|     /* | ||||
|     public static string ByteArrayToMAC(byte[] array) | ||||
|     { | ||||
|         string rt=""; | ||||
|  | ||||
|         if (array == null) | ||||
|             return "00:00:00:00:00:00"; | ||||
|         else | ||||
|         { | ||||
|             //for (int i = 0; i < array.Length - 1; i++) | ||||
|             //    rt += Convert.ToString(array[i], 16) + ":"; | ||||
|  | ||||
|             //rt += Convert.ToString(array[array.Length - 1], 16); | ||||
|  | ||||
|             rt = BitConverter.ToString(array); | ||||
|             rt = rt.Replace('-', ':'); | ||||
|             return rt; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|  | ||||
|  | ||||
|     /* | ||||
|     public static string IPAddressFromInt32(UInt32 IP) | ||||
|     { | ||||
|         //var dIP = DC.ToBytes(IP); | ||||
|  | ||||
|         return (IP >> 24) + "." + ((IP >> 16) & 0xFF) + "." + ((IP >> 8) & 0xFF) + "." + (IP & 0xFF); | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public static KeyList<string, object> Variables | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return variables; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static uint CurrentUnixTime() | ||||
|     { | ||||
|         return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; | ||||
|     } | ||||
|  | ||||
|     public static void SetConsoleColors(ConsoleColor ForegroundColor, ConsoleColor BackgroundColor) | ||||
|     { | ||||
|         if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||||
|         { | ||||
|             switch (ForegroundColor) | ||||
|             { | ||||
|                 case ConsoleColor.Black: | ||||
|                     Console.Write("\u001B[30m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Blue: | ||||
|                     Console.Write("\u001B[1;34m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Cyan: | ||||
|                     Console.Write("\u001B[1;36m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Gray: | ||||
|                 case ConsoleColor.DarkGray: | ||||
|                     Console.Write("\u001B[1;30m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Green: | ||||
|                     Console.Write("\u001B[1;32m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Magenta: | ||||
|                     Console.Write("\u001B[1;35m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Red: | ||||
|                     Console.Write("\u001B[1;31m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.White: | ||||
|                     Console.Write("\u001B[1;37m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Yellow: | ||||
|                     Console.Write("\u001B[1;33m"); | ||||
|                     break; | ||||
|  | ||||
|                 case ConsoleColor.DarkBlue: | ||||
|                     Console.Write("\u001B[34m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkCyan: | ||||
|                     Console.Write("\u001B[36m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkGreen: | ||||
|                     Console.Write("\u001B[32m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkMagenta: | ||||
|                     Console.Write("\u001B[35m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkRed: | ||||
|                     Console.Write("\u001B[31m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkYellow: | ||||
|                     Console.Write("\u001B[33m"); | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             switch (BackgroundColor) | ||||
|             { | ||||
|                 case ConsoleColor.Black: | ||||
|                     Console.Write("\u001B[40m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Blue: | ||||
|                     Console.Write("\u001B[1;44m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Cyan: | ||||
|                     Console.Write("\u001B[1;46m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Gray: | ||||
|                 case ConsoleColor.DarkGray: | ||||
|                     Console.Write("\u001B[1;40m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Green: | ||||
|                     Console.Write("\u001B[1;42m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Magenta: | ||||
|                     Console.Write("\u001B[1;45m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Red: | ||||
|                     Console.Write("\u001B[1;41m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.White: | ||||
|                     Console.Write("\u001B[1;47m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.Yellow: | ||||
|                     Console.Write("\u001B[1;43m"); | ||||
|                     break; | ||||
|  | ||||
|                 case ConsoleColor.DarkBlue: | ||||
|                     Console.Write("\u001B[44m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkCyan: | ||||
|                     Console.Write("\u001B[46m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkGreen: | ||||
|                     Console.Write("\u001B[42m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkMagenta: | ||||
|                     Console.Write("\u001B[45m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkRed: | ||||
|                     Console.Write("\u001B[41m"); | ||||
|                     break; | ||||
|                 case ConsoleColor.DarkYellow: | ||||
|                     Console.Write("\u001B[43m"); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             Console.ForegroundColor = ForegroundColor; | ||||
|             Console.BackgroundColor = BackgroundColor; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static string GetUserPart(string strAddress) | ||||
|     { | ||||
|         return strAddress.Substring(0, strAddress.IndexOf("@", 0)); | ||||
|     } | ||||
|  | ||||
|     public static byte[][] GetBytesFromChunk(byte[] Data, int ChunkSize) | ||||
|     { | ||||
|         if (ChunkSize == 1) | ||||
|         { | ||||
|             byte[][] ar = new byte[0][]; | ||||
|             int ptr = 0; | ||||
|             while (ptr < Data.Length) | ||||
|             { | ||||
|                 Array.Resize<byte[]>(ref ar, ar.Length + 1); | ||||
|                 ar[ar.Length - 1] = new byte[Data[ptr]]; | ||||
|                 Buffer.BlockCopy(Data, ++ptr, ar[ar.Length - 1], 0, Data[ptr]); | ||||
|                 ptr += Data[ptr] + 1; | ||||
|             } | ||||
|             return ar; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     public static string GetFileTitle(string Filename) | ||||
|     { | ||||
|         string[] s = Filename.Split(Path.DirectorySeparatorChar); | ||||
|         return s[s.Length - 1]; | ||||
|     } | ||||
|  | ||||
|     public static string GetNewFileName(string FileDir) | ||||
|     { | ||||
|         string tempGetNewFileName = null; | ||||
|         short i = 0; | ||||
|         string NewFile = null; | ||||
|         NewFile = FileDir; | ||||
|     Begin: | ||||
|         FileInfo FF = new FileInfo(NewFile); | ||||
|         if (FF.Exists) | ||||
|         { | ||||
|             //If FSO.FileExists(NewFile) Then | ||||
|             i++; //= i + 1; | ||||
|             NewFile = FileDir.Substring(0, FileDir.Length - 4) + "_" + i + "." + FileDir.Substring(FileDir.Length - 3); | ||||
|             goto Begin; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             tempGetNewFileName = NewFile; | ||||
|         } | ||||
|         return tempGetNewFileName; | ||||
|     } | ||||
|  | ||||
|     ///////////////////////////////////// | ||||
|     public static string TrimEx(string strIn) | ||||
|     { | ||||
|         return strIn.Replace("\r", "").Replace("\n", ""); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     public static bool IsUnix() | ||||
|     { | ||||
|         // Linux OSs under Mono 1.2 uses unknown integer numbers so this should identify all non windows as unix | ||||
|         return (Environment.OSVersion.Platform != PlatformID.Win32NT | ||||
|             && Environment.OSVersion.Platform != PlatformID.Win32Windows); // || Environment.OSVersion.Platform == PlatformID.Linux;  | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public static string GenerateCode() | ||||
|     { | ||||
|         return GenerateCode(16); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public static byte[] GenerateBytes(int length) | ||||
|     { | ||||
|         var b = new byte[length]; | ||||
|         rand.NextBytes(b); | ||||
|         return b; | ||||
|     } | ||||
|  | ||||
|     public static string GenerateCode(int length) | ||||
|     { | ||||
|         return GenerateCode(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");// ~!@#$%^&*()_-+=\\?/"); | ||||
|     } | ||||
|  | ||||
|     public static string GenerateCode(int length, string chars) | ||||
|     //public static string GenerateCode(int Length) | ||||
|     { | ||||
|         //var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_-+=\\?/"; | ||||
|         var result = new string( | ||||
|             Enumerable.Repeat(chars, length) | ||||
|                       .Select(s => s[rand.Next(s.Length)]) | ||||
|                       .ToArray()); | ||||
|         //if (result.Length < length) | ||||
|         //  Console.WriteLine(); | ||||
|         return result; | ||||
|         /* | ||||
|         int len = 0; | ||||
|         string code = ""; | ||||
|  | ||||
|         while(len < Length) | ||||
|         { | ||||
|             var c = Convert.ToChar((byte)(rand.NextDouble() * 255)); | ||||
|             if (Char.IsLetterOrDigit(c)) | ||||
|             { | ||||
|                 code += c; | ||||
|                 len++; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return code; | ||||
|         */ | ||||
|     } | ||||
|  | ||||
|     public static string ReplaceOnce(string Expression, string Find, string Replacement) | ||||
|     { | ||||
|         int pos = Expression.IndexOf(Find); | ||||
|         if (pos != -1) | ||||
|             return Expression.Substring(0, pos) + Replacement + Expression.Substring(pos + Find.Length); | ||||
|         else | ||||
|             return Expression; | ||||
|     } | ||||
|     //public void Replace(string Expression, string Find, string Replacement, int Start, int Count) | ||||
|     //{ | ||||
|     //    Expression.IndexOf( | ||||
|     //} | ||||
| } | ||||
|   | ||||
| @@ -31,26 +31,24 @@ using Esiur.Data; | ||||
| using Esiur.Net.Packets; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.DataLink | ||||
| namespace Esiur.Net.DataLink; | ||||
| public abstract class PacketFilter : IResource | ||||
| { | ||||
|     public abstract class PacketFilter : IResource | ||||
|  | ||||
|     public Instance Instance | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|     public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|         public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|     public abstract bool Execute(Packet packet); | ||||
|  | ||||
|         public abstract bool Execute(Packet packet); | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,91 +32,89 @@ using System.Runtime.InteropServices; | ||||
| using Esiur.Net.Packets; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.DataLink | ||||
| namespace Esiur.Net.DataLink; | ||||
| public class PacketServer : IResource | ||||
| { | ||||
|     public class PacketServer:IResource | ||||
|     List<PacketSource> sources = new List<PacketSource>(); | ||||
|     List<PacketFilter> filters = new List<PacketFilter>(); | ||||
|  | ||||
|  | ||||
|     [Storable] | ||||
|     public string Mode | ||||
|     { | ||||
|         List<PacketSource> sources = new List<PacketSource>(); | ||||
|         List<PacketFilter> filters = new List<PacketFilter>(); | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|          | ||||
|         [Storable] | ||||
|         public string Mode | ||||
|     public Instance Instance | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public List<PacketSource> Sources | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             return sources; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public List<PacketSource> Sources | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|         { | ||||
|             get | ||||
|             /* | ||||
|             foreach (var resource in Instance.Children<IResource>()) | ||||
|             { | ||||
|                 return sources; | ||||
|  | ||||
|                 if (resource is PacketFilter) | ||||
|                 { | ||||
|                     filters.Add(resource as PacketFilter); | ||||
|                 } | ||||
|                 else if (resource is PacketSource) | ||||
|                 { | ||||
|                     sources.Add(resource as PacketSource); | ||||
|                 } | ||||
|             } | ||||
|             */ | ||||
|             foreach (var src in sources) | ||||
|             { | ||||
|                 src.OnNewPacket += PacketReceived; | ||||
|                 src.Open(); | ||||
|             } | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.Terminate) | ||||
|         { | ||||
|             //            foreach (var src in sources) | ||||
|             //              src.Close(); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemReload) | ||||
|         { | ||||
|             foreach (var src in sources) | ||||
|             { | ||||
|                 src.Close(); | ||||
|                 src.Open(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
|  | ||||
|         public void Destroy() | ||||
|     void PacketReceived(Packet Packet) | ||||
|     { | ||||
|         foreach (var f in filters) | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|             if (f.Execute(Packet)) | ||||
|             { | ||||
|                 /* | ||||
|                 foreach (var resource in Instance.Children<IResource>()) | ||||
|                 { | ||||
|  | ||||
|                     if (resource is PacketFilter) | ||||
|                     { | ||||
|                         filters.Add(resource as PacketFilter); | ||||
|                     } | ||||
|                     else if (resource is PacketSource) | ||||
|                     { | ||||
|                         sources.Add(resource as PacketSource); | ||||
|                     } | ||||
|                 } | ||||
|                 */ | ||||
|                 foreach (var src in sources) | ||||
|                 { | ||||
|                     src.OnNewPacket += PacketReceived; | ||||
|                     src.Open(); | ||||
|                 } | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.Terminate) | ||||
|             { | ||||
|     //            foreach (var src in sources) | ||||
|       //              src.Close(); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemReload) | ||||
|             { | ||||
|                 foreach (var src in sources) | ||||
|                 { | ||||
|                     src.Close(); | ||||
|                     src.Open(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return new AsyncReply<bool>( true); | ||||
|         }         | ||||
|  | ||||
|         void PacketReceived(Packet Packet) | ||||
|         { | ||||
|             foreach (var f in filters) | ||||
|             {                 | ||||
|                 if (f.Execute(Packet)) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -30,66 +30,64 @@ using System.Text; | ||||
| using Esiur.Core; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.DataLink | ||||
| namespace Esiur.Net.DataLink; | ||||
| public abstract class PacketSource : IResource | ||||
| { | ||||
|     public abstract class PacketSource: IResource | ||||
|     public delegate void NewPacket(Packet Packet); | ||||
|     public abstract event NewPacket OnNewPacket; | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public Instance Instance | ||||
|     { | ||||
|         public delegate void NewPacket(Packet Packet); | ||||
|         public abstract event NewPacket OnNewPacket; | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public Instance Instance | ||||
|  | ||||
|     public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|  | ||||
|     public abstract bool RawMode | ||||
|     { | ||||
|         set; | ||||
|         get; | ||||
|     } | ||||
|  | ||||
|     //public PacketSource(PacketServer Server, bool RawMode) | ||||
|     //{ | ||||
|     //  this.RawMode = RawMode; | ||||
|     //} | ||||
|  | ||||
|  | ||||
|     public abstract bool Open(); | ||||
|  | ||||
|     public abstract bool Close(); | ||||
|  | ||||
|  | ||||
|     public abstract bool Write(Packet packet); | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     public virtual string TypeName | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|  | ||||
|         public abstract bool RawMode | ||||
|         { | ||||
|             set; | ||||
|             get; | ||||
|         } | ||||
|  | ||||
|         //public PacketSource(PacketServer Server, bool RawMode) | ||||
|         //{ | ||||
|         //  this.RawMode = RawMode; | ||||
|         //} | ||||
|  | ||||
|  | ||||
|         public abstract bool Open(); | ||||
|  | ||||
|         public abstract bool Close(); | ||||
|  | ||||
|  | ||||
|         public abstract bool Write(Packet packet); | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|         public virtual string TypeName | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return "Raw"; | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public abstract byte[] Address | ||||
|         { | ||||
|             get; | ||||
|         } | ||||
|  | ||||
|         public abstract string DeviceId | ||||
|         { | ||||
|             get; | ||||
|             return "Raw"; | ||||
|         } | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public abstract byte[] Address | ||||
|     { | ||||
|         get; | ||||
|     } | ||||
|  | ||||
|     public abstract string DeviceId | ||||
|     { | ||||
|         get; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -38,400 +38,399 @@ using Esiur.Misc; | ||||
| using System.Security.Cryptography; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Net.HTTP | ||||
| namespace Esiur.Net.HTTP; | ||||
| public class HTTPConnection : NetworkConnection | ||||
| { | ||||
|     public class HTTPConnection : NetworkConnection | ||||
|  | ||||
|  | ||||
|  | ||||
|     public bool WSMode { get; internal set; } | ||||
|     public HTTPServer Server { get; internal set; } | ||||
|  | ||||
|     public WebsocketPacket WSRequest { get; set; } | ||||
|     public HTTPRequestPacket Request { get; set; } | ||||
|     public HTTPResponsePacket Response { get; } = new HTTPResponsePacket(); | ||||
|  | ||||
|     HTTPSession session; | ||||
|  | ||||
|     public KeyList<string, object> Variables { get; } = new KeyList<string, object>(); | ||||
|  | ||||
|  | ||||
|  | ||||
|     internal long Parse(byte[] data) | ||||
|     { | ||||
|  | ||||
|      | ||||
|  | ||||
|         public bool WSMode { get; internal set; } | ||||
|         public HTTPServer Server { get; internal set; } | ||||
|  | ||||
|         public WebsocketPacket WSRequest { get; set; } | ||||
|         public HTTPRequestPacket Request { get; set; } | ||||
|         public HTTPResponsePacket Response { get; } = new HTTPResponsePacket(); | ||||
|  | ||||
|         HTTPSession session; | ||||
|  | ||||
|         public KeyList<string, object> Variables { get; } = new KeyList<string, object>(); | ||||
|  | ||||
|  | ||||
|  | ||||
|         internal long Parse(byte[] data) | ||||
|         if (WSMode) | ||||
|         { | ||||
|             if (WSMode) | ||||
|             // now parse WS protocol | ||||
|             WebsocketPacket ws = new WebsocketPacket(); | ||||
|  | ||||
|             var pSize = ws.Parse(data, 0, (uint)data.Length); | ||||
|  | ||||
|  | ||||
|             if (pSize > 0) | ||||
|             { | ||||
|                 // now parse WS protocol | ||||
|                 WebsocketPacket ws = new WebsocketPacket(); | ||||
|  | ||||
|                 var pSize = ws.Parse(data, 0, (uint)data.Length); | ||||
|  | ||||
|  | ||||
|                 if (pSize > 0) | ||||
|                 { | ||||
|                     WSRequest = ws; | ||||
|                     return 0; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return pSize; | ||||
|                 } | ||||
|                 WSRequest = ws; | ||||
|                 return 0; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var rp = new HTTPRequestPacket(); | ||||
|                 var pSize = rp.Parse(data, 0, (uint)data.Length); | ||||
|                 if (pSize > 0) | ||||
|                 { | ||||
|                     Request = rp; | ||||
|                     return 0; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return pSize; | ||||
|                 } | ||||
|                 return pSize; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Flush() | ||||
|         else | ||||
|         { | ||||
|             // close the connection | ||||
|             if (Request.Headers["connection"].ToLower() != "keep-alive" & IsConnected) | ||||
|                 Close(); | ||||
|             var rp = new HTTPRequestPacket(); | ||||
|             var pSize = rp.Parse(data, 0, (uint)data.Length); | ||||
|             if (pSize > 0) | ||||
|             { | ||||
|                 Request = rp; | ||||
|                 return 0; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return pSize; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Flush() | ||||
|     { | ||||
|         // close the connection | ||||
|         if (Request.Headers["connection"].ToLower() != "keep-alive" & IsConnected) | ||||
|             Close(); | ||||
|     } | ||||
|  | ||||
|     public bool Upgrade() | ||||
|     { | ||||
|         if (IsWebsocketRequest()) | ||||
|         { | ||||
|             string magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | ||||
|             string ret = Request.Headers["Sec-WebSocket-Key"] + magicString; | ||||
|             // Compute the SHA1 hash | ||||
|             SHA1 sha = SHA1.Create(); | ||||
|             byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret)); | ||||
|             Response.Headers["Upgrade"] = Request.Headers["Upgrade"]; | ||||
|             Response.Headers["Connection"] = Request.Headers["Connection"];// "Upgrade"; | ||||
|             Response.Headers["Sec-WebSocket-Accept"] = Convert.ToBase64String(sha1Hash); | ||||
|  | ||||
|             if (Request.Headers.ContainsKey("Sec-WebSocket-Protocol")) | ||||
|                 Response.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"]; | ||||
|  | ||||
|  | ||||
|             Response.Number = HTTPResponsePacket.ResponseCode.Switching; | ||||
|             Response.Text = "Switching Protocols"; | ||||
|             WSMode = true; | ||||
|  | ||||
|             Send(); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public bool Upgrade() | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public HTTPServer Parent | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             if (IsWebsocketRequest()) | ||||
|             return Server; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void Send(WebsocketPacket packet) | ||||
|     { | ||||
|         if (packet.Data != null) | ||||
|             base.Send(packet.Data); | ||||
|     } | ||||
|  | ||||
|     public override void Send(string data) | ||||
|     { | ||||
|         Response.Message = Encoding.UTF8.GetBytes(data); | ||||
|         Send(); | ||||
|     } | ||||
|  | ||||
|     public override void Send(byte[] msg, int offset, int length) | ||||
|     { | ||||
|         Response.Message = DC.Clip(msg, (uint)offset, (uint)length); | ||||
|         Send(); | ||||
|     } | ||||
|  | ||||
|     public override void Send(byte[] message) | ||||
|     { | ||||
|         Response.Message = message; | ||||
|         Send(); | ||||
|     } | ||||
|  | ||||
|     public void Send(HTTPResponsePacket.ComposeOptions Options = HTTPResponsePacket.ComposeOptions.AllCalculateLength) | ||||
|     { | ||||
|         if (Response.Handled) | ||||
|             return; | ||||
|  | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             Response.Compose(Options); | ||||
|             base.Send(Response.Data); | ||||
|  | ||||
|             // Refresh the current session | ||||
|             if (session != null) | ||||
|                 session.Refresh(); | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 string magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | ||||
|                 string ret = Request.Headers["Sec-WebSocket-Key"] + magicString; | ||||
|                 // Compute the SHA1 hash | ||||
|                 SHA1 sha = SHA1.Create(); | ||||
|                 byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret)); | ||||
|                 Response.Headers["Upgrade"] = Request.Headers["Upgrade"]; | ||||
|                 Response.Headers["Connection"] = Request.Headers["Connection"];// "Upgrade"; | ||||
|                 Response.Headers["Sec-WebSocket-Accept"] = Convert.ToBase64String(sha1Hash); | ||||
|                 Close(); | ||||
|             } | ||||
|             finally { } | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|                 if (Request.Headers.ContainsKey("Sec-WebSocket-Protocol")) | ||||
|                     Response.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|                 Response.Number = HTTPResponsePacket.ResponseCode.Switching; | ||||
|                 Response.Text = "Switching Protocols"; | ||||
|                 WSMode = true; | ||||
|     public void CreateNewSession() | ||||
|     { | ||||
|         if (session == null) | ||||
|         { | ||||
|             // Create a new one | ||||
|             session = Server.CreateSession(Global.GenerateCode(12), 60 * 20); | ||||
|  | ||||
|                 Send(); | ||||
|             HTTPResponsePacket.HTTPCookie cookie = new HTTPResponsePacket.HTTPCookie("SID", session.Id); | ||||
|             cookie.Expires = DateTime.MaxValue; | ||||
|             cookie.Path = "/"; | ||||
|             cookie.HttpOnly = true; | ||||
|  | ||||
|             Response.Cookies.Add(cookie); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public bool IsWebsocketRequest() | ||||
|     { | ||||
|         if (Request.Headers.ContainsKey("connection") | ||||
|             && Request.Headers["connection"].ToLower().Contains("upgrade") | ||||
|             && Request.Headers.ContainsKey("upgrade") | ||||
|             && Request.Headers["upgrade"].ToLower() == "websocket" | ||||
|             && Request.Headers.ContainsKey("Sec-WebSocket-Version") | ||||
|             && Request.Headers["Sec-WebSocket-Version"] == "13" | ||||
|             && Request.Headers.ContainsKey("Sec-WebSocket-Key")) | ||||
|         //&& Request.Headers.ContainsKey("Sec-WebSocket-Protocol")) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected override void DataReceived(NetworkBuffer data) | ||||
|     { | ||||
|  | ||||
|         byte[] msg = data.Read(); | ||||
|  | ||||
|         var BL = Parse(msg); | ||||
|  | ||||
|         if (BL == 0) | ||||
|         { | ||||
|             if (Request.Method == HTTPRequestPacket.HTTPMethod.UNKNOWN) | ||||
|             { | ||||
|                 Close(); | ||||
|                 return; | ||||
|             } | ||||
|             if (Request.URL == "") | ||||
|             { | ||||
|                 Close(); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         else if (BL == -1) | ||||
|         { | ||||
|             data.HoldForNextWrite(msg); | ||||
|             return; | ||||
|         } | ||||
|         else if (BL < 0) | ||||
|         { | ||||
|             data.HoldFor(msg, (uint)(msg.Length - BL)); | ||||
|             return; | ||||
|         } | ||||
|         else if (BL > 0) | ||||
|         { | ||||
|             if (BL > Server.MaxPost) | ||||
|             { | ||||
|                 Send( | ||||
|                     "<html><body>POST method content is larger than " | ||||
|                     + Server.MaxPost | ||||
|                     + " bytes.</body></html>"); | ||||
|  | ||||
|                 Close(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 data.HoldFor(msg, (uint)(msg.Length + BL)); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|         else if (BL < 0) // for security | ||||
|         { | ||||
|             Close(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         if (IsWebsocketRequest() & !WSMode) | ||||
|         { | ||||
|             Upgrade(); | ||||
|             //return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //return; | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             if (!Server.Execute(this)) | ||||
|             { | ||||
|                 Response.Number = HTTPResponsePacket.ResponseCode.InternalServerError; | ||||
|                 Send("Bad Request"); | ||||
|                 Close(); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             if (ex.Message != "Thread was being aborted.") | ||||
|             { | ||||
|  | ||||
|                 Global.Log("HTTPServer", LogType.Error, ex.ToString()); | ||||
|  | ||||
|                 //Console.WriteLine(ex.ToString()); | ||||
|                 //EventLog.WriteEntry("HttpServer", ex.ToString(), EventLogEntryType.Error); | ||||
|                 Send(Error500(ex.Message)); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private string Error500(string msg) | ||||
|     { | ||||
|         return "<html><head><title>500 Internal Server Error</title></head><br>\r\n" | ||||
|                  + "<body><br>\r\n" | ||||
|                  + "<b>500</b> Internal Server Error<br>" + msg + "\r\n" | ||||
|                  + "</body><br>\r\n" | ||||
|                  + "</html><br>\r\n"; | ||||
|     } | ||||
|  | ||||
|     public async AsyncReply<bool> SendFile(string filename) | ||||
|     { | ||||
|         if (Response.Handled == true) | ||||
|             return false; | ||||
|  | ||||
|  | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             //HTTP/1.1 200 OK | ||||
|             //Server: Microsoft-IIS/5.0 | ||||
|             //Content-Location: http://127.0.0.1/index.html | ||||
|             //Date: Wed, 10 Dec 2003 19:10:25 GMT | ||||
|             //Content-Type: text/html | ||||
|             //Accept-Ranges: bytes | ||||
|             //Last-Modified: Mon, 22 Sep 2003 22:36:56 GMT | ||||
|             //Content-Length: 1957 | ||||
|  | ||||
|             if (!File.Exists(filename)) | ||||
|             { | ||||
|                 Response.Number = HTTPResponsePacket.ResponseCode.NotFound; | ||||
|                 Send("File Not Found"); | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         public HTTPServer Parent | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return Server; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Send(WebsocketPacket packet) | ||||
|         { | ||||
|             if (packet.Data != null) | ||||
|                 base.Send(packet.Data); | ||||
|         } | ||||
|  | ||||
|         public override void Send(string data) | ||||
|         { | ||||
|             Response.Message = Encoding.UTF8.GetBytes(data); | ||||
|             Send(); | ||||
|         } | ||||
|  | ||||
|         public override void Send(byte[] msg, int offset, int length) | ||||
|         { | ||||
|             Response.Message = DC.Clip(msg, (uint)offset, (uint)length); | ||||
|             Send(); | ||||
|         } | ||||
|  | ||||
|         public override void Send(byte[] message) | ||||
|         { | ||||
|             Response.Message = message; | ||||
|             Send(); | ||||
|         } | ||||
|  | ||||
|         public void Send(HTTPResponsePacket.ComposeOptions Options = HTTPResponsePacket.ComposeOptions.AllCalculateLength) | ||||
|         { | ||||
|             if (Response.Handled) | ||||
|                 return; | ||||
|  | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 Response.Compose(Options); | ||||
|                 base.Send(Response.Data); | ||||
|  | ||||
|                 // Refresh the current session | ||||
|                 if (session != null) | ||||
|                     session.Refresh(); | ||||
|  | ||||
|             } | ||||
|             catch | ||||
|             var fileEditTime = File.GetLastWriteTime(filename).ToUniversalTime(); | ||||
|             if (Request.Headers.ContainsKey("if-modified-since")) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     Close(); | ||||
|                     var ims = DateTime.Parse(Request.Headers["if-modified-since"]); | ||||
|                     if ((fileEditTime - ims).TotalSeconds < 2) | ||||
|                     { | ||||
|                         Response.Number = HTTPResponsePacket.ResponseCode.NotModified; | ||||
|                         Response.Headers.Clear(); | ||||
|                         //Response.Text = "Not Modified"; | ||||
|                         Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly); | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|                 finally { } | ||||
|                 catch | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|  | ||||
|             Response.Number = HTTPResponsePacket.ResponseCode.OK; | ||||
|             // Fri, 30 Oct 2007 14:19:41 GMT | ||||
|             Response.Headers["Last-Modified"] = fileEditTime.ToString("ddd, dd MMM yyyy HH:mm:ss"); | ||||
|             FileInfo fi = new FileInfo(filename); | ||||
|             Response.Headers["Content-Length"] = fi.Length.ToString(); | ||||
|             Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly); | ||||
|  | ||||
|             //var fd = File.ReadAllBytes(filename); | ||||
|  | ||||
|             //base.Send(fd); | ||||
|  | ||||
|  | ||||
|             using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||||
|             { | ||||
|  | ||||
|                 var buffer = new byte[60000]; | ||||
|  | ||||
|  | ||||
|                 while (true) | ||||
|                 { | ||||
|                     var n = fs.Read(buffer, 0, 60000); | ||||
|  | ||||
|                     if (n <= 0) | ||||
|                         break; | ||||
|  | ||||
|                     //Thread.Sleep(50); | ||||
|                     await base.SendAsync(buffer, 0, n); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return true; | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 Close(); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void CreateNewSession() | ||||
|         { | ||||
|             if (session == null) | ||||
|             { | ||||
|                 // Create a new one | ||||
|                 session = Server.CreateSession(Global.GenerateCode(12), 60 * 20); | ||||
|  | ||||
|                 HTTPResponsePacket.HTTPCookie cookie = new HTTPResponsePacket.HTTPCookie("SID", session.Id); | ||||
|                 cookie.Expires = DateTime.MaxValue; | ||||
|                 cookie.Path = "/"; | ||||
|                 cookie.HttpOnly = true; | ||||
|  | ||||
|                 Response.Cookies.Add(cookie); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public bool IsWebsocketRequest() | ||||
|         { | ||||
|             if (Request.Headers.ContainsKey("connection") | ||||
|                 && Request.Headers["connection"].ToLower().Contains("upgrade") | ||||
|                 && Request.Headers.ContainsKey("upgrade") | ||||
|                 && Request.Headers["upgrade"].ToLower() == "websocket" | ||||
|                 && Request.Headers.ContainsKey("Sec-WebSocket-Version") | ||||
|                 && Request.Headers["Sec-WebSocket-Version"] == "13" | ||||
|                 && Request.Headers.ContainsKey("Sec-WebSocket-Key")) | ||||
|             //&& Request.Headers.ContainsKey("Sec-WebSocket-Protocol")) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         protected override void DataReceived(NetworkBuffer data) | ||||
|         { | ||||
|  | ||||
|             byte[] msg = data.Read(); | ||||
|  | ||||
|             var BL = Parse(msg); | ||||
|  | ||||
|             if (BL == 0) | ||||
|             { | ||||
|                 if (Request.Method == HTTPRequestPacket.HTTPMethod.UNKNOWN) | ||||
|                 { | ||||
|                     Close(); | ||||
|                     return; | ||||
|                 } | ||||
|                 if (Request.URL == "") | ||||
|                 { | ||||
|                     Close(); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             else if (BL == -1) | ||||
|             { | ||||
|                 data.HoldForNextWrite(msg); | ||||
|                 return; | ||||
|             } | ||||
|             else if (BL < 0) | ||||
|             { | ||||
|                 data.HoldFor(msg, (uint)(msg.Length - BL)); | ||||
|                 return; | ||||
|             } | ||||
|             else if (BL > 0) | ||||
|             { | ||||
|                 if (BL > Server.MaxPost) | ||||
|                 { | ||||
|                     Send( | ||||
|                         "<html><body>POST method content is larger than " | ||||
|                         + Server.MaxPost | ||||
|                         + " bytes.</body></html>"); | ||||
|  | ||||
|                     Close(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     data.HoldFor(msg, (uint)(msg.Length + BL)); | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|             else if (BL < 0) // for security | ||||
|             { | ||||
|                 Close(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|  | ||||
|  | ||||
|             if (IsWebsocketRequest() & !WSMode) | ||||
|             { | ||||
|                 Upgrade(); | ||||
|                 //return; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             //return; | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 if (!Server.Execute(this)) | ||||
|                 { | ||||
|                     Response.Number = HTTPResponsePacket.ResponseCode.InternalServerError; | ||||
|                     Send("Bad Request"); | ||||
|                     Close(); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 if (ex.Message != "Thread was being aborted.") | ||||
|                 { | ||||
|  | ||||
|                     Global.Log("HTTPServer", LogType.Error, ex.ToString()); | ||||
|  | ||||
|                     //Console.WriteLine(ex.ToString()); | ||||
|                     //EventLog.WriteEntry("HttpServer", ex.ToString(), EventLogEntryType.Error); | ||||
|                     Send(Error500(ex.Message)); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private string Error500(string msg) | ||||
|         { | ||||
|             return "<html><head><title>500 Internal Server Error</title></head><br>\r\n" | ||||
|                      + "<body><br>\r\n" | ||||
|                      + "<b>500</b> Internal Server Error<br>" + msg + "\r\n" | ||||
|                      + "</body><br>\r\n" | ||||
|                      + "</html><br>\r\n"; | ||||
|         } | ||||
|  | ||||
|         public async AsyncReply<bool> SendFile(string filename) | ||||
|         { | ||||
|             if (Response.Handled == true) | ||||
|                 return false; | ||||
|  | ||||
|  | ||||
|             try | ||||
|             { | ||||
|  | ||||
|                 //HTTP/1.1 200 OK | ||||
|                 //Server: Microsoft-IIS/5.0 | ||||
|                 //Content-Location: http://127.0.0.1/index.html | ||||
|                 //Date: Wed, 10 Dec 2003 19:10:25 GMT | ||||
|                 //Content-Type: text/html | ||||
|                 //Accept-Ranges: bytes | ||||
|                 //Last-Modified: Mon, 22 Sep 2003 22:36:56 GMT | ||||
|                 //Content-Length: 1957 | ||||
|  | ||||
|                 if (!File.Exists(filename)) | ||||
|                 { | ||||
|                     Response.Number = HTTPResponsePacket.ResponseCode.NotFound; | ||||
|                     Send("File Not Found"); | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 var fileEditTime = File.GetLastWriteTime(filename).ToUniversalTime(); | ||||
|                 if (Request.Headers.ContainsKey("if-modified-since")) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var ims = DateTime.Parse(Request.Headers["if-modified-since"]); | ||||
|                         if ((fileEditTime - ims).TotalSeconds < 2) | ||||
|                         { | ||||
|                             Response.Number = HTTPResponsePacket.ResponseCode.NotModified; | ||||
|                             Response.Headers.Clear(); | ||||
|                             //Response.Text = "Not Modified"; | ||||
|                             Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly); | ||||
|                             return true; | ||||
|                         } | ||||
|                     } | ||||
|                     catch | ||||
|                     { | ||||
|                         return false; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|  | ||||
|  | ||||
|                 Response.Number = HTTPResponsePacket.ResponseCode.OK; | ||||
|                 // Fri, 30 Oct 2007 14:19:41 GMT | ||||
|                 Response.Headers["Last-Modified"] = fileEditTime.ToString("ddd, dd MMM yyyy HH:mm:ss"); | ||||
|                 FileInfo fi = new FileInfo(filename); | ||||
|                 Response.Headers["Content-Length"] = fi.Length.ToString(); | ||||
|                 Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly); | ||||
|  | ||||
|                 //var fd = File.ReadAllBytes(filename); | ||||
|  | ||||
|                 //base.Send(fd); | ||||
|  | ||||
|                  | ||||
|                 using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||||
|                 { | ||||
|  | ||||
|                     var buffer = new byte[60000]; | ||||
|  | ||||
|  | ||||
|                     while (true) | ||||
|                     { | ||||
|                         var n = fs.Read(buffer, 0, 60000); | ||||
|  | ||||
|                         if (n <= 0) | ||||
|                             break; | ||||
|  | ||||
|                         //Thread.Sleep(50); | ||||
|                         await base.SendAsync(buffer, 0, n); | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 return true; | ||||
|                 | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     Close(); | ||||
|                 } | ||||
|                 finally {  | ||||
|                  | ||||
|                 } | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         protected override void Connected() | ||||
|         { | ||||
|             // do nothing | ||||
|         } | ||||
|  | ||||
|         protected override void Disconencted() | ||||
|         { | ||||
|             // do nothing | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     protected override void Connected() | ||||
|     { | ||||
|         // do nothing | ||||
|     } | ||||
|  | ||||
|     protected override void Disconencted() | ||||
|     { | ||||
|         // do nothing | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,48 +35,46 @@ using Esiur.Data; | ||||
| using Esiur.Core; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.HTTP | ||||
| namespace Esiur.Net.HTTP; | ||||
|  | ||||
| public abstract class HTTPFilter : IResource | ||||
| { | ||||
|  | ||||
|     public abstract class HTTPFilter : IResource | ||||
|     public Instance Instance | ||||
|     { | ||||
|        public Instance Instance | ||||
|        { | ||||
|             get; | ||||
|             set; | ||||
|        } | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|         /* | ||||
|         public virtual void SessionModified(HTTPSession session, string key, object oldValue, object newValue) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public virtual void SessionExpired(HTTPSession session) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public abstract AsyncReply<bool> Execute(HTTPConnection sender); | ||||
|  | ||||
|         public virtual void ClientConnected(HTTPConnection HTTP) | ||||
|         { | ||||
|             //return false; | ||||
|         } | ||||
|  | ||||
|         public virtual void ClientDisconnected(HTTPConnection HTTP) | ||||
|         { | ||||
|             //return false; | ||||
|         } | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|     /* | ||||
|     public virtual void SessionModified(HTTPSession session, string key, object oldValue, object newValue) | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public virtual void SessionExpired(HTTPSession session) | ||||
|     { | ||||
|  | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public abstract AsyncReply<bool> Execute(HTTPConnection sender); | ||||
|  | ||||
|     public virtual void ClientConnected(HTTPConnection HTTP) | ||||
|     { | ||||
|         //return false; | ||||
|     } | ||||
|  | ||||
|     public virtual void ClientDisconnected(HTTPConnection HTTP) | ||||
|     { | ||||
|         //return false; | ||||
|     } | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -39,262 +39,260 @@ using Esiur.Net.Packets; | ||||
| using System.Security.Cryptography.X509Certificates; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.HTTP | ||||
| namespace Esiur.Net.HTTP; | ||||
| public class HTTPServer : NetworkServer<HTTPConnection>, IResource | ||||
| { | ||||
|     public class HTTPServer : NetworkServer<HTTPConnection>, IResource | ||||
|     Dictionary<string, HTTPSession> sessions = new Dictionary<string, HTTPSession>(); | ||||
|     HTTPFilter[] filters = new HTTPFilter[0]; | ||||
|  | ||||
|     public Instance Instance | ||||
|     { | ||||
|         Dictionary<string, HTTPSession> sessions= new Dictionary<string, HTTPSession>(); | ||||
|         HTTPFilter[] filters = new HTTPFilter[0]; | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public Instance Instance | ||||
|     [Attribute] | ||||
|     public virtual string IP | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     public virtual ushort Port | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     //[Attribute] | ||||
|     //public virtual uint Timeout | ||||
|     //{ | ||||
|     //    get; | ||||
|     //    set; | ||||
|     //} | ||||
|  | ||||
|     //[Attribute] | ||||
|     //public virtual uint Clock | ||||
|     //{ | ||||
|     //    get; | ||||
|     //    set; | ||||
|     //} | ||||
|  | ||||
|     [Attribute] | ||||
|     public virtual uint MaxPost | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     public virtual bool SSL | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     public virtual string Certificate | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public HTTPSession CreateSession(string id, int timeout) | ||||
|     { | ||||
|         var s = new HTTPSession(); | ||||
|  | ||||
|         s.Set(id, timeout); | ||||
|  | ||||
|  | ||||
|         sessions.Add(id, s); | ||||
|  | ||||
|         return s; | ||||
|     } | ||||
|  | ||||
|     public static string MakeCookie(string Item, string Value, DateTime Expires, string Domain, string Path, bool HttpOnly) | ||||
|     { | ||||
|  | ||||
|         //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/ | ||||
|         //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/ | ||||
|         string Cookie = Item + "=" + Value; | ||||
|  | ||||
|         if (Expires.Ticks != 0) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             Cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         public virtual string IP | ||||
|         if (Domain != null) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             Cookie += "; domain=" + Domain; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         public virtual ushort Port | ||||
|         if (Path != null) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             Cookie += "; path=" + Path; | ||||
|         } | ||||
|  | ||||
|         //[Attribute] | ||||
|         //public virtual uint Timeout | ||||
|         //{ | ||||
|         //    get; | ||||
|         //    set; | ||||
|         //} | ||||
|  | ||||
|         //[Attribute] | ||||
|         //public virtual uint Clock | ||||
|         //{ | ||||
|         //    get; | ||||
|         //    set; | ||||
|         //} | ||||
|  | ||||
|         [Attribute] | ||||
|         public virtual uint MaxPost | ||||
|         if (HttpOnly) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             Cookie += "; HttpOnly"; | ||||
|         } | ||||
|         return Cookie; | ||||
|     } | ||||
|  | ||||
|         [Attribute] | ||||
|         public virtual bool SSL | ||||
|     protected override void ClientDisconnected(HTTPConnection connection) | ||||
|     { | ||||
|         foreach (var filter in filters) | ||||
|             filter.ClientDisconnected(connection); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     internal bool Execute(HTTPConnection sender) | ||||
|     { | ||||
|         foreach (var resource in filters) | ||||
|             if (resource.Execute(sender).Wait(30000)) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     /* | ||||
|     protected override void SessionEnded(NetworkSession session) | ||||
|     { | ||||
|         // verify wether there are no active connections related to the session | ||||
|  | ||||
|         foreach (HTTPConnection c in Connections)//.Values) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         public virtual string Certificate | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|           | ||||
|     | ||||
|        public HTTPSession CreateSession(string id, int timeout) | ||||
|        { | ||||
|             var s = new HTTPSession(); | ||||
|  | ||||
|             s.Set(id, timeout); | ||||
|  | ||||
|  | ||||
|             sessions.Add(id, s); | ||||
|  | ||||
|             return s; | ||||
|        } | ||||
|  | ||||
|         public static string MakeCookie(string Item, string Value, DateTime Expires, string Domain, string Path, bool HttpOnly) | ||||
|         { | ||||
|  | ||||
|             //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/ | ||||
|             //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/ | ||||
|             string Cookie = Item + "=" + Value; | ||||
|  | ||||
|             if (Expires.Ticks != 0) | ||||
|             if (c.Session == session) | ||||
|             { | ||||
|                 Cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; | ||||
|             } | ||||
|             if (Domain != null) | ||||
|             { | ||||
|                 Cookie += "; domain=" + Domain; | ||||
|             } | ||||
|             if (Path != null) | ||||
|             { | ||||
|                 Cookie += "; path=" + Path; | ||||
|             } | ||||
|             if (HttpOnly) | ||||
|             { | ||||
|                 Cookie += "; HttpOnly"; | ||||
|             } | ||||
|             return Cookie; | ||||
|         } | ||||
|  | ||||
|         protected override void ClientDisconnected(HTTPConnection connection) | ||||
|         { | ||||
|             foreach (var filter in filters) | ||||
|                 filter.ClientDisconnected(connection); | ||||
|         } | ||||
|  | ||||
|          | ||||
|  | ||||
|         internal bool Execute(HTTPConnection sender) | ||||
|         { | ||||
|             foreach (var resource in filters) | ||||
|                 if (resource.Execute(sender).Wait(30000)) | ||||
|                     return true; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         /* | ||||
|         protected override void SessionEnded(NetworkSession session) | ||||
|         { | ||||
|             // verify wether there are no active connections related to the session | ||||
|  | ||||
|             foreach (HTTPConnection c in Connections)//.Values) | ||||
|             { | ||||
|                 if (c.Session == session) | ||||
|                 { | ||||
|                     session.Refresh(); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach (Instance instance in Instance.Children) | ||||
|             { | ||||
|                 var f = (HTTPFilter)instance.Resource; | ||||
|                 f.SessionExpired((HTTPSession)session); | ||||
|             } | ||||
|  | ||||
|             base.SessionEnded((HTTPSession)session); | ||||
|             //Sessions.Remove(Session.ID); | ||||
|             //Session.Dispose(); | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         /* | ||||
|         public int TTL | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return Timeout;// mTimeout; | ||||
|             } | ||||
|         } | ||||
|          */ | ||||
|  | ||||
|  | ||||
|         public async AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|  | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|             { | ||||
|                 //var ip = (IPAddress)Instance.Attributes["ip"]; | ||||
|                 //var port = (int)Instance.Attributes["port"]; | ||||
|                 //var ssl = (bool)Instance.Attributes["ssl"]; | ||||
|                 //var cert = (string)Instance.Attributes["certificate"]; | ||||
|  | ||||
|                 //if (ip == null) ip = IPAddress.Any; | ||||
|  | ||||
|                 Sockets.ISocket listener; | ||||
|                 IPAddress ipAdd; | ||||
|  | ||||
|                 if (IP == null) | ||||
|                     ipAdd = IPAddress.Any; | ||||
|                 else | ||||
|                     ipAdd = IPAddress.Parse(IP); | ||||
|  | ||||
|                 if (SSL) | ||||
|                    listener = new SSLSocket(new IPEndPoint(ipAdd, Port), new X509Certificate2(Certificate)); | ||||
|                 else | ||||
|                     listener = new TCPSocket(new IPEndPoint(ipAdd, Port)); | ||||
|  | ||||
|                 Start(listener); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.Terminate) | ||||
|             { | ||||
|                 Stop(); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemReload) | ||||
|             { | ||||
|                 await Trigger(ResourceTrigger.Terminate); | ||||
|                 await Trigger(ResourceTrigger.Initialize); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemInitialized) | ||||
|             { | ||||
|                 filters = await Instance.Children<HTTPFilter>(); | ||||
|             } | ||||
|  | ||||
|             return true; | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public override void Add(HTTPConnection connection) | ||||
|         { | ||||
|             connection.Server = this; | ||||
|             base.Add(connection); | ||||
|         } | ||||
|  | ||||
|         public override void Remove(HTTPConnection connection) | ||||
|         { | ||||
|             connection.Server = null; | ||||
|             base.Remove(connection); | ||||
|         } | ||||
|  | ||||
|         protected override void ClientConnected(HTTPConnection connection) | ||||
|         { | ||||
|             if (filters.Length == 0) | ||||
|             { | ||||
|                 connection.Close(); | ||||
|                 session.Refresh(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             foreach (var resource in filters) | ||||
|             { | ||||
|                 resource.ClientConnected(connection); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
| 		public int LocalPort | ||||
| 		{ | ||||
| 			get  | ||||
| 			{ | ||||
| 				return cServer.LocalPort; | ||||
| 			} | ||||
| 		} | ||||
|          */ | ||||
|  | ||||
|         /*  | ||||
|         public HTTPServer(int Port) | ||||
|         foreach (Instance instance in Instance.Children) | ||||
|         { | ||||
|             cServer = new TServer(); | ||||
|             cServer.LocalPort = Port; | ||||
|             cServer.StartServer(); | ||||
|             cServer.ClientConnected += new TServer.eClientConnected(ClientConnected); | ||||
|             cServer.ClientDisConnected += new TServer.eClientDisConnected(ClientDisConnected); | ||||
|             cServer.ClientIsSwitching += new TServer.eClientIsSwitching(ClientIsSwitching); | ||||
|             cServer.DataReceived += new TServer.eDataReceived(DataReceived); | ||||
|             var f = (HTTPFilter)instance.Resource; | ||||
|             f.SessionExpired((HTTPSession)session); | ||||
|         } | ||||
|  | ||||
|         }*/ | ||||
|  | ||||
|         //~HTTPServer() | ||||
|         //{ | ||||
|         //    cServer.StopServer(); | ||||
|         //} | ||||
|         base.SessionEnded((HTTPSession)session); | ||||
|         //Sessions.Remove(Session.ID); | ||||
|         //Session.Dispose(); | ||||
|     } | ||||
| } | ||||
|     */ | ||||
|  | ||||
|     /* | ||||
|     public int TTL | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return Timeout;// mTimeout; | ||||
|         } | ||||
|     } | ||||
|      */ | ||||
|  | ||||
|  | ||||
|     public async AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|  | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|         { | ||||
|             //var ip = (IPAddress)Instance.Attributes["ip"]; | ||||
|             //var port = (int)Instance.Attributes["port"]; | ||||
|             //var ssl = (bool)Instance.Attributes["ssl"]; | ||||
|             //var cert = (string)Instance.Attributes["certificate"]; | ||||
|  | ||||
|             //if (ip == null) ip = IPAddress.Any; | ||||
|  | ||||
|             Sockets.ISocket listener; | ||||
|             IPAddress ipAdd; | ||||
|  | ||||
|             if (IP == null) | ||||
|                 ipAdd = IPAddress.Any; | ||||
|             else | ||||
|                 ipAdd = IPAddress.Parse(IP); | ||||
|  | ||||
|             if (SSL) | ||||
|                 listener = new SSLSocket(new IPEndPoint(ipAdd, Port), new X509Certificate2(Certificate)); | ||||
|             else | ||||
|                 listener = new TCPSocket(new IPEndPoint(ipAdd, Port)); | ||||
|  | ||||
|             Start(listener); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.Terminate) | ||||
|         { | ||||
|             Stop(); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemReload) | ||||
|         { | ||||
|             await Trigger(ResourceTrigger.Terminate); | ||||
|             await Trigger(ResourceTrigger.Initialize); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemInitialized) | ||||
|         { | ||||
|             filters = await Instance.Children<HTTPFilter>(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public override void Add(HTTPConnection connection) | ||||
|     { | ||||
|         connection.Server = this; | ||||
|         base.Add(connection); | ||||
|     } | ||||
|  | ||||
|     public override void Remove(HTTPConnection connection) | ||||
|     { | ||||
|         connection.Server = null; | ||||
|         base.Remove(connection); | ||||
|     } | ||||
|  | ||||
|     protected override void ClientConnected(HTTPConnection connection) | ||||
|     { | ||||
|         if (filters.Length == 0) | ||||
|         { | ||||
|             connection.Close(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         foreach (var resource in filters) | ||||
|         { | ||||
|             resource.ClientConnected(connection); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* | ||||
|     public int LocalPort | ||||
|     { | ||||
|         get  | ||||
|         { | ||||
|             return cServer.LocalPort; | ||||
|         } | ||||
|     } | ||||
|      */ | ||||
|  | ||||
|     /*  | ||||
|     public HTTPServer(int Port) | ||||
|     { | ||||
|         cServer = new TServer(); | ||||
|         cServer.LocalPort = Port; | ||||
|         cServer.StartServer(); | ||||
|         cServer.ClientConnected += new TServer.eClientConnected(ClientConnected); | ||||
|         cServer.ClientDisConnected += new TServer.eClientDisConnected(ClientDisConnected); | ||||
|         cServer.ClientIsSwitching += new TServer.eClientIsSwitching(ClientIsSwitching); | ||||
|         cServer.DataReceived += new TServer.eDataReceived(DataReceived); | ||||
|  | ||||
|     }*/ | ||||
|  | ||||
|     //~HTTPServer() | ||||
|     //{ | ||||
|     //    cServer.StopServer(); | ||||
|     //} | ||||
| } | ||||
|   | ||||
| @@ -35,96 +35,94 @@ using Esiur.Data; | ||||
| using Esiur.Misc; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Net.HTTP | ||||
| namespace Esiur.Net.HTTP; | ||||
| public class HTTPSession : IDestructible //<T> where T : TClient | ||||
| { | ||||
|     public class HTTPSession : IDestructible //<T> where T : TClient | ||||
|     public delegate void SessionModifiedEvent(HTTPSession session, string key, object oldValue, object newValue); | ||||
|     public delegate void SessionEndedEvent(HTTPSession session); | ||||
|  | ||||
|     private string id; | ||||
|     private Timer timer; | ||||
|     private int timeout; | ||||
|     DateTime creation; | ||||
|     DateTime lastAction; | ||||
|  | ||||
|     private KeyList<string, object> variables; | ||||
|  | ||||
|     public event SessionEndedEvent OnEnd; | ||||
|     public event SessionModifiedEvent OnModify; | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public KeyList<string, object> Variables | ||||
|     { | ||||
|         public delegate void SessionModifiedEvent(HTTPSession session, string key, object oldValue, object newValue); | ||||
|         public delegate void SessionEndedEvent(HTTPSession session); | ||||
|         get { return variables; } | ||||
|     } | ||||
|  | ||||
|         private string id; | ||||
|         private Timer timer; | ||||
|         private int timeout; | ||||
|         DateTime creation; | ||||
|         DateTime lastAction; | ||||
|     public HTTPSession() | ||||
|     { | ||||
|         variables = new KeyList<string, object>(); | ||||
|         variables.OnModified += new KeyList<string, object>.Modified(VariablesModified); | ||||
|         creation = DateTime.Now; | ||||
|     } | ||||
|  | ||||
|         private KeyList<string, object> variables; | ||||
|     internal void Set(string id, int timeout) | ||||
|     { | ||||
|         //modified = sessionModifiedEvent; | ||||
|         //ended = sessionEndEvent; | ||||
|         this.id = id; | ||||
|  | ||||
|         public event SessionEndedEvent OnEnd; | ||||
|         public event SessionModifiedEvent OnModify; | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public KeyList<string, object> Variables | ||||
|         if (this.timeout != 0) | ||||
|         { | ||||
|             get { return variables; } | ||||
|         } | ||||
|  | ||||
|         public HTTPSession() | ||||
|         { | ||||
|             variables = new KeyList<string, object>(); | ||||
|             variables.OnModified += new KeyList<string, object>.Modified(VariablesModified); | ||||
|             this.timeout = timeout; | ||||
|             timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|             creation = DateTime.Now; | ||||
|         } | ||||
|  | ||||
|         internal void Set(string id, int timeout) | ||||
|         { | ||||
|             //modified = sessionModifiedEvent; | ||||
|             //ended = sessionEndEvent; | ||||
|             this.id = id; | ||||
|  | ||||
|             if (this.timeout != 0) | ||||
|             { | ||||
|                 this.timeout = timeout; | ||||
|                 timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|                 creation = DateTime.Now; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private void OnSessionEndTimerCallback(object o) | ||||
|         { | ||||
|             OnEnd?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender) | ||||
|         { | ||||
|             OnModify?.Invoke(this, key, oldValue, newValue); | ||||
|         } | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|             timer.Dispose(); | ||||
|             timer = null; | ||||
|         } | ||||
|  | ||||
|         internal void Refresh() | ||||
|         { | ||||
|             lastAction = DateTime.Now; | ||||
|             timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|         } | ||||
|  | ||||
|         public int Timeout // Seconds | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return timeout; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 timeout = value; | ||||
|                 Refresh(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public string Id | ||||
|         { | ||||
|             get { return id; } | ||||
|         } | ||||
|  | ||||
|         public DateTime LastAction | ||||
|         { | ||||
|             get { return lastAction; } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|     private void OnSessionEndTimerCallback(object o) | ||||
|     { | ||||
|         OnEnd?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender) | ||||
|     { | ||||
|         OnModify?.Invoke(this, key, oldValue, newValue); | ||||
|     } | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|         timer.Dispose(); | ||||
|         timer = null; | ||||
|     } | ||||
|  | ||||
|     internal void Refresh() | ||||
|     { | ||||
|         lastAction = DateTime.Now; | ||||
|         timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|     } | ||||
|  | ||||
|     public int Timeout // Seconds | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return timeout; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             timeout = value; | ||||
|             Refresh(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string Id | ||||
|     { | ||||
|         get { return id; } | ||||
|     } | ||||
|  | ||||
|     public DateTime LastAction | ||||
|     { | ||||
|         get { return lastAction; } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -6,34 +6,32 @@ using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Net.HTTP | ||||
| namespace Esiur.Net.HTTP; | ||||
| public class IIPoHTTP : HTTPFilter | ||||
| { | ||||
|     public class IIPoHTTP : HTTPFilter | ||||
|     [Attribute] | ||||
|     EntryPoint EntryPoint { get; set; } | ||||
|  | ||||
|     public override AsyncReply<bool> Execute(HTTPConnection sender) | ||||
|     { | ||||
|         [Attribute] | ||||
|         EntryPoint EntryPoint { get; set; } | ||||
|         if (sender.Request.URL != "iip") | ||||
|             return new AsyncReply<bool>(false); | ||||
|  | ||||
|         public override AsyncReply<bool> Execute(HTTPConnection sender) | ||||
|         IIPPacket.IIPPacketAction action = (IIPPacket.IIPPacketAction)Convert.ToByte(sender.Request.Query["a"]); | ||||
|  | ||||
|         if (action == IIPPacket.IIPPacketAction.QueryLink) | ||||
|         { | ||||
|             if (sender.Request.URL != "iip") | ||||
|                 return new AsyncReply<bool>(false); | ||||
|  | ||||
|             IIPPacket.IIPPacketAction action = (IIPPacket.IIPPacketAction)Convert.ToByte(sender.Request.Query["a"]); | ||||
|  | ||||
|             if (action == IIPPacket.IIPPacketAction.QueryLink) | ||||
|             EntryPoint.Query(sender.Request.Query["l"], null).Then(x => | ||||
|             { | ||||
|                 EntryPoint.Query(sender.Request.Query["l"], null).Then(x => | ||||
|                 { | ||||
|  | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             return new AsyncReply<bool>(true); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public override AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             return new AsyncReply<bool>(true); | ||||
|         } | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
|  | ||||
|     public override AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,94 +32,91 @@ using Esiur.Net.IIP; | ||||
| using Esiur.Net.Sockets; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Net.HTTP | ||||
| namespace Esiur.Net.HTTP; | ||||
| public class IIPoWS : HTTPFilter | ||||
| { | ||||
|     public class IIPoWS: HTTPFilter | ||||
|     [Attribute] | ||||
|     public DistributedServer Server | ||||
|     { | ||||
|         [Attribute] | ||||
|         public DistributedServer Server | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public override AsyncReply<bool> Execute(HTTPConnection sender) | ||||
|     { | ||||
|  | ||||
|         if (sender.IsWebsocketRequest()) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|             if (Server == null) | ||||
|                 return new AsyncReply<bool>(false); | ||||
|  | ||||
|         public override AsyncReply<bool> Execute(HTTPConnection sender) | ||||
|         { | ||||
|             var tcpSocket = sender.Unassign(); | ||||
|  | ||||
|             if (sender.IsWebsocketRequest()) | ||||
|             { | ||||
|                 if (Server == null) | ||||
|                     return new AsyncReply<bool>(false); | ||||
|             if (tcpSocket == null) | ||||
|                 return new AsyncReply<bool>(false); | ||||
|  | ||||
|                 var tcpSocket = sender.Unassign(); | ||||
|             var httpServer = sender.Parent; | ||||
|             var wsSocket = new WSocket(tcpSocket); | ||||
|             httpServer.Remove(sender); | ||||
|  | ||||
|                 if (tcpSocket == null) | ||||
|                     return new AsyncReply<bool>(false); | ||||
|             var iipConnection = new DistributedConnection(); | ||||
|  | ||||
|                 var httpServer = sender.Parent; | ||||
|                 var wsSocket = new WSocket(tcpSocket); | ||||
|                 httpServer.Remove(sender); | ||||
|             Server.Add(iipConnection); | ||||
|             iipConnection.Assign(wsSocket); | ||||
|             wsSocket.Begin(); | ||||
|  | ||||
|                 var iipConnection = new DistributedConnection(); | ||||
|  | ||||
|                 Server.Add(iipConnection); | ||||
|                 iipConnection.Assign(wsSocket); | ||||
|                 wsSocket.Begin(); | ||||
|  | ||||
|                 return new AsyncReply<bool>(true); | ||||
|             } | ||||
|  | ||||
|             return new AsyncReply<bool>( false); | ||||
|  | ||||
|             /* | ||||
|             if (sender.Request.Filename.StartsWith("/iip/")) | ||||
|             { | ||||
|                 // find the service | ||||
|                 var path = sender.Request.Filename.Substring(5);// sender.Request.Query["path"]; | ||||
|  | ||||
|  | ||||
|                 Warehouse.Get(path).Then((r) => | ||||
|                 { | ||||
|                     if (r is DistributedServer) | ||||
|                     { | ||||
|                         var httpServer = sender.Parent; | ||||
|                         var iipServer = r as DistributedServer; | ||||
|                         var tcpSocket = sender.Unassign(); | ||||
|                         if (tcpSocket == null) | ||||
|                             return; | ||||
|  | ||||
|                         var wsSocket = new WSSocket(tcpSocket); | ||||
|                         httpServer.RemoveConnection(sender); | ||||
|  | ||||
|                         //httpServer.Connections.Remove(sender); | ||||
|                         var iipConnection = new DistributedConnection(); | ||||
|   //                      iipConnection.OnReady += IipConnection_OnReady; | ||||
| //                        iipConnection.Server = iipServer; | ||||
|     //                    iipConnection.Assign(wsSocket); | ||||
|  | ||||
|                         iipServer.AddConnection(iipConnection); | ||||
|                         iipConnection.Assign(wsSocket); | ||||
|                         wsSocket.Begin(); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|             */ | ||||
|         } | ||||
|  | ||||
|         private void IipConnection_OnReady(DistributedConnection sender) | ||||
|         { | ||||
|             Warehouse.Put(sender.RemoteUsername, sender, null, sender.Server).Wait(); | ||||
|         } | ||||
|  | ||||
|         public override AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             return new AsyncReply<bool>(true); | ||||
|         } | ||||
|  | ||||
|         return new AsyncReply<bool>(false); | ||||
|  | ||||
|         /* | ||||
|         if (sender.Request.Filename.StartsWith("/iip/")) | ||||
|         { | ||||
|             // find the service | ||||
|             var path = sender.Request.Filename.Substring(5);// sender.Request.Query["path"]; | ||||
|  | ||||
|  | ||||
|             Warehouse.Get(path).Then((r) => | ||||
|             { | ||||
|                 if (r is DistributedServer) | ||||
|                 { | ||||
|                     var httpServer = sender.Parent; | ||||
|                     var iipServer = r as DistributedServer; | ||||
|                     var tcpSocket = sender.Unassign(); | ||||
|                     if (tcpSocket == null) | ||||
|                         return; | ||||
|  | ||||
|                     var wsSocket = new WSSocket(tcpSocket); | ||||
|                     httpServer.RemoveConnection(sender); | ||||
|  | ||||
|                     //httpServer.Connections.Remove(sender); | ||||
|                     var iipConnection = new DistributedConnection(); | ||||
| //                      iipConnection.OnReady += IipConnection_OnReady; | ||||
| //                        iipConnection.Server = iipServer; | ||||
| //                    iipConnection.Assign(wsSocket); | ||||
|  | ||||
|                     iipServer.AddConnection(iipConnection); | ||||
|                     iipConnection.Assign(wsSocket); | ||||
|                     wsSocket.Begin(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         */ | ||||
|     } | ||||
|  | ||||
|     private void IipConnection_OnReady(DistributedConnection sender) | ||||
|     { | ||||
|         Warehouse.Put(sender.RemoteUsername, sender, null, sender.Server).Wait(); | ||||
|     } | ||||
|  | ||||
|     public override AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
|      | ||||
| } | ||||
|   | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,26 +2,24 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| namespace Esiur.Net.IIP; | ||||
| public class DistributedPropertyContext | ||||
| { | ||||
|     public class DistributedPropertyContext | ||||
|     public object Value { get; private set; } | ||||
|     public DistributedConnection Connection { get; private set; } | ||||
|     public Func<DistributedConnection, object> Method { get; private set; } | ||||
|  | ||||
|     public DistributedPropertyContext(DistributedConnection connection, object value) | ||||
|     { | ||||
|         public object Value { get; private set; } | ||||
|         public DistributedConnection Connection { get; private set; } | ||||
|         public Func<DistributedConnection, object> Method { get; private set; } | ||||
|  | ||||
|         public DistributedPropertyContext(DistributedConnection connection, object value) | ||||
|         { | ||||
|             this.Value = value; | ||||
|             this.Connection = connection; | ||||
|         } | ||||
|  | ||||
|         public DistributedPropertyContext(Func<DistributedConnection, object> method) | ||||
|         { | ||||
|             this.Method = method; | ||||
|         } | ||||
|  | ||||
|         public static implicit operator DistributedPropertyContext(Func<DistributedConnection, object> method) | ||||
|                                         => new DistributedPropertyContext(method); | ||||
|         this.Value = value; | ||||
|         this.Connection = connection; | ||||
|     } | ||||
|  | ||||
|     public DistributedPropertyContext(Func<DistributedConnection, object> method) | ||||
|     { | ||||
|         this.Method = method; | ||||
|     } | ||||
|  | ||||
|     public static implicit operator DistributedPropertyContext(Func<DistributedConnection, object> method) | ||||
|                                     => new DistributedPropertyContext(method); | ||||
| } | ||||
|   | ||||
| @@ -42,465 +42,463 @@ using System.Threading.Tasks; | ||||
| using Esiur.Resource; | ||||
| using Esiur.Resource.Template; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| namespace Esiur.Net.IIP; | ||||
|  | ||||
| //[System.Runtime.InteropServices.ComVisible(true)] | ||||
| public class DistributedResource : DynamicObject, IResource | ||||
| { | ||||
|  | ||||
|     //[System.Runtime.InteropServices.ComVisible(true)] | ||||
|     public class DistributedResource : DynamicObject, IResource | ||||
|     /// <summary> | ||||
|     /// Raised when the distributed resource is destroyed. | ||||
|     /// </summary> | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|     public event Instance.ResourceModifiedEvent OnModified; | ||||
|     uint instanceId; | ||||
|     DistributedConnection connection; | ||||
|  | ||||
|  | ||||
|     bool attached = false; | ||||
|     bool destroyed = false; | ||||
|     bool suspended = false; | ||||
|  | ||||
|     //Structure properties = new Structure(); | ||||
|  | ||||
|     string link; | ||||
|     //ulong age; | ||||
|     //ulong[] ages; | ||||
|     protected object[] properties; | ||||
|     internal List<DistributedResource> parents = new List<DistributedResource>(); | ||||
|     internal List<DistributedResource> children = new List<DistributedResource>(); | ||||
|  | ||||
|     DistributedResourceEvent[] events; | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Resource template for the remotely located resource. | ||||
|     /// </summary> | ||||
|     //public ResourceTemplate Template | ||||
|     //{ | ||||
|     //    get { return template; } | ||||
|     //} | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Connection responsible for the distributed resource. | ||||
|     /// </summary> | ||||
|     public DistributedConnection Connection | ||||
|     { | ||||
|         get { return connection; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Resource link | ||||
|     /// </summary> | ||||
|     public string Link | ||||
|     { | ||||
|         get { return link; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Instance Id given by the other end. | ||||
|     /// </summary> | ||||
|     public uint Id | ||||
|     { | ||||
|         get { return instanceId; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// IDestructible interface. | ||||
|     /// </summary> | ||||
|     public void Destroy() | ||||
|     { | ||||
|         destroyed = true; | ||||
|         attached = false; | ||||
|         connection.SendDetachRequest(instanceId); | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Suspend resource | ||||
|     /// </summary> | ||||
|  | ||||
|     internal void Suspend() | ||||
|     { | ||||
|         suspended = true; | ||||
|         attached = false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Resource is attached when all its properties are received. | ||||
|     /// </summary> | ||||
|     internal bool Attached => attached; | ||||
|  | ||||
|     internal bool Suspended => suspended; | ||||
|  | ||||
|  | ||||
|     // public DistributedResourceStack Stack | ||||
|     //{ | ||||
|     //     get { return stack; } | ||||
|     //} | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Create a new distributed resource. | ||||
|     /// </summary> | ||||
|     /// <param name="connection">Connection responsible for the distributed resource.</param> | ||||
|     /// <param name="template">Resource template.</param> | ||||
|     /// <param name="instanceId">Instance Id given by the other end.</param> | ||||
|     /// <param name="age">Resource age.</param> | ||||
|     public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link) | ||||
|     { | ||||
|         this.link = link; | ||||
|         this.connection = connection; | ||||
|         this.instanceId = instanceId; | ||||
|  | ||||
|         //this.Instance.Template = template; | ||||
|         //this.Instance.Age = age; | ||||
|         //this.template = template; | ||||
|         //this.age = age; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Export all properties with ResourceProperty attributed as bytes array. | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     internal PropertyValue[] _Serialize() | ||||
|     { | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Raised when the distributed resource is destroyed. | ||||
|         /// </summary> | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|         public event Instance.ResourceModifiedEvent OnModified; | ||||
|         uint instanceId; | ||||
|         DistributedConnection connection; | ||||
|         var props = new PropertyValue[properties.Length]; | ||||
|  | ||||
|  | ||||
|         bool attached = false; | ||||
|         bool destroyed = false; | ||||
|         bool suspended = false; | ||||
|         for (byte i = 0; i < properties.Length; i++) | ||||
|             props[i] = new PropertyValue(properties[i], Instance.GetAge(i), Instance.GetModificationDate(i)); | ||||
|  | ||||
|         //Structure properties = new Structure(); | ||||
|         return props; | ||||
|     } | ||||
|  | ||||
|         string link; | ||||
|         //ulong age; | ||||
|         //ulong[] ages; | ||||
|         protected object[] properties; | ||||
|         internal List<DistributedResource> parents = new List<DistributedResource>(); | ||||
|         internal List<DistributedResource> children = new List<DistributedResource>(); | ||||
|  | ||||
|         DistributedResourceEvent[] events; | ||||
|  | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Resource template for the remotely located resource. | ||||
|         /// </summary> | ||||
|         //public ResourceTemplate Template | ||||
|         //{ | ||||
|         //    get { return template; } | ||||
|         //} | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Connection responsible for the distributed resource. | ||||
|         /// </summary> | ||||
|         public DistributedConnection Connection | ||||
|     internal bool _Attach(PropertyValue[] properties) | ||||
|     { | ||||
|         if (attached) | ||||
|             return false; | ||||
|         else | ||||
|         { | ||||
|             get { return connection; } | ||||
|         } | ||||
|             suspended = false; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Resource link | ||||
|         /// </summary> | ||||
|         public string Link | ||||
|         { | ||||
|             get { return link; } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Instance Id given by the other end. | ||||
|         /// </summary> | ||||
|         public uint Id | ||||
|         { | ||||
|             get { return instanceId; } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// IDestructible interface. | ||||
|         /// </summary> | ||||
|         public void Destroy() | ||||
|         { | ||||
|             destroyed = true; | ||||
|             attached = false; | ||||
|             connection.SendDetachRequest(instanceId); | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Suspend resource | ||||
|         /// </summary> | ||||
|  | ||||
|         internal void Suspend() | ||||
|         { | ||||
|             suspended = true; | ||||
|             attached = false; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Resource is attached when all its properties are received. | ||||
|         /// </summary> | ||||
|         internal bool Attached => attached; | ||||
|  | ||||
|         internal bool Suspended => suspended; | ||||
|  | ||||
|  | ||||
|         // public DistributedResourceStack Stack | ||||
|         //{ | ||||
|         //     get { return stack; } | ||||
|         //} | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create a new distributed resource. | ||||
|         /// </summary> | ||||
|         /// <param name="connection">Connection responsible for the distributed resource.</param> | ||||
|         /// <param name="template">Resource template.</param> | ||||
|         /// <param name="instanceId">Instance Id given by the other end.</param> | ||||
|         /// <param name="age">Resource age.</param> | ||||
|         public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link) | ||||
|         { | ||||
|             this.link = link; | ||||
|             this.connection = connection; | ||||
|             this.instanceId = instanceId; | ||||
|  | ||||
|             //this.Instance.Template = template; | ||||
|             //this.Instance.Age = age; | ||||
|             //this.template = template; | ||||
|             //this.age = age; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Export all properties with ResourceProperty attributed as bytes array. | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         internal PropertyValue[] _Serialize() | ||||
|         { | ||||
|  | ||||
|             var props = new PropertyValue[properties.Length]; | ||||
|             this.properties = new object[properties.Length]; | ||||
|  | ||||
|             this.events = new DistributedResourceEvent[Instance.Template.Events.Length]; | ||||
|  | ||||
|             for (byte i = 0; i < properties.Length; i++) | ||||
|                 props[i] = new PropertyValue(properties[i], Instance.GetAge(i), Instance.GetModificationDate(i)); | ||||
|  | ||||
|             return props; | ||||
|         } | ||||
|  | ||||
|         internal bool _Attach(PropertyValue[] properties) | ||||
|         { | ||||
|             if (attached) | ||||
|                 return false; | ||||
|             else | ||||
|             { | ||||
|                 suspended = false; | ||||
|  | ||||
|                 this.properties = new object[properties.Length]; | ||||
|  | ||||
|                 this.events = new DistributedResourceEvent[Instance.Template.Events.Length]; | ||||
|  | ||||
|                 for (byte i = 0; i < properties.Length; i++) | ||||
|                 { | ||||
|                     Instance.SetAge(i, properties[i].Age); | ||||
|                     Instance.SetModificationDate(i, properties[i].Date); | ||||
|                     this.properties[i] = properties[i].Value; | ||||
|                 } | ||||
|  | ||||
|                 // trigger holded events/property updates. | ||||
|                 //foreach (var r in afterAttachmentTriggers) | ||||
|                 //    r.Key.Trigger(r.Value); | ||||
|  | ||||
|                 //afterAttachmentTriggers.Clear(); | ||||
|  | ||||
|                 attached = true; | ||||
|  | ||||
|                 Instance.SetAge(i, properties[i].Age); | ||||
|                 Instance.SetModificationDate(i, properties[i].Date); | ||||
|                 this.properties[i] = properties[i].Value; | ||||
|             } | ||||
|             return true; | ||||
|  | ||||
|             // trigger holded events/property updates. | ||||
|             //foreach (var r in afterAttachmentTriggers) | ||||
|             //    r.Key.Trigger(r.Value); | ||||
|  | ||||
|             //afterAttachmentTriggers.Clear(); | ||||
|  | ||||
|             attached = true; | ||||
|  | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
|         protected internal virtual void _EmitEventByIndex(byte index, object args) | ||||
|     protected internal virtual void _EmitEventByIndex(byte index, object args) | ||||
|     { | ||||
|         var et = Instance.Template.GetEventTemplateByIndex(index); | ||||
|         events[index]?.Invoke(this, args); | ||||
|         Instance.EmitResourceEvent(et.Name, args); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public AsyncReply<object> _InvokeByNamedArguments(byte index, Structure namedArgs) | ||||
|     { | ||||
|         if (destroyed) | ||||
|             throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|         if (suspended) | ||||
|             throw new Exception("Trying to access suspended object"); | ||||
|  | ||||
|         if (index >= Instance.Template.Functions.Length) | ||||
|             throw new Exception("Function index is incorrect"); | ||||
|  | ||||
|  | ||||
|         return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply<object> _InvokeByArrayArguments(byte index, object[] args) | ||||
|     { | ||||
|         if (destroyed) | ||||
|             throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|         if (suspended) | ||||
|             throw new Exception("Trying to access suspended object"); | ||||
|  | ||||
|         if (index >= Instance.Template.Functions.Length) | ||||
|             throw new Exception("Function index is incorrect"); | ||||
|  | ||||
|  | ||||
|         return connection.SendInvokeByArrayArguments(instanceId, index, args); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply Listen(EventTemplate et) | ||||
|     { | ||||
|         if (et == null) | ||||
|             return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); | ||||
|  | ||||
|         if (!et.Listenable) | ||||
|             return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, "")); | ||||
|  | ||||
|         return connection.SendListenRequest(instanceId, et.Index); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply Listen(string eventName) | ||||
|     { | ||||
|         var et = Instance.Template.GetEventTemplateByName(eventName); | ||||
|  | ||||
|         return Listen(et); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply Unlisten(EventTemplate et) | ||||
|     { | ||||
|         if (et == null) | ||||
|             return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); | ||||
|  | ||||
|         if (!et.Listenable) | ||||
|             return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, "")); | ||||
|  | ||||
|         return connection.SendUnlistenRequest(instanceId, et.Index); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply Unlisten(string eventName) | ||||
|     { | ||||
|         var et = Instance.Template.GetEventTemplateByName(eventName); | ||||
|  | ||||
|         return Unlisten(et); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) | ||||
|     { | ||||
|         var ft = Instance.Template.GetFunctionTemplateByName(binder.Name); | ||||
|  | ||||
|         var reply = new AsyncReply<object>(); | ||||
|  | ||||
|         if (attached && ft != null) | ||||
|         { | ||||
|             var et = Instance.Template.GetEventTemplateByIndex(index); | ||||
|             events[index]?.Invoke(this, args); | ||||
|             Instance.EmitResourceEvent(et.Name, args); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public AsyncReply<object> _InvokeByNamedArguments(byte index, Structure namedArgs) | ||||
|         { | ||||
|             if (destroyed) | ||||
|                 throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|             if (suspended) | ||||
|                 throw new Exception("Trying to access suspended object"); | ||||
|  | ||||
|             if (index >= Instance.Template.Functions.Length) | ||||
|                 throw new Exception("Function index is incorrect"); | ||||
|  | ||||
|  | ||||
|             return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply<object> _InvokeByArrayArguments(byte index, object[] args) | ||||
|         { | ||||
|             if (destroyed) | ||||
|                 throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|             if (suspended) | ||||
|                 throw new Exception("Trying to access suspended object"); | ||||
|  | ||||
|             if (index >= Instance.Template.Functions.Length) | ||||
|                 throw new Exception("Function index is incorrect"); | ||||
|  | ||||
|  | ||||
|             return connection.SendInvokeByArrayArguments(instanceId, index, args); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply Listen(EventTemplate et) | ||||
|         { | ||||
|             if (et == null) | ||||
|                 return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); | ||||
|  | ||||
|             if (!et.Listenable) | ||||
|                 return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, "")); | ||||
|  | ||||
|             return connection.SendListenRequest(instanceId, et.Index); | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Listen(string eventName) | ||||
|         { | ||||
|             var et = Instance.Template.GetEventTemplateByName(eventName); | ||||
|  | ||||
|             return Listen(et); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply Unlisten(EventTemplate et) | ||||
|         { | ||||
|             if (et == null) | ||||
|                 return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); | ||||
|  | ||||
|             if (!et.Listenable) | ||||
|                 return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, "")); | ||||
|  | ||||
|             return connection.SendUnlistenRequest(instanceId, et.Index); | ||||
|         } | ||||
|  | ||||
|         public AsyncReply Unlisten(string eventName) | ||||
|         { | ||||
|             var et = Instance.Template.GetEventTemplateByName(eventName); | ||||
|  | ||||
|             return Unlisten(et); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) | ||||
|         { | ||||
|             var ft = Instance.Template.GetFunctionTemplateByName(binder.Name); | ||||
|  | ||||
|             var reply = new AsyncReply<object>(); | ||||
|  | ||||
|             if (attached && ft != null) | ||||
|             if (args.Length == 1) | ||||
|             { | ||||
|                 if (args.Length == 1) | ||||
|                 // Detect anonymous types | ||||
|                 var type = args[0].GetType(); | ||||
|                 if (Codec.IsAnonymous(type)) | ||||
|                 { | ||||
|                     // Detect anonymous types | ||||
|                     var type = args[0].GetType(); | ||||
|                     if (Codec.IsAnonymous(type)) | ||||
|                     { | ||||
|                         var namedArgs = new Structure(); | ||||
|  | ||||
|                         var pi = type.GetTypeInfo().GetProperties(); | ||||
|                         foreach (var p in pi) | ||||
|                             namedArgs[p.Name] = p.GetValue(args[0]); | ||||
|                         result = _InvokeByNamedArguments(ft.Index, namedArgs); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         result = _InvokeByArrayArguments(ft.Index, args); | ||||
|                     } | ||||
|                     var namedArgs = new Structure(); | ||||
|  | ||||
|                     var pi = type.GetTypeInfo().GetProperties(); | ||||
|                     foreach (var p in pi) | ||||
|                         namedArgs[p.Name] = p.GetValue(args[0]); | ||||
|                     result = _InvokeByNamedArguments(ft.Index, namedArgs); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     result = _InvokeByArrayArguments(ft.Index, args); | ||||
|                 } | ||||
|                 return true; | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 result = null; | ||||
|                 return false; | ||||
|                 result = _InvokeByArrayArguments(ft.Index, args); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Get a property value. | ||||
|         /// </summary> | ||||
|         /// <param name="index">Zero-based property index.</param> | ||||
|         /// <returns>Value</returns> | ||||
|         protected internal object _Get(byte index) | ||||
|         else | ||||
|         { | ||||
|             if (index >= properties.Length) | ||||
|                 return null; | ||||
|             return properties[index]; | ||||
|         } | ||||
|  | ||||
|         public override bool TryGetMember(GetMemberBinder binder, out object result) | ||||
|         { | ||||
|             if (destroyed) | ||||
|                 throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|  | ||||
|             result = null; | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|             if (!attached) | ||||
|     /// <summary> | ||||
|     /// Get a property value. | ||||
|     /// </summary> | ||||
|     /// <param name="index">Zero-based property index.</param> | ||||
|     /// <returns>Value</returns> | ||||
|     protected internal object _Get(byte index) | ||||
|     { | ||||
|         if (index >= properties.Length) | ||||
|             return null; | ||||
|         return properties[index]; | ||||
|     } | ||||
|  | ||||
|     public override bool TryGetMember(GetMemberBinder binder, out object result) | ||||
|     { | ||||
|         if (destroyed) | ||||
|             throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|  | ||||
|         result = null; | ||||
|  | ||||
|         if (!attached) | ||||
|             return false; | ||||
|  | ||||
|         var pt = Instance.Template.GetPropertyTemplateByName(binder.Name); | ||||
|  | ||||
|         if (pt != null) | ||||
|         { | ||||
|             result = properties[pt.Index]; | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             var et = Instance.Template.GetEventTemplateByName(binder.Name); | ||||
|             if (et == null) | ||||
|                 return false; | ||||
|  | ||||
|             var pt = Instance.Template.GetPropertyTemplateByName(binder.Name); | ||||
|             result = events[et.Index]; | ||||
|  | ||||
|             if (pt != null) | ||||
|             { | ||||
|                 result = properties[pt.Index]; | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var et = Instance.Template.GetEventTemplateByName(binder.Name); | ||||
|                 if (et == null) | ||||
|                     return false; | ||||
|  | ||||
|                 result = events[et.Index]; | ||||
|  | ||||
|                 return true; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|         internal void _UpdatePropertyByIndex(byte index, object value) | ||||
|         { | ||||
|             var pt = Instance.Template.GetPropertyTemplateByIndex(index); | ||||
|             properties[index] = value; | ||||
|             Instance.EmitModification(pt, value); | ||||
|         } | ||||
|     internal void _UpdatePropertyByIndex(byte index, object value) | ||||
|     { | ||||
|         var pt = Instance.Template.GetPropertyTemplateByIndex(index); | ||||
|         properties[index] = value; | ||||
|         Instance.EmitModification(pt, value); | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Set property value. | ||||
|         /// </summary> | ||||
|         /// <param name="index">Zero-based property index.</param> | ||||
|         /// <param name="value">Value</param> | ||||
|         /// <returns>Indicator when the property is set.</returns> | ||||
|         protected internal AsyncReply<object> _Set(byte index, object value) | ||||
|         { | ||||
|             if (index >= properties.Length) | ||||
|                 return null; | ||||
|     /// <summary> | ||||
|     /// Set property value. | ||||
|     /// </summary> | ||||
|     /// <param name="index">Zero-based property index.</param> | ||||
|     /// <param name="value">Value</param> | ||||
|     /// <returns>Indicator when the property is set.</returns> | ||||
|     protected internal AsyncReply<object> _Set(byte index, object value) | ||||
|     { | ||||
|         if (index >= properties.Length) | ||||
|             return null; | ||||
|  | ||||
|             var reply = new AsyncReply<object>(); | ||||
|         var reply = new AsyncReply<object>(); | ||||
|  | ||||
|             var parameters = Codec.Compose(value, connection); | ||||
|             connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty) | ||||
|                         .AddUInt32(instanceId) | ||||
|                         .AddUInt8(index) | ||||
|                         .AddUInt8Array(parameters) | ||||
|                         .Done() | ||||
|                         .Then((res) => | ||||
|                         { | ||||
|         var parameters = Codec.Compose(value, connection); | ||||
|         connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty) | ||||
|                     .AddUInt32(instanceId) | ||||
|                     .AddUInt8(index) | ||||
|                     .AddUInt8Array(parameters) | ||||
|                     .Done() | ||||
|                     .Then((res) => | ||||
|                     { | ||||
|                             // not really needed, server will always send property modified,  | ||||
|                             // this only happens if the programmer forgot to emit in property setter | ||||
|                             properties[index] = value; | ||||
|                             reply.Trigger(null); | ||||
|                         }); | ||||
|                         reply.Trigger(null); | ||||
|                     }); | ||||
|  | ||||
|             return reply; | ||||
|         } | ||||
|         return reply; | ||||
|     } | ||||
|  | ||||
|         public override bool TrySetMember(SetMemberBinder binder, object value) | ||||
|     public override bool TrySetMember(SetMemberBinder binder, object value) | ||||
|     { | ||||
|         if (destroyed) | ||||
|             throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|         if (suspended) | ||||
|             throw new Exception("Trying to access suspended object"); | ||||
|  | ||||
|         if (!attached) | ||||
|             return false; | ||||
|  | ||||
|         var pt = Instance.Template.GetPropertyTemplateByName(binder.Name); | ||||
|  | ||||
|         if (pt != null) | ||||
|         { | ||||
|             if (destroyed) | ||||
|                 throw new Exception("Trying to access destroyed object"); | ||||
|  | ||||
|             if (suspended) | ||||
|                 throw new Exception("Trying to access suspended object"); | ||||
|  | ||||
|             if (!attached) | ||||
|             _Set(pt.Index, value); | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             var et = Instance.Template.GetEventTemplateByName(binder.Name); | ||||
|             if (et == null) | ||||
|                 return false; | ||||
|  | ||||
|             var pt = Instance.Template.GetPropertyTemplateByName(binder.Name); | ||||
|  | ||||
|             if (pt != null) | ||||
|             { | ||||
|                 _Set(pt.Index, value); | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var et = Instance.Template.GetEventTemplateByName(binder.Name); | ||||
|                 if (et == null) | ||||
|                     return false; | ||||
|  | ||||
|                 events[et.Index] = (DistributedResourceEvent)value; | ||||
|  | ||||
|                 return true; | ||||
|             } | ||||
|             events[et.Index] = (DistributedResourceEvent)value; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|               public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender) | ||||
|               { | ||||
|                   // get function parameters | ||||
|                   Type t = this.GetType(); | ||||
|     } | ||||
|  | ||||
|                   MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly | | ||||
|                                                           BindingFlags.Public | | ||||
|                                                           BindingFlags.Instance | BindingFlags.InvokeMethod); | ||||
|                   if (mi != null) | ||||
|     /* | ||||
|           public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender) | ||||
|           { | ||||
|               // get function parameters | ||||
|               Type t = this.GetType(); | ||||
|  | ||||
|               MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly | | ||||
|                                                       BindingFlags.Public | | ||||
|                                                       BindingFlags.Instance | BindingFlags.InvokeMethod); | ||||
|               if (mi != null) | ||||
|               { | ||||
|                   try | ||||
|                   { | ||||
|                       try | ||||
|                       { | ||||
|                           var res = await invokeMethod(mi, arguments, sender); | ||||
|                           object rt = Codec.Compose(res); | ||||
|                           sender.SendParams((byte)0x80, instanceId, index, rt); | ||||
|                       } | ||||
|                       catch(Exception ex) | ||||
|                       { | ||||
|                           var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message; | ||||
|                           sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg)); | ||||
|                       } | ||||
|                       var res = await invokeMethod(mi, arguments, sender); | ||||
|                       object rt = Codec.Compose(res); | ||||
|                       sender.SendParams((byte)0x80, instanceId, index, rt); | ||||
|                   } | ||||
|                   catch(Exception ex) | ||||
|                   { | ||||
|                       var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message; | ||||
|                       sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg)); | ||||
|                   } | ||||
|               } | ||||
|               */ | ||||
|           } | ||||
|           */ | ||||
|  | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Resource interface. | ||||
|         /// </summary> | ||||
|         public Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|     /// <summary> | ||||
|     /// Resource interface. | ||||
|     /// </summary> | ||||
|     public Instance Instance | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Create a new instance of distributed resource. | ||||
|         /// </summary> | ||||
|         public DistributedResource() | ||||
|         { | ||||
|             //stack = new DistributedResourceStack(this); | ||||
|             //this.Instance.ResourceModified += this.OnModified; | ||||
|     /// <summary> | ||||
|     /// Create a new instance of distributed resource. | ||||
|     /// </summary> | ||||
|     public DistributedResource() | ||||
|     { | ||||
|         //stack = new DistributedResourceStack(this); | ||||
|         //this.Instance.ResourceModified += this.OnModified; | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Resource interface. | ||||
|         /// </summary> | ||||
|         /// <param name="trigger"></param> | ||||
|         /// <returns></returns> | ||||
|         public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|     /// <summary> | ||||
|     /// Resource interface. | ||||
|     /// </summary> | ||||
|     /// <param name="trigger"></param> | ||||
|     /// <returns></returns> | ||||
|     public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|  | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|                 this.Instance.ResourceModified += this.OnModified; | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|             this.Instance.ResourceModified += this.OnModified; | ||||
|  | ||||
|             // do nothing. | ||||
|             return new AsyncReply<bool>(true); | ||||
|         } | ||||
|         // do nothing. | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
| } | ||||
| @@ -28,7 +28,6 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| { | ||||
|     public delegate void DistributedResourceEvent(DistributedResource sender, object argument); | ||||
| } | ||||
| namespace Esiur.Net.IIP; | ||||
|  | ||||
| public delegate void DistributedResourceEvent(DistributedResource sender, object argument); | ||||
|   | ||||
| @@ -28,46 +28,44 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| namespace Esiur.Net.IIP; | ||||
| public class DistributedResourceQueueItem | ||||
| { | ||||
|     public class DistributedResourceQueueItem | ||||
|     public enum DistributedResourceQueueItemType | ||||
|     { | ||||
|         public enum DistributedResourceQueueItemType | ||||
|         { | ||||
|             Propery, | ||||
|             Event | ||||
|         } | ||||
|         Propery, | ||||
|         Event | ||||
|     } | ||||
|  | ||||
|         DistributedResourceQueueItemType type; | ||||
|         byte index; | ||||
|         object value; | ||||
|         DistributedResource resource; | ||||
|     DistributedResourceQueueItemType type; | ||||
|     byte index; | ||||
|     object value; | ||||
|     DistributedResource resource; | ||||
|  | ||||
|         public DistributedResourceQueueItem(DistributedResource resource, DistributedResourceQueueItemType type, object value, byte index) | ||||
|         { | ||||
|             this.resource = resource; | ||||
|             this.index = index; | ||||
|             this.type = type; | ||||
|             this.value = value; | ||||
|         } | ||||
|     public DistributedResourceQueueItem(DistributedResource resource, DistributedResourceQueueItemType type, object value, byte index) | ||||
|     { | ||||
|         this.resource = resource; | ||||
|         this.index = index; | ||||
|         this.type = type; | ||||
|         this.value = value; | ||||
|     } | ||||
|  | ||||
|         public DistributedResource Resource | ||||
|         { | ||||
|             get { return resource; } | ||||
|         } | ||||
|         public DistributedResourceQueueItemType Type | ||||
|         { | ||||
|             get { return type; } | ||||
|         } | ||||
|     public DistributedResource Resource | ||||
|     { | ||||
|         get { return resource; } | ||||
|     } | ||||
|     public DistributedResourceQueueItemType Type | ||||
|     { | ||||
|         get { return type; } | ||||
|     } | ||||
|  | ||||
|         public byte Index | ||||
|         { | ||||
|             get { return index; } | ||||
|         } | ||||
|     public byte Index | ||||
|     { | ||||
|         get { return index; } | ||||
|     } | ||||
|  | ||||
|         public object Value | ||||
|         { | ||||
|             get { return value; } | ||||
|         } | ||||
|     public object Value | ||||
|     { | ||||
|         get { return value; } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,143 +35,141 @@ using System.Net; | ||||
| using Esiur.Resource; | ||||
| using Esiur.Security.Membership; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| namespace Esiur.Net.IIP; | ||||
| public class DistributedServer : NetworkServer<DistributedConnection>, IResource | ||||
| { | ||||
|     public class DistributedServer : NetworkServer<DistributedConnection>, IResource | ||||
|     [Attribute] | ||||
|     public string IP | ||||
|     { | ||||
|         [Attribute] | ||||
|         public string IP | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         public IMembership Membership | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         public EntryPoint EntryPoint | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         public ushort Port | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } = 10518; | ||||
|  | ||||
|  | ||||
|         [Attribute] | ||||
|         public ExceptionLevel ExceptionLevel { get; set; } | ||||
|             = ExceptionLevel.Code | ||||
|             | ExceptionLevel.Source | ||||
|             | ExceptionLevel.Message | ||||
|             | ExceptionLevel.Trace; | ||||
|  | ||||
|  | ||||
|         public Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|             { | ||||
|                 TCPSocket listener; | ||||
|  | ||||
|                 if (IP != null) | ||||
|                     listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port)); | ||||
|                 else | ||||
|                     listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port)); | ||||
|  | ||||
|                 Start(listener); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.Terminate) | ||||
|             { | ||||
|                 Stop(); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemReload) | ||||
|             { | ||||
|                 Trigger(ResourceTrigger.Terminate); | ||||
|                 Trigger(ResourceTrigger.Initialize); | ||||
|             } | ||||
|  | ||||
|             return new AsyncReply<bool>(true); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         //protected override void DataReceived(DistributedConnection sender, NetworkBuffer data) | ||||
|         //{ | ||||
|         //    //throw new NotImplementedException(); | ||||
|   | ||||
|         //} | ||||
|  | ||||
|   | ||||
|  | ||||
|         //protected override void ClientConnected(DistributedConnection sender) | ||||
|         //{ | ||||
|         //    //Console.WriteLine("DistributedConnection Client Connected"); | ||||
|         //} | ||||
|  | ||||
|         //private void ConnectionReadyEventReceiver(DistributedConnection sender) | ||||
|         //{ | ||||
|         //    sender.OnReady -= ConnectionReadyEventReceiver; | ||||
|         //    Warehouse.Put(sender, sender.LocalUsername, null, this); | ||||
|         //} | ||||
|  | ||||
|          | ||||
|         //public override void RemoveConnection(DistributedConnection connection) | ||||
|         //{ | ||||
|         //    connection.OnReady -= Sender_OnReady; | ||||
|         //    //connection.Server = null; | ||||
|         //    base.RemoveConnection(connection); | ||||
|         //} | ||||
|  | ||||
|         //public override void AddConnection(DistributedConnection connection) | ||||
|         //{ | ||||
|         //    connection.OnReady += Sender_OnReady; | ||||
|         //    connection.Server = this; | ||||
|         //    base.AddConnection(connection); | ||||
|         //} | ||||
|  | ||||
|  | ||||
|  | ||||
|         protected override void ClientConnected(DistributedConnection connection) | ||||
|         { | ||||
|             //connection.OnReady += ConnectionReadyEventReceiver; | ||||
|         } | ||||
|  | ||||
|         public override void Add(DistributedConnection connection) | ||||
|         { | ||||
|             connection.Server = this; | ||||
|             connection.ExceptionLevel = ExceptionLevel; | ||||
|             base.Add(connection); | ||||
|         } | ||||
|  | ||||
|         public override void Remove(DistributedConnection connection) | ||||
|         { | ||||
|             connection.Server = null; | ||||
|             base.Remove(connection); | ||||
|         } | ||||
|  | ||||
|         protected override void ClientDisconnected(DistributedConnection connection) | ||||
|         { | ||||
|             //connection.OnReady -= ConnectionReadyEventReceiver; | ||||
|             //Warehouse.Remove(connection); | ||||
|  | ||||
|  | ||||
|         } | ||||
|  | ||||
|          | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     public IMembership Membership | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     public EntryPoint EntryPoint | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     public ushort Port | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } = 10518; | ||||
|  | ||||
|  | ||||
|     [Attribute] | ||||
|     public ExceptionLevel ExceptionLevel { get; set; } | ||||
|         = ExceptionLevel.Code | ||||
|         | ExceptionLevel.Source | ||||
|         | ExceptionLevel.Message | ||||
|         | ExceptionLevel.Trace; | ||||
|  | ||||
|  | ||||
|     public Instance Instance | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|         { | ||||
|             TCPSocket listener; | ||||
|  | ||||
|             if (IP != null) | ||||
|                 listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port)); | ||||
|             else | ||||
|                 listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port)); | ||||
|  | ||||
|             Start(listener); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.Terminate) | ||||
|         { | ||||
|             Stop(); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemReload) | ||||
|         { | ||||
|             Trigger(ResourceTrigger.Terminate); | ||||
|             Trigger(ResourceTrigger.Initialize); | ||||
|         } | ||||
|  | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     //protected override void DataReceived(DistributedConnection sender, NetworkBuffer data) | ||||
|     //{ | ||||
|     //    //throw new NotImplementedException(); | ||||
|  | ||||
|     //} | ||||
|  | ||||
|  | ||||
|  | ||||
|     //protected override void ClientConnected(DistributedConnection sender) | ||||
|     //{ | ||||
|     //    //Console.WriteLine("DistributedConnection Client Connected"); | ||||
|     //} | ||||
|  | ||||
|     //private void ConnectionReadyEventReceiver(DistributedConnection sender) | ||||
|     //{ | ||||
|     //    sender.OnReady -= ConnectionReadyEventReceiver; | ||||
|     //    Warehouse.Put(sender, sender.LocalUsername, null, this); | ||||
|     //} | ||||
|  | ||||
|  | ||||
|     //public override void RemoveConnection(DistributedConnection connection) | ||||
|     //{ | ||||
|     //    connection.OnReady -= Sender_OnReady; | ||||
|     //    //connection.Server = null; | ||||
|     //    base.RemoveConnection(connection); | ||||
|     //} | ||||
|  | ||||
|     //public override void AddConnection(DistributedConnection connection) | ||||
|     //{ | ||||
|     //    connection.OnReady += Sender_OnReady; | ||||
|     //    connection.Server = this; | ||||
|     //    base.AddConnection(connection); | ||||
|     //} | ||||
|  | ||||
|  | ||||
|  | ||||
|     protected override void ClientConnected(DistributedConnection connection) | ||||
|     { | ||||
|         //connection.OnReady += ConnectionReadyEventReceiver; | ||||
|     } | ||||
|  | ||||
|     public override void Add(DistributedConnection connection) | ||||
|     { | ||||
|         connection.Server = this; | ||||
|         connection.ExceptionLevel = ExceptionLevel; | ||||
|         base.Add(connection); | ||||
|     } | ||||
|  | ||||
|     public override void Remove(DistributedConnection connection) | ||||
|     { | ||||
|         connection.Server = null; | ||||
|         base.Remove(connection); | ||||
|     } | ||||
|  | ||||
|     protected override void ClientDisconnected(DistributedConnection connection) | ||||
|     { | ||||
|         //connection.OnReady -= ConnectionReadyEventReceiver; | ||||
|         //Warehouse.Remove(connection); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -28,11 +28,9 @@ using System.Text; | ||||
| using Esiur.Net.Sockets; | ||||
| using Esiur.Security.Authority; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| namespace Esiur.Net.IIP; | ||||
| public class DistributedSession : NetworkSession | ||||
| { | ||||
|     public class DistributedSession : NetworkSession | ||||
|     { | ||||
|         public Source Source { get; set; } | ||||
|         public Authentication Authentication { get; set; } | ||||
|     } | ||||
|     public Source Source { get; set; } | ||||
|     public Authentication Authentication { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -29,12 +29,11 @@ using Esiur.Data; | ||||
| using Esiur.Resource; | ||||
| using Esiur.Resource.Template; | ||||
|  | ||||
| namespace Esiur.Net.IIP | ||||
| { | ||||
|     public abstract class EntryPoint : Esiur.Resource.Resource | ||||
|     { | ||||
| namespace Esiur.Net.IIP; | ||||
|  | ||||
|         public abstract AsyncReply<IResource[]> Query(string path, DistributedConnection sender); | ||||
|         protected abstract override bool Create(); | ||||
|     } | ||||
| public abstract class EntryPoint : Esiur.Resource.Resource | ||||
| { | ||||
|  | ||||
|     public abstract AsyncReply<IResource[]> Query(string path, DistributedConnection sender); | ||||
|     protected abstract override bool Create(); | ||||
| } | ||||
|   | ||||
| @@ -2,14 +2,13 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Net | ||||
| { | ||||
|     public interface INetworkReceiver<T> | ||||
|     { | ||||
|         void NetworkClose(T sender); | ||||
|         void NetworkReceive(T sender, NetworkBuffer buffer); | ||||
|         //void NetworkError(T sender); | ||||
| namespace Esiur.Net; | ||||
|  | ||||
|         void NetworkConnect(T sender); | ||||
|     } | ||||
| public interface INetworkReceiver<T> | ||||
| { | ||||
|     void NetworkClose(T sender); | ||||
|     void NetworkReceive(T sender, NetworkBuffer buffer); | ||||
|     //void NetworkError(T sender); | ||||
|  | ||||
|     void NetworkConnect(T sender); | ||||
| } | ||||
|   | ||||
| @@ -29,180 +29,178 @@ using System.Text; | ||||
| using Esiur.Data; | ||||
| using Esiur.Misc; | ||||
|  | ||||
| namespace Esiur.Net | ||||
| namespace Esiur.Net; | ||||
| public class NetworkBuffer | ||||
| { | ||||
|     public class NetworkBuffer | ||||
|     byte[] data; | ||||
|  | ||||
|     uint neededDataLength = 0; | ||||
|     //bool trim; | ||||
|  | ||||
|     object syncLock = new object(); | ||||
|  | ||||
|     //public object SyncLock | ||||
|     //{ | ||||
|     //  get { return syncLock; } | ||||
|     //} | ||||
|  | ||||
|     public NetworkBuffer() | ||||
|     { | ||||
|         byte[] data; | ||||
|         data = new byte[0]; | ||||
|     } | ||||
|  | ||||
|         uint neededDataLength = 0; | ||||
|         //bool trim; | ||||
|  | ||||
|         object syncLock = new object(); | ||||
|  | ||||
|         //public object SyncLock | ||||
|         //{ | ||||
|           //  get { return syncLock; } | ||||
|         //} | ||||
|  | ||||
|         public NetworkBuffer() | ||||
|     public bool Protected | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             data = new byte[0]; | ||||
|             return neededDataLength > data.Length; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public bool Protected | ||||
|     public uint Available | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             get | ||||
|             return (uint)data.Length; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     //public void HoldForAtLeast(byte[] src, uint offset, uint size, uint needed) | ||||
|     //{ | ||||
|     //    HoldFor(src, offset, size, needed); | ||||
|     //    //trim = false; | ||||
|     //} | ||||
|  | ||||
|     //public void HoldForAtLeast(byte[] src, uint needed) | ||||
|     //{ | ||||
|     //    HoldForAtLeast(src, 0, (uint)src.Length, needed); | ||||
|     //} | ||||
|  | ||||
|     public void HoldForNextWrite(byte[] src) | ||||
|     { | ||||
|         //HoldForAtLeast(src, (uint)src.Length + 1); | ||||
|         HoldFor(src, (uint)src.Length + 1); | ||||
|     } | ||||
|  | ||||
|     public void HoldForNextWrite(byte[] src, uint offset, uint size) | ||||
|     { | ||||
|         //HoldForAtLeast(src, offset, size, size + 1); | ||||
|         HoldFor(src, offset, size, size + 1); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void HoldFor(byte[] src, uint offset, uint size, uint needed) | ||||
|     { | ||||
|         lock (syncLock) | ||||
|         { | ||||
|             if (size >= needed) | ||||
|                 throw new Exception("Size >= Needed !"); | ||||
|  | ||||
|             //trim = true; | ||||
|             data = DC.Combine(src, offset, size, data, 0, (uint)data.Length); | ||||
|             neededDataLength = needed; | ||||
|  | ||||
|             // Console.WriteLine("Hold StackTrace: '{0}'", Environment.StackTrace); | ||||
|  | ||||
|             //Console.WriteLine("Holded {0} {1} {2} {3} - {4}", offset, size, needed, data.Length, GetHashCode()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void HoldFor(byte[] src, uint needed) | ||||
|     { | ||||
|         HoldFor(src, 0, (uint)src.Length, needed); | ||||
|     } | ||||
|  | ||||
|     public bool Protect(byte[] data, uint offset, uint needed)//, bool exact = false) | ||||
|     { | ||||
|         uint dataLength = (uint)data.Length - offset; | ||||
|  | ||||
|         // protection | ||||
|         if (dataLength < needed) | ||||
|         { | ||||
|             //if (exact) | ||||
|             //    HoldFor(data, offset, dataLength, needed); | ||||
|             //else | ||||
|             //HoldForAtLeast(data, offset, dataLength, needed); | ||||
|             HoldFor(data, offset, dataLength, needed); | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|     public void Write(byte[] src) | ||||
|     { | ||||
|         Write(src, 0, (uint)src.Length); | ||||
|     } | ||||
|  | ||||
|     public void Write(byte[] src, uint offset, uint length) | ||||
|     { | ||||
|         //if (data.Length > 0) | ||||
|         //  Console.WriteLine(); | ||||
|  | ||||
|         lock (syncLock) | ||||
|             DC.Append(ref data, src, offset, length); | ||||
|     } | ||||
|  | ||||
|     public bool CanRead | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             if (data.Length == 0) | ||||
|                 return false; | ||||
|             if (data.Length < neededDataLength) | ||||
|                 return false; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public byte[] Read() | ||||
|     { | ||||
|         lock (syncLock) | ||||
|         { | ||||
|             if (data.Length == 0) | ||||
|                 return null; | ||||
|  | ||||
|             byte[] rt = null; | ||||
|  | ||||
|             if (neededDataLength == 0) | ||||
|             { | ||||
|                 return neededDataLength > data.Length; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public uint Available | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return (uint)data.Length; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //public void HoldForAtLeast(byte[] src, uint offset, uint size, uint needed) | ||||
|         //{ | ||||
|         //    HoldFor(src, offset, size, needed); | ||||
|         //    //trim = false; | ||||
|         //} | ||||
|  | ||||
|         //public void HoldForAtLeast(byte[] src, uint needed) | ||||
|         //{ | ||||
|         //    HoldForAtLeast(src, 0, (uint)src.Length, needed); | ||||
|         //} | ||||
|  | ||||
|         public void HoldForNextWrite(byte[] src) | ||||
|         { | ||||
|             //HoldForAtLeast(src, (uint)src.Length + 1); | ||||
|             HoldFor(src, (uint)src.Length + 1); | ||||
|         } | ||||
|  | ||||
|         public void HoldForNextWrite(byte[] src, uint offset, uint size) | ||||
|         { | ||||
|             //HoldForAtLeast(src, offset, size, size + 1); | ||||
|             HoldFor(src, offset, size, size + 1); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void HoldFor(byte[] src, uint offset, uint size, uint needed) | ||||
|         { | ||||
|             lock (syncLock) | ||||
|             { | ||||
|                 if (size >= needed) | ||||
|                     throw new Exception("Size >= Needed !"); | ||||
|  | ||||
|                 //trim = true; | ||||
|                 data = DC.Combine(src, offset, size, data, 0, (uint)data.Length); | ||||
|                 neededDataLength = needed; | ||||
|  | ||||
|                 // Console.WriteLine("Hold StackTrace: '{0}'", Environment.StackTrace); | ||||
|  | ||||
|                 //Console.WriteLine("Holded {0} {1} {2} {3} - {4}", offset, size, needed, data.Length, GetHashCode()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void HoldFor(byte[] src, uint needed) | ||||
|         { | ||||
|             HoldFor(src, 0, (uint)src.Length, needed); | ||||
|         } | ||||
|  | ||||
|         public bool Protect(byte[] data, uint offset, uint needed)//, bool exact = false) | ||||
|         { | ||||
|             uint dataLength = (uint)data.Length - offset; | ||||
|  | ||||
|             // protection | ||||
|             if (dataLength < needed) | ||||
|             { | ||||
|                 //if (exact) | ||||
|                 //    HoldFor(data, offset, dataLength, needed); | ||||
|                 //else | ||||
|                 //HoldForAtLeast(data, offset, dataLength, needed); | ||||
|                 HoldFor(data, offset, dataLength, needed); | ||||
|                 return true; | ||||
|                 rt = data; | ||||
|                 data = new byte[0]; | ||||
|             } | ||||
|             else | ||||
|                 return false; | ||||
|         } | ||||
|  | ||||
|         public void Write(byte[] src) | ||||
|         { | ||||
|             Write(src, 0, (uint)src.Length); | ||||
|         } | ||||
|  | ||||
|         public void Write(byte[] src, uint offset, uint length) | ||||
|         { | ||||
|             //if (data.Length > 0) | ||||
|               //  Console.WriteLine(); | ||||
|  | ||||
|             lock(syncLock) | ||||
|                 DC.Append(ref data, src, offset, length); | ||||
|         } | ||||
|  | ||||
|         public bool CanRead | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (data.Length == 0) | ||||
|                     return false; | ||||
|                 if (data.Length < neededDataLength) | ||||
|                     return false; | ||||
|                 //Console.WriteLine("P STATE:" + data.Length + " " + neededDataLength); | ||||
|  | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public byte[] Read() | ||||
|         { | ||||
|             lock (syncLock) | ||||
|             { | ||||
|                 if (data.Length == 0) | ||||
|                     return null; | ||||
|  | ||||
|                 byte[] rt = null; | ||||
|  | ||||
|                 if (neededDataLength == 0) | ||||
|                 if (data.Length >= neededDataLength) | ||||
|                 { | ||||
|                     //Console.WriteLine("data.Length >= neededDataLength " + data.Length + " >= " + neededDataLength + " " + trim); | ||||
|  | ||||
|                     //if (trim) | ||||
|                     //{ | ||||
|                     //  rt = DC.Clip(data, 0, neededDataLength); | ||||
|                     //  data = DC.Clip(data, neededDataLength, (uint)data.Length - neededDataLength); | ||||
|                     //} | ||||
|                     //else | ||||
|                     //{ | ||||
|                     // return all data | ||||
|                     rt = data; | ||||
|                     data = new byte[0]; | ||||
|                     //} | ||||
|  | ||||
|                     neededDataLength = 0; | ||||
|                     return rt; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //Console.WriteLine("P STATE:" + data.Length + " " + neededDataLength); | ||||
|  | ||||
|                     if (data.Length >= neededDataLength) | ||||
|                     { | ||||
|                         //Console.WriteLine("data.Length >= neededDataLength " + data.Length + " >= " + neededDataLength + " " + trim); | ||||
|  | ||||
|                         //if (trim) | ||||
|                         //{ | ||||
|                         //  rt = DC.Clip(data, 0, neededDataLength); | ||||
|                         //  data = DC.Clip(data, neededDataLength, (uint)data.Length - neededDataLength); | ||||
|                         //} | ||||
|                         //else | ||||
|                         //{ | ||||
|                         // return all data | ||||
|                         rt = data; | ||||
|                         data = new byte[0]; | ||||
|                         //} | ||||
|  | ||||
|                         neededDataLength = 0; | ||||
|                         return rt; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         return null; | ||||
|                     } | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|                 return rt; | ||||
|            } | ||||
|             return rt; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,332 +36,330 @@ using Esiur.Data; | ||||
| using Esiur.Net.Sockets; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net | ||||
| namespace Esiur.Net; | ||||
| public abstract class NetworkConnection : IDestructible, INetworkReceiver<ISocket>// <TS>: IResource where TS : NetworkSession | ||||
| { | ||||
|     public abstract class NetworkConnection: IDestructible, INetworkReceiver<ISocket>// <TS>: IResource where TS : NetworkSession | ||||
|     private Sockets.ISocket sock; | ||||
|     //        private bool connected; | ||||
|  | ||||
|     private DateTime lastAction; | ||||
|  | ||||
|     //public delegate void DataReceivedEvent(NetworkConnection sender, NetworkBuffer data); | ||||
|     //public delegate void ConnectionClosedEvent(NetworkConnection sender); | ||||
|     public delegate void NetworkConnectionEvent(NetworkConnection connection); | ||||
|  | ||||
|     public event NetworkConnectionEvent OnConnect; | ||||
|     //public event DataReceivedEvent OnDataReceived; | ||||
|     public event NetworkConnectionEvent OnClose; | ||||
|  | ||||
|  | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|     //object receivingLock = new object(); | ||||
|  | ||||
|     //object sendLock = new object(); | ||||
|  | ||||
|     bool processing = false; | ||||
|  | ||||
|     // public INetworkReceiver<NetworkConnection> Receiver { get; set; } | ||||
|  | ||||
|     public virtual void Destroy() | ||||
|     { | ||||
|         private Sockets.ISocket sock; | ||||
|         //        private bool connected; | ||||
|         // remove references | ||||
|         //sock.OnClose -= Socket_OnClose; | ||||
|         //sock.OnConnect -= Socket_OnConnect; | ||||
|         //sock.OnReceive -= Socket_OnReceive; | ||||
|         sock?.Destroy(); | ||||
|         //Receiver = null; | ||||
|         Close(); | ||||
|         sock = null; | ||||
|  | ||||
|         private DateTime lastAction; | ||||
|         OnClose = null; | ||||
|         OnConnect = null; | ||||
|         //OnDataReceived = null; | ||||
|         OnDestroy?.Invoke(this); | ||||
|         OnDestroy = null; | ||||
|     } | ||||
|  | ||||
|         //public delegate void DataReceivedEvent(NetworkConnection sender, NetworkBuffer data); | ||||
|         //public delegate void ConnectionClosedEvent(NetworkConnection sender); | ||||
|         public delegate void NetworkConnectionEvent(NetworkConnection connection); | ||||
|  | ||||
|         public event NetworkConnectionEvent OnConnect; | ||||
|         //public event DataReceivedEvent OnDataReceived; | ||||
|         public event NetworkConnectionEvent OnClose; | ||||
|  | ||||
|              | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|         //object receivingLock = new object(); | ||||
|  | ||||
|         //object sendLock = new object(); | ||||
|  | ||||
|         bool processing = false; | ||||
|  | ||||
|        // public INetworkReceiver<NetworkConnection> Receiver { get; set; } | ||||
|  | ||||
|         public virtual void Destroy() | ||||
|     public ISocket Socket | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             // remove references | ||||
|             return sock; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public virtual void Assign(ISocket socket) | ||||
|     { | ||||
|         lastAction = DateTime.Now; | ||||
|         sock = socket; | ||||
|         sock.Receiver = this; | ||||
|  | ||||
|         //socket.OnReceive += Socket_OnReceive; | ||||
|         //socket.OnClose += Socket_OnClose; | ||||
|         //socket.OnConnect += Socket_OnConnect; | ||||
|     } | ||||
|  | ||||
|     //private void Socket_OnConnect() | ||||
|     //{ | ||||
|     //    OnConnect?.Invoke(this); | ||||
|     //} | ||||
|  | ||||
|     //private void Socket_OnClose() | ||||
|     //{ | ||||
|     //    ConnectionClosed(); | ||||
|     //    OnClose?.Invoke(this); | ||||
|     //} | ||||
|  | ||||
|     //protected virtual void ConnectionClosed() | ||||
|     //{ | ||||
|  | ||||
|     //} | ||||
|  | ||||
|     //private void Socket_OnReceive(NetworkBuffer buffer) | ||||
|     //{ | ||||
|     //} | ||||
|  | ||||
|     public ISocket Unassign() | ||||
|     { | ||||
|         if (sock != null) | ||||
|         { | ||||
|             // connected = false; | ||||
|             //sock.OnClose -= Socket_OnClose; | ||||
|             //sock.OnConnect -= Socket_OnConnect; | ||||
|             //sock.OnReceive -= Socket_OnReceive; | ||||
|             sock?.Destroy(); | ||||
|             //Receiver = null; | ||||
|             Close(); | ||||
|             sock.Receiver = null; | ||||
|  | ||||
|             var rt = sock; | ||||
|             sock = null; | ||||
|  | ||||
|             OnClose = null; | ||||
|             OnConnect = null; | ||||
|             //OnDataReceived = null; | ||||
|             OnDestroy?.Invoke(this); | ||||
|             OnDestroy = null; | ||||
|             return rt; | ||||
|         } | ||||
|         else | ||||
|             return null; | ||||
|     } | ||||
|  | ||||
|         public ISocket Socket | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return sock; | ||||
|             } | ||||
|         } | ||||
|     //protected virtual void DataReceived(NetworkBuffer data) | ||||
|     //{ | ||||
|     //    if (OnDataReceived != null) | ||||
|     //    { | ||||
|     //        try | ||||
|     //        { | ||||
|     //            OnDataReceived?.Invoke(this, data); | ||||
|     //        } | ||||
|     //        catch (Exception ex) | ||||
|     //        { | ||||
|     //            Global.Log("NetworkConenction:DataReceived", LogType.Error, ex.ToString()); | ||||
|     //        } | ||||
|     //    } | ||||
|     //} | ||||
|  | ||||
|         public virtual void Assign(ISocket socket) | ||||
|         { | ||||
|             lastAction = DateTime.Now; | ||||
|             sock = socket; | ||||
|             sock.Receiver = this; | ||||
|     public void Close() | ||||
|     { | ||||
|         //if (!connected) | ||||
|         //  return; | ||||
|  | ||||
|             //socket.OnReceive += Socket_OnReceive; | ||||
|             //socket.OnClose += Socket_OnClose; | ||||
|             //socket.OnConnect += Socket_OnConnect; | ||||
|         } | ||||
|  | ||||
|         //private void Socket_OnConnect() | ||||
|         //{ | ||||
|         //    OnConnect?.Invoke(this); | ||||
|         //} | ||||
|  | ||||
|         //private void Socket_OnClose() | ||||
|         //{ | ||||
|         //    ConnectionClosed(); | ||||
|         //    OnClose?.Invoke(this); | ||||
|         //} | ||||
|  | ||||
|         //protected virtual void ConnectionClosed() | ||||
|         //{ | ||||
|  | ||||
|         //} | ||||
|  | ||||
|         //private void Socket_OnReceive(NetworkBuffer buffer) | ||||
|         //{ | ||||
|         //} | ||||
|  | ||||
|         public ISocket Unassign() | ||||
|         try | ||||
|         { | ||||
|             if (sock != null) | ||||
|             { | ||||
|                 // connected = false; | ||||
|                 //sock.OnClose -= Socket_OnClose; | ||||
|                 //sock.OnConnect -= Socket_OnConnect; | ||||
|                 //sock.OnReceive -= Socket_OnReceive; | ||||
|                 sock.Receiver = null; | ||||
|                 sock.Close(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Global.Log("NetworkConenction:Close", LogType.Error, ex.ToString()); | ||||
|  | ||||
|                 var rt = sock; | ||||
|                 sock = null; | ||||
|         } | ||||
|  | ||||
|                 return rt; | ||||
|             } | ||||
|         //finally | ||||
|         //{ | ||||
|         //connected = false; | ||||
|         //} | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public DateTime LastAction | ||||
|     { | ||||
|         get { return lastAction; } | ||||
|     } | ||||
|  | ||||
|     public IPEndPoint RemoteEndPoint | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             if (sock != null) | ||||
|                 return (IPEndPoint)sock.RemoteEndPoint; | ||||
|             else | ||||
|                 return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         //protected virtual void DataReceived(NetworkBuffer data) | ||||
|         //{ | ||||
|         //    if (OnDataReceived != null) | ||||
|         //    { | ||||
|         //        try | ||||
|         //        { | ||||
|         //            OnDataReceived?.Invoke(this, data); | ||||
|         //        } | ||||
|         //        catch (Exception ex) | ||||
|         //        { | ||||
|         //            Global.Log("NetworkConenction:DataReceived", LogType.Error, ex.ToString()); | ||||
|         //        } | ||||
|         //    } | ||||
|         //} | ||||
|  | ||||
|         public void Close() | ||||
|     public IPEndPoint LocalEndPoint | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             //if (!connected) | ||||
|             //  return; | ||||
|             if (sock != null) | ||||
|                 return (IPEndPoint)sock.LocalEndPoint; | ||||
|             else | ||||
|                 return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|             try | ||||
|             { | ||||
|     public bool IsConnected | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return sock.State == SocketState.Established; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* | ||||
|     public void CloseAndWait() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (!connected) | ||||
|                 return; | ||||
|  | ||||
|                 if (sock != null) | ||||
|                     sock.Close(); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Global.Log("NetworkConenction:Close", LogType.Error, ex.ToString()); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             //finally | ||||
|             //{ | ||||
|             //connected = false; | ||||
|             //} | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public DateTime LastAction | ||||
|         { | ||||
|             get { return lastAction; } | ||||
|         } | ||||
|  | ||||
|         public IPEndPoint RemoteEndPoint | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (sock != null) | ||||
|                     return (IPEndPoint)sock.RemoteEndPoint; | ||||
|                 else | ||||
|                     return null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public IPEndPoint LocalEndPoint | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if (sock != null) | ||||
|                     return (IPEndPoint)sock.LocalEndPoint; | ||||
|                 else | ||||
|                     return null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public bool IsConnected | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return sock.State == SocketState.Established; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|         public void CloseAndWait() | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (!connected) | ||||
|                     return; | ||||
|  | ||||
|                     if (sock != null) | ||||
|                         sock.Close(); | ||||
|  | ||||
|                     while (connected) | ||||
|                     { | ||||
|                         Thread.Sleep(100); | ||||
|                     } | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                  | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         public virtual AsyncReply<bool> SendAsync(byte[] message, int offset, int length) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 lastAction = DateTime.Now; | ||||
|                 return sock.SendAsync(message, offset, length); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return new AsyncReply<bool>(false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public virtual void Send(byte[] msg) | ||||
|         { | ||||
|             try | ||||
|             {                 | ||||
|                 sock?.Send(msg);        | ||||
|                 lastAction = DateTime.Now; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public virtual void Send(byte[] msg, int offset, int length) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 sock.Send(msg, offset, length); | ||||
|                 lastAction = DateTime.Now; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public virtual void Send(string data) | ||||
|         { | ||||
|             Send(Encoding.UTF8.GetBytes(data)); | ||||
|         } | ||||
|  | ||||
|         public  void NetworkClose(ISocket socket) | ||||
|         { | ||||
|             Disconencted(); | ||||
|             OnClose?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         public void NetworkConnect(ISocket socket) | ||||
|         { | ||||
|             Connected(); | ||||
|             OnConnect?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         //{ | ||||
|         //ConnectionClosed(); | ||||
|         //OnClose?.Invoke(this); | ||||
|  | ||||
|         //Receiver?.NetworkClose(this); | ||||
|         //} | ||||
|  | ||||
|         //public void NetworkConenct(ISocket sender) | ||||
|         //{ | ||||
|         //    OnConnect?.Invoke(this); | ||||
|         //} | ||||
|  | ||||
|         protected abstract void DataReceived(NetworkBuffer buffer); | ||||
|         protected abstract void Connected(); | ||||
|         protected abstract void Disconencted(); | ||||
|  | ||||
|         public void NetworkReceive(ISocket sender, NetworkBuffer buffer) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 // Unassigned ? | ||||
|                 if (sock == null) | ||||
|                     return; | ||||
|  | ||||
|                 // Closed ? | ||||
|                 if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated) // || !connected) | ||||
|                     return; | ||||
|  | ||||
|                 lastAction = DateTime.Now; | ||||
|  | ||||
|                 if (!processing) | ||||
|                 while (connected) | ||||
|                 { | ||||
|                     processing = true; | ||||
|                     Thread.Sleep(100); | ||||
|                 } | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|                     try | ||||
|         } | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     public virtual AsyncReply<bool> SendAsync(byte[] message, int offset, int length) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             lastAction = DateTime.Now; | ||||
|             return sock.SendAsync(message, offset, length); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return new AsyncReply<bool>(false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public virtual void Send(byte[] msg) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             sock?.Send(msg); | ||||
|             lastAction = DateTime.Now; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public virtual void Send(byte[] msg, int offset, int length) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             sock.Send(msg, offset, length); | ||||
|             lastAction = DateTime.Now; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public virtual void Send(string data) | ||||
|     { | ||||
|         Send(Encoding.UTF8.GetBytes(data)); | ||||
|     } | ||||
|  | ||||
|     public void NetworkClose(ISocket socket) | ||||
|     { | ||||
|         Disconencted(); | ||||
|         OnClose?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     public void NetworkConnect(ISocket socket) | ||||
|     { | ||||
|         Connected(); | ||||
|         OnConnect?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     //{ | ||||
|     //ConnectionClosed(); | ||||
|     //OnClose?.Invoke(this); | ||||
|  | ||||
|     //Receiver?.NetworkClose(this); | ||||
|     //} | ||||
|  | ||||
|     //public void NetworkConenct(ISocket sender) | ||||
|     //{ | ||||
|     //    OnConnect?.Invoke(this); | ||||
|     //} | ||||
|  | ||||
|     protected abstract void DataReceived(NetworkBuffer buffer); | ||||
|     protected abstract void Connected(); | ||||
|     protected abstract void Disconencted(); | ||||
|  | ||||
|     public void NetworkReceive(ISocket sender, NetworkBuffer buffer) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             // Unassigned ? | ||||
|             if (sock == null) | ||||
|                 return; | ||||
|  | ||||
|             // Closed ? | ||||
|             if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated) // || !connected) | ||||
|                 return; | ||||
|  | ||||
|             lastAction = DateTime.Now; | ||||
|  | ||||
|             if (!processing) | ||||
|             { | ||||
|                 processing = true; | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     //lock(buffer.SyncLock) | ||||
|                     while (buffer.Available > 0 && !buffer.Protected) | ||||
|                     { | ||||
|                         //lock(buffer.SyncLock) | ||||
|                         while (buffer.Available > 0 && !buffer.Protected) | ||||
|                         { | ||||
|                             //Receiver?.NetworkReceive(this, buffer); | ||||
|                             DataReceived(buffer); | ||||
|                         } | ||||
|                         //Receiver?.NetworkReceive(this, buffer); | ||||
|                         DataReceived(buffer); | ||||
|                     } | ||||
|                     catch | ||||
|                     { | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                     processing = false; | ||||
|                 } | ||||
|  | ||||
|                 processing = false; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Global.Log("NetworkConnection", LogType.Warning, ex.ToString()); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Global.Log("NetworkConnection", LogType.Warning, ex.ToString()); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //{ | ||||
|           //  Receiver?.NetworkError(this); | ||||
|             //throw new NotImplementedException(); | ||||
|         //} | ||||
|  | ||||
|         //public void NetworkConnect(ISocket sender) | ||||
|         //{ | ||||
|           //  Receiver?.NetworkConnect(this); | ||||
|             //throw new NotImplementedException(); | ||||
|         //} | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|     //{ | ||||
|     //  Receiver?.NetworkError(this); | ||||
|     //throw new NotImplementedException(); | ||||
|     //} | ||||
|  | ||||
|     //public void NetworkConnect(ISocket sender) | ||||
|     //{ | ||||
|     //  Receiver?.NetworkConnect(this); | ||||
|     //throw new NotImplementedException(); | ||||
|     //} | ||||
| } | ||||
|   | ||||
| @@ -33,279 +33,277 @@ using Esiur.Resource; | ||||
| using System.Threading.Tasks; | ||||
| using System.Diagnostics; | ||||
|  | ||||
| namespace Esiur.Net | ||||
| namespace Esiur.Net; | ||||
|  | ||||
| public abstract class NetworkServer<TConnection> : IDestructible where TConnection : NetworkConnection, new() | ||||
| { | ||||
|     //private bool isRunning; | ||||
|     private Sockets.ISocket listener; | ||||
|     public AutoList<TConnection, NetworkServer<TConnection>> Connections { get; internal set; } | ||||
|  | ||||
|     public abstract class NetworkServer<TConnection> : IDestructible where TConnection : NetworkConnection, new() | ||||
|     private Thread thread; | ||||
|  | ||||
|     //protected abstract void DataReceived(TConnection sender, NetworkBuffer data); | ||||
|     //protected abstract void ClientConnected(TConnection sender); | ||||
|     //protected abstract void ClientDisconnected(TConnection sender); | ||||
|  | ||||
|  | ||||
|     private Timer timer; | ||||
|     //public KeyList<string, TSession> Sessions = new KeyList<string, TSession>(); | ||||
|  | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     //public AutoList<TConnection, NetworkServer<TConnection>> Connections => connections; | ||||
|  | ||||
|     private void MinuteThread(object state) | ||||
|     { | ||||
|         //private bool isRunning; | ||||
|         private Sockets.ISocket listener; | ||||
|         public AutoList<TConnection, NetworkServer<TConnection>> Connections { get; private set; } | ||||
|  | ||||
|         private Thread thread; | ||||
|  | ||||
|         //protected abstract void DataReceived(TConnection sender, NetworkBuffer data); | ||||
|         //protected abstract void ClientConnected(TConnection sender); | ||||
|         //protected abstract void ClientDisconnected(TConnection sender); | ||||
|         List<TConnection> ToBeClosed = null; | ||||
|  | ||||
|  | ||||
|         private Timer timer; | ||||
|         //public KeyList<string, TSession> Sessions = new KeyList<string, TSession>(); | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         //public AutoList<TConnection, NetworkServer<TConnection>> Connections => connections; | ||||
|  | ||||
|         private void MinuteThread(object state) | ||||
|         lock (Connections.SyncRoot) | ||||
|         { | ||||
|             List<TConnection> ToBeClosed = null; | ||||
|  | ||||
|  | ||||
|             lock (Connections.SyncRoot) | ||||
|             foreach (TConnection c in Connections) | ||||
|             { | ||||
|                 foreach (TConnection c in Connections) | ||||
|                 if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= Timeout) | ||||
|                 { | ||||
|                     if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= Timeout) | ||||
|                     { | ||||
|                         if (ToBeClosed == null) | ||||
|                             ToBeClosed = new List<TConnection>(); | ||||
|                         ToBeClosed.Add(c); | ||||
|                     } | ||||
|                     if (ToBeClosed == null) | ||||
|                         ToBeClosed = new List<TConnection>(); | ||||
|                     ToBeClosed.Add(c); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (ToBeClosed != null) | ||||
|             { | ||||
|                 //Console.WriteLine("Term: " + ToBeClosed.Count + " " + this.listener.LocalEndPoint.ToString()); | ||||
|                 foreach (TConnection c in ToBeClosed) | ||||
|                     c.Close();// CloseAndWait(); | ||||
|  | ||||
|                 ToBeClosed.Clear(); | ||||
|                 ToBeClosed = null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Start(Sockets.ISocket socket)//, uint timeout, uint clock) | ||||
|         if (ToBeClosed != null) | ||||
|         { | ||||
|             if (listener != null) | ||||
|                 return; | ||||
|             //Console.WriteLine("Term: " + ToBeClosed.Count + " " + this.listener.LocalEndPoint.ToString()); | ||||
|             foreach (TConnection c in ToBeClosed) | ||||
|                 c.Close();// CloseAndWait(); | ||||
|  | ||||
|             ToBeClosed.Clear(); | ||||
|             ToBeClosed = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void Start(Sockets.ISocket socket)//, uint timeout, uint clock) | ||||
|     { | ||||
|         if (listener != null) | ||||
|             return; | ||||
|  | ||||
|  | ||||
|             Connections = new AutoList<TConnection, NetworkServer<TConnection>>(this); | ||||
|         Connections = new AutoList<TConnection, NetworkServer<TConnection>>(this); | ||||
|  | ||||
|  | ||||
|             if (Timeout > 0 & Clock > 0) | ||||
|         if (Timeout > 0 & Clock > 0) | ||||
|         { | ||||
|             timer = new Timer(MinuteThread, null, TimeSpan.FromMinutes(0), TimeSpan.FromSeconds(Clock)); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         listener = socket; | ||||
|  | ||||
|         // Start accepting | ||||
|         //var r = listener.Accept(); | ||||
|         //r.Then(NewConnection); | ||||
|         //r.timeout?.Dispose(); | ||||
|  | ||||
|         //var rt = listener.Accept().Then() | ||||
|         thread = new Thread(new ThreadStart(() => | ||||
|         { | ||||
|             while (true) | ||||
|             { | ||||
|                 timer = new Timer(MinuteThread, null, TimeSpan.FromMinutes(0), TimeSpan.FromSeconds(Clock)); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             listener = socket; | ||||
|  | ||||
|             // Start accepting | ||||
|             //var r = listener.Accept(); | ||||
|             //r.Then(NewConnection); | ||||
|             //r.timeout?.Dispose(); | ||||
|  | ||||
|             //var rt = listener.Accept().Then() | ||||
|             thread = new Thread(new ThreadStart(() => | ||||
|             { | ||||
|                 while (true) | ||||
|                 try | ||||
|                 { | ||||
|                     try | ||||
|                     var s = listener.Accept(); | ||||
|  | ||||
|                     if (s == null) | ||||
|                     { | ||||
|                         var s = listener.Accept(); | ||||
|                         Console.Write("sock == null"); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                         if (s == null) | ||||
|                         { | ||||
|                             Console.Write("sock == null"); | ||||
|                             return; | ||||
|                         } | ||||
|  | ||||
|                         var c = new TConnection(); | ||||
|                     var c = new TConnection(); | ||||
|                         //c.OnClose += ClientDisconnectedEventReceiver; | ||||
|                         c.Assign(s); | ||||
|                         Add(c); | ||||
|                     Add(c); | ||||
|                         //Connections.Add(c); | ||||
|                          | ||||
|  | ||||
|                         try | ||||
|                         { | ||||
|                     { | ||||
|                             //ClientConnected(c); | ||||
|                             ClientConnected(c); | ||||
|                             //NetworkConnect(c); | ||||
|                         } | ||||
|                         catch | ||||
|                         { | ||||
|                     catch | ||||
|                     { | ||||
|                             // something wrong with the child. | ||||
|                         } | ||||
|  | ||||
|                         s.Begin(); | ||||
|                     s.Begin(); | ||||
|  | ||||
|                         // Accept more | ||||
|                         //listener.Accept().Then(NewConnection); | ||||
|  | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         Console.WriteLine(ex); | ||||
|                     } | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|             thread.Start(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         [Attribute] | ||||
|         public uint Timeout | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         [Attribute] | ||||
|         public uint Clock | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Stop() | ||||
|         { | ||||
|             var port = 0; | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 if (listener != null) | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     port = listener.LocalEndPoint.Port; | ||||
|                     listener.Close(); | ||||
|                     Console.WriteLine(ex); | ||||
|                 } | ||||
|  | ||||
|                 // wait until the listener stops | ||||
|                 //while (isRunning) | ||||
|                 //{ | ||||
|                 //  Thread.Sleep(100); | ||||
|                 //} | ||||
|  | ||||
|                 //Console.WriteLine("Listener stopped"); | ||||
|  | ||||
|                 var cons = Connections.ToArray(); | ||||
|  | ||||
|                 //lock (connections.SyncRoot) | ||||
|                 //{ | ||||
|                 foreach (TConnection con in cons) | ||||
|                     con.Close(); | ||||
|                 //} | ||||
|  | ||||
|                 //Console.WriteLine("Sockets Closed"); | ||||
|  | ||||
|                 //while (connections.Count > 0) | ||||
|                 //{ | ||||
|                 //    Console.WriteLine("Waiting... " + connections.Count);   | ||||
|                 //    Thread.Sleep(1000); | ||||
|                 //} | ||||
|  | ||||
|             } | ||||
|             finally | ||||
|         })); | ||||
|  | ||||
|         thread.Start(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     [Attribute] | ||||
|     public uint Timeout | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     [Attribute] | ||||
|     public uint Clock | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Stop() | ||||
|     { | ||||
|         var port = 0; | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             if (listener != null) | ||||
|             { | ||||
|                 Console.WriteLine("Server@{0} is down", port); | ||||
|                 port = listener.LocalEndPoint.Port; | ||||
|                 listener.Close(); | ||||
|             } | ||||
|  | ||||
|             // wait until the listener stops | ||||
|             //while (isRunning) | ||||
|             //{ | ||||
|             //  Thread.Sleep(100); | ||||
|             //} | ||||
|  | ||||
|             //Console.WriteLine("Listener stopped"); | ||||
|  | ||||
|             var cons = Connections.ToArray(); | ||||
|  | ||||
|             //lock (connections.SyncRoot) | ||||
|             //{ | ||||
|             foreach (TConnection con in cons) | ||||
|                 con.Close(); | ||||
|             //} | ||||
|  | ||||
|             //Console.WriteLine("Sockets Closed"); | ||||
|  | ||||
|             //while (connections.Count > 0) | ||||
|             //{ | ||||
|             //    Console.WriteLine("Waiting... " + connections.Count);   | ||||
|             //    Thread.Sleep(1000); | ||||
|             //} | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public virtual void Remove(TConnection connection) | ||||
|         finally | ||||
|         { | ||||
|             //connection.OnDataReceived -= OnDataReceived; | ||||
|             //connection.OnConnect -= OnClientConnect; | ||||
|             connection.OnClose -= ClientDisconnectedEventReceiver; | ||||
|             Connections.Remove(connection); | ||||
|         } | ||||
|  | ||||
|         public virtual void Add(TConnection connection) | ||||
|         { | ||||
|             //connection.OnDataReceived += OnDataReceived; | ||||
|             //connection.OnConnect += OnClientConnect; | ||||
|             connection.OnClose += ClientDisconnectedEventReceiver;// OnClientClose; | ||||
|             Connections.Add(connection); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public bool IsRunning | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return listener.State == SocketState.Listening; | ||||
|                 //isRunning;  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //public void OnDataReceived(ISocket sender, NetworkBuffer data) | ||||
|         //{ | ||||
|         //    DataReceived((TConnection)sender, data); | ||||
|         //} | ||||
|  | ||||
|         //public void OnClientConnect(ISocket sender) | ||||
|         //{ | ||||
|         //    if (sender == null) | ||||
|         //        return; | ||||
|  | ||||
|         //    if (sender.RemoteEndPoint == null || sender.LocalEndPoint == null) | ||||
|         //    { } | ||||
|         //    //Console.WriteLine("NULL"); | ||||
|         //    else | ||||
|         //        Global.Log("Connections", LogType.Debug, sender.RemoteEndPoint.Address.ToString() | ||||
|         //            + "->" + sender.LocalEndPoint.Port + " at " + DateTime.UtcNow.ToString("d") | ||||
|         //            + " " + DateTime.UtcNow.ToString("d"), false); | ||||
|  | ||||
|         //    // Console.WriteLine("Connected " + sender.RemoteEndPoint.ToString()); | ||||
|         //    ClientConnected((TConnection)sender); | ||||
|         //} | ||||
|  | ||||
|         //public void OnClientClose(ISocket sender) | ||||
|         //{ | ||||
|         //} | ||||
|  | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             Stop(); | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         private void ClientDisconnectedEventReceiver(NetworkConnection connection) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var con = connection as TConnection; | ||||
|                 con.Destroy(); | ||||
|                 //                con.OnClose -= ClientDisconnectedEventReceiver; | ||||
|  | ||||
|                 Remove(con); | ||||
|  | ||||
|                 //Connections.Remove(con); | ||||
|                 ClientDisconnected(con); | ||||
|                 //RemoveConnection((TConnection)sender); | ||||
|                 //connections.Remove(sender) | ||||
|                 //ClientDisconnected((TConnection)sender); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Global.Log("NetworkServer:OnClientDisconnect", LogType.Error, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         protected abstract void ClientDisconnected(TConnection connection); | ||||
|         protected abstract void ClientConnected(TConnection connection); | ||||
|  | ||||
|         ~NetworkServer() | ||||
|         { | ||||
|             Stop(); | ||||
|             listener = null; | ||||
|             Console.WriteLine("Server@{0} is down", port); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|     public virtual void Remove(TConnection connection) | ||||
|     { | ||||
|         //connection.OnDataReceived -= OnDataReceived; | ||||
|         //connection.OnConnect -= OnClientConnect; | ||||
|         connection.OnClose -= ClientDisconnectedEventReceiver; | ||||
|         Connections.Remove(connection); | ||||
|     } | ||||
|  | ||||
|     public virtual void Add(TConnection connection) | ||||
|     { | ||||
|         //connection.OnDataReceived += OnDataReceived; | ||||
|         //connection.OnConnect += OnClientConnect; | ||||
|         connection.OnClose += ClientDisconnectedEventReceiver;// OnClientClose; | ||||
|         Connections.Add(connection); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public bool IsRunning | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return listener.State == SocketState.Listening; | ||||
|             //isRunning;  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     //public void OnDataReceived(ISocket sender, NetworkBuffer data) | ||||
|     //{ | ||||
|     //    DataReceived((TConnection)sender, data); | ||||
|     //} | ||||
|  | ||||
|     //public void OnClientConnect(ISocket sender) | ||||
|     //{ | ||||
|     //    if (sender == null) | ||||
|     //        return; | ||||
|  | ||||
|     //    if (sender.RemoteEndPoint == null || sender.LocalEndPoint == null) | ||||
|     //    { } | ||||
|     //    //Console.WriteLine("NULL"); | ||||
|     //    else | ||||
|     //        Global.Log("Connections", LogType.Debug, sender.RemoteEndPoint.Address.ToString() | ||||
|     //            + "->" + sender.LocalEndPoint.Port + " at " + DateTime.UtcNow.ToString("d") | ||||
|     //            + " " + DateTime.UtcNow.ToString("d"), false); | ||||
|  | ||||
|     //    // Console.WriteLine("Connected " + sender.RemoteEndPoint.ToString()); | ||||
|     //    ClientConnected((TConnection)sender); | ||||
|     //} | ||||
|  | ||||
|     //public void OnClientClose(ISocket sender) | ||||
|     //{ | ||||
|     //} | ||||
|  | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         Stop(); | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     private void ClientDisconnectedEventReceiver(NetworkConnection connection) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var con = connection as TConnection; | ||||
|             con.Destroy(); | ||||
|             //                con.OnClose -= ClientDisconnectedEventReceiver; | ||||
|  | ||||
|             Remove(con); | ||||
|  | ||||
|             //Connections.Remove(con); | ||||
|             ClientDisconnected(con); | ||||
|             //RemoveConnection((TConnection)sender); | ||||
|             //connections.Remove(sender) | ||||
|             //ClientDisconnected((TConnection)sender); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Global.Log("NetworkServer:OnClientDisconnect", LogType.Error, ex.ToString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected abstract void ClientDisconnected(TConnection connection); | ||||
|     protected abstract void ClientConnected(TConnection connection); | ||||
|  | ||||
|     ~NetworkServer() | ||||
|     { | ||||
|         Stop(); | ||||
|         listener = null; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -35,96 +35,94 @@ using Esiur.Data; | ||||
| using Esiur.Misc; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Net | ||||
| namespace Esiur.Net; | ||||
| public class NetworkSession : IDestructible //<T> where T : TClient | ||||
| { | ||||
|     public class NetworkSession:IDestructible //<T> where T : TClient | ||||
|     public delegate void SessionModifiedEvent(NetworkSession session, string key, object oldValue, object newValue); | ||||
|     public delegate void SessionEndedEvent(NetworkSession session); | ||||
|  | ||||
|     private string id; | ||||
|     private Timer timer; | ||||
|     private int timeout; | ||||
|     DateTime creation; | ||||
|     DateTime lastAction; | ||||
|  | ||||
|     private KeyList<string, object> variables; | ||||
|  | ||||
|     public event SessionEndedEvent OnEnd; | ||||
|     public event SessionModifiedEvent OnModify; | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public KeyList<string, object> Variables | ||||
|     { | ||||
|         public delegate void SessionModifiedEvent(NetworkSession session, string key, object oldValue, object newValue); | ||||
|         public delegate void SessionEndedEvent(NetworkSession session); | ||||
|         get { return variables; } | ||||
|     } | ||||
|  | ||||
|         private string id; | ||||
|         private Timer timer; | ||||
|         private int timeout; | ||||
|         DateTime creation; | ||||
|         DateTime lastAction; | ||||
|     public NetworkSession() | ||||
|     { | ||||
|         variables = new KeyList<string, object>(); | ||||
|         variables.OnModified += new KeyList<string, object>.Modified(VariablesModified); | ||||
|         creation = DateTime.Now; | ||||
|     } | ||||
|  | ||||
|         private KeyList<string, object> variables; | ||||
|     internal void Set(string id, int timeout) | ||||
|     { | ||||
|         //modified = sessionModifiedEvent; | ||||
|         //ended = sessionEndEvent; | ||||
|         this.id = id; | ||||
|  | ||||
|         public event SessionEndedEvent OnEnd; | ||||
|         public event SessionModifiedEvent OnModify; | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public KeyList<string, object> Variables | ||||
|         if (this.timeout != 0) | ||||
|         { | ||||
|             get { return variables; } | ||||
|         } | ||||
|  | ||||
|         public NetworkSession() | ||||
|         { | ||||
|             variables = new KeyList<string, object>(); | ||||
|             variables.OnModified += new KeyList<string, object>.Modified(VariablesModified); | ||||
|             this.timeout = timeout; | ||||
|             timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|             creation = DateTime.Now; | ||||
|         } | ||||
|          | ||||
|         internal void Set(string id, int timeout ) | ||||
|         { | ||||
|             //modified = sessionModifiedEvent; | ||||
|             //ended = sessionEndEvent; | ||||
|             this.id = id; | ||||
|  | ||||
|             if (this.timeout != 0) | ||||
|             { | ||||
|                 this.timeout = timeout; | ||||
|                 timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|                 creation = DateTime.Now; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private void OnSessionEndTimerCallback(object o) | ||||
|         { | ||||
|             OnEnd?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender) | ||||
|         { | ||||
|             OnModify?.Invoke(this, key, oldValue, newValue); | ||||
|         } | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|             timer.Dispose(); | ||||
|             timer = null; | ||||
|         } | ||||
|  | ||||
|         internal void Refresh() | ||||
|         { | ||||
|             lastAction = DateTime.Now; | ||||
|             timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|         } | ||||
|  | ||||
|         public int Timeout // Seconds | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return timeout; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 timeout = value; | ||||
|                 Refresh(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public string Id | ||||
|         { | ||||
|             get { return id; } | ||||
|         } | ||||
|  | ||||
|         public DateTime LastAction | ||||
|         { | ||||
|             get { return lastAction; } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|     private void OnSessionEndTimerCallback(object o) | ||||
|     { | ||||
|         OnEnd?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     void VariablesModified(string key, object oldValue, object newValue, KeyList<string, object> sender) | ||||
|     { | ||||
|         OnModify?.Invoke(this, key, oldValue, newValue); | ||||
|     } | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|         timer.Dispose(); | ||||
|         timer = null; | ||||
|     } | ||||
|  | ||||
|     internal void Refresh() | ||||
|     { | ||||
|         lastAction = DateTime.Now; | ||||
|         timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0)); | ||||
|     } | ||||
|  | ||||
|     public int Timeout // Seconds | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return timeout; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             timeout = value; | ||||
|             Refresh(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string Id | ||||
|     { | ||||
|         get { return id; } | ||||
|     } | ||||
|  | ||||
|     public DateTime LastAction | ||||
|     { | ||||
|         get { return lastAction; } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -32,287 +32,284 @@ using Esiur.Data; | ||||
| using System.Net; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace Esiur.Net.Packets | ||||
| namespace Esiur.Net.Packets; | ||||
| public class HTTPRequestPacket : Packet | ||||
| { | ||||
|     public class HTTPRequestPacket : Packet | ||||
|  | ||||
|     public enum HTTPMethod : byte | ||||
|     { | ||||
|         GET, | ||||
|         POST, | ||||
|         HEAD, | ||||
|         PUT, | ||||
|         DELETE, | ||||
|         OPTIONS, | ||||
|         TRACE, | ||||
|         CONNECT, | ||||
|         UNKNOWN | ||||
|     } | ||||
|  | ||||
|         public enum HTTPMethod:byte | ||||
|     public StringKeyList Query; | ||||
|     public HTTPMethod Method; | ||||
|     public StringKeyList Headers; | ||||
|  | ||||
|     public bool WSMode; | ||||
|  | ||||
|     public string Version; | ||||
|     public StringKeyList Cookies; // String | ||||
|     public string URL; /// With query | ||||
|     public string Filename; /// Without query | ||||
|     //public byte[] PostContents; | ||||
|     public KeyList<string, object> PostForms; | ||||
|     public byte[] Message; | ||||
|  | ||||
|  | ||||
|     private HTTPMethod getMethod(string method) | ||||
|     { | ||||
|         switch (method.ToLower()) | ||||
|         { | ||||
|             GET, | ||||
|             POST, | ||||
|             HEAD, | ||||
|             PUT, | ||||
|             DELETE, | ||||
|             OPTIONS, | ||||
|             TRACE, | ||||
|             CONNECT, | ||||
|             UNKNOWN | ||||
|             case "get": | ||||
|                 return HTTPMethod.GET; | ||||
|             case "post": | ||||
|                 return HTTPMethod.POST; | ||||
|             case "head": | ||||
|                 return HTTPMethod.HEAD; | ||||
|             case "put": | ||||
|                 return HTTPMethod.PUT; | ||||
|             case "delete": | ||||
|                 return HTTPMethod.DELETE; | ||||
|             case "options": | ||||
|                 return HTTPMethod.OPTIONS; | ||||
|             case "trace": | ||||
|                 return HTTPMethod.TRACE; | ||||
|             case "connect": | ||||
|                 return HTTPMethod.CONNECT; | ||||
|             default: | ||||
|                 return HTTPMethod.UNKNOWN; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public StringKeyList Query; | ||||
|         public HTTPMethod Method; | ||||
|         public StringKeyList Headers; | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return "HTTPRequestPacket" | ||||
|             + "\n\tVersion: " + Version | ||||
|             + "\n\tMethod: " + Method | ||||
|             + "\n\tURL: " + URL | ||||
|             + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL"); | ||||
|     } | ||||
|  | ||||
|         public bool WSMode; | ||||
|     public override long Parse(byte[] data, uint offset, uint ends) | ||||
|     { | ||||
|         string[] sMethod = null; | ||||
|         string[] sLines = null; | ||||
|  | ||||
|         public string Version; | ||||
|         public StringKeyList Cookies; // String | ||||
|         public string URL; /// With query | ||||
|         public string Filename; /// Without query | ||||
|         //public byte[] PostContents; | ||||
|         public KeyList<string, object> PostForms; | ||||
|         public byte[] Message; | ||||
|         uint headerSize = 0; | ||||
|  | ||||
|  | ||||
|         private HTTPMethod getMethod(string method) | ||||
|         for (uint i = offset; i < ends - 3; i++) | ||||
|         { | ||||
|             switch (method.ToLower()) | ||||
|             if (data[i] == '\r' && data[i + 1] == '\n' | ||||
|                 && data[i + 2] == '\r' && data[i + 3] == '\n') | ||||
|             { | ||||
|                 case "get": | ||||
|                     return HTTPMethod.GET; | ||||
|                 case "post": | ||||
|                     return HTTPMethod.POST; | ||||
|                 case "head": | ||||
|                     return HTTPMethod.HEAD; | ||||
|                 case "put": | ||||
|                     return HTTPMethod.PUT; | ||||
|                 case "delete": | ||||
|                     return HTTPMethod.DELETE; | ||||
|                 case "options": | ||||
|                     return HTTPMethod.OPTIONS; | ||||
|                 case "trace": | ||||
|                     return HTTPMethod.TRACE; | ||||
|                 case "connect": | ||||
|                     return HTTPMethod.CONNECT; | ||||
|                 default: | ||||
|                     return HTTPMethod.UNKNOWN; | ||||
|                 sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" }, | ||||
|                     StringSplitOptions.None); | ||||
|  | ||||
|                 headerSize = i + 4; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return "HTTPRequestPacket" | ||||
|                 + "\n\tVersion: " + Version | ||||
|                 + "\n\tMethod: " + Method | ||||
|                 + "\n\tURL: " + URL | ||||
|                 + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL"); | ||||
|         } | ||||
|         if (headerSize == 0) | ||||
|             return -1; | ||||
|  | ||||
|         public override long Parse(byte[] data, uint offset, uint ends) | ||||
|         { | ||||
|             string[] sMethod = null; | ||||
|             string[] sLines = null; | ||||
|              | ||||
|             uint headerSize = 0; | ||||
|         Cookies = new StringKeyList(); | ||||
|         PostForms = new KeyList<string, object>(); | ||||
|         Query = new StringKeyList(); | ||||
|         Headers = new StringKeyList(); | ||||
|  | ||||
|             for (uint i = offset; i < ends - 3; i++) | ||||
|         sMethod = sLines[0].Split(' '); | ||||
|         Method = getMethod(sMethod[0].Trim()); | ||||
|  | ||||
|         if (sMethod.Length == 3) | ||||
|         { | ||||
|             sMethod[1] = WebUtility.UrlDecode(sMethod[1]); | ||||
|             if (sMethod[1].Length >= 7) | ||||
|             { | ||||
|                 if (data[i] == '\r' && data[i + 1] == '\n' | ||||
|                     && data[i + 2] == '\r' && data[i + 3] == '\n') | ||||
|                 if (sMethod[1].StartsWith("http://")) | ||||
|                 { | ||||
|                     sLines = Encoding.ASCII.GetString(data, (int)offset,(int)( i - offset)).Split(new string[] { "\r\n" }, | ||||
|                         StringSplitOptions.None); | ||||
|  | ||||
|                     headerSize = i + 4; | ||||
|                     break; | ||||
|                     sMethod[1] = sMethod[1].Substring(sMethod[1].IndexOf("/", 7)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (headerSize == 0) | ||||
|                 return -1; | ||||
|             URL = sMethod[1].Trim(); | ||||
|  | ||||
|             Cookies = new StringKeyList(); | ||||
|             PostForms = new KeyList<string, object>(); | ||||
|             Query = new StringKeyList(); | ||||
|             Headers = new StringKeyList(); | ||||
|  | ||||
|             sMethod = sLines[0].Split(' '); | ||||
|             Method = getMethod(sMethod[0].Trim()); | ||||
|  | ||||
|             if (sMethod.Length == 3) | ||||
|             if (URL.IndexOf("?", 0) != -1) | ||||
|             { | ||||
|                 sMethod[1] = WebUtility.UrlDecode(sMethod[1]); | ||||
|                 if (sMethod[1].Length >= 7) | ||||
|                 Filename = URL.Split(new char[] { '?' }, 2)[0]; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 Filename = URL; | ||||
|             } | ||||
|  | ||||
|             if (Filename.IndexOf("%", 0) != -1) | ||||
|             { | ||||
|                 Filename = WebUtility.UrlDecode(Filename); | ||||
|             } | ||||
|  | ||||
|             Version = sMethod[2].Trim(); | ||||
|         } | ||||
|  | ||||
|         // Read all headers | ||||
|  | ||||
|         for (int i = 1; i < sLines.Length; i++) | ||||
|         { | ||||
|             if (sLines[i] == String.Empty) | ||||
|             { | ||||
|                 // Invalid header | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             if (sLines[i].IndexOf(':') == -1) | ||||
|             { | ||||
|                 // Invalid header | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             string[] header = sLines[i].Split(new char[] { ':' }, 2); | ||||
|  | ||||
|             header[0] = header[0].ToLower(); | ||||
|             Headers[header[0]] = header[1].Trim(); | ||||
|  | ||||
|             if (header[0] == "cookie") | ||||
|             { | ||||
|                 string[] cookies = header[1].Split(';'); | ||||
|  | ||||
|                 foreach (string cookie in cookies) | ||||
|                 { | ||||
|                     if (sMethod[1].StartsWith("http://")) | ||||
|                     if (cookie.IndexOf('=') != -1) | ||||
|                     { | ||||
|                         sMethod[1] = sMethod[1].Substring(sMethod[1].IndexOf("/", 7)); | ||||
|                         string[] splitCookie = cookie.Split('='); | ||||
|                         splitCookie[0] = splitCookie[0].Trim(); | ||||
|                         splitCookie[1] = splitCookie[1].Trim(); | ||||
|                         if (!(Cookies.ContainsKey(splitCookie[0].Trim()))) | ||||
|                             Cookies.Add(splitCookie[0], splitCookie[1]); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         if (!(Cookies.ContainsKey(cookie.Trim()))) | ||||
|                         { | ||||
|                             Cookies.Add(cookie.Trim(), String.Empty); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|                 URL = sMethod[1].Trim(); | ||||
|  | ||||
|                 if (URL.IndexOf("?", 0) != -1) | ||||
|         // Query String | ||||
|         if (URL.IndexOf("?", 0) != -1) | ||||
|         { | ||||
|             string[] SQ = URL.Split(new char[] { '?' }, 2)[1].Split('&'); | ||||
|             foreach (string S in SQ) | ||||
|             { | ||||
|                 if (S.IndexOf("=", 0) != -1) | ||||
|                 { | ||||
|                     Filename = URL.Split(new char[] { '?' }, 2)[0]; | ||||
|                     string[] qp = S.Split(new char[] { '=' }, 2); | ||||
|  | ||||
|                     if (!Query.ContainsKey(WebUtility.UrlDecode(qp[0]))) | ||||
|                     { | ||||
|                         Query.Add(WebUtility.UrlDecode(qp[0]), WebUtility.UrlDecode(qp[1])); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     Filename = URL; | ||||
|                 } | ||||
|  | ||||
|                 if (Filename.IndexOf("%", 0) != -1) | ||||
|                 { | ||||
|                     Filename = WebUtility.UrlDecode(Filename); | ||||
|                 } | ||||
|  | ||||
|                 Version = sMethod[2].Trim(); | ||||
|             } | ||||
|  | ||||
|             // Read all headers | ||||
|  | ||||
|             for (int i = 1; i < sLines.Length; i++) | ||||
|             { | ||||
|                 if (sLines[i] == String.Empty) | ||||
|                 { | ||||
|                     // Invalid header | ||||
|                     return 0; | ||||
|                 } | ||||
|  | ||||
|                 if (sLines[i].IndexOf(':') == -1) | ||||
|                 { | ||||
|                     // Invalid header | ||||
|                     return 0; | ||||
|                 } | ||||
|  | ||||
|                 string[] header = sLines[i].Split(new char[] { ':' }, 2); | ||||
|  | ||||
|                 header[0] = header[0].ToLower(); | ||||
|                 Headers[header[0]] = header[1].Trim(); | ||||
|  | ||||
|                 if (header[0] == "cookie") | ||||
|                 { | ||||
|                     string[] cookies = header[1].Split(';'); | ||||
|  | ||||
|                     foreach (string cookie in cookies) | ||||
|                     if (!(Query.ContainsKey(WebUtility.UrlDecode(S)))) | ||||
|                     { | ||||
|                         if (cookie.IndexOf('=') != -1) | ||||
|                         Query.Add(WebUtility.UrlDecode(S), null); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Post Content-Length | ||||
|         if (Method == HTTPMethod.POST) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|  | ||||
|                 uint postSize = uint.Parse((string)Headers["content-length"]); | ||||
|  | ||||
|                 // check limit | ||||
|                 if (postSize > data.Length - headerSize) | ||||
|                     return -(postSize - (data.Length - headerSize)); | ||||
|  | ||||
|  | ||||
|                 if ( | ||||
|                     Headers["content-type"] == null | ||||
|                     || Headers["content-type"] == "" | ||||
|                     || Headers["content-type"].StartsWith("application/x-www-form-urlencoded")) | ||||
|                 { | ||||
|                     string[] PostVars = null; | ||||
|                     PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&'); | ||||
|                     for (int J = 0; J < PostVars.Length; J++) | ||||
|                     { | ||||
|                         if (PostVars[J].IndexOf("=") != -1) | ||||
|                         { | ||||
|                             string[] splitCookie = cookie.Split('='); | ||||
|                             splitCookie[0] = splitCookie[0].Trim(); | ||||
|                             splitCookie[1] = splitCookie[1].Trim(); | ||||
|                             if (!(Cookies.ContainsKey(splitCookie[0].Trim()))) | ||||
|                                 Cookies.Add(splitCookie[0], splitCookie[1]); | ||||
|                             string key = WebUtility.HtmlDecode( | ||||
|                                 WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[0])); | ||||
|                             if (PostForms.Contains(key)) | ||||
|                                 PostForms[key] = WebUtility.HtmlDecode( | ||||
|                                     WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1])); | ||||
|                             else | ||||
|                                 PostForms.Add(key, WebUtility.HtmlDecode( | ||||
|                                     WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1]))); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             if (!(Cookies.ContainsKey(cookie.Trim()))) | ||||
|                             { | ||||
|                                 Cookies.Add(cookie.Trim(), String.Empty); | ||||
|                             } | ||||
|                         } | ||||
|                             if (PostForms.Contains("unknown")) | ||||
|                             PostForms["unknown"] = PostForms["unknown"] | ||||
|                                 + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])); | ||||
|                         else | ||||
|                             PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]))); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|                 else if (Headers["content-type"].StartsWith("multipart/form-data")) | ||||
|                 { | ||||
|                     int st = 1; | ||||
|                     int ed = 0; | ||||
|                     string strBoundry = "--" + Headers["content-type"].Substring( | ||||
|                         Headers["content-type"].IndexOf("boundary=", 0) + 9); | ||||
|  | ||||
|             // Query String | ||||
|             if (URL.IndexOf("?", 0) != -1) | ||||
|                     string[] sc = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split( | ||||
|                                                 new string[] { strBoundry }, StringSplitOptions.None); | ||||
|  | ||||
|  | ||||
|                     for (int j = 1; j < sc.Length - 1; j++) | ||||
|                     { | ||||
|                         string[] ps = sc[j].Split(new string[] { "\r\n\r\n" }, 2, StringSplitOptions.None); | ||||
|                         ps[1] = ps[1].Substring(0, ps[1].Length - 2); // remove the empty line | ||||
|                         st = ps[0].IndexOf("name=", 0) + 6; | ||||
|                         ed = ps[0].IndexOf("\"", st); | ||||
|                         PostForms.Add(ps[0].Substring(st, ed - st), ps[1]); | ||||
|                     } | ||||
|                 } | ||||
|                 //else if (Headers["content-type"] == "application/json") | ||||
|                 //{ | ||||
|                 //    var json = DC.Clip(data, headerSize, postSize); | ||||
|                 //} | ||||
|                 else | ||||
|                 { | ||||
|                     //PostForms.Add(Headers["content-type"], Encoding.Default.GetString( )); | ||||
|                     Message = DC.Clip(data, headerSize, postSize); | ||||
|                 } | ||||
|  | ||||
|                 return headerSize + postSize; | ||||
|  | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 string[] SQ = URL.Split(new char[] { '?' }, 2)[1].Split('&'); | ||||
|                 foreach (string S in SQ) | ||||
|                 { | ||||
|                     if (S.IndexOf("=", 0) != -1) | ||||
|                     { | ||||
|                         string[] qp = S.Split(new char[] { '=' }, 2); | ||||
|  | ||||
|                         if (!Query.ContainsKey(WebUtility.UrlDecode(qp[0]))) | ||||
|                         { | ||||
|                             Query.Add(WebUtility.UrlDecode(qp[0]), WebUtility.UrlDecode(qp[1])); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         if (!(Query.ContainsKey(WebUtility.UrlDecode(S)))) | ||||
|                         { | ||||
|                             Query.Add(WebUtility.UrlDecode(S), null); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             // Post Content-Length | ||||
|             if (Method == HTTPMethod.POST) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|  | ||||
|                     uint postSize = uint.Parse((string)Headers["content-length"]); | ||||
|  | ||||
|                     // check limit | ||||
|                     if (postSize > data.Length - headerSize) | ||||
|                         return -(postSize - (data.Length - headerSize)); | ||||
|  | ||||
|  | ||||
|                     if ( | ||||
|                         Headers["content-type"] == null | ||||
|                         || Headers["content-type"] == "" | ||||
|                         || Headers["content-type"].StartsWith("application/x-www-form-urlencoded")) | ||||
|                     { | ||||
|                         string[] PostVars = null; | ||||
|                         PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&'); | ||||
|                         for (int J = 0; J < PostVars.Length; J++) | ||||
|                         { | ||||
|                             if (PostVars[J].IndexOf("=") != -1) | ||||
|                             { | ||||
|                                 string key = WebUtility.HtmlDecode( | ||||
|                                     WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[0])); | ||||
|                                 if (PostForms.Contains(key)) | ||||
|                                     PostForms[key] = WebUtility.HtmlDecode( | ||||
|                                         WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1])); | ||||
|                                 else | ||||
|                                     PostForms.Add(key, WebUtility.HtmlDecode( | ||||
|                                         WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1]))); | ||||
|                             } | ||||
|                             else | ||||
|                                 if (PostForms.Contains("unknown")) | ||||
|                                 PostForms["unknown"] = PostForms["unknown"] | ||||
|                                     + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])); | ||||
|                             else | ||||
|                                 PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]))); | ||||
|                         } | ||||
|                     } | ||||
|                     else if (Headers["content-type"].StartsWith("multipart/form-data")) | ||||
|                     { | ||||
|                         int st = 1; | ||||
|                         int ed = 0; | ||||
|                         string strBoundry = "--" + Headers["content-type"].Substring( | ||||
|                             Headers["content-type"].IndexOf("boundary=", 0) + 9); | ||||
|  | ||||
|                         string[] sc = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split( | ||||
|                                                     new string[] { strBoundry }, StringSplitOptions.None); | ||||
|  | ||||
|  | ||||
|                         for (int j = 1; j < sc.Length - 1; j++) | ||||
|                         { | ||||
|                             string[] ps = sc[j].Split(new string[] { "\r\n\r\n" }, 2, StringSplitOptions.None); | ||||
|                             ps[1] = ps[1].Substring(0, ps[1].Length - 2); // remove the empty line | ||||
|                             st = ps[0].IndexOf("name=", 0) + 6; | ||||
|                             ed = ps[0].IndexOf("\"", st); | ||||
|                             PostForms.Add(ps[0].Substring(st, ed - st), ps[1]); | ||||
|                         } | ||||
|                     } | ||||
|                     //else if (Headers["content-type"] == "application/json") | ||||
|                     //{ | ||||
|                     //    var json = DC.Clip(data, headerSize, postSize); | ||||
|                     //} | ||||
|                     else | ||||
|                     { | ||||
|                         //PostForms.Add(Headers["content-type"], Encoding.Default.GetString( )); | ||||
|                         Message = DC.Clip(data, headerSize, postSize); | ||||
|                     } | ||||
|  | ||||
|                     return headerSize + postSize; | ||||
|  | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     return 0; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return headerSize; | ||||
|         } | ||||
|  | ||||
|         return headerSize; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -27,278 +27,276 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using Esiur.Misc; | ||||
| using Esiur.Data; | ||||
|   | ||||
| namespace Esiur.Net.Packets | ||||
|  | ||||
| namespace Esiur.Net.Packets; | ||||
| public class HTTPResponsePacket : Packet | ||||
| { | ||||
|     public class HTTPResponsePacket : Packet | ||||
|  | ||||
|     public enum ComposeOptions : int | ||||
|     { | ||||
|         AllCalculateLength, | ||||
|         AllDontCalculateLength, | ||||
|         SpecifiedHeadersOnly, | ||||
|         DataOnly | ||||
|     } | ||||
|  | ||||
|         public enum ComposeOptions : int | ||||
|     public enum ResponseCode : int | ||||
|     { | ||||
|         Switching = 101, | ||||
|         OK = 200, | ||||
|         Created = 201, | ||||
|         Accepted = 202, | ||||
|         NoContent = 204, | ||||
|         MovedPermanently = 301, | ||||
|         Found = 302, | ||||
|         SeeOther = 303, | ||||
|         NotModified = 304, | ||||
|         TemporaryRedirect = 307, | ||||
|         BadRequest = 400, | ||||
|         Unauthorized = 401, | ||||
|         Forbidden = 403, | ||||
|         NotFound = 404, | ||||
|         MethodNotAllowed = 405, | ||||
|         NotAcceptable = 406, | ||||
|         PreconditionFailed = 412, | ||||
|         UnsupportedMediaType = 415, | ||||
|         InternalServerError = 500, | ||||
|         NotImplemented = 501, | ||||
|     } | ||||
|  | ||||
|     public struct HTTPCookie | ||||
|     { | ||||
|         public string Name; | ||||
|         public string Value; | ||||
|         public DateTime Expires; | ||||
|         public string Path; | ||||
|         public bool HttpOnly; | ||||
|         public string Domain; | ||||
|  | ||||
|         public HTTPCookie(string name, string value) | ||||
|         { | ||||
|             AllCalculateLength, | ||||
|             AllDontCalculateLength, | ||||
|             SpecifiedHeadersOnly, | ||||
|             DataOnly | ||||
|             this.Name = name; | ||||
|             this.Value = value; | ||||
|             this.Path = null; | ||||
|             this.Expires = DateTime.MinValue; | ||||
|             this.HttpOnly = false; | ||||
|             this.Domain = null; | ||||
|         } | ||||
|  | ||||
|         public enum ResponseCode : int | ||||
|         public HTTPCookie(string name, string value, DateTime expires) | ||||
|         { | ||||
|             Switching= 101, | ||||
|             OK = 200, | ||||
|             Created = 201, | ||||
|             Accepted = 202, | ||||
|             NoContent = 204, | ||||
|             MovedPermanently = 301, | ||||
|             Found = 302, | ||||
|             SeeOther = 303, | ||||
|             NotModified = 304, | ||||
|             TemporaryRedirect = 307, | ||||
|             BadRequest = 400, | ||||
|             Unauthorized = 401, | ||||
|             Forbidden = 403, | ||||
|             NotFound = 404, | ||||
|             MethodNotAllowed = 405, | ||||
|             NotAcceptable = 406, | ||||
|             PreconditionFailed = 412, | ||||
|             UnsupportedMediaType = 415, | ||||
|             InternalServerError = 500, | ||||
|             NotImplemented = 501, | ||||
|             this.Name = name; | ||||
|             this.Value = value; | ||||
|             this.Expires = expires; | ||||
|             this.HttpOnly = false; | ||||
|             this.Domain = null; | ||||
|             this.Path = null; | ||||
|         } | ||||
|  | ||||
|         public struct HTTPCookie | ||||
|         { | ||||
|             public string Name; | ||||
|             public string Value; | ||||
|             public DateTime Expires; | ||||
|             public string Path; | ||||
|             public bool HttpOnly; | ||||
|             public string Domain; | ||||
|              | ||||
|             public HTTPCookie(string name, string value) | ||||
|             { | ||||
|                 this.Name = name; | ||||
|                 this.Value = value; | ||||
|                 this.Path = null; | ||||
|                 this.Expires = DateTime.MinValue; | ||||
|                 this.HttpOnly = false; | ||||
|                 this.Domain = null; | ||||
|             } | ||||
|  | ||||
|             public HTTPCookie(string name, string value, DateTime expires) | ||||
|             { | ||||
|                 this.Name = name; | ||||
|                 this.Value = value; | ||||
|                 this.Expires = expires; | ||||
|                 this.HttpOnly = false; | ||||
|                 this.Domain = null; | ||||
|                 this.Path = null; | ||||
|             } | ||||
|  | ||||
|             public override string ToString() | ||||
|             { | ||||
|                 //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/ | ||||
|                 //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/ | ||||
|                 var cookie = Name + "=" + Value; | ||||
|  | ||||
|                 if (Expires.Ticks != 0) | ||||
|                     cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; | ||||
|  | ||||
|                 if (Domain != null) | ||||
|                     cookie += "; domain=" + Domain; | ||||
|  | ||||
|                 if (Path != null) | ||||
|                     cookie += "; path=" + Path; | ||||
|  | ||||
|                 if (HttpOnly) | ||||
|                     cookie += "; HttpOnly"; | ||||
|  | ||||
|                 return cookie; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public StringKeyList Headers = new StringKeyList(true); | ||||
|         public string Version = "HTTP/1.1"; | ||||
|  | ||||
|         public byte[] Message; | ||||
|         public ResponseCode Number; | ||||
|         public string Text; | ||||
|  | ||||
|         public List<HTTPCookie> Cookies = new List<HTTPCookie>(); | ||||
|         public bool Handled; | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return "HTTPResponsePacket" | ||||
|                 + "\n\tVersion: " + Version | ||||
|                 //+ "\n\tMethod: " + Method | ||||
|                 //+ "\n\tURL: " + URL | ||||
|                 + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL"); | ||||
|             //Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/ | ||||
|             //Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/ | ||||
|             var cookie = Name + "=" + Value; | ||||
|  | ||||
|             if (Expires.Ticks != 0) | ||||
|                 cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; | ||||
|  | ||||
|             if (Domain != null) | ||||
|                 cookie += "; domain=" + Domain; | ||||
|  | ||||
|             if (Path != null) | ||||
|                 cookie += "; path=" + Path; | ||||
|  | ||||
|             if (HttpOnly) | ||||
|                 cookie += "; HttpOnly"; | ||||
|  | ||||
|             return cookie; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public StringKeyList Headers = new StringKeyList(true); | ||||
|     public string Version = "HTTP/1.1"; | ||||
|  | ||||
|     public byte[] Message; | ||||
|     public ResponseCode Number; | ||||
|     public string Text; | ||||
|  | ||||
|     public List<HTTPCookie> Cookies = new List<HTTPCookie>(); | ||||
|     public bool Handled; | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return "HTTPResponsePacket" | ||||
|             + "\n\tVersion: " + Version | ||||
|             //+ "\n\tMethod: " + Method | ||||
|             //+ "\n\tURL: " + URL | ||||
|             + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL"); | ||||
|     } | ||||
|  | ||||
|     private string MakeHeader(ComposeOptions options) | ||||
|     { | ||||
|         string header = $"{Version} {(int)Number} {Text}\r\nServer: Esiur {Global.Version}\r\nDate: {DateTime.Now.ToUniversalTime().ToString("r")}\r\n"; | ||||
|  | ||||
|         if (options == ComposeOptions.AllCalculateLength) | ||||
|             Headers["Content-Length"] = Message?.Length.ToString() ?? "0"; | ||||
|  | ||||
|         foreach (var kv in Headers) | ||||
|             header += kv.Key + ": " + kv.Value + "\r\n"; | ||||
|  | ||||
|  | ||||
|         // Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2007 21:00:00 GMT; path=/ | ||||
|         // Set-Cookie: ASPSESSIONIDQABBDSQA=IPDPMMMALDGFLMICEJIOCIPM; path=/ | ||||
|  | ||||
|         foreach (var Cookie in Cookies) | ||||
|             header += "Set-Cookie: " + Cookie.ToString() + "\r\n"; | ||||
|  | ||||
|  | ||||
|         header += "\r\n"; | ||||
|  | ||||
|         return header; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public bool Compose(ComposeOptions options) | ||||
|     { | ||||
|         List<byte> msg = new List<byte>(); | ||||
|  | ||||
|         if (options != ComposeOptions.DataOnly) | ||||
|         { | ||||
|             msg.AddRange(Encoding.UTF8.GetBytes(MakeHeader(options))); | ||||
|         } | ||||
|  | ||||
|         private string MakeHeader(ComposeOptions options) | ||||
|         if (options != ComposeOptions.SpecifiedHeadersOnly) | ||||
|         { | ||||
|             string header = $"{Version} {(int)Number} {Text}\r\nServer: Esiur {Global.Version}\r\nDate: {DateTime.Now.ToUniversalTime().ToString("r")}\r\n"; | ||||
|  | ||||
|             if (options == ComposeOptions.AllCalculateLength)  | ||||
|                 Headers["Content-Length"] = Message?.Length.ToString() ?? "0"; | ||||
|  | ||||
|             foreach (var kv in Headers) | ||||
|                 header += kv.Key + ": " + kv.Value + "\r\n"; | ||||
|              | ||||
|  | ||||
|             // Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2007 21:00:00 GMT; path=/ | ||||
|             // Set-Cookie: ASPSESSIONIDQABBDSQA=IPDPMMMALDGFLMICEJIOCIPM; path=/ | ||||
|              | ||||
|             foreach (var Cookie in Cookies) | ||||
|                 header += "Set-Cookie: " + Cookie.ToString() + "\r\n"; | ||||
|  | ||||
|  | ||||
|             header += "\r\n"; | ||||
|  | ||||
|             return header; | ||||
|             if (Message != null) | ||||
|                 msg.AddRange(Message); | ||||
|         } | ||||
|  | ||||
|         Data = msg.ToArray(); | ||||
|  | ||||
|         public bool Compose(ComposeOptions options) | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public override bool Compose() | ||||
|     { | ||||
|         return Compose(ComposeOptions.AllDontCalculateLength); | ||||
|     } | ||||
|  | ||||
|     public override long Parse(byte[] data, uint offset, uint ends) | ||||
|     { | ||||
|         string[] sMethod = null; | ||||
|         string[] sLines = null; | ||||
|  | ||||
|         uint headerSize = 0; | ||||
|  | ||||
|         for (uint i = offset; i < ends - 3; i++) | ||||
|         { | ||||
|             List<byte> msg = new List<byte>(); | ||||
|  | ||||
|             if (options != ComposeOptions.DataOnly) | ||||
|             if (data[i] == '\r' && data[i + 1] == '\n' | ||||
|                 && data[i + 2] == '\r' && data[i + 3] == '\n') | ||||
|             { | ||||
|                 msg.AddRange(Encoding.UTF8.GetBytes(MakeHeader(options))); | ||||
|             } | ||||
|                 sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" }, | ||||
|                     StringSplitOptions.None); | ||||
|  | ||||
|             if (options != ComposeOptions.SpecifiedHeadersOnly) | ||||
|             { | ||||
|                 if (Message != null) | ||||
|                    msg.AddRange(Message); | ||||
|                 headerSize = i + 4; | ||||
|                 break; | ||||
|             } | ||||
|              | ||||
|             Data = msg.ToArray(); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public override bool Compose() | ||||
|         if (headerSize == 0) | ||||
|             return -1; | ||||
|  | ||||
|         //Cookies = new DStringDictionary(); | ||||
|         //Headers = new DStringDictionary(true); | ||||
|  | ||||
|         sMethod = sLines[0].Split(' '); | ||||
|  | ||||
|         if (sMethod.Length == 3) | ||||
|         { | ||||
|             return Compose(ComposeOptions.AllDontCalculateLength); | ||||
|             Version = sMethod[0].Trim(); | ||||
|             Number = (ResponseCode)(Convert.ToInt32(sMethod[1].Trim())); | ||||
|             Text = sMethod[2]; | ||||
|         } | ||||
|  | ||||
|         public override long Parse(byte[] data, uint offset, uint ends) | ||||
|         // Read all headers | ||||
|  | ||||
|         for (int i = 1; i < sLines.Length; i++) | ||||
|         { | ||||
|             string[] sMethod = null; | ||||
|             string[] sLines = null; | ||||
|  | ||||
|             uint headerSize = 0; | ||||
|  | ||||
|             for (uint i = offset; i < ends - 3; i++) | ||||
|             if (sLines[i] == String.Empty) | ||||
|             { | ||||
|                 if (data[i] == '\r' && data[i + 1] == '\n' | ||||
|                     && data[i + 2] == '\r' && data[i + 3] == '\n') | ||||
|                 { | ||||
|                     sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" }, | ||||
|                         StringSplitOptions.None); | ||||
|  | ||||
|                     headerSize = i + 4; | ||||
|                     break; | ||||
|                 } | ||||
|                 // Invalid header | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             if (headerSize == 0) | ||||
|                 return -1; | ||||
|  | ||||
|             //Cookies = new DStringDictionary(); | ||||
|             //Headers = new DStringDictionary(true); | ||||
|  | ||||
|             sMethod = sLines[0].Split(' '); | ||||
|  | ||||
|             if (sMethod.Length == 3) | ||||
|             if (sLines[i].IndexOf(':') == -1) | ||||
|             { | ||||
|                 Version = sMethod[0].Trim(); | ||||
|                 Number = (ResponseCode)(Convert.ToInt32(sMethod[1].Trim())); | ||||
|                 Text = sMethod[2]; | ||||
|                 // Invalid header | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             // Read all headers | ||||
|             string[] header = sLines[i].Split(new char[] { ':' }, 2); | ||||
|  | ||||
|             for (int i = 1; i < sLines.Length; i++) | ||||
|             header[0] = header[0].ToLower(); | ||||
|             Headers[header[0]] = header[1].Trim(); | ||||
|  | ||||
|             //Set-Cookie: NAME=VALUE; expires=DATE; | ||||
|  | ||||
|             if (header[0] == "set-cookie") | ||||
|             { | ||||
|                 if (sLines[i] == String.Empty) | ||||
|                 string[] cookie = header[1].Split(';'); | ||||
|  | ||||
|                 if (cookie.Length >= 1) | ||||
|                 { | ||||
|                     // Invalid header | ||||
|                     return 0; | ||||
|                 } | ||||
|                     string[] splitCookie = cookie[0].Split('='); | ||||
|                     HTTPCookie c = new HTTPCookie(splitCookie[0], splitCookie[1]); | ||||
|  | ||||
|                 if (sLines[i].IndexOf(':') == -1) | ||||
|                 { | ||||
|                     // Invalid header | ||||
|                     return 0; | ||||
|                 } | ||||
|  | ||||
|                 string[] header = sLines[i].Split(new char[] { ':' }, 2); | ||||
|  | ||||
|                 header[0] = header[0].ToLower(); | ||||
|                 Headers[header[0]] = header[1].Trim(); | ||||
|  | ||||
|                 //Set-Cookie: NAME=VALUE; expires=DATE; | ||||
|  | ||||
|                 if (header[0] == "set-cookie") | ||||
|                 { | ||||
|                     string[] cookie = header[1].Split(';'); | ||||
|  | ||||
|                     if (cookie.Length >= 1) | ||||
|                     for (int j = 1; j < cookie.Length; j++) | ||||
|                     { | ||||
|                         string[] splitCookie = cookie[0].Split('='); | ||||
|                         HTTPCookie c = new HTTPCookie(splitCookie[0], splitCookie[1]); | ||||
|  | ||||
|                         for (int j = 1; j < cookie.Length; j++) | ||||
|                         splitCookie = cookie[j].Split('='); | ||||
|                         switch (splitCookie[0].ToLower()) | ||||
|                         { | ||||
|                             splitCookie = cookie[j].Split('='); | ||||
|                             switch (splitCookie[0].ToLower()) | ||||
|                             { | ||||
|                                 case "domain": | ||||
|                                     c.Domain = splitCookie[1]; | ||||
|                                     break; | ||||
|                                 case "path": | ||||
|                                     c.Path = splitCookie[1]; | ||||
|                                     break; | ||||
|                                 case "httponly": | ||||
|                                     c.HttpOnly = true; | ||||
|                                     break; | ||||
|                                 case "expires": | ||||
|                                     // Wed, 13-Jan-2021 22:23:01 GMT | ||||
|                                     c.Expires = DateTime.Parse(splitCookie[1]); | ||||
|                                     break; | ||||
|                             } | ||||
|                             case "domain": | ||||
|                                 c.Domain = splitCookie[1]; | ||||
|                                 break; | ||||
|                             case "path": | ||||
|                                 c.Path = splitCookie[1]; | ||||
|                                 break; | ||||
|                             case "httponly": | ||||
|                                 c.HttpOnly = true; | ||||
|                                 break; | ||||
|                             case "expires": | ||||
|                                 // Wed, 13-Jan-2021 22:23:01 GMT | ||||
|                                 c.Expires = DateTime.Parse(splitCookie[1]); | ||||
|                                 break; | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|             // Content-Length | ||||
|         // Content-Length | ||||
|  | ||||
|             try | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             uint contentLength = uint.Parse((string)Headers["content-length"]); | ||||
|  | ||||
|             // check limit | ||||
|             if (contentLength > data.Length - headerSize) | ||||
|             { | ||||
|  | ||||
|                 uint contentLength = uint.Parse((string)Headers["content-length"]); | ||||
|  | ||||
|                 // check limit | ||||
|                 if (contentLength > data.Length - headerSize) | ||||
|                 { | ||||
|                     return contentLength - (data.Length - headerSize); | ||||
|                 } | ||||
|  | ||||
|                 Message = DC.Clip(data, offset, contentLength); | ||||
|  | ||||
|                 return headerSize + contentLength; | ||||
|  | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return 0; | ||||
|                 return contentLength - (data.Length - headerSize); | ||||
|             } | ||||
|  | ||||
|             Message = DC.Clip(data, offset, contentLength); | ||||
|  | ||||
|             return headerSize + contentLength; | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,385 +31,384 @@ using System.Security.Cryptography; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Net.Packets | ||||
| namespace Esiur.Net.Packets; | ||||
| class IIPAuthPacket : Packet | ||||
| { | ||||
|     class IIPAuthPacket : Packet | ||||
|     public enum IIPAuthPacketCommand : byte | ||||
|     { | ||||
|         public enum IIPAuthPacketCommand : byte | ||||
|         Action = 0, | ||||
|         Declare, | ||||
|         Acknowledge, | ||||
|         Error, | ||||
|     } | ||||
|  | ||||
|     public enum IIPAuthPacketAction : byte | ||||
|     { | ||||
|         // Authenticate | ||||
|         AuthenticateHash, | ||||
|  | ||||
|  | ||||
|         //Challenge, | ||||
|         //CertificateRequest, | ||||
|         //CertificateReply, | ||||
|         //EstablishRequest, | ||||
|         //EstablishReply | ||||
|  | ||||
|         NewConnection = 0x20, | ||||
|         ResumeConnection, | ||||
|  | ||||
|         ConnectionEstablished = 0x28 | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     public IIPAuthPacketCommand Command | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|     public IIPAuthPacketAction Action | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte ErrorCode { get; set; } | ||||
|     public string ErrorMessage { get; set; } | ||||
|  | ||||
|     public AuthenticationMethod LocalMethod | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] SourceInfo | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] Hash | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] SessionId | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public AuthenticationMethod RemoteMethod | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public string Domain | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public long CertificateId | ||||
|     { | ||||
|         get; set; | ||||
|     } | ||||
|  | ||||
|     public string LocalUsername | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public string RemoteUsername | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] LocalPassword | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|     public byte[] RemotePassword | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] LocalToken | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] RemoteToken | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] AsymetricEncryptionKey | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] LocalNonce | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public byte[] RemoteNonce | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     public ulong RemoteTokenIndex { get; set; } | ||||
|  | ||||
|     private uint dataLengthNeeded; | ||||
|  | ||||
|     bool NotEnough(uint offset, uint ends, uint needed) | ||||
|     { | ||||
|         if (offset + needed > ends) | ||||
|         { | ||||
|             Action = 0, | ||||
|             Declare, | ||||
|             Acknowledge, | ||||
|             Error, | ||||
|             dataLengthNeeded = needed - (ends - offset); | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|         public enum IIPAuthPacketAction : byte | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return Command.ToString() + " " + Action.ToString(); | ||||
|     } | ||||
|  | ||||
|     public override long Parse(byte[] data, uint offset, uint ends) | ||||
|     { | ||||
|         var oOffset = offset; | ||||
|  | ||||
|         if (NotEnough(offset, ends, 1)) | ||||
|             return -dataLengthNeeded; | ||||
|  | ||||
|         Command = (IIPAuthPacketCommand)(data[offset] >> 6); | ||||
|  | ||||
|         if (Command == IIPAuthPacketCommand.Action) | ||||
|         { | ||||
|             // Authenticate | ||||
|             AuthenticateHash, | ||||
|             Action = (IIPAuthPacketAction)(data[offset++] & 0x3f); | ||||
|  | ||||
|  | ||||
|             //Challenge, | ||||
|             //CertificateRequest, | ||||
|             //CertificateReply, | ||||
|             //EstablishRequest, | ||||
|             //EstablishReply | ||||
|  | ||||
|             NewConnection = 0x20, | ||||
|             ResumeConnection, | ||||
|  | ||||
|             ConnectionEstablished = 0x28 | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         public IIPAuthPacketCommand Command | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|         public IIPAuthPacketAction Action | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte ErrorCode { get; set; } | ||||
|         public string ErrorMessage { get; set; } | ||||
|  | ||||
|         public AuthenticationMethod LocalMethod | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] SourceInfo | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] Hash | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] SessionId | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public AuthenticationMethod RemoteMethod | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public string Domain | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public long CertificateId | ||||
|         { | ||||
|             get; set; | ||||
|         } | ||||
|  | ||||
|         public string LocalUsername | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public string RemoteUsername | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] LocalPassword | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|         public byte[] RemotePassword | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] LocalToken | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] RemoteToken | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] AsymetricEncryptionKey | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] LocalNonce | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public byte[] RemoteNonce | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         public ulong RemoteTokenIndex { get; set; } | ||||
|  | ||||
|         private uint dataLengthNeeded; | ||||
|  | ||||
|         bool NotEnough(uint offset, uint ends, uint needed) | ||||
|         { | ||||
|             if (offset + needed > ends) | ||||
|             if (Action == IIPAuthPacketAction.AuthenticateHash) | ||||
|             { | ||||
|                 dataLengthNeeded = needed - (ends - offset); | ||||
|                 return true; | ||||
|                 if (NotEnough(offset, ends, 32)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 Hash = data.Clip(offset, 32); | ||||
|  | ||||
|                 //var hash = new byte[32]; | ||||
|                 //Buffer.BlockCopy(data, (int)offset, hash, 0, 32); | ||||
|                 //Hash = hash; | ||||
|  | ||||
|                 offset += 32; | ||||
|             } | ||||
|             else | ||||
|                 return false; | ||||
|         } | ||||
|             else if (Action == IIPAuthPacketAction.NewConnection) | ||||
|             { | ||||
|                 if (NotEnough(offset, ends, 2)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return Command.ToString() + " " + Action.ToString(); | ||||
|         } | ||||
|                 var length = data.GetUInt16(offset); | ||||
|  | ||||
|         public override long Parse(byte[] data, uint offset, uint ends) | ||||
|                 offset += 2; | ||||
|  | ||||
|                 if (NotEnough(offset, ends, length)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 SourceInfo = data.Clip(offset, length); | ||||
|  | ||||
|                 //var sourceInfo = new byte[length]; | ||||
|                 //Buffer.BlockCopy(data, (int)offset, sourceInfo, 0, length); | ||||
|                 //SourceInfo = sourceInfo; | ||||
|  | ||||
|                 offset += 32; | ||||
|             } | ||||
|             else if (Action == IIPAuthPacketAction.ResumeConnection | ||||
|                  || Action == IIPAuthPacketAction.ConnectionEstablished) | ||||
|             { | ||||
|                 //var sessionId = new byte[32]; | ||||
|  | ||||
|                 if (NotEnough(offset, ends, 32)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 SessionId = data.Clip(offset, 32); | ||||
|  | ||||
|                 //Buffer.BlockCopy(data, (int)offset, sessionId, 0, 32); | ||||
|                 //SessionId = sessionId; | ||||
|  | ||||
|                 offset += 32; | ||||
|             } | ||||
|         } | ||||
|         else if (Command == IIPAuthPacketCommand.Declare) | ||||
|         { | ||||
|             var oOffset = offset; | ||||
|             RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3); | ||||
|             LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3); | ||||
|             var encrypt = ((data[offset++] & 0x2) == 0x2); | ||||
|  | ||||
|  | ||||
|             if (NotEnough(offset, ends, 1)) | ||||
|                 return -dataLengthNeeded; | ||||
|  | ||||
|             Command = (IIPAuthPacketCommand)(data[offset] >> 6); | ||||
|             var domainLength = data[offset++]; | ||||
|             if (NotEnough(offset, ends, domainLength)) | ||||
|                 return -dataLengthNeeded; | ||||
|  | ||||
|             if (Command == IIPAuthPacketCommand.Action) | ||||
|             var domain = data.GetString(offset, domainLength); | ||||
|  | ||||
|             Domain = domain; | ||||
|  | ||||
|             offset += domainLength; | ||||
|  | ||||
|  | ||||
|             if (RemoteMethod == AuthenticationMethod.Credentials) | ||||
|             { | ||||
|                 Action = (IIPAuthPacketAction)(data[offset++] & 0x3f); | ||||
|  | ||||
|                 if (Action == IIPAuthPacketAction.AuthenticateHash) | ||||
|                 if (LocalMethod == AuthenticationMethod.None) | ||||
|                 { | ||||
|                     if (NotEnough(offset, ends, 32)) | ||||
|                     if (NotEnough(offset, ends, 33)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     Hash = data.Clip(offset, 32); | ||||
|                     //var remoteNonce = new byte[32]; | ||||
|                     //Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32); | ||||
|                     //RemoteNonce = remoteNonce; | ||||
|  | ||||
|                     //var hash = new byte[32]; | ||||
|                     //Buffer.BlockCopy(data, (int)offset, hash, 0, 32); | ||||
|                     //Hash = hash; | ||||
|                     RemoteNonce = data.Clip(offset, 32); | ||||
|  | ||||
|                     offset += 32; | ||||
|                 } | ||||
|                 else if (Action == IIPAuthPacketAction.NewConnection) | ||||
|                 { | ||||
|                     if (NotEnough(offset, ends, 2)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     var length = data.GetUInt16(offset); | ||||
|  | ||||
|                     offset += 2; | ||||
|                     var length = data[offset++]; | ||||
|  | ||||
|                     if (NotEnough(offset, ends, length)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     SourceInfo = data.Clip(offset, length); | ||||
|                     RemoteUsername = data.GetString(offset, length); | ||||
|  | ||||
|                     //var sourceInfo = new byte[length]; | ||||
|                     //Buffer.BlockCopy(data, (int)offset, sourceInfo, 0, length); | ||||
|                     //SourceInfo = sourceInfo; | ||||
|  | ||||
|                     offset += length; | ||||
|                 } | ||||
|             } | ||||
|             else if (RemoteMethod == AuthenticationMethod.Token) | ||||
|             { | ||||
|                 if (LocalMethod == AuthenticationMethod.None) | ||||
|                 { | ||||
|                     if (NotEnough(offset, ends, 37)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     RemoteNonce = data.Clip(offset, 32); | ||||
|  | ||||
|                     offset += 32; | ||||
|                 } | ||||
|                 else if (Action == IIPAuthPacketAction.ResumeConnection | ||||
|                      || Action == IIPAuthPacketAction.ConnectionEstablished) | ||||
|                 { | ||||
|                     //var sessionId = new byte[32]; | ||||
|  | ||||
|                     RemoteTokenIndex = data.GetUInt64(offset); | ||||
|                     offset += 8; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (encrypt) | ||||
|             { | ||||
|                 if (NotEnough(offset, ends, 2)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 var keyLength = data.GetUInt16(offset); | ||||
|  | ||||
|                 offset += 2; | ||||
|  | ||||
|                 if (NotEnough(offset, ends, keyLength)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 //var key = new byte[keyLength]; | ||||
|                 //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength); | ||||
|                 //AsymetricEncryptionKey = key; | ||||
|  | ||||
|                 AsymetricEncryptionKey = data.Clip(offset, keyLength); | ||||
|  | ||||
|                 offset += keyLength; | ||||
|             } | ||||
|         } | ||||
|         else if (Command == IIPAuthPacketCommand.Acknowledge) | ||||
|         { | ||||
|             RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3); | ||||
|             LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3); | ||||
|             var encrypt = ((data[offset++] & 0x2) == 0x2); | ||||
|  | ||||
|             if (RemoteMethod == AuthenticationMethod.None) | ||||
|             { | ||||
|                 if (LocalMethod == AuthenticationMethod.None) | ||||
|                 { | ||||
|                     // do nothing | ||||
|                 } | ||||
|             } | ||||
|             else if (RemoteMethod == AuthenticationMethod.Credentials | ||||
|                    || RemoteMethod == AuthenticationMethod.Token) | ||||
|             { | ||||
|                 if (LocalMethod == AuthenticationMethod.None) | ||||
|                 { | ||||
|                     if (NotEnough(offset, ends, 32)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     SessionId = data.Clip(offset, 32); | ||||
|  | ||||
|                     //Buffer.BlockCopy(data, (int)offset, sessionId, 0, 32); | ||||
|                     //SessionId = sessionId; | ||||
|  | ||||
|                     RemoteNonce = data.Clip(offset, 32); | ||||
|                     offset += 32; | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else if (Command == IIPAuthPacketCommand.Declare) | ||||
|  | ||||
|             if (encrypt) | ||||
|             { | ||||
|                 RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3); | ||||
|                 LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3); | ||||
|                 var encrypt = ((data[offset++] & 0x2) == 0x2); | ||||
|  | ||||
|  | ||||
|                 if (NotEnough(offset, ends, 1)) | ||||
|                 if (NotEnough(offset, ends, 2)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 var domainLength = data[offset++]; | ||||
|                 if (NotEnough(offset, ends, domainLength)) | ||||
|                     return -dataLengthNeeded; | ||||
|                 var keyLength = data.GetUInt16(offset); | ||||
|  | ||||
|                 var domain = data.GetString(offset, domainLength); | ||||
|  | ||||
|                 Domain = domain; | ||||
|  | ||||
|                 offset += domainLength; | ||||
|  | ||||
|  | ||||
|                 if (RemoteMethod == AuthenticationMethod.Credentials) | ||||
|                 { | ||||
|                     if (LocalMethod == AuthenticationMethod.None) | ||||
|                     { | ||||
|                         if (NotEnough(offset, ends, 33)) | ||||
|                             return -dataLengthNeeded; | ||||
|  | ||||
|                         //var remoteNonce = new byte[32]; | ||||
|                         //Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32); | ||||
|                         //RemoteNonce = remoteNonce; | ||||
|  | ||||
|                         RemoteNonce = data.Clip(offset, 32); | ||||
|  | ||||
|                         offset += 32; | ||||
|  | ||||
|                         var length = data[offset++]; | ||||
|  | ||||
|                         if (NotEnough(offset, ends, length)) | ||||
|                             return -dataLengthNeeded; | ||||
|  | ||||
|                         RemoteUsername = data.GetString(offset, length); | ||||
|  | ||||
|  | ||||
|                         offset += length; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (RemoteMethod == AuthenticationMethod.Token) | ||||
|                 { | ||||
|                     if (LocalMethod == AuthenticationMethod.None) | ||||
|                     { | ||||
|                         if (NotEnough(offset, ends, 37)) | ||||
|                             return -dataLengthNeeded; | ||||
|  | ||||
|                         RemoteNonce = data.Clip(offset, 32); | ||||
|  | ||||
|                         offset += 32; | ||||
|  | ||||
|                         RemoteTokenIndex = data.GetUInt64(offset); | ||||
|                         offset += 8; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (encrypt) | ||||
|                 { | ||||
|                     if (NotEnough(offset, ends, 2)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     var keyLength = data.GetUInt16(offset); | ||||
|  | ||||
|                     offset += 2; | ||||
|  | ||||
|                     if (NotEnough(offset, ends, keyLength)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     //var key = new byte[keyLength]; | ||||
|                     //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength); | ||||
|                     //AsymetricEncryptionKey = key; | ||||
|  | ||||
|                     AsymetricEncryptionKey = data.Clip(offset, keyLength); | ||||
|  | ||||
|                     offset += keyLength; | ||||
|                 } | ||||
|             } | ||||
|             else if (Command == IIPAuthPacketCommand.Acknowledge) | ||||
|             { | ||||
|                 RemoteMethod = (AuthenticationMethod)((data[offset] >> 4) & 0x3); | ||||
|                 LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3); | ||||
|                 var encrypt = ((data[offset++] & 0x2) == 0x2); | ||||
|  | ||||
|                 if (RemoteMethod == AuthenticationMethod.None) | ||||
|                 { | ||||
|                     if (LocalMethod == AuthenticationMethod.None) | ||||
|                     { | ||||
|                         // do nothing | ||||
|                     } | ||||
|                 } | ||||
|                 else if (RemoteMethod == AuthenticationMethod.Credentials | ||||
|                        || RemoteMethod == AuthenticationMethod.Token) | ||||
|                 { | ||||
|                     if (LocalMethod == AuthenticationMethod.None) | ||||
|                     { | ||||
|                         if (NotEnough(offset, ends, 32)) | ||||
|                             return -dataLengthNeeded; | ||||
|  | ||||
|                         RemoteNonce = data.Clip(offset, 32); | ||||
|                         offset += 32; | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (encrypt) | ||||
|                 { | ||||
|                     if (NotEnough(offset, ends, 2)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     var keyLength = data.GetUInt16(offset); | ||||
|  | ||||
|                     offset += 2; | ||||
|  | ||||
|                     if (NotEnough(offset, ends, keyLength)) | ||||
|                         return -dataLengthNeeded; | ||||
|  | ||||
|                     //var key = new byte[keyLength]; | ||||
|                     //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength); | ||||
|                     //AsymetricEncryptionKey = key; | ||||
|  | ||||
|                     AsymetricEncryptionKey = data.Clip(offset, keyLength); | ||||
|  | ||||
|                     offset += keyLength; | ||||
|                 } | ||||
|             } | ||||
|             else if (Command == IIPAuthPacketCommand.Error) | ||||
|             { | ||||
|                 if (NotEnough(offset, ends, 4)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 offset++; | ||||
|                 ErrorCode = data[offset++]; | ||||
|  | ||||
|  | ||||
|                 var cl = data.GetUInt16(offset); | ||||
|                 offset += 2; | ||||
|  | ||||
|                 if (NotEnough(offset, ends, cl)) | ||||
|                 if (NotEnough(offset, ends, keyLength)) | ||||
|                     return -dataLengthNeeded; | ||||
|  | ||||
|                 ErrorMessage = data.GetString(offset, cl); | ||||
|                 offset += cl; | ||||
|                 //var key = new byte[keyLength]; | ||||
|                 //Buffer.BlockCopy(data, (int)offset, key, 0, keyLength); | ||||
|                 //AsymetricEncryptionKey = key; | ||||
|  | ||||
|                 AsymetricEncryptionKey = data.Clip(offset, keyLength); | ||||
|  | ||||
|                 offset += keyLength; | ||||
|             } | ||||
|         } | ||||
|         else if (Command == IIPAuthPacketCommand.Error) | ||||
|         { | ||||
|             if (NotEnough(offset, ends, 4)) | ||||
|                 return -dataLengthNeeded; | ||||
|  | ||||
|             offset++; | ||||
|             ErrorCode = data[offset++]; | ||||
|  | ||||
|  | ||||
|             return offset - oOffset; | ||||
|             var cl = data.GetUInt16(offset); | ||||
|             offset += 2; | ||||
|  | ||||
|             if (NotEnough(offset, ends, cl)) | ||||
|                 return -dataLengthNeeded; | ||||
|  | ||||
|             ErrorMessage = data.GetString(offset, cl); | ||||
|             offset += cl; | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|         return offset - oOffset; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,21 +2,20 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Net.Packets | ||||
| { | ||||
|     struct IIPPacketAttachInfo | ||||
|     { | ||||
|         public string Link; | ||||
|         public ulong Age; | ||||
|         public byte[] Content; | ||||
|         public Guid ClassId; | ||||
| namespace Esiur.Net.Packets; | ||||
|  | ||||
|         public IIPPacketAttachInfo(Guid classId, ulong age, string link, byte[] content) | ||||
|         { | ||||
|             ClassId = classId; | ||||
|             Age = age; | ||||
|             Content = content; | ||||
|             Link = link; | ||||
|         } | ||||
| struct IIPPacketAttachInfo | ||||
| { | ||||
|     public string Link; | ||||
|     public ulong Age; | ||||
|     public byte[] Content; | ||||
|     public Guid ClassId; | ||||
|  | ||||
|     public IIPPacketAttachInfo(Guid classId, ulong age, string link, byte[] content) | ||||
|     { | ||||
|         ClassId = classId; | ||||
|         Age = age; | ||||
|         Content = content; | ||||
|         Link = link; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -38,238 +38,219 @@ using Esiur.Net.DataLink; | ||||
| using System.Net.NetworkInformation; | ||||
| using Esiur.Data; | ||||
|  | ||||
| namespace Esiur.Net.Packets | ||||
| namespace Esiur.Net.Packets; | ||||
| internal static class Functions | ||||
| { | ||||
|     internal static class Functions | ||||
|     public static void AddData(ref byte[] dest, byte[] src) | ||||
|     { | ||||
|         public static void AddData(ref byte[] dest, byte[] src) | ||||
|         int I = 0; | ||||
|         if (src == null) | ||||
|         { | ||||
|             int I = 0; | ||||
|             if (src == null) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             if (dest != null) | ||||
|             { | ||||
|                 I = dest.Length; | ||||
|                 Array.Resize(ref dest, dest.Length + src.Length); | ||||
|                 //dest = (byte[])Resize(dest, dest.Length + src.Length); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 dest = new byte[src.Length]; | ||||
|             } | ||||
|             Array.Copy(src, 0, dest, I, src.Length); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|         public static Array Resize(Array array, int newSize) | ||||
|         if (dest != null) | ||||
|         { | ||||
|             Type myType = Type.GetType(array.GetType().FullName.TrimEnd('[', ']')); | ||||
|             Array nA = Array.CreateInstance(myType, newSize); | ||||
|             Array.Copy(array, nA, (newSize > array.Length ? array.Length : newSize)); | ||||
|             return nA; | ||||
|         } */ | ||||
|  | ||||
|         //Computes the checksum used in IP, ARP..., ie the | ||||
|         // "The 16 bit one's complement of the one 's complement sum | ||||
|         //of all 16 bit words" as seen in RFCs | ||||
|         // Returns a 4 characters hex string | ||||
|         // data's lenght must be multiple of 4, else zero padding | ||||
|         public static ushort IP_CRC16(byte[] data) | ||||
|         { | ||||
|             ulong Sum = 0; | ||||
|             bool Padding = false; | ||||
|             /// * Padding if needed | ||||
|             if (data.Length % 2 != 0) | ||||
|             { | ||||
|                 Array.Resize(ref data, data.Length + 1); | ||||
|                 //data = (byte[])Resize(data, data.Length + 1); | ||||
|                 Padding = true; | ||||
|             } | ||||
|             int count = data.Length; | ||||
|             ///* add 16-bit words */ | ||||
|             while (count > 0) //1) | ||||
|             { | ||||
|                 ///*  this is the inner loop  */  | ||||
|                 Sum += GetInteger(data[count - 2], data[count - 1]); | ||||
|                 ///*  Fold 32-bit sum to 16-bit  */  | ||||
|                 while (Sum >> 16 != 0) | ||||
|                 { | ||||
|                     Sum = (Sum & 0XFFFF) + (Sum >> 16); | ||||
|                 } | ||||
|                 count -= 2; | ||||
|             } | ||||
|             /// * reverse padding  | ||||
|             if (Padding) | ||||
|             { | ||||
|                 Array.Resize(ref data, data.Length - 1); | ||||
|                 //data = (byte[])Resize(data, data.Length - 1); | ||||
|             } | ||||
|             ///* Return one's compliment of final sum.  | ||||
|             //return (ushort)(ushort.MaxValue - (ushort)Sum); | ||||
|             return (ushort)(~Sum); | ||||
|             I = dest.Length; | ||||
|             Array.Resize(ref dest, dest.Length + src.Length); | ||||
|             //dest = (byte[])Resize(dest, dest.Length + src.Length); | ||||
|         } | ||||
|  | ||||
|         public static ushort GetInteger(byte B1, byte B2) | ||||
|         else | ||||
|         { | ||||
|             return BitConverter.ToUInt16(new byte[] { B2, B1 }, 0); | ||||
|             //return System.Convert.ToUInt16("&h" + GetHex(B1) + GetHex(B2)); | ||||
|             dest = new byte[src.Length]; | ||||
|         } | ||||
|  | ||||
|         public static uint GetLong(byte B1, byte B2, byte B3, byte B4) | ||||
|         { | ||||
|             return BitConverter.ToUInt32(new byte[] { B4, B3, B2, B1 }, 0); | ||||
|             //return System.Convert.ToUInt32("&h" + GetHex(B1) + GetHex(B2) + GetHex(B3) + GetHex(B4)); | ||||
|         } | ||||
|  | ||||
|         public static string GetHex(byte B) | ||||
|         { | ||||
|             return (((B < 15) ? 0 + System.Convert.ToString(B, 16).ToUpper() : System.Convert.ToString(B, 16).ToUpper())); | ||||
|         } | ||||
|  | ||||
|         public static bool GetBit(uint B, byte Pos) | ||||
|         { | ||||
|             //return BitConverter.ToBoolean(BitConverter.GetBytes(B), Pos + 1); | ||||
|             return (B & (uint)(Math.Pow(2, (Pos - 1)))) == (Math.Pow(2, (Pos - 1))); | ||||
|         } | ||||
|  | ||||
|         public static ushort RemoveBit(ushort I, byte Pos) | ||||
|         { | ||||
|             return (ushort)RemoveBit((uint)I, Pos); | ||||
|         } | ||||
|  | ||||
|         public static uint RemoveBit(uint I, byte Pos) | ||||
|         { | ||||
|             if (GetBit(I, Pos)) | ||||
|             { | ||||
|                 return I - (uint)(Math.Pow(2, (Pos - 1))); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return I; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static void SplitInteger(ushort I, ref byte BLeft, ref byte BRight) | ||||
|         { | ||||
|             byte[] b = BitConverter.GetBytes(I); | ||||
|             BLeft = b[1]; | ||||
|             BRight = b[0]; | ||||
|             //BLeft = I >> 8; | ||||
|             //BRight = (I << 8) >> 8; | ||||
|         } | ||||
|  | ||||
|         public static void SplitLong(uint I, ref byte BLeft, ref byte BLeftMiddle, ref byte BRightMiddle, ref byte BRight) | ||||
|         { | ||||
|             byte[] b = BitConverter.GetBytes(I); | ||||
|             BLeft = b[3]; | ||||
|             BLeftMiddle = b[2]; | ||||
|             BRightMiddle = b[1]; | ||||
|             BRight = b[0]; | ||||
|             //BLeft = I >> 24; | ||||
|             //BLeftMiddle = (I << 8) >> 24; | ||||
|             //BRightMiddle = (I << 16) >> 24; | ||||
|             //BRight = (I << 24) >> 24; | ||||
|         } | ||||
|  | ||||
|         Array.Copy(src, 0, dest, I, src.Length); | ||||
|     } | ||||
|  | ||||
|     public class PosixTime | ||||
|     /* | ||||
|     public static Array Resize(Array array, int newSize) | ||||
|     { | ||||
|         ulong seconds; | ||||
|         ulong microseconds; | ||||
|         Type myType = Type.GetType(array.GetType().FullName.TrimEnd('[', ']')); | ||||
|         Array nA = Array.CreateInstance(myType, newSize); | ||||
|         Array.Copy(array, nA, (newSize > array.Length ? array.Length : newSize)); | ||||
|         return nA; | ||||
|     } */ | ||||
|  | ||||
|         PosixTime(ulong Seconds, ulong Microseconds) | ||||
|     //Computes the checksum used in IP, ARP..., ie the | ||||
|     // "The 16 bit one's complement of the one 's complement sum | ||||
|     //of all 16 bit words" as seen in RFCs | ||||
|     // Returns a 4 characters hex string | ||||
|     // data's lenght must be multiple of 4, else zero padding | ||||
|     public static ushort IP_CRC16(byte[] data) | ||||
|     { | ||||
|         ulong Sum = 0; | ||||
|         bool Padding = false; | ||||
|         /// * Padding if needed | ||||
|         if (data.Length % 2 != 0) | ||||
|         { | ||||
|             seconds = Seconds; | ||||
|             microseconds = Microseconds; | ||||
|             Array.Resize(ref data, data.Length + 1); | ||||
|             //data = (byte[])Resize(data, data.Length + 1); | ||||
|             Padding = true; | ||||
|         } | ||||
|  | ||||
|         public override string ToString() | ||||
|         int count = data.Length; | ||||
|         ///* add 16-bit words */ | ||||
|         while (count > 0) //1) | ||||
|         { | ||||
|             return seconds + "." + microseconds; | ||||
|             ///*  this is the inner loop  */  | ||||
|             Sum += GetInteger(data[count - 2], data[count - 1]); | ||||
|             ///*  Fold 32-bit sum to 16-bit  */  | ||||
|             while (Sum >> 16 != 0) | ||||
|             { | ||||
|                 Sum = (Sum & 0XFFFF) + (Sum >> 16); | ||||
|             } | ||||
|             count -= 2; | ||||
|         } | ||||
|         /// * reverse padding  | ||||
|         if (Padding) | ||||
|         { | ||||
|             Array.Resize(ref data, data.Length - 1); | ||||
|             //data = (byte[])Resize(data, data.Length - 1); | ||||
|         } | ||||
|         ///* Return one's compliment of final sum.  | ||||
|         //return (ushort)(ushort.MaxValue - (ushort)Sum); | ||||
|         return (ushort)(~Sum); | ||||
|     } | ||||
|  | ||||
|     public static ushort GetInteger(byte B1, byte B2) | ||||
|     { | ||||
|         return BitConverter.ToUInt16(new byte[] { B2, B1 }, 0); | ||||
|         //return System.Convert.ToUInt16("&h" + GetHex(B1) + GetHex(B2)); | ||||
|     } | ||||
|  | ||||
|     public static uint GetLong(byte B1, byte B2, byte B3, byte B4) | ||||
|     { | ||||
|         return BitConverter.ToUInt32(new byte[] { B4, B3, B2, B1 }, 0); | ||||
|         //return System.Convert.ToUInt32("&h" + GetHex(B1) + GetHex(B2) + GetHex(B3) + GetHex(B4)); | ||||
|     } | ||||
|  | ||||
|     public static string GetHex(byte B) | ||||
|     { | ||||
|         return (((B < 15) ? 0 + System.Convert.ToString(B, 16).ToUpper() : System.Convert.ToString(B, 16).ToUpper())); | ||||
|     } | ||||
|  | ||||
|     public static bool GetBit(uint B, byte Pos) | ||||
|     { | ||||
|         //return BitConverter.ToBoolean(BitConverter.GetBytes(B), Pos + 1); | ||||
|         return (B & (uint)(Math.Pow(2, (Pos - 1)))) == (Math.Pow(2, (Pos - 1))); | ||||
|     } | ||||
|  | ||||
|     public static ushort RemoveBit(ushort I, byte Pos) | ||||
|     { | ||||
|         return (ushort)RemoveBit((uint)I, Pos); | ||||
|     } | ||||
|  | ||||
|     public static uint RemoveBit(uint I, byte Pos) | ||||
|     { | ||||
|         if (GetBit(I, Pos)) | ||||
|         { | ||||
|             return I - (uint)(Math.Pow(2, (Pos - 1))); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return I; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class Packet | ||||
|     public static void SplitInteger(ushort I, ref byte BLeft, ref byte BRight) | ||||
|     { | ||||
|         //public EtherServer2.EthernetSource Source; | ||||
|         byte[] b = BitConverter.GetBytes(I); | ||||
|         BLeft = b[1]; | ||||
|         BRight = b[0]; | ||||
|         //BLeft = I >> 8; | ||||
|         //BRight = (I << 8) >> 8; | ||||
|     } | ||||
|  | ||||
|         public PacketSource Source; | ||||
|     public static void SplitLong(uint I, ref byte BLeft, ref byte BLeftMiddle, ref byte BRightMiddle, ref byte BRight) | ||||
|     { | ||||
|         byte[] b = BitConverter.GetBytes(I); | ||||
|         BLeft = b[3]; | ||||
|         BLeftMiddle = b[2]; | ||||
|         BRightMiddle = b[1]; | ||||
|         BRight = b[0]; | ||||
|         //BLeft = I >> 24; | ||||
|         //BLeftMiddle = (I << 8) >> 24; | ||||
|         //BRightMiddle = (I << 16) >> 24; | ||||
|         //BRight = (I << 24) >> 24; | ||||
|     } | ||||
|  | ||||
|         public DateTime Timestamp; | ||||
| } | ||||
|  | ||||
|         public enum PPPType : ushort | ||||
|         { | ||||
|             IP = 0x0021,                         // Internet Protocol version 4                   [RFC1332] | ||||
|             SDTP = 0x0049,                       // Serial Data Transport Protocol (PPP-SDTP)     [RFC1963] | ||||
|             IPv6HeaderCompression = 0x004f,      // IPv6 Header Compression | ||||
|             IPv6 = 0x0057,                       // Internet Protocol version 6                   [RFC5072] | ||||
|             W8021dHelloPacket = 0x0201,          // 802.1d Hello Packets                          [RFC3518] | ||||
|             IPv6ControlProtocol = 0x8057,        // IPv6 Control Protocol                         [RFC5072] | ||||
|         } | ||||
| public class PosixTime | ||||
| { | ||||
|     ulong seconds; | ||||
|     ulong microseconds; | ||||
|  | ||||
|         public enum ProtocolType : ushort | ||||
|         { | ||||
|             IP = 0x800,                          // IPv4 | ||||
|             ARP = 0x806,                         // Address Resolution Protocol | ||||
|             IPv6 = 0x86DD,                       // IPv6 | ||||
|             FrameRelayARP = 0x0808,              // Frame Relay ARP          [RFC1701] | ||||
|             VINESLoopback = 0x0BAE,              // VINES Loopback           [RFC1701] | ||||
|             VINESEcho = 0x0BAF,                  // VINES ECHO               [RFC1701] | ||||
|             TransEtherBridging = 0x6558,         // TransEther Bridging      [RFC1701] | ||||
|             RawFrameRelay = 0x6559,              // Raw Frame Relay          [RFC1701] | ||||
|             IEE8021QVLAN = 0x8100,               // IEEE 802.1Q VLAN-tagged frames (initially Wellfleet) | ||||
|             SNMP = 0x814C,                       // SNMP                     [JKR1] | ||||
|             TCPIP_Compression = 0x876B,          // TCP/IP Compression       [RFC1144] | ||||
|             IPAutonomousSystems = 0x876C,        // IP Autonomous Systems    [RFC1701] | ||||
|             SecureData = 0x876D,                 // Secure Data              [RFC1701] | ||||
|             PPP = 0x880B,                        // PPP                      [IANA]  | ||||
|             MPLS = 0x8847,                       // MPLS                     [RFC5332] | ||||
|             MPLS_UpstreamAssignedLabel = 0x8848, // MPLS with upstream-assigned label   [RFC5332] | ||||
|             PPPoEDiscoveryStage = 0x8863,        // PPPoE Discovery Stage    [RFC2516] | ||||
|             PPPoESessionStage = 0x8864,          // PPPoE Session Stage      [RFC2516] | ||||
|         } | ||||
|     PosixTime(ulong Seconds, ulong Microseconds) | ||||
|     { | ||||
|         seconds = Seconds; | ||||
|         microseconds = Microseconds; | ||||
|     } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return seconds + "." + microseconds; | ||||
|     } | ||||
| } | ||||
|  | ||||
| public class Packet | ||||
| { | ||||
|     //public EtherServer2.EthernetSource Source; | ||||
|  | ||||
|     public PacketSource Source; | ||||
|  | ||||
|     public DateTime Timestamp; | ||||
|  | ||||
|     public enum PPPType : ushort | ||||
|     { | ||||
|         IP = 0x0021,                         // Internet Protocol version 4                   [RFC1332] | ||||
|         SDTP = 0x0049,                       // Serial Data Transport Protocol (PPP-SDTP)     [RFC1963] | ||||
|         IPv6HeaderCompression = 0x004f,      // IPv6 Header Compression | ||||
|         IPv6 = 0x0057,                       // Internet Protocol version 6                   [RFC5072] | ||||
|         W8021dHelloPacket = 0x0201,          // 802.1d Hello Packets                          [RFC3518] | ||||
|         IPv6ControlProtocol = 0x8057,        // IPv6 Control Protocol                         [RFC5072] | ||||
|     } | ||||
|  | ||||
|     public enum ProtocolType : ushort | ||||
|     { | ||||
|         IP = 0x800,                          // IPv4 | ||||
|         ARP = 0x806,                         // Address Resolution Protocol | ||||
|         IPv6 = 0x86DD,                       // IPv6 | ||||
|         FrameRelayARP = 0x0808,              // Frame Relay ARP          [RFC1701] | ||||
|         VINESLoopback = 0x0BAE,              // VINES Loopback           [RFC1701] | ||||
|         VINESEcho = 0x0BAF,                  // VINES ECHO               [RFC1701] | ||||
|         TransEtherBridging = 0x6558,         // TransEther Bridging      [RFC1701] | ||||
|         RawFrameRelay = 0x6559,              // Raw Frame Relay          [RFC1701] | ||||
|         IEE8021QVLAN = 0x8100,               // IEEE 802.1Q VLAN-tagged frames (initially Wellfleet) | ||||
|         SNMP = 0x814C,                       // SNMP                     [JKR1] | ||||
|         TCPIP_Compression = 0x876B,          // TCP/IP Compression       [RFC1144] | ||||
|         IPAutonomousSystems = 0x876C,        // IP Autonomous Systems    [RFC1701] | ||||
|         SecureData = 0x876D,                 // Secure Data              [RFC1701] | ||||
|         PPP = 0x880B,                        // PPP                      [IANA]  | ||||
|         MPLS = 0x8847,                       // MPLS                     [RFC5332] | ||||
|         MPLS_UpstreamAssignedLabel = 0x8848, // MPLS with upstream-assigned label   [RFC5332] | ||||
|         PPPoEDiscoveryStage = 0x8863,        // PPPoE Discovery Stage    [RFC2516] | ||||
|         PPPoESessionStage = 0x8864,          // PPPoE Session Stage      [RFC2516] | ||||
|     } | ||||
|  | ||||
|  | ||||
|         /* | ||||
|         public static void GetPacketMACAddresses(Packet packet, out byte[] srcMAC, out byte[] dstMAC) | ||||
|     /* | ||||
|     public static void GetPacketMACAddresses(Packet packet, out byte[] srcMAC, out byte[] dstMAC) | ||||
|     { | ||||
|  | ||||
|         // get the node address | ||||
|         Packet root = packet.RootPacket; | ||||
|         if (root is TZSPPacket) | ||||
|         { | ||||
|  | ||||
|             // get the node address | ||||
|             Packet root = packet.RootPacket; | ||||
|             if (root is TZSPPacket) | ||||
|             TZSPPacket tp = (TZSPPacket)root; | ||||
|             if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet) | ||||
|             { | ||||
|  | ||||
|                 TZSPPacket tp = (TZSPPacket)root; | ||||
|                 if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet) | ||||
|                 { | ||||
|                     EthernetPacket ep = (EthernetPacket)tp.SubPacket; | ||||
|                     srcMAC = ep.SourceMAC; | ||||
|                     dstMAC = ep.DestinationMAC; | ||||
|                 } | ||||
|                 else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11) | ||||
|                 { | ||||
|                     W802_11Packet wp = (W802_11Packet)tp.SubPacket; | ||||
|                     srcMAC = wp.SA; | ||||
|                     dstMAC = wp.DA; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     srcMAC = null; | ||||
|                     dstMAC = null; | ||||
|                 } | ||||
|             } | ||||
|             else if (root is EthernetPacket) | ||||
|             { | ||||
|                 EthernetPacket ep = (EthernetPacket)root; | ||||
|                 EthernetPacket ep = (EthernetPacket)tp.SubPacket; | ||||
|                 srcMAC = ep.SourceMAC; | ||||
|                 dstMAC = ep.DestinationMAC; | ||||
|             } | ||||
|             else if (root is W802_11Packet) | ||||
|             else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11) | ||||
|             { | ||||
|                 W802_11Packet wp = (W802_11Packet)root; | ||||
|                 W802_11Packet wp = (W802_11Packet)tp.SubPacket; | ||||
|                 srcMAC = wp.SA; | ||||
|                 dstMAC = wp.DA; | ||||
|             } | ||||
| @@ -278,92 +259,109 @@ namespace Esiur.Net.Packets | ||||
|                 srcMAC = null; | ||||
|                 dstMAC = null; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         else if (root is EthernetPacket) | ||||
|         { | ||||
|             EthernetPacket ep = (EthernetPacket)root; | ||||
|             srcMAC = ep.SourceMAC; | ||||
|             dstMAC = ep.DestinationMAC; | ||||
|         } | ||||
|         else if (root is W802_11Packet) | ||||
|         { | ||||
|             W802_11Packet wp = (W802_11Packet)root; | ||||
|             srcMAC = wp.SA; | ||||
|             dstMAC = wp.DA; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             srcMAC = null; | ||||
|             dstMAC = null; | ||||
|         } | ||||
|  | ||||
|          | ||||
|         public static void GetPacketAddresses(Packet packet, ref string srcMAC, ref string dstMAC, ref string srcIP, ref string dstIP) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static void GetPacketAddresses(Packet packet, ref string srcMAC, ref string dstMAC, ref string srcIP, ref string dstIP) | ||||
|     { | ||||
|  | ||||
|         if (packet is TCPv4Packet) | ||||
|         { | ||||
|             if (packet.ParentPacket is IPv4Packet) | ||||
|             { | ||||
|                 IPv4Packet ip = (IPv4Packet)packet.ParentPacket; | ||||
|                 srcIP = ip.SourceIP.ToString(); | ||||
|                 dstIP = ip.DestinationIP.ToString(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // get the node address | ||||
|         Packet root = packet.RootPacket; | ||||
|         if (root is TZSPPacket) | ||||
|         { | ||||
|  | ||||
|             if (packet is TCPv4Packet) | ||||
|             TZSPPacket tp = (TZSPPacket)root; | ||||
|             if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet) | ||||
|             { | ||||
|                 if (packet.ParentPacket is IPv4Packet) | ||||
|                 { | ||||
|                     IPv4Packet ip = (IPv4Packet)packet.ParentPacket; | ||||
|                     srcIP = ip.SourceIP.ToString(); | ||||
|                     dstIP = ip.DestinationIP.ToString(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // get the node address | ||||
|             Packet root = packet.RootPacket; | ||||
|             if (root is TZSPPacket) | ||||
|             { | ||||
|  | ||||
|                 TZSPPacket tp = (TZSPPacket)root; | ||||
|                 if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet) | ||||
|                 { | ||||
|                     EthernetPacket ep = (EthernetPacket)tp.SubPacket; | ||||
|                     srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString(); | ||||
|                     dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString(); | ||||
|                 } | ||||
|                 else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11) | ||||
|                 { | ||||
|                     W802_11Packet wp = (W802_11Packet)tp.SubPacket; | ||||
|                     srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString(); | ||||
|                     dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString(); | ||||
|                 } | ||||
|             } | ||||
|             else if (root is EthernetPacket) | ||||
|             { | ||||
|                 EthernetPacket ep = (EthernetPacket)root; | ||||
|                 EthernetPacket ep = (EthernetPacket)tp.SubPacket; | ||||
|                 srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString(); | ||||
|                 dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString(); | ||||
|             } | ||||
|             else if (root is W802_11Packet) | ||||
|             else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11) | ||||
|             { | ||||
|                 W802_11Packet wp = (W802_11Packet)root; | ||||
|                 W802_11Packet wp = (W802_11Packet)tp.SubPacket; | ||||
|                 srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString(); | ||||
|                 dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString(); | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|  | ||||
|         //PosixTime Timeval; | ||||
|         public byte[] Header; | ||||
|         public byte[] Preamble; | ||||
|         //public byte[] Payload; | ||||
|         public byte[] Data; | ||||
|  | ||||
|         public Packet SubPacket; | ||||
|         public Packet ParentPacket; | ||||
|         public virtual long Parse(byte[] data, uint offset, uint ends) { return 0; } | ||||
|         public virtual bool Compose() { return false; } | ||||
|  | ||||
|         public Packet RootPacket | ||||
|         else if (root is EthernetPacket) | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 Packet root = this; | ||||
|                 while (root.ParentPacket != null) | ||||
|                     root = root.ParentPacket; | ||||
|                 return root; | ||||
|             } | ||||
|             EthernetPacket ep = (EthernetPacket)root; | ||||
|             srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString(); | ||||
|             dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString(); | ||||
|         } | ||||
|  | ||||
|         public Packet LeafPacket | ||||
|         else if (root is W802_11Packet) | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 Packet leaf = this; | ||||
|                 while (leaf.SubPacket != null) | ||||
|                     leaf = leaf.SubPacket; | ||||
|                 return leaf; | ||||
|             } | ||||
|             W802_11Packet wp = (W802_11Packet)root; | ||||
|             srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString(); | ||||
|             dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString(); | ||||
|         } | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     //PosixTime Timeval; | ||||
|     public byte[] Header; | ||||
|     public byte[] Preamble; | ||||
|     //public byte[] Payload; | ||||
|     public byte[] Data; | ||||
|  | ||||
|     public Packet SubPacket; | ||||
|     public Packet ParentPacket; | ||||
|     public virtual long Parse(byte[] data, uint offset, uint ends) { return 0; } | ||||
|     public virtual bool Compose() { return false; } | ||||
|  | ||||
|     public Packet RootPacket | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             Packet root = this; | ||||
|             while (root.ParentPacket != null) | ||||
|                 root = root.ParentPacket; | ||||
|             return root; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Packet LeafPacket | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             Packet leaf = this; | ||||
|             while (leaf.SubPacket != null) | ||||
|                 leaf = leaf.SubPacket; | ||||
|             return leaf; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /************************************ EOF *************************************/ | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -29,189 +29,187 @@ using System.Text; | ||||
| using Esiur.Misc; | ||||
| using Esiur.Data; | ||||
|  | ||||
| namespace Esiur.Net.Packets | ||||
| namespace Esiur.Net.Packets; | ||||
| public class WebsocketPacket : Packet | ||||
| { | ||||
|     public class WebsocketPacket : Packet | ||||
|     public enum WSOpcode : byte | ||||
|     { | ||||
|         public enum WSOpcode : byte | ||||
|         ContinuationFrame = 0x0, //  %x0 denotes a continuation frame | ||||
|  | ||||
|         TextFrame = 0x1, // %x1 denotes a text frame | ||||
|  | ||||
|         BinaryFrame = 0x2,            // %x2 denotes a binary frame | ||||
|  | ||||
|         // %x3-7 are reserved for further non-control frames | ||||
|  | ||||
|         ConnectionClose = 0x8,    // %x8 denotes a connection close | ||||
|  | ||||
|         Ping = 0x9, // %x9 denotes a ping | ||||
|  | ||||
|         Pong = 0xA,            // %xA denotes a pong | ||||
|  | ||||
|         //*  %xB-F are reserved for further control frames | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public bool FIN; | ||||
|     public bool RSV1; | ||||
|     public bool RSV2; | ||||
|     public bool RSV3; | ||||
|     public WSOpcode Opcode; | ||||
|     public bool Mask; | ||||
|     public long PayloadLength; | ||||
|     //        public UInt32 MaskKey; | ||||
|     public byte[] MaskKey; | ||||
|  | ||||
|     public byte[] Message; | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return "WebsocketPacket" | ||||
|             + "\n\tFIN: " + FIN | ||||
|             + "\n\tOpcode: " + Opcode | ||||
|             + "\n\tPayload: " + PayloadLength | ||||
|             + "\n\tMaskKey: " + MaskKey | ||||
|             + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL"); | ||||
|     } | ||||
|  | ||||
|     public override bool Compose() | ||||
|     { | ||||
|         var pkt = new List<byte>(); | ||||
|         pkt.Add((byte)((FIN ? 0x80 : 0x0) | | ||||
|             (RSV1 ? 0x40 : 0x0) | | ||||
|             (RSV2 ? 0x20 : 0x0) | | ||||
|             (RSV3 ? 0x10 : 0x0) | | ||||
|             (byte)Opcode)); | ||||
|  | ||||
|         // calculate length | ||||
|         if (Message.Length > UInt16.MaxValue) | ||||
|         // 4 bytes | ||||
|         { | ||||
|             ContinuationFrame = 0x0, //  %x0 denotes a continuation frame | ||||
|  | ||||
|             TextFrame = 0x1, // %x1 denotes a text frame | ||||
|  | ||||
|             BinaryFrame = 0x2,            // %x2 denotes a binary frame | ||||
|  | ||||
|             // %x3-7 are reserved for further non-control frames | ||||
|  | ||||
|             ConnectionClose = 0x8,    // %x8 denotes a connection close | ||||
|  | ||||
|             Ping = 0x9, // %x9 denotes a ping | ||||
|  | ||||
|             Pong = 0xA,            // %xA denotes a pong | ||||
|  | ||||
|             //*  %xB-F are reserved for further control frames | ||||
|             pkt.Add((byte)((Mask ? 0x80 : 0x0) | 127)); | ||||
|             pkt.AddRange(DC.ToBytes((UInt64)Message.LongCount())); | ||||
|         } | ||||
|         else if (Message.Length > 125) | ||||
|         // 2 bytes | ||||
|         { | ||||
|             pkt.Add((byte)((Mask ? 0x80 : 0x0) | 126)); | ||||
|             pkt.AddRange(DC.ToBytes((UInt16)Message.Length)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             pkt.Add((byte)((Mask ? 0x80 : 0x0) | Message.Length)); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public bool FIN; | ||||
|         public bool RSV1; | ||||
|         public bool RSV2; | ||||
|         public bool RSV3; | ||||
|         public WSOpcode Opcode; | ||||
|         public bool Mask; | ||||
|         public long PayloadLength; | ||||
|         //        public UInt32 MaskKey; | ||||
|         public byte[] MaskKey; | ||||
|  | ||||
|         public byte[] Message; | ||||
|  | ||||
|         public override string ToString() | ||||
|         if (Mask) | ||||
|         { | ||||
|             return "WebsocketPacket" | ||||
|                 + "\n\tFIN: " + FIN | ||||
|                 + "\n\tOpcode: " + Opcode | ||||
|                 + "\n\tPayload: " + PayloadLength | ||||
|                 + "\n\tMaskKey: " + MaskKey | ||||
|                 + "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL"); | ||||
|             pkt.AddRange(MaskKey); | ||||
|         } | ||||
|  | ||||
|         public override bool Compose() | ||||
|         { | ||||
|             var pkt = new List<byte>(); | ||||
|             pkt.Add((byte)((FIN ? 0x80 : 0x0) | | ||||
|                 (RSV1 ? 0x40 : 0x0) | | ||||
|                 (RSV2 ? 0x20 : 0x0) | | ||||
|                 (RSV3 ? 0x10 : 0x0) | | ||||
|                 (byte)Opcode)); | ||||
|         pkt.AddRange(Message); | ||||
|  | ||||
|             // calculate length | ||||
|             if (Message.Length > UInt16.MaxValue) | ||||
|             // 4 bytes | ||||
|         Data = pkt.ToArray(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public override long Parse(byte[] data, uint offset, uint ends) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             long needed = 2; | ||||
|             var length = (ends - offset); | ||||
|             if (length < needed) | ||||
|             { | ||||
|                 pkt.Add((byte)((Mask ? 0x80 : 0x0) | 127)); | ||||
|                 pkt.AddRange(DC.ToBytes((UInt64)Message.LongCount())); | ||||
|             } | ||||
|             else if (Message.Length > 125) | ||||
|             // 2 bytes | ||||
|             { | ||||
|                 pkt.Add((byte)((Mask ? 0x80 : 0x0) | 126)); | ||||
|                 pkt.AddRange(DC.ToBytes((UInt16)Message.Length)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 pkt.Add((byte)((Mask ? 0x80 : 0x0) | Message.Length)); | ||||
|                 //Console.WriteLine("stage 1 " + needed); | ||||
|                 return length - needed; | ||||
|             } | ||||
|  | ||||
|             uint oOffset = offset; | ||||
|             FIN = ((data[offset] & 0x80) == 0x80); | ||||
|             RSV1 = ((data[offset] & 0x40) == 0x40); | ||||
|             RSV2 = ((data[offset] & 0x20) == 0x20); | ||||
|             RSV3 = ((data[offset] & 0x10) == 0x10); | ||||
|             Opcode = (WSOpcode)(data[offset++] & 0xF); | ||||
|             Mask = ((data[offset] & 0x80) == 0x80); | ||||
|             PayloadLength = (long)(data[offset++] & 0x7F); | ||||
|  | ||||
|             if (Mask) | ||||
|                 needed += 4; | ||||
|  | ||||
|             if (PayloadLength == 126) | ||||
|             { | ||||
|                 pkt.AddRange(MaskKey); | ||||
|             } | ||||
|  | ||||
|             pkt.AddRange(Message); | ||||
|  | ||||
|             Data = pkt.ToArray(); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public override long Parse(byte[] data, uint offset, uint ends) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 long needed = 2; | ||||
|                 var length = (ends - offset); | ||||
|                 needed += 2; | ||||
|                 if (length < needed) | ||||
|                 { | ||||
|                     //Console.WriteLine("stage 1 " + needed); | ||||
|                     //Console.WriteLine("stage 2 " + needed); | ||||
|                     return length - needed; | ||||
|                 } | ||||
|                 PayloadLength = data.GetUInt16(offset); | ||||
|                 offset += 2; | ||||
|             } | ||||
|             else if (PayloadLength == 127) | ||||
|             { | ||||
|                 needed += 8; | ||||
|                 if (length < needed) | ||||
|                 { | ||||
|                     //Console.WriteLine("stage 3 " + needed); | ||||
|                     return length - needed; | ||||
|                 } | ||||
|  | ||||
|                 uint oOffset = offset; | ||||
|                 FIN = ((data[offset] & 0x80) == 0x80); | ||||
|                 RSV1 = ((data[offset] & 0x40) == 0x40); | ||||
|                 RSV2 = ((data[offset] & 0x20) == 0x20); | ||||
|                 RSV3 = ((data[offset] & 0x10) == 0x10); | ||||
|                 Opcode = (WSOpcode)(data[offset++] & 0xF); | ||||
|                 Mask = ((data[offset] & 0x80) == 0x80); | ||||
|                 PayloadLength = (long)(data[offset++] & 0x7F); | ||||
|                 PayloadLength = data.GetInt64(offset); | ||||
|                 offset += 8; | ||||
|             } | ||||
|  | ||||
|             /* | ||||
|             if (Mask) | ||||
|             { | ||||
|                                 MaskKey = new byte[4]; | ||||
|                 MaskKey[0] = data[offset++]; | ||||
|                 MaskKey[1] = data[offset++]; | ||||
|                 MaskKey[2] = data[offset++]; | ||||
|                 MaskKey[3] = data[offset++]; | ||||
|  | ||||
|                 //MaskKey = DC.GetUInt32(data, offset); | ||||
|                 //offset += 4; | ||||
|             } | ||||
|             */ | ||||
|  | ||||
|             needed += PayloadLength; | ||||
|             if (length < needed) | ||||
|             { | ||||
|                 //Console.WriteLine("stage 4"); | ||||
|                 return length - needed; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|  | ||||
|                 if (Mask) | ||||
|                     needed += 4; | ||||
|  | ||||
|                 if (PayloadLength == 126) | ||||
|                 { | ||||
|                     needed += 2; | ||||
|                     if (length < needed) | ||||
|                     { | ||||
|                         //Console.WriteLine("stage 2 " + needed); | ||||
|                         return length - needed; | ||||
|                     } | ||||
|                     PayloadLength = data.GetUInt16( offset); | ||||
|                     offset += 2; | ||||
|                 } | ||||
|                 else if (PayloadLength == 127) | ||||
|                 { | ||||
|                     needed += 8; | ||||
|                     if (length < needed) | ||||
|                     { | ||||
|                         //Console.WriteLine("stage 3 " + needed); | ||||
|                         return length - needed; | ||||
|                     } | ||||
|  | ||||
|                     PayloadLength = data.GetInt64(offset); | ||||
|                     offset += 8; | ||||
|                 } | ||||
|  | ||||
|                 /* | ||||
|                 if (Mask) | ||||
|                 { | ||||
|                                     MaskKey = new byte[4]; | ||||
|                     MaskKey = new byte[4]; | ||||
|                     MaskKey[0] = data[offset++]; | ||||
|                     MaskKey[1] = data[offset++]; | ||||
|                     MaskKey[2] = data[offset++]; | ||||
|                     MaskKey[3] = data[offset++]; | ||||
|  | ||||
|                     //MaskKey = DC.GetUInt32(data, offset); | ||||
|                     //offset += 4; | ||||
|                 } | ||||
|                 */ | ||||
|                     Message = DC.Clip(data, offset, (uint)PayloadLength); | ||||
|  | ||||
|                 needed += PayloadLength; | ||||
|                 if (length < needed) | ||||
|                 { | ||||
|                     //Console.WriteLine("stage 4"); | ||||
|                     return length - needed; | ||||
|                     //var aMask = BitConverter.GetBytes(MaskKey); | ||||
|                     for (int i = 0; i < Message.Length; i++) | ||||
|                         Message[i] = (byte)(Message[i] ^ MaskKey[i % 4]); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|  | ||||
|                     if (Mask) | ||||
|                     { | ||||
|                         MaskKey = new byte[4]; | ||||
|                         MaskKey[0] = data[offset++]; | ||||
|                         MaskKey[1] = data[offset++]; | ||||
|                         MaskKey[2] = data[offset++]; | ||||
|                         MaskKey[3] = data[offset++]; | ||||
|  | ||||
|                         Message = DC.Clip(data, offset, (uint)PayloadLength); | ||||
|  | ||||
|                         //var aMask = BitConverter.GetBytes(MaskKey); | ||||
|                         for (int i = 0; i < Message.Length; i++) | ||||
|                             Message[i] = (byte)(Message[i] ^ MaskKey[i % 4]); | ||||
|                     } | ||||
|                     else | ||||
|                         Message = DC.Clip(data, offset, (uint)PayloadLength); | ||||
|                     Message = DC.Clip(data, offset, (uint)PayloadLength); | ||||
|  | ||||
|  | ||||
|                     return (offset - oOffset) + (int)PayloadLength; | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Console.WriteLine(ex.ToString()); | ||||
|                 Console.WriteLine(offset + "::" + DC.ToHex(data)); | ||||
|                 throw ex; | ||||
|                 return (offset - oOffset) + (int)PayloadLength; | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Console.WriteLine(ex.ToString()); | ||||
|             Console.WriteLine(offset + "::" + DC.ToHex(data)); | ||||
|             throw ex; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,23 +4,22 @@ using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Net | ||||
| namespace Esiur.Net; | ||||
|  | ||||
| public class SendList : BinaryList | ||||
| { | ||||
|     public class SendList : BinaryList | ||||
|     NetworkConnection connection; | ||||
|     AsyncReply<object[]> reply; | ||||
|  | ||||
|     public SendList(NetworkConnection connection, AsyncReply<object[]> reply) | ||||
|     { | ||||
|         NetworkConnection connection; | ||||
|         AsyncReply<object[]> reply; | ||||
|         this.reply = reply; | ||||
|         this.connection = connection; | ||||
|     } | ||||
|  | ||||
|         public SendList(NetworkConnection connection, AsyncReply<object[]> reply) | ||||
|         { | ||||
|             this.reply = reply; | ||||
|             this.connection = connection; | ||||
|         } | ||||
|  | ||||
|         public override AsyncReply<object[]> Done() | ||||
|         { | ||||
|             connection.Send(this.ToArray()); | ||||
|             return reply; | ||||
|         } | ||||
|     public override AsyncReply<object[]> Done() | ||||
|     { | ||||
|         connection.Send(this.ToArray()); | ||||
|         return reply; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,39 +36,37 @@ using System.Collections.Concurrent; | ||||
| using Esiur.Resource; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Net.Sockets | ||||
| namespace Esiur.Net.Sockets; | ||||
| //public delegate void ISocketReceiveEvent(NetworkBuffer buffer); | ||||
| //public delegate void ISocketConnectEvent(); | ||||
| //public delegate void ISocketCloseEvent(); | ||||
|  | ||||
| public interface ISocket : IDestructible | ||||
| { | ||||
|     //public delegate void ISocketReceiveEvent(NetworkBuffer buffer); | ||||
|     //public delegate void ISocketConnectEvent(); | ||||
|     //public delegate void ISocketCloseEvent(); | ||||
|     SocketState State { get; } | ||||
|  | ||||
|     public interface ISocket : IDestructible | ||||
|     { | ||||
|         SocketState State { get; } | ||||
|     //event ISocketReceiveEvent OnReceive; | ||||
|     //event ISocketConnectEvent OnConnect; | ||||
|     //event ISocketCloseEvent OnClose; | ||||
|  | ||||
|         //event ISocketReceiveEvent OnReceive; | ||||
|         //event ISocketConnectEvent OnConnect; | ||||
|         //event ISocketCloseEvent OnClose; | ||||
|     INetworkReceiver<ISocket> Receiver { get; set; } | ||||
|  | ||||
|         INetworkReceiver<ISocket> Receiver { get; set; } | ||||
|     AsyncReply<bool> SendAsync(byte[] message, int offset, int length); | ||||
|  | ||||
|         AsyncReply<bool> SendAsync(byte[] message, int offset, int length); | ||||
|     void Send(byte[] message); | ||||
|     void Send(byte[] message, int offset, int length); | ||||
|     void Close(); | ||||
|     AsyncReply<bool> Connect(string hostname, ushort port); | ||||
|     bool Begin(); | ||||
|     AsyncReply<bool> BeginAsync(); | ||||
|     //ISocket Accept(); | ||||
|     AsyncReply<ISocket> AcceptAsync(); | ||||
|     ISocket Accept(); | ||||
|  | ||||
|         void Send(byte[] message); | ||||
|         void Send(byte[] message, int offset, int length); | ||||
|         void Close(); | ||||
|         AsyncReply<bool> Connect(string hostname, ushort port); | ||||
|         bool Begin(); | ||||
|         AsyncReply<bool> BeginAsync(); | ||||
|         //ISocket Accept(); | ||||
|         AsyncReply<ISocket> AcceptAsync(); | ||||
|         ISocket Accept(); | ||||
|     IPEndPoint RemoteEndPoint { get; } | ||||
|     IPEndPoint LocalEndPoint { get; } | ||||
|  | ||||
|         IPEndPoint RemoteEndPoint { get; } | ||||
|         IPEndPoint LocalEndPoint { get; } | ||||
|     void Hold(); | ||||
|  | ||||
|         void Hold(); | ||||
|  | ||||
|         void Unhold(); | ||||
|     } | ||||
|     void Unhold(); | ||||
| } | ||||
|   | ||||
| @@ -37,519 +37,518 @@ using Esiur.Resource; | ||||
| using System.Threading.Tasks; | ||||
| using Esiur.Data; | ||||
|  | ||||
| namespace Esiur.Net.Sockets | ||||
| namespace Esiur.Net.Sockets; | ||||
| public class SSLSocket : ISocket | ||||
| { | ||||
|     public class SSLSocket : ISocket | ||||
|  | ||||
|     public INetworkReceiver<ISocket> Receiver { get; set; } | ||||
|  | ||||
|     Socket sock; | ||||
|     byte[] receiveBuffer; | ||||
|  | ||||
|     bool held; | ||||
|  | ||||
|     //ArraySegment<byte> receiveBufferSegment; | ||||
|  | ||||
|     NetworkBuffer receiveNetworkBuffer = new NetworkBuffer(); | ||||
|  | ||||
|     readonly object sendLock = new object(); | ||||
|  | ||||
|     Queue<KeyValuePair<AsyncReply<bool>, byte[]>> sendBufferQueue = new Queue<KeyValuePair<AsyncReply<bool>, byte[]>>();// Queue<byte[]>(); | ||||
|  | ||||
|     bool asyncSending; | ||||
|     bool began = false; | ||||
|  | ||||
|     SocketState state = SocketState.Initial; | ||||
|  | ||||
|     //public event ISocketReceiveEvent OnReceive; | ||||
|     //public event ISocketConnectEvent OnConnect; | ||||
|     //public event ISocketCloseEvent OnClose; | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|  | ||||
|     SslStream ssl; | ||||
|     X509Certificate2 cert; | ||||
|     bool server; | ||||
|     string hostname; | ||||
|  | ||||
|  | ||||
|     public async AsyncReply<bool> Connect(string hostname, ushort port) | ||||
|     { | ||||
|         var rt = new AsyncReply<bool>(); | ||||
|  | ||||
|         public INetworkReceiver<ISocket> Receiver { get; set; } | ||||
|         this.hostname = hostname; | ||||
|         this.server = false; | ||||
|  | ||||
|         Socket sock; | ||||
|         byte[] receiveBuffer; | ||||
|  | ||||
|         bool held; | ||||
|  | ||||
|         //ArraySegment<byte> receiveBufferSegment; | ||||
|  | ||||
|         NetworkBuffer receiveNetworkBuffer = new NetworkBuffer(); | ||||
|  | ||||
|         readonly object sendLock = new object(); | ||||
|  | ||||
|         Queue<KeyValuePair<AsyncReply<bool>, byte[]>> sendBufferQueue = new Queue<KeyValuePair<AsyncReply<bool>, byte[]>>();// Queue<byte[]>(); | ||||
|  | ||||
|         bool asyncSending; | ||||
|         bool began = false; | ||||
|  | ||||
|         SocketState state = SocketState.Initial; | ||||
|  | ||||
|         //public event ISocketReceiveEvent OnReceive; | ||||
|         //public event ISocketConnectEvent OnConnect; | ||||
|         //public event ISocketCloseEvent OnClose; | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|         state = SocketState.Connecting; | ||||
|         await sock.ConnectAsync(hostname, port); | ||||
|  | ||||
|  | ||||
|         SslStream ssl; | ||||
|         X509Certificate2 cert; | ||||
|         bool server; | ||||
|         string hostname; | ||||
|  | ||||
|  | ||||
|         public async AsyncReply<bool> Connect(string hostname, ushort port) | ||||
|         try | ||||
|         { | ||||
|             var rt = new AsyncReply<bool>(); | ||||
|  | ||||
|             this.hostname = hostname; | ||||
|             this.server = false; | ||||
|  | ||||
|             state = SocketState.Connecting; | ||||
|             await sock.ConnectAsync(hostname, port); | ||||
|  | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 await BeginAsync(); | ||||
|                 state = SocketState.Established; | ||||
|                 //OnConnect?.Invoke(); | ||||
|                 Receiver?.NetworkConnect(this); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 state = SocketState.Closed;// .Terminated; | ||||
|                 Close(); | ||||
|                 Global.Log(ex); | ||||
|             } | ||||
|  | ||||
|             return true; | ||||
|             await BeginAsync(); | ||||
|             state = SocketState.Established; | ||||
|             //OnConnect?.Invoke(); | ||||
|             Receiver?.NetworkConnect(this); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             state = SocketState.Closed;// .Terminated; | ||||
|             Close(); | ||||
|             Global.Log(ex); | ||||
|         } | ||||
|  | ||||
|         //private void DataSent(Task task) | ||||
|         //{ | ||||
|         //    try | ||||
|         //    { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|         //        if (sendBufferQueue.Count > 0) | ||||
|         //        { | ||||
|         //            byte[] data = sendBufferQueue.Dequeue(); | ||||
|         //            lock (sendLock) | ||||
|         //                ssl.WriteAsync(data, 0, data.Length).ContinueWith(DataSent); | ||||
|         //        } | ||||
|         //        else | ||||
|         //        { | ||||
|         //            asyncSending = false; | ||||
|         //        } | ||||
|         //    } | ||||
|         //    catch (Exception ex) | ||||
|         //    { | ||||
|         //        if (state != SocketState.Closed && !sock.Connected) | ||||
|         //        { | ||||
|         //            state = SocketState.Terminated; | ||||
|         //            Close(); | ||||
|         //        } | ||||
|     //private void DataSent(Task task) | ||||
|     //{ | ||||
|     //    try | ||||
|     //    { | ||||
|  | ||||
|         //        asyncSending = false; | ||||
|     //        if (sendBufferQueue.Count > 0) | ||||
|     //        { | ||||
|     //            byte[] data = sendBufferQueue.Dequeue(); | ||||
|     //            lock (sendLock) | ||||
|     //                ssl.WriteAsync(data, 0, data.Length).ContinueWith(DataSent); | ||||
|     //        } | ||||
|     //        else | ||||
|     //        { | ||||
|     //            asyncSending = false; | ||||
|     //        } | ||||
|     //    } | ||||
|     //    catch (Exception ex) | ||||
|     //    { | ||||
|     //        if (state != SocketState.Closed && !sock.Connected) | ||||
|     //        { | ||||
|     //            state = SocketState.Terminated; | ||||
|     //            Close(); | ||||
|     //        } | ||||
|  | ||||
|         //        Global.Log("SSLSocket", LogType.Error, ex.ToString()); | ||||
|         //    } | ||||
|         //} | ||||
|     //        asyncSending = false; | ||||
|  | ||||
|     //        Global.Log("SSLSocket", LogType.Error, ex.ToString()); | ||||
|     //    } | ||||
|     //} | ||||
|  | ||||
|  | ||||
|         private void SendCallback(IAsyncResult ar) | ||||
|         { | ||||
|             if (ar != null) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     ssl.EndWrite(ar); | ||||
|  | ||||
|                     if (ar.AsyncState != null) | ||||
|                         ((AsyncReply<bool>)ar.AsyncState).Trigger(true); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     if (state != SocketState.Closed && !sock.Connected) | ||||
|                     { | ||||
|                         //state = SocketState.Closed;//.Terminated; | ||||
|                         Close(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             lock (sendLock) | ||||
|             { | ||||
|  | ||||
|                 if (sendBufferQueue.Count > 0) | ||||
|                 { | ||||
|                     var kv = sendBufferQueue.Dequeue(); | ||||
|  | ||||
|                     try | ||||
|                     { | ||||
|                         ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key); | ||||
|                     } | ||||
|                     catch //(Exception ex) | ||||
|                     { | ||||
|                         asyncSending = false; | ||||
|                         try | ||||
|                         { | ||||
|                             if (kv.Key != null) | ||||
|                                 kv.Key.Trigger(false); | ||||
|  | ||||
|                             if (state != SocketState.Closed && !sock.Connected) | ||||
|                             { | ||||
|                                 //state = SocketState.Terminated; | ||||
|                                 Close(); | ||||
|                             } | ||||
|                         } | ||||
|                         catch //(Exception ex2) | ||||
|                         { | ||||
|                             //state = SocketState.Closed;// .Terminated; | ||||
|                             Close(); | ||||
|                         } | ||||
|  | ||||
|                         //Global.Log("TCPSocket", LogType.Error, ex.ToString()); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     asyncSending = false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public IPEndPoint LocalEndPoint | ||||
|         { | ||||
|             get { return (IPEndPoint)sock.LocalEndPoint; } | ||||
|         } | ||||
|  | ||||
|         public SSLSocket() | ||||
|         { | ||||
|             sock = new Socket(AddressFamily.InterNetwork, | ||||
|                                  SocketType.Stream, | ||||
|                                  ProtocolType.Tcp); | ||||
|             receiveBuffer = new byte[sock.ReceiveBufferSize]; | ||||
|         } | ||||
|  | ||||
|         public SSLSocket(IPEndPoint localEndPoint, X509Certificate2 certificate) | ||||
|         { | ||||
|             // create the socket | ||||
|             sock = new Socket(AddressFamily.InterNetwork, | ||||
|                                              SocketType.Stream, | ||||
|                                              ProtocolType.Tcp); | ||||
|  | ||||
|             state = SocketState.Listening; | ||||
|  | ||||
|             // bind | ||||
|             sock.Bind(localEndPoint); | ||||
|  | ||||
|             // start listening | ||||
|             sock.Listen(UInt16.MaxValue); | ||||
|  | ||||
|             cert = certificate; | ||||
|         } | ||||
|  | ||||
|         public IPEndPoint RemoteEndPoint | ||||
|         { | ||||
|             get { return (IPEndPoint)sock.RemoteEndPoint; } | ||||
|         } | ||||
|  | ||||
|         public SocketState State | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return state; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         public SSLSocket(Socket socket, X509Certificate2 certificate, bool authenticateAsServer) | ||||
|         { | ||||
|             cert = certificate; | ||||
|             sock = socket; | ||||
|             receiveBuffer = new byte[sock.ReceiveBufferSize]; | ||||
|  | ||||
|             ssl = new SslStream(new NetworkStream(sock)); | ||||
|  | ||||
|             server = authenticateAsServer; | ||||
|  | ||||
|             if (socket.Connected) | ||||
|                 state = SocketState.Established; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Close() | ||||
|         { | ||||
|             if (state != SocketState.Closed)// && state != SocketState.Terminated) | ||||
|             { | ||||
|                 state = SocketState.Closed; | ||||
|  | ||||
|                 if (sock.Connected) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         sock.Shutdown(SocketShutdown.Both); | ||||
|                     } | ||||
|                     catch | ||||
|                     { | ||||
|                         //state = SocketState.Terminated; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 Receiver?.NetworkClose(this); | ||||
|                 //OnClose?.Invoke(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Send(byte[] message) | ||||
|         { | ||||
|             Send(message, 0, message.Length); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Send(byte[] message, int offset, int size) | ||||
|         { | ||||
|  | ||||
|  | ||||
|             var msg = message.Clip((uint)offset, (uint)size); | ||||
|  | ||||
|             lock (sendLock) | ||||
|             { | ||||
|  | ||||
|                 if (!sock.Connected) | ||||
|                     return; | ||||
|  | ||||
|                 if (asyncSending || held) | ||||
|                 { | ||||
|                     sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(null, msg));// message.Clip((uint)offset, (uint)size)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     asyncSending = true; | ||||
|                     try | ||||
|                     { | ||||
|                         ssl.BeginWrite(msg, 0, msg.Length, SendCallback, null); | ||||
|                     } | ||||
|                     catch | ||||
|                     { | ||||
|                         asyncSending = false; | ||||
|                         //state = SocketState.Terminated; | ||||
|                         Close(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //public void Send(byte[] message) | ||||
|         //{ | ||||
|         //    Send(message, 0, message.Length); | ||||
|         //} | ||||
|  | ||||
|         //public void Send(byte[] message, int offset, int size) | ||||
|         //{ | ||||
|         //    lock (sendLock) | ||||
|         //    { | ||||
|         //        if (asyncSending) | ||||
|         //        { | ||||
|         //            sendBufferQueue.Enqueue(message.Clip((uint)offset, (uint)size)); | ||||
|         //        } | ||||
|         //        else | ||||
|         //        { | ||||
|         //            asyncSending = true; | ||||
|         //            ssl.WriteAsync(message, offset, size).ContinueWith(DataSent); | ||||
|         //        } | ||||
|         //    } | ||||
|         //} | ||||
|  | ||||
|  | ||||
|  | ||||
|         //private void DataReceived(Task<int> task) | ||||
|         //{ | ||||
|         //    try | ||||
|         //    { | ||||
|         //        if (state == SocketState.Closed || state == SocketState.Terminated) | ||||
|         //            return; | ||||
|  | ||||
|         //        if (task.Result <= 0) | ||||
|         //        { | ||||
|         //            Close(); | ||||
|         //            return; | ||||
|         //        } | ||||
|  | ||||
|         //        receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)task.Result); | ||||
|         //        OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|         //        if (state == SocketState.Established) | ||||
|         //            ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived); | ||||
|         //    } | ||||
|         //    catch (Exception ex) | ||||
|         //    { | ||||
|         //        if (state != SocketState.Closed && !sock.Connected) | ||||
|         //        { | ||||
|         //            state = SocketState.Terminated; | ||||
|         //            Close(); | ||||
|         //        } | ||||
|  | ||||
|         //        Global.Log("SSLSocket", LogType.Error, ex.ToString()); | ||||
|         //    } | ||||
|         //} | ||||
|  | ||||
|  | ||||
|         public bool Begin() | ||||
|         { | ||||
|             if (began) | ||||
|                 return false; | ||||
|  | ||||
|             began = true; | ||||
|  | ||||
|             if (server) | ||||
|                 ssl.AuthenticateAsServer(cert); | ||||
|             else | ||||
|                 ssl.AuthenticateAsClient(hostname); | ||||
|  | ||||
|             if (state == SocketState.Established) | ||||
|             { | ||||
|                 ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|                 return false; | ||||
|         } | ||||
|  | ||||
|         public async AsyncReply<bool> BeginAsync() | ||||
|         { | ||||
|             if (began) | ||||
|                 return false; | ||||
|  | ||||
|             began = true; | ||||
|  | ||||
|             if (server) | ||||
|                 await ssl.AuthenticateAsServerAsync(cert); | ||||
|             else | ||||
|                 await ssl.AuthenticateAsClientAsync(hostname); | ||||
|  | ||||
|             if (state == SocketState.Established) | ||||
|             { | ||||
|                 ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|                 return false; | ||||
|         } | ||||
|  | ||||
|         private void ReceiveCallback(IAsyncResult results) | ||||
|     private void SendCallback(IAsyncResult ar) | ||||
|     { | ||||
|         if (ar != null) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (state != SocketState.Established) | ||||
|                     return; | ||||
|  | ||||
|                 var bytesReceived = ssl.EndRead(results); | ||||
|  | ||||
|                 if (bytesReceived <= 0) | ||||
|                 { | ||||
|                     Close(); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)bytesReceived); | ||||
|  | ||||
|                 //OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|  | ||||
|                 Receiver?.NetworkReceive(this, receiveNetworkBuffer); | ||||
|  | ||||
|                 ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); | ||||
|                 ssl.EndWrite(ar); | ||||
|  | ||||
|                 if (ar.AsyncState != null) | ||||
|                     ((AsyncReply<bool>)ar.AsyncState).Trigger(true); | ||||
|             } | ||||
|             catch //(Exception ex) | ||||
|             catch | ||||
|             { | ||||
|                 if (state != SocketState.Closed && !sock.Connected) | ||||
|                 { | ||||
|                     //state = SocketState.Terminated; | ||||
|                     //state = SocketState.Closed;//.Terminated; | ||||
|                     Close(); | ||||
|                 } | ||||
|  | ||||
|                 //Global.Log("SSLSocket", LogType.Error, ex.ToString()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public bool Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             Close(); | ||||
|             Receiver = null; | ||||
|             receiveNetworkBuffer = null; | ||||
|             OnDestroy?.Invoke(this); | ||||
|             OnDestroy = null; | ||||
|         } | ||||
|  | ||||
|         public async AsyncReply<ISocket> AcceptAsync() | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var s = await sock.AcceptAsync(); | ||||
|                 return new SSLSocket(s, cert, true); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 state = SocketState.Closed;// Terminated; | ||||
|                 return null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Hold() | ||||
|         { | ||||
|             held = true; | ||||
|         } | ||||
|  | ||||
|         public void Unhold() | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 SendCallback(null); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Global.Log(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 held = false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public AsyncReply<bool> SendAsync(byte[] message, int offset, int length) | ||||
|         lock (sendLock) | ||||
|         { | ||||
|  | ||||
|             var msg = message.Clip((uint)offset, (uint)length); | ||||
|  | ||||
|             lock (sendLock) | ||||
|             if (sendBufferQueue.Count > 0) | ||||
|             { | ||||
|                 if (!sock.Connected) | ||||
|                     return new AsyncReply<bool>(false); | ||||
|                 var kv = sendBufferQueue.Dequeue(); | ||||
|  | ||||
|                 var rt = new AsyncReply<bool>(); | ||||
|  | ||||
|                 if (asyncSending || held) | ||||
|                 try | ||||
|                 { | ||||
|                     sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(rt, msg)); | ||||
|                     ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key); | ||||
|                 } | ||||
|                 else | ||||
|                 catch //(Exception ex) | ||||
|                 { | ||||
|                     asyncSending = true; | ||||
|                     asyncSending = false; | ||||
|                     try | ||||
|                     { | ||||
|                         ssl.BeginWrite(msg, 0, msg.Length, SendCallback, rt);// null); | ||||
|                         if (kv.Key != null) | ||||
|                             kv.Key.Trigger(false); | ||||
|  | ||||
|                         if (state != SocketState.Closed && !sock.Connected) | ||||
|                         { | ||||
|                             //state = SocketState.Terminated; | ||||
|                             Close(); | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     catch //(Exception ex2) | ||||
|                     { | ||||
|                         rt.TriggerError(ex); | ||||
|                         asyncSending = false; | ||||
|                         //state = SocketState.Terminated; | ||||
|                         //state = SocketState.Closed;// .Terminated; | ||||
|                         Close(); | ||||
|                     } | ||||
|  | ||||
|                     //Global.Log("TCPSocket", LogType.Error, ex.ToString()); | ||||
|                 } | ||||
|  | ||||
|                 return rt; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public ISocket Accept() | ||||
|         { | ||||
|             try | ||||
|             else | ||||
|             { | ||||
|                 return new SSLSocket(sock.Accept(), cert, true); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 state = SocketState.Closed;// .Terminated; | ||||
|                 return null; | ||||
|                 asyncSending = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     public IPEndPoint LocalEndPoint | ||||
|     { | ||||
|         get { return (IPEndPoint)sock.LocalEndPoint; } | ||||
|     } | ||||
|  | ||||
|     public SSLSocket() | ||||
|     { | ||||
|         sock = new Socket(AddressFamily.InterNetwork, | ||||
|                              SocketType.Stream, | ||||
|                              ProtocolType.Tcp); | ||||
|         receiveBuffer = new byte[sock.ReceiveBufferSize]; | ||||
|     } | ||||
|  | ||||
|     public SSLSocket(IPEndPoint localEndPoint, X509Certificate2 certificate) | ||||
|     { | ||||
|         // create the socket | ||||
|         sock = new Socket(AddressFamily.InterNetwork, | ||||
|                                          SocketType.Stream, | ||||
|                                          ProtocolType.Tcp); | ||||
|  | ||||
|         state = SocketState.Listening; | ||||
|  | ||||
|         // bind | ||||
|         sock.Bind(localEndPoint); | ||||
|  | ||||
|         // start listening | ||||
|         sock.Listen(UInt16.MaxValue); | ||||
|  | ||||
|         cert = certificate; | ||||
|     } | ||||
|  | ||||
|     public IPEndPoint RemoteEndPoint | ||||
|     { | ||||
|         get { return (IPEndPoint)sock.RemoteEndPoint; } | ||||
|     } | ||||
|  | ||||
|     public SocketState State | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return state; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public SSLSocket(Socket socket, X509Certificate2 certificate, bool authenticateAsServer) | ||||
|     { | ||||
|         cert = certificate; | ||||
|         sock = socket; | ||||
|         receiveBuffer = new byte[sock.ReceiveBufferSize]; | ||||
|  | ||||
|         ssl = new SslStream(new NetworkStream(sock)); | ||||
|  | ||||
|         server = authenticateAsServer; | ||||
|  | ||||
|         if (socket.Connected) | ||||
|             state = SocketState.Established; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Close() | ||||
|     { | ||||
|         if (state != SocketState.Closed)// && state != SocketState.Terminated) | ||||
|         { | ||||
|             state = SocketState.Closed; | ||||
|  | ||||
|             if (sock.Connected) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     sock.Shutdown(SocketShutdown.Both); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     //state = SocketState.Terminated; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Receiver?.NetworkClose(this); | ||||
|             //OnClose?.Invoke(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Send(byte[] message) | ||||
|     { | ||||
|         Send(message, 0, message.Length); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Send(byte[] message, int offset, int size) | ||||
|     { | ||||
|  | ||||
|  | ||||
|         var msg = message.Clip((uint)offset, (uint)size); | ||||
|  | ||||
|         lock (sendLock) | ||||
|         { | ||||
|  | ||||
|             if (!sock.Connected) | ||||
|                 return; | ||||
|  | ||||
|             if (asyncSending || held) | ||||
|             { | ||||
|                 sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(null, msg));// message.Clip((uint)offset, (uint)size)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 asyncSending = true; | ||||
|                 try | ||||
|                 { | ||||
|                     ssl.BeginWrite(msg, 0, msg.Length, SendCallback, null); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     asyncSending = false; | ||||
|                     //state = SocketState.Terminated; | ||||
|                     Close(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     //public void Send(byte[] message) | ||||
|     //{ | ||||
|     //    Send(message, 0, message.Length); | ||||
|     //} | ||||
|  | ||||
|     //public void Send(byte[] message, int offset, int size) | ||||
|     //{ | ||||
|     //    lock (sendLock) | ||||
|     //    { | ||||
|     //        if (asyncSending) | ||||
|     //        { | ||||
|     //            sendBufferQueue.Enqueue(message.Clip((uint)offset, (uint)size)); | ||||
|     //        } | ||||
|     //        else | ||||
|     //        { | ||||
|     //            asyncSending = true; | ||||
|     //            ssl.WriteAsync(message, offset, size).ContinueWith(DataSent); | ||||
|     //        } | ||||
|     //    } | ||||
|     //} | ||||
|  | ||||
|  | ||||
|  | ||||
|     //private void DataReceived(Task<int> task) | ||||
|     //{ | ||||
|     //    try | ||||
|     //    { | ||||
|     //        if (state == SocketState.Closed || state == SocketState.Terminated) | ||||
|     //            return; | ||||
|  | ||||
|     //        if (task.Result <= 0) | ||||
|     //        { | ||||
|     //            Close(); | ||||
|     //            return; | ||||
|     //        } | ||||
|  | ||||
|     //        receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)task.Result); | ||||
|     //        OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|     //        if (state == SocketState.Established) | ||||
|     //            ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived); | ||||
|     //    } | ||||
|     //    catch (Exception ex) | ||||
|     //    { | ||||
|     //        if (state != SocketState.Closed && !sock.Connected) | ||||
|     //        { | ||||
|     //            state = SocketState.Terminated; | ||||
|     //            Close(); | ||||
|     //        } | ||||
|  | ||||
|     //        Global.Log("SSLSocket", LogType.Error, ex.ToString()); | ||||
|     //    } | ||||
|     //} | ||||
|  | ||||
|  | ||||
|     public bool Begin() | ||||
|     { | ||||
|         if (began) | ||||
|             return false; | ||||
|  | ||||
|         began = true; | ||||
|  | ||||
|         if (server) | ||||
|             ssl.AuthenticateAsServer(cert); | ||||
|         else | ||||
|             ssl.AuthenticateAsClient(hostname); | ||||
|  | ||||
|         if (state == SocketState.Established) | ||||
|         { | ||||
|             ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|     public async AsyncReply<bool> BeginAsync() | ||||
|     { | ||||
|         if (began) | ||||
|             return false; | ||||
|  | ||||
|         began = true; | ||||
|  | ||||
|         if (server) | ||||
|             await ssl.AuthenticateAsServerAsync(cert); | ||||
|         else | ||||
|             await ssl.AuthenticateAsClientAsync(hostname); | ||||
|  | ||||
|         if (state == SocketState.Established) | ||||
|         { | ||||
|             ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|     private void ReceiveCallback(IAsyncResult results) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (state != SocketState.Established) | ||||
|                 return; | ||||
|  | ||||
|             var bytesReceived = ssl.EndRead(results); | ||||
|  | ||||
|             if (bytesReceived <= 0) | ||||
|             { | ||||
|                 Close(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)bytesReceived); | ||||
|  | ||||
|             //OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|  | ||||
|             Receiver?.NetworkReceive(this, receiveNetworkBuffer); | ||||
|  | ||||
|             ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); | ||||
|  | ||||
|         } | ||||
|         catch //(Exception ex) | ||||
|         { | ||||
|             if (state != SocketState.Closed && !sock.Connected) | ||||
|             { | ||||
|                 //state = SocketState.Terminated; | ||||
|                 Close(); | ||||
|             } | ||||
|  | ||||
|             //Global.Log("SSLSocket", LogType.Error, ex.ToString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public bool Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         Close(); | ||||
|         Receiver = null; | ||||
|         receiveNetworkBuffer = null; | ||||
|         OnDestroy?.Invoke(this); | ||||
|         OnDestroy = null; | ||||
|     } | ||||
|  | ||||
|     public async AsyncReply<ISocket> AcceptAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var s = await sock.AcceptAsync(); | ||||
|             return new SSLSocket(s, cert, true); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             state = SocketState.Closed;// Terminated; | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Hold() | ||||
|     { | ||||
|         held = true; | ||||
|     } | ||||
|  | ||||
|     public void Unhold() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             SendCallback(null); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Global.Log(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             held = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public AsyncReply<bool> SendAsync(byte[] message, int offset, int length) | ||||
|     { | ||||
|  | ||||
|         var msg = message.Clip((uint)offset, (uint)length); | ||||
|  | ||||
|         lock (sendLock) | ||||
|         { | ||||
|             if (!sock.Connected) | ||||
|                 return new AsyncReply<bool>(false); | ||||
|  | ||||
|             var rt = new AsyncReply<bool>(); | ||||
|  | ||||
|             if (asyncSending || held) | ||||
|             { | ||||
|                 sendBufferQueue.Enqueue(new KeyValuePair<AsyncReply<bool>, byte[]>(rt, msg)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 asyncSending = true; | ||||
|                 try | ||||
|                 { | ||||
|                     ssl.BeginWrite(msg, 0, msg.Length, SendCallback, rt);// null); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     rt.TriggerError(ex); | ||||
|                     asyncSending = false; | ||||
|                     //state = SocketState.Terminated; | ||||
|                     Close(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return rt; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public ISocket Accept() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return new SSLSocket(sock.Accept(), cert, true); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             state = SocketState.Closed;// .Terminated; | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,15 +28,13 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Net.Sockets | ||||
| namespace Esiur.Net.Sockets; | ||||
| public enum SocketState | ||||
| { | ||||
|     public enum SocketState | ||||
|     { | ||||
|         Initial, | ||||
|         Listening, | ||||
|         Connecting, | ||||
|         Established, | ||||
|         Closed, | ||||
|         //Terminated | ||||
|     } | ||||
|     Initial, | ||||
|     Listening, | ||||
|     Connecting, | ||||
|     Established, | ||||
|     Closed, | ||||
|     //Terminated | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -36,326 +36,324 @@ using Esiur.Resource; | ||||
| using Esiur.Data; | ||||
| using System.Globalization; | ||||
|  | ||||
| namespace Esiur.Net.Sockets | ||||
| namespace Esiur.Net.Sockets; | ||||
| public class WSocket : ISocket, INetworkReceiver<ISocket> | ||||
| { | ||||
|     public class WSocket : ISocket, INetworkReceiver<ISocket> | ||||
|     WebsocketPacket pkt_receive = new WebsocketPacket(); | ||||
|     WebsocketPacket pkt_send = new WebsocketPacket(); | ||||
|  | ||||
|     ISocket sock; | ||||
|     NetworkBuffer receiveNetworkBuffer = new NetworkBuffer(); | ||||
|     NetworkBuffer sendNetworkBuffer = new NetworkBuffer(); | ||||
|  | ||||
|     object sendLock = new object(); | ||||
|     bool held; | ||||
|  | ||||
|     //public event ISocketReceiveEvent OnReceive; | ||||
|     //public event ISocketConnectEvent OnConnect; | ||||
|     //public event ISocketCloseEvent OnClose; | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     long totalSent, totalReceived; | ||||
|  | ||||
|     bool processing = false; | ||||
|  | ||||
|     public IPEndPoint LocalEndPoint | ||||
|     { | ||||
|         WebsocketPacket pkt_receive = new WebsocketPacket(); | ||||
|         WebsocketPacket pkt_send = new WebsocketPacket(); | ||||
|         get { return (IPEndPoint)sock.LocalEndPoint; } | ||||
|     } | ||||
|  | ||||
|         ISocket sock; | ||||
|         NetworkBuffer receiveNetworkBuffer = new NetworkBuffer(); | ||||
|         NetworkBuffer sendNetworkBuffer = new NetworkBuffer(); | ||||
|     public IPEndPoint RemoteEndPoint | ||||
|     { | ||||
|         get { return sock.RemoteEndPoint; } | ||||
|     } | ||||
|  | ||||
|         object sendLock = new object(); | ||||
|         bool held; | ||||
|  | ||||
|         //public event ISocketReceiveEvent OnReceive; | ||||
|         //public event ISocketConnectEvent OnConnect; | ||||
|         //public event ISocketCloseEvent OnClose; | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         long totalSent, totalReceived; | ||||
|  | ||||
|         bool processing = false; | ||||
|  | ||||
|         public IPEndPoint LocalEndPoint | ||||
|     public SocketState State | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             get { return (IPEndPoint)sock.LocalEndPoint; } | ||||
|         } | ||||
|  | ||||
|         public IPEndPoint RemoteEndPoint | ||||
|         { | ||||
|             get { return sock.RemoteEndPoint; } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public SocketState State | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return sock.State; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public INetworkReceiver<ISocket> Receiver { get; set; } | ||||
|  | ||||
|         public WSocket(ISocket socket) | ||||
|         { | ||||
|             pkt_send.FIN = true; | ||||
|             pkt_send.Mask = false; | ||||
|             pkt_send.Opcode = WebsocketPacket.WSOpcode.BinaryFrame; | ||||
|             sock = socket; | ||||
|  | ||||
|             sock.Receiver = this; | ||||
|  | ||||
|             //sock.OnClose += Sock_OnClose; | ||||
|             //sock.OnConnect += Sock_OnConnect; | ||||
|             //sock.OnReceive += Sock_OnReceive; | ||||
|         } | ||||
|  | ||||
|         //private void Sock_OnReceive(NetworkBuffer buffer) | ||||
|         //{ | ||||
|  | ||||
|         //} | ||||
|  | ||||
|         //private void Sock_OnConnect() | ||||
|         //{ | ||||
|         //    OnConnect?.Invoke(); | ||||
|         //} | ||||
|  | ||||
|         //private void Sock_OnClose() | ||||
|         //{ | ||||
|         //    OnClose?.Invoke(); | ||||
|         //} | ||||
|  | ||||
|         public void Send(WebsocketPacket packet) | ||||
|         { | ||||
|             lock (sendLock) | ||||
|                 if (packet.Compose()) | ||||
|                     sock.Send(packet.Data); | ||||
|         } | ||||
|  | ||||
|         public void Send(byte[] message) | ||||
|         { | ||||
|  | ||||
|             lock (sendLock) | ||||
|             { | ||||
|                 if (held) | ||||
|                 { | ||||
|                     sendNetworkBuffer.Write(message); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     totalSent += message.Length; | ||||
|                     //Console.WriteLine("TX " + message.Length +"/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size)); | ||||
|  | ||||
|                     pkt_send.Message = message; | ||||
|  | ||||
|                     if (pkt_send.Compose()) | ||||
|                         sock?.Send(pkt_send.Data); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Send(byte[] message, int offset, int size) | ||||
|         { | ||||
|             lock (sendLock) | ||||
|             { | ||||
|                 if (held) | ||||
|                 { | ||||
|                     sendNetworkBuffer.Write(message, (uint)offset, (uint)size); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     totalSent += size; | ||||
|                     //Console.WriteLine("TX " + size + "/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size)); | ||||
|  | ||||
|                     pkt_send.Message = new byte[size]; | ||||
|                     Buffer.BlockCopy(message, offset, pkt_send.Message, 0, size); | ||||
|                     if (pkt_send.Compose()) | ||||
|                         sock.Send(pkt_send.Data); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void Close() | ||||
|         { | ||||
|             sock?.Close(); | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<bool> Connect(string hostname, ushort port) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public bool Begin() | ||||
|         { | ||||
|             return sock.Begin(); | ||||
|         } | ||||
|  | ||||
|         public bool Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             Close(); | ||||
|             //OnClose = null; | ||||
|             //OnConnect = null; | ||||
|             //OnReceive = null; | ||||
|             receiveNetworkBuffer = null; | ||||
|             //sock.OnReceive -= Sock_OnReceive; | ||||
|             //sock.OnClose -= Sock_OnClose; | ||||
|             //sock.OnConnect -= Sock_OnConnect; | ||||
|             sock.Receiver = null; | ||||
|             sock = null; | ||||
|             OnDestroy?.Invoke(this); | ||||
|             OnDestroy = null; | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<ISocket> AcceptAsync() | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         public void Hold() | ||||
|         { | ||||
|             //Console.WriteLine("WS Hold  "); | ||||
|             held = true; | ||||
|         } | ||||
|  | ||||
|         public void Unhold() | ||||
|         { | ||||
|             lock (sendLock) | ||||
|             { | ||||
|                 held = false; | ||||
|  | ||||
|                 var message = sendNetworkBuffer.Read(); | ||||
|  | ||||
|                 //Console.WriteLine("WS Unhold {0}", message == null ? 0 : message.Length); | ||||
|  | ||||
|                 if (message == null) | ||||
|                     return; | ||||
|  | ||||
|                 totalSent += message.Length; | ||||
|  | ||||
|                 pkt_send.Message = message; | ||||
|                 if (pkt_send.Compose()) | ||||
|                     sock.Send(pkt_send.Data); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<bool> SendAsync(byte[] message, int offset, int length) | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         public ISocket Accept() | ||||
|         { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
|  | ||||
|         public AsyncReply<bool> BeginAsync() | ||||
|         { | ||||
|             return sock.BeginAsync(); | ||||
|         } | ||||
|  | ||||
|         public void NetworkClose(ISocket sender) | ||||
|         { | ||||
|             Receiver?.NetworkClose(sender); | ||||
|         } | ||||
|  | ||||
|         public void NetworkReceive(ISocket sender, NetworkBuffer buffer) | ||||
|         { | ||||
|  | ||||
|             if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated) | ||||
|                 return; | ||||
|  | ||||
|             if (buffer.Protected) | ||||
|                 return; | ||||
|  | ||||
|             if (processing) | ||||
|                 return; | ||||
|  | ||||
|  | ||||
|             var msg = buffer.Read(); | ||||
|  | ||||
|             if (msg == null) | ||||
|                 return; | ||||
|  | ||||
|             var wsPacketLength = pkt_receive.Parse(msg, 0, (uint)msg.Length); | ||||
|             //Console.WriteLine("WSP: " + wsPacketLength); | ||||
|  | ||||
|             if (wsPacketLength < 0) | ||||
|             { | ||||
|                 buffer.Protect(msg, 0, (uint)msg.Length + (uint)-wsPacketLength); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             uint offset = 0; | ||||
|  | ||||
|             while (wsPacketLength > 0) | ||||
|             { | ||||
|                 if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.ConnectionClose) | ||||
|                 { | ||||
|                     Close(); | ||||
|                     return; | ||||
|                 } | ||||
|                 else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Ping) | ||||
|                 { | ||||
|                     var pkt_pong = new WebsocketPacket(); | ||||
|  | ||||
|                     pkt_pong.FIN = true; | ||||
|                     pkt_pong.Mask = false; | ||||
|                     pkt_pong.Opcode = WebsocketPacket.WSOpcode.Pong; | ||||
|                     pkt_pong.Message = pkt_receive.Message; | ||||
|                     offset += (uint)wsPacketLength; | ||||
|  | ||||
|                     Send(pkt_pong); | ||||
|                 } | ||||
|                 else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Pong) | ||||
|                 { | ||||
|                     offset += (uint)wsPacketLength; | ||||
|                 } | ||||
|                 else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.BinaryFrame | ||||
|                         || pkt_receive.Opcode == WebsocketPacket.WSOpcode.TextFrame | ||||
|                         || pkt_receive.Opcode == WebsocketPacket.WSOpcode.ContinuationFrame) | ||||
|                 { | ||||
|                     totalReceived += pkt_receive.Message.Length; | ||||
|                     //Console.WriteLine("RX " + pkt_receive.Message.Length + "/" + totalReceived);// + " " + DC.ToHex(message, 0, (uint)size)); | ||||
|  | ||||
|                     receiveNetworkBuffer.Write(pkt_receive.Message); | ||||
|                     offset += (uint)wsPacketLength; | ||||
|  | ||||
|                     //Console.WriteLine("WS IN: " + pkt_receive.Opcode.ToString() + " " + pkt_receive.Message.Length + " | " + offset + " " + string.Join(" ", pkt_receive.Message));//  DC.ToHex(pkt_receive.Message)); | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                     Console.WriteLine("Unknown WS opcode:" + pkt_receive.Opcode); | ||||
|  | ||||
|                 if (offset == msg.Length) | ||||
|                 { | ||||
|  | ||||
|                     //OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|                     Receiver?.NetworkReceive(this, receiveNetworkBuffer); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 wsPacketLength = pkt_receive.Parse(msg, offset, (uint)msg.Length); | ||||
|             } | ||||
|  | ||||
|             if (wsPacketLength < 0)//(offset < msg.Length) && (offset > 0)) | ||||
|             { | ||||
|                 //receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)msg.Length + (uint)-wsPacketLength); | ||||
|                 // save the incomplete packet to the heldBuffer queue | ||||
|  | ||||
|                 buffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)(msg.Length - offset) + (uint)-wsPacketLength); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             //Console.WriteLine("WS IN: " + receiveNetworkBuffer.Available); | ||||
|  | ||||
|             //OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|             Receiver?.NetworkReceive(this, receiveNetworkBuffer); | ||||
|  | ||||
|             processing = false; | ||||
|  | ||||
|             if (buffer.Available > 0 && !buffer.Protected) | ||||
|                 Receiver?.NetworkReceive(this, buffer); | ||||
|                 //Sock_OnReceive(buffer); | ||||
|         } | ||||
|  | ||||
|   | ||||
|         public void NetworkConnect(ISocket sender) | ||||
|         { | ||||
|             Receiver?.NetworkConnect(this); | ||||
|             return sock.State; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     public INetworkReceiver<ISocket> Receiver { get; set; } | ||||
|  | ||||
|     public WSocket(ISocket socket) | ||||
|     { | ||||
|         pkt_send.FIN = true; | ||||
|         pkt_send.Mask = false; | ||||
|         pkt_send.Opcode = WebsocketPacket.WSOpcode.BinaryFrame; | ||||
|         sock = socket; | ||||
|  | ||||
|         sock.Receiver = this; | ||||
|  | ||||
|         //sock.OnClose += Sock_OnClose; | ||||
|         //sock.OnConnect += Sock_OnConnect; | ||||
|         //sock.OnReceive += Sock_OnReceive; | ||||
|     } | ||||
|  | ||||
|     //private void Sock_OnReceive(NetworkBuffer buffer) | ||||
|     //{ | ||||
|  | ||||
|     //} | ||||
|  | ||||
|     //private void Sock_OnConnect() | ||||
|     //{ | ||||
|     //    OnConnect?.Invoke(); | ||||
|     //} | ||||
|  | ||||
|     //private void Sock_OnClose() | ||||
|     //{ | ||||
|     //    OnClose?.Invoke(); | ||||
|     //} | ||||
|  | ||||
|     public void Send(WebsocketPacket packet) | ||||
|     { | ||||
|         lock (sendLock) | ||||
|             if (packet.Compose()) | ||||
|                 sock.Send(packet.Data); | ||||
|     } | ||||
|  | ||||
|     public void Send(byte[] message) | ||||
|     { | ||||
|  | ||||
|         lock (sendLock) | ||||
|         { | ||||
|             if (held) | ||||
|             { | ||||
|                 sendNetworkBuffer.Write(message); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 totalSent += message.Length; | ||||
|                 //Console.WriteLine("TX " + message.Length +"/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size)); | ||||
|  | ||||
|                 pkt_send.Message = message; | ||||
|  | ||||
|                 if (pkt_send.Compose()) | ||||
|                     sock?.Send(pkt_send.Data); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Send(byte[] message, int offset, int size) | ||||
|     { | ||||
|         lock (sendLock) | ||||
|         { | ||||
|             if (held) | ||||
|             { | ||||
|                 sendNetworkBuffer.Write(message, (uint)offset, (uint)size); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 totalSent += size; | ||||
|                 //Console.WriteLine("TX " + size + "/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size)); | ||||
|  | ||||
|                 pkt_send.Message = new byte[size]; | ||||
|                 Buffer.BlockCopy(message, offset, pkt_send.Message, 0, size); | ||||
|                 if (pkt_send.Compose()) | ||||
|                     sock.Send(pkt_send.Data); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void Close() | ||||
|     { | ||||
|         sock?.Close(); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<bool> Connect(string hostname, ushort port) | ||||
|     { | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public bool Begin() | ||||
|     { | ||||
|         return sock.Begin(); | ||||
|     } | ||||
|  | ||||
|     public bool Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         Close(); | ||||
|         //OnClose = null; | ||||
|         //OnConnect = null; | ||||
|         //OnReceive = null; | ||||
|         receiveNetworkBuffer = null; | ||||
|         //sock.OnReceive -= Sock_OnReceive; | ||||
|         //sock.OnClose -= Sock_OnClose; | ||||
|         //sock.OnConnect -= Sock_OnConnect; | ||||
|         sock.Receiver = null; | ||||
|         sock = null; | ||||
|         OnDestroy?.Invoke(this); | ||||
|         OnDestroy = null; | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<ISocket> AcceptAsync() | ||||
|     { | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|     public void Hold() | ||||
|     { | ||||
|         //Console.WriteLine("WS Hold  "); | ||||
|         held = true; | ||||
|     } | ||||
|  | ||||
|     public void Unhold() | ||||
|     { | ||||
|         lock (sendLock) | ||||
|         { | ||||
|             held = false; | ||||
|  | ||||
|             var message = sendNetworkBuffer.Read(); | ||||
|  | ||||
|             //Console.WriteLine("WS Unhold {0}", message == null ? 0 : message.Length); | ||||
|  | ||||
|             if (message == null) | ||||
|                 return; | ||||
|  | ||||
|             totalSent += message.Length; | ||||
|  | ||||
|             pkt_send.Message = message; | ||||
|             if (pkt_send.Compose()) | ||||
|                 sock.Send(pkt_send.Data); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<bool> SendAsync(byte[] message, int offset, int length) | ||||
|     { | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|     public ISocket Accept() | ||||
|     { | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
|  | ||||
|     public AsyncReply<bool> BeginAsync() | ||||
|     { | ||||
|         return sock.BeginAsync(); | ||||
|     } | ||||
|  | ||||
|     public void NetworkClose(ISocket sender) | ||||
|     { | ||||
|         Receiver?.NetworkClose(sender); | ||||
|     } | ||||
|  | ||||
|     public void NetworkReceive(ISocket sender, NetworkBuffer buffer) | ||||
|     { | ||||
|  | ||||
|         if (sock.State == SocketState.Closed)// || sock.State == SocketState.Terminated) | ||||
|             return; | ||||
|  | ||||
|         if (buffer.Protected) | ||||
|             return; | ||||
|  | ||||
|         if (processing) | ||||
|             return; | ||||
|  | ||||
|  | ||||
|         var msg = buffer.Read(); | ||||
|  | ||||
|         if (msg == null) | ||||
|             return; | ||||
|  | ||||
|         var wsPacketLength = pkt_receive.Parse(msg, 0, (uint)msg.Length); | ||||
|         //Console.WriteLine("WSP: " + wsPacketLength); | ||||
|  | ||||
|         if (wsPacketLength < 0) | ||||
|         { | ||||
|             buffer.Protect(msg, 0, (uint)msg.Length + (uint)-wsPacketLength); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         uint offset = 0; | ||||
|  | ||||
|         while (wsPacketLength > 0) | ||||
|         { | ||||
|             if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.ConnectionClose) | ||||
|             { | ||||
|                 Close(); | ||||
|                 return; | ||||
|             } | ||||
|             else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Ping) | ||||
|             { | ||||
|                 var pkt_pong = new WebsocketPacket(); | ||||
|  | ||||
|                 pkt_pong.FIN = true; | ||||
|                 pkt_pong.Mask = false; | ||||
|                 pkt_pong.Opcode = WebsocketPacket.WSOpcode.Pong; | ||||
|                 pkt_pong.Message = pkt_receive.Message; | ||||
|                 offset += (uint)wsPacketLength; | ||||
|  | ||||
|                 Send(pkt_pong); | ||||
|             } | ||||
|             else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Pong) | ||||
|             { | ||||
|                 offset += (uint)wsPacketLength; | ||||
|             } | ||||
|             else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.BinaryFrame | ||||
|                     || pkt_receive.Opcode == WebsocketPacket.WSOpcode.TextFrame | ||||
|                     || pkt_receive.Opcode == WebsocketPacket.WSOpcode.ContinuationFrame) | ||||
|             { | ||||
|                 totalReceived += pkt_receive.Message.Length; | ||||
|                 //Console.WriteLine("RX " + pkt_receive.Message.Length + "/" + totalReceived);// + " " + DC.ToHex(message, 0, (uint)size)); | ||||
|  | ||||
|                 receiveNetworkBuffer.Write(pkt_receive.Message); | ||||
|                 offset += (uint)wsPacketLength; | ||||
|  | ||||
|                 //Console.WriteLine("WS IN: " + pkt_receive.Opcode.ToString() + " " + pkt_receive.Message.Length + " | " + offset + " " + string.Join(" ", pkt_receive.Message));//  DC.ToHex(pkt_receive.Message)); | ||||
|  | ||||
|             } | ||||
|             else | ||||
|                 Console.WriteLine("Unknown WS opcode:" + pkt_receive.Opcode); | ||||
|  | ||||
|             if (offset == msg.Length) | ||||
|             { | ||||
|  | ||||
|                 //OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|                 Receiver?.NetworkReceive(this, receiveNetworkBuffer); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             wsPacketLength = pkt_receive.Parse(msg, offset, (uint)msg.Length); | ||||
|         } | ||||
|  | ||||
|         if (wsPacketLength < 0)//(offset < msg.Length) && (offset > 0)) | ||||
|         { | ||||
|             //receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)msg.Length + (uint)-wsPacketLength); | ||||
|             // save the incomplete packet to the heldBuffer queue | ||||
|  | ||||
|             buffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)(msg.Length - offset) + (uint)-wsPacketLength); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         //Console.WriteLine("WS IN: " + receiveNetworkBuffer.Available); | ||||
|  | ||||
|         //OnReceive?.Invoke(receiveNetworkBuffer); | ||||
|         Receiver?.NetworkReceive(this, receiveNetworkBuffer); | ||||
|  | ||||
|         processing = false; | ||||
|  | ||||
|         if (buffer.Available > 0 && !buffer.Protected) | ||||
|             Receiver?.NetworkReceive(this, buffer); | ||||
|         //Sock_OnReceive(buffer); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public void NetworkConnect(ISocket sender) | ||||
|     { | ||||
|         Receiver?.NetworkConnect(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,35 +32,34 @@ using System.Collections; | ||||
| using Esiur.Misc; | ||||
| using Esiur.Data; | ||||
|  | ||||
| namespace Esiur.Net.TCP | ||||
| namespace Esiur.Net.TCP; | ||||
| public class TCPConnection : NetworkConnection | ||||
| { | ||||
|     public class TCPConnection:NetworkConnection    { | ||||
|  | ||||
|         private KeyList<string, object> variables = new KeyList<string, object>(); | ||||
|     private KeyList<string, object> variables = new KeyList<string, object>(); | ||||
|  | ||||
|         public TCPServer Server { get; internal set; } | ||||
|     public TCPServer Server { get; internal set; } | ||||
|  | ||||
|         public KeyList<string, object> Variables | ||||
|     public KeyList<string, object> Variables | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return variables; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         protected override void Connected() | ||||
|         { | ||||
|             // do nothing | ||||
|         } | ||||
|  | ||||
|         protected override void DataReceived(NetworkBuffer buffer) | ||||
|         { | ||||
|             Server?.Execute(this, buffer); | ||||
|         } | ||||
|  | ||||
|         protected override void Disconencted() | ||||
|         { | ||||
|             // do nothing | ||||
|             return variables; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected override void Connected() | ||||
|     { | ||||
|         // do nothing | ||||
|     } | ||||
|  | ||||
|     protected override void DataReceived(NetworkBuffer buffer) | ||||
|     { | ||||
|         Server?.Execute(this, buffer); | ||||
|     } | ||||
|  | ||||
|     protected override void Disconencted() | ||||
|     { | ||||
|         // do nothing | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,35 +32,34 @@ using Esiur.Net.Sockets; | ||||
| using Esiur.Core; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.TCP | ||||
| namespace Esiur.Net.TCP; | ||||
|  | ||||
| public abstract class TCPFilter : IResource | ||||
| { | ||||
|     public abstract class TCPFilter: IResource | ||||
|     public Instance Instance | ||||
|     { | ||||
|         public Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|     public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|         public virtual bool Connected(TCPConnection sender) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     public virtual bool Connected(TCPConnection sender) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|         public virtual bool Disconnected(TCPConnection sender) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     public virtual bool Disconnected(TCPConnection sender) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|         public abstract bool Execute(byte[] msg, NetworkBuffer data, TCPConnection sender); | ||||
|     public abstract bool Execute(byte[] msg, NetworkBuffer data, TCPConnection sender); | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -34,123 +34,121 @@ using Esiur.Core; | ||||
| using System.Net; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.TCP | ||||
| namespace Esiur.Net.TCP; | ||||
| public class TCPServer : NetworkServer<TCPConnection>, IResource | ||||
| { | ||||
|     public class TCPServer : NetworkServer<TCPConnection>, IResource | ||||
|  | ||||
|     [Attribute] | ||||
|     public string IP | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|     [Attribute] | ||||
|     public ushort Port | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|     //[Storable] | ||||
|     //public uint Timeout | ||||
|     //{ | ||||
|     //    get; | ||||
|     //    set; | ||||
|     //} | ||||
|     //[Attribute] | ||||
|     //public uint Clock | ||||
|     //{ | ||||
|     //    get; | ||||
|     //    set; | ||||
|     //} | ||||
|     public Instance Instance { get; set; } | ||||
|  | ||||
|     TCPFilter[] filters = null; | ||||
|  | ||||
|  | ||||
|     public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|         { | ||||
|             TCPSocket listener; | ||||
|  | ||||
|  | ||||
|             if (IP != null) | ||||
|                 listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port)); | ||||
|             else | ||||
|                 listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port)); | ||||
|  | ||||
|             Start(listener); | ||||
|  | ||||
|  | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.Terminate) | ||||
|         { | ||||
|             Stop(); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemReload) | ||||
|         { | ||||
|             Trigger(ResourceTrigger.Terminate); | ||||
|             Trigger(ResourceTrigger.Initialize); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemInitialized) | ||||
|         { | ||||
|             Instance.Children<TCPFilter>().Then(x => filters = x); | ||||
|         } | ||||
|  | ||||
|         return new AsyncReply<bool>(true); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     internal bool Execute(TCPConnection sender, NetworkBuffer data) | ||||
|     { | ||||
|         var msg = data.Read(); | ||||
|  | ||||
|         foreach (var filter in filters) | ||||
|         { | ||||
|             if (filter.Execute(msg, data, sender)) | ||||
|                 return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private void SessionModified(TCPConnection session, string key, object newValue) | ||||
|     { | ||||
|  | ||||
|         [Attribute] | ||||
|         public string IP | ||||
|     } | ||||
|  | ||||
|     protected override void ClientDisconnected(TCPConnection connection) | ||||
|     { | ||||
|  | ||||
|         foreach (var filter in filters) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             filter.Disconnected(connection); | ||||
|         } | ||||
|         [Attribute] | ||||
|         public ushort Port | ||||
|     } | ||||
|  | ||||
|     public override void Add(TCPConnection connection) | ||||
|     { | ||||
|         connection.Server = this; | ||||
|         base.Add(connection); | ||||
|     } | ||||
|  | ||||
|     public override void Remove(TCPConnection connection) | ||||
|     { | ||||
|         connection.Server = null; | ||||
|         base.Remove(connection); | ||||
|     } | ||||
|  | ||||
|     protected override void ClientConnected(TCPConnection connection) | ||||
|     { | ||||
|         foreach (var filter in filters) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|             filter.Connected(connection); | ||||
|         } | ||||
|         //[Storable] | ||||
|         //public uint Timeout | ||||
|         //{ | ||||
|         //    get; | ||||
|         //    set; | ||||
|         //} | ||||
|         //[Attribute] | ||||
|         //public uint Clock | ||||
|         //{ | ||||
|         //    get; | ||||
|         //    set; | ||||
|         //} | ||||
|         public Instance Instance { get; set; } | ||||
|     } | ||||
|  | ||||
|         TCPFilter[] filters = null; | ||||
|  | ||||
|  | ||||
|         public AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|             { | ||||
|                 TCPSocket listener; | ||||
|  | ||||
|  | ||||
|                 if (IP != null) | ||||
|                     listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(IP), Port)); | ||||
|                 else | ||||
|                     listener = new TCPSocket(new IPEndPoint(IPAddress.Any, Port)); | ||||
|  | ||||
|                 Start(listener); | ||||
|  | ||||
|  | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.Terminate) | ||||
|             { | ||||
|                 Stop(); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemReload) | ||||
|             { | ||||
|                 Trigger(ResourceTrigger.Terminate); | ||||
|                 Trigger(ResourceTrigger.Initialize); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemInitialized) | ||||
|             { | ||||
|                 Instance.Children<TCPFilter>().Then(x => filters = x); | ||||
|             } | ||||
|  | ||||
|             return new AsyncReply<bool>(true); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         internal bool Execute(TCPConnection sender, NetworkBuffer data) | ||||
|         { | ||||
|             var msg = data.Read(); | ||||
|  | ||||
|             foreach (var filter in filters) | ||||
|             { | ||||
|                 if (filter.Execute(msg, data, sender)) | ||||
|                     return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         private void SessionModified(TCPConnection session, string key, object newValue) | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         protected override void ClientDisconnected(TCPConnection connection) | ||||
|         { | ||||
|              | ||||
|             foreach (var filter in filters) | ||||
|             { | ||||
|                 filter.Disconnected(connection); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public override void Add(TCPConnection connection) | ||||
|         { | ||||
|             connection.Server = this; | ||||
|             base.Add(connection); | ||||
|         } | ||||
|  | ||||
|         public override void Remove(TCPConnection connection) | ||||
|         { | ||||
|             connection.Server = null; | ||||
|             base.Remove(connection); | ||||
|         } | ||||
|  | ||||
|         protected override void ClientConnected(TCPConnection connection) | ||||
|         { | ||||
|             foreach (var filter in filters) | ||||
|             { | ||||
|                 filter.Connected(connection); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|      } | ||||
| } | ||||
|   | ||||
| @@ -28,10 +28,8 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Net.TCP | ||||
| namespace Esiur.Net.TCP; | ||||
| public class TCPSession : NetworkSession | ||||
| { | ||||
|     public class TCPSession : NetworkSession | ||||
|     { | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,26 +32,24 @@ using Esiur.Data; | ||||
| using Esiur.Core; | ||||
| using Esiur.Resource; | ||||
|  | ||||
| namespace Esiur.Net.UDP | ||||
| namespace Esiur.Net.UDP; | ||||
| public abstract class UDPFilter : IResource | ||||
| { | ||||
|     public abstract class UDPFilter : IResource | ||||
|     public Instance Instance | ||||
|     { | ||||
|         public Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|         public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|         public abstract bool Execute(byte[] data, IPEndPoint sender); | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|     public abstract bool Execute(byte[] data, IPEndPoint sender); | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -33,172 +33,170 @@ using Esiur.Misc; | ||||
| using Esiur.Resource; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Net.UDP | ||||
| namespace Esiur.Net.UDP; | ||||
|  | ||||
| /* public class IIPConnection | ||||
| { | ||||
|     public EndPoint SenderPoint; | ||||
|     public  | ||||
| }*/ | ||||
| public class UDPServer : IResource | ||||
| { | ||||
|     Thread receiver; | ||||
|     UdpClient udp; | ||||
|     UDPFilter[] filters = new UDPFilter[0]; | ||||
|  | ||||
|     /* public class IIPConnection | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public Instance Instance | ||||
|     { | ||||
|         public EndPoint SenderPoint; | ||||
|         public  | ||||
|     }*/ | ||||
|     public class UDPServer : IResource | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|     [Attribute] | ||||
|     string IP | ||||
|     { | ||||
|         Thread receiver; | ||||
|         UdpClient udp; | ||||
|         UDPFilter[] filters = new UDPFilter[0]; | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|     [Attribute] | ||||
|     ushort Port | ||||
|     { | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|         public Instance Instance | ||||
|     private void Receiving() | ||||
|     { | ||||
|         IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); | ||||
|  | ||||
|  | ||||
|         while (true) | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|             byte[] b = udp.Receive(ref ep); | ||||
|  | ||||
|         [Attribute] | ||||
|         string IP | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|         [Attribute] | ||||
|         ushort Port | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|          private void Receiving() | ||||
|         { | ||||
|             IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); | ||||
|  | ||||
|  | ||||
|             while (true) | ||||
|             foreach (var child in filters) | ||||
|             { | ||||
|                 byte[] b = udp.Receive(ref ep); | ||||
|                 var f = child as UDPFilter; | ||||
|  | ||||
|                 foreach (var child in filters) | ||||
|                 try | ||||
|                 { | ||||
|                     var f = child as UDPFilter; | ||||
|  | ||||
|                     try | ||||
|                     if (f.Execute(b, ep)) | ||||
|                     { | ||||
|                         if (f.Execute(b, ep)) | ||||
|                         { | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         Global.Log("UDPServer", LogType.Error, ex.ToString()); | ||||
|                         //Console.WriteLine(ex.ToString()); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     Global.Log("UDPServer", LogType.Error, ex.ToString()); | ||||
|                     //Console.WriteLine(ex.ToString()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         public bool Send(byte[] Data, int Count, IPEndPoint EP) | ||||
|     public bool Send(byte[] Data, int Count, IPEndPoint EP) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 udp.Send(Data, Count, EP); | ||||
|                 return true; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         public bool Send(byte[] Data, IPEndPoint EP) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 udp.Send(Data, Data.Length, EP); | ||||
|                 return true; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         public bool Send(byte[] Data, int Count, string Host, int Port) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 udp.Send(Data, Count, Host, Port); | ||||
|                 return true; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         public bool Send(byte[] Data, string Host, int Port) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 udp.Send(Data, Data.Length, Host, Port); | ||||
|                 return true; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         public bool Send(string Data, IPEndPoint EP) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 udp.Send(Encoding.Default.GetBytes(Data), Data.Length, EP); | ||||
|                 return true; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         public bool Send(string Data, string Host, int Port) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 udp.Send(Encoding.Default.GetBytes(Data), Data.Length, Host, Port); | ||||
|                 return true; | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Destroy() | ||||
|         { | ||||
|             udp.Close(); | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         async AsyncReply<bool> IResource.Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|             { | ||||
|                 var address = IP == null ? IPAddress.Any : IPAddress.Parse(IP); | ||||
|  | ||||
|                 udp = new UdpClient(new IPEndPoint(address, Port)); | ||||
|  | ||||
|                 receiver = new Thread(Receiving); | ||||
|                 receiver.Start(); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.Terminate) | ||||
|             { | ||||
|                 if (receiver != null) | ||||
|                     receiver.Abort(); | ||||
|             } | ||||
|             else if (trigger == ResourceTrigger.SystemInitialized) | ||||
|             { | ||||
|                 filters = await Instance.Children<UDPFilter>(); | ||||
|             } | ||||
|  | ||||
|             udp.Send(Data, Count, EP); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|     public bool Send(byte[] Data, IPEndPoint EP) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             udp.Send(Data, Data.Length, EP); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     public bool Send(byte[] Data, int Count, string Host, int Port) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             udp.Send(Data, Count, Host, Port); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     public bool Send(byte[] Data, string Host, int Port) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             udp.Send(Data, Data.Length, Host, Port); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     public bool Send(string Data, IPEndPoint EP) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             udp.Send(Encoding.Default.GetBytes(Data), Data.Length, EP); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     public bool Send(string Data, string Host, int Port) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             udp.Send(Encoding.Default.GetBytes(Data), Data.Length, Host, Port); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void Destroy() | ||||
|     { | ||||
|         udp.Close(); | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|     async AsyncReply<bool> IResource.Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|         { | ||||
|             var address = IP == null ? IPAddress.Any : IPAddress.Parse(IP); | ||||
|  | ||||
|             udp = new UdpClient(new IPEndPoint(address, Port)); | ||||
|  | ||||
|             receiver = new Thread(Receiving); | ||||
|             receiver.Start(); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.Terminate) | ||||
|         { | ||||
|             if (receiver != null) | ||||
|                 receiver.Abort(); | ||||
|         } | ||||
|         else if (trigger == ResourceTrigger.SystemInitialized) | ||||
|         { | ||||
|             filters = await Instance.Children<UDPFilter>(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,177 +14,176 @@ using Esiur.Data; | ||||
| using System.IO; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Proxy | ||||
| namespace Esiur.Proxy; | ||||
|  | ||||
| [Generator] | ||||
| public class ResourceGenerator : ISourceGenerator | ||||
| { | ||||
|     [Generator] | ||||
|     public class ResourceGenerator : ISourceGenerator | ||||
|  | ||||
|     private KeyList<string, TypeTemplate[]> cache = new(); | ||||
|     // private List<string> inProgress = new(); | ||||
|  | ||||
|     public void Initialize(GeneratorInitializationContext context) | ||||
|     { | ||||
|         // Register receiver | ||||
|  | ||||
|         context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     void ReportError(GeneratorExecutionContext context, string title, string msg, string category) | ||||
|     { | ||||
|         context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("MySG001", title, msg, category, DiagnosticSeverity.Error, true), Location.None)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     void GenerateModel(GeneratorExecutionContext context, TypeTemplate[] templates) | ||||
|     { | ||||
|         foreach (var tmp in templates) | ||||
|         { | ||||
|             if (tmp.Type == TemplateType.Resource) | ||||
|             { | ||||
|                 var source = TemplateGenerator.GenerateClass(tmp, templates); | ||||
|                 // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source); | ||||
|                 context.AddSource(tmp.ClassName + ".Generated.cs", source); | ||||
|             } | ||||
|             else if (tmp.Type == TemplateType.Record) | ||||
|             { | ||||
|                 var source = TemplateGenerator.GenerateRecord(tmp, templates); | ||||
|                 // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source); | ||||
|                 context.AddSource(tmp.ClassName + ".Generated.cs", source); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // generate info class | ||||
|  | ||||
|  | ||||
|         var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " + | ||||
|                             string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})")) | ||||
|                         + " }; \r\n public static Type[] Records { get; } = new Type[] { " + | ||||
|                             string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})")) | ||||
|                         + " }; " + | ||||
|  | ||||
|                         "\r\n } \r\n}"; | ||||
|  | ||||
|         //File.WriteAllText($@"C:\gen\Esiur.Generated.cs", gen); | ||||
|  | ||||
|         context.AddSource("Esiur.Generated.cs", typesFile); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public void Execute(GeneratorExecutionContext context) | ||||
|     { | ||||
|  | ||||
|         private KeyList<string, TypeTemplate[]> cache = new(); | ||||
|         // private List<string> inProgress = new(); | ||||
|         if (!(context.SyntaxContextReceiver is ResourceGeneratorReceiver receiver)) | ||||
|             return; | ||||
|  | ||||
|         public void Initialize(GeneratorInitializationContext context) | ||||
|         //if (receiver.Imports.Count > 0 && !Debugger.IsAttached) | ||||
|         //{ | ||||
|         //    Debugger.Launch(); | ||||
|         //} | ||||
|  | ||||
|         foreach (var path in receiver.Imports) | ||||
|         { | ||||
|             // Register receiver | ||||
|             if (!TemplateGenerator.urlRegex.IsMatch(path)) | ||||
|                 continue; | ||||
|  | ||||
|             context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver()); | ||||
|         } | ||||
|  | ||||
|        | ||||
|         void ReportError(GeneratorExecutionContext context, string title, string msg, string category) | ||||
|         { | ||||
|             context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("MySG001", title, msg, category, DiagnosticSeverity.Error, true), Location.None)); | ||||
|         } | ||||
|             //File.WriteAllLines("C:\\gen\\ref.log", context.Compilation.ReferencedAssemblyNames.Select(x => x.ToString())); | ||||
|  | ||||
|       | ||||
|         void GenerateModel(GeneratorExecutionContext context, TypeTemplate[] templates) | ||||
|         { | ||||
|             foreach (var tmp in templates) | ||||
|             if (cache.Contains(path)) | ||||
|             { | ||||
|                 if (tmp.Type == TemplateType.Resource) | ||||
|                 { | ||||
|                     var source = TemplateGenerator.GenerateClass(tmp, templates); | ||||
|                     // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source); | ||||
|                     context.AddSource(tmp.ClassName + ".Generated.cs", source); | ||||
|                 } | ||||
|                 else if (tmp.Type == TemplateType.Record) | ||||
|                 { | ||||
|                     var source = TemplateGenerator.GenerateRecord(tmp, templates); | ||||
|                     // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source); | ||||
|                     context.AddSource(tmp.ClassName + ".Generated.cs", source); | ||||
|                 } | ||||
|                 GenerateModel(context, cache[path]); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // generate info class | ||||
|             // Syncronization | ||||
|             //if (inProgress.Contains(path)) | ||||
|             //  continue; | ||||
|  | ||||
|             //inProgress.Add(path); | ||||
|  | ||||
|             var url = TemplateGenerator.urlRegex.Split(path); | ||||
|  | ||||
|  | ||||
|             var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " + | ||||
|                                 string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})")) | ||||
|                             + " }; \r\n public static Type[] Records { get; } = new Type[] { " + | ||||
|                                 string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})")) | ||||
|                             + " }; " + | ||||
|             try | ||||
|             { | ||||
|                 var con = Warehouse.Get<DistributedConnection>(url[1] + "://" + url[2]).Wait(20000); | ||||
|                 var templates = con.GetLinkTemplates(url[3]).Wait(60000); | ||||
|  | ||||
|                             "\r\n } \r\n}"; | ||||
|                 cache[path] = templates; | ||||
|  | ||||
|             //File.WriteAllText($@"C:\gen\Esiur.Generated.cs", gen); | ||||
|                 // make sources | ||||
|                 GenerateModel(context, templates); | ||||
|  | ||||
|             context.AddSource("Esiur.Generated.cs", typesFile); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 ReportError(context, ex.Source, ex.Message, "Esiur"); | ||||
|                 //System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n"); | ||||
|             } | ||||
|  | ||||
|             //inProgress.Remove(path); | ||||
|         } | ||||
|  | ||||
|  | ||||
|    | ||||
|         public void Execute(GeneratorExecutionContext context) | ||||
|         //#if DEBUG | ||||
|  | ||||
|         //#endif | ||||
|  | ||||
|         //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0); | ||||
|  | ||||
|         foreach (var ci in receiver.Classes.Values) | ||||
|         { | ||||
|  | ||||
|             if (!(context.SyntaxContextReceiver is ResourceGeneratorReceiver receiver)) | ||||
|                 return; | ||||
|  | ||||
|             //if (receiver.Imports.Count > 0 && !Debugger.IsAttached) | ||||
|             //{ | ||||
|             //    Debugger.Launch(); | ||||
|             //} | ||||
|  | ||||
|             foreach (var path in receiver.Imports) | ||||
|             try | ||||
|             { | ||||
|                 if (!TemplateGenerator.urlRegex.IsMatch(path)) | ||||
|                     continue; | ||||
|  | ||||
|  | ||||
|                 //File.WriteAllLines("C:\\gen\\ref.log", context.Compilation.ReferencedAssemblyNames.Select(x => x.ToString())); | ||||
|  | ||||
|                 if (cache.Contains(path)) | ||||
|                 { | ||||
|                     GenerateModel(context, cache[path]); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 // Syncronization | ||||
|                 //if (inProgress.Contains(path)) | ||||
|                 //  continue; | ||||
|  | ||||
|                 //inProgress.Add(path); | ||||
|  | ||||
|                 var url = TemplateGenerator.urlRegex.Split(path); | ||||
|  | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     var con = Warehouse.Get<DistributedConnection>(url[1] + "://" + url[2]).Wait(20000); | ||||
|                     var templates = con.GetLinkTemplates(url[3]).Wait(60000); | ||||
|  | ||||
|                     cache[path] = templates; | ||||
|  | ||||
|                     // make sources | ||||
|                     GenerateModel(context, templates); | ||||
|  | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     ReportError(context, ex.Source, ex.Message, "Esiur"); | ||||
|                     //System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n"); | ||||
|                 } | ||||
|  | ||||
|                 //inProgress.Remove(path); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             //#if DEBUG | ||||
|  | ||||
|             //#endif | ||||
|  | ||||
|             //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0); | ||||
|  | ||||
|             foreach (var ci in receiver.Classes.Values) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|  | ||||
|                     var code = @$"using Esiur.Resource;  | ||||
|                 var code = @$"using Esiur.Resource;  | ||||
| using Esiur.Core;  | ||||
| namespace { ci.ClassSymbol.ContainingNamespace.ToDisplayString() } {{ | ||||
| "; | ||||
|  | ||||
|                     if (ci.HasInterface) | ||||
|                         code += $"public partial class {ci.Name} {{"; | ||||
|                     else | ||||
|                     { | ||||
|                         code += @$"public partial class {ci.Name} : IResource {{ | ||||
|                 if (ci.HasInterface) | ||||
|                     code += $"public partial class {ci.Name} {{"; | ||||
|                 else | ||||
|                 { | ||||
|                     code += @$"public partial class {ci.Name} : IResource {{ | ||||
| public Instance Instance {{ get; set; }} | ||||
| public event DestroyedEvent OnDestroy; | ||||
| public virtual void Destroy() {{ OnDestroy?.Invoke(this); }} | ||||
| "; | ||||
|  | ||||
|                         if (!ci.HasTrigger) | ||||
|                             code += "public AsyncReply<bool> Trigger(ResourceTrigger trigger) => new AsyncReply<bool>(true);\r\n"; | ||||
|                     } | ||||
|  | ||||
|                     //Debugger.Launch(); | ||||
|  | ||||
|                     foreach (var f in ci.Fields) | ||||
|                     { | ||||
|                         var givenName = f.GetAttributes().Where(x=>x.AttributeClass.Name == "PublicAttribute").FirstOrDefault()?.ConstructorArguments.FirstOrDefault().Value; | ||||
|  | ||||
|                         var fn = f.Name; | ||||
|                         var pn = givenName ?? fn.Substring(0, 1).ToUpper() + fn.Substring(1); | ||||
|  | ||||
|                         //System.IO.File.AppendAllText("c:\\gen\\fields.txt", fn + " -> " + pn + "\r\n"); | ||||
|  | ||||
|                         // copy attributes  | ||||
|                         var attrs = string.Join(" ", f.GetAttributes().Select(x => $"[{x.ToString()}]")); | ||||
|                         code += $"{attrs} public {f.Type} {pn} {{ get => {fn}; set {{ {fn} = value; Instance?.Modified(); }} }}\r\n"; | ||||
|                     } | ||||
|  | ||||
|                     code += "}}\r\n"; | ||||
|  | ||||
|                     //System.IO.File.WriteAllText("c:\\gen\\" + ci.Name + "_esiur.cs", code); | ||||
|                     context.AddSource(ci.Name + ".Generated.cs", code); | ||||
|  | ||||
|                     if (!ci.HasTrigger) | ||||
|                         code += "public AsyncReply<bool> Trigger(ResourceTrigger trigger) => new AsyncReply<bool>(true);\r\n"; | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|  | ||||
|                 //Debugger.Launch(); | ||||
|  | ||||
|                 foreach (var f in ci.Fields) | ||||
|                 { | ||||
|                     //System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n"); | ||||
|                     var givenName = f.GetAttributes().Where(x => x.AttributeClass.Name == "PublicAttribute").FirstOrDefault()?.ConstructorArguments.FirstOrDefault().Value; | ||||
|  | ||||
|                     var fn = f.Name; | ||||
|                     var pn = givenName ?? fn.Substring(0, 1).ToUpper() + fn.Substring(1); | ||||
|  | ||||
|                     //System.IO.File.AppendAllText("c:\\gen\\fields.txt", fn + " -> " + pn + "\r\n"); | ||||
|  | ||||
|                     // copy attributes  | ||||
|                     var attrs = string.Join(" ", f.GetAttributes().Select(x => $"[{x.ToString()}]")); | ||||
|                     code += $"{attrs} public {f.Type} {pn} {{ get => {fn}; set {{ {fn} = value; Instance?.Modified(); }} }}\r\n"; | ||||
|                 } | ||||
|  | ||||
|                 code += "}}\r\n"; | ||||
|  | ||||
|                 //System.IO.File.WriteAllText("c:\\gen\\" + ci.Name + "_esiur.cs", code); | ||||
|                 context.AddSource(ci.Name + ".Generated.cs", code); | ||||
|  | ||||
|             } | ||||
|             catch //(Exception ex) | ||||
|             { | ||||
|                 //System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -4,18 +4,16 @@ using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Proxy | ||||
| namespace Esiur.Proxy; | ||||
| public struct ResourceGeneratorClassInfo | ||||
| { | ||||
|     public struct ResourceGeneratorClassInfo | ||||
|     { | ||||
|         public string Name { get; set; } | ||||
|         public bool HasInterface { get; set; } | ||||
|     public string Name { get; set; } | ||||
|     public bool HasInterface { get; set; } | ||||
|  | ||||
|         public bool HasTrigger { get; set; } | ||||
|         public List<IFieldSymbol> Fields { get; set; } | ||||
|         public ITypeSymbol ClassSymbol { get; set; } | ||||
|     public bool HasTrigger { get; set; } | ||||
|     public List<IFieldSymbol> Fields { get; set; } | ||||
|     public ITypeSymbol ClassSymbol { get; set; } | ||||
|  | ||||
|         public ClassDeclarationSyntax ClassDeclaration { get; set; } | ||||
|     public ClassDeclarationSyntax ClassDeclaration { get; set; } | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,11 +3,9 @@ using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Proxy | ||||
| namespace Esiur.Proxy; | ||||
| public struct ResourceGeneratorFieldInfo | ||||
| { | ||||
|     public struct ResourceGeneratorFieldInfo | ||||
|     { | ||||
|         public IFieldSymbol FieldSymbol { get; set; } | ||||
|         public string[] Attributes { get; set; } | ||||
|     } | ||||
|     public IFieldSymbol FieldSymbol { get; set; } | ||||
|     public string[] Attributes { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -6,92 +6,91 @@ using System.Diagnostics; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Proxy | ||||
| namespace Esiur.Proxy; | ||||
| public class ResourceGeneratorReceiver : ISyntaxContextReceiver | ||||
| { | ||||
|     public class ResourceGeneratorReceiver : ISyntaxContextReceiver | ||||
|  | ||||
|     public Dictionary<string, ResourceGeneratorClassInfo> Classes { get; } = new(); | ||||
|  | ||||
|     public List<string> Imports { get; } = new(); | ||||
|  | ||||
|     public void OnVisitSyntaxNode(GeneratorSyntaxContext context) | ||||
|     { | ||||
|  | ||||
|         public Dictionary<string, ResourceGeneratorClassInfo> Classes { get; } = new(); | ||||
|  | ||||
|         public List<string> Imports { get; } = new (); | ||||
|  | ||||
|         public void OnVisitSyntaxNode(GeneratorSyntaxContext context) | ||||
|         if (context.Node is ClassDeclarationSyntax) | ||||
|         { | ||||
|             var cds = context.Node as ClassDeclarationSyntax; | ||||
|             var cls = context.SemanticModel.GetDeclaredSymbol(cds) as ITypeSymbol; | ||||
|             var attrs = cls.GetAttributes(); | ||||
|  | ||||
|             if (context.Node is ClassDeclarationSyntax) | ||||
|             var imports = attrs.Where(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ImportAttribute"); | ||||
|  | ||||
|             foreach (var import in imports) | ||||
|             { | ||||
|                 var cds = context.Node as ClassDeclarationSyntax; | ||||
|                 var cls = context.SemanticModel.GetDeclaredSymbol(cds) as ITypeSymbol; | ||||
|                 var attrs = cls.GetAttributes(); | ||||
|                 // Debugger.Launch(); | ||||
|  | ||||
|                 var imports = attrs.Where(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ImportAttribute"); | ||||
|                 var url = import.ConstructorArguments.First().Value.ToString(); | ||||
|  | ||||
|                 foreach (var import in imports) | ||||
|                 if (!Imports.Contains(url)) | ||||
|                     Imports.Add(url); | ||||
|             } | ||||
|  | ||||
|             if (attrs.Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute")) | ||||
|             { | ||||
|  | ||||
|  | ||||
|                 var hasTrigger = cds.Members | ||||
|                     .Where(x => x is MethodDeclarationSyntax) | ||||
|                     .Select(x => context.SemanticModel.GetDeclaredSymbol(x) as IMethodSymbol) | ||||
|                     .Any(x => x.Name == "Trigger" | ||||
|                             && x.Parameters.Length == 1 | ||||
|                             && x.Parameters[0].Type.ToDisplayString() == "Esiur.Resource.ResourceTrigger"); | ||||
|  | ||||
|                 var fields = cds.Members.Where(x => x is FieldDeclarationSyntax) | ||||
|                                         .Select(x => context.SemanticModel.GetDeclaredSymbol((x as FieldDeclarationSyntax).Declaration.Variables.First()) as IFieldSymbol) | ||||
|                                         .Where(x => x.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.PublicAttribute")) | ||||
|                                         .ToArray(); | ||||
|  | ||||
|                 //if (!Debugger.IsAttached) | ||||
|                 //{ | ||||
|                 //    if (cls.Name == "User") | ||||
|                 //        Debugger.Launch(); | ||||
|                 //} | ||||
|  | ||||
|  | ||||
|  | ||||
|                 // get fields | ||||
|  | ||||
|                 var fullName = cls.ContainingAssembly + "." + cls.Name; | ||||
|  | ||||
|                 // Partial class check | ||||
|                 if (Classes.ContainsKey(fullName)) | ||||
|                 { | ||||
|                    // Debugger.Launch(); | ||||
|  | ||||
|                     var url = import.ConstructorArguments.First().Value.ToString(); | ||||
|  | ||||
|                     if (!Imports.Contains(url)) | ||||
|                         Imports.Add(url); | ||||
|                     // append fields | ||||
|                     var c = Classes[fullName]; | ||||
|                     c.Fields.AddRange(fields); | ||||
|                     if (!c.HasInterface) | ||||
|                         c.HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"); | ||||
|                     if (!c.HasTrigger) | ||||
|                         c.HasTrigger = hasTrigger; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     Classes.Add(fullName, new ResourceGeneratorClassInfo() | ||||
|                     { | ||||
|                         Name = cls.Name, | ||||
|                         ClassDeclaration = cds, | ||||
|                         ClassSymbol = cls, | ||||
|                         Fields = fields.ToList(), | ||||
|                         HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"), | ||||
|                         HasTrigger = hasTrigger | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 if (attrs.Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute")) | ||||
|                 { | ||||
|                      | ||||
|                      | ||||
|                     var hasTrigger = cds.Members | ||||
|                         .Where(x => x is MethodDeclarationSyntax) | ||||
|                         .Select(x => context.SemanticModel.GetDeclaredSymbol(x) as IMethodSymbol) | ||||
|                         .Any(x => x.Name == "Trigger" | ||||
|                                 && x.Parameters.Length == 1 | ||||
|                                 && x.Parameters[0].Type.ToDisplayString() == "Esiur.Resource.ResourceTrigger"); | ||||
|  | ||||
|                     var fields = cds.Members.Where(x => x is FieldDeclarationSyntax) | ||||
|                                             .Select(x => context.SemanticModel.GetDeclaredSymbol((x as FieldDeclarationSyntax).Declaration.Variables.First()) as IFieldSymbol) | ||||
|                                             .Where(x => x.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.PublicAttribute")) | ||||
|                                             .ToArray(); | ||||
|  | ||||
|                     //if (!Debugger.IsAttached) | ||||
|                     //{ | ||||
|                     //    if (cls.Name == "User") | ||||
|                     //        Debugger.Launch(); | ||||
|                     //} | ||||
|  | ||||
|  | ||||
|  | ||||
|                     // get fields | ||||
|  | ||||
|                     var fullName = cls.ContainingAssembly + "." + cls.Name; | ||||
|  | ||||
|                     // Partial class check | ||||
|                     if (Classes.ContainsKey(fullName)) | ||||
|                     { | ||||
|                         // append fields | ||||
|                         var c = Classes[fullName]; | ||||
|                         c.Fields.AddRange(fields); | ||||
|                         if (!c.HasInterface) | ||||
|                             c.HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"); | ||||
|                         if (!c.HasTrigger) | ||||
|                             c.HasTrigger = hasTrigger; | ||||
|                     } else | ||||
|                     { | ||||
|                         Classes.Add(fullName, new ResourceGeneratorClassInfo() | ||||
|                         { | ||||
|                             Name = cls.Name, | ||||
|                             ClassDeclaration = cds, | ||||
|                             ClassSymbol = cls, | ||||
|                             Fields = fields.ToList(), | ||||
|                             HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"), | ||||
|                             HasTrigger = hasTrigger | ||||
|                         }); | ||||
|                     } | ||||
|  | ||||
|      | ||||
|                     return; | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,72 +7,71 @@ using System.Reflection; | ||||
| using System.Reflection.Emit; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Proxy | ||||
| namespace Esiur.Proxy; | ||||
| public static class ResourceProxy | ||||
| { | ||||
|     public static class ResourceProxy | ||||
|     { | ||||
|         static Dictionary<Type, Type> cache = new Dictionary<Type, Type>(); | ||||
|          | ||||
|     static Dictionary<Type, Type> cache = new Dictionary<Type, Type>(); | ||||
|  | ||||
| #if NETSTANDARD | ||||
|         static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified"); | ||||
|         static MethodInfo instanceGet = typeof(IResource).GetTypeInfo().GetProperty("Instance").GetGetMethod(); | ||||
|     static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified"); | ||||
|     static MethodInfo instanceGet = typeof(IResource).GetTypeInfo().GetProperty("Instance").GetGetMethod(); | ||||
| #else | ||||
|         static MethodInfo modifyMethod = typeof(Instance).GetMethod("Modified"); | ||||
|         static MethodInfo instanceGet = typeof(IResource).GetProperty("Instance").GetGetMethod(); | ||||
| #endif | ||||
|  | ||||
|  | ||||
|         public static Type GetBaseType(object resource) | ||||
|     public static Type GetBaseType(object resource) | ||||
|     { | ||||
|         return GetBaseType(resource.GetType()); | ||||
|     } | ||||
|  | ||||
|     public static Type GetBaseType(Type type) | ||||
|     { | ||||
|         if (type.Assembly.IsDynamic) | ||||
|             return type.GetTypeInfo().BaseType; | ||||
|         else | ||||
|             return type; | ||||
|  | ||||
|         //            if (type.FullName.Contains("Esiur.Proxy.T")) | ||||
|         //#if NETSTANDARD | ||||
|         //                return type.GetTypeInfo().BaseType; | ||||
|         //#else | ||||
|         //            return type.BaseType; | ||||
|         //#endif | ||||
|         //            else | ||||
|         //                return type; | ||||
|     } | ||||
|  | ||||
|     public static Type GetProxy(Type type) | ||||
|     { | ||||
|  | ||||
|         if (cache.ContainsKey(type)) | ||||
|             return cache[type]; | ||||
|  | ||||
|         // check if the type was made with code generation | ||||
|         if (type.GetCustomAttribute<ResourceAttribute>(false) != null) | ||||
|         { | ||||
|             return GetBaseType(resource.GetType()); | ||||
|             cache.Add(type, type); | ||||
|             return type; | ||||
|         } | ||||
|  | ||||
|         public static Type GetBaseType(Type type) | ||||
|         if (!Codec.ImplementsInterface(type, typeof(IResource))) | ||||
|         { | ||||
|             if (type.Assembly.IsDynamic) | ||||
|                 return type.GetTypeInfo().BaseType; | ||||
|             else | ||||
|                 return type; | ||||
|  | ||||
| //            if (type.FullName.Contains("Esiur.Proxy.T")) | ||||
| //#if NETSTANDARD | ||||
| //                return type.GetTypeInfo().BaseType; | ||||
| //#else | ||||
| //            return type.BaseType; | ||||
| //#endif | ||||
| //            else | ||||
| //                return type; | ||||
|             cache.Add(type, type); | ||||
|             return type; | ||||
|         } | ||||
|  | ||||
|         public static Type GetProxy(Type type) | ||||
|         { | ||||
|  | ||||
|             if (cache.ContainsKey(type)) | ||||
|                 return cache[type]; | ||||
|  | ||||
|             // check if the type was made with code generation | ||||
|             if (type.GetCustomAttribute<ResourceAttribute>(false) != null) | ||||
|             { | ||||
|                 cache.Add(type, type); | ||||
|                 return type; | ||||
|             } | ||||
|  | ||||
|             if (!Codec.ImplementsInterface(type, typeof(IResource))) | ||||
|             { | ||||
|                 cache.Add(type, type); | ||||
|                 return type; | ||||
|             } | ||||
|  | ||||
| #if NETSTANDARD | ||||
|             var typeInfo = type.GetTypeInfo(); | ||||
|         var typeInfo = type.GetTypeInfo(); | ||||
|  | ||||
|             if (typeInfo.IsSealed || typeInfo.IsAbstract) | ||||
|                 throw new Exception("Sealed/Abastract classes can't be proxied."); | ||||
|         if (typeInfo.IsSealed || typeInfo.IsAbstract) | ||||
|             throw new Exception("Sealed/Abastract classes can't be proxied."); | ||||
|  | ||||
|             var props = from p in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public) | ||||
|                         where p.CanWrite && p.SetMethod.IsVirtual && !p.SetMethod.IsFinal &&  | ||||
|                         p.GetCustomAttribute<PublicAttribute>(false) != null | ||||
|                         select p; | ||||
|         var props = from p in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public) | ||||
|                     where p.CanWrite && p.SetMethod.IsVirtual && !p.SetMethod.IsFinal && | ||||
|                     p.GetCustomAttribute<PublicAttribute>(false) != null | ||||
|                     select p; | ||||
|  | ||||
| #else | ||||
|             if (type.IsSealed) | ||||
| @@ -84,172 +83,171 @@ namespace Esiur.Proxy | ||||
|                 select p; | ||||
|  | ||||
| #endif | ||||
|             var assemblyName = new AssemblyName("Esiur.Proxy.T." + type.Assembly.GetName().Name);// type.Namespace); | ||||
|             assemblyName.Version = type.Assembly.GetName().Version; | ||||
|             assemblyName.CultureInfo = type.Assembly.GetName().CultureInfo; | ||||
|             //assemblyName.SetPublicKeyToken(null); | ||||
|         var assemblyName = new AssemblyName("Esiur.Proxy.T." + type.Assembly.GetName().Name);// type.Namespace); | ||||
|         assemblyName.Version = type.Assembly.GetName().Version; | ||||
|         assemblyName.CultureInfo = type.Assembly.GetName().CultureInfo; | ||||
|         //assemblyName.SetPublicKeyToken(null); | ||||
|  | ||||
|             var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); | ||||
|             var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); | ||||
|             var typeName = "Esiur.Proxy.T." + type.FullName;// Assembly.CreateQualifiedName(assemblyName.FullName, "Esiur.Proxy.T." + type.FullName); | ||||
|         var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); | ||||
|         var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); | ||||
|         var typeName = "Esiur.Proxy.T." + type.FullName;// Assembly.CreateQualifiedName(assemblyName.FullName, "Esiur.Proxy.T." + type.FullName); | ||||
|  | ||||
|             var typeBuilder = moduleBuilder.DefineType(typeName, | ||||
|                 TypeAttributes.Public | TypeAttributes.Class, type); | ||||
|         var typeBuilder = moduleBuilder.DefineType(typeName, | ||||
|             TypeAttributes.Public | TypeAttributes.Class, type); | ||||
|  | ||||
|             foreach (PropertyInfo propertyInfo in props) | ||||
|                 CreateProperty(propertyInfo, typeBuilder, type); | ||||
|         foreach (PropertyInfo propertyInfo in props) | ||||
|             CreateProperty(propertyInfo, typeBuilder, type); | ||||
|  | ||||
|  | ||||
|  | ||||
| #if NETSTANDARD | ||||
|             var t = typeBuilder.CreateTypeInfo().AsType(); | ||||
|             cache.Add(type, t); | ||||
|             return t; | ||||
|         var t = typeBuilder.CreateTypeInfo().AsType(); | ||||
|         cache.Add(type, t); | ||||
|         return t; | ||||
| #else | ||||
|              | ||||
|             var t = typeBuilder.CreateType(); | ||||
|             cache.Add(type, t); | ||||
|             return t; | ||||
| #endif | ||||
|         } | ||||
|  | ||||
|         public static Type GetProxy<T>() | ||||
|             where T : IResource | ||||
|         { | ||||
|             return GetProxy(typeof(T)); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         //private static void C | ||||
|         private static void CreateProperty(PropertyInfo pi, TypeBuilder typeBuilder, Type resourceType) | ||||
|         { | ||||
|             var propertyBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, null); | ||||
|  | ||||
|             // Create set method | ||||
|             MethodBuilder builder = typeBuilder.DefineMethod("set_" + pi.Name, | ||||
|                 MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { pi.PropertyType }); | ||||
|             builder.DefineParameter(1, ParameterAttributes.None, "value"); | ||||
|             ILGenerator g = builder.GetILGenerator(); | ||||
|  | ||||
|             var getInstance = resourceType.GetTypeInfo().GetProperty("Instance").GetGetMethod(); | ||||
|  | ||||
|  | ||||
|             //g.Emit(OpCodes.Ldarg_0); | ||||
|             //g.Emit(OpCodes.Ldarg_1); | ||||
|             //g.Emit(OpCodes.Call, pi.GetSetMethod()); | ||||
|             //g.Emit(OpCodes.Nop); | ||||
|  | ||||
|             //g.Emit(OpCodes.Ldarg_0); | ||||
|             //g.Emit(OpCodes.Call, getInstance); | ||||
|             //g.Emit(OpCodes.Ldstr, pi.Name); | ||||
|             //g.Emit(OpCodes.Call, modifyMethod); | ||||
|             //g.Emit(OpCodes.Nop); | ||||
|  | ||||
|             //g.Emit(OpCodes.Ret); | ||||
|  | ||||
|             Label exitMethod = g.DefineLabel(); | ||||
|             Label callModified = g.DefineLabel(); | ||||
|  | ||||
|             g.Emit(OpCodes.Nop); | ||||
|  | ||||
|             g.Emit(OpCodes.Ldarg_0); | ||||
|             g.Emit(OpCodes.Ldarg_1); | ||||
|             g.Emit(OpCodes.Call, pi.GetSetMethod()); | ||||
|             g.Emit(OpCodes.Nop); | ||||
|  | ||||
|             g.Emit(OpCodes.Ldarg_0); | ||||
|             g.Emit(OpCodes.Call, getInstance); | ||||
|             g.Emit(OpCodes.Dup); | ||||
|  | ||||
|             g.Emit(OpCodes.Brtrue_S, callModified); | ||||
|  | ||||
|             g.Emit(OpCodes.Pop); | ||||
|             g.Emit(OpCodes.Br_S, exitMethod); | ||||
|  | ||||
|             g.MarkLabel(callModified); | ||||
|  | ||||
|             g.Emit(OpCodes.Ldstr, pi.Name); | ||||
|             g.Emit(OpCodes.Call, modifyMethod); | ||||
|             g.Emit(OpCodes.Nop); | ||||
|  | ||||
|             g.MarkLabel(exitMethod); | ||||
|             g.Emit(OpCodes.Ret); | ||||
|             propertyBuilder.SetSetMethod(builder); | ||||
|  | ||||
|  | ||||
|             builder = typeBuilder.DefineMethod("get_" + pi.Name, MethodAttributes.Public | MethodAttributes.Virtual, pi.PropertyType, null); | ||||
|             g = builder.GetILGenerator(); | ||||
|             g.Emit(OpCodes.Ldarg_0); | ||||
|             g.Emit(OpCodes.Call, pi.GetGetMethod()); | ||||
|             g.Emit(OpCodes.Ret); | ||||
|  | ||||
|             propertyBuilder.SetGetMethod(builder); | ||||
|  | ||||
|             // g.Emit(OpCodes.Ldarg_0); | ||||
|             // g.Emit(OpCodes.Call, pi.GetGetMethod()); | ||||
|             // g.Emit(OpCodes.Ret); | ||||
|  | ||||
|             // propertyBuilder.SetGetMethod(builder); | ||||
|  | ||||
|  | ||||
|             /* | ||||
|             Label callModified = g.DefineLabel(); | ||||
|             Label exitMethod = g.DefineLabel(); | ||||
|  | ||||
|              | ||||
|              //   IL_0000: ldarg.0 | ||||
| 	            //IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance() | ||||
| 	            //// (no C# code) | ||||
| 	            //IL_0006: dup | ||||
| 	            //IL_0007: brtrue.s IL_000c | ||||
| 	            //IL_0009: pop | ||||
| 	            //// } | ||||
| 	            //IL_000a: br.s IL_0017 | ||||
| 	            //// (no C# code) | ||||
| 	            //IL_000c: ldstr "Level3" | ||||
| 	            //IL_0011: call instance void [Esiur]Esiur.Resource.Instance::Modified(string) | ||||
| 	            //IL_0016: nop | ||||
| 	            //IL_0017: ret | ||||
|               | ||||
|  | ||||
|             // Add IL code for set method | ||||
|             g.Emit(OpCodes.Nop); | ||||
|             g.Emit(OpCodes.Ldarg_0); | ||||
|             g.Emit(OpCodes.Ldarg_1); | ||||
|             g.Emit(OpCodes.Call, pi.GetSetMethod()); | ||||
|  | ||||
|              | ||||
|              //   IL_0000: ldarg.0 | ||||
|  	           // IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance() | ||||
|  	           // IL_0006: ldstr "Level3" | ||||
| 	            //IL_000b: callvirt instance void [Esiur]Esiur.Resource.Instance::Modified(string) | ||||
| 	            //IL_0010: ret | ||||
|               | ||||
|  | ||||
|             // Call property changed for object | ||||
|             g.Emit(OpCodes.Nop); | ||||
|             g.Emit(OpCodes.Ldarg_0); | ||||
|             g.Emit(OpCodes.Call, instanceGet); | ||||
|  | ||||
|             g.Emit(OpCodes.Dup); | ||||
|             g.Emit(OpCodes.Brtrue_S, callModified); | ||||
|             g.Emit(OpCodes.Pop); | ||||
|             g.Emit(OpCodes.Br_S, exitMethod); | ||||
|  | ||||
|             g.MarkLabel(callModified); | ||||
|             g.Emit(OpCodes.Ldstr, pi.Name); | ||||
|             g.Emit(OpCodes.Callvirt, modifyMethod); | ||||
|             g.Emit(OpCodes.Nop); | ||||
|             g.MarkLabel(exitMethod); | ||||
|             g.Emit(OpCodes.Ret); | ||||
|             propertyBuilder.SetSetMethod(builder); | ||||
|  | ||||
|  | ||||
|             // create get method | ||||
|  | ||||
|             */ | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public static Type GetProxy<T>() | ||||
|         where T : IResource | ||||
|     { | ||||
|         return GetProxy(typeof(T)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     //private static void C | ||||
|     private static void CreateProperty(PropertyInfo pi, TypeBuilder typeBuilder, Type resourceType) | ||||
|     { | ||||
|         var propertyBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, null); | ||||
|  | ||||
|         // Create set method | ||||
|         MethodBuilder builder = typeBuilder.DefineMethod("set_" + pi.Name, | ||||
|             MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { pi.PropertyType }); | ||||
|         builder.DefineParameter(1, ParameterAttributes.None, "value"); | ||||
|         ILGenerator g = builder.GetILGenerator(); | ||||
|  | ||||
|         var getInstance = resourceType.GetTypeInfo().GetProperty("Instance").GetGetMethod(); | ||||
|  | ||||
|  | ||||
|         //g.Emit(OpCodes.Ldarg_0); | ||||
|         //g.Emit(OpCodes.Ldarg_1); | ||||
|         //g.Emit(OpCodes.Call, pi.GetSetMethod()); | ||||
|         //g.Emit(OpCodes.Nop); | ||||
|  | ||||
|         //g.Emit(OpCodes.Ldarg_0); | ||||
|         //g.Emit(OpCodes.Call, getInstance); | ||||
|         //g.Emit(OpCodes.Ldstr, pi.Name); | ||||
|         //g.Emit(OpCodes.Call, modifyMethod); | ||||
|         //g.Emit(OpCodes.Nop); | ||||
|  | ||||
|         //g.Emit(OpCodes.Ret); | ||||
|  | ||||
|         Label exitMethod = g.DefineLabel(); | ||||
|         Label callModified = g.DefineLabel(); | ||||
|  | ||||
|         g.Emit(OpCodes.Nop); | ||||
|  | ||||
|         g.Emit(OpCodes.Ldarg_0); | ||||
|         g.Emit(OpCodes.Ldarg_1); | ||||
|         g.Emit(OpCodes.Call, pi.GetSetMethod()); | ||||
|         g.Emit(OpCodes.Nop); | ||||
|  | ||||
|         g.Emit(OpCodes.Ldarg_0); | ||||
|         g.Emit(OpCodes.Call, getInstance); | ||||
|         g.Emit(OpCodes.Dup); | ||||
|  | ||||
|         g.Emit(OpCodes.Brtrue_S, callModified); | ||||
|  | ||||
|         g.Emit(OpCodes.Pop); | ||||
|         g.Emit(OpCodes.Br_S, exitMethod); | ||||
|  | ||||
|         g.MarkLabel(callModified); | ||||
|  | ||||
|         g.Emit(OpCodes.Ldstr, pi.Name); | ||||
|         g.Emit(OpCodes.Call, modifyMethod); | ||||
|         g.Emit(OpCodes.Nop); | ||||
|  | ||||
|         g.MarkLabel(exitMethod); | ||||
|         g.Emit(OpCodes.Ret); | ||||
|         propertyBuilder.SetSetMethod(builder); | ||||
|  | ||||
|  | ||||
|         builder = typeBuilder.DefineMethod("get_" + pi.Name, MethodAttributes.Public | MethodAttributes.Virtual, pi.PropertyType, null); | ||||
|         g = builder.GetILGenerator(); | ||||
|         g.Emit(OpCodes.Ldarg_0); | ||||
|         g.Emit(OpCodes.Call, pi.GetGetMethod()); | ||||
|         g.Emit(OpCodes.Ret); | ||||
|  | ||||
|         propertyBuilder.SetGetMethod(builder); | ||||
|  | ||||
|         // g.Emit(OpCodes.Ldarg_0); | ||||
|         // g.Emit(OpCodes.Call, pi.GetGetMethod()); | ||||
|         // g.Emit(OpCodes.Ret); | ||||
|  | ||||
|         // propertyBuilder.SetGetMethod(builder); | ||||
|  | ||||
|  | ||||
|         /* | ||||
|         Label callModified = g.DefineLabel(); | ||||
|         Label exitMethod = g.DefineLabel(); | ||||
|  | ||||
|  | ||||
|          //   IL_0000: ldarg.0 | ||||
|             //IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance() | ||||
|             //// (no C# code) | ||||
|             //IL_0006: dup | ||||
|             //IL_0007: brtrue.s IL_000c | ||||
|             //IL_0009: pop | ||||
|             //// } | ||||
|             //IL_000a: br.s IL_0017 | ||||
|             //// (no C# code) | ||||
|             //IL_000c: ldstr "Level3" | ||||
|             //IL_0011: call instance void [Esiur]Esiur.Resource.Instance::Modified(string) | ||||
|             //IL_0016: nop | ||||
|             //IL_0017: ret | ||||
|  | ||||
|  | ||||
|         // Add IL code for set method | ||||
|         g.Emit(OpCodes.Nop); | ||||
|         g.Emit(OpCodes.Ldarg_0); | ||||
|         g.Emit(OpCodes.Ldarg_1); | ||||
|         g.Emit(OpCodes.Call, pi.GetSetMethod()); | ||||
|  | ||||
|  | ||||
|          //   IL_0000: ldarg.0 | ||||
|            // IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance() | ||||
|            // IL_0006: ldstr "Level3" | ||||
|             //IL_000b: callvirt instance void [Esiur]Esiur.Resource.Instance::Modified(string) | ||||
|             //IL_0010: ret | ||||
|  | ||||
|  | ||||
|         // Call property changed for object | ||||
|         g.Emit(OpCodes.Nop); | ||||
|         g.Emit(OpCodes.Ldarg_0); | ||||
|         g.Emit(OpCodes.Call, instanceGet); | ||||
|  | ||||
|         g.Emit(OpCodes.Dup); | ||||
|         g.Emit(OpCodes.Brtrue_S, callModified); | ||||
|         g.Emit(OpCodes.Pop); | ||||
|         g.Emit(OpCodes.Br_S, exitMethod); | ||||
|  | ||||
|         g.MarkLabel(callModified); | ||||
|         g.Emit(OpCodes.Ldstr, pi.Name); | ||||
|         g.Emit(OpCodes.Callvirt, modifyMethod); | ||||
|         g.Emit(OpCodes.Nop); | ||||
|         g.MarkLabel(exitMethod); | ||||
|         g.Emit(OpCodes.Ret); | ||||
|         propertyBuilder.SetSetMethod(builder); | ||||
|  | ||||
|  | ||||
|         // create get method | ||||
|  | ||||
|         */ | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -10,229 +10,227 @@ using Esiur.Resource; | ||||
| using Esiur.Net.IIP; | ||||
| using System.Diagnostics; | ||||
|  | ||||
| namespace Esiur.Proxy | ||||
| namespace Esiur.Proxy; | ||||
| public static class TemplateGenerator | ||||
| { | ||||
|     public static class TemplateGenerator | ||||
|     internal static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)"); | ||||
|  | ||||
|     internal static string GenerateRecord(TypeTemplate template, TypeTemplate[] templates) | ||||
|     { | ||||
|         internal static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)"); | ||||
|         var cls = template.ClassName.Split('.'); | ||||
|  | ||||
|         internal static string GenerateRecord(TypeTemplate template, TypeTemplate[] templates) | ||||
|         var nameSpace = string.Join(".", cls.Take(cls.Length - 1)); | ||||
|         var className = cls.Last(); | ||||
|  | ||||
|         var rt = new StringBuilder(); | ||||
|  | ||||
|         rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); | ||||
|         rt.AppendLine($"namespace { nameSpace} {{"); | ||||
|         rt.AppendLine($"public class {className} : IRecord {{"); | ||||
|  | ||||
|  | ||||
|         foreach (var p in template.Properties) | ||||
|         { | ||||
|             var cls = template.ClassName.Split('.'); | ||||
|  | ||||
|             var nameSpace = string.Join(".", cls.Take(cls.Length - 1)); | ||||
|             var className = cls.Last(); | ||||
|  | ||||
|             var rt = new StringBuilder(); | ||||
|  | ||||
|             rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); | ||||
|             rt.AppendLine($"namespace { nameSpace} {{"); | ||||
|             rt.AppendLine($"public class {className} : IRecord {{"); | ||||
|  | ||||
|  | ||||
|             foreach (var p in template.Properties) | ||||
|             { | ||||
|                 var ptTypeName = GetTypeName(p.ValueType, templates); | ||||
|                 rt.AppendLine($"public {ptTypeName} {p.Name} {{ get; set; }}"); | ||||
|                 rt.AppendLine(); | ||||
|             } | ||||
|  | ||||
|             rt.AppendLine("\r\n}\r\n}"); | ||||
|  | ||||
|             return rt.ToString(); | ||||
|             var ptTypeName = GetTypeName(p.ValueType, templates); | ||||
|             rt.AppendLine($"public {ptTypeName} {p.Name} {{ get; set; }}"); | ||||
|             rt.AppendLine(); | ||||
|         } | ||||
|  | ||||
|         static string GetTypeName(TemplateDataType templateDataType, TypeTemplate[] templates) | ||||
|         { | ||||
|  | ||||
|             if (templateDataType.Type == DataType.Resource) | ||||
|                 return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper )).ClassName; | ||||
|             else if (templateDataType.Type == DataType.ResourceArray) | ||||
|                 return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper )).ClassName + "[]"; | ||||
|             else if (templateDataType.Type == DataType.Record) | ||||
|                 return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName; | ||||
|             else if (templateDataType.Type == DataType.RecordArray) | ||||
|                 return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName + "[]"; | ||||
|  | ||||
|             var name = templateDataType.Type switch | ||||
|             { | ||||
|                 DataType.Bool => "bool", | ||||
|                 DataType.BoolArray => "bool[]", | ||||
|                 DataType.Char => "char", | ||||
|                 DataType.CharArray => "char[]", | ||||
|                 DataType.DateTime => "DateTime", | ||||
|                 DataType.DateTimeArray => "DateTime[]", | ||||
|                 DataType.Decimal => "decimal", | ||||
|                 DataType.DecimalArray => "decimal[]", | ||||
|                 DataType.Float32 => "float", | ||||
|                 DataType.Float32Array => "float[]", | ||||
|                 DataType.Float64 => "double", | ||||
|                 DataType.Float64Array => "double[]", | ||||
|                 DataType.Int16 => "short", | ||||
|                 DataType.Int16Array => "short[]", | ||||
|                 DataType.Int32 => "int", | ||||
|                 DataType.Int32Array => "int[]", | ||||
|                 DataType.Int64 => "long", | ||||
|                 DataType.Int64Array => "long[]", | ||||
|                 DataType.Int8 => "sbyte", | ||||
|                 DataType.Int8Array => "sbyte[]", | ||||
|                 DataType.String => "string", | ||||
|                 DataType.StringArray => "string[]", | ||||
|                 DataType.Structure => "Structure", | ||||
|                 DataType.StructureArray => "Structure[]", | ||||
|                 DataType.UInt16 => "ushort", | ||||
|                 DataType.UInt16Array => "ushort[]", | ||||
|                 DataType.UInt32 => "uint", | ||||
|                 DataType.UInt32Array => "uint[]", | ||||
|                 DataType.UInt64 => "ulong", | ||||
|                 DataType.UInt64Array => "ulong[]", | ||||
|                 DataType.UInt8 => "byte", | ||||
|                 DataType.UInt8Array => "byte[]", | ||||
|                 DataType.VarArray => "object[]", | ||||
|                 DataType.Void => "object", | ||||
|                 _ => "object" | ||||
|             }; | ||||
|  | ||||
|             return name; | ||||
|         } | ||||
|  | ||||
|         public static string GetTemplate(string url, string dir = null, string username= null, string password = null) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|  | ||||
|                 if (!urlRegex.IsMatch(url)) | ||||
|                     throw new Exception("Invalid IIP URL"); | ||||
|  | ||||
|                 var path = urlRegex.Split(url); | ||||
|                 var con = Warehouse.Get<DistributedConnection>(path[1] + "://" + path[2], | ||||
|                         !string.IsNullOrEmpty( username) && !string.IsNullOrEmpty( password) ? new { Username = username, Password = password } : null | ||||
|                     ).Wait(20000); | ||||
|  | ||||
|                 if (con == null) | ||||
|                     throw new Exception("Can't connect to server"); | ||||
|  | ||||
|                 if (string.IsNullOrEmpty(dir)) | ||||
|                     dir = path[2].Replace(":", "_"); | ||||
|  | ||||
|                 var templates = con.GetLinkTemplates(path[3]).Wait(60000); | ||||
|                 // no longer needed | ||||
|                 Warehouse.Remove(con); | ||||
|  | ||||
|                 var tempDir = new DirectoryInfo(Path.GetTempPath() + Path.DirectorySeparatorChar | ||||
|                                 + Misc.Global.GenerateCode(20) + Path.DirectorySeparatorChar + dir); | ||||
|  | ||||
|                 if (!tempDir.Exists) | ||||
|                     tempDir.Create(); | ||||
|                 else | ||||
|                 { | ||||
|                     foreach (FileInfo file in tempDir.GetFiles()) | ||||
|                         file.Delete(); | ||||
|                 } | ||||
|  | ||||
|                 // make sources | ||||
|                 foreach (var tmp in templates) | ||||
|                 { | ||||
|                     if (tmp.Type == TemplateType.Resource) | ||||
|                     { | ||||
|                         var source = GenerateClass(tmp, templates); | ||||
|                         File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source); | ||||
|                     } | ||||
|                     else if (tmp.Type == TemplateType.Record) | ||||
|                     { | ||||
|                         var source = GenerateRecord(tmp, templates); | ||||
|                         File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // generate info class | ||||
|  | ||||
|                 var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " + | ||||
|                         string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})")) | ||||
|                     + " }; \r\n public static Type[] Records { get; } = new Type[] { " + | ||||
|                         string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})")) | ||||
|                     + " }; " + | ||||
|  | ||||
|                     "\r\n } \r\n}"; | ||||
|  | ||||
|  | ||||
|                 File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + "Esiur.Generated.cs", typesFile); | ||||
|  | ||||
|                  | ||||
|                 return tempDir.FullName; | ||||
|  | ||||
|             } | ||||
|             catch(Exception ex) | ||||
|             { | ||||
|                 //File.WriteAllText("C:\\gen\\gettemplate.err", ex.ToString()); | ||||
|                 throw ex; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         internal static string GenerateClass(TypeTemplate template, TypeTemplate[] templates) | ||||
|         { | ||||
|             var cls = template.ClassName.Split('.'); | ||||
|  | ||||
|             var nameSpace = string.Join(".", cls.Take(cls.Length - 1)); | ||||
|             var className = cls.Last(); | ||||
|  | ||||
|             var rt = new StringBuilder(); | ||||
|  | ||||
|             rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); | ||||
|             rt.AppendLine($"namespace { nameSpace} {{"); | ||||
|             rt.AppendLine($"public class {className} : DistributedResource {{"); | ||||
|  | ||||
|             rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}"); | ||||
|             rt.AppendLine($"public {className}() {{}}"); | ||||
|  | ||||
|             foreach (var f in template.Functions) | ||||
|             { | ||||
|                 var rtTypeName = GetTypeName(f.ReturnType, templates); | ||||
|                 rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}("); | ||||
|                 rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name))); | ||||
|  | ||||
|                 rt.AppendLine(") {"); | ||||
|                 rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();"); | ||||
|                 rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})"); | ||||
|                 rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))"); | ||||
|                 rt.AppendLine($".Error(x => rt.TriggerError(x))"); | ||||
|                 rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));"); | ||||
|                 rt.AppendLine("return rt; }"); | ||||
|             } | ||||
|  | ||||
|             foreach (var p in template.Properties) | ||||
|             { | ||||
|                 var ptTypeName = GetTypeName(p.ValueType, templates); | ||||
|                 rt.AppendLine($"public {ptTypeName} {p.Name} {{"); | ||||
|                 rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];"); | ||||
|                 rt.AppendLine($"set =>  _Set({p.Index}, value);"); | ||||
|                 rt.AppendLine("}"); | ||||
|             } | ||||
|  | ||||
|             if (template.Events.Length > 0) | ||||
|             { | ||||
|                 rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {"); | ||||
|                 rt.AppendLine("switch (index) {"); | ||||
|  | ||||
|                 var eventsList = new StringBuilder(); | ||||
|  | ||||
|                 foreach (var e in template.Events) | ||||
|                 { | ||||
|                     var etTypeName = GetTypeName(e.ArgumentType, templates); | ||||
|                     rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;"); | ||||
|                     eventsList.AppendLine($"public event ResourceEventHandler<{etTypeName}> {e.Name};"); | ||||
|                 } | ||||
|  | ||||
|                 rt.AppendLine("}}"); | ||||
|  | ||||
|                 rt.AppendLine(eventsList.ToString()); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             rt.AppendLine("\r\n}\r\n}"); | ||||
|  | ||||
|             return rt.ToString(); | ||||
|         } | ||||
|         rt.AppendLine("\r\n}\r\n}"); | ||||
|  | ||||
|         return rt.ToString(); | ||||
|     } | ||||
|  | ||||
|     static string GetTypeName(TemplateDataType templateDataType, TypeTemplate[] templates) | ||||
|     { | ||||
|  | ||||
|         if (templateDataType.Type == DataType.Resource) | ||||
|             return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper)).ClassName; | ||||
|         else if (templateDataType.Type == DataType.ResourceArray) | ||||
|             return templates.First(x => x.ClassId == templateDataType.TypeGuid && (x.Type == TemplateType.Resource || x.Type == TemplateType.Wrapper)).ClassName + "[]"; | ||||
|         else if (templateDataType.Type == DataType.Record) | ||||
|             return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName; | ||||
|         else if (templateDataType.Type == DataType.RecordArray) | ||||
|             return templates.First(x => x.ClassId == templateDataType.TypeGuid && x.Type == TemplateType.Record).ClassName + "[]"; | ||||
|  | ||||
|         var name = templateDataType.Type switch | ||||
|         { | ||||
|             DataType.Bool => "bool", | ||||
|             DataType.BoolArray => "bool[]", | ||||
|             DataType.Char => "char", | ||||
|             DataType.CharArray => "char[]", | ||||
|             DataType.DateTime => "DateTime", | ||||
|             DataType.DateTimeArray => "DateTime[]", | ||||
|             DataType.Decimal => "decimal", | ||||
|             DataType.DecimalArray => "decimal[]", | ||||
|             DataType.Float32 => "float", | ||||
|             DataType.Float32Array => "float[]", | ||||
|             DataType.Float64 => "double", | ||||
|             DataType.Float64Array => "double[]", | ||||
|             DataType.Int16 => "short", | ||||
|             DataType.Int16Array => "short[]", | ||||
|             DataType.Int32 => "int", | ||||
|             DataType.Int32Array => "int[]", | ||||
|             DataType.Int64 => "long", | ||||
|             DataType.Int64Array => "long[]", | ||||
|             DataType.Int8 => "sbyte", | ||||
|             DataType.Int8Array => "sbyte[]", | ||||
|             DataType.String => "string", | ||||
|             DataType.StringArray => "string[]", | ||||
|             DataType.Structure => "Structure", | ||||
|             DataType.StructureArray => "Structure[]", | ||||
|             DataType.UInt16 => "ushort", | ||||
|             DataType.UInt16Array => "ushort[]", | ||||
|             DataType.UInt32 => "uint", | ||||
|             DataType.UInt32Array => "uint[]", | ||||
|             DataType.UInt64 => "ulong", | ||||
|             DataType.UInt64Array => "ulong[]", | ||||
|             DataType.UInt8 => "byte", | ||||
|             DataType.UInt8Array => "byte[]", | ||||
|             DataType.VarArray => "object[]", | ||||
|             DataType.Void => "object", | ||||
|             _ => "object" | ||||
|         }; | ||||
|  | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     public static string GetTemplate(string url, string dir = null, string username = null, string password = null) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             if (!urlRegex.IsMatch(url)) | ||||
|                 throw new Exception("Invalid IIP URL"); | ||||
|  | ||||
|             var path = urlRegex.Split(url); | ||||
|             var con = Warehouse.Get<DistributedConnection>(path[1] + "://" + path[2], | ||||
|                     !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) ? new { Username = username, Password = password } : null | ||||
|                 ).Wait(20000); | ||||
|  | ||||
|             if (con == null) | ||||
|                 throw new Exception("Can't connect to server"); | ||||
|  | ||||
|             if (string.IsNullOrEmpty(dir)) | ||||
|                 dir = path[2].Replace(":", "_"); | ||||
|  | ||||
|             var templates = con.GetLinkTemplates(path[3]).Wait(60000); | ||||
|             // no longer needed | ||||
|             Warehouse.Remove(con); | ||||
|  | ||||
|             var tempDir = new DirectoryInfo(Path.GetTempPath() + Path.DirectorySeparatorChar | ||||
|                             + Misc.Global.GenerateCode(20) + Path.DirectorySeparatorChar + dir); | ||||
|  | ||||
|             if (!tempDir.Exists) | ||||
|                 tempDir.Create(); | ||||
|             else | ||||
|             { | ||||
|                 foreach (FileInfo file in tempDir.GetFiles()) | ||||
|                     file.Delete(); | ||||
|             } | ||||
|  | ||||
|             // make sources | ||||
|             foreach (var tmp in templates) | ||||
|             { | ||||
|                 if (tmp.Type == TemplateType.Resource) | ||||
|                 { | ||||
|                     var source = GenerateClass(tmp, templates); | ||||
|                     File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source); | ||||
|                 } | ||||
|                 else if (tmp.Type == TemplateType.Record) | ||||
|                 { | ||||
|                     var source = GenerateRecord(tmp, templates); | ||||
|                     File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // generate info class | ||||
|  | ||||
|             var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " + | ||||
|                     string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})")) | ||||
|                 + " }; \r\n public static Type[] Records { get; } = new Type[] { " + | ||||
|                     string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})")) | ||||
|                 + " }; " + | ||||
|  | ||||
|                 "\r\n } \r\n}"; | ||||
|  | ||||
|  | ||||
|             File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + "Esiur.Generated.cs", typesFile); | ||||
|  | ||||
|  | ||||
|             return tempDir.FullName; | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             //File.WriteAllText("C:\\gen\\gettemplate.err", ex.ToString()); | ||||
|             throw ex; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static string GenerateClass(TypeTemplate template, TypeTemplate[] templates) | ||||
|     { | ||||
|         var cls = template.ClassName.Split('.'); | ||||
|  | ||||
|         var nameSpace = string.Join(".", cls.Take(cls.Length - 1)); | ||||
|         var className = cls.Last(); | ||||
|  | ||||
|         var rt = new StringBuilder(); | ||||
|  | ||||
|         rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); | ||||
|         rt.AppendLine($"namespace { nameSpace} {{"); | ||||
|         rt.AppendLine($"public class {className} : DistributedResource {{"); | ||||
|  | ||||
|         rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}"); | ||||
|         rt.AppendLine($"public {className}() {{}}"); | ||||
|  | ||||
|         foreach (var f in template.Functions) | ||||
|         { | ||||
|             var rtTypeName = GetTypeName(f.ReturnType, templates); | ||||
|             rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}("); | ||||
|             rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name))); | ||||
|  | ||||
|             rt.AppendLine(") {"); | ||||
|             rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();"); | ||||
|             rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})"); | ||||
|             rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))"); | ||||
|             rt.AppendLine($".Error(x => rt.TriggerError(x))"); | ||||
|             rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));"); | ||||
|             rt.AppendLine("return rt; }"); | ||||
|         } | ||||
|  | ||||
|         foreach (var p in template.Properties) | ||||
|         { | ||||
|             var ptTypeName = GetTypeName(p.ValueType, templates); | ||||
|             rt.AppendLine($"public {ptTypeName} {p.Name} {{"); | ||||
|             rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];"); | ||||
|             rt.AppendLine($"set =>  _Set({p.Index}, value);"); | ||||
|             rt.AppendLine("}"); | ||||
|         } | ||||
|  | ||||
|         if (template.Events.Length > 0) | ||||
|         { | ||||
|             rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {"); | ||||
|             rt.AppendLine("switch (index) {"); | ||||
|  | ||||
|             var eventsList = new StringBuilder(); | ||||
|  | ||||
|             foreach (var e in template.Events) | ||||
|             { | ||||
|                 var etTypeName = GetTypeName(e.ArgumentType, templates); | ||||
|                 rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;"); | ||||
|                 eventsList.AppendLine($"public event ResourceEventHandler<{etTypeName}> {e.Name};"); | ||||
|             } | ||||
|  | ||||
|             rt.AppendLine("}}"); | ||||
|  | ||||
|             rt.AppendLine(eventsList.ToString()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         rt.AppendLine("\r\n}\r\n}"); | ||||
|  | ||||
|         return rt.ToString(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,17 +2,15 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
|  | ||||
| [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event)] | ||||
| public class AnnotationAttribute : Attribute | ||||
| { | ||||
|  | ||||
|     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event)] | ||||
|     public class AnnotationAttribute : Attribute | ||||
|     public string Annotation { get; set; } | ||||
|     public AnnotationAttribute(string annotation) | ||||
|     { | ||||
|  | ||||
|         public string Annotation { get; set; } | ||||
|         public AnnotationAttribute(string annotation) | ||||
|         { | ||||
|             this.Annotation = annotation; | ||||
|         } | ||||
|         this.Annotation = annotation; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -27,16 +27,14 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| { | ||||
| namespace Esiur.Resource; | ||||
|  | ||||
|     [AttributeUsage(AttributeTargets.Property)] | ||||
|     public class AttributeAttribute : System.Attribute | ||||
| [AttributeUsage(AttributeTargets.Property)] | ||||
| public class AttributeAttribute : System.Attribute | ||||
| { | ||||
|     public string Name { get; set; } | ||||
|     public AttributeAttribute(string name = null) | ||||
|     { | ||||
|         public string Name { get; set; } | ||||
|         public AttributeAttribute(string name = null) | ||||
|         { | ||||
|             this.Name = name; | ||||
|         } | ||||
|         this.Name = name; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,24 +32,23 @@ using System.ComponentModel; | ||||
| using System.Text.Json.Serialization; | ||||
| using System.ComponentModel.DataAnnotations.Schema; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
|  | ||||
| public delegate bool QueryFilter<T>(T value); | ||||
|  | ||||
| //[JsonConverter(typeof(ResourceJsonConverter))] | ||||
|  | ||||
| public interface IResource : IDestructible///, INotifyPropertyChanged | ||||
| { | ||||
|     public delegate bool QueryFilter<T>(T value); | ||||
|  | ||||
|     //[JsonConverter(typeof(ResourceJsonConverter))] | ||||
|     AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|     public interface IResource : IDestructible///, INotifyPropertyChanged | ||||
|     [NotMapped] | ||||
|     Instance Instance | ||||
|     { | ||||
|  | ||||
|         AsyncReply<bool> Trigger(ResourceTrigger trigger); | ||||
|  | ||||
|         [NotMapped] | ||||
|         Instance Instance | ||||
|         { | ||||
|             get; | ||||
|             set; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         get; | ||||
|         set; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -33,44 +33,42 @@ using System.Threading.Tasks; | ||||
| using Esiur.Security.Permissions; | ||||
| using Esiur.Security.Authority; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
| public interface IStore : IResource | ||||
| { | ||||
|     public interface IStore:IResource | ||||
|     { | ||||
|         AsyncReply<IResource> Get(string path);//, Func<IResource, bool> filter = null); | ||||
|         //AsyncReply<IResource> Retrieve(uint iid); | ||||
|         AsyncReply<bool> Put(IResource resource); | ||||
|         string Link(IResource resource); | ||||
|         bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime); | ||||
|         bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime); | ||||
|         bool Remove(IResource resource); | ||||
|     AsyncReply<IResource> Get(string path);//, Func<IResource, bool> filter = null); | ||||
|                                            //AsyncReply<IResource> Retrieve(uint iid); | ||||
|     AsyncReply<bool> Put(IResource resource); | ||||
|     string Link(IResource resource); | ||||
|     bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime); | ||||
|     bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime); | ||||
|     bool Remove(IResource resource); | ||||
|  | ||||
|         //bool RemoveAttributes(IResource resource, string[] attributes = null); | ||||
|     //bool RemoveAttributes(IResource resource, string[] attributes = null); | ||||
|  | ||||
|         //Structure GetAttributes(IResource resource, string[] attributes = null); | ||||
|     //Structure GetAttributes(IResource resource, string[] attributes = null); | ||||
|  | ||||
|         //bool SetAttributes(IResource resource, Structure attributes, bool clearAttributes = false); | ||||
|  | ||||
|          | ||||
|  | ||||
|         AsyncReply<bool> AddChild(IResource parent, IResource child); | ||||
|         AsyncReply<bool> RemoveChild(IResource parent, IResource child); | ||||
|  | ||||
|         AsyncReply<bool> AddParent(IResource child, IResource parent); | ||||
|         AsyncReply<bool> RemoveParent(IResource child, IResource parent); | ||||
|  | ||||
|  | ||||
|         AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource; | ||||
|         AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource; | ||||
|     //bool SetAttributes(IResource resource, Structure attributes, bool clearAttributes = false); | ||||
|  | ||||
|  | ||||
|  | ||||
|         //AsyncReply<PropertyValue[]> GetPropertyRecord(IResource resource, string propertyName, ulong fromAge, ulong toAge); | ||||
|         //AsyncReply<PropertyValue[]> GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate); | ||||
|     AsyncReply<bool> AddChild(IResource parent, IResource child); | ||||
|     AsyncReply<bool> RemoveChild(IResource parent, IResource child); | ||||
|  | ||||
|         //AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, ulong fromAge, ulong toAge); | ||||
|         // AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecordByDate(IResource resource, DateTime fromDate, DateTime toDate); | ||||
|     AsyncReply<bool> AddParent(IResource child, IResource parent); | ||||
|     AsyncReply<bool> RemoveParent(IResource child, IResource parent); | ||||
|  | ||||
|         AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate); | ||||
|     } | ||||
|  | ||||
|     AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource; | ||||
|     AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource; | ||||
|  | ||||
|  | ||||
|  | ||||
|     //AsyncReply<PropertyValue[]> GetPropertyRecord(IResource resource, string propertyName, ulong fromAge, ulong toAge); | ||||
|     //AsyncReply<PropertyValue[]> GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate); | ||||
|  | ||||
|     //AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, ulong fromAge, ulong toAge); | ||||
|     // AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecordByDate(IResource resource, DateTime fromDate, DateTime toDate); | ||||
|  | ||||
|     AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate); | ||||
| } | ||||
|   | ||||
| @@ -2,14 +2,13 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| { | ||||
|     [AttributeUsage(AttributeTargets.Class)] | ||||
|     public class ImportAttribute:Attribute | ||||
|     { | ||||
|         public ImportAttribute(string url) | ||||
|         { | ||||
| namespace Esiur.Resource; | ||||
|  | ||||
| [AttributeUsage(AttributeTargets.Class)] | ||||
| public class ImportAttribute : Attribute | ||||
| { | ||||
|     public ImportAttribute(string url) | ||||
|     { | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,16 +27,14 @@ using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
|  | ||||
| [AttributeUsage(AttributeTargets.Event)] | ||||
| public class ListenableAttribute : System.Attribute | ||||
| { | ||||
|  | ||||
|     [AttributeUsage(AttributeTargets.Event)] | ||||
|     public class ListenableAttribute : System.Attribute | ||||
|     public ListenableAttribute() | ||||
|     { | ||||
|  | ||||
|         public ListenableAttribute() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,10 +2,8 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
| public class PrivateAttribute : Attribute | ||||
| { | ||||
|     public class PrivateAttribute:Attribute | ||||
|     { | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,17 +2,16 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
|  | ||||
| [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Class)] | ||||
|  | ||||
| public class PublicAttribute : Attribute | ||||
| { | ||||
|     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Class)] | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     public class PublicAttribute : Attribute | ||||
|     public PublicAttribute(string name = null) | ||||
|     { | ||||
|         public string Name { get; set; } | ||||
|  | ||||
|         public PublicAttribute(string name = null) | ||||
|         { | ||||
|             Name = name; | ||||
|         } | ||||
|         Name = name; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,36 +26,34 @@ using System.Collections.Generic; | ||||
| using System.Text; | ||||
| using Esiur.Core; | ||||
|  | ||||
| namespace Esiur.Resource | ||||
| namespace Esiur.Resource; | ||||
| public class Resource : IResource | ||||
| { | ||||
|     public class Resource : IResource | ||||
|     public Instance Instance { get; set; } | ||||
|  | ||||
|     public event DestroyedEvent OnDestroy; | ||||
|  | ||||
|     public virtual void Destroy() | ||||
|     { | ||||
|         public Instance Instance { get; set; } | ||||
|         OnDestroy?.Invoke(this); | ||||
|     } | ||||
|  | ||||
|         public event DestroyedEvent OnDestroy; | ||||
|     public virtual AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|     { | ||||
|         if (trigger == ResourceTrigger.Initialize) | ||||
|             return new AsyncReply<bool>(this.Create()); | ||||
|         else | ||||
|             return new AsyncReply<bool>(true); | ||||
|     } | ||||
|  | ||||
|         public virtual void Destroy() | ||||
|         { | ||||
|             OnDestroy?.Invoke(this); | ||||
|         } | ||||
|  | ||||
|         public virtual AsyncReply<bool> Trigger(ResourceTrigger trigger) | ||||
|         { | ||||
|             if (trigger == ResourceTrigger.Initialize) | ||||
|                 return new AsyncReply<bool>(this.Create()); | ||||
|             else | ||||
|                 return new AsyncReply<bool>(true); | ||||
|         } | ||||
|     protected virtual bool Create() | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|          | ||||
|         protected virtual bool Create() | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         ~Resource() | ||||
|         { | ||||
|             Destroy(); | ||||
|         } | ||||
|     ~Resource() | ||||
|     { | ||||
|         Destroy(); | ||||
|     } | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user