/// <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, TResult> > expression, CancellationToken cancellationToken = default) { // Sync 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 { ThrowCoveoException(response); ThrowGenericException(response); throw new PipeInvokeFailedException(response.Error); } }
/// <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> /// 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> /// 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); } }
private static void ThrowExceptionForAssembly(PipeResponse response, Assembly assembly) { var type = assembly.GetType(response.Exception.ExceptionType); if (response.Exception.InnerException != null) { var innerType = assembly.GetType(response.Exception.InnerException.ExceptionType); var innerException = Activator.CreateInstance(innerType, response.Exception.InnerException.ExceptionMessage) as Exception; throw Activator.CreateInstance(type, response.Exception.ExceptionMessage, innerException) as Exception; } throw Activator.CreateInstance(type, response.Exception.ExceptionMessage) as Exception; }
private void ThrowCoveoException(PipeResponse response) { if (response.Exception != null && !string.IsNullOrEmpty(response.Exception.ExceptionType)) { if (response.Exception.ExceptionType.Contains(COVEO_DOCUMENTPROCESSING_EXCEPTION)) { var assembly = Assembly.Load(COVEOCUSTOMCRAWLERS_DOCUMENTPROCESSING_ASSEMBLY); ThrowExceptionForAssembly(response, assembly); } else if (response.Exception.ExceptionType.Contains(COVEO_EXCEPTION)) { var assembly = Assembly.Load(COVEOCUSTOMCRAWLERS_ASSEMBLY); ThrowExceptionForAssembly(response, assembly); } } }
/// <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) { PendingCall pendingCall = null; lock (this.pendingCallsLock) { if (this.pendingCalls.TryGetValue(response.CallId, out pendingCall)) { // Call has completed. Remove from pending list. this.pendingCalls.Remove(response.CallId); } else { throw new InvalidOperationException($"No pending call found for ID {response.CallId}"); } } // Mark method call task as completed. pendingCall.TaskCompletionSource.TrySetResult(response); }
private void ThrowGenericException(PipeResponse response) { if (response.Exception != null && !string.IsNullOrEmpty(response.Exception.ExceptionType)) { var type = Type.GetType(response.Exception.ExceptionType); if (response.Exception.InnerException != null) { var innerType = Type.GetType(response.Exception.InnerException.ExceptionType); var innerException = Activator.CreateInstance(innerType, response.Exception.InnerException.ExceptionMessage) as Exception; throw Activator.CreateInstance(type, response.Exception.ExceptionMessage, innerException) as Exception; } if (response.Exception.ExceptionType.Contains(ARGUMENT_EXCEPTION)) { throw Activator.CreateInstance(type, null, response.Exception.ExceptionMessage) as Exception; } throw Activator.CreateInstance(type, response.Exception.ExceptionMessage) as Exception; } }
/// <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 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)); }
/// <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())); } }