示例#1
0
        public async Task <SendingAmqpLink> CreateSendingLinkAsync(string path, TimeSpan timeout)
        {
            var timeoutHelper = new TimeoutHelper(timeout);

            AmqpSession session;

            if (!_faultTolerantSession.TryGetOpenedObject(out session))
            {
                session = await _faultTolerantSession.GetOrCreateAsync(timeoutHelper.RemainingTime()).ConfigureAwait(false);
            }

            var linkAddress = ConnectionString.BuildLinkAddress(path);

            var linkSettings = new AmqpLinkSettings
            {
                Role = false,
                InitialDeliveryCount = 0,
                Target = new Target {
                    Address = linkAddress.AbsoluteUri
                },
                SndSettleMode = null,                                                       // SenderSettleMode.Unsettled (null as it is the default and to avoid bytes on the wire)
                RcvSettleMode = null,                                                       // (byte)ReceiverSettleMode.First (null as it is the default and to avoid bytes on the wire)
                LinkName      = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture), // Use a human readable link name to help with debugging
            };

            SetLinkSettingsCommonProperties(linkSettings, timeoutHelper.RemainingTime());

            var link = new SendingAmqpLink(linkSettings);

            link.AttachTo(session);

            await OpenLinkAsync(link, timeoutHelper.RemainingTime()).ConfigureAwait(false);

            return(link);
        }
        private async Task EnableSendingLinkAsync(CancellationToken cancellationToken)
        {
            SendingAmqpLink methodSendingLink = await this.GetMethodSendingLinkAsync(cancellationToken);

            this.SafeAddClosedSendingLinkHandler = this.linkClosedListener;
            methodSendingLink.SafeAddClosed((o, ea) => this.SafeAddClosedSendingLinkHandler(o, ea));
        }
        /// <summary>
        ///   Opens an AMQP link for use with producer operations.
        /// </summary>
        /// <param name="entityPath"></param>
        /// <param name="viaEntityPath">The entity path to route the message through. Useful when using transactions.</param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A link for use with producer operations.</returns>
        ///
        public virtual async Task <SendingAmqpLink> OpenSenderLinkAsync(
            string entityPath,
            string viaEntityPath,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var stopWatch = Stopwatch.StartNew();

            AmqpConnection connection = await ActiveConnection.GetOrCreateAsync(timeout).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            SendingAmqpLink link = await CreateSendingLinkAsync(
                entityPath,
                viaEntityPath,
                connection,
                timeout.CalculateRemaining(stopWatch.Elapsed), cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            await OpenAmqpObjectAsync(link, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            stopWatch.Stop();
            return(link);
        }
示例#4
0
        public DuplexAmqpLink(AmqpSession session, AmqpLinkSettings settings)
            : base("duplex")
        {
            AmqpTrace.Provider.AmqpLogOperationInformational(this, TraceOperation.Create, "Create");

            var senderSettings = new AmqpLinkSettings
            {
                Role = false,
                LinkName = settings.LinkName + ":out",
                SettleType = settings.SettleType,
                Source = new Source(),
                TotalLinkCredit = settings.TotalLinkCredit,
                AutoSendFlow = settings.AutoSendFlow,
                Target = settings.Target,
                Properties = settings.Properties
            };
            this.sender = new SendingAmqpLink(session, senderSettings);

            var receiverSettings = new AmqpLinkSettings
            {
                Role = true,
                LinkName = settings.LinkName + ":in",
                SettleType = settings.SettleType,
                Source = settings.Source,
                TotalLinkCredit = settings.TotalLinkCredit,
                AutoSendFlow = settings.AutoSendFlow,
                Target = new Target(),
                Properties = settings.Properties
            };
            this.receiver = new ReceivingAmqpLink(session, receiverSettings);
            this.receiver.SetTotalLinkCredit(receiverSettings.TotalLinkCredit, true); // WHY set both here AND on settings? Follow up with Xin.

            this.sender.SafeAddClosed(this.OnLinkClosed);
            this.receiver.SafeAddClosed(this.OnLinkClosed);
        }
        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);
            }
        }
示例#6
0
        async Task <SendingAmqpLink> CreateTwinSendingLinkAsync(TimeSpan timeout, CancellationToken cancellationToken)
        {
            string path = this.BuildPath(CommonConstants.DeviceTwinPathTemplate, CommonConstants.ModuleTwinPathTemplate);

            SendingAmqpLink twinSendingLink = await this.IotHubConnection.CreateSendingLinkAsync(path, this.iotHubConnectionString, this.twinConnectionCorrelationId, IotHubConnection.SendingLinkType.Twin, timeout, this.productInfo, cancellationToken).ConfigureAwait(false);

            MyStringCopy(twinSendingLink.Name, out twinSendingLinkName);
            twinSendingLink.SafeAddClosed(async(o, ea) =>
                                          await Task.Run(async() =>
            {
                foreach (var entry in twinResponseCompletions)
                {
                    TaskCompletionSource <AmqpMessage> task;
                    if (this.twinResponseCompletions.TryRemove(entry.Key, out task))
                    {
                        task.SetCanceled();
                    }

                    OnAmqpConnectionClose(o, ea);
                }
            }
                                                         ).ConfigureAwait(false));

            return(twinSendingLink);
        }
