Bladeren bron

Refactored InABox.Client.Remote / InABox.Client.Remote.Json into InABox.Client.Rest

Frank van den Bos 2 jaren geleden
bovenliggende
commit
1acda3b420

+ 0 - 336
InABox.Client.Remote.Json/JsonClient.cs

@@ -1,336 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-using InABox.Core;
-using InABox.Remote.Shared;
-using RestSharp;
-
-//using InABox.Logging;
-
-namespace InABox.Clients
-{
-    public class CompressedResponse
-    {
-        public CompressedResponse()
-        {
-            Headers = new Dictionary<string, string>();
-            Options = new Dictionary<string, string>();
-        }
-
-        public string ContentType { get; set; }
-        public Dictionary<string, string> Headers { get; set; }
-        public int Status { get; set; }
-        public string StatusCode { get; set; }
-        public string Response { get; set; }
-        public Dictionary<string, string> Options { get; set; }
-    }
-
-    public class JsonClient<TEntity> : RemoteClient<TEntity> where TEntity : Entity, new()
-    {
-        private readonly bool _compression = true;
-
-        private BinarySerializationSettings BinarySerializationSettings;
-
-        public JsonClient(string url, bool useSimpleEncryption, bool compression, BinarySerializationSettings binarySerializationSettings) : base(url, useSimpleEncryption)
-        {
-            _compression = compression;
-            BinarySerializationSettings = binarySerializationSettings;
-        }
-
-        public JsonClient(string url, bool useSimpleEncryption) : this(url, useSimpleEncryption, true, BinarySerializationSettings.Latest)
-        {
-        }
-
-        public JsonClient(string url) : this(url, false, true, BinarySerializationSettings.Latest)
-        {
-        }
-
-        public static string Ping(String[] urls, out DatabaseInfo info)
-        {
-            String result = "";
-            info = new DatabaseInfo();
-            List<Task<Tuple<String,DatabaseInfo>>> pings = urls.Select(x => Task.Run(
-                () => new Tuple<String,DatabaseInfo>(x,new JsonClient<User>(x).Info())
-            )).ToList();
-            while (pings.Count > 0)
-            {
-                var ping = Task.WhenAny(pings).Result;
-                if (ping.Status == TaskStatus.RanToCompletion && !String.IsNullOrWhiteSpace(ping.Result.Item2.Version))
-                {
-                    result = ping.Result.Item1;
-                    info = ping.Result.Item2;
-                    break;
-                }
-                else
-                    pings.Remove(ping);
-            }
-            return result;
-        }
-
-
-        //private static void CheckSecuritySettings()
-        //{
-        //    Boolean platformSupportsTls12 = false;
-        //    foreach (SecurityProtocolType protocol in Enum.GetValues(typeof(SecurityProtocolType)))
-        //        platformSupportsTls12 = platformSupportsTls12 || (protocol.GetHashCode() == 3072);
-
-        //    if (!ServicePointManager.SecurityProtocol.HasFlag((SecurityProtocolType)3072) && platformSupportsTls12)
-        //            ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
-
-        //    if (ServicePointManager.SecurityProtocol.HasFlag(SecurityProtocolType.Ssl3))
-        //        System.Net.ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
-        //}
-
-        protected override TResponse SendRequest<TRequest, TResponse>(TRequest request, string Action, SerializationFormat requestFormat, SerializationFormat responseFormat, bool includeEntity = true)
-        {
-            //DateTime now = DateTime.Now;
-            //Log("  * {0}{1}() Starting..", Action, typeof(TEntity).Name);
-            //Stopwatch sw = new Stopwatch();
-            //sw.Start();
-
-            //CheckSecuritySettings();
-
-            var result = default(TResponse);
-
-            if (string.IsNullOrEmpty(URL))
-            {
-                result = (TResponse)Activator.CreateInstance(typeof(TResponse));
-                result.Status = StatusCode.BadServer;
-                result.Messages.Add("Server URL not set!");
-                return result;
-            }
-
-            //sw.Restart();
-            //var deserialized = Serialization.Deserialize<TRequest>(json);
-            //Log("  * {0}{1}() Serializing data took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, json.Length);
-            //sw.Restart();
-
-            var uri = new Uri(URL);
-            var cli = new RestClient(uri);
-            var cmd = string.Format(
-                "{0}{1}?format={2}&responseFormat={3}&serializationVersion={4}", 
-                Action, 
-                includeEntity ? typeof(TEntity).Name : "",
-                requestFormat,
-                responseFormat,
-                BinarySerializationSettings.Version
-            );
-            var req = new RestRequest(cmd, Method.POST)
-            {
-                Timeout = Timeout.Milliseconds,
-            };
-            //Log("  * {0}{1}() Creating Uri, Client and RestRequest took {2}ms", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds);
-            //sw.Restart();
-
-            req.AdvancedResponseWriter = (stream, response) =>
-            {
-                //Log("  * {0}{1}() Response from Server took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, response.ContentLength);
-                //length = response.ContentLength;
-                //sw.Restart();
-                try
-                {
-                    if (responseFormat == SerializationFormat.Binary && typeof(TResponse).HasInterface<ISerializeBinary>())
-                    {
-                        result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, BinarySerializationSettings);
-                    }
-                    else
-                    {
-                        result = Serialization.Deserialize<TResponse>(stream, true);
-                    }
-                }
-                catch (Exception e)
-                {
-                    Logger.Send(LogType.Information, "", $"Error deserializing response: {e.Message}");
-                }
-                //Log("  * {0}{1}() Deserializing Stream took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, response.ContentLength);
-            };
-
-            if(requestFormat == SerializationFormat.Binary && request is ISerializeBinary binary)
-            {
-                var data = binary.WriteBinary(BinarySerializationSettings);
-
-                req.AddOrUpdateParameter("application/octet-stream", data, ParameterType.RequestBody);
-                req.RequestFormat = DataFormat.None;
-            }
-            else
-            {
-                var json = Serialization.Serialize(request);
-
-                req.AddOrUpdateParameter("application/json; charset=utf-8", json, ParameterType.RequestBody);
-                req.RequestFormat = DataFormat.Json;
-            }
-            try
-            {
-                //sw.Restart();
-                var res = cli.Execute(req);
-                //Log("  * {0}{1}() returns {2} bytes in {3}ms", Action, typeof(TEntity).Name, res.ContentLength, sw.ElapsedMilliseconds);
-                if (result == null)
-                {
-                    if (res.ErrorException == null)
-                    {
-                        if (res.StatusCode != HttpStatusCode.OK)
-                            throw new Exception(String.Format("HTTP Request returns {0} {1}" + (int)res.StatusCode, CoreUtils.SplitCamelCase(res.StatusCode.ToString())));
-                        
-                        try
-                        {
-                            Stream stream;
-
-                            if (_compression)
-                            {
-                                //sw.Restart();
-                                var comp = Serialization.Deserialize<CompressedResponse>(res.Content, true);
-                                var bytes = Convert.FromBase64String(comp.Response);
-                                var ms = new MemoryStream(bytes);
-
-                                stream = new MemoryStream();
-                                using (var decompressionStream = new DeflateStream(ms, CompressionMode.Decompress))
-                                {
-                                    decompressionStream.CopyTo(stream);
-                                }
-                            }
-                            else
-                            {
-                                stream = new MemoryStream(res.RawBytes);
-                            }
-
-                            if (responseFormat == SerializationFormat.Binary && typeof(TResponse).HasInterface<ISerializeBinary>())
-                            {
-                                result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, BinarySerializationSettings);
-                            }
-                            else
-                            {
-                                result = Serialization.Deserialize<TResponse>(stream, true);
-                            }
-
-                            stream.Dispose();
-                        }
-                        catch (Exception eDeserialize)
-                        {
-                            throw new Exception(string.Format("Unable to deserialize response!\n\n{0}\n\n{1}", eDeserialize.Message, res.Content));
-                        }
-                    }
-                    else
-                    {
-                        // Connectivity
-                        result = new TResponse();
-                        result.Status = StatusCode.BadServer;
-                        result.Messages.Add(res.ErrorMessage);
-                    }
-                }
-            }
-            catch (Exception err)
-            {
-                result = new TResponse();
-                result.Status = StatusCode.BadServer;
-                result.Messages.Add(err.Message);
-                if (err.InnerException != null)
-                    result.Messages.Add("- " + err.InnerException.Message);
-            }
-
-            req = null;
-            cli = null;
-            //double elapsed = (DateTime.Now - now).TotalMilliseconds;
-            //Log("  * {0}{1}() completed in {2:F0}ms", Action, typeof(TEntity).Name, elapsed);
-            return result;
-        }
-
-        // SupportedTypes() function fallback for if the server is running on ServiceStack
-        private void SupportedTypesOld(RestClient cli, List<string> result)
-        {
-            var req = new RestRequest("/operations/metadata?format=csv", Method.GET) { Timeout = 20000 };
-
-            try
-            {
-                var res = cli.Execute(req);
-                if (res.ErrorException == null)
-                {
-                    var lines = res.Content.Split('\n').Skip(1);
-                    foreach (var line in lines)
-                    {
-                        var fields = line.Split(',');
-                        var svc = fields[2];
-                        if (!result.Contains(svc)) result.Add(svc);
-                        //if (svc.Equals("Comal_Classes_Login"))
-                        //    result.Add("InABox_Core_Login");
-                    }
-                }
-            }
-            catch (Exception)
-            {
-            }
-
-            req = null;
-            cli = null;
-        }
-
-        // This code is duplicated in InABox.Clients.Remote.MsgPack
-        // This should stay as-is, and work to remove RestSharp from MsgPack Client
-        public override IEnumerable<string> SupportedTypes()
-        {
-            var result = new List<string>();
-            var uri = new Uri(URL);
-            var cli = new RestClient(uri);
-            var req = new RestRequest("/classes", Method.GET) { Timeout = 20000 };
-
-            try
-            {
-                var res = cli.Execute(req);
-                if (res.StatusCode != HttpStatusCode.OK)
-                {
-                    SupportedTypesOld(cli, result);
-                }
-                else if (res.ErrorException == null)
-                {
-                    var list = res.Content.Trim('[', ']').Split(',');
-                    foreach (var operation in list)
-                    {
-                        var trimmed = operation.Trim('"');
-                        if (!result.Contains(trimmed)) result.Add(trimmed);
-                        //if (svc.Equals("Comal_Classes_Login"))
-                        //    result.Add("InABox_Core_Login");
-                    }
-                }
-            }
-            catch (Exception e)
-            {
-            }
-
-            req = null;
-            cli = null;
-            return result.ToArray();
-        }
-
-        public override DatabaseInfo Info()
-        {
-            var uri = new Uri(URL);
-            var cli = new RestClient(uri);
-            var req = new RestRequest("/info", Method.GET) { Timeout = 20000 };
-
-            try
-            {
-                var res = cli.Execute(req);
-                if (res.StatusCode != HttpStatusCode.OK)
-                {
-                    return new DatabaseInfo();
-                }
-                else if (res.ErrorException == null)
-                {
-                    var info = Core.Serialization.Deserialize<InfoResponse>(res.Content);
-                    return info.Info;
-                }
-            }
-            catch (Exception)
-            {
-                return new DatabaseInfo();
-            }
-            return new DatabaseInfo();
-        }
-    }
-}

