public void GetUriSchemeUnderstandsAmqpConnectionTypes(EventHubsTransportType transportType)
        {
            var scheme = transportType.GetUriScheme();

            Assert.That(scheme, Is.Not.Null.And.Not.Empty);
            Assert.That(transportType.GetUriScheme(), Contains.Substring("amqp"));
        }
        /// <summary>
        ///   Initializes a new instance of the <see cref="AmqpConnectionScope"/> class.
        /// </summary>
        ///
        /// <param name="serviceEndpoint">Endpoint for the Event Hubs service to which the scope is associated.</param>
        /// <param name="eventHubName"> The name of the Event Hub to which the scope is associated</param>
        /// <param name="credential">The credential to use for authorization with the Event Hubs service.</param>
        /// <param name="transport">The transport to use for communication.</param>
        /// <param name="proxy">The proxy, if any, to use for communication.</param>
        /// <param name="identifier">The identifier to assign this scope; if not provided, one will be generated.</param>
        ///
        public AmqpConnectionScope(Uri serviceEndpoint,
                                   string eventHubName,
                                   EventHubTokenCredential credential,
                                   EventHubsTransportType transport,
                                   IWebProxy proxy,
                                   string identifier = default)
        {
            Argument.AssertNotNull(serviceEndpoint, nameof(serviceEndpoint));
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(credential, nameof(credential));
            ValidateTransport(transport);

            ServiceEndpoint = serviceEndpoint;
            EventHubName    = eventHubName;
            Transport       = transport;
            Proxy           = proxy;
            TokenProvider   = new CbsTokenProvider(new EventHubTokenCredential(credential, serviceEndpoint.ToString()), OperationCancellationSource.Token);
            Id = identifier ?? $"{ eventHubName }-{ Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture).Substring(0, 8) }";

#pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes.
            Task <AmqpConnection> connectionFactory(TimeSpan timeout) => CreateAndOpenConnectionAsync(AmqpVersion, ServiceEndpoint, Transport, Proxy, Id, timeout);

#pragma warning restore CA2214 // Do not call overridable methods in constructors

            ActiveConnection = new FaultTolerantAmqpObject <AmqpConnection>(connectionFactory, CloseConnection);
        }
Example #3
0
        public async Task ProducerCanPublishBatches(EventHubsTransportType transportType)
        {
            await using (EventHubScope scope = await EventHubScope.CreateAsync(1))
            {
                var connectionString = EventHubsTestEnvironment.Instance.BuildConnectionStringForEventHub(scope.EventHubName);
                var options          = new EventHubProducerClientOptions {
                    EnableIdempotentPartitions = true, ConnectionOptions = new EventHubConnectionOptions {
                        TransportType = transportType
                    }
                };

                await using var producer = new EventHubProducerClient(connectionString, options);

                var cancellationSource = new CancellationTokenSource();
                cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit);

                var partition    = (await producer.GetPartitionIdsAsync()).First();
                var batchOptions = new CreateBatchOptions {
                    PartitionId = partition
                };

                using var firstBatch = await producer.CreateBatchAsync(batchOptions, cancellationSource.Token);

                firstBatch.TryAdd(EventGenerator.CreateEvents(1).First());

                using var secondBatch = await producer.CreateBatchAsync(batchOptions, cancellationSource.Token);

                secondBatch.TryAdd(EventGenerator.CreateEvents(1).First());
                secondBatch.TryAdd(EventGenerator.CreateEvents(1).First());

                Assert.That(async() => await producer.SendAsync(firstBatch, cancellationSource.Token), Throws.Nothing, "The first publishing operation was not successful.");
                Assert.That(async() => await producer.SendAsync(secondBatch, cancellationSource.Token), Throws.Nothing, "The second publishing operation was not successful.");
            }
        }
Example #4
0
 /// <summary>
 ///   Provides a test shim for retrieving the credential that a client was
 ///   created with.
 /// </summary>
 ///
 /// <param name="client">The client to retrieve the credential for.</param>
 ///
 /// <returns>The credential with which the client was created.</returns>
 ///
 private string BuildResource(EventHubConnection client,
                              EventHubsTransportType transportType,
                              string fullyQualifiedNamespace,
                              string eventHubName) =>
 typeof(EventHubConnection)
 .GetMethod("BuildAudienceResource", BindingFlags.Static | BindingFlags.NonPublic)
 .Invoke(client, new object[] { transportType, fullyQualifiedNamespace, eventHubName }) as string;