示例#7
0
        /// <summary>
        ///   Creates the AMQP link to be used for producer-related operations and ensures
        ///   that the corresponding state for the producer has been updated based on the link
        ///   configuration.
        /// </summary>
        ///
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">The cancellation token to consider when creating the link.</param>
        ///
        /// <returns>The AMQP link to use for producer-related operations.</returns>
        ///
        /// <remarks>
        ///   This method will modify class-level state, setting those attributes that depend on the AMQP
        ///   link configuration.  There exists a benign race condition in doing so, as there may be multiple
        ///   concurrent callers.  In this case, the attributes may be set multiple times but the resulting
        ///   value will be the same.
        /// </remarks>
        ///
        protected virtual async Task <SendingAmqpLink> CreateLinkAndEnsureSenderStateAsync(
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            SendingAmqpLink link = await _connectionScope.OpenSenderLinkAsync(
                _entityPath,
                timeout,
                cancellationToken).ConfigureAwait(false);

            if (!MaximumMessageSize.HasValue)
            {
                // This delay is necessary to prevent the link from causing issues for subsequent
                // operations after creating a batch.  Without it, operations using the link consistently
                // timeout.  The length of the delay does not appear significant, just the act of introducing
                // an asynchronous delay.
                //
                // For consistency the value used by the legacy Service Bus client has been brought forward and
                // used here.

                await Task.Delay(15, cancellationToken).ConfigureAwait(false);

                MaximumMessageSize = (long)link.Settings.MaxMessageSize;
            }

            return(link);
        }
示例#8
0
        async Task <SendingAmqpLink> CreateTwinSendingLinkAsync(TimeSpan timeout, CancellationToken cancellationToken)
        {
            string path = string.Format(CultureInfo.InvariantCulture, CommonConstants.DeviceTwinPathTemplate, System.Net.WebUtility.UrlEncode(this.deviceId));

            SendingAmqpLink twinSendingLink = await this.IotHubConnection.CreateSendingLinkAsync(path, this.iotHubConnectionString, this.twinConnectionCorrelationId, IotHubConnection.SendingLinkType.Twin, timeout, cancellationToken);

            MyStringCopy(twinSendingLink.Name, out twinSendingLinkName);
            this.SafeAddClosedTwinSendingLinkHandler = this.linkClosedListener;
            twinSendingLink.SafeAddClosed(async(o, ea) =>
                                          await Task.Run(async() =>
            {
                await this.SafeAddClosedTwinSendingLinkHandler(
                    o,
                    new ConnectionEventArgs
                {
                    ConnectionType               = ConnectionType.AmqpTwinSending,
                    ConnectionStatus             = ConnectionStatus.Disconnected_Retrying,
                    ConnectionStatusChangeReason = ConnectionStatusChangeReason.No_Network
                });
                foreach (var entry in twinResponseCompletions)
                {
                    TaskCompletionSource <AmqpMessage> task;
                    if (this.twinResponseCompletions.TryRemove(entry.Key, out task))
                    {
                        task.SetCanceled();
                    }
                }
            }
                                                         ));

            return(twinSendingLink);
        }
        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);
        }
示例#10
0
        public AmqpLink CreateLink(AmqpSession session, AmqpLinkSettings settings)
        {
            bool     isReceiver = settings.Role.Value;
            AmqpLink link;

            if (isReceiver)
            {
                if (settings.Target is Target && ((Target)settings.Target).Dynamic())
                {
                    string name = string.Format("$dynamic.{0}", Interlocked.Increment(ref this.dynamicId));
                    this.queues.Add(name, new TestQueue(this));
                    ((Target)settings.Target).Address = name;
                }

                link = new ReceivingAmqpLink(session, settings);
            }
            else
            {
                if (((Source)settings.Source).Dynamic())
                {
                    string name = string.Format("$dynamic.{0}", Interlocked.Increment(ref this.dynamicId));
                    this.queues.Add(name, new TestQueue(this));
                    ((Source)settings.Source).Address = name;
                }

                link = new SendingAmqpLink(session, settings);
            }

            return(link);
        }
示例#11
0
        async Task <Outcome> SendAmqpMethodResponseAsync(AmqpMessage amqpMessage, CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, amqpMessage, cancellationToken, $"{nameof(AmqpTransportHandler)}.{nameof(SendAmqpMethodResponseAsync)}");
            }
            Outcome outcome;

            try
            {
                SendingAmqpLink methodRespSendingLink = await this.GetMethodSendingLinkAsync(cancellationToken).ConfigureAwait(false);

                outcome = await methodRespSendingLink.SendMessageAsync(amqpMessage, new ArraySegment <byte>(Guid.NewGuid().ToByteArray()), AmqpConstants.NullBinary, this.operationTimeout).ConfigureAwait(false);
            }
            catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException))
            {
                throw AmqpClientHelper.ToIotHubClientContract(exception);
            }
            finally
            {
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, amqpMessage, cancellationToken, $"{nameof(AmqpTransportHandler)}.{nameof(SendAmqpMethodResponseAsync)}");
                }
            }

            return(outcome);
        }