+ 1 - 1
InABox.Client.Remote.Shared/InABox.Client.Remote.Shared.projitems

@@ -9,7 +9,7 @@
     <Import_RootNamespace>InABox.Client.Remote.Shared</Import_RootNamespace>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="$(MSBuildThisFileDirectory)RemoteClient.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)RestClient.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)RemoteException.cs" />
   </ItemGroup>
 </Project>

+ 1 - 0
InABox.Client.Remote.Shared/RestClient.cs

@@ -0,0 +1 @@
+

+ 8 - 0
InABox.Core/SerializationFormat.cs

@@ -0,0 +1,8 @@
+namespace InABox.Core
+{
+    public enum SerializationFormat
+    {
+        Json,
+        Binary
+    }
+}

+ 1 - 5
InABox.Remote.Shared/SerializationFormat.cs

@@ -8,9 +8,5 @@ using System.Threading;
 
 namespace InABox.Remote.Shared
 {
-    public enum SerializationFormat
-    {
-        Json,
-        Binary
-    }
+
 }

+ 20 - 0
inabox.client.rest/InABox.Client.Rest/CompressedResponse.cs

@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+
+namespace InABox.Clients
+{
+    public class CompressedResponse
+    {
+        public CompressedResponse()
+        {
+            Headers = new Dictionary<string, string>();
+            Options = new Dictionary<string, string>();
+        }
+
+        public string ContentType { get; set; }
+        public Dictionary<string, string> Headers { get; set; }
+        public int Status { get; set; }
+        public string StatusCode { get; set; }
+        public string Response { get; set; }
+        public Dictionary<string, string> Options { get; set; }
+    }
+}

