/// <summary> /// Reads the value from the data store with the given key. If the value doesn't /// exist, or it is of the wrong type then an error will occur. /// </summary> /// /// <param name="key">The key to look up.</param> /// /// <returns>The Json Object value.</returns> public ReadOnlyDictionary <string, object> GetJsonObject(string key) { ReleaseAssert.IsNotNull(key, "Cannot provide a null key."); object value = null; lock (m_lock) { m_dataStore.TryGetValue(key, out value); } ReleaseAssert.IsNotNull(value, "Value for key '" + key + "' is missing from data store."); ReleaseAssert.IsTrue(value is ReadOnlyDictionary <string, object>, "Value is incorrect type."); return((ReadOnlyDictionary <string, object>)value); }
/// <summary> /// Reads the value from the data store with the given key. If the value doesn't /// exist, or it is of the wrong type then an error will occur. /// </summary> /// /// <param name="key">The key to look up.</param> /// /// <returns>The integer value.</returns> public int GetInt(string key) { ReleaseAssert.IsNotNull(key, "Cannot provide a null key."); object value = null; lock (m_lock) { m_dataStore.TryGetValue(key, out value); } ReleaseAssert.IsNotNull(value, "Value for key '" + key + "' is missing from data store."); ReleaseAssert.IsTrue(value is int, "Value is incorrect type."); return((int)value); }
/// <summary> /// Parses the HTTP response code from the given HTTP STATUS string. The string should /// be in the format 'HTTP/X YYY...' or 'HTTP/X.X YYY...' where YYY is the response /// code. /// </summary> /// /// <returns>The response code.</returns> /// /// <param name="httpStatus">The HTTP status string in the format 'HTTP/X YYY...' /// or 'HTTP/X.X YYY...'.</param> private int ParseHttpStatus(string httpStatus) { ReleaseAssert.IsTrue(httpStatus != null, "The HTTP status string must not be null when parsing a response code."); var regex = new Regex("[a-zA-Z]*\\/\\d+(\\.\\d)?\\s(?<httpResponseCode>\\d+)\\s"); var match = regex.Match(httpStatus); ReleaseAssert.IsTrue(match.Groups.Count == 3, "There must be exactly 3 match groups when using a regex on a HTTP status."); var responseCodeString = match.Groups ["httpResponseCode"].Value; ReleaseAssert.IsTrue(responseCodeString != null, "The response code string cannot be null when using a regex on a HTTP status."); return(Int32.Parse(responseCodeString)); }
/// <summary> /// <para>The coroutine for processing the HTTP request. This will yield until the /// request has completed then get the data from the Web Request object.</para> /// </summary> /// /// <returns>The coroutine enumerator.</returns> /// /// <param name="url">The URL that the request is targetting.</param> /// <param name="headers">The headers for the HTTP request.</param> /// <param name="body">The body of the request. If null, a GET request will be sent.</param> /// <param name="callback">The callback providing the response from the server.</param> private IEnumerator ProcessRequest(String url, IDictionary <string, string> headers, byte[] body, Action <HttpResponse> callback) { ReleaseAssert.IsTrue(callback != null, "The callback must not be null when sending a request."); int responseCode = 500; float delayInSeconds = 0.0f; float delayIncrementInSeconds = 1.0f; float delayMultiplier = 2.0f; int retryAttempt = 1; for (int retries = 4; retries != 0; --retries) { if (retries < 4) { headers.Remove("X-Chilli-Retry"); headers.Add("X-Chilli-Retry", retryAttempt.ToString()); retryAttempt++; } yield return(new WaitForSecondsRealtime(delayInSeconds)); UnityWebRequest webRequest = CreateUnityWebRequest(url, headers, body); ReleaseAssert.IsTrue(webRequest != null, "The webRequest must not be null when sending a request."); m_logging.LogVerboseMessage(string.Format("Sending request after delay {1}. Retries remaining {0}.", retries, delayInSeconds)); yield return(webRequest.Send()); responseCode = (int)webRequest.responseCode; if (ERROR_CODES.Contains(responseCode) == false || retries == 1) { m_logging.LogVerboseMessage(string.Format("Request sent with response code {0}", responseCode)); ProcessSentRequest(webRequest, responseCode, callback); break; } if (delayInSeconds == 0.0f) { delayInSeconds += delayIncrementInSeconds; } else { delayInSeconds += delayMultiplier; } } }
/// <summary> /// Makes a HTTP POST request with the given request object. This is /// performed asynchronously, with the callback block run on a background /// thread. /// </summary> /// /// <param name="request">The POST HTTP request.</param> /// <param name="callback">The callback which will provide the response from the server. /// The callback will be made on a background thread.</param> public void SendRequest(HttpPostRequest request, Action <HttpPostRequest, HttpResponse> callback) { ReleaseAssert.IsTrue(request != null, "The HTTP POST request must not be null when sending a request."); ReleaseAssert.IsTrue(callback != null, "The callback must not be null when sending a request."); var headers = new Dictionary <string, string>(request.Headers); if (request.ContentType != null) { headers.Add("Content-Type", request.ContentType); } SendRequest(request.Url, headers, request.Body, (HttpResponse response) => { callback(request, response); }); }
/// <summary> /// Serialised a list to a Json compatible IList. A callback is provided for /// serialising each element in the input list. /// </summary> /// /// <param name="list">The list to be serialised.</param> /// <param name="elementCallback">The callback for serialising the elements in the list.</param> /// /// <returns>The serialised list.</returns> public static IList <object> Serialise <TType>(IList <TType> list, Func <TType, object> elementCallback) { var output = new List <object>(); foreach (var obj in list) { ReleaseAssert.IsNotNull(obj, "List elements should not be null."); var serialisedObj = elementCallback(obj); ReleaseAssert.IsTrue(serialisedObj is bool || serialisedObj is int || serialisedObj is long || serialisedObj is float || serialisedObj is string || serialisedObj is IList <object> || serialisedObj is IDictionary <string, object>, "Serialised element must be a valid Json type."); output.Add(serialisedObj); } return(output); }
/// <summary> /// Provides the means to send both GET and POST requests depending on the /// input data. /// </summary> /// /// <param name="url">The URL that the request is targetting.</param> /// <param name="headers">The headers for the HTTP request.</param> /// <param name="body">The body of the request. If null, a GET request will be sent.</param> /// <param name="callback">The callback providing the response from the server.</param> private void SendRequest(String url, IDictionary <string, string> headers, byte[] body, Action <HttpResponse> callback) { ReleaseAssert.IsTrue(url != null, "The URL must not be null when sending a request."); ReleaseAssert.IsTrue(headers != null, "The headers must not be null when sending a request."); ReleaseAssert.IsTrue(callback != null, "The callback must not be null when sending a request."); // Unity's WWW class works with the Dictionary concrete class rather than the abstract // IDictionary. Rather than cast a copy is made so we can be sure other dictionary types // will work. Dictionary <string, string> headersConcreteDict = new Dictionary <string, string>(headers); m_taskScheduler.ScheduleMainThreadTask(() => { var www = new WWW(url, body, headersConcreteDict); m_taskScheduler.StartCoroutine(ProcessRequest(www, callback)); }); }
/// <summary> /// Deserialised a list from a Json compatible IList. A callback is /// provided for deserialising each element in the list. /// </summary> /// /// <param name="list">The list to be deserialised.</param> /// <param name="elementCallback">The callback for deserialising the elements in the list.</param> /// /// <returns>The deserialised list.</returns> public static ReadOnlyCollection <TType> DeserialiseList <TType>(IList <object> list, Func <object, TType> elementCallback) { var output = new List <TType>(); foreach (var obj in list) { ReleaseAssert.IsNotNull(obj, "List elements should not be null."); ReleaseAssert.IsTrue(obj is bool || obj is int || obj is long || obj is float || obj is string || obj is IList <object> || obj is IDictionary <string, object>, "Serialised element must be a valid Json type."); var deserialisedObj = elementCallback(obj); ReleaseAssert.IsNotNull(deserialisedObj, "Deserialised list elements should not be null."); output.Add(deserialisedObj); } return(new ReadOnlyCollection <TType>(output)); }
/// <summary> /// Initialises the new instance of server response from the given HTTP /// response. /// </summary> /// /// <param name="httpResponse">The HTTP response.</param> public ServerResponse(HttpResponse httpResponse) { Result = httpResponse.Result; HttpResponseCode = httpResponse.HttpResponseCode; if (httpResponse.Body.Length > 0) { var bodyString = Encoding.UTF8.GetString(httpResponse.Body); var bodyDictionary = Json.Deserialize(bodyString) as Dictionary <string, object>; ReleaseAssert.IsTrue(bodyDictionary != null, "Invalid server response JSON."); Body = new ReadOnlyDictionary <string, object>(bodyDictionary); } else { Body = new ReadOnlyDictionary <string, object>(); } }
/// <summary> /// Serialised a map to a Json compatible IDictionary. A callback is provided for /// serialising each element in the input map. /// </summary> /// /// <param name="map">The map to be serialised.</param> /// <param name="elementCallback">The callback for serialising the elements in the map.</param> /// /// <returns>The serialised map.</returns> public static IDictionary <string, object> Serialise <TType>(IDictionary <string, TType> map, Func <TType, object> elementCallback) { var output = new Dictionary <string, object>(); foreach (var entry in map) { ReleaseAssert.IsNotNull(entry.Key, "Map keys should not be null."); ReleaseAssert.IsNotNull(entry.Value, "Map elements should not be null."); var serialisedObj = elementCallback(entry.Value); ReleaseAssert.IsNotNull(serialisedObj, "Serialised list elements should not be null."); ReleaseAssert.IsTrue(serialisedObj is bool || serialisedObj is int || serialisedObj is long || serialisedObj is float || serialisedObj is string || serialisedObj is IList <object> || serialisedObj is IDictionary <string, object>, "Serialised element must be a valid Json type."); output.Add(entry.Key, serialisedObj); } return(output); }
/// <summary> /// Parses the HTTP response code from the given HTTP error string. The string should /// be in the format 'XXX ...' where XXX is the HTTP response code. /// </summary> /// /// <returns>The response code parsed from the error, or 0 if there wasn't one.</returns> /// /// <param name="httpError">The HTTP error string.</param> private int ParseHttpError(string httpError) { ReleaseAssert.IsTrue(httpError != null, "The HTTP error string must not be null when parsing a response code."); var regex = new Regex("(?<httpResponseCode>[0-9][0-9][0-9])\\s"); if (regex.IsMatch(httpError)) { var match = regex.Match(httpError); ReleaseAssert.IsTrue(match.Groups.Count == 2, "There must be exactly 2 match groups when using a regex on a HTTP error."); var responseCodeString = match.Groups ["httpResponseCode"].Value; ReleaseAssert.IsTrue(responseCodeString != null, "The response code string cannot be null when using a regex on a HTTP error."); return(Int32.Parse(responseCodeString)); } return(0); }
/// <summary> /// Performs a server request with the given request parameters. This is /// performed asynchronously. /// </summary> /// /// <param name="request">The request that should be performed, which must adhere /// to the immediate request protocol.</param> /// <param name="callback">The callback containing the response. The callback /// will be on a background thread.</param> public void SendImmediateRequest(IImmediateServerRequest request, Action <IImmediateServerRequest, ServerResponse> callback) { m_taskScheduler.ScheduleBackgroundTask(() => { // if (request.HttpRequestMethod == HttpRequestMethod.Post) // { var bodyString = MiniJSON.Json.Serialize(request.SerialiseBody()); ReleaseAssert.IsTrue(bodyString != null, "Invalid body."); var bodyData = Encoding.UTF8.GetBytes(bodyString); var httpRequestDesc = new HttpPostRequestDesc(request.Url, bodyData); httpRequestDesc.Headers = request.SerialiseHeaders(); httpRequestDesc.ContentType = "application/json"; var httpRequest = new HttpPostRequest(httpRequestDesc); m_httpSystem.SendRequest(httpRequest, (HttpPostRequest receivedHttpRequest, HttpResponse httpResponse) => { ReleaseAssert.IsTrue(httpRequest == receivedHttpRequest, "Received response for wrong request."); var serverResponse = new ServerResponse(httpResponse); callback(request, serverResponse); }); // } // else // { // var httpRequestDesc = new HttpGetRequestDesc(request.Url); // httpRequestDesc.Headers = request.SerialiseHeaders(); // // var httpRequest = new HttpGetRequest(httpRequestDesc); // m_httpSystem.SendRequest(httpRequest, (HttpGetRequest receivedHttpRequest, HttpResponse httpResponse) => // { // ReleaseAssert.IsTrue(httpRequest == receivedHttpRequest, "Received response for wrong request."); // // var serverResponse = new ServerResponse(httpResponse); // callback(request, serverResponse); // }); // } }); }
/// <summary> /// Deserialised a map from a Json compatible IDictionary. A callback is provided /// for deserialising each element in the IDictionary. /// </summary> /// /// <param name="map">The map to be deserialised.</param> /// <param name="elementCallback">The callback for deserialising the elements in the map.</param> /// /// <returns>The deserialised map.</returns> public static ReadOnlyDictionary <string, TType> DeserialiseMap <TType>(IDictionary <string, object> map, Func <object, TType> elementCallback) { var output = new Dictionary <string, TType>(); foreach (var entry in map) { if (entry.Value == null) { output.Add(entry.Key, default(TType)); } else { ReleaseAssert.IsTrue(entry.Value is bool || entry.Value is int || entry.Value is long || entry.Value is float || entry.Value is string || entry.Value == null || entry.Value is IList <object> || entry.Value is IDictionary <string, object>, "Serialised element must be a valid Json type."); var deserialisedObj = elementCallback(entry.Value); ReleaseAssert.IsNotNull(deserialisedObj, "Deserialised list elements should not be null."); output.Add(entry.Key, deserialisedObj); } } return(new ReadOnlyDictionary <string, TType>(output)); }
/// <summary> /// Initializes a new instance of the HTTP system with the given task /// scheduler. /// </summary> /// /// <param name="taskScheduler">The task scheduler.</param> public HttpSystem(TaskScheduler taskScheduler) { ReleaseAssert.IsTrue(taskScheduler != null, "The task scheduler in a HTTP request system must not be null."); m_taskScheduler = taskScheduler; }
/// <summary> /// <para>The coroutine for processing the HTTP request. This will yield until the /// request has completed then parse the information required by a HTTP response /// from the WWW object.</para> /// </summary> /// /// <returns>The coroutine enumerator.</returns> /// /// <param name="www">The WWW object.</param> /// <param name="callback">The callback providing the response from the server.</param> private IEnumerator ProcessRequest(WWW www, Action <HttpResponse> callback) { ReleaseAssert.IsTrue(www != null, "The WWW must not be null when sending a request."); ReleaseAssert.IsTrue(callback != null, "The callback must not be null when sending a request."); yield return(www); HttpResponseDesc desc = null; if (string.IsNullOrEmpty(www.error)) { ReleaseAssert.IsTrue(www.responseHeaders != null, "A successful HTTP response must have a headers object."); desc = new HttpResponseDesc(HttpResult.Success); desc.Headers = new Dictionary <string, string>(www.responseHeaders); if (www.bytes != null) { desc.Body = www.bytes; } var httpStatus = www.responseHeaders ["STATUS"]; ReleaseAssert.IsTrue(!string.IsNullOrEmpty(httpStatus), "A successful HTTP response must have a HTTP status value in the header."); desc.HttpResponseCode = ParseHttpStatus(httpStatus); } else { var bytes = www.bytes; int httpResponseCode = 0; #if UNITY_IPHONE && !UNITY_EDITOR var text = www.text; if (!string.IsNullOrEmpty(text)) { var bodyDictionary = Json.Deserialize(text) as Dictionary <string, object>; if (bodyDictionary != null && bodyDictionary.ContainsKey("HttpCode")) { httpResponseCode = Convert.ToInt32(bodyDictionary ["HttpCode"]); bytes = Encoding.UTF8.GetBytes(text); } } #else httpResponseCode = ParseHttpError(www.error); #endif if (httpResponseCode != 0) { desc = new HttpResponseDesc(HttpResult.Success); desc.Headers = new Dictionary <string, string>(www.responseHeaders); if (www.bytes != null) { desc.Body = www.bytes; } desc.HttpResponseCode = httpResponseCode; } else { desc = new HttpResponseDesc(HttpResult.CouldNotConnect); } } HttpResponse response = new HttpResponse(desc); m_taskScheduler.ScheduleBackgroundTask(() => { callback(response); }); }
/// <summary> /// Adds a new value to the dictionary description. /// </summary> /// /// <param name="key">The key this value should be stored under.</param> /// <param name="value">The value which should be added.</param> public MultiTypeDictionaryBuilder Add(string key, MultiTypeValue value) { ReleaseAssert.IsFalse(m_dictionary.ContainsKey(key), "Dictionary already contains the key '" + key + "'"); m_dictionary.Add(key, value); return(this); }