示例#12
0
        public async Task DisableMethodsAsync(TimeSpan timeout)
        {
            if (Logging.IsEnabled) Logging.Enter(this, timeout, $"{nameof(DisableMethodsAsync)}");

            Debug.Assert(_methodSendingLink != null);
            Debug.Assert(_methodReceivingLink != null);

            try
            {
                ICollection<Task> tasks = new List<Task>();
                if (_methodReceivingLink != null)
                {
                    tasks.Add(_methodReceivingLink.CloseAsync(timeout));
                }

                if (_methodSendingLink != null)
                {
                    tasks.Add(_methodSendingLink.CloseAsync(timeout));
                }

                if (tasks.Count > 0)
                {
                    await Task.WhenAll(tasks).ConfigureAwait(false);
                    _methodReceivingLink = null;
                    _methodSendingLink = null;
                }
            }
            finally
            {
                if (Logging.IsEnabled) Logging.Exit(this, timeout, $"{nameof(DisableMethodsAsync)}");
            }
        }
        public override async Task <Twin> SendTwinGetAsync(CancellationToken cancellationToken)
        {
            Outcome outcome;

            try
            {
                await EnableTwinAsync(cancellationToken);

                SendingAmqpLink eventSendingLink = await this.GetTwinSendingLinkAsync(cancellationToken);

                // This is test code and will be re-written in future commits

                AmqpMessage getTwinMessage   = AmqpMessage.Create();
                string      getCorrelationId = Guid.NewGuid().ToString();
                getTwinMessage.Properties.CorrelationId            = getCorrelationId;
                getTwinMessage.MessageAnnotations.Map["operation"] = "GET";

                outcome = await eventSendingLink.SendMessageAsync(getTwinMessage, new ArraySegment <byte>(Guid.NewGuid().ToByteArray()), AmqpConstants.NullBinary, this.operationTimeout);
            }
            catch (Exception exception)
            {
                if (exception.IsFatal())
                {
                    throw;
                }

                throw AmqpClientHelper.ToIotHubClientContract(exception);
            }

            return(null);
        }
示例#14
0
        public async Task<SendingAmqpLink> CreateSendingLinkAsync(string path, IotHubConnectionString connectionString, TimeSpan timeout)
        {
            this.OnCreateSendingLink(connectionString);

            var timeoutHelper = new TimeoutHelper(timeout);

            AmqpSession session;
            if (!this.FaultTolerantSession.TryGetOpenedObject(out session))
            {
                session = await this.FaultTolerantSession.GetOrCreateAsync(timeoutHelper.RemainingTime());
            }

            var linkAddress = this.BuildLinkAddress(connectionString, path);

            var linkSettings = new AmqpLinkSettings()
            {
                Role = false,
                InitialDeliveryCount = 0,
                Target = new Target() { Address = linkAddress.AbsoluteUri },
                SndSettleMode = null, // SenderSettleMode.Unsettled (null as it is the default and to avoid bytes on the wire)
                RcvSettleMode = null, // (byte)ReceiverSettleMode.First (null as it is the default and to avoid bytes on the wire)
                LinkName = Guid.NewGuid().ToString("N") // Use a human readable link name to help with debugging
            };

            SetLinkSettingsCommonProperties(linkSettings, timeoutHelper.RemainingTime());

            var link = new SendingAmqpLink(linkSettings);
            link.AttachTo(session);

            var audience = this.BuildAudience(connectionString, path);
            await this.OpenLinkAsync(link, connectionString, audience, timeoutHelper.RemainingTime());

            return link;
        }
        // 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));
            }
        }
        async Task <SendingAmqpLink> CreateLinkAsync(TimeSpan timeout)
        {
            MessagingEventSource.Log.AmqpSendLinkCreateStart(this.ClientId, this.EntityType, this.Path);

            AmqpLinkSettings linkSettings = new AmqpLinkSettings
            {
                Role = false,
                InitialDeliveryCount = 0,
                Target = new Target {
                    Address = this.Path
                },
                Source = new Source {
                    Address = this.ClientId
                },
            };

            if (this.EntityType != null)
            {
                linkSettings.AddProperty(AmqpClientConstants.EntityTypeName, (int)this.EntityType);
            }

            AmqpSendReceiveLinkCreator sendReceiveLinkCreator = new AmqpSendReceiveLinkCreator(this.Path, this.ServiceBusConnection, new[] { ClaimConstants.Send }, this.CbsTokenProvider, linkSettings);
            SendingAmqpLink            sendingAmqpLink        = (SendingAmqpLink)await sendReceiveLinkCreator.CreateAndOpenAmqpLinkAsync().ConfigureAwait(false);

            MessagingEventSource.Log.AmqpSendLinkCreateStop(this.ClientId);
            return(sendingAmqpLink);
        }