+ 0 - 0
InABox.Client.Remote.Json/HttpJsonClient.cs → inabox.client.rest/InABox.Client.Rest/HttpJsonClient.cs


+ 19 - 0
inabox.client.rest/InABox.Client.Rest/InABox.Client.Rest.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\..\inabox.client.websocket\InABox.Client.WebSocket.csproj" />
+      <ProjectReference Include="..\..\InABox.Core\InABox.Core.csproj" />
+      <ProjectReference Include="..\..\inabox.websocket.shared\InABox.WebSocket.Shared.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <PackageReference Include="RestSharp" Version="106.15.0" />
+    </ItemGroup>
+
+</Project>

+ 7 - 0
inabox.client.rest/InABox.Client.Rest/PasswordCache.cs

@@ -0,0 +1,7 @@
+namespace InABox.Clients
+{
+    internal static class PasswordCache
+    {
+        public static string? Password { get; set; }
+    }
+}

+ 16 - 0
inabox.client.rest/InABox.Client.Rest/RemoteException.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+
+namespace InABox.Clients
+{
+    public class RemoteException : RequestException
+    {
+        public RemoteException(IEnumerable<string> messages, Request request) : base(string.Join("\n", messages), StatusCode.Error, request)
+        {
+        }
+
+        public RemoteException(string message, Request request) : base(message, StatusCode.Error, request) { }
+
+        public RemoteException(string message, StatusCode statusCode, Request request) : base(message, statusCode, request) { }
+    }
+}

