Files
SocialPub/PrivaPub/Infrastructure/Geo/CdnRanges.cs
T

65 lines
2.3 KiB
C#
Raw Normal View History

using MongoDB.Entities;
using PrivaPub.Models.Jobs;
using System.Net;
namespace PrivaPub.Infrastructure.Geo
{
public interface ICdnRanges
{
// the CDN whose published ranges hold the address, or default
CdnProvider Of(IPAddress address);
void Replace(CdnRangeSet set);
Task Load(CancellationToken token);
// per CDN key: how many ranges are known and when they were downloaded
IReadOnlyDictionary<string, (int Ranges, DateTime FetchedAt)> Status { get; }
}
// The published CDN ranges in memory, rebuilt from the stored CdnRangeSets at start and replaced set by set as
// CdnUpdater downloads them. A few thousand networks: a linear scan per describe is cheap, and describes are weekly.
public class CdnRanges : ICdnRanges
{
readonly object _gate = new();
Dictionary<string, (CdnProvider Provider, IPNetwork[] Networks, DateTime FetchedAt)> _sets = new(StringComparer.OrdinalIgnoreCase);
public IReadOnlyDictionary<string, (int Ranges, DateTime FetchedAt)> Status
{
get
{
lock (_gate)
return _sets.ToDictionary(s => s.Key, s => (s.Value.Networks.Length, s.Value.FetchedAt), StringComparer.OrdinalIgnoreCase);
}
}
public CdnProvider Of(IPAddress address)
{
if (address == default)
return default;
if (address.IsIPv4MappedToIPv6)
address = address.MapToIPv4();
Dictionary<string, (CdnProvider Provider, IPNetwork[] Networks, DateTime FetchedAt)> sets;
lock (_gate)
sets = _sets;
foreach (var (provider, networks, _) in sets.Values)
foreach (var network in networks)
if (network.BaseAddress.AddressFamily == address.AddressFamily && network.Contains(address))
return provider;
return default;
}
public void Replace(CdnRangeSet set)
{
var provider = CdnCatalog.ByKey(set.Key) ?? new CdnProvider(set.Name, set.Key, Array.Empty<int>(), Array.Empty<CdnList>(), _ => false);
var networks = set.Ranges.Select(r => IPNetwork.TryParse(r, out var n) ? n : (IPNetwork?)null).Where(n => n != default).Select(n => n!.Value).ToArray();
lock (_gate)
_sets = new(_sets, StringComparer.OrdinalIgnoreCase) { [set.Key] = (provider, networks, set.FetchedAt) };
}
public async Task Load(CancellationToken token)
{
foreach (var set in await DB.Default.Find<CdnRangeSet>().ExecuteAsync(token))
Replace(set);
}
}
}