示例#17
0
        protected override async Task OnSendAsync(IEnumerable <BrokeredMessage> brokeredMessages)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(this.OperationTimeout, true);

            using (AmqpMessage amqpMessage = AmqpMessageConverter.BrokeredMessagesToAmqpMessage(brokeredMessages, true))
            {
                SendingAmqpLink amqpLink = await this.SendLinkManager.GetOrCreateAsync(timeoutHelper.RemainingTime()).ConfigureAwait(false);

                if (amqpLink.Settings.MaxMessageSize.HasValue)
                {
                    ulong size = (ulong)amqpMessage.SerializedMessageSize;
                    if (size > amqpLink.Settings.MaxMessageSize.Value)
                    {
                        // TODO: Add MessageSizeExceededException
                        throw new NotImplementedException("MessageSizeExceededException: " + Resources.AmqpMessageSizeExceeded.FormatForUser(amqpMessage.DeliveryId.Value, size, amqpLink.Settings.MaxMessageSize.Value));
                        ////throw Fx.Exception.AsError(new MessageSizeExceededException(
                        ////Resources.AmqpMessageSizeExceeded.FormatForUser(amqpMessage.DeliveryId.Value, size, amqpLink.Settings.MaxMessageSize.Value)));
                    }
                }

                Outcome outcome = await amqpLink.SendMessageAsync(amqpMessage, this.GetNextDeliveryTag(), AmqpConstants.NullBinary, timeoutHelper.RemainingTime()).ConfigureAwait(false);

                if (outcome.DescriptorCode != Accepted.Code)
                {
                    Rejected rejected = (Rejected)outcome;
                    throw Fx.Exception.AsError(AmqpExceptionHelper.ToMessagingContract(rejected.Error));
                }
            }
        }
            public AmqpLink CreateLink(AmqpSession session, AmqpLinkSettings settings)
            {
                AmqpLink link;

                if (settings.Role.Value)
                {
                    var receiver = new ReceivingAmqpLink(session, settings);
                    receiver.RegisterMessageListener(m =>
                    {
                        this.messages.Enqueue(m.Clone());
                        receiver.AcceptMessage(m, true, true);
                        m.Dispose();
                    });
                    link = receiver;
                }
                else
                {
                    var sender = new SendingAmqpLink(session, settings);
                    sender.RegisterCreditListener((credit, drain, tx) =>
                    {
                        AmqpMessage message = this.messages.Dequeue();
                        message.DeliveryAnnotations.Map["x-opt-sequence-number"] = 1;
                        sender.SendMessageNoWait(message, EmptyBinary, NullBinary);
                    });
                    sender.RegisterDispositionListener(d =>
                    {
                        sender.DisposeDelivery(d, true, d.State);
                    });
                    link = sender;
                }

                return(link);
            }
示例#19
0
        public async Task EnsureTwinLinksAreOpenedAsync(TimeSpan timeout)
        {
            if (Volatile.Read(ref _twinLinksOpened) == true) return;
            if (Logging.IsEnabled) Logging.Enter(this, timeout, $"{nameof(EnsureTwinLinksAreOpenedAsync)}");

            try
            {
                await _twinLinksLock.WaitAsync().ConfigureAwait(false);
                if (_twinLinksOpened) return;

                Debug.Assert(_twinSendingLink == null);
                Debug.Assert(_twinReceivingLink == null);

                string correlationIdSuffix = Guid.NewGuid().ToString();

                Task<ReceivingAmqpLink> receiveLinkCreator =
                    AmqpLinkHelper.OpenTwinReceiverLinkAsync(
                        _deviceIdentity,
                        _amqpSession,
                        correlationIdSuffix,
                        timeout);

                Task<SendingAmqpLink> sendingLinkCreator =
                    AmqpLinkHelper.OpenTwinSenderLinkAsync(
                        _deviceIdentity,
                        _amqpSession,
                        correlationIdSuffix,
                        timeout);

                await Task.WhenAll(receiveLinkCreator, sendingLinkCreator).ConfigureAwait(false);

                _twinSendingLink = sendingLinkCreator.Result;
                _twinSendingLink.Closed += OnLinkDisconnected;

                _twinReceivingLink = receiveLinkCreator.Result;
                _twinReceivingLink.RegisterMessageListener(OnDesiredPropertyReceived);
                _twinReceivingLink.Closed += OnLinkDisconnected;

                _twinLinksOpened = true;

                if (Logging.IsEnabled) Logging.Associate(this, this, _twinReceivingLink, $"{nameof(EnsureTwinLinksAreOpenedAsync)}");
                if (Logging.IsEnabled) Logging.Associate(this, this, _twinSendingLink, $"{nameof(EnsureTwinLinksAreOpenedAsync)}");
            }
            catch (Exception ex) when (!ex.IsFatal())
            {
                _twinReceivingLink?.Abort();
                _twinSendingLink?.Abort();
                _twinReceivingLink = null;
                _twinSendingLink = null;

                throw;
            }
            finally
            {
                _twinLinksLock.Release();
                if (Logging.IsEnabled) Logging.Exit(this, timeout, $"{nameof(EnsureTwinLinksAreOpenedAsync)}");
            }
        }
