|
@@ -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);
|
|
|
+ }
|
|
|
}
|
|
|
}
|