/// <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"); } }
/// <summary> /// Invokes an operation method. /// </summary> /// <typeparam name="TT">The task return type.</typeparam> /// <param name="method">The method.</param> /// <param name="args">The arguments.</param> /// <param name="returnType">The real return type.</param> /// <returns></returns> protected virtual async Task <TT> InvokeOperationAsync <TT>(MethodInfo method, object[] args, Type returnType) { // build arguments Dictionary <string, object> argsPayload = new Dictionary <string, object>(); Dictionary <string, Type> argsTypes = new Dictionary <string, Type>(); 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, argsTypes); // serialize byte[] requestBody = new ProtobufRpcSerializer().SerializeRequest(req); RpcHeader header = new RpcHeader(RpcHeader.HEADER_VERSION, ProtobufRpcSerializer.SerializerName, RpcMessageType.Single); // create headers IDictionary <string, object> headers = new Dictionary <string, object>() { { RpcHeader.HEADER_NAME, header.ToString() }, { RpcHeader.HEADER_NAME_LEGACY, header.ToString() } }; // ask or send if (method.GetCustomAttribute <RpcOperationAttribute>().NoReply) { // send operation await _channel.SendAsync(new Message() { Body = requestBody, Headers = headers, TraceId = _configuration.TraceId }); return(default(TT)); } else { Envelope res = await _channel.AskAsync(new Message() { Body = requestBody, Headers = headers, TraceId = _configuration.TraceId }, _configuration.Timeout); // transform response byte[] responseBody = res.Body; // try and get response header if (!res.Headers.TryGetValue(RpcHeader.HEADER_NAME, out object resHeaderData)) { if (!res.Headers.TryGetValue(RpcHeader.HEADER_NAME_LEGACY, out resHeaderData)) { throw new InvalidOperationException("The response envelope is not a valid RPC message"); } } RpcHeader resHeader = new RpcHeader(Encoding.UTF8.GetString(resHeaderData as byte[])); // deserialize response if (!RpcSerializer.Serializers.TryGetValue(resHeader.Serializer, out IRpcSerializer deserializer)) { throw new NotSupportedException("The response serializer is not supported"); } // deserialize RpcResponse resPayload = null; if (method.ReturnType == typeof(Task)) { // deserialize resPayload = deserializer.DeserializeResponse(responseBody, typeof(void)); // return result if (resPayload.IsSuccess) { return((TT)(object)true); } } else { // deserialize Type taskType = method.ReturnType.GetGenericArguments()[0]; resPayload = deserializer.DeserializeResponse(responseBody, taskType); // return result if (resPayload.IsSuccess) { return((TT)resPayload.Data); } } // throw error throw new RpcException(resPayload.Error); } }