/// <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() { RequestEventHelper.ProcessQueue(); ConnectionEventHelper.ProcessQueue(); ProtocolEventHelper.ProcessQueue(); PluginEventHelper.ProcessQueue(); BestHTTP.Extensions.Timer.Process(); if (heartbeats != null) { heartbeats.Update(); } BufferPool.Maintain(); }
/// <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(); } }
public static void OnUpdate() { lock (Locker) { IsCallingCallbacks = true; try { for (int i = 0; i < ActiveConnections.Count; i++) { ConnectionBase connectionBase = ActiveConnections[i]; switch (connectionBase.State) { case HTTPConnectionStates.Processing: connectionBase.HandleProgressCallback(); if (connectionBase.CurrentRequest.UseStreaming && connectionBase.CurrentRequest.Response != null && connectionBase.CurrentRequest.Response.HasStreamedFragments()) { connectionBase.HandleCallback(); } if (((!connectionBase.CurrentRequest.UseStreaming && connectionBase.CurrentRequest.UploadStream == null) || connectionBase.CurrentRequest.EnableTimoutForStreaming) && DateTime.UtcNow - connectionBase.StartTime > connectionBase.CurrentRequest.Timeout) { connectionBase.Abort(HTTPConnectionStates.TimedOut); } break; case HTTPConnectionStates.TimedOut: if (DateTime.UtcNow - connectionBase.TimedOutStart > TimeSpan.FromMilliseconds(500.0)) { Logger.Information("HTTPManager", "Hard aborting connection becouse of a long waiting TimedOut state"); connectionBase.CurrentRequest.Response = null; connectionBase.CurrentRequest.State = HTTPRequestStates.TimedOut; connectionBase.HandleCallback(); RecycleConnection(connectionBase); } break; case HTTPConnectionStates.Redirected: SendRequest(connectionBase.CurrentRequest); RecycleConnection(connectionBase); break; case HTTPConnectionStates.WaitForRecycle: connectionBase.CurrentRequest.FinishStreaming(); connectionBase.HandleCallback(); RecycleConnection(connectionBase); break; case HTTPConnectionStates.Upgraded: connectionBase.HandleCallback(); break; case HTTPConnectionStates.WaitForProtocolShutdown: { IProtocol protocol = connectionBase.CurrentRequest.Response as IProtocol; protocol?.HandleEvents(); if (protocol == null || protocol.IsClosed) { connectionBase.HandleCallback(); connectionBase.Dispose(); RecycleConnection(connectionBase); } break; } case HTTPConnectionStates.AbortRequested: { IProtocol protocol = connectionBase.CurrentRequest.Response as IProtocol; if (protocol != null) { protocol.HandleEvents(); if (protocol.IsClosed) { connectionBase.HandleCallback(); connectionBase.Dispose(); RecycleConnection(connectionBase); } } break; } case HTTPConnectionStates.Closed: connectionBase.CurrentRequest.FinishStreaming(); connectionBase.HandleCallback(); RecycleConnection(connectionBase); break; case HTTPConnectionStates.Free: RecycleConnection(connectionBase); break; } } } finally { IsCallingCallbacks = false; } lock (RecycledConnections) { if (RecycledConnections.Count > 0) { for (int j = 0; j < RecycledConnections.Count; j++) { ConnectionBase connectionBase2 = RecycledConnections[j]; if (connectionBase2.IsFree) { ActiveConnections.Remove(connectionBase2); FreeConnections.Add(connectionBase2); } } RecycledConnections.Clear(); } } if (FreeConnections.Count > 0) { for (int k = 0; k < FreeConnections.Count; k++) { ConnectionBase connectionBase3 = FreeConnections[k]; if (connectionBase3.IsRemovable) { List <ConnectionBase> value = null; if (Connections.TryGetValue(connectionBase3.ServerAddress, out value)) { value.Remove(connectionBase3); } connectionBase3.Dispose(); FreeConnections.RemoveAt(k); k--; } } } if (CanProcessFromQueue()) { if (RequestQueue.Find((HTTPRequest req) => req.Priority != 0) != null) { RequestQueue.Sort((HTTPRequest req1, HTTPRequest req2) => req1.Priority - req2.Priority); } HTTPRequest[] array = RequestQueue.ToArray(); RequestQueue.Clear(); for (int l = 0; l < array.Length; l++) { SendRequest(array[l]); } } } if (heartbeats != null) { heartbeats.Update(); } }