Files
.github
.idx
DysonNetwork.Sphere
Account
Activity
Auth
Chat
Connection
Handlers
GeoIpService.cs
IWebSocketPacketHandler.cs
WebSocketController.cs
WebSocketPacket.cs
WebSocketService.cs
Developer
Email
Localization
Migrations
Pages
Permission
Post
Properties
Publisher
Realm
Resources
Sticker
Storage
Wallet
wwwroot
.DS_Store
.gitignore
AppDatabase.cs
Dockerfile
DysonNetwork.Sphere.csproj
DysonNetwork.Sphere.csproj.DotSettings.user
DysonNetwork.Sphere.http
Program.cs
appsettings.json
package.json
postcss.config.js
tailwind.config.js
.dockerignore
.gitignore
DysonNetwork.sln
DysonNetwork.sln.DotSettings.user
compose.yaml
Swarm/DysonNetwork.Sphere/Connection/WebSocketController.cs

108 lines
3.4 KiB
C#

using System.Collections.Concurrent;
using System.Net.WebSockets;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Swashbuckle.AspNetCore.Annotations;
namespace DysonNetwork.Sphere.Connection;
[ApiController]
[Route("/ws")]
public class WebSocketController(WebSocketService ws, ILogger<WebSocketContext> logger) : ControllerBase
{
[Route("/ws")]
[Authorize]
[SwaggerIgnore]
public async Task TheGateway()
{
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue);
if (currentUserValue is not Account.Account currentUser ||
currentSessionValue is not Auth.Session currentSession)
{
HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
var accountId = currentUser.Id;
var deviceId = currentSession.Challenge.DeviceId;
if (string.IsNullOrEmpty(deviceId))
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
var cts = new CancellationTokenSource();
var connectionKey = (accountId, deviceId);
if (!ws.TryAdd(connectionKey, webSocket, cts))
{
await webSocket.CloseAsync(
WebSocketCloseStatus.InternalServerError,
"Failed to establish connection.",
CancellationToken.None
);
return;
}
logger.LogInformation(
$"Connection established with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}");
try
{
await _ConnectionEventLoop(deviceId, currentUser, webSocket, cts.Token);
}
catch (Exception ex)
{
Console.WriteLine($"WebSocket Error: {ex.Message}");
}
finally
{
ws.Disconnect(connectionKey);
logger.LogInformation(
$"Connection disconnected with user @{currentUser.Name}#{currentUser.Id} and device #{deviceId}");
}
}
private async Task _ConnectionEventLoop(
string deviceId,
Account.Account currentUser,
WebSocket webSocket,
CancellationToken cancellationToken
)
{
var connectionKey = (AccountId: currentUser.Id, DeviceId: deviceId);
var buffer = new byte[1024 * 4];
try
{
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
cancellationToken
);
while (!receiveResult.CloseStatus.HasValue)
{
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer),
cancellationToken
);
var packet = WebSocketPacket.FromBytes(buffer[..receiveResult.Count]);
_ = ws.HandlePacket(currentUser, connectionKey.DeviceId, packet, webSocket);
}
}
catch (OperationCanceledException)
{
if (
webSocket.State != WebSocketState.Closed
&& webSocket.State != WebSocketState.Aborted
)
{
ws.Disconnect(connectionKey);
}
}
}
}