AngleSharp by Florian Rappl

<PackageReference Include="AngleSharp" Version="0.8.4" />

 RequesterExtensions

static class RequesterExtensions
Useful extensions for IRequester objects.
using AngleSharp.Dom; using AngleSharp.Events; using AngleSharp.Network; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading; using System.Threading.Tasks; namespace AngleSharp.Extensions { [DebuggerStepThrough] internal static class RequesterExtensions { private static readonly Task<IResponse> DefaultResponse = CreateDefaultResponse(); public static async Task<IResponse> LoadAsync(this IEnumerable<IRequester> requesters, IRequest request, IEventAggregator events, CancellationToken cancel) { foreach (IRequester requester in requesters) { if (requester.SupportsProtocol(request.Address.Scheme)) { RequestStartEvent evt = new RequestStartEvent(requester, request); events?.Publish(evt); IResponse result = await requester.RequestAsync(request, cancel).ConfigureAwait(false); evt.SetResult(result); return result; } } return null; } public static Task<IResponse> SendAsync(this IDocumentLoader loader, DocumentRequest request, CancellationToken cancel) { if (loader == null) return DefaultResponse; return loader.LoadAsync(request, cancel); } public static Task<IResponse> FetchAsync(this IResourceLoader loader, ResourceRequest request, CancellationToken cancel) { if (loader == null) return DefaultResponse; return loader.LoadAsync(request, cancel); } public static async Task<IResponse> FetchWithCorsAsync(this IResourceLoader loader, ResourceRequest request, CorsSetting setting, OriginBehavior behavior, CancellationToken cancel) { Url url = request.Target; if (request.Origin == url.Origin || url.Scheme == KnownProtocols.Data || url.Href == "about:blank") { IResponse result; while (true) { ResourceRequest data = new ResourceRequest(request.Source, url) { Origin = request.Origin, IsManualRedirectDesired = true }; result = await loader.LoadAsync(data, cancel).ConfigureAwait(false); if (result.StatusCode != HttpStatusCode.Found && result.StatusCode != HttpStatusCode.TemporaryRedirect && result.StatusCode != HttpStatusCode.SeeOther && result.StatusCode != HttpStatusCode.TemporaryRedirect && result.StatusCode != HttpStatusCode.MovedPermanently && result.StatusCode != HttpStatusCode.MultipleChoices) break; url = new Url(result.Headers.GetOrDefault(HeaderNames.Location, url.Href)); if (request.Origin == url.Origin) { request = new ResourceRequest(request.Source, url) { IsCookieBlocked = request.IsCookieBlocked, IsSameOriginForced = request.IsSameOriginForced, Origin = request.Origin }; return await loader.FetchWithCorsAsync(request, setting, behavior, cancel).ConfigureAwait(false); } } return result; } if (setting == CorsSetting.None && behavior == OriginBehavior.Taint) await loader.LoadAsync(request, cancel).ConfigureAwait(false); if (setting == CorsSetting.Anonymous) request.IsCredentialOmitted = true; if (setting == CorsSetting.Anonymous || setting == CorsSetting.UseCredentials) { IResponse result2 = await loader.FetchAsync(request, cancel).ConfigureAwait(false); if (result2.StatusCode == HttpStatusCode.OK) return result2; } throw new DomException(DomError.Network); } private static Task<IResponse> CreateDefaultResponse() { TaskCompletionSource<IResponse> taskCompletionSource = new TaskCompletionSource<IResponse>(); taskCompletionSource.SetResult(null); return taskCompletionSource.Task; } } }