/// <summary>
        /// Handles a response message received from a remote endpoint.
        /// </summary>
        /// <param name="response">The response message to handle.</param>
        public void HandleResponse(PipeResponse response)
        {
            if (!this.pendingCalls.TryGetValue(response.CallId, out PendingCall pendingCall))
            {
                throw new InvalidOperationException($"No pending call found for ID {response.CallId}");
            }

            pendingCall.TaskCompletionSource.TrySetResult(response);
        }
        /// <summary>
        /// Invokes a method on the server.
        /// </summary>
        /// <param name="expression">The method to invoke.</param>
        /// <param name="cancellationToken">A token to cancel the request.</param>
        /// <exception cref="PipeInvokeFailedException">Thrown when the invoked method throws an exception.</exception>
        /// <exception cref="IOException">Thrown when there is an issue with the pipe communication.</exception>
        /// <exception cref="OperationCanceledException">Thrown when the cancellation token is invoked.</exception>
        public async Task InvokeAsync(Expression <Func <TRequesting, Task> > expression, CancellationToken cancellationToken = default)
        {
            // Async, no result

            Utilities.EnsureReadyForInvoke(this.pipeHost.State, this.pipeHost.PipeFault);

            PipeResponse response = await this.GetResponseFromExpressionAsync(expression, cancellationToken).ConfigureAwait(false);

            if (!response.Succeeded)
            {
                throw new PipeInvokeFailedException(response.Error);
            }
        }
Beispiel #3
0
        /// <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.
            }
        }
Beispiel #4
0
        /// <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);
                nlogger_.Trace($"ProcessMessageAsync: Handling request: [{json}]");
                PipeRequest request = JsonConvert.DeserializeObject <PipeRequest>(json, serializerSettings);

                if (this.RequestHandler == null)
                {
                    nlogger_.Trace("ProcessMessageAsync: throwing - Request received but this endpoint is not set up to handle requests");
                    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);
                nlogger_.Trace($"ProcessMessageAsync: Handling response: [{json}]");
                PipeResponse response = JsonConvert.DeserializeObject <PipeResponse>(json, serializerSettings);

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

                this.ResponseHandler.HandleResponse(response);
                break;

            default:
                nlogger_.Trace($"ProcessMessageAsync: throwing - Unrecognized message type: {message.messageType}");
                throw new InvalidOperationException($"Unrecognized message type: {message.messageType}");
            }
        }
        /// <summary>
        /// Invokes a method on the server.
        /// </summary>
        /// <typeparam name="TResult">The type of result from the method.</typeparam>
        /// <param name="expression">The method to invoke.</param>
        /// <param name="cancellationToken">A token to cancel the request.</param>
        /// <returns>The method result.</returns>
        /// <exception cref="PipeInvokeFailedException">Thrown when the invoked method throws an exception.</exception>
        /// <exception cref="IOException">Thrown when there is an issue with the pipe communication.</exception>
        /// <exception cref="OperationCanceledException">Thrown when the cancellation token is invoked.</exception>
        public async Task <TResult> InvokeAsync <TResult>(Expression <Func <TRequesting, Task <TResult> > > expression, CancellationToken cancellationToken = default)
        {
            // Async with result

            Utilities.EnsureReadyForInvoke(this.pipeHost.State, this.pipeHost.PipeFault);

            PipeResponse response = await this.GetResponseFromExpressionAsync(expression, cancellationToken).ConfigureAwait(false);

            if (response.Succeeded)
            {
                if (Utilities.TryConvert(response.Data, typeof(TResult), out object result))
                {
                    return((TResult)result);
                }
                else
                {
                    throw new InvalidOperationException($"Unable to convert returned value to '{typeof(TResult).Name}'.");
                }
            }
            else
            {
                throw new PipeInvokeFailedException(response.Error);
            }
        }
Beispiel #6
0
 /// <summary>
 /// Sends a response.
 /// </summary>
 /// <param name="response">The response to send.</param>
 /// <param name="cancellationToken">A token to cancel the operation.</param>
 public Task SendResponseAsync(PipeResponse response, CancellationToken cancellationToken)
 {
     return(this.SendMessageAsync(MessageType.Response, response, cancellationToken));
 }
Beispiel #7
0
        /// <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()));
            }
        }