JsonClient.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.IO.Compression;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Http;
  8. using System.Runtime.CompilerServices;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using InABox.Core;
  12. using InABox.Remote.Shared;
  13. using RestSharp;
  14. //using InABox.Logging;
  15. namespace InABox.Clients
  16. {
  17. public class CompressedResponse
  18. {
  19. public CompressedResponse()
  20. {
  21. Headers = new Dictionary<string, string>();
  22. Options = new Dictionary<string, string>();
  23. }
  24. public string ContentType { get; set; }
  25. public Dictionary<string, string> Headers { get; set; }
  26. public int Status { get; set; }
  27. public string StatusCode { get; set; }
  28. public string Response { get; set; }
  29. public Dictionary<string, string> Options { get; set; }
  30. }
  31. public class JsonClient<TEntity> : RemoteClient<TEntity> where TEntity : Entity, new()
  32. {
  33. private static RestClient cli = null;
  34. private readonly bool _compression = true;
  35. private BinarySerializationSettings BinarySerializationSettings;
  36. public JsonClient(string url, bool useSimpleEncryption, bool compression, BinarySerializationSettings binarySerializationSettings) : base(url, useSimpleEncryption)
  37. {
  38. _compression = compression;
  39. BinarySerializationSettings = binarySerializationSettings;
  40. }
  41. public static class StaticClients
  42. {
  43. public static Dictionary<string, RestClient> Clients = new Dictionary<string, RestClient>();
  44. }
  45. public JsonClient(string url, bool useSimpleEncryption) : this(url, useSimpleEncryption, false, BinarySerializationSettings.Latest)
  46. {
  47. }
  48. public JsonClient(string url) : this(url, false, false, BinarySerializationSettings.Latest)
  49. {
  50. }
  51. public static string Ping(String[] urls, out DatabaseInfo info)
  52. {
  53. String result = "";
  54. info = new DatabaseInfo();
  55. List<Task<Tuple<String,DatabaseInfo>>> pings = urls.Select(x => Task.Run(
  56. () => new Tuple<String,DatabaseInfo>(x,new JsonClient<User>(x).Info())
  57. )).ToList();
  58. while (pings.Count > 0)
  59. {
  60. var ping = Task.WhenAny(pings).Result;
  61. if (ping.Status == TaskStatus.RanToCompletion && !String.IsNullOrWhiteSpace(ping.Result.Item2.Version))
  62. {
  63. result = ping.Result.Item1;
  64. info = ping.Result.Item2;
  65. break;
  66. }
  67. else
  68. pings.Remove(ping);
  69. }
  70. return result;
  71. }
  72. //private static void CheckSecuritySettings()
  73. //{
  74. // Boolean platformSupportsTls12 = false;
  75. // foreach (SecurityProtocolType protocol in Enum.GetValues(typeof(SecurityProtocolType)))
  76. // platformSupportsTls12 = platformSupportsTls12 || (protocol.GetHashCode() == 3072);
  77. // if (!ServicePointManager.SecurityProtocol.HasFlag((SecurityProtocolType)3072) && platformSupportsTls12)
  78. // ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
  79. // if (ServicePointManager.SecurityProtocol.HasFlag(SecurityProtocolType.Ssl3))
  80. // System.Net.ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
  81. //}
  82. protected override TResponse SendRequest<TRequest, TResponse>(TRequest request, string Action, SerializationFormat requestFormat, SerializationFormat responseFormat, bool includeEntity = true)
  83. {
  84. //DateTime now = DateTime.Now;
  85. //Log(" * {0}{1}() Starting..", Action, typeof(TEntity).Name);
  86. //Stopwatch sw = new Stopwatch();
  87. //sw.Start();
  88. //CheckSecuritySettings();
  89. var result = default(TResponse);
  90. if (string.IsNullOrEmpty(URL))
  91. {
  92. result = (TResponse)Activator.CreateInstance(typeof(TResponse));
  93. result.Status = StatusCode.BadServer;
  94. result.Messages.Add("Server URL not set!");
  95. return result;
  96. }
  97. //sw.Restart();
  98. //var deserialized = Serialization.Deserialize<TRequest>(json);
  99. //Log(" * {0}{1}() Serializing data took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, json.Length);
  100. //sw.Restart();
  101. var uri = new Uri(URL);
  102. if (cli == null)
  103. SetClient(URL);
  104. var cmd = string.Format(
  105. "{0}{1}?format={2}&responseFormat={3}&serializationVersion={4}",
  106. Action,
  107. includeEntity ? typeof(TEntity).Name : "",
  108. requestFormat,
  109. responseFormat,
  110. BinarySerializationSettings.Version
  111. );
  112. var req = new RestRequest(cmd, Method.Post)
  113. {
  114. Timeout = Timeout.Milliseconds,
  115. };
  116. //Log(" * {0}{1}() Creating Uri, Client and RestRequest took {2}ms", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds);
  117. //sw.Restart();
  118. //req.AdvancedResponseWriter = (stream, response) =>
  119. //{
  120. // //Log(" * {0}{1}() Response from Server took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, response.ContentLength);
  121. // //length = response.ContentLength;
  122. // //sw.Restart();
  123. // try
  124. // {
  125. // if (responseFormat == SerializationFormat.Binary && typeof(TResponse).HasInterface<ISerializeBinary>())
  126. // {
  127. // result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, BinarySerializationSettings);
  128. // }
  129. // else
  130. // {
  131. // result = Serialization.Deserialize<TResponse>(stream, true);
  132. // }
  133. // }
  134. // catch (Exception e)
  135. // {
  136. // Logger.Send(LogType.Information, "", $"Error deserializing response: {e.Message}");
  137. // }
  138. // //Log(" * {0}{1}() Deserializing Stream took {2}ms ({3} bytes)", Action, typeof(TEntity).Name, sw.ElapsedMilliseconds, response.ContentLength);
  139. //};
  140. if(requestFormat == SerializationFormat.Binary && request is ISerializeBinary binary)
  141. {
  142. var data = binary.WriteBinary(BinarySerializationSettings);
  143. req.AddOrUpdateParameter("application/octet-stream", data, ParameterType.RequestBody);
  144. req.RequestFormat = DataFormat.None;
  145. }
  146. else
  147. {
  148. var json = Serialization.Serialize(request);
  149. req.AddOrUpdateParameter("application/json", json, ParameterType.RequestBody);
  150. req.RequestFormat = DataFormat.Json;
  151. }
  152. try
  153. {
  154. //sw.Restart();
  155. var res = cli.Execute(req);
  156. //Log(" * {0}{1}() returns {2} bytes in {3}ms", Action, typeof(TEntity).Name, res.ContentLength, sw.ElapsedMilliseconds);
  157. if (result == null)
  158. {
  159. if (res.ErrorException == null)
  160. {
  161. if (res.StatusCode != HttpStatusCode.OK)
  162. throw new Exception(String.Format("HTTP Request returns {0} {1}" + (int)res.StatusCode, CoreUtils.SplitCamelCase(res.StatusCode.ToString())));
  163. try
  164. {
  165. Stream stream;
  166. if (_compression)
  167. {
  168. //sw.Restart();
  169. var comp = Serialization.Deserialize<CompressedResponse>(res.Content, true);
  170. var bytes = Convert.FromBase64String(comp.Response);
  171. var ms = new MemoryStream(bytes);
  172. stream = new MemoryStream();
  173. using (var decompressionStream = new DeflateStream(ms, CompressionMode.Decompress))
  174. {
  175. decompressionStream.CopyTo(stream);
  176. }
  177. }
  178. else
  179. {
  180. stream = new MemoryStream(res.RawBytes);
  181. }
  182. if (responseFormat == SerializationFormat.Binary && typeof(TResponse).HasInterface<ISerializeBinary>())
  183. {
  184. result = (TResponse)Serialization.ReadBinary(typeof(TResponse), stream, BinarySerializationSettings);
  185. }
  186. else
  187. {
  188. result = Serialization.Deserialize<TResponse>(stream, true);
  189. }
  190. stream.Dispose();
  191. }
  192. catch (Exception eDeserialize)
  193. {
  194. throw new Exception(string.Format("Unable to deserialize response!\n\n{0}\n\n{1}", eDeserialize.Message, res.Content));
  195. }
  196. }
  197. else
  198. {
  199. // Connectivity
  200. result = new TResponse();
  201. result.Status = StatusCode.BadServer;
  202. result.Messages.Add(res.ErrorMessage);
  203. }
  204. }
  205. }
  206. catch (Exception err)
  207. {
  208. result = new TResponse();
  209. result.Status = StatusCode.BadServer;
  210. result.Messages.Add(err.Message);
  211. if (err.InnerException != null)
  212. result.Messages.Add("- " + err.InnerException.Message);
  213. }
  214. req = null;
  215. //double elapsed = (DateTime.Now - now).TotalMilliseconds;
  216. //Log(" * {0}{1}() completed in {2:F0}ms", Action, typeof(TEntity).Name, elapsed);
  217. return result;
  218. }
  219. private void SetClient(string uRL)
  220. {
  221. var uri = new Uri(URL);
  222. if (StaticClients.Clients.ContainsKey(URL))
  223. cli = StaticClients.Clients[URL];
  224. else
  225. {
  226. cli = new RestClient(uri);
  227. StaticClients.Clients.Add(URL, cli);
  228. }
  229. }
  230. // SupportedTypes() function fallback for if the server is running on ServiceStack
  231. private void SupportedTypesOld(RestClient cli, List<string> result)
  232. {
  233. var req = new RestRequest("/operations/metadata?format=csv", Method.Get) { Timeout = 20000 };
  234. try
  235. {
  236. var res = cli.Execute(req);
  237. if (res.ErrorException == null)
  238. {
  239. var lines = res.Content.Split('\n').Skip(1);
  240. foreach (var line in lines)
  241. {
  242. var fields = line.Split(',');
  243. var svc = fields[2];
  244. if (!result.Contains(svc)) result.Add(svc);
  245. //if (svc.Equals("Comal_Classes_Login"))
  246. // result.Add("InABox_Core_Login");
  247. }
  248. }
  249. }
  250. catch (Exception)
  251. {
  252. }
  253. req = null;
  254. cli = null;
  255. }
  256. // This code is duplicated in InABox.Clients.Remote.MsgPack
  257. // This should stay as-is, and work to remove RestSharp from MsgPack Client
  258. public override IEnumerable<string> SupportedTypes()
  259. {
  260. var result = new List<string>();
  261. var uri = new Uri(URL);
  262. var cli = new RestClient(uri);
  263. var req = new RestRequest("/classes", Method.Get) { Timeout = 20000 };
  264. try
  265. {
  266. var res = cli.Execute(req);
  267. if (res.StatusCode != HttpStatusCode.OK)
  268. {
  269. SupportedTypesOld(cli, result);
  270. }
  271. else if (res.ErrorException == null)
  272. {
  273. var list = res.Content.Trim('[', ']').Split(',');
  274. foreach (var operation in list)
  275. {
  276. var trimmed = operation.Trim('"');
  277. if (!result.Contains(trimmed)) result.Add(trimmed);
  278. //if (svc.Equals("Comal_Classes_Login"))
  279. // result.Add("InABox_Core_Login");
  280. }
  281. }
  282. }
  283. catch (Exception e)
  284. {
  285. }
  286. req = null;
  287. cli = null;
  288. return result.ToArray();
  289. }
  290. public override DatabaseInfo Info()
  291. {
  292. var uri = new Uri(URL);
  293. var cli = new RestClient(uri);
  294. var req = new RestRequest("/info", Method.Get) { Timeout = 20000 };
  295. try
  296. {
  297. var res = cli.Execute(req);
  298. if (res.StatusCode != HttpStatusCode.OK)
  299. {
  300. return new DatabaseInfo();
  301. }
  302. else if (res.ErrorException == null)
  303. {
  304. var info = Core.Serialization.Deserialize<InfoResponse>(res.Content);
  305. return info.Info;
  306. }
  307. }
  308. catch (Exception)
  309. {
  310. return new DatabaseInfo();
  311. }
  312. return new DatabaseInfo();
  313. }
  314. }
  315. }