BaseLoader
Represents the base class for all loaders.
using AngleSharp.Dom;
using AngleSharp.Dom.Events;
using AngleSharp.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp.Network.Default
{
public abstract class BaseLoader : ILoader
{
private readonly IBrowsingContext _context;
private readonly Predicate<IRequest> _filter;
private readonly List<IDownload> _downloads;
public int MaxRedirects { get; set; }
public BaseLoader(IBrowsingContext context, Predicate<IRequest> filter)
{
_context = context;
_filter = (filter ?? ((Predicate<IRequest>)((IRequest _) => true)));
_downloads = new List<IDownload>();
MaxRedirects = 50;
}
protected virtual void Add(IDownload download)
{
lock (this) {
_downloads.Add(download);
}
}
protected virtual void Remove(IDownload download)
{
lock (this) {
_downloads.Remove(download);
}
}
protected virtual string GetCookie(Url url)
{
return _context.Configuration.GetCookie(url.Origin);
}
protected virtual void SetCookie(Url url, string value)
{
_context.Configuration.SetCookie(url.Origin, value);
}
protected virtual IDownload DownloadAsync(Request request, INode originator)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
if (_filter(request)) {
Task<IResponse> task = LoadAsync(request, cancellationTokenSource.Token);
Download download = new Download(task, cancellationTokenSource, request.Address, originator);
Add(download);
task.ContinueWith(delegate {
Remove(download);
});
return download;
}
return new Download(TaskEx.FromResult<IResponse>(null), cancellationTokenSource, request.Address, originator);
}
public IEnumerable<IDownload> GetDownloads()
{
lock (this) {
return _downloads.ToArray();
}
}
protected async Task<IResponse> LoadAsync(Request request, CancellationToken cancel)
{
IEnumerable<IRequester> requesters = _context.Configuration.GetServices<IRequester>();
IResponse response = null;
int redirectCount = 0;
AppendCookieTo(request);
do {
if (response != null) {
redirectCount++;
ExtractCookieFrom(response);
request = CreateNewRequest(request, response);
AppendCookieTo(request);
}
foreach (IRequester item in requesters) {
if (item.SupportsProtocol(request.Address.Scheme)) {
_context.Fire(new RequestEvent(request, null));
response = await item.RequestAsync(request, cancel).ConfigureAwait(false);
_context.Fire(new RequestEvent(request, response));
break;
}
}
} while (response != null && response.StatusCode.IsRedirected() && redirectCount < MaxRedirects);
return response;
}
protected static Request CreateNewRequest(IRequest request, IResponse response)
{
HttpMethod method = request.Method;
Stream stream = request.Content;
Dictionary<string, string> dictionary = new Dictionary<string, string>(request.Headers);
string relativeAddress = response.Headers[HeaderNames.Location];
if (response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.SeeOther) {
method = HttpMethod.Get;
stream = Stream.Null;
} else if (stream.Length > 0) {
stream.Position = 0;
}
dictionary.Remove(HeaderNames.Cookie);
return new Request {
Address = new Url(request.Address, relativeAddress),
Method = method,
Content = stream,
Headers = dictionary
};
}
private void AppendCookieTo(Request request)
{
string cookie = GetCookie(request.Address);
if (cookie != null)
request.Headers[HeaderNames.Cookie] = cookie;
}
private void ExtractCookieFrom(IResponse response)
{
string orDefault = response.Headers.GetOrDefault(HeaderNames.SetCookie, null);
if (orDefault != null)
SetCookie(response.Address, orDefault);
}
}
}