/// <summary> /// Creates an AMQP link for use with management operations. /// </summary> /// <param name="entityPath"></param> /// /// <param name="connection">The active and opened AMQP connection to use for this link.</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 management operations.</returns> /// protected virtual async Task <RequestResponseAmqpLink> CreateManagementLinkAsync( string entityPath, AmqpConnection connection, TimeSpan timeout, CancellationToken cancellationToken) { Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var session = default(AmqpSession); var stopWatch = Stopwatch.StartNew(); try { // Create and open the AMQP session associated with the link. var sessionSettings = new AmqpSessionSettings { Properties = new Fields() }; session = connection.CreateSession(sessionSettings); await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Create and open the link. var linkSettings = new AmqpLinkSettings(); linkSettings.AddProperty(AmqpProperty.Timeout, (uint)timeout.CalculateRemaining(stopWatch.Elapsed).TotalMilliseconds); linkSettings.AddProperty(AmqpClientConstants.EntityTypeName, AmqpClientConstants.EntityTypeManagement); entityPath += '/' + AmqpClientConstants.ManagementAddress; // Perform the initial authorization for the link. string[] claims = { ServiceBusClaim.Manage, ServiceBusClaim.Listen, ServiceBusClaim.Send }; var endpoint = new Uri(ServiceEndpoint, entityPath); var audience = new[] { endpoint.AbsoluteUri }; DateTime authExpirationUtc = await RequestAuthorizationUsingCbsAsync( connection, TokenProvider, ServiceEndpoint, audience, claims, timeout.CalculateRemaining(stopWatch.Elapsed)) .ConfigureAwait(false); var link = new RequestResponseAmqpLink( AmqpClientConstants.EntityTypeManagement, session, entityPath, linkSettings.Properties); linkSettings.LinkName = $"{connection.Settings.ContainerId};{connection.Identifier}:{session.Identifier}:{link.Identifier}"; stopWatch.Stop(); // Track the link before returning it, so that it can be managed with the scope. var refreshTimer = default(Timer); TimerCallback refreshHandler = CreateAuthorizationRefreshHandler ( entityPath, connection, link, TokenProvider, ServiceEndpoint, audience, claims, AuthorizationRefreshTimeout, () => (ActiveLinks.ContainsKey(link) ? refreshTimer : null) ); refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan); // Track the link before returning it, so that it can be managed with the scope. BeginTrackingLinkAsActive(entityPath, link, refreshTimer); return(link); } catch (Exception exception) { // Aborting the session will perform any necessary cleanup of // the associated link as well. session?.Abort(); throw AmqpExceptionHelper.TranslateException( exception, null, session.GetInnerException(), connection.IsClosing()); } }
/// <summary> /// Creates an AMQP link for use with receiving operations. /// </summary> /// <param name="entityPath"></param> /// /// <param name="connection">The active and opened AMQP connection to use for this link.</param> /// <param name="endpoint">The fully qualified endpoint to open the link for.</param> /// <param name="prefetchCount">Controls the number of events received and queued locally without regard to whether an operation was requested.</param> /// <param name="receiveMode">The <see cref="ReceiveMode"/> used to specify how messages are received. Defaults to PeekLock mode.</param> /// <param name="sessionId"></param> /// <param name="isSessionReceiver"></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 for operations related to receiving events.</returns> /// protected virtual async Task <ReceivingAmqpLink> CreateReceivingLinkAsync( string entityPath, AmqpConnection connection, Uri endpoint, TimeSpan timeout, uint prefetchCount, ReceiveMode receiveMode, string sessionId, bool isSessionReceiver, CancellationToken cancellationToken) { Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var session = default(AmqpSession); var stopWatch = Stopwatch.StartNew(); try { // Perform the initial authorization for the link. string[] authClaims = new string[] { ServiceBusClaim.Send }; var audience = new[] { endpoint.AbsoluteUri }; DateTime authExpirationUtc = await RequestAuthorizationUsingCbsAsync( connection, TokenProvider, endpoint, audience, authClaims, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Create and open the AMQP session associated with the link. var sessionSettings = new AmqpSessionSettings { Properties = new Fields() }; session = connection.CreateSession(sessionSettings); await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var filters = new FilterSet(); // even if supplied sessionId is null, we need to add the Session filter if it is a session receiver if (isSessionReceiver) { filters.Add(AmqpClientConstants.SessionFilterName, sessionId); } var linkSettings = new AmqpLinkSettings { Role = true, TotalLinkCredit = prefetchCount, AutoSendFlow = prefetchCount > 0, SettleType = (receiveMode == ReceiveMode.PeekLock) ? SettleMode.SettleOnDispose : SettleMode.SettleOnSend, Source = new Source { Address = endpoint.AbsolutePath, FilterSet = filters }, Target = new Target { Address = Guid.NewGuid().ToString() } }; var link = new ReceivingAmqpLink(linkSettings); linkSettings.LinkName = $"{connection.Settings.ContainerId};{connection.Identifier}:{session.Identifier}:{link.Identifier}:{linkSettings.Source.ToString()}"; link.AttachTo(session); stopWatch.Stop(); // Configure refresh for authorization of the link. var refreshTimer = default(Timer); TimerCallback refreshHandler = CreateAuthorizationRefreshHandler ( entityPath, connection, link, TokenProvider, endpoint, audience, authClaims, AuthorizationRefreshTimeout, () => (ActiveLinks.ContainsKey(link) ? refreshTimer : null) ); refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan); // Track the link before returning it, so that it can be managed with the scope. BeginTrackingLinkAsActive(entityPath, link, refreshTimer); return(link); } catch (Exception exception) { // Aborting the session will perform any necessary cleanup of // the associated link as well. session?.Abort(); throw AmqpExceptionHelper.TranslateException( exception, null, session.GetInnerException(), connection.IsClosing()); } }
/// <summary> /// Creates an AMQP link for use with receiving operations. /// </summary> /// /// <param name="connection">The active and opened AMQP connection to use for this link.</param> /// <param name="endpoint">The fully qualified endpoint to open the link for.</param> /// <param name="eventPosition">The position of the event in the partition where the link should be filtered to.</param> /// <param name="prefetchCount">Controls the number of events received and queued locally without regard to whether an operation was requested.</param> /// <param name="ownerLevel">The relative priority to associate with the link; for a non-exclusive link, this value should be <c>null</c>.</param> /// <param name="trackLastEnqueuedEventProperties">Indicates whether information on the last enqueued event on the partition is sent as events are received.</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 for operations related to receiving events.</returns> /// protected virtual async Task <ReceivingAmqpLink> CreateReceivingLinkAsync(AmqpConnection connection, Uri endpoint, EventPosition eventPosition, TimeSpan timeout, uint prefetchCount, long?ownerLevel, bool trackLastEnqueuedEventProperties, CancellationToken cancellationToken) { Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var session = default(AmqpSession); var stopWatch = Stopwatch.StartNew(); try { // Perform the initial authorization for the link. var authClaims = new[] { EventHubsClaim.Listen }; var authExpirationUtc = await RequestAuthorizationUsingCbsAsync(connection, TokenProvider, endpoint, endpoint.AbsoluteUri, endpoint.AbsoluteUri, authClaims, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Create and open the AMQP session associated with the link. var sessionSettings = new AmqpSessionSettings { Properties = new Fields() }; session = connection.CreateSession(sessionSettings); await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Create and open the link. var filters = new FilterSet(); filters.Add(AmqpFilter.ConsumerFilterName, AmqpFilter.CreateConsumerFilter(AmqpFilter.BuildFilterExpression(eventPosition))); var linkSettings = new AmqpLinkSettings { Role = true, TotalLinkCredit = prefetchCount, AutoSendFlow = prefetchCount > 0, SettleType = SettleMode.SettleOnSend, Source = new Source { Address = endpoint.AbsolutePath, FilterSet = filters }, Target = new Target { Address = Guid.NewGuid().ToString() } }; linkSettings.AddProperty(AmqpProperty.EntityType, (int)AmqpProperty.Entity.ConsumerGroup); if (ownerLevel.HasValue) { linkSettings.AddProperty(AmqpProperty.OwnerLevel, ownerLevel.Value); } if (trackLastEnqueuedEventProperties) { linkSettings.DesiredCapabilities = new Multiple <AmqpSymbol>(new List <AmqpSymbol> { AmqpProperty.TrackLastEnqueuedEventProperties }); } var link = new ReceivingAmqpLink(linkSettings); linkSettings.LinkName = $"{ Id };{ connection.Identifier }:{ session.Identifier }:{ link.Identifier }"; link.AttachTo(session); stopWatch.Stop(); // Configure refresh for authorization of the link. var refreshTimer = default(Timer); var refreshHandler = CreateAuthorizationRefreshHandler ( connection, link, TokenProvider, endpoint, endpoint.AbsoluteUri, endpoint.AbsoluteUri, authClaims, AuthorizationRefreshTimeout, () => (ActiveLinks.ContainsKey(link) ? refreshTimer : null) ); refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan); // Track the link before returning it, so that it can be managed with the scope. BeginTrackingLinkAsActive(link, refreshTimer); return(link); } catch { // Aborting the session will perform any necessary cleanup of // the associated link as well. session?.Abort(); throw; } }