/* * Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ using Amazon.Runtime.Internal.Transform; using Amazon.Runtime.Internal.Util; using Amazon.Util; using System; using System.Collections; using System.Reflection; using System.Net; using System.Threading; using UnityEngine; using Logger = Amazon.Runtime.Internal.Util.Logger; using Amazon.Util.Internal.PlatformServices; using Amazon.Util.Internal; namespace Amazon.Runtime.Internal { /// /// This class is used to dispatch web requests using the WWW API and /// to call user callbacks on the main thread. /// public class UnityMainThreadDispatcher : MonoBehaviour { private Logger _logger; private float _nextUpdateTime; private float _updateInterval = 0.1f; private NetworkStatus _currentNetworkStatus; /// /// This method is called called when the script instance is /// being loaded. /// public void Awake() { _logger = Logger.GetLogger(this.GetType()); // Call the method to process requests at a regular interval. _nextUpdateTime = Time.unscaledTime; _nextUpdateTime += _updateInterval; } /// /// This method is called as often as possible. /// void Update() { if (Time.unscaledTime >= _nextUpdateTime) { ProcessRequests(); _nextUpdateTime += _updateInterval; } } /// /// This method processes queued web requests and user callbacks. /// void ProcessRequests() { // Make a network call for queued requests on the main thread var request = UnityRequestQueue.Instance.DequeueRequest(); if (request != null) { StartCoroutine(InvokeRequest(request)); } // Invoke queued callbacks on the main thread var asyncResult = UnityRequestQueue.Instance.DequeueCallback(); if (asyncResult != null && asyncResult.Action != null) { try { asyncResult.Action(asyncResult.Request, asyncResult.Response, asyncResult.Exception, asyncResult.AsyncOptions); } catch (Exception exception) { // Catch any unhandled exceptions from the user callback // and log it. _logger.Error(exception, "An unhandled exception was thrown from the callback method {0}.", asyncResult.Request.ToString()); } } //Invoke queued main thread executions var mainThreadCallback = UnityRequestQueue.Instance.DequeueMainThreadOperation(); if (mainThreadCallback != null) { try { mainThreadCallback(); } catch (Exception exception) { // Catch any unhandled exceptions from the user callback // and log it. _logger.Error(exception, "An unhandled exception was thrown from the callback method"); } } //trigger network updates if status has changed var nr = ServiceFactory.Instance.GetService() as Amazon.Util.Internal.PlatformServices.NetworkReachability; if (_currentNetworkStatus != nr.NetworkStatus) { _currentNetworkStatus = nr.NetworkStatus; nr.OnNetworkReachabilityChanged(_currentNetworkStatus); } } /// /// Makes a single web request using the WWW or UnityWebRequest API. /// /// /// IEnumerator which indicated if the operation is pending. IEnumerator InvokeRequest(IUnityHttpRequest request) { // Fire the request var nr = ServiceFactory.Instance.GetService() as Amazon.Util.Internal.PlatformServices.NetworkReachability; if (nr.NetworkStatus != NetworkStatus.NotReachable) { bool isWwwRequest = (request as UnityWwwRequest) != null; if (isWwwRequest) { var wwwRequest = new WWW((request as UnityWwwRequest).RequestUri.AbsoluteUri, request.RequestContent, request.Headers); if (wwwRequest == null) { yield return null; } bool uploadCompleted = false; while (!wwwRequest.isDone) { var uploadProgress = wwwRequest.uploadProgress; if (!uploadCompleted) { request.OnUploadProgressChanged(uploadProgress); if (uploadProgress == 1) uploadCompleted = true; } yield return null; } request.WwwRequest = wwwRequest; request.Response = new UnityWebResponseData(wwwRequest); } else { var unityRequest = request as UnityRequest; if (unityRequest == null) { yield return null; } var unityWebRequest = new UnityWebRequestWrapper( unityRequest.RequestUri.AbsoluteUri, unityRequest.Method); unityWebRequest.DownloadHandler = new DownloadHandlerBufferWrapper(); if (request.RequestContent != null && request.RequestContent.Length > 0) unityWebRequest.UploadHandler = new UploadHandlerRawWrapper(request.RequestContent); bool uploadCompleted = false; foreach (var header in request.Headers) { unityWebRequest.SetRequestHeader(header.Key, header.Value); } var operation = unityWebRequest.Send(); while (!operation.isDone) { var uploadProgress = operation.progress; if (!uploadCompleted) { request.OnUploadProgressChanged(uploadProgress); if (uploadProgress == 1) uploadCompleted = true; } yield return null; } request.WwwRequest = unityWebRequest; request.Response = new UnityWebResponseData(unityWebRequest); } } else { request.Exception = new WebException("Network Unavailable", WebExceptionStatus.ConnectFailure); } if (request.IsSync) { // For synchronous calls, signal the wait handle // so that the calling thread which waits on the wait handle // is unblocked. if (request.Response != null && !request.Response.IsSuccessStatusCode) { request.Exception = new HttpErrorResponseException(request.Response); } request.WaitHandle.Set(); } else { if (request.Response != null && !request.Response.IsSuccessStatusCode) { request.Exception = new HttpErrorResponseException(request.Response); } // For asychronous calls invoke the callback method with the // state object that was originally passed in. // Invoke the callback method for the request on the thread pool // after the web request is executed. This callback triggers the // post processing of the response from the server. ThreadPool.QueueUserWorkItem((state) => { try { request.Callback(request.AsyncResult); } catch (Exception exception) { // The callback method (HttpHandler.GetResponseCallback) and // subsequent calls to handler callbacks capture any exceptions // thrown from the runtime pipeline during post processing. // Log the exception, in case we get an unhandled exception // from the callback. _logger.Error(exception, "An exception was thrown from the callback method executed from" + "UnityMainThreadDispatcher.InvokeRequest method."); } }); } } } }