PrivaPub now finds CDNs three ways, best first: the address ranges the CDNs publish (Cloudflare, Fastly, Amazon CloudFront, Bunny, Gcore, Imperva), downloaded daily by CdnUpdater and kept in CdnRangeSet; the CDN's fingerprint in the responses it already gets from a server (EdgeHintsHandler on the federation client); and the networks that carry only a CDN. The fixed ASN list is gone; ASNs shared with plain hosting (AWS, DataPacket) no longer hide a server. A server's Geo records the CDN, its domain and how it was found, and weekly snapshots now keep the city and coordinates too. Servers through time (ServerPlaces): /instances/:host/history lists a server's weekly snapshots, a CDN-fronted server's geo names the CDN's domain and where the server was before it (before_cdn), and /api/privapub/v1/cdns and /cdns/:domain group servers by CDN with week by week who joined and who left. Owner decisions recorded in ROADMAP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
56 lines
1.7 KiB
C#
56 lines
1.7 KiB
C#
using PrivaPub.Infrastructure.Geo;
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace PrivaPub.Infrastructure.Http
|
|
{
|
|
public interface IEdgeHints
|
|
{
|
|
void Remember(string host, CdnProvider cdn);
|
|
CdnProvider Of(string host);
|
|
}
|
|
|
|
// The CDN each remote host's responses came through, as their headers tell it (CdnCatalog fingerprints), for the
|
|
// weekly description of the server. Like ConnectedAddresses, memory only: what a host answers is never stored per
|
|
// request.
|
|
public class EdgeHints : IEdgeHints
|
|
{
|
|
const int MaxHosts = 50_000;
|
|
|
|
readonly ConcurrentDictionary<string, CdnProvider> _hints = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public void Remember(string host, CdnProvider cdn)
|
|
{
|
|
if (string.IsNullOrEmpty(host))
|
|
return;
|
|
if (cdn == default)
|
|
{
|
|
_hints.TryRemove(host, out _);
|
|
return;
|
|
}
|
|
if (_hints.Count >= MaxHosts)
|
|
_hints.Clear();
|
|
_hints[host] = cdn;
|
|
}
|
|
|
|
public CdnProvider Of(string host) => host != default && _hints.TryGetValue(host, out var cdn) ? cdn : default;
|
|
}
|
|
|
|
// On the federation client: reads each response's CDN fingerprint for its host. A response without one forgets the
|
|
// host's hint, so a server that leaves its CDN is seen as itself again.
|
|
public class EdgeHintsHandler : DelegatingHandler
|
|
{
|
|
readonly IEdgeHints _hints;
|
|
|
|
public EdgeHintsHandler(IEdgeHints hints) => _hints = hints;
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
{
|
|
var response = await base.SendAsync(request, cancellationToken);
|
|
if (request.RequestUri is { } uri && (int)response.StatusCode is >= 200 and < 500)
|
|
_hints.Remember(uri.Host, CdnCatalog.FromResponse(response));
|
|
return response;
|
|
}
|
|
}
|
|
}
|