+ 235 - 74
InABox.Client.Remote.Shared/RemoteClient.cs → inabox.client.rest/InABox.Client.Rest/RestClient.cs

@@ -1,84 +1,63 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.IO.Compression;
 using System.Net;
-using System.Net.Http;
-using InABox.Client.WebSocket;
 using InABox.Core;
-using InABox.Remote.Shared;
 using InABox.WebSocket.Shared;
 using RestSharp;
-using RestSharp.Extensions;
 
 namespace InABox.Clients
 {
-    internal static class LocalCache
+    public class RestClient<TEntity> : BaseClient<TEntity> where TEntity : Entity, new()
     {
-        public static string? Password { get; set; }
-    }
-
-    public static class URLCache
-    {
-        private static string URL { get; set; } = "";
-
-        private static bool isHTTPS;
+        private bool _simpleencryption;
+        private string _server;
+        private bool _compression;
+        private BinarySerializationSettings _binarysettings;
+        
+        public RestClient(string server, bool simpleencryption, bool compression, BinarySerializationSettings binarySerializationSettings)
+        {
+            _server = server;
+            _simpleencryption = simpleencryption;
+            _compression = compression;
+            _binarysettings = binarySerializationSettings;
 
-        public static bool IsHTTPS { get => isHTTPS; }
+            RestClientCache.Check(server);
+            
+        }
 
-        public static void Clear()
+        public RestClient(string server, bool simpleencryption, bool compression) : this(server, simpleencryption, compression, BinarySerializationSettings.Latest)
         {
-            URL = "";
-            isHTTPS = false;
         }
-
-        public static string GetURL(string url)
+        
+        public RestClient(string server, bool simpleencryption) : this(server, simpleencryption, true)
         {
-            url = url.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).Last();
-            if (!String.Equals(url,URL))
-            {
-                var client = new HttpClient { BaseAddress = new Uri($"https://{url}") };
-                try
-                {
-                    client.GetAsync("operations").Wait();
-                    URL = $"https://{url}";
-                    isHTTPS = true;
-                }
-                catch (Exception)
-                {
-                    URL = $"http://{url}";
-                    isHTTPS = false;
-                }
-            }
-            return URL;
         }
-    }
-
-    public static class WebSocketFactory
-    {
-        private static Dictionary<string, WebSocketClient> Clients = new Dictionary<string, WebSocketClient>();
 
-        public static void StartWebSocket(string url, int port, Guid session)
+        public RestClient(string server) : this(server, false)
         {
-            url = url.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).Last();
-            var key = $"{url}:{port}${session}";
-            if (!Clients.ContainsKey(key))
-            {
-                Clients[key] = new WebSocketClient(url, port, session);
-            }
         }
