/// <summary>
        /// Handles a request message received from a remote endpoint.
        /// </summary>
        /// <param name="request">The request message.</param>
        public async void HandleRequest(PipeRequest request)
        {
            try
            {
                PipeResponse response = await this.HandleRequestAsync(request).ConfigureAwait(false);

                await this.pipeStreamWrapper.SendResponseAsync(response, CancellationToken.None).ConfigureAwait(false);
            }
            catch (Exception)
            {
                // If the pipe has closed and can't hear the response, we can't let the other end know about it, so we just eat the exception.
            }
        }
        /// <summary>
        /// Gets a pipe response for the given pipe request.
        /// </summary>
        /// <param name="request">The request to send.</param>
        /// <param name="cancellationToken">A token to cancel the request.</param>
        /// <returns>The pipe response.</returns>
        private async Task <PipeResponse> GetResponseAsync(PipeRequest request, CancellationToken cancellationToken)
        {
            var pendingCall = new PendingCall();

            this.pendingCalls.Add(request.CallId, pendingCall);

            await this.pipeStreamWrapper.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);

            cancellationToken.Register(
                () =>
            {
                pendingCall.TaskCompletionSource.TrySetException(new OperationCanceledException("Request has been canceled."));
            },
                false);

            return(await pendingCall.TaskCompletionSource.Task.ConfigureAwait(false));
        }
        /// <summary>
        /// Processes the next message on the input stream.
        /// </summary>
        /// <param name="cancellationToken">A token to cancel the operation.</param>
        public async Task ProcessMessageAsync(CancellationToken cancellationToken)
        {
            var message = await this.ReadMessageAsync(cancellationToken).ConfigureAwait(false);

            string json = message.jsonPayload;

            switch (message.messageType)
            {
            case MessageType.Request:
                this.logger.Log(() => "Handling request" + Environment.NewLine + json);
                PipeRequest request = JsonConvert.DeserializeObject <PipeRequest>(json, serializerSettings);

                if (this.RequestHandler == null)
                {
                    throw new InvalidOperationException("Request received but this endpoint is not set up to handle requests.");
                }

                this.RequestHandler.HandleRequest(request);
                break;

            case MessageType.Response:
                this.logger.Log(() => "Handling response" + Environment.NewLine + json);
                PipeResponse response = JsonConvert.DeserializeObject <PipeResponse>(json, serializerSettings);

                if (this.ResponseHandler == null)
                {
                    throw new InvalidOperationException("Response received but this endpoint is not set up to make requests.");
                }

                this.ResponseHandler.HandleResponse(response);
                break;

            default:
                throw new InvalidOperationException($"Unrecognized message type: {message.messageType}");
            }
        }
 /// <summary>
 /// Sends a request.
 /// </summary>
 /// <param name="request">The request to send.</param>
 /// <param name="cancellationToken">A token to cancel the operation.</param>
 public Task SendRequestAsync(PipeRequest request, CancellationToken cancellationToken)
 {
     return(this.SendMessageAsync(MessageType.Request, request, cancellationToken));
 }
        /// <summary>
        /// Handles a request from a remote endpoint.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>The response.</returns>
        private async Task <PipeResponse> HandleRequestAsync(PipeRequest request)
        {
            if (this.handlerFactoryFunc == null)
            {
                return(PipeResponse.Failure(request.CallId, $"No handler implementation registered for interface '{typeof(THandling).FullName}' found."));
            }

            THandling handlerInstance = this.handlerFactoryFunc();

            if (handlerInstance == null)
            {
                return(PipeResponse.Failure(request.CallId, $"Handler implementation returned null for interface '{typeof(THandling).FullName}'"));
            }

            MethodInfo method = handlerInstance.GetType().GetMethod(request.MethodName);

            if (method == null)
            {
                return(PipeResponse.Failure(request.CallId, $"Method '{request.MethodName}' not found in interface '{typeof(THandling).FullName}'."));
            }

            ParameterInfo[] paramInfoList = method.GetParameters();
            if (paramInfoList.Length != request.Parameters.Length)
            {
                return(PipeResponse.Failure(request.CallId, $"Parameter count mismatch for method '{request.MethodName}'."));
            }

            Type[] genericArguments = method.GetGenericArguments();
            if (genericArguments.Length != request.GenericArguments.Length)
            {
                return(PipeResponse.Failure(request.CallId, $"Generic argument count mismatch for method '{request.MethodName}'."));
            }

            if (paramInfoList.Any(info => info.IsOut || info.ParameterType.IsByRef))
            {
                return(PipeResponse.Failure(request.CallId, $"ref parameters are not supported. Method: '{request.MethodName}'"));
            }

            object[] args = new object[paramInfoList.Length];
            for (int i = 0; i < args.Length; i++)
            {
                object origValue = request.Parameters[i];
                Type   destType  = paramInfoList[i].ParameterType;
                if (destType.IsGenericParameter)
                {
                    destType = request.GenericArguments[destType.GenericParameterPosition];
                }

                if (Utilities.TryConvert(origValue, destType, out object arg))
                {
                    args[i] = arg;
                }
                else
                {
                    return(PipeResponse.Failure(request.CallId, $"Cannot convert value of parameter '{paramInfoList[i].Name}' ({origValue}) from {origValue.GetType().Name} to {destType.Name}."));
                }
            }

            try
            {
                if (method.IsGenericMethod)
                {
                    method = method.MakeGenericMethod(request.GenericArguments);
                }

                object result = method.Invoke(handlerInstance, args);

                if (result is Task)
                {
                    await((Task)result).ConfigureAwait(false);

                    var resultProperty = result.GetType().GetProperty("Result");
                    return(PipeResponse.Success(request.CallId, resultProperty?.GetValue(result)));
                }
                else
                {
                    return(PipeResponse.Success(request.CallId, result));
                }
            }
            catch (Exception exception)
            {
                return(PipeResponse.Failure(request.CallId, exception.ToString()));
            }
        }
        /// <summary>
        /// Gets a response from the given expression.
        /// </summary>
        /// <param name="expression">The expression to execute.</param>
        /// <param name="cancellationToken">A token to cancel the request.</param>
        /// <returns>A response for the given expression.</returns>
        private async Task <PipeResponse> GetResponseFromExpressionAsync(Expression expression, CancellationToken cancellationToken)
        {
            PipeRequest request = this.CreateRequest(expression);

            return(await this.GetResponseAsync(request, cancellationToken).ConfigureAwait(false));
        }