2017-02-07 12:59:30 +08:00
|
|
|
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
|
|
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using osu.Framework.IO.Network;
|
|
|
|
|
|
|
|
|
|
namespace osu.Game.Online.API
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// An API request with a well-defined response type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
2017-08-24 19:14:17 +08:00
|
|
|
|
public abstract class APIRequest<T> : APIRequest
|
2016-08-31 18:49:34 +08:00
|
|
|
|
{
|
|
|
|
|
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
|
|
|
|
|
2017-08-24 19:14:17 +08:00
|
|
|
|
protected APIRequest()
|
2016-08-31 18:49:34 +08:00
|
|
|
|
{
|
|
|
|
|
base.Success += onSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onSuccess()
|
|
|
|
|
{
|
2017-03-07 09:59:19 +08:00
|
|
|
|
Success?.Invoke(((JsonWebRequest<T>)WebRequest).ResponseObject);
|
2016-08-31 18:49:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public new event APISuccessHandler<T> Success;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-24 19:14:17 +08:00
|
|
|
|
public abstract class APIDownloadRequest : APIRequest
|
|
|
|
|
{
|
|
|
|
|
protected override WebRequest CreateWebRequest()
|
|
|
|
|
{
|
|
|
|
|
var request = new WebRequest(Uri);
|
|
|
|
|
request.DownloadProgress += request_Progress;
|
|
|
|
|
return request;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-26 15:37:44 +08:00
|
|
|
|
private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
|
2017-08-24 19:14:17 +08:00
|
|
|
|
|
|
|
|
|
protected APIDownloadRequest()
|
|
|
|
|
{
|
|
|
|
|
base.Success += onSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onSuccess()
|
|
|
|
|
{
|
|
|
|
|
Success?.Invoke(WebRequest.ResponseData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public event APIProgressHandler Progress;
|
|
|
|
|
|
|
|
|
|
public new event APISuccessHandler<byte[]> Success;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-31 18:49:34 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// AN API request with no specified response type.
|
|
|
|
|
/// </summary>
|
2017-08-24 19:14:17 +08:00
|
|
|
|
public abstract class APIRequest
|
2016-08-31 18:49:34 +08:00
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The maximum amount of time before this request will fail.
|
|
|
|
|
/// </summary>
|
2016-09-27 17:31:36 +08:00
|
|
|
|
public int Timeout = WebRequest.DEFAULT_TIMEOUT;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
|
|
|
|
protected virtual string Target => string.Empty;
|
|
|
|
|
|
|
|
|
|
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
|
|
|
|
|
2017-08-24 19:14:17 +08:00
|
|
|
|
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
2017-09-24 03:23:31 +08:00
|
|
|
|
private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds);
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
2016-09-27 17:31:36 +08:00
|
|
|
|
public bool ExceededTimeout => remainingTime == 0;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
2017-09-24 03:23:31 +08:00
|
|
|
|
private DateTimeOffset? startTime;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
2017-08-24 19:14:17 +08:00
|
|
|
|
protected APIAccess API;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
protected WebRequest WebRequest;
|
|
|
|
|
|
|
|
|
|
public event APISuccessHandler Success;
|
|
|
|
|
public event APIFailureHandler Failure;
|
|
|
|
|
|
2017-03-15 17:07:26 +08:00
|
|
|
|
private bool cancelled;
|
|
|
|
|
|
2017-03-15 19:08:12 +08:00
|
|
|
|
private Action pendingFailure;
|
|
|
|
|
|
2016-09-27 17:31:36 +08:00
|
|
|
|
public void Perform(APIAccess api)
|
2016-08-31 18:49:34 +08:00
|
|
|
|
{
|
2017-08-24 19:14:17 +08:00
|
|
|
|
API = api;
|
2017-03-15 19:08:12 +08:00
|
|
|
|
|
|
|
|
|
if (checkAndProcessFailure())
|
|
|
|
|
return;
|
2017-03-15 17:07:26 +08:00
|
|
|
|
|
2016-08-31 18:49:34 +08:00
|
|
|
|
if (startTime == null)
|
2017-09-24 03:23:31 +08:00
|
|
|
|
startTime = DateTimeOffset.UtcNow;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
|
|
|
|
if (remainingTime <= 0)
|
|
|
|
|
throw new TimeoutException(@"API request timeout hit");
|
|
|
|
|
|
|
|
|
|
WebRequest = CreateWebRequest();
|
2017-10-30 16:28:53 +08:00
|
|
|
|
WebRequest.AllowRetryOnTimeout = false;
|
2017-10-30 20:23:47 +08:00
|
|
|
|
WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}");
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
2017-03-15 19:08:12 +08:00
|
|
|
|
if (checkAndProcessFailure())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!WebRequest.Aborted) //could have been aborted by a Cancel() call
|
2017-10-23 13:46:17 +08:00
|
|
|
|
WebRequest.Perform();
|
2017-03-15 19:08:12 +08:00
|
|
|
|
|
|
|
|
|
if (checkAndProcessFailure())
|
|
|
|
|
return;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
|
2017-03-15 19:08:12 +08:00
|
|
|
|
api.Scheduler.Add(delegate { Success?.Invoke(); });
|
2016-08-31 18:49:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-15 17:07:26 +08:00
|
|
|
|
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));
|
|
|
|
|
|
2016-09-27 17:31:36 +08:00
|
|
|
|
public void Fail(Exception e)
|
2016-08-31 18:49:34 +08:00
|
|
|
|
{
|
2017-03-15 17:07:26 +08:00
|
|
|
|
cancelled = true;
|
|
|
|
|
|
2016-08-31 18:49:34 +08:00
|
|
|
|
WebRequest?.Abort();
|
2017-03-15 19:08:12 +08:00
|
|
|
|
|
|
|
|
|
pendingFailure = () => Failure?.Invoke(e);
|
|
|
|
|
checkAndProcessFailure();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checked for cancellation or error. Also queues up the Failed event if we can.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Whether we are in a failed or cancelled state.</returns>
|
|
|
|
|
private bool checkAndProcessFailure()
|
|
|
|
|
{
|
2017-08-24 19:14:17 +08:00
|
|
|
|
if (API == null || pendingFailure == null) return cancelled;
|
2017-03-15 19:08:12 +08:00
|
|
|
|
|
2017-08-24 19:14:17 +08:00
|
|
|
|
API.Scheduler.Add(pendingFailure);
|
2017-03-15 19:08:12 +08:00
|
|
|
|
pendingFailure = null;
|
|
|
|
|
return true;
|
2016-08-31 18:49:34 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public delegate void APIFailureHandler(Exception e);
|
|
|
|
|
public delegate void APISuccessHandler();
|
2017-08-24 19:14:17 +08:00
|
|
|
|
public delegate void APIProgressHandler(long current, long total);
|
2017-03-09 13:24:16 +08:00
|
|
|
|
public delegate void APISuccessHandler<in T>(T content);
|
2016-08-31 18:49:34 +08:00
|
|
|
|
}
|