-    }
-
-    public abstract class RemoteClient<TEntity> : BaseClient<TEntity> where TEntity : Entity, new()
-    {
-        public RemoteClient(string url, bool useSimpleEncryption = false)
+        
+        public static string Ping(String[] urls, out DatabaseInfo info)
         {
-            URL = URLCache.GetURL(url);
-            UseSimpleEncryption = useSimpleEncryption;
+            String result = "";
+            info = new DatabaseInfo();
+            List<Task<Tuple<String,DatabaseInfo>>> pings = urls.Select(x => Task.Run(
+                () => new Tuple<String,DatabaseInfo>(x,new RestClient<User>(x).Info())
+            )).ToList();
+            while (pings.Count > 0)
+            {
+                var ping = Task.WhenAny(pings).Result;
+                if (ping.Status == TaskStatus.RanToCompletion && !String.IsNullOrWhiteSpace(ping.Result.Item2.Version))
+                {
+                    result = ping.Result.Item1;
+                    info = ping.Result.Item2;
+                    break;
+                }
+                else
+                    pings.Remove(ping);
+            }
+            return result;
         }
-
-        public string URL { get; set; }
-
-        public bool UseSimpleEncryption { get; set; }
+        
 
         private void PrepareRequest(Request request)
         {
@@ -88,19 +67,19 @@ namespace InABox.Clients
             Request.BeforeRequest?.Invoke(request);
         }
 
-        protected override ValidationData DoValidate(Guid session)
+        protected override ValidationData DoValidate(Guid session = default)
         {
             return Validate(
                 null, null, false, session);
         }
 
-        protected override ValidationData DoValidate(string pin, Guid session)
+        protected override ValidationData DoValidate(string pin, Guid session = default)
         {
             return Validate(
                 null, pin, true, session);
         }
 
-        protected override ValidationData DoValidate(string userid, string password, Guid session)
+        protected override ValidationData DoValidate(string userid, string password, Guid session = default)
         {
             return Validate(
                 userid, password, false, session);
@@ -114,8 +93,8 @@ namespace InABox.Clients
             request.UsePIN = usePin;
             if (usePin)
             {
-                request.UserID = Encryption.Encrypt(ticks, "wCq9rryEJEuHIifYrxRjxg", UseSimpleEncryption);
-                request.Password = Encryption.Encrypt(ticks, "7mhvLnqMwkCAzN+zNGlyyg", UseSimpleEncryption);
+                request.UserID = Encryption.Encrypt(ticks, "wCq9rryEJEuHIifYrxRjxg", _simpleencryption);
+                request.Password = Encryption.Encrypt(ticks, "7mhvLnqMwkCAzN+zNGlyyg", _simpleencryption);
                 request.PIN = password;
             }
             else
@@ -144,11 +123,11 @@ namespace InABox.Clients
                         {
                             if (notifyResponse.SocketPort.HasValue)
                             {
-                                WebSocketFactory.StartWebSocket(URL, notifyResponse.SocketPort.Value, response.Session);
+                                SocketClientCache.StartWebSocket(_server, notifyResponse.SocketPort.Value, response.Session);
                             }
                         }
                     }
-                    LocalCache.Password = password;
+                    PasswordCache.Password = password;
                     return new ValidationData(
                         response.ValidationResult,
                         response.UserID,
@@ -175,8 +154,152 @@ namespace InABox.Clients
             );
         }
 
-        protected abstract TResponse SendRequest<TRequest, TResponse>(TRequest request, string Action, SerializationFormat requestFormat, SerializationFormat responseFormat, bool includeEntity = true)
-            where TRequest : Request, new() where TResponse : Response, new();
+        protected TResponse SendRequest<TRequest, TResponse>(TRequest request, string Action, SerializationFormat requestFormat, SerializationFormat responseFormat, bool includeEntity = true)
+            where TRequest : Request, new() where TResponse : Response, new()
+        {
+
+            var result = default(TResponse);
+
+            var url = RestClientCache.URL(_server);
+            
+            if (string.IsNullOrEmpty(url))
+            {
+                result = (TResponse)Activator.CreateInstance(typeof(TResponse));
+                result.Status = StatusCode.BadServer;
+                result.Messages.Add("Server URL not set!");
+                return result;
+            }
+
+            var uri = new Uri(url);
+            var cli = new RestClient(uri);
+            var cmd = string.Format(
+                "{0}{1}?format={2}&responseFormat={3}&serializationVersion={4}", 
+                Action, 
+                includeEntity ? typeof(TEntity).Name : "",
+                requestFormat,
+                responseFormat,
+                _binarysettings.Version
+            );
+            var req = new RestRequest(cmd, Method.POST)
+            {
+                Timeout = Timeout.Milliseconds,
+            };
+            //Log("  * {0}{1}() Creating Uri, Client and RestRequest took {2}ms", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds);
+            //sw.Restart();
+
+            req.AdvancedResponseWriter = (stream, response) =>
+            {
+                //Log("  * {0}{1}() Response from Server took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, response.ContentLength);
+                //length = response.ContentLength;
+                //sw.Restart();
+                try
+                {
+                    if (responseFormat == SerializationFormat.Binary && typeof(TResponse).HasInterface<ISerializeBinary>())
+                    {
+                        result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, _binarysettings);
+                    }
+                    else
+                    {
+                        result = Serialization.Deserialize<TResponse>(stream, true);
+                    }
+                }
+                catch (Exception e)
+                {
+                    Logger.Send(LogType.Information, "", $"Error deserializing response: {e.Message}");
+                }
+                //Log("  * {0}{1}() Deserializing Stream took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, response.ContentLength);
+            };
+
+            if(requestFormat == SerializationFormat.Binary && request is ISerializeBinary binary)
+            {
+                var data = binary.WriteBinary(_binarysettings);
+
+                req.AddOrUpdateParameter("application/octet-stream", data, ParameterType.RequestBody);
+                req.RequestFormat = DataFormat.None;
+            }
+            else
+            {
+                var json = Serialization.Serialize(request);
+
+                req.AddOrUpdateParameter("application/json; charset=utf-8", json, ParameterType.RequestBody);
+                req.RequestFormat = DataFormat.Json;
+            }
+            try
+            {
+                //sw.Restart();
+                var res = cli.Execute(req);
+                //Log("  * {0}{1}() returns {2} bytes in {3}ms", Action, typeof(TEntity).Name, res.ContentLength, sw.ElapsedMilliseconds);
+                if (result == null)
+                {
+                    if (res.ErrorException == null)
+                    {
+                        if (res.StatusCode != HttpStatusCode.OK)
+                            throw new Exception(String.Format("HTTP Request returns {0} {1}" + (int)res.StatusCode, CoreUtils.SplitCamelCase(res.StatusCode.ToString())));
+                        
+                        try
+                        {
+                            Stream stream;
+
+                            if (_compression)
+                            {
+                                //sw.Restart();
+                                var comp = Serialization.Deserialize<CompressedResponse>(res.Content, true);
+                                var bytes = Convert.FromBase64String(comp.Response);
+                                var ms = new MemoryStream(bytes);
+
+                                stream = new MemoryStream();
+                                using (var decompressionStream = new DeflateStream(ms, CompressionMode.Decompress))
+                                {
+                                    decompressionStream.CopyTo(stream);
+                                }
+                            }
+                            else
+                            {
+                                stream = new MemoryStream(res.RawBytes);
+                            }
+
+                            if (responseFormat == SerializationFormat.Binary && typeof(TResponse).HasInterface<ISerializeBinary>())
+                            {
+                                result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, _binarysettings);
+                            }
+                            else
+                            {
+                                result = Serialization.Deserialize<TResponse>(stream, true);
+                            }
+
+                            stream.Dispose();
+                        }
+                        catch (Exception eDeserialize)
+                        {
+                            throw new Exception(string.Format("Unable to deserialize response!\n\n{0}\n\n{1}", eDeserialize.Message, res.Content));
+                        }
+                    }
+                    else
+                    {
+                        // Connectivity
+                        result = new TResponse();
+                        result.Status = StatusCode.BadServer;
+                        result.Messages.Add(res.ErrorMessage);
+                    }
+                }
+            }
+            catch (Exception err)
+            {
+                result = new TResponse();
+                result.Status = StatusCode.BadServer;
+                result.Messages.Add(err.Message);
+                if (err.InnerException != null)
+                    result.Messages.Add("- " + err.InnerException.Message);
+            }
+
+            req = null;
+            cli = null;
+            //double elapsed = (DateTime.Now - now).TotalMilliseconds;
+            //Log("  * {0}{1}() completed in {2:F0}ms", Action, typeof(TEntity).Name, elapsed);
+            return result;
+        }     
+        
+        
 
         #region Query Data
 