示例#20
0
        private async Task EnableMethodSendingLinkAsync(CancellationToken cancellationToken)
        {
            SendingAmqpLink methodSendingLink = await this.GetMethodSendingLinkAsync(cancellationToken);

            this.SafeAddClosedMethodSendingLinkHandler = this.linkClosedListener;
            methodSendingLink.SafeAddClosed((o, ea) => this.SafeAddClosedMethodSendingLinkHandler(o, new ConnectionEventArgs {
                ConnectionKey = ConnectionKeys.AmqpMessaging, ConnectionStatus = ConnectionStatus.Disconnected_Retrying
            }));
        }
示例#21
0
        AmqpLink ILinkFactory.CreateLink(AmqpSession session, AmqpLinkSettings settings)
        {
            try
            {
                this.ValidateLinkSettings(settings);

                // Override AmqpLinkSetting MaxMessageSize to restrict it to Constants.AmqpMaxMessageSize
                if (settings.MaxMessageSize == null || settings.MaxMessageSize == 0 || settings.MaxMessageSize > Constants.AmqpMaxMessageSize)
                {
                    settings.MaxMessageSize = Constants.AmqpMaxMessageSize;
                }

                AmqpLink  amqpLink;
                IAmqpLink wrappingAmqpLink;
                string    linkAddress;
                if (settings.IsReceiver())
                {
                    amqpLink         = new ReceivingAmqpLink(session, settings);
                    wrappingAmqpLink = new EdgeReceivingAmqpLink((ReceivingAmqpLink)amqpLink);
                    linkAddress      = ((Target)settings.Target).Address.ToString();
                }
                else
                {
                    amqpLink         = new SendingAmqpLink(session, settings);
                    wrappingAmqpLink = new EdgeSendingAmqpLink((SendingAmqpLink)amqpLink);
                    linkAddress      = ((Source)settings.Source).Address.ToString();
                }

                // TODO: implement the rules below
                // Link address may be of the forms:
                //
                //  amqp[s]://my.servicebus.windows.net/a/b     <-- FQ address where host name should match connection remote host name
                //  amqp[s]:a/b                                 <-- path relative to hostname specified in OPEN
                //  a/b                                         <-- pre-global addressing style path relative to hostname specified in OPEN
                //  /a/b                                        <-- same as above

                Uri linkUri;
                if (!linkAddress.StartsWith(Constants.AmqpsScheme, StringComparison.OrdinalIgnoreCase))
                {
                    string host = session.Connection.Settings.RemoteHostName;
                    linkUri = new Uri("amqps://" + host + linkAddress.EnsureStartsWith('/'));
                }
                else
                {
                    linkUri = new Uri(linkAddress, UriKind.RelativeOrAbsolute);
                }

                ILinkHandler linkHandler = this.linkHandlerProvider.Create(wrappingAmqpLink, linkUri);
                amqpLink.Settings.AddProperty(LinkHandlerPropertyKey, linkHandler);
                return(amqpLink);
            }
            catch (Exception e) when(!ExceptionEx.IsFatal(e))
            {
                // Don't throw here because we cannot provide error info. Instead delay and throw from Link.Open.
                return(new FaultedLink(e, session, settings));
            }
        }
示例#22
0
 public Consumer(TestQueue queue, SendingAmqpLink link, int id)
 {
     this.queue        = queue;
     this.link         = link;
     this.id           = id;
     this.link.Closed += new EventHandler(link_Closed);
     this.link.RegisterCreditListener(this.OnCredit);
     this.link.RegisterDispositionListener(this.OnDispose);
 }
        private async Task EnableTwinSendingLinkAsync(CancellationToken cancellationToken)
        {
            SendingAmqpLink twinSendingLink = await this.GetTwinSendingLinkAsync(cancellationToken);

            this.SafeAddClosedTwinSendingLinkHandler = this.linkClosedListener;
            twinSendingLink.SafeAddClosed((o, ea) => this.SafeAddClosedTwinSendingLinkHandler(o, new ConnectionEventArgs {
                ConnectionKey = ConnectionKeys.AmqpTwinSending, ConnectionStatus = ConnectionStatus.Disconnected_Retrying, ConnectionStatusChangeReason = ConnectionStatusChangeReason.No_Network
            }));
        }
