Browse Source

Started Discord.Net.Relay

tags/1.0-rc
RogueException 8 years ago
parent
commit
8630185ac9
11 changed files with 269 additions and 43 deletions
  1. +18
    -1
      Discord.Net.sln
  2. +1
    -0
      src/Discord.Net.Core/AssemblyInfo.cs
  3. +20
    -0
      src/Discord.Net.Relay/ApplicationBuilderExtensions.cs
  4. +3
    -0
      src/Discord.Net.Relay/AssemblyInfo.cs
  5. +32
    -0
      src/Discord.Net.Relay/Discord.Net.Relay.csproj
  6. +79
    -0
      src/Discord.Net.Relay/RelayConnection.cs
  7. +103
    -0
      src/Discord.Net.Relay/RelayServer.cs
  8. +1
    -0
      src/Discord.Net.WebSocket/AssemblyInfo.cs
  9. +6
    -5
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  10. +1
    -1
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  11. +5
    -36
      src/Discord.Net.WebSocket/DiscordSocketConfig.cs

+ 18
- 1
Discord.Net.sln View File

@@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26014.0
@@ -25,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\Discord.Net.Tests\Discord.Net.Tests.csproj", "{C38E5BC1-11CB-4101-8A38-5B40A1BC6433}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F66D75C0-E304-46E0-9C3A-294F340DB37D}"
EndProject
Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "Discord.Net.Relay", "src\Discord.Net.Relay\Discord.Net.Relay.csproj", "{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -143,6 +147,18 @@ Global
{C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x64.Build.0 = Release|x64
{C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x86.ActiveCfg = Release|x86
{C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x86.Build.0 = Release|x86
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x64.ActiveCfg = Debug|x64
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x64.Build.0 = Debug|x64
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x86.ActiveCfg = Debug|x86
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x86.Build.0 = Debug|x86
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|Any CPU.Build.0 = Release|Any CPU
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x64.ActiveCfg = Release|x64
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x64.Build.0 = Release|x64
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x86.ActiveCfg = Release|x86
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -154,5 +170,6 @@ Global
{688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012}
{ABC9F4B9-2452-4725-B522-754E0A02E282} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012}
{2705FCB3-68C9-4CEB-89CC-01F8EC80512B} = {F66D75C0-E304-46E0-9C3A-294F340DB37D}
EndGlobalSection
EndGlobal

+ 1
- 0
src/Discord.Net.Core/AssemblyInfo.cs View File

@@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Discord.Net.Relay")]
[assembly: InternalsVisibleTo("Discord.Net.Rest")]
[assembly: InternalsVisibleTo("Discord.Net.Rpc")]
[assembly: InternalsVisibleTo("Discord.Net.WebSocket")]


+ 20
- 0
src/Discord.Net.Relay/ApplicationBuilderExtensions.cs View 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
- 0
src/Discord.Net.Relay/AssemblyInfo.cs View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Discord.Net.Tests")]

+ 32
- 0
src/Discord.Net.Relay/Discord.Net.Relay.csproj View File

@@ -0,0 +1,32 @@
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix Condition="'$(BuildNumber)' == ''">rc-dev</VersionSuffix>
<VersionSuffix Condition="'$(BuildNumber)' != ''">rc-$(BuildNumber)</VersionSuffix>
<TargetFramework>netstandard1.3</TargetFramework>
<AssemblyName>Discord.Net.Relay</AssemblyName>
<Authors>RogueException</Authors>
<Description>A core Discord.Net library containing the Relay server.</Description>
<PackageTags>discord;discordapp</PackageTags>
<PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl>
<PackageLicenseUrl>http://opensource.org/licenses/MIT</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>git://github.com/RogueException/Discord.Net</RepositoryUrl>
<RootNamespace>Discord.Relay</RootNamespace>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</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>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<WarningsAsErrors>true</WarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>

+ 79
- 0
src/Discord.Net.Relay/RelayConnection.cs View 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
- 0
src/Discord.Net.Relay/RelayServer.cs View 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;
}
}
}
}

+ 1
- 0
src/Discord.Net.WebSocket/AssemblyInfo.cs View File

@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Discord.Net.Relay")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]

+ 6
- 5
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs View File

@@ -33,10 +33,11 @@ namespace Discord.API

public ConnectionState ConnectionState { get; private set; }

public DiscordSocketApiClient(RestClientProvider restClientProvider, string userAgent, WebSocketProvider webSocketProvider,
RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null)
public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent,
string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null)
: base(restClientProvider, userAgent, defaultRetryMode, serializer)
{
{
_gatewayUrl = url;
WebSocketClient = webSocketProvider();
//WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+)
WebSocketClient.BinaryMessage += async (data, index, count) =>
@@ -115,9 +116,9 @@ namespace Discord.API

ConnectionState = ConnectionState.Connected;
}
catch (Exception)
catch
{
_gatewayUrl = null; //Uncache in case the gateway url changed
_gatewayUrl = null; //Uncache in case the gateway url changed
await DisconnectInternalAsync().ConfigureAwait(false);
throw;
}


+ 1
- 1
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -142,7 +142,7 @@ namespace Discord.WebSocket
_largeGuilds = new ConcurrentQueue<ulong>();
}
private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
=> new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent);
=> new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost);
protected override async Task OnLoginAsync(TokenType tokenType, string token)
{


+ 5
- 36
src/Discord.Net.WebSocket/DiscordSocketConfig.cs View File

@@ -2,7 +2,6 @@
using Discord.Net.Udp;
using Discord.Net.WebSockets;
using Discord.Rest;
using System;

namespace Discord.WebSocket
{
@@ -10,6 +9,9 @@ namespace Discord.WebSocket
{
public const string GatewayEncoding = "json";

/// <summary> Gets or sets the websocket host to connect to. If null, the client will use the /gateway endpoint.
public string GatewayHost { get; set; } = null;

/// <summary> Gets or sets the time, in milliseconds, to wait for a connection to complete before aborting. </summary>
public int ConnectionTimeout { get; set; } = 30000;

@@ -38,41 +40,8 @@ namespace Discord.WebSocket

public DiscordSocketConfig()
{
#if NETSTANDARD1_3
WebSocketProvider = () =>
{
try
{
return new DefaultWebSocketClient();
}
catch (PlatformNotSupportedException ex)
{
throw new PlatformNotSupportedException("The default websocket provider is not supported on this platform.", ex);
}
};
UdpSocketProvider = () =>
{
try
{
return new DefaultUdpSocket();
}
catch (PlatformNotSupportedException ex)
{
throw new PlatformNotSupportedException("The default UDP provider is not supported on this platform.", ex);
}
};
#else
WebSocketProvider = () =>
{
throw new PlatformNotSupportedException("The default websocket provider is not supported on this platform.\n" +
"You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+.");
};
UdpSocketProvider = () =>
{
throw new PlatformNotSupportedException("The default UDP provider is not supported on this platform.\n" +
"You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+.");
};
#endif
WebSocketProvider = DefaultWebSocketProvider.Instance;
UdpSocketProvider = DefaultUdpSocketProvider.Instance;
}

internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig;


Loading…
Cancel
Save