public override async Task SendEventAsync(IEnumerable <Message> messages, CancellationToken cancellationToken) { await this.HandleTimeoutCancellation(async() => { // List to hold messages in Amqp friendly format var messageList = new List <Data>(); foreach (Message message in messages) { using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { var data = new Data() { Value = MessageConverter.ReadStream(amqpMessage.ToStream()) }; messageList.Add(data); } } Outcome outcome; using (AmqpMessage amqpMessage = AmqpMessage.Create(messageList)) { amqpMessage.MessageFormat = AmqpConstants.AmqpBatchedMessageFormat; outcome = await this.SendAmqpMessageAsync(amqpMessage, cancellationToken).ConfigureAwait(false); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }, cancellationToken).ConfigureAwait(false); }
// This call is executed over AMQP. public override async Task SendAsync(string deviceId, string moduleId, Message message) { Logging.Enter(this, $"Sending message with Id [{message?.MessageId}] for device {deviceId}, module {moduleId}", nameof(SendAsync)); if (string.IsNullOrWhiteSpace(deviceId)) { throw new ArgumentNullException(nameof(deviceId)); } if (string.IsNullOrWhiteSpace(moduleId)) { throw new ArgumentNullException(nameof(moduleId)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } if (_clientOptions?.SdkAssignsMessageId == SdkAssignsMessageId.WhenUnset && message.MessageId == null) { message.MessageId = Guid.NewGuid().ToString(); } if (message.IsBodyCalled) { message.ResetBody(); } using AmqpMessage amqpMessage = MessageConverter.MessageToAmqpMessage(message); amqpMessage.Properties.To = "/devices/" + WebUtility.UrlEncode(deviceId) + "/modules/" + WebUtility.UrlEncode(moduleId) + "/messages/deviceBound"; try { SendingAmqpLink sendingLink = await GetSendingLinkAsync().ConfigureAwait(false); Outcome outcome = await sendingLink .SendMessageAsync( amqpMessage, IotHubConnection.GetNextDeliveryTag(ref _sendingDeliveryTag), AmqpConstants.NullBinary, OperationTimeout) .ConfigureAwait(false); Logging.Info(this, $"Outcome was: {outcome?.DescriptorName}", nameof(SendAsync)); if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } } catch (Exception ex) when(!ex.IsFatal()) { Logging.Error(this, $"{nameof(SendAsync)} threw an exception: {ex}", nameof(SendAsync)); throw AmqpClientHelper.ToIotHubClientContract(ex); } finally { Logging.Exit(this, $"Sending message with Id [{message?.MessageId}] for device {deviceId}, module {moduleId}", nameof(SendAsync)); } }
public override async Task SendMethodResponseAsync(MethodResponseInternal methodResponse, CancellationToken cancellationToken) { if (Logging.IsEnabled) { Logging.Enter(this, methodResponse, cancellationToken, $"{nameof(SendMethodResponseAsync)}"); } try { cancellationToken.ThrowIfCancellationRequested(); Outcome outcome; using (AmqpMessage amqpMessage = methodResponse.ToAmqpMessage()) { outcome = await _amqpUnit.SendMethodResponseAsync(amqpMessage, _operationTimeout).ConfigureAwait(false); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } } finally { if (Logging.IsEnabled) { Logging.Exit(this, methodResponse, cancellationToken, $"{nameof(SendMethodResponseAsync)}"); } } }
protected override async Task OnSendEventAsync(IEnumerable <Message> messages) { // List to hold messages in Amqp friendly format var messageList = new List <Data>(); foreach (var message in messages) { using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { var data = new Data() { Value = MessageConverter.ReadStream(amqpMessage.ToStream()) }; messageList.Add(data); } } Outcome outcome; using (var amqpMessage = AmqpMessage.Create(messageList)) { amqpMessage.MessageFormat = AmqpConstants.AmqpBatchedMessageFormat; outcome = await this.SendAmqpMessageAsync(amqpMessage); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }
public async override Task SendAsync(string deviceId, Message message, TimeSpan?timeout = null) { if (string.IsNullOrWhiteSpace(deviceId)) { throw new ArgumentNullException(nameof(deviceId)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } timeout ??= OperationTimeout; using AmqpMessage amqpMessage = message.ToAmqpMessage(); amqpMessage.Properties.To = "/devices/" + WebUtility.UrlEncode(deviceId) + "/messages/deviceBound"; try { SendingAmqpLink sendingLink = await GetSendingLinkAsync().ConfigureAwait(false); Outcome outcome = await sendingLink .SendMessageAsync(amqpMessage, IotHubConnection.GetNextDeliveryTag(ref _sendingDeliveryTag), AmqpConstants.NullBinary, timeout.Value) .ConfigureAwait(false); if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } } catch (Exception ex) when(!(ex is TimeoutException) && !ex.IsFatal()) { throw AmqpClientHelper.ToIotHubClientContract(ex); } }
public override async Task SendEventAsync(Message message, CancellationToken cancellationToken) { try { if (Logging.IsEnabled) { Logging.Enter(this, message, cancellationToken, $"{nameof(AmqpTransportHandler)}.{nameof(SendEventAsync)}"); } cancellationToken.ThrowIfCancellationRequested(); Outcome outcome; using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { outcome = await this.SendAmqpMessageAsync(amqpMessage, cancellationToken).ConfigureAwait(false); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } } finally { if (Logging.IsEnabled) { Logging.Exit(this, message, cancellationToken, $"{nameof(AmqpTransportHandler)}.{nameof(SendEventAsync)}"); } } }
async Task DisposeMessageAsync(string lockToken, Outcome outcome) { var deliveryTag = IotHubConnection.ConvertToDeliveryTag(lockToken); Outcome disposeOutcome; try { ReceivingAmqpLink deviceBoundReceivingLink = await this.GetReceivingLinkAsync(); disposeOutcome = await deviceBoundReceivingLink.DisposeMessageAsync(deliveryTag, outcome, batchable : true, timeout : this.OperationTimeout); } catch (Exception exception) { if (exception.IsFatal()) { throw; } throw AmqpClientHelper.ToIotHubClientContract(exception); } if (disposeOutcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(disposeOutcome); } }
internal static async Task DisposeMessageAsync(FaultTolerantAmqpObject <ReceivingAmqpLink> faultTolerantReceivingLink, string lockToken, Outcome outcome, bool batchable) { var deliveryTag = IotHubConnection.ConvertToDeliveryTag(lockToken); Outcome disposeOutcome; try { ReceivingAmqpLink deviceBoundReceivingLink = await faultTolerantReceivingLink.GetReceivingLinkAsync().ConfigureAwait(false); disposeOutcome = await deviceBoundReceivingLink.DisposeMessageAsync(deliveryTag, outcome, batchable, IotHubConnection.DefaultOperationTimeout).ConfigureAwait(false); } catch (Exception exception) { if (exception.IsFatal()) { throw; } throw AmqpClientHelper.ToIotHubClientContract(exception); } if (disposeOutcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(disposeOutcome); } }
private async Task <AmqpMessage> RoundTripTwinMessage(AmqpMessage amqpMessage, CancellationToken cancellationToken) { string correlationId = Guid.NewGuid().ToString(); AmqpMessage response = null; try { Outcome outcome; SendingAmqpLink eventSendingLink = await this.GetTwinSendingLinkAsync(cancellationToken); amqpMessage.Properties.CorrelationId = correlationId; this.twinResponseCompletions[correlationId] = new TaskCompletionSource <AmqpMessage>(); outcome = await eventSendingLink.SendMessageAsync(amqpMessage, new ArraySegment <byte>(Guid.NewGuid().ToByteArray()), AmqpConstants.NullBinary, this.operationTimeout); if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } response = await this.twinResponseCompletions[correlationId].Task; } finally { TaskCompletionSource <AmqpMessage> throwAway; this.twinResponseCompletions.TryRemove(correlationId, out throwAway); } return(response); }
private async Task <AmqpMessage> RoundTripTwinMessage(AmqpMessage amqpMessage, CancellationToken cancellationToken) { if (Logging.IsEnabled) { Logging.Enter(this, amqpMessage, cancellationToken, $"{nameof(AmqpTransportHandler)}.{nameof(RoundTripTwinMessage)}"); } string correlationId = Guid.NewGuid().ToString(); AmqpMessage response = null; try { Outcome outcome; SendingAmqpLink eventSendingLink = await this.GetTwinSendingLinkAsync(cancellationToken).ConfigureAwait(false); amqpMessage.Properties.CorrelationId = correlationId; var taskCompletionSource = new TaskCompletionSource <AmqpMessage>(); this.twinResponseCompletions[correlationId] = taskCompletionSource; outcome = await eventSendingLink.SendMessageAsync(amqpMessage, new ArraySegment <byte>(Guid.NewGuid().ToByteArray()), AmqpConstants.NullBinary, this.operationTimeout).ConfigureAwait(false); if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } var receivingTask = taskCompletionSource.Task; if (await Task.WhenAny(receivingTask, Task.Delay(TimeSpan.FromSeconds(ResponseTimeoutInSeconds))).ConfigureAwait(false) == receivingTask) { // Task completed within timeout. // Consider that the task may have faulted or been canceled. // We re-await the task so that any exceptions/cancellation is rethrown. response = await receivingTask.ConfigureAwait(false); } else { // Timeout happen throw new TimeoutException(); } } finally { TaskCompletionSource <AmqpMessage> throwAway; this.twinResponseCompletions.TryRemove(correlationId, out throwAway); if (Logging.IsEnabled) { Logging.Exit(this, amqpMessage, cancellationToken, $"{nameof(AmqpTransportHandler)}.{nameof(RoundTripTwinMessage)}"); } } return(response); }
internal static async Task DisposeMessageAsync( FaultTolerantAmqpObject <ReceivingAmqpLink> faultTolerantReceivingLink, string lockToken, Outcome outcome, bool batchable, CancellationToken cancellationToken) { Logging.Enter(faultTolerantReceivingLink, lockToken, outcome.DescriptorCode, batchable, nameof(DisposeMessageAsync)); try { cancellationToken.ThrowIfCancellationRequested(); ArraySegment <byte> deliveryTag = IotHubConnection.ConvertToDeliveryTag(lockToken); Outcome disposeOutcome; try { ReceivingAmqpLink deviceBoundReceivingLink = await faultTolerantReceivingLink.GetReceivingLinkAsync().ConfigureAwait(false); disposeOutcome = await deviceBoundReceivingLink .DisposeMessageAsync( deliveryTag, outcome, batchable, cancellationToken) .ConfigureAwait(false); } catch (Exception exception) { Logging.Error(faultTolerantReceivingLink, exception, nameof(DisposeMessageAsync)); if (exception.IsFatal()) { throw; } throw ToIotHubClientContract(exception); } Logging.Info(faultTolerantReceivingLink, disposeOutcome.DescriptorCode, nameof(DisposeMessageAsync)); if (disposeOutcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(disposeOutcome); } } finally { Logging.Exit(faultTolerantReceivingLink, lockToken, outcome.DescriptorCode, batchable, nameof(DisposeMessageAsync)); } }
protected async override Task OnSendEventAsync(Message message) { Outcome outcome; using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { outcome = await this.SendAmqpMessageAsync(amqpMessage); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }
public override async Task SendEventAsync(IEnumerable <Message> messages, CancellationToken cancellationToken) { if (Logging.IsEnabled) { Logging.Enter(this, messages, cancellationToken, $"{nameof(SendEventAsync)}"); } try { cancellationToken.ThrowIfCancellationRequested(); // List to hold messages in Amqp friendly format var messageList = new List <Data>(); foreach (Message message in messages) { using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { var data = new Data() { Value = MessageConverter.ReadStream(amqpMessage.ToStream()) }; messageList.Add(data); } } Outcome outcome; using (AmqpMessage amqpMessage = AmqpMessage.Create(messageList)) { amqpMessage.MessageFormat = AmqpConstants.AmqpBatchedMessageFormat; outcome = await _amqpUnit.SendEventAsync(amqpMessage, _operationTimeout).ConfigureAwait(false); } if (outcome != null) { if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } } } finally { if (Logging.IsEnabled) { Logging.Exit(this, messages, cancellationToken, $"{nameof(SendEventAsync)}"); } } }
public async override Task SendAsync(string deviceId, Message message, TimeSpan?timeout = null) { if (string.IsNullOrWhiteSpace(deviceId)) { throw new ArgumentException("Value should be non null and non empty", "deviceId"); } if (message == null) { throw new ArgumentNullException("message"); } Outcome outcome; using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { amqpMessage.Properties.To = "/devices/" + WebUtility.UrlEncode(deviceId) + "/messages/deviceBound"; try { SendingAmqpLink sendingLink = await GetSendingLinkAsync().ConfigureAwait(false); if (timeout != null) { outcome = await sendingLink.SendMessageAsync(amqpMessage, IotHubConnection.GetNextDeliveryTag(ref sendingDeliveryTag), AmqpConstants.NullBinary, (TimeSpan)timeout).ConfigureAwait(false); } else { outcome = await sendingLink.SendMessageAsync(amqpMessage, IotHubConnection.GetNextDeliveryTag(ref sendingDeliveryTag), AmqpConstants.NullBinary, OperationTimeout).ConfigureAwait(false); } } catch (TimeoutException exception) { throw exception; } catch (Exception exception) { if (exception.IsFatal()) { throw; } throw AmqpClientHelper.ToIotHubClientContract(exception); } } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }
public override async Task SendMethodResponseAsync(MethodResponseInternal methodResponse, CancellationToken cancellationToken) { await this.HandleTimeoutCancellation(async() => { Outcome outcome; using (AmqpMessage amqpMessage = methodResponse.ToAmqpMessage()) { outcome = await this.SendAmqpMethodResponseAsync(amqpMessage, cancellationToken); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }, cancellationToken); }
public override async Task SendEventAsync(Message message, CancellationToken cancellationToken) { await this.HandleTimeoutCancellation(async() => { Outcome outcome; using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { outcome = await this.SendAmqpMessageAsync(amqpMessage, cancellationToken).ConfigureAwait(false); } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }, cancellationToken).ConfigureAwait(false); }
public override async Task SendAsync(string deviceId, string moduleId, Message message) { if (string.IsNullOrWhiteSpace(deviceId)) { throw new ArgumentNullException(nameof(deviceId)); } if (string.IsNullOrWhiteSpace(moduleId)) { throw new ArgumentNullException(nameof(moduleId)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } Outcome outcome; using (AmqpMessage amqpMessage = message.ToAmqpMessage()) { amqpMessage.Properties.To = "/devices/" + WebUtility.UrlEncode(deviceId) + "/modules/" + WebUtility.UrlEncode(moduleId) + "/messages/deviceBound"; try { SendingAmqpLink sendingLink = await this.GetSendingLinkAsync().ConfigureAwait(false); outcome = await sendingLink.SendMessageAsync(amqpMessage, IotHubConnection.GetNextDeliveryTag(ref this.sendingDeliveryTag), AmqpConstants.NullBinary, this.OperationTimeout).ConfigureAwait(false); } catch (Exception exception) { if (exception.IsFatal()) { throw; } throw AmqpClientHelper.ToIotHubClientContract(exception); } } if (outcome.DescriptorCode != Accepted.Code) { throw AmqpErrorMapper.GetExceptionFromOutcome(outcome); } }
public static Exception ToIotHubClientContract(Exception exception) { if (exception is TimeoutException) { return(new IotHubCommunicationException(exception.Message)); } else if (exception is UnauthorizedAccessException) { return(new UnauthorizedException(exception.Message)); } else { if (exception is AmqpException amqpException) { return(AmqpErrorMapper.ToIotHubClientContract(amqpException.Error)); } return(exception); } }
async Task DisposeMessageAsync(string lockToken, Outcome outcome, CancellationToken cancellationToken) { ArraySegment <byte> deliveryTag = IotHubConnection.ConvertToDeliveryTag(lockToken); Outcome disposeOutcome; try { // Currently, the same mechanism is used for sending feedback for C2D messages and events received by modules. // However, devices only support C2D messages (they cannot receive events), and modules only support receiving events // (they cannot receive C2D messages). So we use this to distinguish whether to dispose the message (i.e. send outcome on) // the DeviceBoundReceivingLink or the EventsReceivingLink. // If this changes (i.e. modules are able to receive C2D messages, or devices are able to receive telemetry), this logic // will have to be updated. ReceivingAmqpLink deviceBoundReceivingLink = !string.IsNullOrWhiteSpace(this.moduleId) ? await this.GetEventReceivingLinkAsync(cancellationToken).ConfigureAwait(false) : await this.GetDeviceBoundReceivingLinkAsync(cancellationToken).ConfigureAwait(false); disposeOutcome = await deviceBoundReceivingLink.DisposeMessageAsync(deliveryTag, outcome, batchable : true, timeout : this.operationTimeout).ConfigureAwait(false); } catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException)) { throw AmqpClientHelper.ToIotHubClientContract(exception); } if (disposeOutcome.DescriptorCode != Accepted.Code) { if (disposeOutcome.DescriptorCode == Rejected.Code) { var rejected = (Rejected)disposeOutcome; // Special treatment for NotFound amqp rejected error code in case of DisposeMessage if (rejected.Error != null && rejected.Error.Condition.Equals(AmqpErrorCode.NotFound)) { throw new DeviceMessageLockLostException(rejected.Error.Description); } } throw AmqpErrorMapper.GetExceptionFromOutcome(disposeOutcome); } }
private async Task DisposeMessageAsync(string lockToken, Outcome outcome, CancellationToken cancellationToken) { Outcome disposeOutcome; try { // Currently, the same mechanism is used for sending feedback for C2D messages and events received by modules. // However, devices only support C2D messages (they cannot receive events), and modules only support receiving events // (they cannot receive C2D messages). So we use this to distinguish whether to dispose the message (i.e. send outcome on) // the DeviceBoundReceivingLink or the EventsReceivingLink. // If this changes (i.e. modules are able to receive C2D messages, or devices are able to receive telemetry), this logic // will have to be updated. disposeOutcome = await _amqpUnit.DisposeMessageAsync(lockToken, outcome, _operationTimeout).ConfigureAwait(false); } catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException)) { throw AmqpClientHelper.ToIotHubClientContract(exception); } if (disposeOutcome.DescriptorCode != Accepted.Code) { if (disposeOutcome.DescriptorCode == Rejected.Code) { var rejected = (Rejected)disposeOutcome; // Special treatment for NotFound amqp rejected error code in case of DisposeMessage if (rejected.Error != null && rejected.Error.Condition.Equals(AmqpErrorCode.NotFound)) { Error error = new Error { Condition = IotHubAmqpErrorCode.MessageLockLostError }; throw AmqpErrorMapper.ToIotHubClientContract(error); } } throw AmqpErrorMapper.GetExceptionFromOutcome(disposeOutcome); } }
async Task DisposeMessageAsync(string lockToken, Outcome outcome) { var deliveryTag = IotHubConnection.ConvertToDeliveryTag(lockToken); Outcome disposeOutcome; try { ReceivingAmqpLink deviceBoundReceivingLink = await this.GetDeviceBoundReceivingLinkAsync(); disposeOutcome = await deviceBoundReceivingLink.DisposeMessageAsync(deliveryTag, outcome, batchable : true, timeout : this.OperationTimeout); } catch (Exception exception) { if (exception.IsFatal()) { throw; } throw AmqpClientHelper.ToIotHubClientContract(exception); } if (disposeOutcome.DescriptorCode != Accepted.Code) { if (disposeOutcome.DescriptorCode == Rejected.Code) { var rejected = (Rejected)disposeOutcome; // Special treatment for NotFound amqp rejected error code in case of DisposeMessage if (rejected.Error != null && rejected.Error.Condition.Equals(AmqpErrorCode.NotFound)) { throw new DeviceMessageLockLostException(rejected.Error.Description); } } throw AmqpErrorMapper.GetExceptionFromOutcome(disposeOutcome); } }