From 6a19d0dbd8fcc43da4ac6ea656f319cf6a85c468 Mon Sep 17 00:00:00 2001 From: Alan Doherty Date: Mon, 14 Jan 2019 15:00:37 +0000 Subject: [PATCH] Fixes for object type handling - Moved to 0.1.7.1 - Fixed that SetData in the protobuf serializer used the object type and not the actual type of the operation signature. This caused certain objects to be serialized as wire types and then fail to deserialize as a serialized type when received, resulting in null values --- src/Holon/Holon.csproj | 6 +-- src/Holon/Remoting/RpcBehaviour.cs | 4 +- src/Holon/Remoting/RpcProxy.cs | 4 +- src/Holon/Remoting/RpcRequest.cs | 14 +++++- src/Holon/Remoting/RpcResponse.cs | 15 +++++- .../Serializers/ProtobufRpcSerializer.cs | 50 ++++++++++--------- .../Remoting/Serializers/XmlRpcSerializer.cs | 6 ++- 7 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/Holon/Holon.csproj b/src/Holon/Holon.csproj index b4cd6cf..56e9126 100644 --- a/src/Holon/Holon.csproj +++ b/src/Holon/Holon.csproj @@ -2,7 +2,7 @@ netstandard1.6 - 0.1.7 + 0.1.7.1 Alan Doherty Alan Doherty A minimal service and event bus with additional support for RPC @@ -10,11 +10,11 @@ https://github.com/alandoherty/holon-net https://github.com/alandoherty/holon-net git - 0.1.7.0 + 0.1.7.1 https://github.com/alandoherty/holon-net/blob/master/LICENSE true https://s3-eu-west-1.amazonaws.com/assets.alandoherty.co.uk/github/holon-net-nuget.png - 0.1.7.0 + 0.1.7.1 diff --git a/src/Holon/Remoting/RpcBehaviour.cs b/src/Holon/Remoting/RpcBehaviour.cs index 73eb0be..2f112b4 100644 --- a/src/Holon/Remoting/RpcBehaviour.cs +++ b/src/Holon/Remoting/RpcBehaviour.cs @@ -223,12 +223,12 @@ private async Task ApplyRequestAsync(RpcRequest req, MemberInfo mem // check if the operation returns anything if (operationMethod.ReturnType == typeof(Task)) { - return new RpcResponse(null); + return new RpcResponse(null, typeof(void)); } else { // get result object realRes = methodResult.GetType().GetTypeInfo().GetProperty("Result").GetValue(methodResult); - return new RpcResponse(realRes); + return new RpcResponse(realRes, operationMethod.ReturnType); } } diff --git a/src/Holon/Remoting/RpcProxy.cs b/src/Holon/Remoting/RpcProxy.cs index b1d5fa8..b5ba1e7 100644 --- a/src/Holon/Remoting/RpcProxy.cs +++ b/src/Holon/Remoting/RpcProxy.cs @@ -109,14 +109,16 @@ protected override object Invoke(MethodInfo targetMethod, object[] args) { protected virtual async Task InvokeOperationAsync(MethodInfo method, object[] args, Type returnType) { // build arguments Dictionary argsPayload = new Dictionary(); + Dictionary argsTypes = new Dictionary(); ParameterInfo[] argsMethod = method.GetParameters(); for (int i = 0; i < args.Length; i++) { argsPayload[argsMethod[i].Name] = args[i]; + argsTypes[argsMethod[i].Name] = argsMethod[i].ParameterType; } // create request - RpcRequest req = new RpcRequest(_contractAttr.Name != null ? _contractAttr.Name : _typeInfo.Name, method.Name, argsPayload); + RpcRequest req = new RpcRequest(_contractAttr.Name != null ? _contractAttr.Name : _typeInfo.Name, method.Name, argsPayload, argsTypes); // serialize byte[] requestBody = new ProtobufRpcSerializer().SerializeRequest(req); diff --git a/src/Holon/Remoting/RpcRequest.cs b/src/Holon/Remoting/RpcRequest.cs index 81775dd..031bc57 100644 --- a/src/Holon/Remoting/RpcRequest.cs +++ b/src/Holon/Remoting/RpcRequest.cs @@ -13,6 +13,7 @@ public sealed class RpcRequest private string _interface; private string _operation; private Dictionary _arguments; + private Dictionary _argumentTypes; #endregion #region Properties @@ -42,6 +43,15 @@ public Dictionary Arguments { return _arguments; } } + + /// + /// Gets the types of the arguments. + /// + public Dictionary ArgumentTypes { + get { + return _argumentTypes; + } + } #endregion #region Methods @@ -54,10 +64,12 @@ public Dictionary Arguments { /// The target interface. /// The target operation. /// The arguments. - internal RpcRequest(string @interface, string operation, Dictionary arguments) { + /// The argument types. + internal RpcRequest(string @interface, string operation, Dictionary arguments, Dictionary argumentTypes) { _interface = @interface; _operation = operation; _arguments = arguments; + _argumentTypes = argumentTypes; } /// diff --git a/src/Holon/Remoting/RpcResponse.cs b/src/Holon/Remoting/RpcResponse.cs index 38e8f24..c4422e1 100644 --- a/src/Holon/Remoting/RpcResponse.cs +++ b/src/Holon/Remoting/RpcResponse.cs @@ -11,12 +11,13 @@ public sealed class RpcResponse { #region Fields private object _data; + private Type _dataType; private RpcError _error; #endregion #region Properties /// - /// Gets or sets the data. + /// Gets the data. /// public object Data { get { @@ -24,6 +25,15 @@ public object Data { } } + /// + /// Gets the data type. + /// + public Type DataType { + get { + return _dataType; + } + } + /// /// Gets the error, if any. /// @@ -48,7 +58,8 @@ public bool IsSuccess { /// Creates a new RPC response. /// /// The provided data. - internal RpcResponse(object data) { + /// The data type. + internal RpcResponse(object data, Type type) { _data = data; _error = null; } diff --git a/src/Holon/Remoting/Serializers/ProtobufRpcSerializer.cs b/src/Holon/Remoting/Serializers/ProtobufRpcSerializer.cs index 37111d4..54eaca5 100644 --- a/src/Holon/Remoting/Serializers/ProtobufRpcSerializer.cs +++ b/src/Holon/Remoting/Serializers/ProtobufRpcSerializer.cs @@ -31,6 +31,7 @@ public RpcRequest DeserializeRequest(byte[] body, RpcSignatureResolver resolver) // convert arguments Dictionary args = new Dictionary(); + Dictionary argsTypes = new Dictionary(); RpcArgument[] rpcArgs = resolver(msg.Interface, msg.Operation); if (msg.Arguments != null) { @@ -39,11 +40,14 @@ public RpcRequest DeserializeRequest(byte[] body, RpcSignatureResolver resolver) continue; // add argument - args.Add(arg.Key, arg.GetData(rpcArgs.Single(a => a.Name.Equals(arg.Key, StringComparison.CurrentCultureIgnoreCase)).Type)); + RpcArgument rpcArg = rpcArgs.Single(a => a.Name.Equals(arg.Key, StringComparison.CurrentCultureIgnoreCase)); + + args.Add(arg.Key, arg.GetData(rpcArg.Type)); + argsTypes.Add(arg.Key, rpcArg.Type); } } - return new RpcRequest(msg.Interface, msg.Operation, args); + return new RpcRequest(msg.Interface, msg.Operation, args, argsTypes); } } @@ -57,7 +61,7 @@ public RpcResponse DeserializeResponse(byte[] body, Type responseType) { ResponseMsg msg = Serializer.Deserialize(ms); if (msg.IsSuccess) - return new RpcResponse(msg.Data.GetData(responseType)); + return new RpcResponse(msg.Data.GetData(responseType), responseType); else return new RpcResponse(msg.Error.Code, msg.Error.Message); } @@ -73,9 +77,10 @@ public byte[] SerializeRequest(RpcRequest request) { RequestMsg req = new RequestMsg(); req.Interface = request.Interface; req.Operation = request.Operation; + req.Arguments = request.Arguments.Select(kv => { ValueMsg value = new ValueMsg() { Key = kv.Key }; - value.SetData(kv.Value); + value.SetData(kv.Value, request.ArgumentTypes[kv.Key]); return value; }).ToArray(); @@ -96,7 +101,7 @@ public byte[] SerializeResponse(RpcResponse response) { if (response.IsSuccess) { ValueMsg result = new ValueMsg(); - result.SetData(response.Data); + result.SetData(response.Data, response.DataType); res.Data = result; } else { res.Error = new ErrorMsg() { @@ -147,40 +152,39 @@ class ValueMsg /// Sets the data from a .NET type. /// /// The value. - public void SetData(object val) { + /// The type to serialize as. + public void SetData(object val, Type type) { if (val == null) Data = null; - else if (val is string) + else if (type == typeof(string)) Data = Encoding.UTF8.GetBytes((string)val); - else if (val is sbyte) + else if (type == typeof(sbyte)) Data = new byte[] { (byte)(sbyte)val }; - else if (val is short) + else if (type == typeof(short)) Data = BitConverter.GetBytes((short)val); - else if (val is int) + else if (type == typeof(int)) Data = BitConverter.GetBytes((int)val); - else if (val is long) + else if (type == typeof(long)) Data = BitConverter.GetBytes((long)val); - else if (val is byte) + else if (type == typeof(byte)) Data = new byte[] { (byte)val }; - else if (val is ushort) + else if (type == typeof(ushort)) Data = BitConverter.GetBytes((ushort)val); - else if (val is uint) + else if (type == typeof(uint)) Data = BitConverter.GetBytes((uint)val); - else if (val is ulong) + else if (type == typeof(ulong)) Data = BitConverter.GetBytes((ulong)val); - else if (val is double) + else if (type == typeof(double)) Data = BitConverter.GetBytes((double)val); - else if (val is bool) + else if (type == typeof(bool)) Data = new byte[] { (bool)val ? (byte)1 : (byte)0 }; - else if (val == null) - Data = null; - else if (val is Guid) + else if (type == typeof(Guid)) Data = ((Guid)val).ToByteArray(); - else if (val is ServiceAddress) + else if (type == typeof(ServiceAddress)) Data = Encoding.UTF8.GetBytes(((ServiceAddress)val).ToString()); - else if (val is byte[]) + else if (type == typeof(byte[])) Data = ((byte[])val); - else if (val is DateTime) + else if (type == typeof(DateTime)) Data = Encoding.UTF8.GetBytes(((DateTime)val).ToString()); else { TypeInfo typeInfo = val.GetType().GetTypeInfo(); diff --git a/src/Holon/Remoting/Serializers/XmlRpcSerializer.cs b/src/Holon/Remoting/Serializers/XmlRpcSerializer.cs index 1a04bc9..30c888e 100644 --- a/src/Holon/Remoting/Serializers/XmlRpcSerializer.cs +++ b/src/Holon/Remoting/Serializers/XmlRpcSerializer.cs @@ -37,6 +37,7 @@ public RpcRequest DeserializeRequest(byte[] body, RpcSignatureResolver resolver) // read arguments XElement arguments = req.Elements().SingleOrDefault((e) => e.Name == "Arguments"); Dictionary argumentsData = new Dictionary(); + Dictionary argumentTypes = new Dictionary(); if (arguments != null) { // resolve arguments @@ -52,11 +53,12 @@ public RpcRequest DeserializeRequest(byte[] body, RpcSignatureResolver resolver) if (argsMap.TryGetValue(argElement.Name.LocalName, out RpcArgument arg)) { argumentsData[arg.Name] = DeserializeValue(arg.Type, argElement); + argumentTypes[arg.Name] = arg.Type; } } } - return new RpcRequest(iface.Value, op.Value, argumentsData); + return new RpcRequest(iface.Value, op.Value, argumentsData, argumentTypes); } } @@ -128,7 +130,7 @@ public RpcResponse DeserializeResponse(byte[] body, Type dataType) { XElement data = req.Element(XName.Get("Data")); if (data != null) { - return new RpcResponse(DeserializeValue(dataType, data)); + return new RpcResponse(DeserializeValue(dataType, data), dataType); } else { // find the error element then XElement err = req.Element(XName.Get("Error"));