PluralKit/Myriad/Gateway/ShardConnection.cs

104 lines
3.1 KiB
C#
Raw Normal View History

2020-12-22 12:15:26 +00:00
using System;
using System.Net.WebSockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Serilog;
namespace Myriad.Gateway
{
public class ShardConnection: IAsyncDisposable
{
2021-04-29 09:10:19 +00:00
private ClientWebSocket? _client;
2020-12-22 12:15:26 +00:00
private readonly ILogger _logger;
2021-04-29 09:10:19 +00:00
private readonly ShardPacketSerializer _serializer;
2021-01-30 00:07:43 +00:00
2021-04-29 09:10:19 +00:00
public WebSocketState State => _client?.State ?? WebSocketState.Closed;
public WebSocketCloseStatus? CloseStatus => _client?.CloseStatus;
public string? CloseStatusDescription => _client?.CloseStatusDescription;
public ShardConnection(JsonSerializerOptions jsonSerializerOptions, ILogger logger)
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
_logger = logger.ForContext<ShardConnection>();
_serializer = new(jsonSerializerOptions);
2020-12-22 12:15:26 +00:00
}
2021-04-29 09:10:19 +00:00
public async Task Connect(string url, CancellationToken ct)
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
_client?.Dispose();
_client = new ClientWebSocket();
2021-01-30 00:07:43 +00:00
2021-04-29 09:10:19 +00:00
await _client.ConnectAsync(GetConnectionUri(url), ct);
2020-12-22 12:15:26 +00:00
}
2021-04-29 09:10:19 +00:00
public async Task Disconnect(WebSocketCloseStatus closeStatus, string? reason)
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
await CloseInner(closeStatus, reason);
2020-12-22 12:15:26 +00:00
}
2021-04-29 09:10:19 +00:00
public async Task Send(GatewayPacket packet)
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
if (_client == null || _client.State != WebSocketState.Open)
return;
2020-12-22 12:15:26 +00:00
try
{
2021-04-29 09:10:19 +00:00
await _serializer.WritePacket(_client, packet);
2020-12-22 12:15:26 +00:00
}
catch (Exception e)
{
2021-04-29 09:10:19 +00:00
_logger.Error(e, "Error sending WebSocket message");
2020-12-22 12:15:26 +00:00
}
}
2021-04-29 09:10:19 +00:00
public async ValueTask DisposeAsync()
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
await CloseInner(WebSocketCloseStatus.NormalClosure, null);
_client?.Dispose();
2020-12-22 12:15:26 +00:00
}
2021-04-29 09:10:19 +00:00
public async Task<GatewayPacket?> Read()
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
if (_client == null || _client.State != WebSocketState.Open)
return null;
try
{
var (_, packet) = await _serializer.ReadPacket(_client);
return packet;
}
catch (Exception e)
{
_logger.Error(e, "Error reading from WebSocket");
}
return null;
2020-12-22 12:15:26 +00:00
}
2021-04-29 09:10:19 +00:00
private Uri GetConnectionUri(string baseUri) => new UriBuilder(baseUri)
{
Query = "v=8&encoding=json"
}.Uri;
2020-12-22 12:15:26 +00:00
2021-04-29 09:10:19 +00:00
private async Task CloseInner(WebSocketCloseStatus closeStatus, string? description)
2020-12-22 12:15:26 +00:00
{
2021-04-29 09:10:19 +00:00
if (_client == null)
return;
if (_client.State != WebSocketState.Connecting && _client.State != WebSocketState.Open)
return;
// Close with timeout, mostly to work around https://github.com/dotnet/runtime/issues/51590
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
await _client.CloseAsync(closeStatus, description, cts.Token);
}
catch (Exception e)
{
_logger.Error(e, "Error closing WebSocket connection");
}
2020-12-22 12:15:26 +00:00
}
}
}