public object DecodeRpcArgument(IRpcChannel channel, IRpcObjectRepository localRepository, IRpcObjectRepository remoteRepository, IRpcSerializer serializer, RpcArgument argument, Type argumentType) { switch (argument.Type) { case RpcType.Builtin: if (argument.Value == null || argumentType == typeof(void)) { return(null); } if (argumentType.IsAssignableFrom(argument.Value.GetType())) { return(argument.Value); } return(serializer.ChangeType(argument.Value, argumentType)); case RpcType.Proxy: var instanceId = (int)serializer.ChangeType(argument.Value, typeof(int)); var instance = localRepository.GetInstance(instanceId); if (instance == null) { var intfTypes = localRepository.ResolveTypes(argument.TypeId, argumentType); instance = remoteRepository.GetProxyObject(channel, intfTypes, instanceId); } return(instance); case RpcType.Serialized: var type = localRepository.ResolveTypes(argument.TypeId, argumentType)[0]; return(serializer.ChangeType(argument.Value, type)); case RpcType.ObjectArray: var arrayType = localRepository.ResolveTypes(argument.TypeId, argumentType)[0]; var elementType = arrayType?.GetElementType() ?? throw new InvalidOperationException(); var array = Array.CreateInstance(elementType, (int)argument.Value); for (int i = 0; i < array.Length; i++) { array.SetValue(DecodeRpcArgument(channel, localRepository, remoteRepository, serializer, argument.ArrayElements[i], elementType), i); } return(array); default: throw new InvalidDataException(); } }
protected bool HandleRemoteMessage(TChannel channel, byte[] data, RpcMessage msg) { switch (msg.Type) { case RpcMessageType.CallMethod: { Exception resultException = null; RpcCallResultMessage resultMessage; IRpcServerContextObject remoteRpcServerContextObject = null; var m = msg as RpcMethodCallMessage ?? Serializer.DeserializeMessage <RpcMethodCallMessage>(data); try { LogTrace($"Received method call '{m.MethodName}' with instance id '{m.InstanceId}'"); var obj = LocalRepository.GetInstance(m.InstanceId); MethodInfo targetMethod; try { targetMethod = obj.GetType().GetMethod(m.MethodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); } catch (AmbiguousMatchException) { targetMethod = obj.GetType() .GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) .SingleOrDefault(fm => fm.Name == m.MethodName && fm.GetParameters().Length == m.Arguments.Length); } if (targetMethod == null) { targetMethod = FindExplicitInterfaceImplementation(obj.GetType(), m.MethodName); } if (targetMethod == null) { throw new MissingMethodException(obj.GetType().FullName, m.MethodName); } LogTrace($"Resolved method '{targetMethod}' on object '{obj.GetType()}'"); var targetParameterTypes = targetMethod.GetParameters().Select(p => p.ParameterType).ToArray(); var args = new object[m.Arguments.Length]; var remoteRepository = GetRemoteRepository(channel); var rpcChannel = GetRpcChannelForClient(channel); for (int i = 0; i < m.Arguments.Length; i++) { args[i] = MessageFactory.DecodeRpcArgument(rpcChannel, LocalRepository, remoteRepository, Serializer, m.Arguments[i], targetParameterTypes[i]); } remoteRpcServerContextObject = obj as IRpcServerContextObject; if (remoteRpcServerContextObject != null) { LogTrace($"Object {m.InstanceId} implements IRpcServerContextObject, setting context"); remoteRpcServerContextObject.RpcChannel = channel; } var result = targetMethod.Invoke(obj, args); LogTrace("Method called without exception."); resultMessage = MessageFactory.CreateCallResultMessage(channel, LocalRepository, m, targetMethod, result); } catch (TargetInvocationException ex) { LogTrace($"Method call resulted in exception: {ex}"); resultException = ex.InnerException; resultMessage = MessageFactory.CreateExceptionResultMessage(m, ex.InnerException); } catch (Exception ex) { _logger?.LogError($"Failed to process message call: {ex}"); resultException = ex; resultMessage = MessageFactory.CreateExceptionResultMessage(m, ex); } finally { if (remoteRpcServerContextObject != null) { LogTrace($"Object {m.InstanceId} implements IRpcServerContextObject, removing context"); remoteRpcServerContextObject.RpcChannel = null; } } LogTrace("Serializing response."); byte[] response; try { response = Serializer.SerializeMessage(resultMessage); } catch (Exception ex) { if (resultMessage.Type == RpcMessageType.Exception) { _logger?.LogError(ex, $"Failed to serialize result exception of type '{resultException?.GetType()}'"); // if the exception is not serializable, do our best resultMessage = MessageFactory.CreateExceptionResultMessage(m, new Exception(resultException?.Message ?? "Internal Error")); response = Serializer.SerializeMessage(resultMessage); } else { throw; } } LogTrace("Sending response."); SendMessage(channel.GetStream(), response); LogTrace("Sent response"); return(true); } case RpcMessageType.RemoveInstance: { var m = Serializer.DeserializeMessage <RpcRemoveInstanceMessage>(data); LogTrace($"Removing instance '{m.InstanceId}'"); LocalRepository.RemoveInstance(m.InstanceId); var response = Serializer.SerializeMessage(new RpcMessage { CallId = m.CallId, Type = RpcMessageType.Ok }); SendMessage(channel.GetStream(), response); return(true); } } return(false); }