library.network.maui/Network/Platforms/iOS/PlatformNetworkHelper.cs

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);
}
}