Skip to content

Commit

Permalink
feat: Add initial bootstrapping for voice server interception
Browse files Browse the repository at this point in the history
  • Loading branch information
angelobreuer committed Apr 19, 2024
1 parent 1046709 commit 72de0c5
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Lavalink4NET.Experiments.Receive.Extensions;

using Lavalink4NET.Players;
using Microsoft.Extensions.DependencyInjection.Extensions;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddLavalinkReceive(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);

services.TryAddSingleton<IVoiceServerSessionManager, VoiceServerSessionManager>();
services.Replace(ServiceDescriptor.Singleton<ILavalinkVoiceServerInterceptor, LavalinkReceiveVoiceServerInterceptor>());

return services;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Lavalink4NET.Experiments.Receive;

using Lavalink4NET.Clients;

interface IVoiceServerSessionManager
{
Guid Allocate(ulong guildId, VoiceServer voiceServer);

bool TryResolve(Guid sessionId, out ulong guildId, out VoiceServer voiceServer);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Lavalink4NET\Lavalink4NET.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Lavalink4NET.Experiments.Receive;

using System.Threading;
using System.Threading.Tasks;
using Lavalink4NET.Clients;
using Lavalink4NET.Players;

internal sealed class LavalinkReceiveVoiceServerInterceptor : ILavalinkVoiceServerInterceptor
{
private readonly IVoiceServerSessionManager _sessionManager;
private readonly ILogger<LavalinkReceiveVoiceServerInterceptor> _logger;

public LavalinkReceiveVoiceServerInterceptor(
IVoiceServerSessionManager sessionManager,
ILogger<LavalinkReceiveVoiceServerInterceptor> logger)
{
ArgumentNullException.ThrowIfNull(sessionManager);
ArgumentNullException.ThrowIfNull(logger);

_sessionManager = sessionManager;
_logger = logger;
}

public ValueTask<VoiceServer> InterceptAsync(
ulong guildId,
VoiceServer voiceServer,
CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

var sessionToken = _sessionManager.Allocate(guildId, voiceServer);
var proxiedVoiceServer = new VoiceServer(sessionToken.ToString("N"), "localhost:16389");

_logger.LogInformation(
"Mapping voice server '{OriginalEndpoint}' ({OriginalToken}) to '{ProxiedEndpoint}' ({ProxiedToken})",
voiceServer.Endpoint, voiceServer.Token, proxiedVoiceServer.Endpoint, proxiedVoiceServer.Token);

return new ValueTask<VoiceServer>(proxiedVoiceServer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
namespace Lavalink4NET.Experiments.Receive;

using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
using Microsoft.AspNetCore.WebSockets;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;

internal sealed class LavalinkVoiceServer : IHttpApplication<HttpContext>
{
private readonly IVoiceServerSessionManager _serverSessionManager;
private readonly KestrelServer _kestrelServer;
private readonly WebSocketMiddleware _webSocketMiddleware;

public LavalinkVoiceServer(
IVoiceServerSessionManager serverSessionManager,
ILoggerFactory loggerFactory)
{
ArgumentNullException.ThrowIfNull(serverSessionManager);

_serverSessionManager = serverSessionManager;

var kestrelServerOptions = new KestrelServerOptions { AddServerHeader = false, };
kestrelServerOptions.ListenLocalhost(16389);

var socketTransportOptions = new SocketTransportOptions { Backlog = 4, };
var socketTransportFactory = new SocketTransportFactory(Options.Create(socketTransportOptions), loggerFactory);

_kestrelServer = new KestrelServer(
options: Options.Create(kestrelServerOptions),
transportFactory: socketTransportFactory,
loggerFactory: loggerFactory);

var webSocketOptions = new WebSocketOptions { };

_webSocketMiddleware = new WebSocketMiddleware(
next: ProcessRequestInternalAsync,
options: Options.Create(webSocketOptions),
loggerFactory: loggerFactory);
}

public async ValueTask StartAsync(CancellationToken cancellationToken = default)
{
await _kestrelServer
.StartAsync(this, cancellationToken)
.ConfigureAwait(false);
}

public async ValueTask StopAsync(CancellationToken cancellationToken = default)
{
await _kestrelServer
.StopAsync(cancellationToken)
.ConfigureAwait(false);
}

HttpContext IHttpApplication<HttpContext>.CreateContext(IFeatureCollection contextFeatures)
{
ArgumentNullException.ThrowIfNull(contextFeatures);

return new DefaultHttpContext(contextFeatures);
}

void IHttpApplication<HttpContext>.DisposeContext(HttpContext context, Exception? exception)
{
}

Task IHttpApplication<HttpContext>.ProcessRequestAsync(HttpContext context)
{
ArgumentNullException.ThrowIfNull(context);

return _webSocketMiddleware.Invoke(context);
}

private async Task ProcessRequestInternalAsync(HttpContext httpContext)
{
ArgumentNullException.ThrowIfNull(httpContext);

var cancellationToken = httpContext.RequestAborted;
cancellationToken.ThrowIfCancellationRequested();

if (!httpContext.WebSockets.IsWebSocketRequest)
{
httpContext.Response.StatusCode = StatusCodes.Status426UpgradeRequired;
httpContext.Response.Headers[HeaderNames.Upgrade] = "websocket";
httpContext.Response.Headers[HeaderNames.Connection] = "Upgrade";

return;
}

var webSocketAcceptContext = new WebSocketAcceptContext { };

var webSocket = await httpContext.WebSockets
.AcceptWebSocketAsync(webSocketAcceptContext)
.ConfigureAwait(false);
}
}
2 changes: 2 additions & 0 deletions experiments/Lavalink4NET.Experiments.Receive/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Lavalink4NET.Experiments.Receive;

using System.Collections.Concurrent;
using Lavalink4NET.Clients;

internal sealed class VoiceServerSessionManager : IVoiceServerSessionManager
{
private readonly ConcurrentDictionary<Guid, (ulong GuildId, VoiceServer VoiceServer)> _voiceServers;

public VoiceServerSessionManager()
{
_voiceServers = new ConcurrentDictionary<Guid, (ulong GuildId, VoiceServer VoiceServer)>();
}

public Guid Allocate(ulong guildId, VoiceServer voiceServer)
{
var sessionId = Guid.NewGuid();
_voiceServers.TryAdd(sessionId, (guildId, voiceServer));

return sessionId;
}

public bool TryResolve(Guid sessionId, out ulong guildId, out VoiceServer voiceServer)
{
if (_voiceServers.TryGetValue(sessionId, out var pair))
{
guildId = pair.GuildId;
voiceServer = pair.VoiceServer;

return true;
}

guildId = default;
voiceServer = default;

return false;
}
}
14 changes: 10 additions & 4 deletions src/Lavalink4NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lavalink4NET.Integrations.L
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lavalink4NET.Integrations.Lavasrc.Tests", "..\tests\Lavalink4NET.Integrations.Lavasrc.Tests\Lavalink4NET.Integrations.Lavasrc.Tests.csproj", "{5779F765-5F0D-422C-984A-7E44EAE737C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lavalink4NET.NetCord", "Lavalink4NET.NetCord\Lavalink4NET.NetCord.csproj", "{8587F98B-CFE1-4559-9614-ED3B2B0C4F4E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lavalink4NET.NetCord", "Lavalink4NET.NetCord\Lavalink4NET.NetCord.csproj", "{8587F98B-CFE1-4559-9614-ED3B2B0C4F4E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lavalink4NET.Samples.NetCord", "..\samples\Lavalink4NET.Samples.NetCord\Lavalink4NET.Samples.NetCord.csproj", "{02FE863F-D979-439A-9A51-C4EA69D58D29}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lavalink4NET.Samples.NetCord", "..\samples\Lavalink4NET.Samples.NetCord\Lavalink4NET.Samples.NetCord.csproj", "{02FE863F-D979-439A-9A51-C4EA69D58D29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lavalink4NET.Integrations.LyricsJava", "Lavalink4NET.Integrations.LyricsJava\Lavalink4NET.Integrations.LyricsJava.csproj", "{9A30E985-6D67-41D4-A12F-F1ADCD2ED0FE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lavalink4NET.Integrations.LyricsJava", "Lavalink4NET.Integrations.LyricsJava\Lavalink4NET.Integrations.LyricsJava.csproj", "{9A30E985-6D67-41D4-A12F-F1ADCD2ED0FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lavalink4NET.Integrations.LyricsJava.Tests", "Lavalink4NET.Integrations.LyricsJava.Tests\Lavalink4NET.Integrations.LyricsJava.Tests.csproj", "{176B0345-DF57-42B4-A8FD-4E6436D9554C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lavalink4NET.Integrations.LyricsJava.Tests", "Lavalink4NET.Integrations.LyricsJava.Tests\Lavalink4NET.Integrations.LyricsJava.Tests.csproj", "{176B0345-DF57-42B4-A8FD-4E6436D9554C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lavalink4NET.Experiments.Receive", "..\experiments\Lavalink4NET.Experiments.Receive\Lavalink4NET.Experiments.Receive.csproj", "{F7EC54EF-8C23-4586-BC86-D9B7B702CE14}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -258,6 +260,10 @@ Global
{176B0345-DF57-42B4-A8FD-4E6436D9554C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{176B0345-DF57-42B4-A8FD-4E6436D9554C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{176B0345-DF57-42B4-A8FD-4E6436D9554C}.Release|Any CPU.Build.0 = Release|Any CPU
{F7EC54EF-8C23-4586-BC86-D9B7B702CE14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7EC54EF-8C23-4586-BC86-D9B7B702CE14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7EC54EF-8C23-4586-BC86-D9B7B702CE14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7EC54EF-8C23-4586-BC86-D9B7B702CE14}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down

0 comments on commit 72de0c5

Please sign in to comment.