using System.Net.WebSockets;
using Vanguard.Shared.Protocol;
namespace Vanguard.Server.Realtime;
/// Чтение одного кадра протокола из бинарных сообщений WebSocket.
internal static class ProtocolFrameReader
{
public static async Task ReadAsync(WebSocket socket, CancellationToken cancellationToken)
{
var buffer = new byte[16 * 1024];
using var stream = new MemoryStream();
while (true)
{
var result = await socket.ReceiveAsync(buffer.AsMemory(), cancellationToken);
if (result.MessageType == WebSocketMessageType.Close)
return null;
if (result.MessageType != WebSocketMessageType.Binary)
throw new ProtocolFormatException("Ожидался бинарный кадр.");
stream.Write(buffer, 0, result.Count);
if (stream.Length > ProtocolConstants.MaxFrameBytes)
throw new ProtocolFormatException(
$"Кадр больше {ProtocolConstants.MaxFrameBytes} байт.");
if (result.EndOfMessage)
break;
}
return ProtocolSerializer.DecodeClient(stream.ToArray());
}
}
/// Не больше N интентов в секунду с одного соединения.
internal sealed class IntentRateLimiter
{
private readonly int _limit;
private readonly TimeProvider _time;
private DateTimeOffset _windowStart;
private int _count;
public IntentRateLimiter(int limit, TimeProvider time)
{
_limit = limit;
_time = time;
_windowStart = time.GetUtcNow();
}
public bool TryAcquire()
{
var now = _time.GetUtcNow();
if (now - _windowStart >= TimeSpan.FromSeconds(1))
{
_windowStart = now;
_count = 0;
}
return ++_count <= _limit;
}
}