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.Text; using InABox.Core; 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 readonly bool _compression = true; public JsonClient(string url, int port, bool useSimpleEncryption, bool compression) : base(url, port, useSimpleEncryption) { _compression = compression; } public JsonClient(string url, int port, bool useSimpleEncryption) : this(url, port, useSimpleEncryption, true) { } public JsonClient(string url, int port) : this(url, port, false, true) { } //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, 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); long length = 0; 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 json = Serialization.Serialize(request); 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(string.Format("{0}:{1}", URL, Port)); var cli = new RestClient(uri); var cmd = string.Format( "{0}{1}?format=json{2}", Action, includeEntity ? typeof(TEntity).Name : "", "" ); 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(); //var str = new StreamReader(stream);//.ReadToEnd(); try { 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); }; //result = new TResponse(); 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 { var data = ""; if (_compression) { //sw.Restart(); var comp = Serialization.Deserialize(res.Content, true); var bytes = Convert.FromBase64String(comp.Response); var ms = new MemoryStream(bytes); using (var resultDeCompressedStream = new MemoryStream()) { using (var decompressionStream = new DeflateStream(ms, CompressionMode.Decompress)) { decompressionStream.CopyTo(resultDeCompressedStream); data = Encoding.UTF8.GetString(resultDeCompressedStream.ToArray(), 0, Convert.ToInt32(resultDeCompressedStream.Length)); } } //Log(" * {0}{1}() Decompressing {2} -> {3} ({4:F2}%) bytes took {5}ms", Action, typeof(TEntity).Name, res.ContentLength, data.Length, ((double)data.Length *100.0F / (double)res.ContentLength), sw.ElapsedMilliseconds); } else { data = res.Content; } //sw.Restart(); result = Serialization.Deserialize(data, true); //Log(" * {0}{1}() Deserializing data took {2}ms", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds); //sw.Stop(); } 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 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 e) { } 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(string.Format("{0}:{1}", URL, Port)); 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(string.Format("{0}:{1}", URL, Port)); 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 e) { return new DatabaseInfo(); } return new DatabaseInfo(); } } }