RequesterExtensions
Useful extensions for IRequester objects.
using AngleSharp.Dom;
using AngleSharp.Network;
using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp.Extensions
{
[DebuggerStepThrough]
internal static class RequesterExtensions
{
public static IDownload FetchWithCors(this IResourceLoader loader, ResourceRequest request, CorsSetting setting, OriginBehavior behavior)
{
Url target = request.Target;
if (!(request.Origin == target.Origin) && !(target.Scheme == ProtocolNames.Data) && !(target.Href == "about:blank")) {
switch (setting) {
case CorsSetting.Anonymous:
case CorsSetting.UseCredentials:
return loader.FetchWithCors(request, setting);
case CorsSetting.None:
return loader.FetchWithoutCors(request, behavior);
default:
throw new DomException(DomError.Network);
}
}
return loader.FetchWithCors(target, request, setting, behavior);
}
private static IDownload FetchWithCors(this IResourceLoader loader, Url url, ResourceRequest request, CorsSetting setting, OriginBehavior behavior)
{
IDownload download = loader.DownloadAsync(new ResourceRequest(request.Source, url) {
Origin = request.Origin,
IsManualRedirectDesired = true
});
return download.Wrap(delegate(IResponse response) {
if (response.IsRedirected()) {
url.Href = response.Headers.GetOrDefault(HeaderNames.Location, url.Href);
if (request.Origin.Is(url.Origin))
return loader.FetchWithCors(new ResourceRequest(request.Source, url) {
IsCookieBlocked = request.IsCookieBlocked,
IsSameOriginForced = request.IsSameOriginForced,
Origin = request.Origin
}, setting, behavior);
return loader.FetchWithCors(url, request, setting, behavior);
}
return download;
});
}
private static IDownload FetchWithoutCors(this IResourceLoader loader, ResourceRequest request, OriginBehavior behavior)
{
if (behavior == OriginBehavior.Fail)
throw new DomException(DomError.Network);
return loader.DownloadAsync(request);
}
private static IDownload FetchWithCors(this IResourceLoader loader, ResourceRequest request, CorsSetting setting)
{
request.IsCredentialOmitted = (setting == CorsSetting.Anonymous);
IDownload download = loader.DownloadAsync(request);
return download.Wrap(delegate(IResponse response) {
if (response != null && response.StatusCode == HttpStatusCode.OK)
return download;
response?.Dispose();
throw new DomException(DomError.Network);
});
}
private static IDownload Wrap(this IDownload download, Func<IResponse, IDownload> callback)
{
CancellationTokenSource cts = new CancellationTokenSource();
return new Download(download.Task.Wrap(callback), cts, download.Target, download.Originator);
}
private static async Task<IResponse> Wrap(this Task<IResponse> task, Func<IResponse, IDownload> callback)
{
return await callback(await task.ConfigureAwait(false)).Task.ConfigureAwait(false);
}
private static bool IsRedirected(this IResponse response)
{
HttpStatusCode httpStatusCode = response?.StatusCode ?? HttpStatusCode.NotFound;
switch (httpStatusCode) {
default:
return httpStatusCode == HttpStatusCode.MultipleChoices;
case HttpStatusCode.MovedPermanently:
case HttpStatusCode.Found:
case HttpStatusCode.SeeOther:
case HttpStatusCode.TemporaryRedirect:
return true;
}
}
}
}