@@ -441,9 +564,8 @@ namespace InABox.Clients
 
         protected override bool DoPing()
         {
-            var uri = new Uri(URL);
-            var cli = new RestClient(uri);
-            var req = new RestRequest("/classes", Method.GET) { Timeout = 20000 };
+            var cli = new RestClient(new Uri(RestClientCache.URL(_server)));
+            var req = new RestRequest("/info", Method.GET) { Timeout = 20000 };
 
             try
             {
@@ -459,5 +581,44 @@ namespace InABox.Clients
         }
 
         #endregion
+        
+        public override IEnumerable<string> SupportedTypes()
+        {
+            var result = new List<string>();
+
+            var url = RestClientCache.URL(_server);
+            
+            var uri = new Uri(url);
+            var cli = new RestClient(uri);
+            var req = new RestRequest("/classes", Method.GET) { Timeout = 20000 };
+
+            try
+            {
+                var res = cli.Execute(req);
+                if (res.ErrorException == null)
+                {
+                    var list = res.Content.Trim('[', ']').Split(',');
+                    foreach (var operation in list)
+                    {
+                        var trimmed = operation.Trim('"');
+                        if (!result.Contains(trimmed)) result.Add(trimmed);
+                        //if (svc.Equals("Comal_Classes_Login"))
+                        //    result.Add("InABox_Core_Login");
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+            }
+
+            req = null;
+            cli = null;
+            return result.ToArray();
+        }
+
+        public override DatabaseInfo Info()
+        {
+            return RestClientCache.Info(_server);
+        }
     }
 }

+ 71 - 0
inabox.client.rest/InABox.Client.Rest/RestClientCache.cs

@@ -0,0 +1,71 @@
+using System.Net;
+using RestSharp;
+
+namespace InABox.Clients
+{
+    internal static class RestClientCache
+    {
+        private static Dictionary<String, Tuple<String, bool, DatabaseInfo>> _cache = new Dictionary<string, Tuple<string, bool, DatabaseInfo>>();
+            
+        public static String URL(String host)
+        {
+            if (_cache.TryGetValue(host, out var value))
+                return value.Item1;
+            return "";
+        }
+
+        public static bool IsSecure(String host)
+        {
+            if (_cache.TryGetValue(host, out var value))
+                return value.Item2;
+            return false;
+        }
+        
+        public static DatabaseInfo Info(String host)
+        {
+            if (_cache.TryGetValue(host, out var value))
+                return value.Item3;
+            return new DatabaseInfo();
+        }
+            
+        private static bool Check(String host, bool https)
+        {
+            var uri = new Uri(host);
+            string schema = https ? "https" : "http";
+            var url = $"{schema}://{uri.Host}:{uri.Port}";
+            var req = new RestRequest("/info", Method.GET) { Timeout = 20000 };
+            var res = new RestClient(new Uri(url)).Execute(req);
+            if ((res.StatusCode != HttpStatusCode.OK) || (res.ErrorException != null))
+                return false;
+            try
+            {
+                var response = Core.Serialization.Deserialize<InfoResponse>(res.Content);
+                if (response?.Info == null)
+                    return false;
+                _cache[host] = new Tuple<string, bool, DatabaseInfo>(url, https, response.Info);
+                return true;
+            }
+            catch (Exception e)
+            {
+                return false;
+            }
+        }
+        
+        public static String Check(string server)
+        {
+            var host = server.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
+            
+            if (String.IsNullOrWhiteSpace(host))
+                return "";
+            
+            if (_cache.TryGetValue(host, out var cached))
+                return cached.Item1;
+            
+            if (!Check(host, true))
+                Check(host, false);
+            
+            return _cache.TryGetValue(host, out var check) ? check.Item1 : "";
+
+        }
+    }
+}

+ 17 - 0
inabox.client.rest/InABox.Client.Rest/SocketClientCache.cs

@@ -0,0 +1,17 @@
+using InABox.Client.WebSocket;
+
+namespace InABox.Clients
+{
+    public static class SocketClientCache
+    {
+        private static Dictionary<string, WebSocketClient> Clients = new Dictionary<string, WebSocketClient>();
+
+        public static void StartWebSocket(string url, int port, Guid session)
+        {
+            Uri uri = new Uri(url);
+            var key = $"{uri.Host}${session}";
+            if (!Clients.ContainsKey(key))
+                Clients[key] = new WebSocketClient(uri.Host, port, session);
+        }
+    }
+}