示例#24
0
        public async Task<SendingAmqpLink> CreateSendingLinkAsync(
            string path,
            IotHubConnectionString connectionString,
            string corrId,
            SendingLinkType linkType,
            TimeSpan timeout,
            ProductInfo productInfo,
            CancellationToken cancellationToken)
        {
            this.OnCreateSendingLink(connectionString);

            var timeoutHelper = new TimeoutHelper(timeout);

            AmqpSession session = await this.GetSessionAsync(timeoutHelper, cancellationToken).ConfigureAwait(false);

            var linkAddress = this.BuildLinkAddress(connectionString, path);

            var linkSettings = new AmqpLinkSettings()
            {
                Role = false,
                InitialDeliveryCount = 0,
                Target = new Target() { Address = linkAddress.AbsoluteUri },
                LinkName = Guid.NewGuid().ToString("N") // Use a human readable link name to help with debugging
            };

            switch (linkType)
            {
                case SendingLinkType.TelemetryEvents:
                    linkSettings.SndSettleMode = null; // SenderSettleMode.Unsettled (null as it is the default and to avoid bytes on the wire)
                    linkSettings.RcvSettleMode = null; // (byte)ReceiverSettleMode.First (null as it is the default and to avoid bytes on the wire)
                    break;
                case SendingLinkType.Methods:
                case SendingLinkType.Twin:
                    linkSettings.SndSettleMode = (byte)SenderSettleMode.Settled;
                    linkSettings.RcvSettleMode = (byte)ReceiverSettleMode.First;
                    break;
            }

            SetLinkSettingsCommonProperties(linkSettings, timeoutHelper.RemainingTime(), productInfo);
            if (linkType == SendingLinkType.Methods)
            {
                SetLinkSettingsCommonPropertiesForMethod(linkSettings, corrId);
            }
            else if (linkType == SendingLinkType.Twin)
            {
                SetLinkSettingsCommonPropertiesForTwin(linkSettings, corrId);
            }

            var link = new SendingAmqpLink(linkSettings);
            link.AttachTo(session);

            var audience = this.BuildAudience(connectionString, path);
            await this.OpenLinkAsync(link, connectionString, audience, timeoutHelper.RemainingTime(), cancellationToken).ConfigureAwait(false);

            return link;
        }
示例#25
0
        async Task <SendingAmqpLink> CreateMethodSendingLinkAsync(TimeSpan timeout, CancellationToken cancellationToken)
        {
            string path = this.BuildPath(CommonConstants.DeviceMethodPathTemplate, CommonConstants.ModuleMethodPathTemplate);

            SendingAmqpLink methodSendingLink = await this.IotHubConnection.CreateSendingLinkAsync(path, this.iotHubConnectionString, this.methodConnectionCorrelationId, IotHubConnection.SendingLinkType.Methods, timeout, this.productInfo, cancellationToken).ConfigureAwait(false);

            MyStringCopy(methodSendingLink.Name, out methodSendingLinkName);

            methodSendingLink.SafeAddClosed(OnAmqpConnectionClose);
            return(methodSendingLink);
        }
示例#26
0
        public void AmqpWebSocketTransportTest()
        {
            string address = "ws://localhost:28088";
            var    broker  = new TestAmqpBroker(new string[] { address }, null, null, null);

            try
            {
                broker.Start();

                string queue = "AmqpWebSocketTransportTest";
                broker.AddQueue(queue);

                AmqpConnection connection = AmqpConnection.Factory.OpenConnectionAsync(address).GetAwaiter().GetResult();

                AmqpSession session = connection.CreateSession(new AmqpSessionSettings());
                session.Open();

                SendingAmqpLink sLink = new SendingAmqpLink(session, AmqpUtils.GetLinkSettings(true, queue, SettleMode.SettleOnSend));
                sLink.Open();

                int messageCount = 100;
                for (int i = 0; i < messageCount; i++)
                {
                    AmqpMessage message = AmqpMessage.Create(new AmqpValue()
                    {
                        Value = "message" + i
                    });
                    sLink.SendMessageAsync(message, AmqpConstants.EmptyBinary, AmqpConstants.NullBinary, TimeSpan.FromSeconds(10)).Wait();
                }

                sLink.Close();

                ReceivingAmqpLink rLink = new ReceivingAmqpLink(session, AmqpUtils.GetLinkSettings(false, queue, SettleMode.SettleOnReceive, 100));
                rLink.Open();

                for (int i = 0; i < messageCount; i++)
                {
                    AmqpMessage message2 = rLink.ReceiveMessageAsync(TimeSpan.FromSeconds(60)).GetAwaiter().GetResult();
                    Assert.NotNull(message2);

                    rLink.AcceptMessage(message2, false);
                    message2.Dispose();
                }

                rLink.Close();

                connection.Close();
            }
            finally
            {
                broker.Stop();
            }
        }
示例#27
0
 internal static async Task <Outcome> SendAmqpMessageAsync(
     SendingAmqpLink sendingAmqpLink,
     AmqpMessage message,
     TimeSpan timeout)
 {
     return(await sendingAmqpLink.SendMessageAsync(
                message,
                new ArraySegment <byte>(Guid.NewGuid().ToByteArray()),
                AmqpConstants.NullBinary,
                timeout
                ).ConfigureAwait(false));
 }
示例#28
0
        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);
        }