Example #5
0
        /// <summary>
        ///   Determines the URI scheme to be used for the given connection type.
        /// </summary>
        ///
        /// <param name="instance">The instance that this method was invoked on.</param>
        ///
        /// <returns>The scheme that should be used for the given connection type when forming an associated URI.</returns>
        ///
        public static string GetUriScheme(this EventHubsTransportType instance)
        {
            switch (instance)
            {
            case EventHubsTransportType.AmqpTcp:
            case EventHubsTransportType.AmqpWebSockets:
                return(AmqpUriScheme);

            default:
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidTransportType, instance.ToString(), nameof(instance)));
            }
        }
        /// <summary>
        ///   Creates an AMQP connection for a given scope.
        /// </summary>
        ///
        /// <param name="amqpVersion">The version of AMQP to use for the connection.</param>
        /// <param name="serviceEndpoint">The endpoint for the Event Hubs service to which the scope is associated.</param>
        /// <param name="transportType">The type of transport to use for communication.</param>
        /// <param name="proxy">The proxy, if any, to use for communication.</param>
        /// <param name="scopeIdentifier">The unique identifier for the associated scope.</param>
        /// <param name="timeout">The timeout to consider when creating the connection.</param>
        ///
        /// <returns>An AMQP connection that may be used for communicating with the Event Hubs service.</returns>
        ///
        protected virtual async Task <AmqpConnection> CreateAndOpenConnectionAsync(Version amqpVersion,
                                                                                   Uri serviceEndpoint,
                                                                                   EventHubsTransportType transportType,
                                                                                   IWebProxy proxy,
                                                                                   string scopeIdentifier,
                                                                                   TimeSpan timeout)
        {
            var                    hostName          = serviceEndpoint.Host;
            AmqpSettings           amqpSettings      = CreateAmpqSettings(AmqpVersion);
            AmqpConnectionSettings connectionSetings = CreateAmqpConnectionSettings(hostName, scopeIdentifier);

            TransportSettings transportSettings = transportType.IsWebSocketTransport()
                ? CreateTransportSettingsForWebSockets(hostName, proxy)
                : CreateTransportSettingsforTcp(hostName, serviceEndpoint.Port);

            // Create and open the connection, respecting the timeout constraint
            // that was received.

            var stopWatch = Stopwatch.StartNew();

            var           initiator = new AmqpTransportInitiator(amqpSettings, transportSettings);
            TransportBase transport = await initiator.ConnectTaskAsync(timeout).ConfigureAwait(false);

            var connection = new AmqpConnection(transport, amqpSettings, connectionSetings);

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

            stopWatch.Stop();

            // Create the CBS link that will be used for authorization.  The act of creating the link will associate
            // it with the connection.

            new AmqpCbsLink(connection);

            // When the connection is closed, close each of the links associated with it.

            EventHandler closeHandler = null;

            closeHandler = (snd, args) =>
            {
                foreach (var link in ActiveLinks.Keys)
                {
                    link.SafeClose();
                }

                connection.Closed -= closeHandler;
            };

            connection.Closed += closeHandler;
            return(connection);
        }
Example #7
0
        public void BuildTransportClientAllowsLegalConnectionTypes(EventHubsTransportType connectionType)
        {
            var fullyQualifiedNamespace = "my.eventhubs.com";
            var path     = "some-hub";
            var keyName  = "aWonderfulKey";
            var key      = "ABC4223";
            var resource = $"amqps://{ fullyQualifiedNamespace }/{ path }";
            var options  = new EventHubConnectionOptions {
                TransportType = connectionType
            };
            var signature          = new SharedAccessSignature(resource, keyName, key);
            var credential         = new SharedAccessSignatureCredential(signature);
            var eventHubCredential = new EventHubTokenCredential(credential, resource);
            var client             = new EventHubConnection(fullyQualifiedNamespace, path, credential);

            Assert.That(() => client.CreateTransportClient(fullyQualifiedNamespace, path, eventHubCredential, options), Throws.Nothing);
        }
