Finished (probably?) ETF serialization, started spliting Rest into ETFRestClient and JsonRestClient.
This commit is contained in:
@@ -87,7 +87,7 @@ namespace Discord.Audio
|
|||||||
//Networking
|
//Networking
|
||||||
if (Config.EnableMultiserver)
|
if (Config.EnableMultiserver)
|
||||||
{
|
{
|
||||||
ClientAPI = new RestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}"));
|
ClientAPI = new JsonRestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}"));
|
||||||
GatewaySocket = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}"));
|
GatewaySocket = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}"));
|
||||||
GatewaySocket.Connected += (s, e) =>
|
GatewaySocket.Connected += (s, e) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -532,9 +532,15 @@
|
|||||||
<Compile Include="..\Discord.Net\Net\Rest\CompletedRequestEventArgs.cs">
|
<Compile Include="..\Discord.Net\Net\Rest\CompletedRequestEventArgs.cs">
|
||||||
<Link>Net\Rest\CompletedRequestEventArgs.cs</Link>
|
<Link>Net\Rest\CompletedRequestEventArgs.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Discord.Net\Net\Rest\ETFRestClient.cs">
|
||||||
|
<Link>Net\Rest\ETFRestClient.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net\Net\Rest\IRestEngine.cs">
|
<Compile Include="..\Discord.Net\Net\Rest\IRestEngine.cs">
|
||||||
<Link>Net\Rest\IRestEngine.cs</Link>
|
<Link>Net\Rest\IRestEngine.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Discord.Net\Net\Rest\JsonRestClient.cs">
|
||||||
|
<Link>Net\Rest\JsonRestClient.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net\Net\Rest\RequestEventArgs.cs">
|
<Compile Include="..\Discord.Net\Net\Rest\RequestEventArgs.cs">
|
||||||
<Link>Net\Rest\RequestEventArgs.cs</Link>
|
<Link>Net\Rest\RequestEventArgs.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ namespace Discord
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Networking
|
//Networking
|
||||||
ClientAPI = new RestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI"));
|
ClientAPI = new JsonRestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI"));
|
||||||
StatusAPI = new RestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI"));
|
StatusAPI = new JsonRestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI"));
|
||||||
GatewaySocket = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway"));
|
GatewaySocket = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway"));
|
||||||
GatewaySocket.Connected += (s, e) =>
|
GatewaySocket.Connected += (s, e) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -12,6 +13,7 @@ namespace Discord.ETF
|
|||||||
public unsafe class ETFWriter : IDisposable
|
public unsafe class ETFWriter : IDisposable
|
||||||
{
|
{
|
||||||
private readonly static byte[] _nilBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 3, (byte)'n', (byte)'i', (byte)'l' };
|
private readonly static byte[] _nilBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 3, (byte)'n', (byte)'i', (byte)'l' };
|
||||||
|
private readonly static byte[] _nilExtBytes = new byte[] { (byte)ETFType.NIL_EXT};
|
||||||
private readonly static byte[] _falseBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 5, (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' };
|
private readonly static byte[] _falseBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 5, (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' };
|
||||||
private readonly static byte[] _trueBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 4, (byte)'t', (byte)'r', (byte)'u', (byte)'e' };
|
private readonly static byte[] _trueBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 4, (byte)'t', (byte)'r', (byte)'u', (byte)'e' };
|
||||||
|
|
||||||
@@ -238,6 +240,45 @@ namespace Discord.ETF
|
|||||||
else
|
else
|
||||||
WriteNil();
|
WriteNil();
|
||||||
}
|
}
|
||||||
|
public void Write<T>(IEnumerable<T> obj)
|
||||||
|
{
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
var array = obj.ToArray();
|
||||||
|
int length = array.Length;
|
||||||
|
_buffer[0] = (byte)ETFType.LIST_EXT;
|
||||||
|
_buffer[1] = (byte)((length >> 24) & 0xFF);
|
||||||
|
_buffer[2] = (byte)((length >> 16) & 0xFF);
|
||||||
|
_buffer[3] = (byte)((length >> 8) & 0xFF);
|
||||||
|
_buffer[4] = (byte)(length & 0xFF);
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
Write(array[i]);
|
||||||
|
WriteNilExt();
|
||||||
|
_stream.Write(_buffer, 0, 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WriteNil();
|
||||||
|
}
|
||||||
|
public void Write<TKey, TValue>(IDictionary<TKey, TValue> obj)
|
||||||
|
{
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
int length = obj.Count;
|
||||||
|
_buffer[0] = (byte)ETFType.MAP_EXT;
|
||||||
|
_buffer[1] = (byte)((length >> 24) & 0xFF);
|
||||||
|
_buffer[2] = (byte)((length >> 16) & 0xFF);
|
||||||
|
_buffer[3] = (byte)((length >> 8) & 0xFF);
|
||||||
|
_buffer[4] = (byte)(length & 0xFF);
|
||||||
|
foreach (var pair in obj)
|
||||||
|
{
|
||||||
|
Write(pair.Key);
|
||||||
|
Write(pair.Value);
|
||||||
|
}
|
||||||
|
_stream.Write(_buffer, 0, 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WriteNil();
|
||||||
|
}
|
||||||
public void Write(object obj)
|
public void Write(object obj)
|
||||||
{
|
{
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
@@ -302,8 +343,12 @@ namespace Discord.ETF
|
|||||||
//TODO: Add field/property names
|
//TODO: Add field/property names
|
||||||
typeInfo.ForEachField(f =>
|
typeInfo.ForEachField(f =>
|
||||||
{
|
{
|
||||||
if (!f.IsPublic) return;
|
string name;
|
||||||
|
if (!f.IsPublic || !IsETFProperty(f, out name)) return;
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldarg_0); //ETFWriter(this)
|
||||||
|
generator.Emit(OpCodes.Ldstr, name); //ETFWriter(this), name
|
||||||
|
generator.EmitCall(OpCodes.Call, GetWriteMethod(typeof(string)), null);
|
||||||
generator.Emit(OpCodes.Ldarg_0); //ETFWriter(this)
|
generator.Emit(OpCodes.Ldarg_0); //ETFWriter(this)
|
||||||
generator.Emit(OpCodes.Ldarg_1); //ETFWriter(this), obj
|
generator.Emit(OpCodes.Ldarg_1); //ETFWriter(this), obj
|
||||||
generator.Emit(OpCodes.Ldfld, f); //ETFWriter(this), obj.fieldValue
|
generator.Emit(OpCodes.Ldfld, f); //ETFWriter(this), obj.fieldValue
|
||||||
@@ -314,8 +359,12 @@ namespace Discord.ETF
|
|||||||
|
|
||||||
typeInfo.ForEachProperty(p =>
|
typeInfo.ForEachProperty(p =>
|
||||||
{
|
{
|
||||||
if (!p.CanRead || !p.GetMethod.IsPublic) return;
|
string name;
|
||||||
|
if (!p.CanRead || !p.GetMethod.IsPublic || !IsETFProperty(p, out name)) return;
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldarg_0); //ETFWriter(this)
|
||||||
|
generator.Emit(OpCodes.Ldstr, name); //ETFWriter(this), name
|
||||||
|
generator.EmitCall(OpCodes.Call, GetWriteMethod(typeof(string)), null);
|
||||||
generator.Emit(OpCodes.Ldarg_0); //ETFWriter(this)
|
generator.Emit(OpCodes.Ldarg_0); //ETFWriter(this)
|
||||||
generator.Emit(OpCodes.Ldarg_1); //ETFWriter(this), obj
|
generator.Emit(OpCodes.Ldarg_1); //ETFWriter(this), obj
|
||||||
generator.EmitCall(OpCodes.Callvirt, p.GetMethod, null); //ETFWriter(this), obj.propValue
|
generator.EmitCall(OpCodes.Callvirt, p.GetMethod, null); //ETFWriter(this), obj.propValue
|
||||||
@@ -400,6 +449,30 @@ namespace Discord.ETF
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void WriteNil() => _stream.Write(_nilBytes, 0, _nilBytes.Length);
|
private void WriteNil() => _stream.Write(_nilBytes, 0, _nilBytes.Length);
|
||||||
|
private void WriteNilExt() => _stream.Write(_nilExtBytes, 0, _nilExtBytes.Length);
|
||||||
|
|
||||||
|
private bool IsETFProperty(FieldInfo f, out string name)
|
||||||
|
{
|
||||||
|
var attrib = f.CustomAttributes.Where(x => x.AttributeType == typeof(JsonPropertyAttribute)).FirstOrDefault();
|
||||||
|
if (attrib != null)
|
||||||
|
{
|
||||||
|
name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? f.Name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
name = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
private bool IsETFProperty(PropertyInfo p, out string name)
|
||||||
|
{
|
||||||
|
var attrib = p.CustomAttributes.Where(x => x.AttributeType == typeof(JsonPropertyAttribute)).FirstOrDefault();
|
||||||
|
if (attrib != null)
|
||||||
|
{
|
||||||
|
name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? p.Name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
name = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
private bool _isDisposed = false;
|
private bool _isDisposed = false;
|
||||||
|
|||||||
27
src/Discord.Net/Net/Rest/ETFRestClient.cs
Normal file
27
src/Discord.Net/Net/Rest/ETFRestClient.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Discord.ETF;
|
||||||
|
using System.IO;
|
||||||
|
using System;
|
||||||
|
using Discord.Logging;
|
||||||
|
|
||||||
|
namespace Discord.Net.Rest
|
||||||
|
{
|
||||||
|
public class ETFRestClient : RestClient
|
||||||
|
{
|
||||||
|
private readonly ETFWriter _serializer;
|
||||||
|
|
||||||
|
public ETFRestClient(DiscordConfig config, string baseUrl, Logger logger)
|
||||||
|
: base(config, baseUrl, logger)
|
||||||
|
{
|
||||||
|
_serializer = new ETFWriter(new MemoryStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Serialize<T>(T obj)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
protected override T Deserialize<T>(string json)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/Discord.Net/Net/Rest/JsonRestClient.cs
Normal file
37
src/Discord.Net/Net/Rest/JsonRestClient.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using Discord.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Discord.Net.Rest
|
||||||
|
{
|
||||||
|
public class JsonRestClient : RestClient
|
||||||
|
{
|
||||||
|
private JsonSerializerSettings _deserializeSettings;
|
||||||
|
|
||||||
|
public JsonRestClient(DiscordConfig config, string baseUrl, Logger logger)
|
||||||
|
: base(config, baseUrl, logger)
|
||||||
|
{
|
||||||
|
_deserializeSettings = new JsonSerializerSettings();
|
||||||
|
#if TEST_RESPONSES
|
||||||
|
_deserializeSettings.CheckAdditionalContent = true;
|
||||||
|
_deserializeSettings.MissingMemberHandling = MissingMemberHandling.Error;
|
||||||
|
#else
|
||||||
|
_deserializeSettings.CheckAdditionalContent = false;
|
||||||
|
_deserializeSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Serialize<T>(T obj)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override T Deserialize<T>(string json)
|
||||||
|
{
|
||||||
|
#if TEST_RESPONSES
|
||||||
|
if (string.IsNullOrEmpty(json))
|
||||||
|
throw new Exception("API check failed: Response is empty.");
|
||||||
|
#endif
|
||||||
|
return JsonConvert.DeserializeObject<T>(json, _deserializeSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
using Discord.API;
|
using Discord.API;
|
||||||
|
using Discord.ETF;
|
||||||
using Discord.Logging;
|
using Discord.Logging;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Discord.Net.Rest
|
namespace Discord.Net.Rest
|
||||||
{
|
{
|
||||||
public partial class RestClient
|
public abstract partial class RestClient
|
||||||
{
|
{
|
||||||
private struct RestResults
|
private struct RestResults
|
||||||
{
|
{
|
||||||
@@ -32,8 +33,8 @@ namespace Discord.Net.Rest
|
|||||||
|
|
||||||
private readonly DiscordConfig _config;
|
private readonly DiscordConfig _config;
|
||||||
private readonly IRestEngine _engine;
|
private readonly IRestEngine _engine;
|
||||||
|
private readonly ETFWriter _serializer;
|
||||||
private string _token;
|
private string _token;
|
||||||
private JsonSerializerSettings _deserializeSettings;
|
|
||||||
|
|
||||||
internal Logger Logger { get; }
|
internal Logger Logger { get; }
|
||||||
|
|
||||||
@@ -49,26 +50,17 @@ namespace Discord.Net.Rest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RestClient(DiscordConfig config, string baseUrl, Logger logger)
|
protected RestClient(DiscordConfig config, string baseUrl, Logger logger)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
|
|
||||||
#if !DOTNET5_4
|
#if !DOTNET5_4
|
||||||
_engine = new RestSharpEngine(config, baseUrl, logger);
|
_engine = new RestSharpEngine(config, baseUrl, logger);
|
||||||
#else
|
#else
|
||||||
_engine = new BuiltInEngine(config, baseUrl, logger);
|
_engine = new BuiltInEngine(config, baseUrl, logger);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_deserializeSettings = new JsonSerializerSettings();
|
|
||||||
#if TEST_RESPONSES
|
|
||||||
_deserializeSettings.CheckAdditionalContent = true;
|
|
||||||
_deserializeSettings.MissingMemberHandling = MissingMemberHandling.Error;
|
|
||||||
#else
|
|
||||||
_deserializeSettings.CheckAdditionalContent = false;
|
|
||||||
_deserializeSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (Logger.Level >= LogSeverity.Verbose)
|
if (Logger.Level >= LogSeverity.Verbose)
|
||||||
{
|
{
|
||||||
this.SentRequest += (s, e) =>
|
this.SentRequest += (s, e) =>
|
||||||
@@ -98,7 +90,7 @@ namespace Discord.Net.Rest
|
|||||||
|
|
||||||
OnSendingRequest(request);
|
OnSendingRequest(request);
|
||||||
var results = await Send(request, true).ConfigureAwait(false);
|
var results = await Send(request, true).ConfigureAwait(false);
|
||||||
var response = DeserializeResponse<ResponseT>(results.Response);
|
var response = Deserialize<ResponseT>(results.Response);
|
||||||
OnSentRequest(request, response, results.Response, results.Milliseconds);
|
OnSentRequest(request, response, results.Response, results.Milliseconds);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@@ -118,9 +110,8 @@ namespace Discord.Net.Rest
|
|||||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
OnSendingRequest(request);
|
OnSendingRequest(request);
|
||||||
var requestJson = JsonConvert.SerializeObject(request.Payload);
|
|
||||||
var results = await SendFile(request, true).ConfigureAwait(false);
|
var results = await SendFile(request, true).ConfigureAwait(false);
|
||||||
var response = DeserializeResponse<ResponseT>(results.Response);
|
var response = Deserialize<ResponseT>(results.Response);
|
||||||
OnSentRequest(request, response, results.Response, results.Milliseconds);
|
OnSentRequest(request, response, results.Response, results.Milliseconds);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@@ -139,7 +130,7 @@ namespace Discord.Net.Rest
|
|||||||
object payload = request.Payload;
|
object payload = request.Payload;
|
||||||
string requestJson = null;
|
string requestJson = null;
|
||||||
if (payload != null)
|
if (payload != null)
|
||||||
requestJson = JsonConvert.SerializeObject(payload);
|
requestJson = Serialize(payload);
|
||||||
|
|
||||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||||
string responseJson = await _engine.Send(request.Method, request.Endpoint, requestJson, CancelToken).ConfigureAwait(false);
|
string responseJson = await _engine.Send(request.Method, request.Endpoint, requestJson, CancelToken).ConfigureAwait(false);
|
||||||
@@ -159,13 +150,7 @@ namespace Discord.Net.Rest
|
|||||||
return new RestResults(responseJson, milliseconds);
|
return new RestResults(responseJson, milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private T DeserializeResponse<T>(string json)
|
protected abstract string Serialize<T>(T obj);
|
||||||
{
|
protected abstract T Deserialize<T>(string json);
|
||||||
#if TEST_RESPONSES
|
|
||||||
if (string.IsNullOrEmpty(json))
|
|
||||||
throw new Exception("API check failed: Response is empty.");
|
|
||||||
#endif
|
|
||||||
return JsonConvert.DeserializeObject<T>(json, _deserializeSettings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user