From 7ee10e4a6d4374d9545302058e88906307ec7ed7 Mon Sep 17 00:00:00 2001 From: Andreas Mueller Date: Fri, 5 Aug 2022 17:21:31 +0200 Subject: [PATCH] Adding IPNetwork to Converter/Formatters --- AMWD.Common/Converters/IPAddressConverter.cs | 2 +- AMWD.Common/Converters/IPNetworkConverter.cs | 93 +++++++++++++++++++ .../Formatters/IPAddressArrayFormatter.cs | 11 +-- .../Formatters/IPAddressListFormatter.cs | 9 +- .../Formatters/IPNetworkArrayFormatter.cs | 74 +++++++++++++++ AMWD.Common/Formatters/IPNetworkFormatter.cs | 59 ++++++++++++ .../Formatters/IPNetworkListFormatter.cs | 67 +++++++++++++ AMWD.Common/Utilities/NetworkHelper.cs | 6 ++ CHANGELOG.md | 6 +- 9 files changed, 310 insertions(+), 17 deletions(-) create mode 100644 AMWD.Common/Converters/IPNetworkConverter.cs create mode 100644 AMWD.Common/Formatters/IPNetworkArrayFormatter.cs create mode 100644 AMWD.Common/Formatters/IPNetworkFormatter.cs create mode 100644 AMWD.Common/Formatters/IPNetworkListFormatter.cs diff --git a/AMWD.Common/Converters/IPAddressConverter.cs b/AMWD.Common/Converters/IPAddressConverter.cs index 184fead..94987e2 100644 --- a/AMWD.Common/Converters/IPAddressConverter.cs +++ b/AMWD.Common/Converters/IPAddressConverter.cs @@ -69,7 +69,7 @@ namespace Newtonsoft.Json if (value is List addrList) str = string.Join(";", addrList.Select(ip => ip.ToString())); - if (value is List addrEnum) + if (value is IEnumerable addrEnum) str = string.Join(";", addrEnum.Select(ip => ip.ToString())); writer.WriteValue(str); diff --git a/AMWD.Common/Converters/IPNetworkConverter.cs b/AMWD.Common/Converters/IPNetworkConverter.cs new file mode 100644 index 0000000..f48f45b --- /dev/null +++ b/AMWD.Common/Converters/IPNetworkConverter.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.HttpOverrides; + +namespace Newtonsoft.Json +{ + /// + /// Converts an from and to JSON. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public class IPNetworkConverter : JsonConverter + { + /// + /// List of known types to use this converver. + /// + public static readonly Type[] KnownTypes = new[] + { + typeof(IPNetwork), + typeof(IPNetwork[]), + typeof(List), + typeof(IEnumerable) + }; + + /// + public override bool CanConvert(Type objectType) + { + return KnownTypes.Contains(objectType); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.Value == null) + return null; + + string str = (string)reader.Value; + + if (typeof(IPNetwork) == objectType) + return Parse(str); + + var networks = str.Split(';').Select(s => Parse(s)); + + if (typeof(IPNetwork[]) == objectType) + return networks.ToArray(); + + if (typeof(List) == objectType) + return networks.ToList(); + + return networks; + } + + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + string str = null; + if (value == null) + { + writer.WriteValue(str); + return; + } + + if (value is IPNetwork net) + str = ToString(net); + + if (value is IPNetwork[] netArray) + str = string.Join(";", netArray.Select(n => ToString(n))); + + if (value is List netList) + str = string.Join(";", netList.Select(n => ToString(n))); + + if (value is IEnumerable netEnum) + str = string.Join(";", netEnum.Select(n => ToString(n))); + + writer.WriteValue(str); + } + + private string ToString(IPNetwork net) + { + return $"{net.Prefix}/{net.PrefixLength}"; + } + + private IPNetwork Parse(string str) + { + string[] parts = str.Split('/'); + var prefix = IPAddress.Parse(parts.First()); + int prefixLength = int.Parse(parts.Last()); + + return new IPNetwork(prefix, prefixLength); + } + } +} diff --git a/AMWD.Common/Formatters/IPAddressArrayFormatter.cs b/AMWD.Common/Formatters/IPAddressArrayFormatter.cs index 4b15b56..6e9a119 100644 --- a/AMWD.Common/Formatters/IPAddressArrayFormatter.cs +++ b/AMWD.Common/Formatters/IPAddressArrayFormatter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using AMWD.Common.Utilities; namespace MessagePack.Formatters { @@ -22,7 +23,7 @@ namespace MessagePack.Formatters byte[] buffer = bytes.Skip(bytePos).Take(sizeof(int)).ToArray(); bytePos += sizeof(int); - Swap(buffer); + NetworkHelper.SwapBigEndian(buffer); int length = BitConverter.ToInt32(buffer, 0); int arrayPos = 0; @@ -55,7 +56,7 @@ namespace MessagePack.Formatters int length = value.Length; byte[] buffer = BitConverter.GetBytes(length); - Swap(buffer); + NetworkHelper.SwapBigEndian(buffer); bytes.AddRange(buffer); foreach (var ip in value) @@ -67,11 +68,5 @@ namespace MessagePack.Formatters options.Resolver.GetFormatterWithVerify().Serialize(ref writer, bytes.ToArray(), options); } - - private void Swap(byte[] bytes) - { - if (BitConverter.IsLittleEndian) - Array.Reverse(bytes); - } } } diff --git a/AMWD.Common/Formatters/IPAddressListFormatter.cs b/AMWD.Common/Formatters/IPAddressListFormatter.cs index 7eab684..0a750bb 100644 --- a/AMWD.Common/Formatters/IPAddressListFormatter.cs +++ b/AMWD.Common/Formatters/IPAddressListFormatter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using AMWD.Common.Utilities; namespace MessagePack.Formatters { @@ -50,7 +51,7 @@ namespace MessagePack.Formatters int length = value.Count; byte[] buffer = BitConverter.GetBytes(length); - Swap(buffer); + NetworkHelper.SwapBigEndian(buffer); bytes.AddRange(buffer); foreach (var ip in value) @@ -62,11 +63,5 @@ namespace MessagePack.Formatters options.Resolver.GetFormatterWithVerify().Serialize(ref writer, bytes.ToArray(), options); } - - private void Swap(byte[] bytes) - { - if (BitConverter.IsLittleEndian) - Array.Reverse(bytes); - } } } diff --git a/AMWD.Common/Formatters/IPNetworkArrayFormatter.cs b/AMWD.Common/Formatters/IPNetworkArrayFormatter.cs new file mode 100644 index 0000000..9866b1f --- /dev/null +++ b/AMWD.Common/Formatters/IPNetworkArrayFormatter.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AMWD.Common.Utilities; +using MessagePack; +using MessagePack.Formatters; +using Microsoft.AspNetCore.HttpOverrides; + +namespace AMWD.Common.Formatters +{ + /// + /// Serialization of an array to and from . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public class IPNetworkArrayFormatter : IMessagePackFormatter + { + /// + public IPNetwork[] Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.IsNil) + return null; + + int bytePos = 0; + byte[] bytes = options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + byte[] buffer = bytes.Skip(bytePos).Take(sizeof(int)).ToArray(); + bytePos += sizeof(int); + + NetworkHelper.SwapBigEndian(buffer); + int length = BitConverter.ToInt32(buffer, 0); + + int arrayPos = 0; + var array = new IPNetwork[length]; + while (bytePos < bytes.Length) + { + byte len = bytes.Skip(bytePos).First(); + bytePos++; + + buffer = bytes.Skip(bytePos).Take(len).ToArray(); + bytePos += len; + + array[arrayPos] = IPNetworkFormatter.DeserializeInternal(buffer); + arrayPos++; + } + + return array; + } + + /// + public void Serialize(ref MessagePackWriter writer, IPNetwork[] value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + return; + } + + var bytes = new List(); + + int length = value.Length; + byte[] buffer = BitConverter.GetBytes(length); + NetworkHelper.SwapBigEndian(buffer); + bytes.AddRange(buffer); + + foreach (var network in value) + { + buffer = IPNetworkFormatter.SerializeInternal(network); + bytes.Add((byte)buffer.Length); + bytes.AddRange(buffer); + } + + options.Resolver.GetFormatterWithVerify().Serialize(ref writer, bytes.ToArray(), options); + } + } +} diff --git a/AMWD.Common/Formatters/IPNetworkFormatter.cs b/AMWD.Common/Formatters/IPNetworkFormatter.cs new file mode 100644 index 0000000..9c408d9 --- /dev/null +++ b/AMWD.Common/Formatters/IPNetworkFormatter.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.HttpOverrides; + +namespace MessagePack.Formatters +{ + /// + /// Serialization of an to and from . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public class IPNetworkFormatter : IMessagePackFormatter + { + /// + public IPNetwork Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.IsNil) + return null; + + byte[] bytes = options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + return DeserializeInternal(bytes); + } + + /// + public void Serialize(ref MessagePackWriter writer, IPNetwork value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + return; + } + + byte[] bytes = SerializeInternal(value); + options.Resolver.GetFormatterWithVerify().Serialize(ref writer, bytes, options); + } + + internal static byte[] SerializeInternal(IPNetwork network) + { + // IP network prefix has a maximum of 128 bit - therefore the length can be covered with a byte. + byte prefixLength = (byte)network.PrefixLength; + byte[] prefixBytes = network.Prefix.GetAddressBytes(); + + byte[] bytes = new byte[prefixBytes.Length + 1]; + bytes[0] = prefixLength; + Array.Copy(prefixBytes, 0, bytes, 1, prefixBytes.Length); + + return bytes; + } + + internal static IPNetwork DeserializeInternal(byte[] bytes) + { + byte prefixLength = bytes[0]; + byte[] prefixBytes = bytes.Skip(1).ToArray(); + + var prefix = new IPAddress(prefixBytes); + return new IPNetwork(prefix, prefixLength); + } + } +} diff --git a/AMWD.Common/Formatters/IPNetworkListFormatter.cs b/AMWD.Common/Formatters/IPNetworkListFormatter.cs new file mode 100644 index 0000000..79b3499 --- /dev/null +++ b/AMWD.Common/Formatters/IPNetworkListFormatter.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AMWD.Common.Utilities; +using Microsoft.AspNetCore.HttpOverrides; + +namespace MessagePack.Formatters +{ + /// + /// Serialization of an list to and from . + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public class IPNetworkListFormatter : IMessagePackFormatter> + { + /// + public List Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.IsNil) + return null; + + byte[] bytes = options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); + + // skipping the length information + int bytePos = sizeof(int); + + var list = new List(); + while (bytePos < bytes.Length) + { + byte len = bytes.Skip(bytePos).First(); + bytePos++; + + byte[] buffer = bytes.Skip(bytePos).Take(len).ToArray(); + bytePos += len; + + list.Add(IPNetworkFormatter.DeserializeInternal(buffer)); + } + + return list; + } + + /// + public void Serialize(ref MessagePackWriter writer, List value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + return; + } + + var bytes = new List(); + + int length = value.Count; + byte[] buffer = BitConverter.GetBytes(length); + NetworkHelper.SwapBigEndian(buffer); + bytes.AddRange(buffer); + + foreach (var network in value) + { + buffer = IPNetworkFormatter.SerializeInternal(network); + bytes.Add((byte)buffer.Length); + bytes.AddRange(buffer); + } + + options.Resolver.GetFormatterWithVerify().Serialize(ref writer, bytes.ToArray(), options); + } + } +} diff --git a/AMWD.Common/Utilities/NetworkHelper.cs b/AMWD.Common/Utilities/NetworkHelper.cs index 33dcd99..7e6ac9f 100644 --- a/AMWD.Common/Utilities/NetworkHelper.cs +++ b/AMWD.Common/Utilities/NetworkHelper.cs @@ -163,5 +163,11 @@ namespace AMWD.Common.Utilities .Where(a => addressFamily == AddressFamily.Unspecified || a.AddressFamily == addressFamily) .OrderBy(a => a.AddressFamily); } + + internal static void SwapBigEndian(byte[] array) + { + if (BitConverter.IsLittleEndian) + Array.Reverse(array); + } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index aa48bfa..e07cd74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Converters for `Newtonsoft.Json` - `ByteArrayHexConverter` to get an byte array as hex string instead of Base64 - - `IPAddressConverter` to make `System.Net.IPAdress`es (and list forms) serializable. + - `IPAddressConverter` to make `System.Net.IPAdress`es (and list forms) serializable + - `IPNetworkConverter` to make `Microsoft.AspNetCore.HttpOverrides.IPNetwork` (and list forms) serializable - Formatters for `MessagePack` - `IPAddressFormatter` to serialize an `System.Net.IPAddress` - `IPAddressArrayFormatter` to serialize arrays of `System.Net.IPAddress`es - `IPAddressListFormatter` to serialize lists of `System.Net.IPAddress`es + - `IPNetworkFormatter` to serialize an `Microsoft.AspNetCore.HttpOverrides.IPNetwork` + - `IPNetworkArrayFormatter` to serialize arrays of `Microsoft.AspNetCore.HttpOverrides.IPNetwork` + - `IPNetworkListFormatter` to serialize lists of `Microsoft.AspNetCore.HttpOverrides.IPNetwork` ## [v1.7.0](https://git.am-wd.de/AM.WD/common/compare/v1.6.1...v1.7.0) - 2022-07-24