Example #8
0
        private static EventHubProducerClientOptions CreateEventHubClientOptions(
            EventHubConfiguration configuration)
        {
            EventHubsTransportType transportType = EventHubsTransportType.AmqpTcp;

            if (!string.IsNullOrEmpty(configuration.TransportType))
            {
                Enum.TryParse(configuration.TransportType, out transportType);
            }

            return(new EventHubProducerClientOptions
            {
                ConnectionOptions = new EventHubConnectionOptions
                {
                    TransportType = transportType
                }
            });
        }
        /// <summary>
        ///   Builds the audience for use in the signature.
        /// </summary>
        ///
        /// <param name="transportType">The type of protocol and transport that will be used for communicating with the Event Hubs service.</param>
        /// <param name="fullyQualifiedNamespace">The fully qualified Event Hubs namespace.  This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to connect the client to.</param>
        ///
        /// <returns>The value to use as the audience of the signature.</returns>
        ///
        private static string BuildAudienceResource(EventHubsTransportType transportType,
                                                    string fullyQualifiedNamespace,
                                                    string eventHubName)
        {
            var builder = new UriBuilder(fullyQualifiedNamespace)
            {
                Scheme   = transportType.GetUriScheme(),
                Path     = eventHubName,
                Port     = -1,
                Fragment = string.Empty,
                Password = string.Empty,
                UserName = string.Empty,
            };

            if (builder.Path.EndsWith("/"))
            {
                builder.Path = builder.Path.TrimEnd('/');
            }

            return(builder.Uri.AbsoluteUri.ToLowerInvariant());
        }
Example #10
0
        public async Task ConnectionTransportCanRetrieveProperties(EventHubsTransportType transportType)
        {
            var partitionCount = 4;

            await using (EventHubScope scope = await EventHubScope.CreateAsync(partitionCount))
            {
                var connectionString = TestEnvironment.EventHubsConnectionString;

                await using (var connection = new TestConnectionWithTransport(connectionString, scope.EventHubName, new EventHubConnectionOptions {
                    TransportType = transportType
                }))
                {
                    EventHubProperties properties = await connection.GetPropertiesAsync();

                    Assert.That(properties, Is.Not.Null, "A set of properties should have been returned.");
                    Assert.That(properties.Name, Is.EqualTo(scope.EventHubName), "The property Event Hub name should match the scope.");
                    Assert.That(properties.PartitionIds.Length, Is.EqualTo(partitionCount), "The properties should have the requested number of partitions.");
                    Assert.That(properties.CreatedOn, Is.EqualTo(DateTimeOffset.UtcNow).Within(TimeSpan.FromSeconds(60)), "The Event Hub should have been created just about now.");
                }
            }
        }
