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(); Options = new Dictionary(); } public string ContentType { get; set; } public Dictionary Headers { get; set; } public int Status { get; set; } public string StatusCode { get; set; } public string Response { get; set; } public Dictionary Options { get; set; } } public class JsonClient : RemoteClient where TEntity : Entity, new() { private static RestClient cli = null; 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 static class StaticClients { public static Dictionary Clients = new Dictionary(); } public JsonClient(string url, bool useSimpleEncryption) : this(url, useSimpleEncryption, false, BinarySerializationSettings.Latest) { } public JsonClient(string url) : this(url, false, false, BinarySerializationSettings.Latest) { } public static string Ping(String[] urls, out DatabaseInfo info) { String result = ""; info = new DatabaseInfo(); List>> pings = urls.Select(x => Task.Run( () => new Tuple(x,new JsonClient(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 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(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); if (cli == null) SetClient(URL); 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()) // { // result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, BinarySerializationSettings); // } // else // { // result = Serialization.Deserialize(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", 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(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()) { result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, BinarySerializationSettings); } else { result = Serialization.Deserialize(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; //double elapsed = (DateTime.Now - now).TotalMilliseconds; //Log(" * {0}{1}() completed in {2:F0}ms", Action, typeof(TEntity).Name, elapsed); return result; } private void SetClient(string uRL) { var uri = new Uri(URL); if (StaticClients.Clients.ContainsKey(URL)) cli = StaticClients.Clients[URL]; else { cli = new RestClient(uri); StaticClients.Clients.Add(URL, cli); } } // SupportedTypes() function fallback for if the server is running on ServiceStack private void SupportedTypesOld(RestClient cli, List 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 SupportedTypes() { var result = new List(); 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(res.Content); return info.Info; } } catch (Exception) { return new DatabaseInfo(); } return new DatabaseInfo(); } } }