System.Text.Json deserialization is incompatible with this usage

This commit is contained in:
Jöran Malek 2024-01-14 00:14:32 +01:00
parent af6a02b6dc
commit 854b51b46b
3 changed files with 77 additions and 29 deletions

View file

@ -1,21 +1,12 @@
using System.Text.Json.Serialization;
namespace pdns_dhcp.PowerDns; namespace pdns_dhcp.PowerDns;
public interface IMethod; public interface IMethod;
[JsonPolymorphic(TypeDiscriminatorPropertyName = "method")] public record MethodBase(string Method);
[JsonDerivedType(typeof(InitializeMethod), "initialize")]
[JsonDerivedType(typeof(LookupMethod), "lookup")]
public class Method;
public abstract class Method<TParam>(TParam parameters) : Method public abstract record Method<TParam>(string Method, TParam Parameters) : MethodBase(Method)
where TParam : MethodParameters where TParam : MethodParameters;
{
[JsonPropertyName("parameters")]
public TParam Parameters => parameters;
}
public class InitializeMethod(InitializeParameters parameters) : Method<InitializeParameters>(parameters), IMethod; public record InitializeMethod(InitializeParameters Parameters) : Method<InitializeParameters>("initialize", Parameters), IMethod;
public class LookupMethod(LookupParameters parameters) : Method<LookupParameters>(parameters), IMethod; public record LookupMethod(LookupParameters Parameters) : Method<LookupParameters>("lookup", Parameters), IMethod;

View file

@ -1,4 +1,5 @@
using System.Buffers; using System.Buffers;
using System.Collections.ObjectModel;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text.Json; using System.Text.Json;
@ -15,9 +16,34 @@ namespace pdns_dhcp.PowerDns;
public class PowerDnsHandler : ConnectionHandler public class PowerDnsHandler : ConnectionHandler
{ {
delegate MethodBase? HandlerConverter(in JsonElement element);
private static readonly ReadOnlyDictionary<string, HandlerConverter> Converters;
private readonly ILogger<PowerDnsHandler> _logger; private readonly ILogger<PowerDnsHandler> _logger;
private readonly DnsRepository _repository; private readonly DnsRepository _repository;
static PowerDnsHandler()
{
Dictionary<string, HandlerConverter> converters = new(StringComparer.OrdinalIgnoreCase)
{
["initialize"] = ToInitialize,
["lookup"] = ToLookup
};
Converters = converters.AsReadOnly();
static InitializeMethod ToInitialize(in JsonElement element)
{
return new(element.Deserialize(PowerDnsSerializerContext.Default.InitializeParameters)!);
}
static LookupMethod ToLookup(in JsonElement element)
{
return new(element.Deserialize(PowerDnsSerializerContext.Default.LookupParameters)!);
}
}
public PowerDnsHandler(DnsRepository repository, ILogger<PowerDnsHandler> logger) public PowerDnsHandler(DnsRepository repository, ILogger<PowerDnsHandler> logger)
{ {
_logger = logger; _logger = logger;
@ -41,22 +67,43 @@ public class PowerDnsHandler : ConnectionHandler
foreach (var memory in read.Buffer) foreach (var memory in read.Buffer)
{ {
buffer.Write(memory.Span); buffer.Write(memory.Span);
if (ConsumeJson(buffer, json, ref state)) if (!ConsumeJson(buffer, json, ref state))
{
continue;
}
MethodBase method;
try
{
using var jsonDocument = JsonDocument.Parse(json.WrittenMemory);
var root = jsonDocument.RootElement;
if (!root.TryGetProperty("method", out var methodElement))
{
continue;
}
if (Parse(methodElement, root.GetProperty("parameters")) is not { } methodLocal)
{
continue;
}
method = methodLocal;
}
finally
{ {
var method = JsonSerializer.Deserialize(json.WrittenSpan, PowerDnsSerializerContext.Default.Method)!;
json.Clear(); json.Clear();
state = default; state = default;
Reply reply = BoolReply.False;
try
{
reply = await Handle(method, connection.ConnectionClosed).ConfigureAwait(false);
}
catch (Exception e) { }
await JsonSerializer.SerializeAsync(writer, reply, PowerDnsSerializerContext.Default.Reply, connection.ConnectionClosed)
.ConfigureAwait(continueOnCapturedContext: false);
} }
Reply reply = BoolReply.False;
try
{
reply = await Handle(method, connection.ConnectionClosed).ConfigureAwait(false);
}
catch (Exception e) { }
await JsonSerializer.SerializeAsync(writer, reply, PowerDnsSerializerContext.Default.Reply, connection.ConnectionClosed)
.ConfigureAwait(continueOnCapturedContext: false);
} }
input.AdvanceTo(read.Buffer.End); input.AdvanceTo(read.Buffer.End);
@ -111,9 +158,20 @@ public class PowerDnsHandler : ConnectionHandler
return final; return final;
} }
static MethodBase? Parse(in JsonElement element, in JsonElement parameters)
{
HandlerConverter? converter = default;
return element.GetString() switch
{
null => null,
{ } methodName when !Converters.TryGetValue(methodName, out converter) => new MethodBase(methodName),
_ => converter(parameters)
};
}
} }
private ValueTask<Reply> Handle(Method method, CancellationToken cancellationToken = default) private ValueTask<Reply> Handle(MethodBase method, CancellationToken cancellationToken = default)
{ {
return method switch return method switch
{ {
@ -123,7 +181,7 @@ public class PowerDnsHandler : ConnectionHandler
_ => LogUnhandled(_logger, method) _ => LogUnhandled(_logger, method)
}; };
static ValueTask<Reply> LogUnhandled(ILogger logger, Method method) static ValueTask<Reply> LogUnhandled(ILogger logger, MethodBase method)
{ {
logger.LogWarning("Unhandled Method {Method}", method); logger.LogWarning("Unhandled Method {Method}", method);
return ValueTask.FromResult<Reply>(BoolReply.False); return ValueTask.FromResult<Reply>(BoolReply.False);

View file

@ -3,7 +3,6 @@ using System.Text.Json.Serialization;
namespace pdns_dhcp.PowerDns; namespace pdns_dhcp.PowerDns;
[JsonSerializable(typeof(Reply))] [JsonSerializable(typeof(Reply))]
[JsonSerializable(typeof(Method))]
[JsonSerializable(typeof(Parameters))] [JsonSerializable(typeof(Parameters))]
[JsonSourceGenerationOptions( [JsonSourceGenerationOptions(
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,