238 lines
8.5 KiB
C#
238 lines
8.5 KiB
C#
using CoreGraphics;
|
|
using Foundation;
|
|
|
|
namespace Blahblah.Library.Network;
|
|
|
|
partial class NetworkHelper : NSObject, INSUrlSessionDataDelegate
|
|
{
|
|
static bool IsResponseSuccess(NSHttpUrlResponse response)
|
|
{
|
|
return (int)response.StatusCode is >= 200 and < 300;
|
|
}
|
|
|
|
NSUrlSession? urlSession;
|
|
|
|
readonly Dictionary<NSUrlSessionTask, NetworkTask> tasks = new();
|
|
|
|
private NetworkHelper(int timeout, bool useCookie, bool waitsConnectivity = true, Dictionary<string, string>? additionalHeaders = null)
|
|
{
|
|
var configuration = NSUrlSessionConfiguration.EphemeralSessionConfiguration;
|
|
configuration.WaitsForConnectivity = waitsConnectivity;
|
|
configuration.RequestCachePolicy = NSUrlRequestCachePolicy.UseProtocolCachePolicy;
|
|
configuration.TimeoutIntervalForRequest = timeout;
|
|
if (additionalHeaders != null)
|
|
{
|
|
configuration.HttpAdditionalHeaders = NSDictionary.FromObjectsAndKeys(
|
|
additionalHeaders.Values.Select(v => FromObject(v)).ToArray(),
|
|
additionalHeaders.Keys.Select(k => FromObject(k)).ToArray());
|
|
}
|
|
if (useCookie)
|
|
{
|
|
configuration.HttpCookieStorage = NSHttpCookieStorage.SharedStorage;
|
|
}
|
|
else
|
|
{
|
|
configuration.HttpShouldSetCookies = false;
|
|
}
|
|
if (proxyHost != null && proxyPort != null)
|
|
{
|
|
configuration.StrongConnectionProxyDictionary = new ProxyConfigurationDictionary
|
|
{
|
|
HttpEnable = true,
|
|
HttpsProxyHost = proxyHost,
|
|
HttpsProxyPort = proxyPort,
|
|
HttpProxyHost = proxyHost,
|
|
HttpProxyPort = proxyPort
|
|
};
|
|
}
|
|
urlSession = NSUrlSession.FromConfiguration(configuration, this, null);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
lock (sync)
|
|
{
|
|
foreach (var task in tasks.Values)
|
|
{
|
|
task.Dispose();
|
|
}
|
|
}
|
|
if (urlSession != null)
|
|
{
|
|
urlSession.FinishTasksAndInvalidate();
|
|
urlSession.Dispose();
|
|
urlSession = null;
|
|
}
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
public partial Task<NetworkResult<string>> GetContentAsync(string url, StringHandler? process, CancellationToken token)
|
|
{
|
|
var uri = NSUrl.FromString(url) ?? throw new ArgumentNullException(nameof(url));
|
|
var request = new NSMutableUrlRequest(uri)
|
|
{
|
|
[AcceptHeader] = Accept ?? AcceptHttp,
|
|
[ReferrerHeader] = Referrer
|
|
};
|
|
|
|
var task = (urlSession?.CreateDataTask(request)) ?? throw new NullReferenceException("Url request is null");
|
|
var taskSource = new TaskCompletionSource<NetworkResult<string>>();
|
|
var stringTask = new StringTask(url, taskSource, token)
|
|
{
|
|
Process = process
|
|
};
|
|
lock (sync)
|
|
{
|
|
tasks.Add(task, stringTask);
|
|
}
|
|
task.Resume();
|
|
return taskSource.Task;
|
|
}
|
|
|
|
public Task<NetworkResult<bool>> GetImageAsync(string url, string filePath, StepHandler<CGImage>? step = null, CancellationToken token = default)
|
|
{
|
|
var uri = NSUrl.FromString(url) ?? throw new ArgumentNullException(nameof(url));
|
|
var request = new NSMutableUrlRequest(uri)
|
|
{
|
|
[AcceptHeader] = Accept ?? AcceptImage,
|
|
[ReferrerHeader] = Referrer
|
|
};
|
|
|
|
var task = (urlSession?.CreateDataTask(request)) ?? throw new NullReferenceException("Url request is null");
|
|
var taskSource = new TaskCompletionSource<NetworkResult<bool>>();
|
|
var imageTask = new ImageTask(url, filePath, step != null, taskSource, token)
|
|
{
|
|
Step = step
|
|
};
|
|
lock (sync)
|
|
{
|
|
tasks.Add(task, imageTask);
|
|
}
|
|
task.Resume();
|
|
return taskSource.Task;
|
|
}
|
|
|
|
public Task<NetworkResult<string>> PostAsync(string url, Action<NSMutableUrlRequest>? prepare = null, CancellationToken token = default)
|
|
{
|
|
var uri = NSUrl.FromString(url) ?? throw new ArgumentNullException(nameof(url));
|
|
var request = new NSMutableUrlRequest(uri)
|
|
{
|
|
HttpMethod = "POST",
|
|
[AcceptHeader] = Accept ?? AcceptImage,
|
|
[ReferrerHeader] = Referrer
|
|
};
|
|
prepare?.Invoke(request);
|
|
|
|
var task = (urlSession?.CreateDataTask(request)) ?? throw new NullReferenceException("Url request is null");
|
|
var taskSource = new TaskCompletionSource<NetworkResult<string>>();
|
|
var stringTask = new StringTask(url, taskSource, token);
|
|
lock (sync)
|
|
{
|
|
tasks.Add(task, stringTask);
|
|
}
|
|
task.Resume();
|
|
return taskSource.Task;
|
|
}
|
|
|
|
bool TryGetTask(NSUrlSessionTask task, out NetworkTask? networkTask)
|
|
{
|
|
bool got;
|
|
lock (sync)
|
|
{
|
|
got = tasks.TryGetValue(task, out networkTask);
|
|
}
|
|
return got;
|
|
}
|
|
|
|
[Export("URLSession:dataTask:didReceiveResponse:completionHandler:")]
|
|
public void DidReceiveResponse(NSUrlSession session, NSUrlSessionDataTask dataTask, NSUrlResponse response, Action<NSUrlSessionResponseDisposition> completionHandler)
|
|
{
|
|
if (!TryGetTask(dataTask, out NetworkTask? networkTask) || networkTask == null)
|
|
{
|
|
completionHandler(NSUrlSessionResponseDisposition.Cancel);
|
|
return;
|
|
}
|
|
if (networkTask.IsCancelled)
|
|
{
|
|
completionHandler(NSUrlSessionResponseDisposition.Cancel);
|
|
networkTask.SetException(new TaskCanceledException($"Cancelled on response received: {dataTask.CurrentRequest?.Url}", null, networkTask.Token));
|
|
return;
|
|
}
|
|
if (response is NSHttpUrlResponse httpResponse)
|
|
{
|
|
if (IsResponseSuccess(httpResponse))
|
|
{
|
|
if (networkTask.OnResponsed(httpResponse))
|
|
{
|
|
completionHandler(NSUrlSessionResponseDisposition.Allow);
|
|
}
|
|
else
|
|
{
|
|
completionHandler(NSUrlSessionResponseDisposition.Cancel);
|
|
networkTask.SetException(new TaskCanceledException($"Cancelled on task responsed: {dataTask.CurrentRequest?.Url}", null, networkTask.Token));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
completionHandler(NSUrlSessionResponseDisposition.Cancel);
|
|
networkTask.SetException(new HttpResponseException((int)httpResponse.StatusCode, httpResponse.Url.AbsoluteString ?? networkTask.Url));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
completionHandler(NSUrlSessionResponseDisposition.Cancel);
|
|
networkTask.SetException(new InvalidDataException($"Response is not instance of <NSHttpUrlResponse> but <{response?.GetType()}>"));
|
|
}
|
|
}
|
|
|
|
[Export("URLSession:dataTask:didReceiveData:")]
|
|
public void DidReceiveData(NSUrlSession session, NSUrlSessionDataTask dataTask, NSData data)
|
|
{
|
|
if (!TryGetTask(dataTask, out NetworkTask? networkTask) || networkTask == null)
|
|
{
|
|
return;
|
|
}
|
|
if (networkTask.IsCancelled)
|
|
{
|
|
networkTask.SetException(new TaskCanceledException($"Cancelled on data received: {dataTask.CurrentRequest?.Url}", null, networkTask.Token));
|
|
return;
|
|
}
|
|
if (networkTask.Data == null)
|
|
{
|
|
networkTask.SetException(new InvalidDataException("<NSMutableData> is null"));
|
|
return;
|
|
}
|
|
networkTask.Data.AppendData(data);
|
|
networkTask.OnReceived((int)data.Length);
|
|
}
|
|
|
|
[Export("URLSession:task:didCompleteWithError:")]
|
|
public void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error)
|
|
{
|
|
if (!TryGetTask(task, out NetworkTask? networkTask) || networkTask == null)
|
|
{
|
|
return;
|
|
}
|
|
if (networkTask.IsCancelled)
|
|
{
|
|
networkTask.SetException(new TaskCanceledException($"Cancelled on completed: {task.CurrentRequest?.Url}", null, networkTask.Token));
|
|
return;
|
|
}
|
|
if (networkTask.Data == null)
|
|
{
|
|
networkTask.SetException(new InvalidDataException("<NSMutableData> is null"));
|
|
return;
|
|
}
|
|
if (error != null)
|
|
{
|
|
networkTask.SetException(new Exception(error.ToString()));
|
|
return;
|
|
}
|
|
networkTask.SetCompleted(task.Response as NSHttpUrlResponse);
|
|
}
|
|
}
|
|
|