示例#29
0
        internal static async Task <SendingAmqpLink> OpenSendingAmqpLinkAsync(
            DeviceIdentity deviceIdentity,
            AmqpSession amqpSession,
            byte?senderSettleMode,
            byte?receiverSettleMode,
            string deviceTemplate,
            string moduleTemplate,
            string linkSuffix,
            string CorrelationId,
            TimeSpan timeout
            )
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(typeof(AmqpLinkHelper), deviceIdentity, $"{nameof(OpenSendingAmqpLinkAsync)}");
            }
            AmqpLinkSettings amqpLinkSettings = new AmqpLinkSettings
            {
                LinkName             = CommonResources.GetNewStringGuid(linkSuffix),
                Role                 = false,
                InitialDeliveryCount = 0,
                Target               = new Target()
                {
                    Address = BuildLinkAddress(deviceIdentity, deviceTemplate, moduleTemplate)
                },
                Source = new Source()
                {
                    Address = deviceIdentity.IotHubConnectionString.DeviceId
                }
            };

            amqpLinkSettings.SndSettleMode = senderSettleMode;
            amqpLinkSettings.RcvSettleMode = receiverSettleMode;
            amqpLinkSettings.AddProperty(IotHubAmqpProperty.TimeoutName, timeout.TotalMilliseconds);
            amqpLinkSettings.AddProperty(IotHubAmqpProperty.ClientVersion, deviceIdentity.ProductInfo.ToString());
            amqpLinkSettings.AddProperty(IotHubAmqpProperty.ApiVersion, ClientApiVersionHelper.ApiVersionString);
            if (CorrelationId != null)
            {
                amqpLinkSettings.AddProperty(IotHubAmqpProperty.ChannelCorrelationId, CorrelationId);
            }

            SendingAmqpLink sendingLink = new SendingAmqpLink(amqpLinkSettings);

            sendingLink.AttachTo(amqpSession);
            await sendingLink.OpenAsync(timeout).ConfigureAwait(false);

            if (Logging.IsEnabled)
            {
                Logging.Exit(typeof(AmqpLinkHelper), deviceIdentity, $"{nameof(OpenSendingAmqpLinkAsync)}");
            }
            return(sendingLink);
        }
示例#30
0
        async Task <SendingAmqpLink> CreateTwinSendingLinkAsync(TimeSpan timeout, CancellationToken cancellationToken)
        {
            string path = string.Format(CultureInfo.InvariantCulture, CommonConstants.DeviceTwinPathTemplate, System.Net.WebUtility.UrlEncode(this.deviceId));

            SendingAmqpLink twinSendingLink = await this.IotHubConnection.CreateSendingLinkAsync(path, this.iotHubConnectionString, this.deviceId, IotHubConnection.SendingLinkType.Twin, timeout, cancellationToken);

            this.SafeAddClosedTwinSendingLinkHandler = this.linkClosedListener;
            twinSendingLink.SafeAddClosed((o, ea) => this.SafeAddClosedTwinSendingLinkHandler(o, new ConnectionEventArgs {
                ConnectionType = ConnectionType.AmqpTwinSending, ConnectionStatus = ConnectionStatus.Disconnected_Retrying, ConnectionStatusChangeReason = ConnectionStatusChangeReason.No_Network
            }));

            return(twinSendingLink);
        }
示例#31
0
        public async Task EnableMethodsAsync(TimeSpan timeout)
        {
            if (Logging.IsEnabled) Logging.Enter(this, timeout, $"{nameof(EnableMethodsAsync)}");

            try
            {
                Debug.Assert(_methodSendingLink == null);
                Debug.Assert(_methodReceivingLink == null);

                string correlationIdSuffix = Guid.NewGuid().ToString();
                Task<ReceivingAmqpLink> receiveLinkCreator =
                    AmqpLinkHelper.OpenMethodsReceiverLinkAsync(
                        _deviceIdentity,
                        _amqpSession,
                        correlationIdSuffix,
                        timeout);

                Task<SendingAmqpLink> sendingLinkCreator =
                    AmqpLinkHelper.OpenMethodsSenderLinkAsync(
                        _deviceIdentity,
                        _amqpSession,
                        correlationIdSuffix,
                        timeout);

                await Task.WhenAll(receiveLinkCreator, sendingLinkCreator).ConfigureAwait(false);

                _methodReceivingLink = receiveLinkCreator.Result;
                _methodSendingLink = sendingLinkCreator.Result;

                _methodReceivingLink.RegisterMessageListener(OnMethodReceived);
                _methodSendingLink.Closed += OnLinkDisconnected;
                _methodReceivingLink.Closed += OnLinkDisconnected;

                if (Logging.IsEnabled) Logging.Associate(this, _methodReceivingLink, $"{nameof(_methodReceivingLink)}");
                if (Logging.IsEnabled) Logging.Associate(this, _methodSendingLink, $"{nameof(_methodSendingLink)}");
            }
            catch (Exception)
            {
                _methodReceivingLink?.Abort();
                _methodReceivingLink = null;

                _methodSendingLink?.Abort();
                _methodSendingLink = null;

                throw;
            }
            finally
            {
                if (Logging.IsEnabled) Logging.Exit(this, timeout, $"{nameof(EnableMethodsAsync)}");
            }
        }
