< Summary

Class:Azure.Messaging.ServiceBus.ServiceBusConnection
Assembly:Azure.Messaging.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Azure.Messaging.ServiceBus\src\Primitives\ServiceBusConnection.cs
Covered lines:74
Uncovered lines:11
Coverable lines:85
Total lines:340
Line coverage:87% (74 of 85)
Covered branches:20
Total branches:24
Branch coverage:83.3% (20 of 24)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_FullyQualifiedNamespace()-100%100%
get_IsClosed()-100%100%
get_EntityPath()-100%100%
get_ServiceEndpoint()-0%100%
get_TransportType()-100%100%
get_RetryOptions()-100%100%
.ctor()-100%100%
.ctor(...)-100%100%
.ctor(...)-92.86%75%
CloseAsync()-0%100%
DisposeAsync()-0%100%
Equals(...)-0%100%
GetHashCode()-0%100%
ToString()-0%100%
CreateTransportSender(...)-100%100%
CreateTransportReceiver(...)-100%100%
CreateTransportRuleManager(...)-0%100%
CreateTransportClient(...)-66.67%50%
BuildAudienceResource(...)-100%100%
ValidateConnectionOptions(...)-80%83.33%
ThrowIfClosed()-66.67%50%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Azure.Messaging.ServiceBus\src\Primitives\ServiceBusConnection.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.ComponentModel;
 6using System.Globalization;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Azure.Core;
 10using Azure.Messaging.ServiceBus.Amqp;
 11using Azure.Messaging.ServiceBus.Authorization;
 12using Azure.Messaging.ServiceBus.Core;
 13
 14namespace Azure.Messaging.ServiceBus
 15{
 16    /// <summary>
 17    ///   A connection to the Azure Service Bus service, enabling client communications with a specific
 18    ///   Service Bus entity instance within an Service Bus namespace.  A single connection may be
 19    ///   shared among multiple Service Bus entity senders and/or receivers, or may be used as a
 20    ///   dedicated connection for a single sender or receiver client.
 21    /// </summary>
 22    internal class ServiceBusConnection : IAsyncDisposable
 23    {
 24        /// <summary>
 25        ///   The fully qualified Service Bus namespace that the connection is associated with.
 26        ///   This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.
 27        /// </summary>
 41828        public string FullyQualifiedNamespace { get; }
 29
 30        /// <summary>
 31        ///   Indicates whether or not this <see cref="ServiceBusConnection"/> has been closed.
 32        /// </summary>
 33        ///
 34        /// <value>
 35        ///   <c>true</c> if the connection is closed; otherwise, <c>false</c>.
 36        /// </value>
 2037        public bool IsClosed => _innerClient.IsClosed;
 38
 39        /// <summary>
 40        /// The entity path that the connection is bound to.
 41        /// </summary>
 11442        public string EntityPath { get; }
 43
 44        /// <summary>
 45        ///   The endpoint for the Service Bus service to which the connection is associated.
 46        ///   This is essentially the <see cref="FullyQualifiedNamespace"/> but with
 47        ///   the scheme included.
 48        /// </summary>
 049        internal Uri ServiceEndpoint => _innerClient.ServiceEndpoint;
 50
 51        /// <summary>
 52        /// The transport type used for this connection.
 53        /// </summary>
 8054        public ServiceBusTransportType TransportType { get; }
 55
 56        /// <summary>
 57        /// The retry options associated with this connection.
 58        /// </summary>
 3659        public virtual ServiceBusRetryOptions RetryOptions { get; }
 60
 61        private readonly TransportClient _innerClient;
 62
 63        /// <summary>
 64        /// Parameterless constructor to allow mocking.
 65        /// </summary>
 19666        internal ServiceBusConnection() { }
 67
 68        /// <summary>
 69        ///   Initializes a new instance of the <see cref="ServiceBusConnection"/> class.
 70        /// </summary>
 71        ///
 72        /// <param name="connectionString">The connection string to use for connecting to the Service Bus namespace.</pa
 73        /// <param name="options">A set of options to apply when configuring the connection.</param>
 74        ///
 75        /// <remarks>
 76        ///   If the connection string is copied from the Service Bus entity itself, it will contain the name of the des
 77        ///   and can be used directly without passing the  name="entityName" />.  The name of the Service Bus entity sh
 78        ///   passed only once, either as part of the connection string or separately.
 79        /// </remarks>
 80        ///
 5481        internal ServiceBusConnection(
 5482            string connectionString,
 5483            ServiceBusClientOptions options)
 84        {
 5485            Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString));
 86
 4687            ValidateConnectionOptions(options);
 4488            ConnectionStringProperties connectionStringProperties = ConnectionStringParser.Parse(connectionString);
 89
 4490            if (string.IsNullOrEmpty(connectionStringProperties.Endpoint?.Host)
 4491                || string.IsNullOrEmpty(connectionStringProperties.SharedAccessKeyName)
 4492                || string.IsNullOrEmpty(connectionStringProperties.SharedAccessKey))
 93            {
 694                throw new ArgumentException(Resources.MissingConnectionInformation, nameof(connectionString));
 95            }
 96
 3897            FullyQualifiedNamespace = connectionStringProperties.Endpoint.Host;
 3898            TransportType = options.TransportType;
 3899            EntityPath = connectionStringProperties.EntityPath;
 38100            RetryOptions = options.RetryOptions;
 101
 38102            var sharedAccessSignature = new SharedAccessSignature
 38103            (
 38104                 BuildAudienceResource(options.TransportType, FullyQualifiedNamespace, EntityPath),
 38105                 connectionStringProperties.SharedAccessKeyName,
 38106                 connectionStringProperties.SharedAccessKey
 38107            );
 108
 38109            var sharedCredential = new SharedAccessSignatureCredential(sharedAccessSignature);
 38110            var tokenCredential = new ServiceBusTokenCredential(
 38111                sharedCredential,
 38112                BuildAudienceResource(TransportType, FullyQualifiedNamespace, EntityPath));
 113#pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for t
 38114            _innerClient = CreateTransportClient(tokenCredential, options);
 115#pragma warning restore CA2214 // Do not call overridable methods in constructors
 38116        }
 117
 118        /// <summary>
 119        ///   Initializes a new instance of the <see cref="ServiceBusConnection"/> class.
 120        /// </summary>
 121        ///
 122        /// <param name="fullyQualifiedNamespace">The fully qualified Service Bus namespace to connect to.  This is like
 123        /// <param name="credential">The Azure managed identity credential to use for authorization.  Access controls ma
 124        /// <param name="options">A set of options to apply when configuring the connection.</param>
 14125        internal ServiceBusConnection(
 14126            string fullyQualifiedNamespace,
 14127            TokenCredential credential,
 14128            ServiceBusClientOptions options)
 129        {
 14130            Argument.AssertWellFormedServiceBusNamespace(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace));
 8131            Argument.AssertNotNull(credential, nameof(credential));
 132
 6133            ValidateConnectionOptions(options);
 134            switch (credential)
 135            {
 136                case SharedAccessSignatureCredential _:
 137                    break;
 138
 139                case ServiceBusSharedKeyCredential sharedKeyCredential:
 0140                    credential = sharedKeyCredential.AsSharedAccessSignatureCredential(BuildAudienceResource(options.Tra
 141                    break;
 142            }
 143
 4144            var tokenCredential = new ServiceBusTokenCredential(credential, BuildAudienceResource(options.TransportType,
 145
 4146            FullyQualifiedNamespace = fullyQualifiedNamespace;
 4147            TransportType = options.TransportType;
 4148            RetryOptions = options.RetryOptions;
 149
 150#pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for t
 4151            _innerClient = CreateTransportClient(tokenCredential, options);
 152#pragma warning restore CA2214 // Do not call overridable methods in constructors
 4153        }
 154
 155        /// <summary>
 156        ///   Closes the connection to the Service Bus namespace and associated Service Bus entity.
 157        /// </summary>
 158        ///
 159        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 160        ///
 161        /// <returns>A task to be resolved on when the operation has completed.</returns>
 162        ///
 163        public virtual async Task CloseAsync(CancellationToken cancellationToken = default) =>
 0164            await _innerClient.CloseAsync(cancellationToken).ConfigureAwait(false);
 165
 166        /// <summary>
 167        ///   Performs the task needed to clean up resources used by the <see cref="ServiceBusConnection" />,
 168        ///   including ensuring that the connection itself has been closed.
 169        /// </summary>
 170        ///
 171        /// <returns>A task to be resolved on when the operation has completed.</returns>
 172        ///
 0173        public virtual async ValueTask DisposeAsync() => await CloseAsync().ConfigureAwait(false);
 174
 175        /// <summary>
 176        ///   Determines whether the specified <see cref="System.Object" /> is equal to this instance.
 177        /// </summary>
 178        ///
 179        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
 180        ///
 181        /// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>
 182        ///
 183        [EditorBrowsable(EditorBrowsableState.Never)]
 0184        public override bool Equals(object obj) => base.Equals(obj);
 185
 186        /// <summary>
 187        ///   Returns a hash code for this instance.
 188        /// </summary>
 189        ///
 190        /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a ha
 191        ///
 192        [EditorBrowsable(EditorBrowsableState.Never)]
 0193        public override int GetHashCode() => base.GetHashCode();
 194
 195        /// <summary>
 196        ///   Converts the instance to string representation.
 197        /// </summary>
 198        ///
 199        /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
 200        ///
 201        [EditorBrowsable(EditorBrowsableState.Never)]
 0202        public override string ToString() => base.ToString();
 203
 204        internal virtual TransportSender CreateTransportSender(
 205            string entityPath,
 206            string viaEntityPath,
 207            ServiceBusRetryPolicy retryPolicy,
 208            string identifier) =>
 12209            _innerClient.CreateSender(entityPath, viaEntityPath, retryPolicy, identifier);
 210
 211        internal virtual TransportReceiver CreateTransportReceiver(
 212            string entityPath,
 213            ServiceBusRetryPolicy retryPolicy,
 214            ReceiveMode receiveMode,
 215            uint prefetchCount,
 216            string identifier,
 217            string sessionId = default,
 218            bool isSessionReceiver = default) =>
 4219                _innerClient.CreateReceiver(
 4220                    entityPath,
 4221                    retryPolicy,
 4222                    receiveMode,
 4223                    prefetchCount,
 4224                    identifier,
 4225                    sessionId,
 4226                    isSessionReceiver);
 227
 228        internal virtual TransportRuleManager CreateTransportRuleManager(
 229            string subscriptionPath,
 230            ServiceBusRetryPolicy retryPolicy,
 231            string identifier) =>
 0232            _innerClient.CreateRuleManager(subscriptionPath, retryPolicy, identifier);
 233
 234        /// <summary>
 235        ///   Builds a Service Bus client specific to the protocol and transport specified by the
 236        ///   requested connection type of the <see cref="ServiceBusClientOptions"/>.
 237        /// </summary>
 238        ///
 239        /// <param name="credential">The Azure managed identity credential to use for authorization.</param>
 240        /// <param name="options"></param>
 241        ///
 242        /// <returns>A client generalization specific to the specified protocol/transport to which operations may be del
 243        ///
 244        /// <remarks>
 245        ///   As an internal method, only basic sanity checks are performed against arguments.  It is
 246        ///   assumed that callers are trusted and have performed deep validation.
 247        ///
 248        ///   Parameters passed are also assumed to be owned by thee transport client and safe to mutate or dispose;
 249        ///   creation of clones or otherwise protecting the parameters is assumed to be the purview of the caller.
 250        /// </remarks>
 251        ///
 252        internal virtual TransportClient CreateTransportClient(
 253            ServiceBusTokenCredential credential,
 254            ServiceBusClientOptions options)
 255        {
 42256            switch (TransportType)
 257            {
 258                case ServiceBusTransportType.AmqpTcp:
 259                case ServiceBusTransportType.AmqpWebSockets:
 42260                    return new AmqpClient(FullyQualifiedNamespace, credential, options);
 261
 262                default:
 0263                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidTransportType
 264            }
 265        }
 266
 267        /// <summary>
 268        ///   Builds the audience for use in the signature.
 269        /// </summary>
 270        ///
 271        /// <param name="transportType">The type of protocol and transport that will be used for communicating with the 
 272        /// <param name="fullyQualifiedNamespace">The fully qualified Service Bus namespace.  This is likely to be simil
 273        /// <param name="entityName">The name of the specific entity to connect the client to.</param>
 274        ///
 275        /// <returns>The value to use as the audience of the signature.</returns>
 276        ///
 277        private static string BuildAudienceResource(
 278            ServiceBusTransportType transportType,
 279            string fullyQualifiedNamespace,
 280            string entityName)
 281        {
 80282            var builder = new UriBuilder(fullyQualifiedNamespace)
 80283            {
 80284                Scheme = transportType.GetUriScheme(),
 80285                Path = entityName,
 80286                Port = -1,
 80287                Fragment = string.Empty,
 80288                Password = string.Empty,
 80289                UserName = string.Empty,
 80290            };
 291
 80292            if (builder.Path.EndsWith("/", StringComparison.Ordinal))
 293            {
 44294                builder.Path = builder.Path.TrimEnd('/');
 295            }
 296
 80297            return builder.Uri.AbsoluteUri.ToLowerInvariant();
 298        }
 299
 300        /// <summary>
 301        ///   Performs the actions needed to validate the <see cref="ServiceBusClientOptions" /> associated
 302        ///   with this client.
 303        /// </summary>
 304        ///
 305        /// <param name="connectionOptions">The set of options to validate.</param>
 306        ///
 307        /// <remarks>
 308        ///   In the case that the options violate an invariant or otherwise represent a combination that
 309        ///   is not permissible, an appropriate exception will be thrown.
 310        /// </remarks>
 311        ///
 312        private static void ValidateConnectionOptions(ServiceBusClientOptions connectionOptions)
 313        {
 314            // If there were no options passed, they cannot be in an invalid state.
 315
 52316            if (connectionOptions == null)
 317            {
 0318                return;
 319            }
 320
 321            // A proxy is only valid when web sockets is used as the transport.
 322
 52323            if ((!connectionOptions.TransportType.IsWebSocketTransport()) && (connectionOptions.Proxy != null))
 324            {
 4325                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ProxyMustUseWebSockets),
 326            }
 48327        }
 328
 329        /// <summary>
 330        /// Throw an ObjectDisposedException if the object is Closing.
 331        /// </summary>
 332        internal virtual void ThrowIfClosed()
 333        {
 20334            if (IsClosed)
 335            {
 0336                throw new ObjectDisposedException($"{nameof(ServiceBusConnection)} has already been closed. Please creat
 337            }
 20338        }
 339    }
 340}