Files
MeWeb/src/MeWeb.Server/SpeedService.cs
T

65 lines
1.7 KiB
C#
Raw Normal View History

using System.Collections.Concurrent;
using System.Net;
using System.Security.Cryptography;
using MeWeb.Shared;
namespace MeWeb.Server;
public class SpeedService
{
const int PerHour = 12;
const int AtOnce = 4;
int active;
static readonly byte[] Noise = RandomNumberGenerator.GetBytes(1024 * 1024);
readonly ConcurrentDictionary<string, List<DateTimeOffset>> uses = new();
public bool Allow(IPAddress caller)
{
var now = DateTimeOffset.UtcNow;
var times = uses.GetOrAdd(ProbeService.NetworkOf(caller), _ => []);
lock (times)
{
times.RemoveAll(t => now - t > TimeSpan.FromHours(1));
if (times.Count >= PerHour) return false;
times.Add(now);
return true;
}
}
public bool Enter()
{
if (Interlocked.Increment(ref active) <= AtOnce) return true;
Interlocked.Decrement(ref active);
return false;
}
public void Leave() => Interlocked.Decrement(ref active);
public static async Task Stream(HttpResponse response, int bytes, CancellationToken ct)
{
response.ContentType = "application/octet-stream";
response.ContentLength = bytes;
response.Headers.CacheControl = "no-store";
var left = bytes;
while (left > 0)
{
var chunk = Math.Min(left, Noise.Length);
await response.Body.WriteAsync(Noise.AsMemory(0, chunk), ct);
left -= chunk;
}
}
public static async Task<long> Drain(Stream body, CancellationToken ct)
{
var buffer = new byte[64 * 1024];
long total = 0;
var read = default(int);
while ((read = await body.ReadAsync(buffer, ct)) > 0)
{
total += read;
if (total > Endpoints.Speed.MaxUpBytes) return -1;
}
return total;
}
}