Example #1
0
        /// <summary>
        /// Update function that should be called regularly from a Unity event(Update, LateUpdate). Callbacks are dispatched from this function.
        /// </summary>
        public static void OnUpdate()
        {
            // We will try to acquire a lock. If it fails, we will skip this frame without calling any callback.
            if (System.Threading.Monitor.TryEnter(Locker))
            {
                try
                {
                    IsCallingCallbacks = true;
                    try
                    {
                        for (int i = 0; i < ActiveConnections.Count; ++i)
                        {
                            ConnectionBase conn = ActiveConnections[i];

                            switch (conn.State)
                            {
                            case HTTPConnectionStates.Processing:
                                conn.HandleProgressCallback();

                                if (conn.CurrentRequest.UseStreaming && conn.CurrentRequest.Response != null && conn.CurrentRequest.Response.HasStreamedFragments())
                                {
                                    conn.HandleCallback();
                                }

                                try
                                {
                                    if (((!conn.CurrentRequest.UseStreaming && conn.CurrentRequest.UploadStream == null) || conn.CurrentRequest.EnableTimoutForStreaming) &&
                                        DateTime.UtcNow - conn.StartTime > conn.CurrentRequest.Timeout)
                                    {
                                        conn.Abort(HTTPConnectionStates.TimedOut);
                                    }
                                }
                                catch (OverflowException)
                                {
                                    HTTPManager.Logger.Warning("HTTPManager", "TimeSpan overflow");
                                }
                                break;

                            case HTTPConnectionStates.TimedOut:
                                // The connection is still in TimedOut state, and if we waited enough time, we will dispatch the
                                //  callback and recycle the connection
                                try
                                {
                                    if (DateTime.UtcNow - conn.TimedOutStart > TimeSpan.FromMilliseconds(500))
                                    {
                                        HTTPManager.Logger.Information("HTTPManager", "Hard aborting connection because of a long waiting TimedOut state");

                                        conn.CurrentRequest.Response = null;
                                        conn.CurrentRequest.State    = HTTPRequestStates.TimedOut;
                                        conn.HandleCallback();

                                        // this will set the connection's CurrentRequest to null
                                        RecycleConnection(conn);
                                    }
                                }
                                catch (OverflowException)
                                {
                                    HTTPManager.Logger.Warning("HTTPManager", "TimeSpan overflow");
                                }
                                break;

                            case HTTPConnectionStates.Redirected:
                                // If the server redirected us, we need to find or create a connection to the new server and send out the request again.
                                SendRequest(conn.CurrentRequest);

                                RecycleConnection(conn);
                                break;

                            case HTTPConnectionStates.WaitForRecycle:
                                // If it's a streamed request, it's finished now
                                conn.CurrentRequest.FinishStreaming();

                                // Call the callback
                                conn.HandleCallback();

                                // Then recycle the connection
                                RecycleConnection(conn);
                                break;

                            case HTTPConnectionStates.Upgraded:
                                // The connection upgraded to an other protocol
                                conn.HandleCallback();
                                break;

                            case HTTPConnectionStates.WaitForProtocolShutdown:
                                var ws = conn.CurrentRequest.Response as IProtocol;
                                if (ws != null)
                                {
                                    ws.HandleEvents();
                                }

                                if (ws == null || ws.IsClosed)
                                {
                                    conn.HandleCallback();

                                    // After both sending and receiving a Close message, an endpoint considers the WebSocket connection closed and MUST close the underlying TCP connection.
                                    conn.Dispose();
                                    RecycleConnection(conn);
                                }
                                break;

                            case HTTPConnectionStates.AbortRequested:
                                // Corner case: we aborted a WebSocket connection
                            {
                                ws = conn.CurrentRequest.Response as IProtocol;
                                if (ws != null)
                                {
                                    ws.HandleEvents();

                                    if (ws.IsClosed)
                                    {
                                        conn.HandleCallback();
                                        conn.Dispose();

                                        RecycleConnection(conn);
                                    }
                                }
                            }
                            break;

                            case HTTPConnectionStates.Closed:
                                // If it's a streamed request, it's finished now
                                conn.CurrentRequest.FinishStreaming();

                                // Call the callback
                                conn.HandleCallback();

                                // It will remove from the ActiveConnections
                                RecycleConnection(conn);
                                break;

                            case HTTPConnectionStates.Free:
                                RecycleConnection(conn);
                                break;
                            }
                        }
                    }
                    finally
                    {
                        IsCallingCallbacks = false;
                    }

                    // Just try to grab the lock, if we can't have it we can wait another turn because it isn't
                    //  critical to do it right now.
                    if (System.Threading.Monitor.TryEnter(RecycledConnections))
                    {
                        try
                        {
                            if (RecycledConnections.Count > 0)
                            {
                                for (int i = 0; i < RecycledConnections.Count; ++i)
                                {
                                    var connection = RecycledConnections[i];
                                    // If in a callback made a request that acquired this connection, then we will not remove it from the
                                    //  active connections.
                                    if (connection.IsFree)
                                    {
                                        ActiveConnections.Remove(connection);
                                        FreeConnections.Add(connection);
                                    }
                                }
                                RecycledConnections.Clear();
                            }
                        }
                        finally
                        {
                            System.Threading.Monitor.Exit(RecycledConnections);
                        }
                    }

                    if (FreeConnections.Count > 0)
                    {
                        for (int i = 0; i < FreeConnections.Count; i++)
                        {
                            var connection = FreeConnections[i];

                            if (connection.IsRemovable)
                            {
                                // Remove the connection from the connection reference table
                                List <ConnectionBase> connections = null;
                                if (Connections.TryGetValue(connection.ServerAddress, out connections))
                                {
                                    connections.Remove(connection);
                                }

                                // Dispose the connection
                                connection.Dispose();

                                FreeConnections.RemoveAt(i);
                                i--;
                            }
                        }
                    }


                    if (CanProcessFromQueue())
                    {
                        // Sort the queue by priority, only if we have to
                        if (RequestQueue.Find((req) => req.Priority != 0) != null)
                        {
                            RequestQueue.Sort((req1, req2) => req1.Priority - req2.Priority);
                        }

                        // Create an array from the queue and clear it. When we call the SendRequest while still no room for new connections, the same queue will be rebuilt.

                        var queue = RequestQueue.ToArray();
                        RequestQueue.Clear();

                        for (int i = 0; i < queue.Length; ++i)
                        {
                            SendRequest(queue[i]);
                        }
                    }
                }
                finally
                {
                    System.Threading.Monitor.Exit(Locker);
                }
            }

            if (heartbeats != null)
            {
                heartbeats.Update();
            }
        }