diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs
index 279557029..c7d9d182d 100644
--- a/src/Discord.Net.Audio/AudioClient.cs
+++ b/src/Discord.Net.Audio/AudioClient.cs
@@ -87,7 +87,7 @@ namespace Discord.Audio
//Networking
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.Connected += (s, e) =>
{
diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj
index 0dce8fc1c..665efdb95 100644
--- a/src/Discord.Net.Net45/Discord.Net.csproj
+++ b/src/Discord.Net.Net45/Discord.Net.csproj
@@ -532,9 +532,15 @@
Net\Rest\CompletedRequestEventArgs.cs
+
+ Net\Rest\ETFRestClient.cs
+
Net\Rest\IRestEngine.cs
+
+ Net\Rest\JsonRestClient.cs
+
Net\Rest\RequestEventArgs.cs
diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs
index 46f740763..5d929a581 100644
--- a/src/Discord.Net/DiscordClient.cs
+++ b/src/Discord.Net/DiscordClient.cs
@@ -135,8 +135,8 @@ namespace Discord
};
//Networking
- ClientAPI = new RestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI"));
- StatusAPI = new RestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI"));
+ ClientAPI = new JsonRestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI"));
+ StatusAPI = new JsonRestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI"));
GatewaySocket = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway"));
GatewaySocket.Connected += (s, e) =>
{
diff --git a/src/Discord.Net/ETF/ETFWriter.cs b/src/Discord.Net/ETF/ETFWriter.cs
index 06641e664..6222d6427 100644
--- a/src/Discord.Net/ETF/ETFWriter.cs
+++ b/src/Discord.Net/ETF/ETFWriter.cs
@@ -1,4 +1,5 @@
-using System;
+using Newtonsoft.Json;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
@@ -12,6 +13,7 @@ namespace Discord.ETF
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[] _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[] _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
WriteNil();
}
+ public void Write(IEnumerable 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(IDictionary 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)
{
if (obj != null)
@@ -302,8 +343,12 @@ namespace Discord.ETF
//TODO: Add field/property names
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_1); //ETFWriter(this), obj
generator.Emit(OpCodes.Ldfld, f); //ETFWriter(this), obj.fieldValue
@@ -314,8 +359,12 @@ namespace Discord.ETF
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_1); //ETFWriter(this), obj
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 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
private bool _isDisposed = false;
diff --git a/src/Discord.Net/Net/Rest/ETFRestClient.cs b/src/Discord.Net/Net/Rest/ETFRestClient.cs
new file mode 100644
index 000000000..b52da1037
--- /dev/null
+++ b/src/Discord.Net/Net/Rest/ETFRestClient.cs
@@ -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 obj)
+ {
+ throw new NotImplementedException();
+ }
+ protected override T Deserialize(string json)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Discord.Net/Net/Rest/JsonRestClient.cs b/src/Discord.Net/Net/Rest/JsonRestClient.cs
new file mode 100644
index 000000000..37bb093ab
--- /dev/null
+++ b/src/Discord.Net/Net/Rest/JsonRestClient.cs
@@ -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 obj)
+ {
+ return JsonConvert.SerializeObject(obj);
+ }
+
+ protected override T Deserialize(string json)
+ {
+#if TEST_RESPONSES
+ if (string.IsNullOrEmpty(json))
+ throw new Exception("API check failed: Response is empty.");
+#endif
+ return JsonConvert.DeserializeObject(json, _deserializeSettings);
+ }
+ }
+}
diff --git a/src/Discord.Net/Net/Rest/RestClient.cs b/src/Discord.Net/Net/Rest/RestClient.cs
index 8e5ce180c..a32771e0d 100644
--- a/src/Discord.Net/Net/Rest/RestClient.cs
+++ b/src/Discord.Net/Net/Rest/RestClient.cs
@@ -1,14 +1,15 @@
using Discord.API;
+using Discord.ETF;
using Discord.Logging;
-using Newtonsoft.Json;
using System;
using System.Diagnostics;
+using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Discord.Net.Rest
{
- public partial class RestClient
+ public abstract partial class RestClient
{
private struct RestResults
{
@@ -32,8 +33,8 @@ namespace Discord.Net.Rest
private readonly DiscordConfig _config;
private readonly IRestEngine _engine;
+ private readonly ETFWriter _serializer;
private string _token;
- private JsonSerializerSettings _deserializeSettings;
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;
Logger = logger;
#if !DOTNET5_4
- _engine = new RestSharpEngine(config, baseUrl, logger);
+ _engine = new RestSharpEngine(config, baseUrl, logger);
#else
_engine = new BuiltInEngine(config, baseUrl, logger);
#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)
{
this.SentRequest += (s, e) =>
@@ -98,7 +90,7 @@ namespace Discord.Net.Rest
OnSendingRequest(request);
var results = await Send(request, true).ConfigureAwait(false);
- var response = DeserializeResponse(results.Response);
+ var response = Deserialize(results.Response);
OnSentRequest(request, response, results.Response, results.Milliseconds);
return response;
@@ -118,9 +110,8 @@ namespace Discord.Net.Rest
if (request == null) throw new ArgumentNullException(nameof(request));
OnSendingRequest(request);
- var requestJson = JsonConvert.SerializeObject(request.Payload);
var results = await SendFile(request, true).ConfigureAwait(false);
- var response = DeserializeResponse(results.Response);
+ var response = Deserialize(results.Response);
OnSentRequest(request, response, results.Response, results.Milliseconds);
return response;
@@ -139,7 +130,7 @@ namespace Discord.Net.Rest
object payload = request.Payload;
string requestJson = null;
if (payload != null)
- requestJson = JsonConvert.SerializeObject(payload);
+ requestJson = Serialize(payload);
Stopwatch stopwatch = Stopwatch.StartNew();
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);
}
- private T DeserializeResponse(string json)
- {
-#if TEST_RESPONSES
- if (string.IsNullOrEmpty(json))
- throw new Exception("API check failed: Response is empty.");
-#endif
- return JsonConvert.DeserializeObject(json, _deserializeSettings);
- }
+ protected abstract string Serialize(T obj);
+ protected abstract T Deserialize(string json);
}
}