public static async void HandleMethodCall(RpcConnection connection, IRequest rpcCallRequest, object rpcTarget)
        {
            using (var deferral = rpcCallRequest.GetDeferral())
            {
                try
                {
                    if (rpcTarget == null)
                    {
                        await rpcCallRequest.SendResponseAsync(
                            RpcReturn.Faulted("No remote procedure calls are allowed on this connection"));
                    }

                    var call   = (RpcCall)rpcCallRequest.Message;
                    var result = await InvokeMethodAsync(rpcTarget, connection, call);

                    await rpcCallRequest.SendResponseAsync(result);
                }
                catch
                {
                    await rpcCallRequest.SendResponseAsync(RpcReturn.Faulted("Unknown error"));
                }
            }
        }
        public static async Task <RpcReturn> InvokeMethodAsync(object rpcTarget, RpcConnection connection, RpcCall call)
        {
            // Current limitations
            // - no support for overloaded methods

            if (string.IsNullOrEmpty(call.MethodName))
            {
                return(RpcReturn.Faulted("No method name specified"));
            }

            var method = rpcTarget.GetType().GetMethod(call.MethodName);

            if (method == null)
            {
                return(RpcReturn.Faulted(
                           $"No method with name '{call.MethodName}' could be found (attempted call: {call.ToString()})"));
            }

            // Parameter check
            var formalParams = method.GetParameters();
            var actualParams = call.Parameters.ToList();

            if (call.Parameters.Length > formalParams.Length)
            {
                return(RpcReturn.Faulted("Parameter mismatch: Too many arguments specified " +
                                         $"(attempted call: {call.ToString()}, local method: {method.ToDescriptionString()})"));
            }

            for (var i = 0; i < formalParams.Length; i++)
            {
                var formalParam = formalParams[i];
                var actualParam = (actualParams.Count <= i) ? null : actualParams[i];

                if (actualParam == null)
                {
                    actualParams.Add(actualParam = Type.Missing);
                }
                else if (!formalParam.ParameterType.IsAssignableFrom(actualParam.GetType()))
                {
                    return(RpcReturn.Faulted("Parameter mismatch: Got value of type " +
                                             $"'{actualParam.GetType().FullName}' for parameter " +
                                             $"'{formalParam.ParameterType.FullName} {formalParam.Name}' " +
                                             $"(attempted call: {call.ToString()}, local method: {method.ToDescriptionString()})"));
                }

                if (formalParam.CustomAttributes.Any(a => a.AttributeType == typeof(RpcCallerAttribute)) &&
                    formalParam.ParameterType.IsAssignableFrom(typeof(RpcConnection)))
                {
                    // Insert RpcProxy into [RpcCaller]-annotated parameter
                    actualParams[i] = connection;
                }
                else if (actualParam == Type.Missing && !formalParam.IsOptional)
                {
                    return(RpcReturn.Faulted("Parameter mismatch: Not enough parameters specified " +
                                             $"(attempted call: {call.ToString()}, local method: {method.ToDescriptionString()})"));
                }
            }

            // Invoke method
            try
            {
                var returnValue = method.Invoke(rpcTarget, actualParams.ToArray());

                if (returnValue is Task)
                {
                    await(Task) returnValue;

                    if (returnValue.GetType().GetGenericTypeDefinition() == typeof(Task <>) &&
                        returnValue.GetType().GetGenericArguments()[0].Name != "VoidTaskResult")
                    {
                        returnValue = ((dynamic)returnValue).Result;
                    }
                    else
                    {
                        returnValue = null;
                    }
                }

                return(RpcReturn.Success(returnValue));
            }
            catch (Exception e)
            {
                return(RpcReturn.Faulted($"Local execution failed (exception: {e.ToString()})"));
            }
        }