Isolated Analyzers and Relay projects
This commit is contained in:
20
experiment/Discord.Net.Relay/ApplicationBuilderExtensions.cs
Normal file
20
experiment/Discord.Net.Relay/ApplicationBuilderExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
|
||||
namespace Discord.Relay
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static void UseDiscordRelay(this IApplicationBuilder app, Action<RelayServer> configAction = null)
|
||||
{
|
||||
var server = new RelayServer(configAction);
|
||||
server.StartAsync();
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
await server.AcceptAsync(context);
|
||||
await next();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
3
experiment/Discord.Net.Relay/AssemblyInfo.cs
Normal file
3
experiment/Discord.Net.Relay/AssemblyInfo.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
|
||||
18
experiment/Discord.Net.Relay/Discord.Net.Relay.csproj
Normal file
18
experiment/Discord.Net.Relay/Discord.Net.Relay.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="../../Discord.Net.targets" />
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Discord.Net.Relay</AssemblyName>
|
||||
<RootNamespace>Discord.Relay</RootNamespace>
|
||||
<Description>A core Discord.Net library containing the Relay server.</Description>
|
||||
<TargetFrameworks>netstandard1.3</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
|
||||
<ProjectReference Include="..\Discord.Net.Rest\Discord.Net.Rest.csproj" />
|
||||
<ProjectReference Include="..\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
79
experiment/Discord.Net.Relay/RelayConnection.cs
Normal file
79
experiment/Discord.Net.Relay/RelayConnection.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Discord.API;
|
||||
using Discord.API.Gateway;
|
||||
using Discord.Logging;
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using WebSocketClient = System.Net.WebSockets.WebSocket;
|
||||
|
||||
namespace Discord.Relay
|
||||
{
|
||||
public class RelayConnection
|
||||
{
|
||||
private readonly RelayServer _server;
|
||||
private readonly WebSocketClient _socket;
|
||||
private readonly CancellationTokenSource _cancelToken;
|
||||
private readonly byte[] _inBuffer, _outBuffer;
|
||||
private readonly Logger _logger;
|
||||
|
||||
internal RelayConnection(RelayServer server, WebSocketClient socket, int id)
|
||||
{
|
||||
_server = server;
|
||||
_socket = socket;
|
||||
_cancelToken = new CancellationTokenSource();
|
||||
_inBuffer = new byte[4000];
|
||||
_outBuffer = new byte[4000];
|
||||
_logger = server.LogManager.CreateLogger($"Client #{id}");
|
||||
}
|
||||
|
||||
internal async Task RunAsync()
|
||||
{
|
||||
await _logger.InfoAsync($"Connected");
|
||||
var token = _cancelToken.Token;
|
||||
try
|
||||
{
|
||||
var segment = new ArraySegment<byte>(_inBuffer);
|
||||
|
||||
//Send HELLO
|
||||
await SendAsync(GatewayOpCode.Hello, new HelloEvent { HeartbeatInterval = 15000 }).ConfigureAwait(false);
|
||||
|
||||
while (_socket.State == WebSocketState.Open)
|
||||
{
|
||||
var result = await _socket.ReceiveAsync(segment, token).ConfigureAwait(false);
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
await _logger.WarningAsync($"Received Close {result.CloseStatus} ({result.CloseStatusDescription ?? "No Reason"})").ConfigureAwait(false);
|
||||
else
|
||||
await _logger.InfoAsync($"Received {result.Count} bytes");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
try { await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).ConfigureAwait(false); }
|
||||
catch { }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { await _socket.CloseAsync(WebSocketCloseStatus.InternalServerError, ex.Message, CancellationToken.None).ConfigureAwait(false); }
|
||||
catch { }
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _logger.InfoAsync($"Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
_cancelToken.Cancel();
|
||||
}
|
||||
|
||||
private async Task SendAsync(GatewayOpCode opCode, object payload)
|
||||
{
|
||||
var frame = new SocketFrame { Operation = (int)opCode, Payload = payload };
|
||||
var bytes = _server.Serialize(frame, _outBuffer);
|
||||
var segment = new ArraySegment<byte>(_outBuffer, 0, bytes);
|
||||
await _socket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
experiment/Discord.Net.Relay/RelayServer.cs
Normal file
103
experiment/Discord.Net.Relay/RelayServer.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Discord.API;
|
||||
using Discord.Logging;
|
||||
using Discord.Net.Rest;
|
||||
using Discord.Net.WebSockets;
|
||||
using Discord.Rest;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using WebSocketClient = System.Net.WebSockets.WebSocket;
|
||||
|
||||
namespace Discord.Relay
|
||||
{
|
||||
public class RelayServer
|
||||
{
|
||||
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
|
||||
internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();
|
||||
|
||||
private readonly HashSet<RelayConnection> _connections;
|
||||
private readonly SemaphoreSlim _lock;
|
||||
private readonly JsonSerializer _serializer;
|
||||
private readonly DiscordSocketApiClient _discord;
|
||||
private int _nextId;
|
||||
|
||||
internal LogManager LogManager { get; }
|
||||
|
||||
internal RelayServer(Action<RelayServer> configAction)
|
||||
{
|
||||
_connections = new HashSet<RelayConnection>();
|
||||
_lock = new SemaphoreSlim(1, 1);
|
||||
_serializer = new JsonSerializer();
|
||||
_discord = new DiscordSocketApiClient(
|
||||
DefaultRestClientProvider.Instance,
|
||||
DefaultWebSocketProvider.Instance,
|
||||
DiscordRestConfig.UserAgent);
|
||||
configAction?.Invoke(this);
|
||||
|
||||
LogManager = new LogManager(LogSeverity.Debug);
|
||||
LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task AcceptAsync(HttpContext context)
|
||||
{
|
||||
WebSocketClient socket;
|
||||
try
|
||||
{
|
||||
socket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch { return; }
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var conn = new RelayConnection(this, socket, Interlocked.Increment(ref _nextId));
|
||||
await AddConnection(conn).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await conn.RunAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally { await RemoveConnection(conn).ConfigureAwait(false); }
|
||||
});
|
||||
}
|
||||
|
||||
internal void StartAsync()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await _discord.ConnectAsync().ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task AddConnection(RelayConnection conn)
|
||||
{
|
||||
await _lock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_connections.Add(conn);
|
||||
}
|
||||
finally { _lock.Release(); }
|
||||
}
|
||||
internal async Task RemoveConnection(RelayConnection conn)
|
||||
{
|
||||
await _lock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_connections.Remove(conn);
|
||||
}
|
||||
finally { _lock.Release(); }
|
||||
}
|
||||
|
||||
internal int Serialize(object obj, byte[] buffer)
|
||||
{
|
||||
using (var stream = new MemoryStream(buffer))
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
_serializer.Serialize(writer, obj);
|
||||
return (int)stream.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user