/// <summary> /// Handles invokations on the proxy. /// </summary> /// <param name="targetMethod">The target method.</param> /// <param name="args">The arguments.</param> /// <returns></returns> protected override object Invoke(MethodInfo targetMethod, object[] args) { // get operation attribute RpcOperationAttribute attr = targetMethod.GetCustomAttribute <RpcOperationAttribute>(); if (attr == null) { throw new InvalidOperationException("The interface member must be decorated with an operation attribute"); } // check encryption requirement if ((_contractAttr.RequireEncryption || attr.RequireEncryption) && !_channel.IsEncrypted) { throw new SecurityException("The contract or operation requires an encrypted channel"); } // generate generic method Type genericType = typeof(bool); Type memberType = targetMethod.ReturnType; TypeInfo memberTypeInfo = memberType.GetTypeInfo(); //TODO: add support for sync methods if (memberType != typeof(Task) && memberTypeInfo.BaseType != typeof(Task)) { throw new InvalidOperationException("The interface member must return an awaitable task"); } if (memberTypeInfo.IsGenericType) { if (attr.NoReply) { throw new InvalidOperationException("The method result cannot be retrieved with no reply on"); } genericType = memberTypeInfo.GetGenericArguments()[0]; } // invoke return(_invokeMethodInfo.MakeGenericMethod(genericType).Invoke(this, new object[] { targetMethod, args, targetMethod.ReturnType })); }
/// <summary> /// Takes the parsed header and envelope and applies correct type handler. /// </summary> /// <param name="header">The header.</param> /// <param name="envelope">The envelope.</param> /// <returns></returns> private async Task ApplyAsync(RpcHeader header, Envelope envelope) { if (header.Type == RpcMessageType.Single) { // find serializer for this request RpcRequest req = null; IRpcSerializer serializer = null; if (!RpcSerializer.Serializers.TryGetValue(header.Serializer, out serializer)) { throw new NotSupportedException("The serializer is not supported by RPC"); } // deserialize into a request RpcResponse res = null; try { req = serializer.DeserializeRequest(envelope.Body, (i, o) => RpcArgument.FromMember(GetMember(i, o))); } catch (KeyNotFoundException) { res = new RpcResponse("InterfaceNotFound", "The interface or operation could not be found", null); } catch (Exception ex) { res = new RpcResponse("ArgumentInvalid", string.Format("The request format is invalid: {0}", ex.Message), null); } // apply single request MemberInfo memberInfo = null; try { if (req != null) { memberInfo = GetMember(req.Interface, req.Operation); } } catch (KeyNotFoundException) { res = new RpcResponse("OperationNotFound", "The interface or operation could not be found", null); } // get operation information RpcOperationAttribute opAttr = null; if (memberInfo != null) { Type interfaceType = GetInterface(req.Interface); MemberInfo[] interfaceMember = interfaceType.GetMember(req.Operation); opAttr = interfaceMember[0].GetCustomAttribute <RpcOperationAttribute>(); } // get if no reply is enabled bool noReply = opAttr != null && opAttr.NoReply; // check if they have a response ID if no reply isn't enabled if (!noReply && envelope.ID == Guid.Empty) { res = new RpcResponse("InvalidOperation", "The envelope does not specify a correlation ID", null); } // apply request if we don't have a response already, typically an error if (res == null) { // setup context RpcContext.Current = new RpcContext() { Envelope = envelope }; // apply request res = await ApplyRequestAsync(req, memberInfo).ConfigureAwait(false); // destroy context RpcContext.Current = null; } // send response unless no-reply if (!noReply) { // serialize response byte[] resBody = serializer.SerializeResponse(res); // send reply string rpcHeader = new RpcHeader(RpcHeader.HEADER_VERSION, serializer.Name, RpcMessageType.Single).ToString(); Dictionary <string, object> headers = new Dictionary <string, object>(StringComparer.CurrentCultureIgnoreCase) { { RpcHeader.HEADER_NAME, rpcHeader }, { RpcHeader.HEADER_NAME_LEGACY, rpcHeader } }; // reply await envelope.ReplyAsync(resBody, headers).ConfigureAwait(false); } // throw exceptions } else { throw new NotImplementedException("Batched RPC is not supported currently"); } }