using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Plugins.Countly.Models; using Plugins.Countly.Persistance.Repositories; using UnityEngine; using UnityEngine.Networking; namespace Plugins.Countly.Helpers { public class RequestCountlyHelper { private readonly CountlyConfigModel _config; private readonly ICountlyUtils _countlyUtils; private readonly RequestRepository _requestRepo; internal RequestCountlyHelper(CountlyConfigModel config, ICountlyUtils countlyUtils, RequestRepository requestRepo) { _config = config; _countlyUtils = countlyUtils; _requestRepo = requestRepo; } private void AddRequestToQueue(CountlyRequestModel request) { if (_requestRepo.Count == _config.StoredRequestLimit) _requestRepo.Dequeue(); _requestRepo.Enqueue(request); } // private CountlyRequestModel GetRequestFromQueue() // { // return _totalRequests.Peek(); // } internal void ProcessQueue() { var requests = _requestRepo.Models.ToArray(); // Debug.Log("[RequestCountlyHelper] Process queue, requests: " + requests.Length); foreach (var reqModel in requests) { var isProcessed = false; var retryCount = 0; while (!isProcessed && retryCount < 3) { try { ProcessRequest(reqModel); isProcessed = true; } catch { retryCount++; isProcessed = false; } finally { if (isProcessed) _requestRepo.Dequeue(); } } } } private void ProcessRequest(CountlyRequestModel model) { if (model.IsRequestGetType) { Task.Run(() => GetAsync(model.RequestUrl)); } else { Task.Run(() => PostAsync(model.RequestUrl, model.RequestData)); } } /// /// Builds request URL using ServerUrl, AppKey, DeviceID and supplied queryParams parameters. /// The data is appended in the URL. /// /// /// private string BuildGetRequest(Dictionary queryParams) { var requestStringBuilder = new StringBuilder(); //Metrics added to each request foreach (var item in _countlyUtils.GetBaseParams()) { requestStringBuilder.AppendFormat((item.Key != "app_key" ? "&" : string.Empty) + "{0}={1}", UnityWebRequest.EscapeURL(item.Key), UnityWebRequest.EscapeURL(Convert.ToString(item.Value))); } //Query params supplied for creating request foreach (var item in queryParams) { if (!string.IsNullOrEmpty(item.Key) && item.Value != null) { requestStringBuilder.AppendFormat("&{0}={1}", UnityWebRequest.EscapeURL(item.Key), UnityWebRequest.EscapeURL(Convert.ToString(item.Value))); } } if (!string.IsNullOrEmpty(_config.Salt)) { // Create a SHA256 using (var sha256Hash = SHA256.Create()) { var data = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(requestStringBuilder + _config.Salt)); requestStringBuilder.Insert(0, _countlyUtils.GetBaseInputUrl()); return requestStringBuilder.AppendFormat("&checksum256={0}", _countlyUtils.GetStringFromBytes(data)).ToString(); } } requestStringBuilder.Insert(0, _countlyUtils.GetBaseInputUrl()); return requestStringBuilder.ToString(); } /// /// Serializes the post data (base params required for a request, and the supplied queryParams) in a string. /// /// /// private string BuildPostRequest(Dictionary queryParams) { var baseParams = _countlyUtils.GetBaseParams(); foreach (var item in queryParams) { baseParams.Add(UnityWebRequest.EscapeURL(item.Key), item.Value); } var data = JsonConvert.SerializeObject(baseParams); if (!string.IsNullOrEmpty(_config.Salt)) { // Create a SHA256 using (var sha256Hash = SHA256.Create()) { var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(data + _config.Salt)); baseParams.Add("checksum256", bytes); return JsonConvert.SerializeObject(baseParams); } } return data; } /// /// Uses Get/Post method to make request to the Countly server and returns the response. /// /// /// internal CountlyResponse GetResponse(Dictionary queryParams, bool addToRequestQueue = false) { var data = BuildPostRequest(queryParams); if (_config.EnablePost || data.Length > 1800) { return Post(_countlyUtils.GetBaseInputUrl(), data, addToRequestQueue); } return Get(BuildGetRequest(queryParams), addToRequestQueue); } /// /// Uses GetAsync/PostAsync method to make request to the Countly server and returns the response. /// /// /// /// internal Task GetResponseAsync(Dictionary queryParams, bool addToRequestQueue = false) { var data = BuildPostRequest(queryParams); if (_config.EnablePost || data.Length > 1800) { return Task.Run( () => PostAsync(_countlyUtils.GetBaseInputUrl(), BuildPostRequest(queryParams), addToRequestQueue)); } return Task.Run(() => GetAsync(BuildGetRequest(queryParams), addToRequestQueue)); } /// /// Makes a GET request to the Counlty server. /// /// /// private CountlyResponse Get(string url, bool addToRequestQueue = false) { var countlyResponse = new CountlyResponse(); try { if (addToRequestQueue) throw new Exception("Request added to queue."); var request = (HttpWebRequest) WebRequest.Create(url); using (var response = (HttpWebResponse) request.GetResponse()) using (var stream = response.GetResponseStream()) using (var reader = new StreamReader(stream)) { var res = reader.ReadToEnd(); countlyResponse.IsSuccess = !string.IsNullOrEmpty(res); countlyResponse.Data = res; } } catch (Exception ex) { addToRequestQueue = true; countlyResponse.ErrorMessage = ex.Message; } if (addToRequestQueue) { var requestModel = new CountlyRequestModel(true, url, null, DateTime.UtcNow); AddRequestToQueue(requestModel); } #if UNITY_EDITOR //Log to Unity Console if (_config.EnableConsoleErrorLogging) Debug.Log(countlyResponse.IsSuccess); #endif return countlyResponse; } /// /// Makes an Asynchronous GET request to the Countly server. /// /// /// /// internal async Task GetAsync(string url, bool addToRequestQueue = false) { var countlyResponse = new CountlyResponse(); try { if (addToRequestQueue) throw new Exception("Request added to queue."); var request = (HttpWebRequest) WebRequest.Create(url); using (var response = (HttpWebResponse) await request.GetResponseAsync()) using (var stream = response.GetResponseStream()) using (var reader = new StreamReader(stream)) { var res = await reader.ReadToEndAsync(); countlyResponse.IsSuccess = !string.IsNullOrEmpty(res); countlyResponse.Data = res; #if UNITY_EDITOR //Log to Unity Console if (_config.EnableConsoleErrorLogging) Debug.Log(countlyResponse.IsSuccess); #endif return countlyResponse; } } catch (Exception ex) { addToRequestQueue = true; countlyResponse.ErrorMessage = ex.Message; } if (addToRequestQueue) { var requestModel = new CountlyRequestModel(true, url, null, DateTime.UtcNow); AddRequestToQueue(requestModel); } return countlyResponse; } /// /// Makes a POST request to the Countly server. /// /// /// /// /// private CountlyResponse Post(string uri, string data, bool addToRequestQueue = false) { var countlyResponse = new CountlyResponse(); try { if (addToRequestQueue) throw new Exception("Request added to queue."); var dataBytes = Encoding.UTF8.GetBytes(data); var request = (HttpWebRequest) WebRequest.Create(uri); request.ContentLength = dataBytes.Length; request.ContentType = "application/json"; request.Method = "POST"; using (var requestBody = request.GetRequestStream()) { requestBody.Write(dataBytes, 0, dataBytes.Length); } using (var response = (HttpWebResponse) request.GetResponse()) using (var stream = response.GetResponseStream()) using (var reader = new StreamReader(stream)) { var res = reader.ReadToEnd(); countlyResponse.IsSuccess = !string.IsNullOrEmpty(res); countlyResponse.Data = res; #if UNITY_EDITOR //Log to Unity Console if (_config.EnableConsoleErrorLogging) Debug.Log(countlyResponse.IsSuccess); #endif } } catch (Exception ex) { addToRequestQueue = true; countlyResponse.ErrorMessage = ex.Message; } if (addToRequestQueue) { var requestModel = new CountlyRequestModel(false, uri, data, DateTime.UtcNow); AddRequestToQueue(requestModel); } return countlyResponse; } /// /// Makes an Asynchronous POST request to the Countly server. /// /// /// /// /// /// private async Task PostAsync(string uri, string data, bool addToRequestQueue = false) { var countlyResponse = new CountlyResponse(); try { if (addToRequestQueue) throw new Exception("Request added to queue."); var dataBytes = Encoding.UTF8.GetBytes(data); var request = (HttpWebRequest) WebRequest.Create(uri); request.ContentLength = dataBytes.Length; request.ContentType = "application/json"; request.Method = "POST"; using (var requestBody = request.GetRequestStream()) { await requestBody.WriteAsync(dataBytes, 0, dataBytes.Length); } using (var response = (HttpWebResponse) await request.GetResponseAsync()) using (var stream = response.GetResponseStream()) using (var reader = new StreamReader(stream)) { var res = await reader.ReadToEndAsync(); countlyResponse.IsSuccess = !string.IsNullOrEmpty(res); countlyResponse.Data = res; #if UNITY_EDITOR //Log to Unity Console if (_config.EnableConsoleErrorLogging) Debug.Log(countlyResponse.IsSuccess); #endif return countlyResponse; } } catch (Exception ex) { addToRequestQueue = true; countlyResponse.ErrorMessage = ex.Message; } if (addToRequestQueue) { var requestModel = new CountlyRequestModel(false, uri, data, DateTime.UtcNow); AddRequestToQueue(requestModel); } return countlyResponse; } // internal static void InvokeMethod(Type type, string methodName, object[] payLoadData) // { // Testing tt = new Testing(); // MethodInfo info = type.GetMethod(methodName); // info.Invoke(tt, new object[] { payLoadData }); // } } }