public async Task<RequestResponseAmqpLink> CreateRequestResponseLink(string path, TimeSpan timeout) { var timeoutHelper = new TimeoutHelper(timeout); AmqpSession session; if (!this.faultTolerantSession.TryGetOpenedObject(out session)) { session = await this.faultTolerantSession.GetOrCreateAsync(timeoutHelper.RemainingTime()); } var linkAddress = this.connectionString.BuildLinkAddress(path); var linkSettings = new AmqpLinkSettings() { TotalLinkCredit = 0, AutoSendFlow = false, Source = new Source() { Address = linkAddress.AbsoluteUri }, SettleType = SettleMode.SettleOnDispose, LinkName = Guid.NewGuid().ToString("N") // Use a human readable link name to help with debuggin }; SetLinkSettingsCommonProperties(linkSettings, timeoutHelper.RemainingTime()); var link = new RequestResponseAmqpLink(session, linkSettings); await OpenLinkAsync(link, timeoutHelper.RemainingTime()); return link; }
/// <summary> /// Creates an AMQP link for use with management operations. /// </summary> /// /// <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(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); var entityPath = EntityName + '/' + AmqpClientConstants.ManagementAddress; string[] claims = { ClaimConstants.Manage, ClaimConstants.Listen }; var endpointUri = new Uri(ServiceEndpoint, entityPath); var authExpirationUtc = await RequestAuthorizationUsingCbsAsync(connection, TokenProvider, ServiceEndpoint, endpointUri.AbsoluteUri, endpointUri.AbsoluteUri, claims, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false); var link = new RequestResponseAmqpLink(/*AmqpManagement.LinkType*/ "entity-mgmt", session, entityPath, linkSettings.Properties); linkSettings.LinkName = $"{connection.Settings.ContainerId};{connection.Identifier}:{session.Identifier}:{link.Identifier}:111"; stopWatch.Stop(); // Track the link before returning it, so that it can be managed with the scope. var refreshTimer = default(Timer); var refreshHandler = CreateAuthorizationRefreshHandler ( connection, link, TokenProvider, ServiceEndpoint, ServiceEndpoint.AbsoluteUri, ServiceEndpoint.AbsoluteUri, 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(link, refreshTimer); return(link); } catch { // Aborting the session will perform any necessary cleanup of // the associated link as well. session?.Abort(); throw; } }
/// <summary> /// /// </summary> /// <param name="message"></param> /// <param name="timeout"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task <long> ScheduleMessageInternalAsync( ServiceBusMessage message, TimeSpan timeout, CancellationToken cancellationToken = default) { var stopWatch = Stopwatch.StartNew(); using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message)) { var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.ScheduleMessageOperation, timeout, null); if (_sendLink.TryGetOpenedObject(out SendingAmqpLink sendLink)) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = sendLink.Name; } ArraySegment <byte>[] payload = amqpMessage.GetPayload(); var buffer = new BufferListStream(payload); ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length); var entry = new AmqpMap(); { entry[ManagementConstants.Properties.Message] = value; entry[ManagementConstants.Properties.MessageId] = message.MessageId; if (!string.IsNullOrWhiteSpace(message.SessionId)) { entry[ManagementConstants.Properties.SessionId] = message.SessionId; } if (!string.IsNullOrWhiteSpace(message.PartitionKey)) { entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey; } if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey)) { entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey; } } request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> { entry }; RequestResponseAmqpLink mgmtLink = await _managementLink.GetOrCreateAsync( UseMinimum(_connectionScope.SessionTimeout, timeout.CalculateRemaining(stopWatch.Elapsed))) .ConfigureAwait(false); using AmqpMessage response = await mgmtLink.RequestAsync( request.AmqpMessage, timeout.CalculateRemaining(stopWatch.Elapsed)) .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); stopWatch.Stop(); AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(response); if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK) { var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers); if (sequenceNumbers == null || sequenceNumbers.Length < 1) { throw new ServiceBusException(true, "Could not schedule message successfully."); } return(sequenceNumbers[0]); } else { throw amqpResponseMessage.ToMessagingContractException(); } } }
void CloseRequestResponseSession(RequestResponseAmqpLink requestResponseAmqpLink) { requestResponseAmqpLink.Session.SafeClose(); }
/// <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 = ValueStopwatch.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(AmqpClientConstants.TimeoutName, (uint)timeout.CalculateRemaining(stopWatch.GetElapsedTime()).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.GetElapsedTime())) .ConfigureAwait(false); var link = new RequestResponseAmqpLink( AmqpClientConstants.EntityTypeManagement, session, entityPath, linkSettings.Properties); linkSettings.LinkName = $"{connection.Settings.ContainerId};{connection.Identifier}:{session.Identifier}:{link.Identifier}"; // 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(); ExceptionDispatchInfo.Capture(AmqpExceptionHelper.TranslateException( exception, null, session.GetInnerException(), connection.IsClosing())) .Throw(); throw; // will never be reached } }
/// <summary> /// Retrieves information about an Event Hub, including the number of partitions present /// and their identifiers. /// </summary> /// /// <param name="retryPolicy">The retry policy to use as the basis for retrieving the information.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The set of information for the Event Hub that this client is associated with.</returns> /// public override async Task <EventHubProperties> GetPropertiesAsync(EventHubsRetryPolicy retryPolicy, CancellationToken cancellationToken) { Argument.AssertNotClosed(_closed, nameof(AmqpClient)); Argument.AssertNotNull(retryPolicy, nameof(retryPolicy)); var failedAttemptCount = 0; var retryDelay = default(TimeSpan?); var stopWatch = ValueStopwatch.StartNew(); try { var tryTimeout = retryPolicy.CalculateTryTimeout(0); while (!cancellationToken.IsCancellationRequested) { try { EventHubsEventSource.Log.GetPropertiesStart(EventHubName); // Create the request message and the management link. var token = await AquireAccessTokenAsync(cancellationToken).ConfigureAwait(false); using AmqpMessage request = MessageConverter.CreateEventHubPropertiesRequest(EventHubName, token); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); RequestResponseAmqpLink link = await ManagementLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout.CalculateRemaining(stopWatch.GetElapsedTime()))).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Send the request and wait for the response. using AmqpMessage response = await link.RequestAsync(request, tryTimeout.CalculateRemaining(stopWatch.GetElapsedTime())).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Process the response. AmqpError.ThrowIfErrorResponse(response, EventHubName); return(MessageConverter.CreateEventHubPropertiesFromResponse(response)); } catch (Exception ex) { Exception activeEx = ex.TranslateServiceException(EventHubName); // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop. // Otherwise, mark the exception as active and break out of the loop. ++failedAttemptCount; retryDelay = retryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount); if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested)) { EventHubsEventSource.Log.GetPropertiesError(EventHubName, activeEx.Message); await Task.Delay(retryDelay.Value, cancellationToken).ConfigureAwait(false); tryTimeout = retryPolicy.CalculateTryTimeout(failedAttemptCount); stopWatch = ValueStopwatch.StartNew(); } else if (ex is AmqpException) { ExceptionDispatchInfo.Capture(activeEx).Throw(); } else { throw; } } } // If no value has been returned nor exception thrown by this point, // then cancellation has been requested. throw new TaskCanceledException(); } catch (Exception ex) { EventHubsEventSource.Log.GetPropertiesError(EventHubName, ex.Message); throw; } finally { EventHubsEventSource.Log.GetPropertiesComplete(EventHubName); } }