示例#32
0
        /// <summary>
        ///    Sends a set of messages to the associated Queue/Topic using a batched approach.
        /// </summary>
        ///
        /// <param name="messageFactory"></param>
        /// <param name="timeout"></param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        internal virtual async Task SendBatchInternalAsync(
            Func <AmqpMessage> messageFactory,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            var stopWatch = Stopwatch.StartNew();

            using (AmqpMessage batchMessage = messageFactory())
            {
                string messageHash = batchMessage.GetHashCode().ToString();

                ArraySegment <byte> transactionId      = AmqpConstants.NullBinary;
                Transaction         ambientTransaction = Transaction.Current;
                if (ambientTransaction != null)
                {
                    transactionId = await AmqpTransactionManager.Instance.EnlistAsync(
                        ambientTransaction,
                        _connectionScope,
                        timeout).ConfigureAwait(false);
                }

                SendingAmqpLink link = await _sendLink.GetOrCreateAsync(UseMinimum(_connectionScope.SessionTimeout, timeout)).ConfigureAwait(false);

                // Validate that the message is not too large to send.  This is done after the link is created to ensure
                // that the maximum message size is known, as it is dictated by the service using the link.

                if (batchMessage.SerializedMessageSize > MaxMessageSize)
                {
                    throw new ServiceBusException(string.Format(Resources.MessageSizeExceeded, messageHash, batchMessage.SerializedMessageSize, MaxMessageSize, _entityPath), ServiceBusException.FailureReason.MessageSizeExceeded);
                }

                // Attempt to send the message batch.

                var     deliveryTag = new ArraySegment <byte>(BitConverter.GetBytes(Interlocked.Increment(ref _deliveryCount)));
                Outcome outcome     = await link.SendMessageAsync(
                    batchMessage,
                    deliveryTag,
                    transactionId, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                if (outcome.DescriptorCode != Accepted.Code)
                {
                    throw (outcome as Rejected)?.Error.ToMessagingContractException();
                }

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();
                stopWatch.Stop();
            }
        }
示例#33
0
        public IAsyncResult BeginOpen(AmqpSession session, TimeSpan timeout, AsyncCallback callback, object state)
        {
            string uniqueueName = Guid.NewGuid().ToString("N");
            Source source = new Source();
            source.Address = uniqueueName;
            source.DistributionMode = DistributionMode.Move;

            Coordinator coordinator = new Coordinator();
            AmqpLinkSettings settings = new AmqpLinkSettings();
            settings.Source = source;
            settings.Target = coordinator;
            settings.LinkName = uniqueueName;
            settings.Role = false;

            this.sendLink = new SendingAmqpLink(session, settings);
            return this.sendLink.BeginOpen(timeout, callback, state);
        }
示例#34
0
        public DuplexAmqpLink(SendingAmqpLink sender, ReceivingAmqpLink receiver)
            : base("duplex")
        {
            AmqpTrace.Provider.AmqpLogOperationInformational(this, TraceOperation.Create, "Create");
            this.sender = sender;
            this.receiver = receiver;

            this.sender.SafeAddClosed(this.OnLinkClosed);
            this.receiver.SafeAddClosed(this.OnLinkClosed);

            // TODO:
            ////if (this.sender.State != AmqpObjectState.Opened)
            ////{
            ////    this.SafeClose();
            ////    throw Fx.Exception.AsWarning(new ArgumentException("Sender wasn't open", "sender"));
            ////}

            ////if (this.receiver.State != AmqpObjectState.Opened)
            ////{
            ////    this.SafeClose();
            ////    throw Fx.Exception.AsWarning(new ArgumentException("Reciever wasn't open", "receiver"));
            ////}
        }
示例#35
0
        public AmqpLink CreateLink(AmqpSession session, AmqpLinkSettings settings)
        {
            bool isReceiver = settings.Role.Value;
            AmqpLink link;
            if (isReceiver)
            {
                if (settings.Target is Target && ((Target)settings.Target).Dynamic())
                {
                    string name = string.Format("$dynamic.{0}", Interlocked.Increment(ref this.dynamicId));
                    this.queues.Add(name, new TestQueue(this));
                    ((Target)settings.Target).Address = name;
                }

                link = new ReceivingAmqpLink(session, settings);
            }
            else
            {
                if (((Source)settings.Source).Dynamic())
                {
                    string name = string.Format("$dynamic.{0}", Interlocked.Increment(ref this.dynamicId));
                    this.queues.Add(name, new TestQueue(this));
                    ((Source)settings.Source).Address = name;
                }

                link = new SendingAmqpLink(session, settings);
            }

            return link;
        }
示例#36
0
 public Consumer(TestQueue queue, SendingAmqpLink link, int id)
 {
     this.queue = queue;
     this.link = link;
     this.id = id;
     this.link.Closed += new EventHandler(link_Closed);
     this.link.RegisterCreditListener(this.OnCredit);
     this.link.RegisterDispositionListener(this.OnDispose);
 }
示例#37
0
 public void EndClose(IAsyncResult result)
 {
     this.sendLink.EndClose(result);
     this.sendLink = null;
 }