AngleSharp by Florian Rappl

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

 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.FireEnd(); 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) { if (loader == null) return null; 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; } switch (setting) { case CorsSetting.None: if (behavior == OriginBehavior.Fail) throw new DomException(DomError.Network); return await loader.LoadAsync(request, cancel).ConfigureAwait(false); case CorsSetting.Anonymous: case CorsSetting.UseCredentials: { request.IsCredentialOmitted = (setting == CorsSetting.Anonymous); IResponse result2 = await loader.FetchAsync(request, cancel).ConfigureAwait(false); if (result2 != null && result2.StatusCode == HttpStatusCode.OK) return result2; break; } } throw new DomException(DomError.Network); } public static string GetContentType(this IResponse response) { string path = response.Address.Path; int num = path.LastIndexOf('.'); if (num >= 0) path.Substring(num); string defaultValue = MimeTypes.FromExtension(MimeTypes.Binary); return response.Headers.GetOrDefault(HeaderNames.ContentType, defaultValue); } private static Task<IResponse> CreateDefaultResponse() { TaskCompletionSource<IResponse> taskCompletionSource = new TaskCompletionSource<IResponse>(); taskCompletionSource.SetResult(null); return taskCompletionSource.Task; } } }