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;
        }
Beispiel #2
0
        /// <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;
            }
        }
Beispiel #3
0
        /// <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();
                }
            }
        }
Beispiel #4
0
 void CloseRequestResponseSession(RequestResponseAmqpLink requestResponseAmqpLink)
 {
     requestResponseAmqpLink.Session.SafeClose();
 }
Beispiel #5
0
        /// <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);
            }
        }