Browse Source

Finished (probably?) ETF serialization, started spliting Rest into ETFRestClient and JsonRestClient.

tags/docs-0.9
RogueException 9 years ago
parent
commit
11cf3beae0
7 changed files with 160 additions and 32 deletions
  1. +1
    -1
      src/Discord.Net.Audio/AudioClient.cs
  2. +6
    -0
      src/Discord.Net.Net45/Discord.Net.csproj
  3. +2
    -2
      src/Discord.Net/DiscordClient.cs
  4. +76
    -3
      src/Discord.Net/ETF/ETFWriter.cs
  5. +27
    -0
      src/Discord.Net/Net/Rest/ETFRestClient.cs
  6. +37
    -0
      src/Discord.Net/Net/Rest/JsonRestClient.cs
  7. +11
    -26
      src/Discord.Net/Net/Rest/RestClient.cs

+ 1
- 1
src/Discord.Net.Audio/AudioClient.cs View File

@@ -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) =>
{ {


+ 6
- 0
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -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>


+ 2
- 2
src/Discord.Net/DiscordClient.cs View File

@@ -135,8 +135,8 @@ namespace Discord
}; };


//Networking //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 = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway"));
GatewaySocket.Connected += (s, e) => GatewaySocket.Connected += (s, e) =>
{ {


+ 76
- 3
src/Discord.Net/ETF/ETFWriter.cs View File

@@ -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
- 0
src/Discord.Net/Net/Rest/ETFRestClient.cs View 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
- 0
src/Discord.Net/Net/Rest/JsonRestClient.cs View 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);
}
}
}

+ 11
- 26
src/Discord.Net/Net/Rest/RestClient.cs View File

@@ -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)
{
#if TEST_RESPONSES
if (string.IsNullOrEmpty(json))
throw new Exception("API check failed: Response is empty.");
#endif
return JsonConvert.DeserializeObject<T>(json, _deserializeSettings);
}
protected abstract string Serialize<T>(T obj);
protected abstract T Deserialize<T>(string json);
} }
} }

Loading…
Cancel
Save