Example #11
0
        public async Task ConnectionTransportCanRetrievePartitionProperties(EventHubsTransportType transportType)
        {
            var partitionCount = 4;

            await using (EventHubScope scope = await EventHubScope.CreateAsync(partitionCount))
            {
                var options              = new EventHubConnectionOptions();
                var connectionString     = TestEnvironment.BuildConnectionStringForEventHub(scope.EventHubName);
                var connectionProperties = ConnectionStringParser.Parse(connectionString);

                var credential = new SharedAccessSignatureCredential
                                 (
                    new SharedAccessSignature
                    (
                        $"{ options.TransportType.GetUriScheme() }://{ connectionProperties.Endpoint.Host }/{ connectionProperties.EventHubName }".ToLowerInvariant(),
                        connectionProperties.SharedAccessKeyName,
                        connectionProperties.SharedAccessKey,
                        TimeSpan.FromHours(4)
                    )
                                 );

                await using (var connection = new TestConnectionWithTransport(connectionProperties.Endpoint.Host, connectionProperties.EventHubName, credential, new EventHubConnectionOptions {
                    TransportType = transportType
                }))
                {
                    var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(20));
                    var properties   = await connection.GetPropertiesAsync();

                    var partition           = properties.PartitionIds.First();
                    var partitionProperties = await connection.GetPartitionPropertiesAsync(partition, cancellation.Token);

                    Assert.That(partitionProperties, Is.Not.Null, "A set of partition properties should have been returned.");
                    Assert.That(partitionProperties.Id, Is.EqualTo(partition), "The partition identifier should match.");
                    Assert.That(partitionProperties.EventHubName, Is.EqualTo(connectionProperties.EventHubName).Using((IEqualityComparer <string>)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match.");
                    Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated.");
                    Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated.");
                    Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated.");
                }
            }
        }
        /// <summary>
        ///   Initializes a new instance of the <see cref="AmqpConnectionScope"/> class.
        /// </summary>
        ///
        /// <param name="serviceEndpoint">Endpoint for the Event Hubs service to which the scope is associated.</param>
        /// <param name="eventHubName"> The name of the Event Hub to which the scope is associated</param>
        /// <param name="credential">The credential to use for authorization with the Event Hubs service.</param>
        /// <param name="transport">The transport to use for communication.</param>
        /// <param name="proxy">The proxy, if any, to use for communication.</param>
        /// <param name="identifier">The identifier to assign this scope; if not provided, one will be generated.</param>
        ///
        public AmqpConnectionScope(Uri serviceEndpoint,
                                   string eventHubName,
                                   EventHubTokenCredential credential,
                                   EventHubsTransportType transport,
                                   IWebProxy proxy,
                                   string identifier = default)
        {
            Argument.AssertNotNull(serviceEndpoint, nameof(serviceEndpoint));
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(credential, nameof(credential));
            ValidateTransport(transport);

            ServiceEndpoint = serviceEndpoint;
            EventHubName    = eventHubName;
            Transport       = transport;
            Proxy           = proxy;
            TokenProvider   = new CbsTokenProvider(new EventHubTokenCredential(credential, serviceEndpoint.ToString()), OperationCancellationSource.Token);
            Id = identifier ?? $"{ eventHubName }-{ Guid.NewGuid().ToString("D").Substring(0, 8) }";

            Task <AmqpConnection> connectionFactory(TimeSpan timeout) => CreateAndOpenConnectionAsync(AmqpVersion, ServiceEndpoint, Transport, Proxy, Id, timeout);

            ActiveConnection = new FaultTolerantAmqpObject <AmqpConnection>(connectionFactory, CloseConnection);
        }
        public async Task PartitionReceiverCanRetrievePartitionProperties(EventHubsTransportType transportType)
        {
            var partitionCount = 1;

            await using (EventHubScope scope = await EventHubScope.CreateAsync(partitionCount))
            {
                var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(20));

                var connectionString = TestEnvironment.BuildConnectionStringForEventHub(scope.EventHubName);
                var receiverOptions  = new PartitionReceiverOptions {
                    ConnectionOptions = new EventHubConnectionOptions {
                        TransportType = transportType
                    }
                };

                var partitionId = default(string);

                await using (var producer = new EventHubProducerClient(connectionString))
                {
                    partitionId = (await producer.GetPartitionIdsAsync(cancellationSource.Token)).First();
                }

                await using (var receiver = new PartitionReceiver(EventHubConsumerClient.DefaultConsumerGroupName, partitionId, EventPosition.Earliest, connectionString, receiverOptions))
                {
                    var partitionProperties = await receiver.GetPartitionPropertiesAsync(cancellationSource.Token);

                    Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled.");
                    Assert.That(partitionProperties, Is.Not.Null, "A set of partition properties should have been returned.");
                    Assert.That(partitionProperties.Id, Is.EqualTo(partitionId), "The partition identifier should match.");
                    Assert.That(partitionProperties.EventHubName, Is.EqualTo(scope.EventHubName).Using((IEqualityComparer <string>)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match.");
                    Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated.");
                    Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated.");
                    Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated.");
                }
            }
        }
 public void IsWebSocketTransportRecognizesSocketTransports(EventHubsTransportType transportType,
                                                            bool expectedResult)
 {
     Assert.That(transportType.IsWebSocketTransport(), Is.EqualTo(expectedResult));
 }
Example #15
0
 /// <summary>
 ///   Determines whether the specified transport makes use of web sockets.
 /// </summary>
 ///
 /// <param name="instance">The instance that this method was invoked on.</param>
 ///
 /// <returns><c>true</c> if the transport uses web sockets; otherwise, <c>false</c>.</returns>
 ///
 public static bool IsWebSocketTransport(this EventHubsTransportType instance) => (instance == EventHubsTransportType.AmqpWebSockets);