diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 57dd4f1568..cfbcf0326a 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -102,7 +102,7 @@ namespace osu.Game.Online.API if (queue.Count == 0) { log.Add(@"Queueing a ping request"); - Queue(new ListChannelsRequest { Timeout = 5000 }); + Queue(new GetUserRequest()); } break; @@ -141,7 +141,7 @@ namespace osu.Game.Online.API State = APIState.Online; }; - if (!handleRequest(userReq, out _)) + if (!handleRequest(userReq)) { Thread.Sleep(500); continue; @@ -170,15 +170,10 @@ namespace osu.Game.Online.API lock (queue) { if (queue.Count == 0) break; - req = queue.Peek(); + req = queue.Dequeue(); } - // TODO: handle failures better - handleRequest(req, out var removeFromQueue); - - if (removeFromQueue) - lock (queue) - queue.Dequeue(); + handleRequest(req); } Thread.Sleep(50); @@ -195,46 +190,29 @@ namespace osu.Game.Online.API /// /// Handle a single API request. + /// Ensures all exceptions are caught and dealt with correctly. /// /// The request. /// true if the request succeeded. - private bool handleRequest(APIRequest req, out bool removeFromQueue) + private bool handleRequest(APIRequest req) { - removeFromQueue = true; - try { - Logger.Log($@"Performing request {req}", LoggingTarget.Network); - req.Failure += ex => - { - if (ex is WebException we) - handleWebException(we); - }; - req.Perform(this); //we could still be in initialisation, at which point we don't want to say we're Online yet. - if (IsLoggedIn) - State = APIState.Online; + if (IsLoggedIn) State = APIState.Online; failureCount = 0; return true; } catch (WebException we) { - removeFromQueue = handleWebException(we); - - if (removeFromQueue) - req.Fail(we); - + handleWebException(we); return false; } catch (Exception e) { - if (e is TimeoutException) - log.Add(@"API level timeout exception was hit"); - - req.Fail(e); return false; } } @@ -290,8 +268,12 @@ namespace osu.Game.Online.API //we might try again at an api level. return false; - State = APIState.Failing; - flushQueue(); + if (State == APIState.Online) + { + State = APIState.Failing; + flushQueue(); + } + return true; } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index adbedb2aac..41f774e83c 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.IO.Network; +using osu.Framework.Logging; namespace osu.Game.Online.API { @@ -35,23 +36,12 @@ namespace osu.Game.Online.API /// public abstract class APIRequest { - /// - /// The maximum amount of time before this request will fail. - /// - public int Timeout = WebRequest.DEFAULT_TIMEOUT; - protected virtual string Target => string.Empty; protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri); protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}"; - private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds); - - public bool ExceededTimeout => remainingTime == 0; - - private DateTimeOffset? startTime; - protected APIAccess API; protected WebRequest WebRequest; @@ -75,27 +65,24 @@ namespace osu.Game.Online.API { API = api; - if (checkAndProcessFailure()) + if (checkAndScheduleFailure()) return; - if (startTime == null) - startTime = DateTimeOffset.UtcNow; - - if (remainingTime <= 0) - throw new TimeoutException(@"API request timeout hit"); - WebRequest = CreateWebRequest(); WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); - if (checkAndProcessFailure()) + if (checkAndScheduleFailure()) return; if (!WebRequest.Aborted) //could have been aborted by a Cancel() call + { + Logger.Log($@"Performing request {this}", LoggingTarget.Network); WebRequest.Perform(); + } - if (checkAndProcessFailure()) + if (checkAndScheduleFailure()) return; api.Schedule(delegate { Success?.Invoke(); }); @@ -105,19 +92,21 @@ namespace osu.Game.Online.API public void Fail(Exception e) { - cancelled = true; + if (cancelled) return; + cancelled = true; WebRequest?.Abort(); + Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); pendingFailure = () => Failure?.Invoke(e); - checkAndProcessFailure(); + checkAndScheduleFailure(); } /// /// Checked for cancellation or error. Also queues up the Failed event if we can. /// /// Whether we are in a failed or cancelled state. - private bool checkAndProcessFailure() + private bool checkAndScheduleFailure() { if (API == null || pendingFailure == null) return cancelled; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 305c9035b5..8ae644c06a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - +