65 lines
1.9 KiB
C#
65 lines
1.9 KiB
C#
using System.Net.WebSockets;
|
||
using Vanguard.Shared.Protocol;
|
||
|
||
namespace Vanguard.Server.Realtime;
|
||
|
||
/// <summary>Чтение одного кадра протокола из бинарных сообщений WebSocket.</summary>
|
||
internal static class ProtocolFrameReader
|
||
{
|
||
public static async Task<ClientFrame?> 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());
|
||
}
|
||
}
|
||
|
||
/// <summary>Не больше N интентов в секунду с одного соединения.</summary>
|
||
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;
|
||
}
|
||
}
|