protected virtual async Task <TResponseMessage> SendInternal <TResponseMessage>(IRequestMessage <TResponseMessage> request, TimeSpan?timeout, string name, CancellationToken cancellationToken) { AssertActive(); AssertRequestResponseConfigured(); // check if the cancellation was already requested cancellationToken.ThrowIfCancellationRequested(); var requestType = request.GetType(); var producerSettings = GetProducerSettings(requestType); if (name == null) { name = GetDefaultName(requestType, producerSettings); } if (timeout == null) { timeout = GetDefaultRequestTimeout(requestType, producerSettings); } var created = CurrentTime; var expires = created.Add(timeout.Value); // generate the request guid var requestId = GenerateRequestId(); var requestMessage = new MessageWithHeaders(); requestMessage.SetHeader(ReqRespMessageHeaders.RequestId, requestId); requestMessage.SetHeader(ReqRespMessageHeaders.Expires, expires); // record the request state var requestState = new PendingRequestState(requestId, request, requestType, typeof(TResponseMessage), created, expires, cancellationToken); PendingRequestStore.Add(requestState); if (Log.IsTraceEnabled) { Log.TraceFormat(CultureInfo.InvariantCulture, "Added to PendingRequests, total is {0}", PendingRequestStore.GetCount()); } try { Log.DebugFormat(CultureInfo.InvariantCulture, "Sending request message {0} to name {1} with reply to {2}", requestState, name, Settings.RequestResponse.Topic); await ProduceRequest(request, requestMessage, name, producerSettings).ConfigureAwait(false); } catch (PublishMessageBusException e) { Log.DebugFormat(CultureInfo.InvariantCulture, "Publishing of request message failed", e); // remove from registry PendingRequestStore.Remove(requestId); throw; } // convert Task<object> to Task<TResponseMessage> var responseUntyped = await requestState.TaskCompletionSource.Task.ConfigureAwait(true); return((TResponseMessage)responseUntyped); }
/// <summary> /// Should be invoked by the concrete bus implementation whenever there is a message arrived on the reply to topic name. /// </summary> /// <param name="reponse"></param> /// <param name="name"></param> /// <param name="requestId"></param> /// <param name="errorMessage"></param> /// <returns></returns> public virtual Task <Exception> OnResponseArrived(byte[] responsePayload, string name, string requestId, string errorMessage, object response = null) { var requestState = PendingRequestStore.GetById(requestId); if (requestState == null) { _logger.LogDebug("The response message for request id {0} arriving on name {1} will be disregarded. Either the request had already expired, had been cancelled or it was already handled (this response message is a duplicate).", requestId, name); // ToDo: add and API hook to these kind of situation return(Task.FromResult <Exception>(null)); } try { if (_logger.IsEnabled(LogLevel.Debug)) { var tookTimespan = CurrentTime.Subtract(requestState.Created); _logger.LogDebug("Response arrived for {0} on name {1} (time: {2} ms)", requestState, name, tookTimespan); } if (errorMessage != null) { // error response arrived _logger.LogDebug("Response arrived for {0} on name {1} with error: {2}", requestState, name, errorMessage); var e = new RequestHandlerFaultedMessageBusException(errorMessage); requestState.TaskCompletionSource.TrySetException(e); } else { // response arrived try { // deserialize the response message response = responsePayload != null?DeserializeResponse(requestState.ResponseType, responsePayload) : response; // resolve the response requestState.TaskCompletionSource.TrySetResult(response); } catch (Exception e) { _logger.LogDebug("Could not deserialize the response message for {0} arriving on name {1}: {2}", requestState, name, e); requestState.TaskCompletionSource.TrySetException(e); } } } finally { // remove the request from the queue PendingRequestStore.Remove(requestId); } return(Task.FromResult <Exception>(null)); }
/// <summary> /// Should be invoked by the concrete bus implementation whenever there is a message arrived on the reply to topic name. /// </summary> /// <param name="payload"></param> /// <param name="name"></param> /// <param name="requestId"></param> /// <param name="errorMessage"></param> /// <returns></returns> public virtual Task OnResponseArrived(byte[] payload, string name, string requestId, string errorMessage) { var requestState = PendingRequestStore.GetById(requestId); if (requestState == null) { Log.DebugFormat(CultureInfo.InvariantCulture, "The response message for request id {0} arriving on name {1} will be disregarded. Either the request had already expired, had been cancelled or it was already handled (this response message is a duplicate).", requestId, name); // ToDo: add and API hook to these kind of situation return(Task.CompletedTask); } try { if (Log.IsDebugEnabled) { var tookTimespan = CurrentTime.Subtract(requestState.Created); Log.DebugFormat(CultureInfo.InvariantCulture, "Response arrived for {0} on name {1} (time: {2} ms)", requestState, name, tookTimespan); } if (errorMessage != null) { // error response arrived Log.DebugFormat(CultureInfo.InvariantCulture, "Response arrived for {0} on name {1} with error: {2}", requestState, name, errorMessage); var e = new RequestHandlerFaultedMessageBusException(errorMessage); requestState.TaskCompletionSource.TrySetException(e); } else { // response arrived try { // deserialize the response message var response = Settings.Serializer.Deserialize(requestState.ResponseType, payload); // resolve the response requestState.TaskCompletionSource.TrySetResult(response); } catch (Exception e) { Log.DebugFormat(CultureInfo.InvariantCulture, "Could not deserialize the response message for {0} arriving on name {1}: {2}", requestState, name, e); requestState.TaskCompletionSource.TrySetException(e); } } } finally { // remove the request from the queue PendingRequestStore.Remove(requestId); } return(Task.CompletedTask); }